1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3  * Copyright (C) 1998-2000 AbiSource, Inc.
4  * Copyright (c) 2001,2002 Tomas Frydrych
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 #include <stdlib.h>
23 #include <math.h>
24 #include <locale.h> 			// localeconv()
25 #include "ut_types.h"	// for FREEP
26 
27 #include "fl_DocLayout.h"
28 #include "fl_FootnoteLayout.h"
29 #include "fl_BlockLayout.h"
30 #include "fb_Alignment.h"
31 #include "fp_Column.h"
32 #include "fp_TableContainer.h"
33 #include "fp_Line.h"
34 #include "fp_Run.h"
35 #include "fp_TextRun.h"
36 #include "fp_Page.h"
37 #include "fv_View.h"
38 #include "fl_SectionLayout.h"
39 #include "fl_TableLayout.h"
40 #include "gr_DrawArgs.h"
41 #include "gr_Graphics.h"
42 #include "ut_assert.h"
43 #include "ut_debugmsg.h"
44 #include "ut_string.h"
45 #include "ap_Prefs.h"
46 #include "fp_FootnoteContainer.h"
47 #include "fp_FrameContainer.h"
48 #include "ut_color.h"
49 
50 #ifdef USE_STATIC_MAP
51 //initialize the static members of the class
52 UT_UCS4Char * fp_Line::s_pPseudoString = 0;
53 UT_uint32   * fp_Line::s_pMapOfRunsL2V = 0;
54 UT_uint32   * fp_Line::s_pMapOfRunsV2L = 0;
55 UT_Byte     * fp_Line::s_pEmbeddingLevels = 0;
56 UT_sint32     fp_Line::s_iMapOfRunsSize = 0;
57 fp_Line     * fp_Line::s_pMapOwner = 0;
58 #else
59 //make sure that any references to the static members are renamed to their non-static versions
60 #define s_iMapOfRunsSize m_iMapOfRunsSize
61 #define s_pMapOfRuns m_pMapOfRuns
62 #endif
63 
64 #define STATIC_BUFFER_INITIAL 150
65 
66 UT_sint32 * fp_Line::s_pOldXs = NULL;
67 UT_uint32   fp_Line::s_iOldXsSize = 0;
68 UT_uint32	fp_Line::s_iClassInstanceCounter = 0;
69 
fp_Line(fl_SectionLayout * pSectionLayout)70 fp_Line::fp_Line(fl_SectionLayout * pSectionLayout) :
71 	fp_Container(FP_CONTAINER_LINE, pSectionLayout),
72 	m_pBlock(NULL),
73 	m_iWidth(0),
74 	m_iMaxWidth(0),
75 	m_iClearToPos(0),
76 	m_iClearLeftOffset(0),
77 	m_iHeight(0),
78 	m_iScreenHeight(-1),
79 	m_iAscent(0),
80 	m_iDescent(0),
81 	m_iX(0),
82 	m_iY(INITIAL_OFFSET), // So setY(0) triggers a clearscreen and redraw!
83 		            // I do not like this at all; we have no business
84 		            // of clearing at fictional coordinances
85 	//m_bRedoLayout(true),
86 	m_bNeedsRedraw(false),
87 	m_bMapDirty(true), //map that has not been initialized is dirty by deafault
88 	m_iRunsRTLcount(0),
89 	m_iRunsLTRcount(0),
90 	m_bIsCleared(true),
91 	m_bContainsFootnoteRef(false),
92 	m_bIsWrapped(false),
93 	m_bIsSameYAsPrevious(false),
94 	m_bIsAlongTopBorder(false),
95 	m_bIsAlongBotBorder(false),
96 	m_iAdditionalMarginAfter(0),
97 	m_iLeftThick(0),
98 	m_iRightThick(0),
99 	m_iTopThick(0),
100 	m_iBotThick(0)
101 {
102 	if(!s_iClassInstanceCounter)
103 	{
104 		s_pOldXs = new UT_sint32[STATIC_BUFFER_INITIAL];
105 		UT_ASSERT(s_pOldXs);
106 		s_iOldXsSize = STATIC_BUFFER_INITIAL;
107 	}
108 
109 	#ifdef USE_STATIC_MAP
110 	if(!s_pMapOfRunsL2V)
111 	{
112 		s_pMapOfRunsL2V = new UT_uint32 [RUNS_MAP_SIZE];
113 		s_pMapOfRunsV2L = new UT_uint32[RUNS_MAP_SIZE];
114 		s_pPseudoString    = new UT_UCS4Char[RUNS_MAP_SIZE];
115 		s_pEmbeddingLevels =  new UT_Byte[RUNS_MAP_SIZE];
116 		s_iMapOfRunsSize = RUNS_MAP_SIZE;
117 	}
118     #else
119 	m_pMapOfRunsL2V = new UT_uint32[RUNS_MAP_SIZE];
120 	m_pMapOfRunsV2L = new UT_uint32[RUNS_MAP_SIZE];
121 	m_pPseudoString    = new UT_UCS4Char[RUNS_MAP_SIZE];
122 	m_pEmbeddingLevels =  new UT_Byte[RUNS_MAP_SIZE];
123 	m_iMapOfRunsSize = RUNS_MAP_SIZE;
124     #endif
125 
126 	UT_ASSERT(s_pMapOfRunsL2V && s_pMapOfRunsV2L && s_pPseudoString && s_pEmbeddingLevels);
127 
128 	++s_iClassInstanceCounter; // this tells us how many instances of Line are out there
129 							   //we use this to decide whether the above should be
130 							   //deleted by the destructor
131 
132 	UT_ASSERT((getPrev() == NULL));
133 	UT_ASSERT((getNext() == NULL));
134 }
135 
~fp_Line()136 fp_Line::~fp_Line()
137 {
138 	--s_iClassInstanceCounter;
139 	if(!s_iClassInstanceCounter)
140 	{
141 		delete [] s_pOldXs;
142 		s_pOldXs = NULL;
143 		s_iOldXsSize = 0;
144 	}
145 #ifdef USE_STATIC_MAP
146 	if(!s_iClassInstanceCounter) //this is the last/only instance of the class Line
147 	{
148 		delete[] s_pMapOfRunsL2V;
149 		s_pMapOfRunsL2V = 0;
150 
151 		delete[] s_pMapOfRunsV2L;
152 		s_pMapOfRunsV2L = 0;
153 
154 		delete[] s_pPseudoString;
155 		s_pPseudoString = 0;
156 
157 		delete[] s_pEmbeddingLevels;
158 		s_pEmbeddingLevels = 0;
159 	}
160 #else
161 	delete[] m_pMapOfRunsL2V;
162 	m_pMapOfRunsL2V = 0;
163 	delete[] m_pMapOfRunsV2L;
164 	m_pMapOfRunsV2L = 0;
165 	delete[] m_pPseudoString;
166 	m_pPseudoString = 0;
167 	delete[] s_pEmbeddingLevels;
168 	m_pEmbeddingLevels = 0;
169 #endif
170 	setScreenCleared(true);
171 	xxx_UT_DEBUGMSG(("Line %x delete refCount %d \n",this,getRefCount()));
172 	//UT_ASSERT(getRefCount() == 0);
173 }
174 
175 //
176 // All these methods need to be adjusted to take account of the thinkness
177 // of the borders drawn around paragraphs. The borders take up additional space
178 // within the dimensions of the lines assigned by the layout calculations.
179 //
180 //
181 // Calculation of the border thickness occurs when:
182 // 1. The previous paragraph changes it's properties
183 // 2. The line is placed in a different container
184 // 3. When the next or previous line is placed in a different container.
185 
186 // The Ascent of the line is the  max ascent of all the runs (plus the top
187 // border thickness for first line of a block)
getAscent(void) const188 UT_sint32 fp_Line::getAscent(void) const
189 {
190     if (getBlock() && getBlock()->hasBorders() && isAlongTopBorder())
191     {
192 	return m_iAscent + getTopThick();
193     }
194     else
195     {
196 	return m_iAscent;
197     }
198 }
199 
200 //
201 // The Descent is the max descent of all the runs (plus the bottom
202 // border thickness for last line of a block)
getDescent(void) const203 UT_sint32 fp_Line::getDescent(void) const
204 {
205     if (getBlock() && getBlock()->hasBorders() && isAlongBotBorder())
206     {
207 	return m_iDescent + getBotThick();
208     }
209     else
210     {
211 	return m_iDescent;
212     }
213 }
214 
215 //
216 // The height is calculated from the sum of all the getAscents and getDescents
217 // so has the height of the top and bot borders included.
218 //
getHeight(void) const219 UT_sint32 fp_Line::getHeight(void) const
220 {
221   return m_iHeight;
222 }
223 
drawBorders(GR_Graphics * pG)224 void fp_Line::drawBorders(GR_Graphics * pG)
225 {
226   if(!getBlock())
227       return;
228   fp_Line * pFirst = const_cast<fp_Line *>(getFirstInContainer());
229   bool bDrawTop = false;
230   bool bDrawBot = false;
231   if(!pFirst)
232       return;
233   fp_Line * pLast = const_cast<fp_Line *>(getLastInContainer());
234   if(!pLast)
235       return;
236   if(pFirst->canDrawTopBorder())
237       bDrawTop = true;
238   if(pLast->canDrawBotBorder())
239       bDrawBot = true;
240 
241   UT_Rect * pFirstR = pFirst->getScreenRect();
242   if(!pFirstR)
243       return;
244   UT_Rect * pLastR = pLast->getScreenRect();
245   if(!pLastR)
246   {
247       delete pFirstR;
248       return;
249   }
250   UT_Rect * pConR = static_cast<fp_VerticalContainer *>(getContainer())->getScreenRect();
251   if(!pConR)
252   {
253       delete pFirstR;
254       delete pLastR;
255       return;
256   }
257   UT_sint32 iTop = pFirstR->top;
258   UT_sint32 iBot = pLastR->top + pLastR->height;
259   UT_sint32 iLeft = pConR->left + getLeftEdge();
260   UT_sint32 iRight = pConR->left + getRightEdge();
261   if(getBlock()->getBottom().m_t_linestyle > 1)
262   {
263       iBot = iBot-getBlock()->getBottom().m_thickness;
264   }
265   //
266   // Now correct for printing
267   //
268   fp_Page * pPage = getPage();
269   if(pPage == NULL)
270        return;
271   if(pPage->getDocLayout()->getView() && pG->queryProperties(GR_Graphics::DGP_PAPER))
272   {
273        UT_sint32 xdiff,ydiff;
274        pPage->getDocLayout()->getView()->getPageScreenOffsets(pPage, xdiff, ydiff);
275        iTop = iTop - ydiff;
276        iBot = iBot - ydiff;
277        iLeft = iLeft - xdiff;
278        iRight = iRight - xdiff;
279        if(pPage->getDocLayout()->getView()->getViewMode() != VIEW_PRINT)
280        {
281 	    iTop += static_cast<fl_DocSectionLayout *>(getSectionLayout()->getDocSectionLayout())->getTopMargin();
282 	    iBot += static_cast<fl_DocSectionLayout *>(getSectionLayout()->getDocSectionLayout())->getTopMargin();
283        }
284   }
285 
286   //
287   // Draw top border
288   PP_PropertyMap::Line line;
289 
290   line = getBlock()->getLeft();
291   iLeft += line.m_thickness/2;
292   line = getBlock()->getRight();
293   iRight -= line.m_thickness/2;
294 
295   if(bDrawTop && (getBlock()->getTop().m_t_linestyle > 1))
296   {
297       line = getBlock()->getTop();
298       drawLine(line,iLeft,iTop,iRight,iTop,pG);
299   }
300   if(getBlock()->getLeft().m_t_linestyle > 1)
301   {
302       line = getBlock()->getLeft();
303       drawLine(line,iLeft,iTop,iLeft,iBot,pG);
304   }
305   if(getBlock()->getRight().m_t_linestyle > 1)
306   {
307       line = getBlock()->getRight();
308       drawLine(line,iRight,iTop,iRight,iBot,pG);
309   }
310   if(bDrawBot && (getBlock()->getBottom().m_t_linestyle > 1))
311   {
312       line = getBlock()->getBottom();
313       drawLine(line,iLeft,iBot,iRight,iBot,pG);
314   }
315   delete pFirstR;
316   delete pLastR;
317   delete pConR;
318 }
319 
320 /*!
321  * The left most esge of the paragraph relative to it's container.
322  */
getLeftEdge(void) const323 UT_sint32 fp_Line::getLeftEdge(void) const
324 {
325         if(!getBlock())
326 	     return 0;
327 	UT_sint32 iLeft = getBlock()->getLeftMargin();
328 	if(getBlock()->getTextIndent() < 0)
329 	{
330 	     iLeft += getBlock()->getTextIndent();
331 	}
332 	return iLeft;
333 }
334 
335 
336 /*!
337  * The left most esge of the paragraph relative to it's container.
338  */
getRightEdge(void) const339 UT_sint32 fp_Line::getRightEdge(void) const
340 {
341 	fp_VerticalContainer * pVCon = static_cast<fp_VerticalContainer *>(getContainer());
342 	if(!pVCon)
343 	  return getMaxWidth();
344 	if(!getBlock())
345 	  return getMaxWidth();
346 	UT_sint32 iRight = pVCon->getWidth() - getBlock()->getRightMargin();
347 	return iRight;
348 }
349 
350 /*!
351  * The absolue left and right edge of the paragraph in terms of the
352  * current graphics context.
353  */
getAbsLeftRight(UT_sint32 & left,UT_sint32 & right)354 bool    fp_Line::getAbsLeftRight(UT_sint32& left,UT_sint32& right)
355 {
356 	fp_VerticalContainer * pVCon = static_cast<fp_VerticalContainer *>(getContainer());
357 	if(!pVCon)
358 	  return false;
359 	if(!getBlock())
360 	  return false;
361 	UT_Rect * pR = pVCon->getScreenRect();
362 	left = pR->left + getLeftEdge();
363 	right = pR->left + pVCon->getWidth() - getBlock()->getRightMargin();
364 	delete pR;
365 	//
366 	// Correct for printing
367 	//
368 	fp_Page * pPage = getPage();
369 	if(pPage == NULL)
370 	    return false;
371 	if(pPage->getDocLayout()->getView() &&  getGraphics()->queryProperties(GR_Graphics::DGP_PAPER))
372 	{
373 	    UT_sint32 xdiff,ydiff;
374 	    pPage->getDocLayout()->getView()->getPageScreenOffsets(pPage, xdiff, ydiff);
375 	    left = left - xdiff;
376 	    right= right - xdiff;
377 	}
378 	return true;
379 }
380 //
381 // The location of the start of the line should take account of the
382 // LeftBorder thickness
383 //
getX(void) const384 UT_sint32 fp_Line::getX(void) const
385 {
386   return m_iX;
387 }
388 
389 //
390 // The top of the line locates the position of the line including
391 // the top border thickness
392 //
getY(void) const393 UT_sint32 fp_Line::getY(void) const
394 {
395   return m_iY;
396 }
397 
398 //
399 // The calculation of the MaxWidth includes the effects of the thickness
400 // of the left and right borders
401 //
getMaxWidth(void) const402 UT_sint32 fp_Line::getMaxWidth(void) const
403 {
404   return m_iMaxWidth;
405 }
406 
getAvailableWidth(void) const407 UT_sint32 fp_Line::getAvailableWidth(void) const
408 {
409   return getMaxWidth() - getLeftThick() - getRightThick();
410 }
411 
calcLeftBorderThick(void)412 UT_sint32 fp_Line::calcLeftBorderThick(void)
413 {
414   m_iLeftThick = 0;
415   if(getBlock() && !getBlock()->hasBorders())
416   {
417       m_iLeftThick = 0;
418   }
419   else if(getBlock())
420   {
421       bool bGetThick = true;
422       if(!getPrev() || getPrev()->getContainerType() != FP_CONTAINER_LINE)
423       {
424 	  bGetThick = true;
425       }
426       else if (isSameYAsPrevious())
427       {
428 	  bGetThick = false;
429       }
430       if(bGetThick)
431       {
432 	  m_iLeftThick =getBlock()->getLeft().m_thickness + getBlock()->getLeft().m_spacing;
433       }
434   }
435   return m_iLeftThick;
436 }
437 
calcRightBorderThick(void)438 UT_sint32 fp_Line::calcRightBorderThick(void)
439 {
440   m_iRightThick = 0;
441   if(getBlock() && !getBlock()->hasBorders())
442   {
443       m_iRightThick = 0;
444   }
445   else if(getBlock())
446   {
447       bool bGetThick = true;
448       if(!getNext() || getNext()->getContainerType() != FP_CONTAINER_LINE)
449       {
450 	  bGetThick = true;
451       }
452       else if (static_cast<fp_Line *>(getNext())->isSameYAsPrevious())
453       {
454 	  bGetThick = false;
455       }
456       if(bGetThick)
457       {
458 	  m_iRightThick =getBlock()->getRight().m_thickness + getBlock()->getRight().m_spacing;
459       }
460   }
461   return m_iRightThick;
462 }
463 
hasBordersOrShading(void) const464 bool fp_Line::hasBordersOrShading(void) const
465 {
466   if(getBlock() && (getBlock()->hasBorders() || (getBlock()->getPattern() > 0)))
467   {
468     return true;
469   }
470   return false;
471 }
472 
calcTopBorderThick(void)473 UT_sint32 fp_Line::calcTopBorderThick(void)
474 {
475   m_iTopThick = 0;
476   if(getBlock() && !getBlock()->hasBorders())
477   {
478        m_iTopThick = 0;
479   }
480   else if(getBlock() && canDrawTopBorder())
481   {
482        m_iTopThick = getBlock()->getTop().m_thickness + getBlock()->getTop().m_spacing;
483   }
484   return m_iTopThick;
485 }
486 
calcBotBorderThick(void)487 UT_sint32 fp_Line::calcBotBorderThick(void)
488 {
489   m_iBotThick = 0;
490   if(getBlock() && !getBlock()->hasBorders())
491   {
492        m_iBotThick = 0;
493   }
494   else if(getBlock() && canDrawBotBorder())
495   {
496        m_iBotThick = getBlock()->getBottom().m_thickness + getBlock()->getBottom().m_spacing;
497   }
498   return m_iBotThick;
499 }
500 
calcBorderThickness(void)501 void fp_Line::calcBorderThickness(void)
502 {
503     calcLeftBorderThick();
504     calcRightBorderThick();
505     calcTopBorderThick();
506     calcBotBorderThick();
507 
508 // set the boolean flags m_bIsAlongTopBorder and m_bIsAlongBotBorder
509     if (canDrawTopBorder())
510     {
511 	if (isFirstLineInBlock())
512 	{
513 	    m_bIsAlongTopBorder = true;
514 	}
515 	if (isSameYAsPrevious())
516 	{
517 	    fp_Line * ppLine = static_cast<fp_Line *> (getPrev());
518 	    while(ppLine && ppLine->isSameYAsPrevious())
519 	    {
520 		ppLine = static_cast<fp_Line *>(ppLine->getPrev());
521 	    }
522 	    if (ppLine && ppLine->isFirstLineInBlock())
523 	    {
524 		m_bIsAlongTopBorder = true;
525 	    }
526 	}
527     }
528     if(canDrawBotBorder())
529     {
530 	if (isLastLineInBlock())
531 	{
532 	    m_bIsAlongBotBorder = true;
533 	}
534 	if (isWrapped())
535 	{
536 	    fp_Line * npLine = static_cast<fp_Line *>(getNext());;
537 	    if (npLine && isSameYAsPrevious())
538 	    {
539 		do
540 		{
541 		    if (npLine->isLastLineInBlock())
542 		    {
543 			m_bIsAlongBotBorder = true;
544 			break;
545 		    }
546 		    npLine = static_cast<fp_Line *>(npLine->getNext());
547 		}
548 		while(npLine && npLine->isSameYAsPrevious());
549 	    }
550 	}
551 	if (m_bIsAlongBotBorder)
552 	{
553 	    fp_Line * ppLine =this;
554 	    while(ppLine && ppLine->isSameYAsPrevious())
555 	    {
556 		ppLine = static_cast<fp_Line *>(ppLine->getPrev());
557 	    }
558 	    if(ppLine)
559 	      ppLine = static_cast<fp_Line *> (ppLine->getPrev());
560 	    while (ppLine && ppLine->isAlongBotBorder())
561 	    {
562 		ppLine->setAlongBotBorder(false);
563 		ppLine->recalcHeight();
564 	    }
565 	}
566     }
567 
568     if (isFirstLineInBlock() && !canDrawTopBorder())
569     {
570 	fl_BlockLayout *pBl = static_cast < fl_BlockLayout * > (getBlock()->getPrev());
571 	fp_Line *pLine = static_cast < fp_Line * > (pBl->getLastContainer());
572 	if(pLine && pLine->isAlongBotBorder())
573 	{
574 	    pBl->setLineHeightBlockWithBorders(-1);
575 	}
576     }
577 }
578 
getFirstInContainer(void) const579 const fp_Line * fp_Line::getFirstInContainer(void) const
580 {
581   const fp_Container * pMyCon = getContainer();
582   if(pMyCon == NULL)
583     return NULL;
584   const fp_ContainerObject * pPrev = getPrev();
585   const fp_ContainerObject * pCurrent = static_cast<const fp_ContainerObject*>(this);
586   while(pPrev && (pPrev->getContainerType() == FP_CONTAINER_LINE) &&
587 	(static_cast<const fp_Line *>(pPrev)->getBlock()) &&
588 	(static_cast<const fp_Line *>(pPrev)->getBlock() == getBlock()) &&
589 	(static_cast<const fp_Line *>(pPrev)->getContainer() == pMyCon))
590   {
591         pCurrent = pPrev;
592         pPrev = pPrev->getPrev();
593   }
594   return static_cast<const fp_Line *>(pCurrent);
595 }
596 
getLastInContainer(void) const597 const fp_Line * fp_Line::getLastInContainer(void) const
598 {
599   const fp_Container * pMyCon = getContainer();
600   if(pMyCon == NULL)
601     return NULL;
602   const fp_ContainerObject * pNext = getNext();
603   const fp_ContainerObject * pCurrent = static_cast<const fp_ContainerObject *>(this);
604   while(pNext && (pNext->getContainerType() == FP_CONTAINER_LINE) &&
605 	(static_cast<const fp_Line *>(pNext)->getBlock()) &&
606 	(static_cast<const fp_Line *>(pNext)->getBlock() == getBlock()) &&
607 	(static_cast<const fp_Line *>(pNext)->getContainer() == pMyCon))
608   {
609         pCurrent = pNext;
610         pNext = pNext->getNext();
611   }
612   return static_cast<const fp_Line *>(pCurrent);
613 }
614 
canDrawTopBorder(void) const615 bool fp_Line::canDrawTopBorder(void) const
616 {
617   const fp_Line * pFirst = getFirstInContainer();
618   if(pFirst == NULL)
619     return false;
620   //
621   // This line could be wrapped at the same Y as the first line
622   //
623   if((pFirst != this) && (pFirst->getY() != getY()))
624      return false;
625   fp_Container * pMyCon = getContainer();
626   if(pMyCon == NULL)
627     return false;
628   if(pFirst == pMyCon->getNthCon(0))
629     return true;
630   if(!getBlock())
631     return true;
632   fp_ContainerObject * pPrev = pFirst->getPrevContainerInSection();
633   if(!pPrev || (pPrev->getContainerType() != FP_CONTAINER_LINE))
634     return true;
635   fl_BlockLayout * pPrevBlock = static_cast<fp_Line *>(pPrev)->getBlock();
636   if(pPrevBlock->canMergeBordersWithNext())
637     return false;
638   return (pFirst == this);
639 }
640 
641 
canDrawBotBorder(void) const642 bool fp_Line::canDrawBotBorder(void) const
643 {
644   const fp_Line * pLast = getLastInContainer();
645   if(pLast == NULL)
646     return false;
647   //
648   // This line could be wrapped at the same Y as the last line
649   //
650   if((pLast != this) && (pLast->getY() != getY()))
651      return false;
652   fp_Container * pMyCon = getContainer();
653   if(pMyCon == NULL)
654     return false;
655   fp_Container * pNext = pLast->getNextContainerInSection();
656   if(pNext == NULL)
657     return true;
658   fp_Line * pNextL = static_cast<fp_Line *>(pNext);
659   if(pNextL->getContainer() == NULL)
660     return true;
661   if(pNextL->getContainer() != pMyCon)
662     return true;
663   fl_BlockLayout * pNextBlock = pNextL->getBlock();
664   if(pNextBlock->canMergeBordersWithPrev())
665     return false;
666   return (pLast == this);
667 }
668 
getLeftThick(void) const669 UT_sint32 fp_Line::getLeftThick(void) const
670 {
671   return m_iLeftThick;
672 }
673 
674 
getRightThick(void) const675 UT_sint32 fp_Line::getRightThick(void) const
676 {
677   return m_iRightThick;
678 }
679 
680 
getTopThick(void) const681 UT_sint32 fp_Line::getTopThick(void) const
682 {
683   return m_iTopThick;
684 }
685 
686 
getBotThick(void) const687 UT_sint32 fp_Line::getBotThick(void) const
688 {
689   return m_iBotThick;
690 }
691 
692 #ifdef DEBUG
assertLineListIntegrity(void)693 bool fp_Line::assertLineListIntegrity(void)
694 {
695 	UT_sint32 k =0;
696 	UT_sint32 width = 0;
697 	xxx_UT_DEBUGMSG(("For line %x \n",this));
698 	fp_Run * pRunBlock = getFirstRun();
699 	fp_Run * pRunLine = NULL;
700 	for(k=0;k<getNumRunsInLine();k++)
701 	{
702 		pRunLine = getRunFromIndex(k);
703 		xxx_UT_DEBUGMSG(("Width of run %d pointer %x width %d \n",k,pRunLine,pRunLine->getWidth()));
704 		width += pRunLine->getWidth();
705 		if(pRunLine != pRunBlock)
706 		{
707 			UT_DEBUGMSG(("Whoops! bug in Line at run %d %p offset %d Type %d \n",k,pRunLine,pRunLine->getBlockOffset(),pRunLine->getType()));
708 			pRunLine->printText();
709 			UT_sint32 i =0;
710 			for(i=0;i<getNumRunsInLine();i++)
711 			{
712 				fp_Run *pRun = getRunFromIndex(i);
713 				pRun->printText();
714 				UT_DEBUGMSG(("Line run %d is %p \n",i,pRun));
715 			}
716 			UT_ASSERT(pRunLine == pRunBlock);
717 		}
718 		UT_return_val_if_fail(pRunBlock,false);
719 		pRunBlock = pRunBlock->getNextRun();
720 	}
721 	xxx_UT_DEBUGMSG(("Line %x Width of line is %d num runs is %d \n",this,width,k)); //   UT_ASSERT(width < getMaxWidth());
722 	return true;
723 }
724 #else
assertLineListIntegrity(void)725 bool fp_Line::assertLineListIntegrity(void)
726 {
727 	return true;
728 }
729 #endif
730 /*!
731  * Return the gap between columns.
732  */
getColumnGap(void)733 UT_sint32  fp_Line::getColumnGap(void)
734 {
735 	return (static_cast<fp_Column *>(getColumn()))->getColumnGap();
736 }
737 
containsOffset(PT_DocPosition blockOffset)738 bool fp_Line::containsOffset(PT_DocPosition blockOffset)
739 {
740 	fp_Run * pRun = getFirstVisRun();
741 	if(blockOffset < pRun->getBlockOffset())
742 	{
743 		return false;
744 	}
745 	pRun = getLastVisRun();
746 	if(blockOffset > (pRun->getBlockOffset() + pRun->getLength()))
747 	{
748 		return false;
749 	}
750 	return true;
751 }
752 
753 /*!
754  * Return two rectangles that represent the space left to the left and right
755  * right of the line where a wrapped object might be.
756  */
genOverlapRects(UT_Rect & recLeft,UT_Rect & recRight)757 void fp_Line::genOverlapRects(UT_Rect & recLeft,UT_Rect & recRight)
758 {
759 	UT_Rect * pRec = getScreenRect();
760 	UT_ASSERT(pRec);
761 	if(pRec == NULL)
762 	{
763 		return;
764 	}
765 	recLeft.top = pRec->top;
766 	recRight.top = pRec->top;
767 	recLeft.height = pRec->height;
768 	recRight.height = pRec->height;
769 
770 	UT_sint32 iLeftX = m_pBlock->getLeftMargin();
771 	UT_sint32 iMaxWidth = getContainer()->getWidth();
772 	UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
773 	if (isFirstLineInBlock())
774 	{
775 		if(iBlockDir == UT_BIDI_LTR)
776 			iLeftX += m_pBlock->getTextIndent();
777 	}
778 	UT_sint32 xdiff = pRec->left - getX();
779 	fp_Line * pPrev = static_cast<fp_Line *>(getPrev());
780 	if(pPrev && isSameYAsPrevious())
781 	{
782 		recLeft.left = pPrev->getX() + pPrev->getMaxWidth() + xdiff;
783 		recLeft.width = getX() + xdiff - recLeft.left;
784 		if(recLeft.width < 0)
785 		  {
786 		    UT_DEBUGMSG(("Same Prev left width -ve!! %d \n",recLeft.width));
787 		  }
788 	}
789 	else
790 	{
791 		recLeft.left = iLeftX + xdiff;
792 		recLeft.width = pRec->left - recLeft.left;
793 		if(recLeft.width < 0)
794 		  {
795 		    UT_DEBUGMSG(("RecLeft width -ve!! %d \n",recLeft.width));
796 		  }
797 	}
798 	recRight.left = pRec->left + pRec->width;
799 	fp_Line * pNext = static_cast<fp_Line *>(getNext());
800 	if(pNext && pNext->isSameYAsPrevious())
801 	{
802 		recRight.width =  pNext->getX() - (getX() + getMaxWidth());
803 		if(recRight.width < 0)
804 		  {
805 		    UT_DEBUGMSG(("Line %p Same Prev RecRight width -ve!! %d \n",this,recRight.width));
806 		  }
807 	}
808 	else
809 	{
810 		iMaxWidth -= m_pBlock->getRightMargin();
811 		recRight.width = iMaxWidth +xdiff - recRight.left;
812 		if(recRight.width < 0)
813 		  {
814 		    UT_DEBUGMSG(("Line %p Not Same RecRight width -ve!! %d \n",this,recRight.width));
815 		  }
816 	}
817 //	UT_ASSERT(recLeft.width >= 0);
818 //	UT_ASSERT(recRight.width >= 0);
819 	delete pRec;
820 }
821 
setSameYAsPrevious(bool bSameAsPrevious)822 void fp_Line::setSameYAsPrevious(bool bSameAsPrevious)
823 {
824   if(getMaxWidth() > 9000 && bSameAsPrevious)
825     {
826       UT_DEBUGMSG(("Same as Previous with Max width %d \n",getMaxWidth()));
827     }
828   xxx_UT_DEBUGMSG(("Line %x Same as Previous with Max width %d Same %d \n",this,getMaxWidth(),bSameAsPrevious));
829   m_bIsSameYAsPrevious = bSameAsPrevious;
830 }
831 
832 /*!
833  * return an rectangle that covers this object on the screen
834  * The calling routine is responsible for deleting the returned struct
835  */
getScreenRect(void)836 UT_Rect * fp_Line::getScreenRect(void)
837 {
838 	UT_sint32 xoff = 0;
839 	UT_sint32 yoff = 0;
840 	UT_Rect * pRec = NULL;
841 	getScreenOffsets(NULL,xoff,yoff);
842 	if (getBlock() && getBlock()->hasBorders())
843 	{
844 		xoff -= getLeftThick();
845 	}
846 	pRec= new UT_Rect(xoff,yoff,getMaxWidth(),getHeight());
847 	return pRec;
848 }
849 
850 /*!
851  * Marks Dirty any runs that overlap the supplied rectangle. This rectangle
852  * is relative to the screen.
853  */
markDirtyOverlappingRuns(UT_Rect & recScreen)854 void fp_Line::markDirtyOverlappingRuns(UT_Rect & recScreen)
855 {
856 	UT_Rect * pRec = NULL;
857 	pRec = getScreenRect();
858 	if(pRec && recScreen.intersectsRect(pRec))
859 	{
860 		DELETEP(pRec);
861 		fp_Run * pRun = fp_Line::getFirstRun();
862 		fp_Run * pLastRun = fp_Line::getLastRun();
863 		while(pRun && pRun != pLastRun)
864 		{
865 			pRun->markDirtyOverlappingRuns(recScreen);
866 			pRun = pRun->getNextRun();
867 		}
868 		if(pRun)
869 		{
870 			pRun->markDirtyOverlappingRuns(recScreen);
871 		}
872 		return;
873 	}
874 	DELETEP(pRec);
875 	return;
876 }
877 
878 /*!
879  * Returns the column containing this line. This takes account of broken tables.
880  */
getColumn(void)881 fp_Container * fp_Line::getColumn(void)
882 {
883 	fp_Container * pCon = getContainer();
884 	if(pCon == NULL)
885 	{
886 		return NULL;
887 	}
888 	else if(pCon->getContainerType() == FP_CONTAINER_FRAME)
889         {
890 	    fp_Page * pPage = static_cast<fp_FrameContainer *>(pCon)->getPage();
891 	    if(pPage == NULL)
892 	      return NULL;
893 	    fp_Container * pCol = static_cast<fp_Container *>(pPage->getNthColumnLeader(0));
894 	    return pCol;
895 
896 	}
897 	else if(pCon->getContainerType() != FP_CONTAINER_CELL)
898 	{
899 		return pCon->getColumn();
900 	}
901 
902 	fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pCon);
903 	return pCell->getColumn(this);
904 }
905 
906 /*!
907  * Returns the page containing this line. Takes account of broken tables.
908  */
getPage(void)909 fp_Page * fp_Line::getPage(void)
910 {
911 	fp_Container * pCon = getColumn();
912 	if(pCon)
913 	{
914 		return pCon->getPage();
915 	}
916 	else
917 	{
918 		return NULL;
919 	}
920 }
921 
canContainPoint() const922 bool fp_Line::canContainPoint() const
923 {
924 	if(!m_pBlock)
925 		return false;
926 
927 	return m_pBlock->canContainPoint();
928 }
929 
930 /*!
931  * Returns true if this is the first line in the block.
932  */
isFirstLineInBlock(void) const933 bool fp_Line::isFirstLineInBlock(void) const
934 {
935 	return (m_pBlock->getFirstContainer() == static_cast<const fp_Container *>(this));
936 }
937 
938 /*!
939  * Returns true if this is the last line in the block.
940  */
isLastLineInBlock(void) const941 bool fp_Line::isLastLineInBlock(void) const
942 {
943 	return (m_pBlock->getLastContainer() == static_cast<const fp_Container *>(this));
944 }
945 
946 /*!
947  * Mark containing block for reformat from the first run of the line.
948  */
setReformat(void)949 void fp_Line::setReformat(void)
950 {
951   if(!getFirstRun())
952       return;
953   UT_sint32 iOff = getFirstRun()->getBlockOffset();
954   if(getBlock())
955       getBlock()->setNeedsReformat(getBlock(),iOff);
956 }
setMaxWidth(UT_sint32 iMaxWidth)957 void fp_Line::setMaxWidth(UT_sint32 iMaxWidth)
958 {
959 	if(iMaxWidth < 60) // This is hardwired to disallow -ve or too small values
960 	{
961 	        UT_DEBUGMSG(("Attempt Max width set to... %d now 60 \n",iMaxWidth));
962 		iMaxWidth = 60;
963 	}
964 	if((m_iMaxWidth > 0) && (m_iMaxWidth != iMaxWidth))
965 	{
966 	    setReformat();
967 	}
968 	m_iMaxWidth = iMaxWidth;
969 	xxx_UT_DEBUGMSG(("Line %x MaxWidth set %d SameY %d \n",this,iMaxWidth,isSameYAsPrevious()));
970 	if(m_iMaxWidth > 9000 && isSameYAsPrevious())
971 	{
972 	    UT_DEBUGMSG(("Found unlikely width set!!! \n"));
973 	}
974 	//
975 	// OK set up the clearscreen parameters
976 	//
977 	m_iClearToPos = iMaxWidth;
978 	if(hasBordersOrShading())
979 	{
980 	  m_iClearToPos = getRightEdge();
981 	}
982 	//
983 	// The problem we're trying to solve here is that some character have
984 	// extensions to left of their position on a line. So if you just
985 	// clear from the start of a line you leave a bit of screen dirt
986 	// from character extension. To solve this we have to clear a bit to
987 	// the left of the line.
988 	// The code below is a heuristic to give us a first approximation for
989 	// when we do not have the info we need. We recalculate later in
990 	// recalcHeight
991 	//
992 	m_iClearLeftOffset = getHeight()/5;
993 	if(getGraphics() && (m_iClearLeftOffset < getGraphics()->tlu(3)))
994 	{
995 		m_iClearLeftOffset = getGraphics()->tlu(3);
996 	}
997 	if(hasBordersOrShading())
998 	{
999 		m_iClearLeftOffset = 0;
1000 	}
1001 	if (getPage() && (getPage()->getWidth() - m_iMaxWidth < m_iClearLeftOffset))
1002 	{
1003 		m_iClearLeftOffset = getPage()->getWidth() - m_iMaxWidth;
1004 		UT_ASSERT(m_iClearLeftOffset >= 0);
1005 	}
1006 }
1007 
setContainer(fp_Container * pContainer)1008 void fp_Line::setContainer(fp_Container* pContainer)
1009 {
1010 	if (pContainer == getContainer())
1011 	{
1012 		return;
1013 	}
1014 
1015 	if (getContainer() && (pContainer != NULL))
1016 	{
1017 		xxx_UT_DEBUGMSG(("SetContainer clearScreen in fp_Line %x page %x \n",this,getPage()));
1018 		clearScreen();
1019 	}
1020 	if(pContainer != NULL)
1021 	{
1022 		getFillType().setParent(&pContainer->getFillType());
1023 	}
1024 	else
1025 	{
1026 		getFillType().setParent(NULL);
1027 	}
1028 
1029 	fp_Container::setContainer(pContainer);
1030 	if(pContainer == NULL)
1031 	{
1032 		return;
1033 	}
1034 	if(getMaxWidth()  == 0 || (pContainer->getWidth() < getMaxWidth()))
1035 	{
1036 		setMaxWidth(pContainer->getWidth());
1037 	}
1038 	if (getBlock() && getBlock()->hasBorders())
1039 	{
1040 	    calcBorderThickness();
1041 	}
1042 	recalcHeight();
1043 }
1044 
getWidthToRun(fp_Run * pLastRun)1045 UT_sint32 fp_Line::getWidthToRun(fp_Run * pLastRun)
1046 {
1047 	calcLeftBorderThick();
1048 	UT_sint32 width = getLeftThick();
1049 	UT_sint32 count = m_vecRuns.getItemCount();
1050 	UT_sint32 i = 0;
1051 	for(i=0;i<count;i++)
1052 	{
1053 		fp_Run * pRun = m_vecRuns.getNthItem(i);
1054 		if(pRun == pLastRun)
1055 		{
1056 			return width;
1057 		}
1058 		width += pRun->getWidth();
1059 	}
1060 	return getLeftThick();
1061 }
1062 
getFilledWidth(void)1063 UT_sint32 fp_Line::getFilledWidth(void)
1064 {
1065 	UT_sint32 width = getLeftThick();
1066 	UT_sint32 count = m_vecRuns.getItemCount();
1067 	UT_sint32 i = 0;
1068 	UT_sint32 incr;
1069 	for(i=0;i<count;i++)
1070 	{
1071 		fp_Run * pRun = m_vecRuns.getNthItem(i);
1072 		width += (incr = pRun->getWidth());
1073 		/* return the largest possible value if there is an obvious overflow.
1074 		 * See 13709 */
1075 		if (incr < 0 || width < 0)
1076 			return G_MAXINT32;
1077 	}
1078 	return width;
1079 }
1080 
removeRun(fp_Run * pRun,bool bTellTheRunAboutIt)1081 bool fp_Line::removeRun(fp_Run* pRun, bool bTellTheRunAboutIt)
1082 {
1083 	// need to tell the previous run to redraw, in case this run contained
1084 	// overstriking characters
1085 //	fp_Run* pPrevRun  = pRun->getPrevRun();
1086 //	if(pPrevRun)
1087 //		pPrevRun->clearScreen();
1088 
1089         if(pRun->getType() == FPRUN_FORCEDPAGEBREAK)
1090 	{
1091 	    getBlock()->forceSectionBreak();
1092 	}
1093 	if (bTellTheRunAboutIt)
1094 	{
1095 	        if(pRun == getLastRun())
1096 	        {
1097 		     clearScreenFromRunToEnd(pRun);
1098 		}
1099 		pRun->setLine(NULL);
1100 	}
1101 
1102 	UT_sint32 ndx = m_vecRuns.findItem(pRun);
1103 	UT_return_val_if_fail(ndx>=0,false);
1104 	m_vecRuns.deleteNthItem(ndx);
1105 
1106 	removeDirectionUsed(pRun->getDirection());
1107 
1108 	return true;
1109 }
1110 
insertRunBefore(fp_Run * pNewRun,fp_Run * pBefore)1111 void fp_Line::insertRunBefore(fp_Run* pNewRun, fp_Run* pBefore)
1112 {
1113 	//UT_DEBUGMSG(("insertRunBefore (line 0x%x, run 0x%x, type %d, dir %d)\n", this, pNewRun, pNewRun->getType(), pNewRun->getDirection()));
1114 	UT_ASSERT(m_vecRuns.findItem(pNewRun) < 0);
1115 	UT_ASSERT(pNewRun);
1116 	UT_ASSERT(pBefore);
1117 
1118 	if (pNewRun->getType() == FPRUN_FIELD)
1119 	{
1120 		fp_FieldRun * fr = static_cast<fp_FieldRun*>(pNewRun);
1121 		if (fr->getFieldType() == FPFIELD_endnote_ref)
1122 			m_bContainsFootnoteRef = true;
1123 	}
1124 
1125 	pNewRun->setLine(this);
1126 
1127 	UT_sint32 ndx = m_vecRuns.findItem(pBefore);
1128 	UT_ASSERT(ndx >= 0);
1129 
1130 	m_vecRuns.insertItemAt(pNewRun, ndx);
1131 
1132 	addDirectionUsed(pNewRun->getDirection());
1133 }
1134 
insertRun(fp_Run * pNewRun)1135 void fp_Line::insertRun(fp_Run* pNewRun)
1136 {
1137 	//UT_DEBUGMSG(("insertRun (line 0x%x, run 0x%x, type %d)\n", this, pNewRun, pNewRun->getType()));
1138 
1139 	UT_ASSERT(m_vecRuns.findItem(pNewRun) < 0);
1140 	pNewRun->setLine(this);
1141 
1142 	m_vecRuns.insertItemAt(pNewRun, 0);
1143 
1144 	addDirectionUsed(pNewRun->getDirection());
1145 }
1146 
addRun(fp_Run * pNewRun)1147 void fp_Line::addRun(fp_Run* pNewRun)
1148 {
1149 	//UT_DEBUGMSG(("addRun (line 0x%x, run 0x%x, type %d)\n", this, pNewRun, pNewRun->getType()));
1150 	if (pNewRun->getType() == FPRUN_FIELD)
1151 	{
1152 		fp_FieldRun * fr = static_cast<fp_FieldRun*>(pNewRun);
1153 		if (fr->getFieldType() == FPFIELD_endnote_ref)
1154 			m_bContainsFootnoteRef = true;
1155 	}
1156 
1157 	UT_ASSERT(m_vecRuns.findItem(pNewRun) < 0);
1158 	pNewRun->setLine(this);
1159 
1160 	m_vecRuns.addItem(pNewRun);
1161 
1162 	addDirectionUsed(pNewRun->getDirection());
1163 	//setNeedsRedraw();
1164 }
1165 
insertRunAfter(fp_Run * pNewRun,fp_Run * pAfter)1166 void fp_Line::insertRunAfter(fp_Run* pNewRun, fp_Run* pAfter)
1167 {
1168 	//UT_DEBUGMSG(("insertRunAfter (line 0x%x, run 0x%x, type %d)\n", this, pNewRun, pNewRun->getType()));
1169 	if (pNewRun->getType() == FPRUN_FIELD)
1170 	{
1171 		fp_FieldRun * fr = static_cast<fp_FieldRun*>(pNewRun);
1172 		if (fr->getFieldType() == FPFIELD_endnote_ref)
1173 			m_bContainsFootnoteRef = true;
1174 	}
1175 
1176 	UT_ASSERT(m_vecRuns.findItem(pNewRun) < 0);
1177 	UT_ASSERT(pNewRun);
1178 	UT_ASSERT(pAfter);
1179 
1180 	pNewRun->setLine(this);
1181 
1182 	UT_sint32 ndx = m_vecRuns.findItem(pAfter);
1183 	UT_ASSERT(ndx >= 0);
1184 
1185 	m_vecRuns.insertItemAt(pNewRun, ndx+1);
1186 
1187 	addDirectionUsed(pNewRun->getDirection());
1188 }
1189 
remove(void)1190 void fp_Line::remove(void)
1191 {
1192 	// getNext()/getPrev() appear hight in the performance statistics
1193 	// called from this function so we will cache them
1194 	fp_ContainerObject * pPrev = getPrev();
1195 	fp_ContainerObject * pNext = getNext();
1196 	if (pNext)
1197 	{
1198 	        pNext->unref();
1199 		pNext->setPrev(pPrev);
1200 		unref();
1201 	}
1202 
1203 	if (pPrev)
1204 	{
1205 	        pPrev->unref();
1206 		pPrev->setNext(pNext);
1207 		unref();
1208 	}
1209 	if(m_pBlock && (m_pBlock->getDocSectionLayout()->isCollapsing()))
1210 	        return;
1211 	if(getContainer())
1212 	{
1213 		xxx_UT_DEBUGMSG(("Removing line %x from container \n",this));
1214 		static_cast<fp_VerticalContainer *>(getContainer())->removeContainer(this);
1215 		setContainer(NULL);
1216 	}
1217 #ifdef USE_STATIC_MAP
1218 	if (s_pMapOwner == this)
1219 		s_pMapOwner = NULL;
1220 #endif
1221 	fp_Line * pNLine = static_cast<fp_Line *>(pNext);
1222 	if(pNLine && pNLine->isSameYAsPrevious())
1223 	{
1224 	  if(!isSameYAsPrevious())
1225 	  {
1226 	       pNLine->setSameYAsPrevious(false);
1227 	       pNLine->setY(getY());
1228 	  }
1229 	}
1230 }
1231 
mapXYToPosition(UT_sint32 x,UT_sint32 y,PT_DocPosition & pos,bool & bBOL,bool & bEOL,bool & isTOC)1232 void fp_Line::mapXYToPosition(UT_sint32 x, UT_sint32 y, PT_DocPosition& pos,
1233 							  bool& bBOL, bool& bEOL, bool &isTOC)
1234 {
1235 	UT_sint32 count = m_vecRuns.getItemCount();
1236 	UT_sint32 i = 0;
1237 	fp_Run* pFirstRun;
1238 	xxx_UT_DEBUGMSG(("fp_line: mapXYToPosition this %x Y %d \n",this,getY()));
1239 	do {
1240 
1241 		pFirstRun = m_vecRuns.getNthItem(_getRunLogIndx(i++)); //#TF retrieve first visual run
1242 		UT_ASSERT(pFirstRun);
1243 
1244 	}while((i < count) && pFirstRun->isHidden());
1245 
1246 	if((i < count) && pFirstRun->isHidden())
1247 	{
1248 		//all runs on this line are hidden, at the moment just assert
1249 		UT_ASSERT( UT_SHOULD_NOT_HAPPEN );
1250 	}
1251 
1252 
1253 
1254 	bBOL = false;
1255 	if ( pFirstRun && (x <= pFirstRun->getX()))
1256 	{
1257 		xxx_UT_DEBUGMSG(("fp_Line::mapXYToPosition [0x%x]: x=%d, first run x=%d\n", this, x, pFirstRun->getX()));
1258 		bBOL = true;
1259 		bool bBBOL = true;
1260 		UT_sint32 y2 = y - pFirstRun->getY() - getAscent() + pFirstRun->getAscent();
1261 		pFirstRun->mapXYToPosition(0, y2, pos, bBBOL, bEOL,isTOC);
1262 		return;
1263 	}
1264 
1265 	// check all of the runs.
1266 
1267 	fp_Run* pClosestRun = NULL;
1268 	UT_sint32 iClosestDistance = 0;
1269 
1270 	for (i=0; i<count; i++)
1271 	{
1272 		fp_Run* pRun2 = m_vecRuns.getNthItem(_getRunLogIndx(i));	//#TF get i-th visual run
1273 
1274 		if (pRun2->canContainPoint() || pRun2->isField())
1275 		{
1276 		  UT_sint32 y2 = y - pRun2->getY() - getAscent() + pRun2->getAscent();
1277 			if ((x >= static_cast<UT_sint32>(pRun2->getX())) && (x < static_cast<UT_sint32>(pRun2->getX() + pRun2->getWidth())))
1278 			{
1279 				// when hit testing runs within a line, we ignore the Y coord
1280 //			if (((y2) >= 0) && ((y2) < (pRun2->getHeight())))
1281 				{
1282 					pRun2->mapXYToPosition(x - pRun2->getX(), y2, pos, bBOL, bEOL,isTOC);
1283 					xxx_UT_DEBUGMSG(("Found run %x offset %d pos %d \n",pRun2,pRun2->getBlockOffset(),pos));
1284 					return;
1285 				}
1286 			}
1287 			else if (((x - pRun2->getX()) == 0) && (pRun2->getWidth() == 0))
1288 			{
1289 				// Zero-length run. This should only happen with
1290 				// FmtMrk Runs.
1291 				// #TF this can also happen legitimately with overstriking text runs
1292 				//UT_ASSERT(FPRUN_FMTMARK == pRun2->getType());
1293 
1294 				pRun2->mapXYToPosition(x - pRun2->getX(), y2, pos, bBOL, bEOL,isTOC);
1295 				return;
1296 			}
1297 
1298 			if (!pClosestRun)
1299 			{
1300 				pClosestRun = pRun2;
1301 				if (x < pRun2->getX())
1302 				{
1303 					iClosestDistance = pRun2->getX() - x;
1304 				}
1305 				else if (x >= pRun2->getX() + pRun2->getWidth())
1306 				{
1307 					iClosestDistance = x - (pRun2->getX() + pRun2->getWidth());
1308 				}
1309 			}
1310 			else
1311 			{
1312 				if (x < pRun2->getX())
1313 				{
1314 					if ((pRun2->getX() - x) < iClosestDistance)
1315 					{
1316 						iClosestDistance = pRun2->getX() - x;
1317 						pClosestRun = pRun2;
1318 					}
1319 				}
1320 				else if (x >= (pRun2->getX() + pRun2->getWidth()))
1321 				{
1322 					if (x - ((pRun2->getX() + pRun2->getWidth())) < iClosestDistance)
1323 					{
1324 						iClosestDistance = x - (pRun2->getX() + pRun2->getWidth());
1325 						pClosestRun = pRun2;
1326 					}
1327 				}
1328 			}
1329 		}
1330 	}
1331 
1332 	// if we do not have a closest run by now, then all the content of
1333 	// this line is hidden; the only circumstance under which this
1334 	// should be legal is if this is a last line in a paragraph and
1335 	// this is the only paragraph in the document, in which case we
1336 	// will use the EndOfParagraph run
1337 	// However, for now we will allow this for all last lines in a
1338 	// paragraph, whether it is the only one in the doc or not, since
1339 	// hidden paragraphs need to be handled elsewhere
1340 
1341 	if(!pClosestRun)
1342 	{
1343 		pClosestRun = getLastVisRun();
1344 
1345 		if(pClosestRun && pClosestRun->getType() == FPRUN_ENDOFPARAGRAPH)
1346 		{
1347 		  UT_sint32 y2 = y - pClosestRun->getY() - getAscent() + pClosestRun->getAscent();
1348 			pClosestRun->mapXYToPosition(x - pClosestRun->getX(), y2, pos, bBOL, bEOL,isTOC);
1349 			return;
1350 		}
1351 
1352 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
1353 		pos = 2; // start of document; this is just to avoid crashing
1354 		return;
1355 	}
1356 
1357 
1358 	UT_sint32 y2 = y - pClosestRun->getY() - getAscent() + pClosestRun->getAscent();
1359 	if(pClosestRun->isField())
1360 	{
1361 		UT_uint32 width = pClosestRun->getWidth() + 1;
1362 		pClosestRun->mapXYToPosition(width , y2, pos, bBOL, bEOL,isTOC);
1363 	}
1364 	else
1365 	{
1366 		pClosestRun->mapXYToPosition(x - pClosestRun->getX(), y2, pos, bBOL, bEOL,isTOC);
1367 	}
1368 }
1369 
getOffsets(fp_Run * pRun,UT_sint32 & xoff,UT_sint32 & yoff)1370 void fp_Line::getOffsets(fp_Run* pRun, UT_sint32& xoff, UT_sint32& yoff)
1371 {
1372 	// This returns the baseline of run. ie the bottom of the line of text
1373 	 //
1374 	UT_sint32 my_xoff = -31999;
1375 	UT_sint32 my_yoff = -31999;
1376 	fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
1377 	pVCon->getOffsets(this, my_xoff, my_yoff);
1378 	xoff = my_xoff + pRun->getX();
1379 	yoff = my_yoff + pRun->getY() + getAscent() - pRun->getAscent();
1380 }
1381 
getScreenOffsets(fp_Run * pRun,UT_sint32 & xoff,UT_sint32 & yoff)1382 void fp_Line::getScreenOffsets(fp_Run* pRun,
1383 							   UT_sint32& xoff,
1384 							   UT_sint32& yoff)
1385 {
1386 	UT_sint32 my_xoff = -31999;
1387 	UT_sint32 my_yoff = -31999;
1388 
1389 	/*
1390 		This method returns the screen offsets of the given
1391 		run, referring to the UPPER-LEFT corner of the run.
1392 	*/
1393 	fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
1394 	pVCon->getScreenOffsets(this, my_xoff, my_yoff);
1395 	if(pRun)
1396         {
1397 	  xoff = my_xoff + pRun->getX();
1398 	  yoff = my_yoff + pRun->getY();
1399 	}
1400 	else
1401 	{
1402 	  xoff = my_xoff;
1403 	  yoff = my_yoff;
1404 	}
1405 }
1406 
setHeight(UT_sint32 i)1407 void fp_Line::setHeight(UT_sint32 i)
1408 {
1409     xxx_UT_DEBUGMSG(("Line %p set to height %d \n",this,i));
1410     m_iHeight = i;
1411 }
1412 
setBlock(fl_BlockLayout * pBlock)1413 void fp_Line::setBlock(fl_BlockLayout * pBlock)
1414 {
1415     if(pBlock != NULL)
1416     {
1417       UT_ASSERT(m_pBlock == NULL);
1418       //      UT_ASSERT(pBlock->findLineInBlock(this) >= 0);
1419     }
1420     m_pBlock = pBlock;
1421     if(m_pBlock && (m_pBlock->getPattern() > 0))
1422     {
1423         UT_RGBColor c = m_pBlock->getShadingingForeColor();
1424         getFillType().setColor(c);
1425     }
1426 }
1427 
1428 /*!
1429   Set height assigned to line on screen
1430   \param iHeight Height in screen units
1431 
1432   While recalcHeight computes the height of the line as it will render
1433   on the screen, the fp_Column does the actual line layout and does so
1434   with greater accuracy. In particular, the line may be assigned a
1435   different height on the screen than what it asked for.
1436 
1437   This function allows the line representation to reflect the actual
1438   screen layout size, which improves the precision of XY/position
1439   conversion functions.
1440 
1441   \note This function is quite intentionally <b>not</b> called
1442 		setHeight. It should <b>only</b> be called from
1443 		fp_Column::layout.
1444 
1445   \see fp_Column::layout
1446   Note by Sevior: This method is causing pixel dirt by making lines smaller
1447   than their calculated heights!
1448 */
setAssignedScreenHeight(UT_sint32 iHeight)1449 void fp_Line::setAssignedScreenHeight(UT_sint32 iHeight)
1450 {
1451 	m_iScreenHeight = iHeight;
1452 }
1453 
1454 /*!
1455   Compute the height of the line
1456 
1457   Note that while the line is asked to provide height/width and
1458   computes this based on its content Runs, it may later be assigned
1459   additional screen estate by having its height changed. That does not
1460   affect or override layout details, but increases precision of
1461   XY/position conversions.
1462 
1463   \fixme I originally put in an assertion that checked that the line
1464 		 was only ever asked to grow in size. But that fired a lot, so
1465 		 it had to be removed. This suggests that we actually try to
1466 		 render stuff to closely on screen - the fp_Line::recalcHeight
1467 		 function should probably be fixed to round height and widths
1468 		 up always. But it gets its data from Runs, so this is not
1469 		 where the problem should be fixed.
1470 
1471   \see fp_Column::layout, fp_Line::setAssignedScreenHeight
1472 */
recalcHeight(fp_Run * pLastRun)1473 void fp_Line::recalcHeight(fp_Run * pLastRun)
1474 {
1475 	UT_sint32 count = m_vecRuns.getItemCount();
1476 	if(count == 0)
1477 	{
1478 		return;
1479 	}
1480 	UT_sint32 i;
1481 
1482 	UT_sint32 iMaxAscent = 0;
1483 	UT_sint32 iMaxDescent = 0;
1484 	UT_sint32 iMaxImage =0;
1485 	UT_sint32 iMaxText = 0;
1486 	fp_Line * pPrev = static_cast<fp_Line *>(getPrev());
1487 	if(pPrev && isSameYAsPrevious())
1488 	{
1489 		iMaxAscent = pPrev->getAscent();
1490 		iMaxDescent = pPrev->getDescent();
1491 		iMaxText = pPrev->getHeight();
1492 	}
1493 	bool bSetByImage = false;
1494 	fp_Run* pRun = m_vecRuns.getNthItem(0);
1495 	xxx_UT_DEBUGMSG(("Orig Height = %d \n",getHeight()));
1496 	UT_sint32 iOldHeight = getHeight();
1497 	UT_sint32 iOldAscent = getAscent();
1498 	UT_sint32 iOldDescent = getDescent();
1499 	for (i=0; (i<count && ((pRun != pLastRun) || ((i== 0) && (getHeight() ==0)))); i++)
1500 	{
1501 		UT_sint32 iAscent;
1502 		UT_sint32 iDescent;
1503 
1504 		pRun = m_vecRuns.getNthItem(i);
1505 
1506 		iAscent = pRun->getAscent();
1507 		iDescent = pRun->getDescent();
1508 
1509 		if (pRun->isSuperscript() || pRun->isSubscript())
1510 		{
1511 			iAscent += iAscent * 1/2;
1512 			iDescent += iDescent;
1513 		}
1514 		if(pRun->getType() == FPRUN_IMAGE)
1515 		{
1516 			iMaxImage = UT_MAX(iAscent,iMaxImage);
1517 		}
1518 		else
1519 		{
1520 			iMaxText = UT_MAX(iAscent,iMaxText);
1521 		}
1522 		iMaxAscent = UT_MAX(iMaxAscent, iAscent);
1523 		iMaxDescent = UT_MAX(iMaxDescent, iDescent);
1524 	}
1525 	//
1526 	// More accurate calculation of the amount we need to clear to the
1527 	// left of the line.
1528 	//
1529 	m_iClearLeftOffset = iMaxDescent;
1530 	if(hasBordersOrShading())
1531 	{
1532 		m_iClearLeftOffset = 0;
1533 	}
1534 	if (getPage() && (getPage()->getWidth() - m_iMaxWidth < m_iClearLeftOffset))
1535 	{
1536 		m_iClearLeftOffset = getPage()->getWidth() - m_iMaxWidth;
1537 		UT_ASSERT(m_iClearLeftOffset >= 0);
1538 	}
1539 
1540 	UT_sint32 iNewHeight = iMaxAscent + iMaxDescent;
1541 	UT_sint32 iNewAscent = iMaxAscent;
1542 	UT_sint32 iNewDescent = iMaxDescent;
1543 
1544 	// adjust line height to include leading
1545 	double dLineSpace;
1546 
1547 	fl_BlockLayout::eSpacingPolicy eSpacing;
1548 
1549 	m_pBlock->getLineSpacing(dLineSpace, eSpacing);
1550 
1551 	if(fabs(dLineSpace) < 0.0001)
1552 	{
1553 		xxx_UT_DEBUGMSG(("fp_Line: Set Linespace to 1.0 \n"));
1554 		dLineSpace = 1.0;
1555 	}
1556 	if(iMaxImage > 0 && (iMaxImage > iMaxText * dLineSpace))
1557 	{
1558 		bSetByImage = true;
1559 	}
1560 	if (eSpacing == fl_BlockLayout::spacing_EXACT)
1561 	{
1562 		xxx_UT_DEBUGMSG(("recalcHeight exact \n"));
1563 		iNewHeight = static_cast<UT_sint32>(dLineSpace);
1564 	}
1565 	else if (eSpacing == fl_BlockLayout::spacing_ATLEAST)
1566 	{
1567 		xxx_UT_DEBUGMSG(("SEVIOR: recalcHeight at least \n"));
1568 		iNewHeight = UT_MAX(iNewHeight, static_cast<UT_sint32>(dLineSpace));
1569 	}
1570 	else
1571 	{
1572 		// multiple
1573 		if(!bSetByImage)
1574 		{
1575 			iNewHeight = static_cast<UT_sint32>(iNewHeight * dLineSpace +0.5);
1576 		}
1577 		else
1578 		{
1579 			iNewHeight = UT_MAX(iMaxAscent+static_cast<UT_sint32>(iMaxDescent*dLineSpace + 0.5), static_cast<UT_sint32>(dLineSpace));
1580 		}
1581 	}
1582 	if(getBlock() && getBlock()->hasBorders())
1583 	{
1584 		if (isAlongTopBorder())
1585 		{
1586 			iNewHeight += m_iTopThick;
1587 		}
1588 		if (isAlongBotBorder())
1589 		{
1590 			iNewHeight += m_iBotThick;
1591 		}
1592 	}
1593 	if(isSameYAsPrevious() && pPrev)
1594 	{
1595 		if(iNewHeight > pPrev->getHeight())
1596 		{
1597 			getBlock()->forceSectionBreak();
1598 			pPrev->clearScreen();
1599 			pPrev->setHeight(iNewHeight);
1600 			pPrev->setAscent(iNewAscent);
1601 			pPrev->setDescent(iNewDescent);
1602 			pPrev->setScreenHeight(-1);
1603 			while(pPrev->getPrev() && pPrev->isSameYAsPrevious())
1604 			{
1605 				pPrev = static_cast<fp_Line *>(pPrev->getPrev());
1606 				pPrev->clearScreen();
1607 				pPrev->setHeight(iNewHeight);
1608 				pPrev->setAscent(iNewAscent);
1609 				pPrev->setDescent(iNewDescent);
1610 				pPrev->setScreenHeight(-1);
1611 			}
1612 			return;
1613 		}
1614 		else if(iNewHeight < pPrev->getHeight())
1615 		{
1616 			clearScreen();
1617 			setHeight(pPrev->getHeight());
1618 			setAscent(pPrev->getAscent());
1619 			m_iScreenHeight = -1;	// undefine screen height
1620 			setDescent(pPrev->getDescent());
1621 			return;
1622 		}
1623 	}
1624 
1625 	if (
1626 		(iOldHeight != iNewHeight)
1627 		|| (iOldAscent != iNewAscent)
1628 		|| (iOldDescent != iNewDescent)
1629 //		|| (iNewHeight > m_iScreenHeight)
1630 		)
1631 	{
1632 		clearScreen();
1633 		getBlock()->forceSectionBreak();
1634 		setHeight(iNewHeight);
1635 		m_iScreenHeight = -1;	// undefine screen height
1636 		m_iAscent = iNewAscent;
1637 		m_iDescent = iNewDescent;
1638 	}
1639 	if((getHeight() == 0) && (pLastRun != NULL))
1640 	{
1641 		setHeight(pLastRun->getHeight());
1642 		m_iAscent = pLastRun->getAscent();
1643 		m_iDescent = pLastRun->getDescent();
1644 	}
1645 // See bug 11158
1646 // According to TF this happen for hidden text.
1647 // Disable the assert.
1648 //	UT_ASSERT(getHeight() > 0);
1649 }
1650 
1651 /*!
1652  * Return a pointer to the run given by runIndex in the  line
1653 \param runIndex the nth run in the line
1654 \returns fp_Run * pRun the pointer to the nth run
1655 */
1656 
getRunFromIndex(UT_uint32 runIndex)1657 fp_Run * fp_Line::getRunFromIndex(UT_uint32 runIndex)
1658 {
1659 	UT_sint32 count = m_vecRuns.getItemCount();
1660 	fp_Run * pRun = NULL;
1661 	if(count > 0 && static_cast<UT_sint32>(runIndex) < count)
1662 	{
1663 		pRun = m_vecRuns.getNthItem(runIndex);
1664 	}
1665 	return pRun;
1666 }
1667 
clearScreen(void)1668 void fp_Line::clearScreen(void)
1669 {
1670 	if(getBlock() == NULL)
1671 	{
1672 		return;
1673 	}
1674 	if(getBlock()->isHdrFtr() || isScreenCleared())
1675 	{
1676 		return;
1677 	}
1678 	xxx_UT_DEBUGMSG(("fp_Line: Doing regular full clearscreen %x \n",this));
1679 	UT_sint32 count = m_vecRuns.getItemCount();
1680 	if(getPage() && !getPage()->isOnScreen())
1681 	{
1682 		return;
1683 	}
1684 	getFillType().setIgnoreLineLevel(true);
1685 	if(count)
1686 	{
1687 		fp_Run* pRun;
1688 		bool bNeedsClearing = true;
1689 
1690 		UT_sint32 j;
1691 
1692 		pRun = m_vecRuns.getNthItem(0);
1693 		if(!pRun->getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN))
1694 			return;
1695 
1696 		for (j = 0; j < count; j++)
1697 		{
1698 			pRun = m_vecRuns.getNthItem(j);
1699 
1700 			if(!pRun->isDirty())
1701 			{
1702 				bNeedsClearing = true;
1703 
1704 				pRun->markAsDirty();
1705 			}
1706 		}
1707 
1708 		if(bNeedsClearing)
1709 		{
1710 			pRun = m_vecRuns.getNthItem(0);
1711 
1712 			UT_sint32 xoffLine, yoffLine;
1713 			fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
1714 			pVCon->getScreenOffsets(this, xoffLine, yoffLine);
1715 
1716 			// Note: we use getHeight here instead of m_iScreenHeight
1717 			// in case the line is asked to render before it's been
1718 			// assigned a height. Call it robustness, if you want.
1719 
1720 			UT_sint32 height = getHeight();
1721 			UT_sint32 sHeight = m_iScreenHeight;
1722 			if(sHeight > height)
1723 			{
1724 				height = sHeight;
1725 			}
1726 //
1727 // Screen Height might extend beyond the bottom of the column. We take account
1728 // of that here
1729 //
1730 			if(pVCon->getHeight() < (getY() + height))
1731 			{
1732 				height -= (getY() + height - pVCon->getHeight());
1733 			}
1734 			xxx_UT_DEBUGMSG(("ClearScreen height is %d \n",height));
1735 			// I have added the +1 to clear dirt after squiggles and
1736 			// revision underlines
1737 			if(getPage() == NULL)
1738 			{
1739 			        getFillType().setIgnoreLineLevel(false);
1740 				return;
1741 			}
1742 //			UT_sint32 iExtra = getGraphics()->getFontAscent()/2;
1743 			fl_DocSectionLayout * pSL =  getBlock()->getDocSectionLayout();
1744 			UT_sint32 iExtra = getGraphics()->tlu(2);
1745 			xxx_UT_DEBUGMSG(("full clearscren prect %x \n",getGraphics()->getClipRect()));
1746 			if(getContainer() && (getContainer()->getContainerType() != FP_CONTAINER_CELL) && (getContainer()->getContainerType() != FP_CONTAINER_FRAME))
1747 
1748 			{
1749 				if(pSL->getNumColumns() >1)
1750 				{
1751 					iExtra = pSL->getColumnGap()/2;
1752 				}
1753 				else
1754 				{
1755 					iExtra = pSL->getRightMargin()/2;
1756 				}
1757 			}
1758 			UT_ASSERT(m_iClearToPos + m_iClearLeftOffset <= getPage()->getWidth());
1759 //			pRun->Fill(getGraphics(),xoffLine - m_iClearLeftOffset, yoffLine, m_iClearToPos + m_iClearLeftOffset+iExtra, height);
1760 			pRun->Fill(getGraphics(),xoffLine - m_iClearLeftOffset, yoffLine, getMaxWidth() + m_iClearLeftOffset +iExtra, height);
1761 			xxx_UT_DEBUGMSG(("Clear pLine %x clearoffset %d xoffline %d width %d \n",this,m_iClearLeftOffset,xoffLine,getMaxWidth() + m_iClearLeftOffset +iExtra));
1762 //
1763 // Sevior: I added this for robustness.
1764 //
1765 			setScreenCleared(true);
1766 			m_pBlock->setNeedsRedraw();
1767 			setNeedsRedraw();
1768 			UT_sint32 i;
1769 			for(i=0; i < m_vecRuns.getItemCount();i++)
1770 			{
1771 				pRun = m_vecRuns.getNthItem(i);
1772 				pRun->markAsDirty();
1773 				pRun->setCleared();
1774 			}
1775 		}
1776 	}
1777 	getFillType().setIgnoreLineLevel(false);
1778 }
1779 
1780 /*!
1781    helper function containing the code shared by the two
1782    clearScreenFromRunToEnd() functions
1783 */
_doClearScreenFromRunToEnd(UT_sint32 runIndex)1784 void fp_Line::_doClearScreenFromRunToEnd(UT_sint32 runIndex)
1785 {
1786 	// need to get _visually_ first run on screen
1787 	xxx_UT_DEBUGMSG((" _doClearScreenFromRunIndex %d this %x \n",runIndex,this));
1788 	fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(0));
1789 	UT_sint32 count = m_vecRuns.getItemCount();
1790 
1791 	if(count > 0 && !pRun->getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN))
1792 		return;
1793 
1794 	getFillType().setIgnoreLineLevel(true);
1795 
1796 	// not sure what the reason for this is (Tomas, Oct 25, 2003)
1797 	fp_Run * pLeftVisualRun = pRun;
1798 	fp_Run * pRunToEraseFrom  = m_vecRuns.getNthItem(runIndex);
1799 
1800 	bool bUseFirst = false;
1801 
1802 	if(runIndex == 1)
1803 	{
1804 		bUseFirst = true;
1805 	}
1806 
1807 	// Find the first non dirty run. NO!! fp_Run::clearScreen sets the
1808 	// run Dirty first thing is it. Lets just do what we're told to do..
1809 
1810 	// the run index is visual and if we are in LTR paragraph we
1811 	// are deleting to the right of the run, while if we are in RTL
1812 	// paragraph we are deleting to the _left_ (Tomas, Oct 25, 2003)
1813 	UT_BidiCharType iDomDirection = m_pBlock->getDominantDirection();
1814 
1815 #if 0
1816 	UT_sint32 i;
1817 	if(iDomDirection == UT_BIDI_LTR)
1818 	{
1819 		for(i = runIndex; i < count; i++)
1820 		{
1821 			pRun = m_vecRuns.getNthItem(_getRunLogIndx(i));
1822 
1823 			if(pRun->isDirty())
1824 			{
1825 				if(runIndex < count-1)
1826 				{
1827 					runIndex++;
1828 				}
1829 			}
1830 			else
1831 			{
1832 				break;
1833 			}
1834 		}
1835 	}
1836 	else
1837 	{
1838 		for(i = runIndex; i>=0; i--)
1839 		{
1840 			pRun = m_vecRuns.getNthItem(_getRunLogIndx(i));
1841 
1842 			if(pRun->isDirty() && runIndex > 0)
1843 			{
1844 				runIndex--;
1845 			}
1846 			else
1847 			{
1848 				break;
1849 			}
1850 		}
1851 	}
1852 
1853 #endif
1854 	// if we have a valid index to clear from, let's do it ...
1855 
1856 	if(runIndex < count)
1857 	{
1858 		UT_sint32 xoff, yoff;
1859 
1860 		// get the run at the (visual) index
1861 		pRun = m_vecRuns.getNthItem(_getRunLogIndx(runIndex));
1862 
1863 		// Handle case where character extends behind the left side
1864 		// like italic Times New Roman f. Clear a litle bit before if
1865 		// there is clear screen there
1866 		UT_sint32 j = runIndex - 1;
1867 
1868 		// need to get previous _visual_ run
1869 		fp_Run * pPrev = j >= 0 ? getRunAtVisPos(j) : NULL;
1870 
1871 		UT_sint32 leftClear = 0;
1872 		while(j >= 0 && pPrev != NULL && pPrev->getLength() == 0)
1873 		{
1874 			//pPrev = static_cast<fp_Run *>(m_vecRuns.getNthItem(j));
1875 			pPrev->markAsDirty();
1876 			pPrev = getRunAtVisPos(j);
1877 			j--;
1878 		}
1879 
1880 		// now mark the last pPrev run as dirty, so we do not have to do
1881 		// that lateer
1882 
1883 		if(pPrev)
1884 			pPrev->markAsDirty();
1885 
1886 		leftClear = pRun->getDescent();
1887 		if(j>0 && pPrev != NULL && pPrev->getType() == FPRUN_TEXT)
1888 		{
1889 			// the text run is not at the very left edge, so we need
1890 			// not to clear into the margin
1891 			leftClear = 0;
1892 		}
1893 		else if(j>=0 && pPrev != NULL && pPrev->getType() == FPRUN_FIELD)
1894 		{
1895 			leftClear = 0;
1896 		}
1897 		else if(j>=0 && pPrev != NULL && pPrev->getType() == FPRUN_IMAGE)
1898 		{
1899 			leftClear = 0;
1900 		}
1901 		if(pRun->getType() == FPRUN_IMAGE)
1902 		{
1903 			leftClear = 0;
1904 		}
1905 		if(bUseFirst)
1906 		{
1907 			getScreenOffsets(pLeftVisualRun, xoff, yoff);
1908 		}
1909 		else
1910 		{
1911 			getScreenOffsets(pRun, xoff, yoff);
1912 		}
1913 
1914 		UT_sint32 xoffLine, yoffLine;
1915 #if DEBUG
1916 		//UT_sint32 oldheight = getHeight();
1917 #endif
1918 		recalcHeight();
1919 #if DEBUG
1920 		//UT_ASSERT(oldheight == getHeight());
1921 #endif
1922 
1923 		fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
1924 		pVCon->getScreenOffsets(this, xoffLine, yoffLine);
1925 		UT_sint32 diff = xoff - xoffLine;
1926 		UT_ASSERT(yoff == yoffLine);
1927 
1928 		// static_cast might fail after a table
1929 		// resulting in a runtime error and a potential crash
1930 		fp_Line * pPrevLine = dynamic_cast<fp_Line *>(getPrevContainerInSection());
1931 		if(pPrevLine != NULL && (pPrevLine->getContainerType() == FP_CONTAINER_LINE))
1932 		{
1933 			UT_sint32 xPrev=0;
1934 			UT_sint32 yPrev=0;
1935 
1936 			fp_Run * pLastRun = pPrevLine->getLastRun();
1937 			if(pLastRun != NULL)
1938 			{
1939 				pPrevLine->getScreenOffsets(pLastRun,xPrev,yPrev);
1940 				if((leftClear >0) && (yPrev > 0) && (yPrev == yoffLine))
1941 				{
1942 					leftClear = 0;
1943 				}
1944 			}
1945 		}
1946 		if(xoff == xoffLine)
1947 		        leftClear = m_iClearLeftOffset;
1948 		if(getPage() == NULL)
1949 		{
1950 			xxx_UT_DEBUGMSG(("pl_Line _doClear no Page \n"));
1951 			getFillType().setIgnoreLineLevel(false);
1952 			return;
1953 		}
1954 
1955 		UT_sint32 iExtra = getGraphics()->tlu(2);
1956 
1957 		// this code adds half of the section/column margin to the left of the erasure are
1958 		// -- we can only do this if the run we are to erase from is the leftmost run
1959 		// (otherwise we erase area that belongs to runs that will not redraw themselves)
1960 
1961 		if(pLeftVisualRun == pRunToEraseFrom)
1962 		{
1963 			fl_DocSectionLayout * pSL =  getBlock()->getDocSectionLayout();
1964 			if(getContainer() &&
1965 			   (getContainer()->getContainerType() != FP_CONTAINER_CELL) &&
1966 			   (getContainer()->getContainerType() != FP_CONTAINER_FRAME))
1967 			{
1968 				if(pSL->getNumColumns() >1)
1969 				{
1970 					iExtra = pSL->getColumnGap()/2;
1971 				}
1972 				else
1973 				{
1974 					iExtra = pSL->getRightMargin()/2;
1975 				}
1976 			}
1977 		}
1978 
1979 //		UT_ASSERT((m_iClearToPos + leftClear - (xoff-xoffLine)) <= getPage()->getWidth());
1980 		xxx_UT_DEBUGMSG(("Clear from runindex to end height %d \n",getHeight()));
1981 		xxx_UT_DEBUGMSG(("Width of clear %d \n",m_iClearToPos + leftClear - xoff));
1982 		xxx_UT_DEBUGMSG((" m_iClearToPos %d leftClear %d xoff %d xoffline %d \n",m_iClearToPos,leftClear,xoff,xoffLine));
1983 		xxx_UT_DEBUGMSG(("_doclearscren prect %x \n",getGraphics()->getClipRect()));
1984 		// now we do the clearing
1985 		if(iDomDirection == UT_BIDI_LTR)
1986 		{
1987 			// clear from the start of the run (minus any adjustments)
1988 			// to the end of the line
1989 			pRun->Fill(getGraphics(), xoff - leftClear,
1990 					   yoff,
1991 					   getMaxWidth() + leftClear + iExtra -diff,
1992 					   getHeight());
1993 		}
1994 		else
1995 		{
1996 			// clear from the left edge of the line (minus any
1997 			// adjustments) to the end of the run
1998 			pRun->Fill(getGraphics(), xoffLine - leftClear,
1999 					   yoff,
2000 					   (xoff-xoffLine) + pRun->getWidth() + leftClear,
2001 					   getHeight());
2002 		}
2003 
2004 
2005 //
2006 // Sevior: I added this for robustness.
2007 //
2008 		getBlock()->setNeedsRedraw();
2009 		setNeedsRedraw();
2010 
2011 		// finally, mark all runs concerned as dirty, starting with
2012 		// the first run
2013 		if(bUseFirst)
2014 		{
2015 			//pRun = static_cast<fp_Run*>(m_vecRuns.getNthItem(_getRunLogIndx(0)));
2016 			pRun = pLeftVisualRun;
2017 			runIndex = 0;
2018 		}
2019 		pRun->markAsDirty();
2020 		pRun->setCleared();
2021 		xxx_UT_DEBUGMSG(("Run %x marked Dirty \n",pRun));
2022 		// now we need to mark all the runs between us and the end of
2023 		// the line as dirty
2024 		if(iDomDirection == UT_BIDI_RTL)
2025 		{
2026 			// we are working from the visual index to the left
2027 			if(runIndex > 0)
2028 			{
2029 				runIndex--;
2030 
2031 				while(runIndex >= 0)
2032 				{
2033 					pRun = m_vecRuns.getNthItem(_getRunLogIndx(runIndex));
2034 					UT_ASSERT(pRun);
2035 					pRun->markAsDirty();
2036 					// do not have to check for line identity, since we
2037 					// are working with runs on this line only
2038 					runIndex--;
2039 				}
2040 			}
2041 		}
2042 		else
2043 		{
2044 			// we are working from the visual index to the right
2045 			runIndex++;
2046 			while(runIndex < count)
2047 			{
2048 				pRun = m_vecRuns.getNthItem(_getRunLogIndx(runIndex));
2049 				UT_ASSERT(pRun);
2050 				pRun->markAsDirty();
2051 				runIndex++;
2052 			}
2053 		}
2054 
2055 
2056 	}
2057 	else
2058 	{
2059 		xxx_UT_DEBUGMSG(("Invalid run index %d count %d \n",runIndex,count));
2060 		clearScreen();
2061 		// no valid indext to clear from ...
2062 		getBlock()->setNeedsRedraw();
2063 		setNeedsRedraw();
2064 	}
2065 	getFillType().setIgnoreLineLevel(false);
2066 }
2067 
2068 /*!
2069  * This method clears a line from the run given to the end of the line.
2070 \param fp_Run * pRun
2071 */
clearScreenFromRunToEnd(fp_Run * ppRun)2072 void fp_Line::clearScreenFromRunToEnd(fp_Run * ppRun)
2073 {
2074 	xxx_UT_DEBUGMSG(("Clear 1 from run %x to end of line %x \n",ppRun,this));
2075 	if(getBlock()->isHdrFtr())
2076 	{
2077 		return;
2078 	}
2079 
2080 	fp_Run * pRun = NULL;
2081 	UT_sint32 count =  m_vecRuns.getItemCount();
2082 	if(count > 0)
2083 	{
2084 		pRun = m_vecRuns.getNthItem(0);
2085 		if(!pRun->getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN))
2086 			return;
2087 
2088 		UT_sint32 k = m_vecRuns.findItem(ppRun);
2089 		if(k>=0)
2090 		{
2091 			UT_sint32 runIndex = _getRunVisIndx((UT_uint32)k);
2092 			_doClearScreenFromRunToEnd(runIndex);
2093 		}
2094 	}
2095 }
2096 
2097 
2098 /*!
2099  * This method clears a line from the first non-dirty run at the given index
2100  * to the end of the line.
2101 \param UT_uint32 runIndex - visual run index
2102 \note clearing to end in RTL paragraph means clearing from run end to
2103 the left edge
2104 */
2105 
clearScreenFromRunToEnd(UT_uint32 runIndex)2106 void fp_Line::clearScreenFromRunToEnd(UT_uint32 runIndex)
2107 {
2108 	xxx_UT_DEBUGMSG(("Clear 2 from run %d to end line %x \n",runIndex,this));
2109 	if(getBlock()->isHdrFtr())
2110 	{
2111 		return;
2112 	}
2113 
2114 	_doClearScreenFromRunToEnd((UT_sint32)runIndex);
2115 }
2116 
2117 
setNeedsRedraw(void)2118 void fp_Line::setNeedsRedraw(void)
2119 {
2120 	m_bNeedsRedraw = true;
2121 	if(getContainer() && getContainer()->getContainerType() == FP_CONTAINER_CELL)
2122 	{
2123 		fp_CellContainer * pCell = static_cast<fp_CellContainer *>(getContainer());
2124 		pCell->markAsDirty();
2125 	}
2126 	m_pBlock->setNeedsRedraw();
2127 }
2128 
2129 /*! returns true if the line is on screen, false otherwise
2130     the caller can use this to test whether further processing is
2131     necessary, for instance inside a loop if the return value changes
2132     from true to false then no more visible lines are forthcoming and
2133     the loop can be terminated
2134  */
redrawUpdate(void)2135 bool fp_Line::redrawUpdate(void)
2136 {
2137 	if(!isOnScreen())
2138 		return false;
2139 
2140 	UT_sint32 count = m_vecRuns.getItemCount();
2141 	if(count)
2142 	{
2143 		draw(m_vecRuns.getNthItem(0)->getGraphics());
2144 	}
2145 
2146 	m_bNeedsRedraw = false;
2147 	return true;
2148 }
2149 
2150 
draw(GR_Graphics * pG)2151 void fp_Line::draw(GR_Graphics* pG)
2152 {
2153 	//line can be wider than the max width due to trailing spaces
2154 	//UT_ASSERT(m_iWidth <= m_iMaxWidth);
2155 
2156 	UT_sint32 count = m_vecRuns.getItemCount();
2157 	if(count <= 0)
2158 		return;
2159 
2160 	UT_sint32 my_xoff = 0, my_yoff = 0;
2161 	fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
2162 	pVCon->getScreenOffsets(this, my_xoff, my_yoff);
2163 	xxx_UT_DEBUGMSG(("SEVIOR: Drawing line in line pG, my_yoff=%d  my_xoff %d \n",my_yoff,my_xoff));
2164 
2165 	if(((my_yoff < -128000) || (my_yoff > 128000)) && pG->queryProperties(GR_Graphics::DGP_SCREEN))
2166 	{
2167 		UT_DEBUGMSG(("FREM: Not drawing line in line pG, my_yoff=%d  my_xoff %d \n",my_yoff,my_xoff));
2168 //
2169 // offscreen don't bother.
2170 //
2171 		return;
2172 	}
2173 	dg_DrawArgs da;
2174 
2175 	da.yoff = my_yoff + getAscent();
2176 	da.xoff = my_xoff;
2177 	da.pG = pG;
2178 	da.bDirtyRunsOnly = true; //magic line to give a factor 2 speed up!
2179 	const UT_Rect* pRect = pG->getClipRect();
2180 	bool bDoShade = (getBlock() && (getBlock()->getPattern() > 0));
2181 	if(bDoShade)
2182         {
2183 	    da.bDirtyRunsOnly = false;
2184 	    //
2185 	    // Calculate the region of the fill for a shaded paragraph.
2186 	    //
2187 	    UT_Rect * pVRec = 	pVCon->getScreenRect();
2188 	    UT_sint32 xs = pVRec->left + getLeftEdge();
2189 	    UT_sint32 width = getRightEdge() - getLeftEdge();
2190 	    UT_sint32 ys = my_yoff;
2191 	    getFillType().Fill(pG,xs,ys,xs,ys,width,getHeight());
2192 	    xxx_UT_DEBUGMSG(("pG Fill at y %d and to width %d \n",ys,getMaxWidth()));
2193 	}
2194 	for (int i=0; i < count; i++)
2195 	{
2196 		fp_Run* pRun = getRunAtVisPos(i);
2197 		if(pRun->isHidden())
2198 			continue;
2199 
2200 		FP_RUN_TYPE rType = pRun->getType();
2201 
2202 		// for these two types of runs, we want to draw for the
2203 		// entire line-width on the next line. see bug 1301
2204 		if (rType == FPRUN_FORCEDCOLUMNBREAK ||
2205 			rType == FPRUN_FORCEDPAGEBREAK)
2206 		{
2207 			// there's no need to reset anything - a page or column
2208 			// break is logically always the last thing on a line or
2209 			// a page
2210 			da.xoff = my_xoff;
2211 		}
2212 		else
2213 		{
2214 			da.xoff += pRun->getX();
2215 		}
2216 
2217 		da.yoff += pRun->getY();
2218 		// shortcircuit drawing if we're not included in the dirty region
2219 		UT_Rect runRect(da.xoff, da.yoff, pRun->getWidth(), pRun->getHeight());
2220 
2221 		if (pRect == NULL || pRect->intersectsRect(&runRect))
2222 			pRun->draw(&da);
2223 
2224 		da.xoff -= pRun->getX();
2225 		da.yoff -= pRun->getY();
2226 	}
2227 //
2228 // Check if this is in a cell, if so redraw the lines around it.
2229 //
2230 #if 0
2231 	fp_Container * pCon = getContainer();
2232 	if(pCon->getContainerType() == FP_CONTAINER_CELL)
2233 	{
2234 		fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pCon);
2235 		pCell->drawLinesAdjacent();
2236 	}
2237 #endif
2238 	if(getBlock() && getBlock()->hasBorders())
2239 	     drawBorders(pG);
2240 }
2241 
draw(dg_DrawArgs * pDA)2242 void fp_Line::draw(dg_DrawArgs* pDA)
2243 {
2244 	UT_sint32 count = m_vecRuns.getItemCount();
2245 	if(count <= 0)
2246 		return;
2247 	bool bQuickPrint = pDA->pG->canQuickPrint();
2248 	UT_sint32 i = 0;
2249 	if(bQuickPrint)
2250         {
2251 	      for (i=0; i<count; i++)
2252 	      {
2253 		   fp_Run* pRun = static_cast<fp_Run*>(m_vecRuns.getNthItem(i));
2254 		   pRun->lookupProperties(pDA->pG);
2255 	      }
2256 	      if(getBlock()->getAlignment() && getBlock()->getAlignment()->getType() == FB_ALIGNMENT_JUSTIFY)
2257 	      {
2258 		   getBlock()->getAlignment()->initialize(this);
2259 	      }
2260 	}
2261 	xxx_UT_DEBUGMSG(("Drawing line %x in line pDA, width %d \n",this,getWidth()));
2262 	pDA->yoff += getAscent();
2263 	xxx_UT_DEBUGMSG(("fp_Line::draw getAscent() %d getAscent() %d yoff %d \n",getAscent(),getAscent(),pDA->yoff));
2264 	const UT_Rect* pRect = pDA->pG->getClipRect();
2265 	if(getBlock() && (getBlock()->getPattern() > 0))
2266 	{
2267 	      xxx_UT_DEBUGMSG(("pRect in fp_Line::draw is %p \n",pRect));
2268 	      UT_sint32 x = pDA->xoff;
2269 	      UT_sint32 y = pDA->yoff - getAscent();
2270 	      x= x - getX() + getLeftEdge();
2271 	      UT_sint32 width = getRightEdge() - getLeftEdge();
2272 	      if(!pDA->bDirtyRunsOnly)
2273 	      {
2274 		    getFillType().Fill(pDA->pG,x,y,x,y,width,getHeight());
2275 		    xxx_UT_DEBUGMSG(("Fill at y %d and to width %d \n",y,getMaxWidth()));
2276 	      }
2277 	}
2278 	for (i=0; i<count; i++)
2279 	{
2280 	      fp_Run* pRun = getRunAtVisPos(i);
2281 	      if(pRun->isHidden())
2282 		  continue;
2283 
2284 	      FP_RUN_TYPE rType = pRun->getType();
2285 
2286 	      dg_DrawArgs da = *pDA;
2287 
2288 	      // for these two types of runs, we want to draw for the
2289 	      // entire line-width on the next line. see bug 1301
2290 	      if (rType == FPRUN_FORCEDCOLUMNBREAK ||
2291 			rType == FPRUN_FORCEDPAGEBREAK)
2292 	      {
2293 		   UT_sint32 my_xoff = 0, my_yoff = 0;
2294 		   fp_VerticalContainer * pVCon= (static_cast<fp_VerticalContainer *>(getContainer()));
2295 		   pVCon->getScreenOffsets(this, my_xoff, my_yoff);
2296 		   da.xoff = my_xoff;
2297 	      }
2298 	      else
2299 	      {
2300 		   da.xoff += pRun->getX();
2301 	      }
2302 
2303 	      da.yoff += pRun->getY();
2304 	      UT_Rect runRect(da.xoff, da.yoff - pRun->getAscent(), pRun->getWidth(), pRun->getHeight());
2305 	      xxx_UT_DEBUGMSG(("fp_Line: Draw run in line at yoff %d pRun->getY() \n",da.yoff,pRun->getY()));
2306 	      if (pRect == NULL || pRect->intersectsRect(&runRect))
2307 	      {
2308 		   pRun->draw(&da);
2309 	      }
2310 	      else
2311 	      {
2312 		   xxx_UT_DEBUGMSG(("Run not in clip, pRect top %d height %d run top %d height %d \n",pRect->top,pRect->height,runRect.top,runRect.height));
2313 	      }
2314 	      da.yoff -= pRun->getY();
2315 	}
2316 	if(bQuickPrint)
2317         {
2318 	      if(getBlock()->getAlignment() && getBlock()->getAlignment()->getType() == FB_ALIGNMENT_JUSTIFY)
2319 	      {
2320 		   getBlock()->getAlignment()->initialize(this);
2321 	      }
2322 	}
2323 //
2324 // Check if this is in a cell, if so redraw the lines around it.
2325 //
2326 #if 0
2327 	fp_Container * pCon = getContainer();
2328 	if(pCon->getContainerType() == FP_CONTAINER_CELL)
2329 	{
2330 		fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pCon);
2331 		pCell->drawLinesAdjacent();
2332 	}
2333 #endif
2334 	if(getBlock() && getBlock()->hasBorders())
2335 	        drawBorders(pDA->pG);
2336 
2337 }
2338 
2339 //this is a helper function for getRunWith; it works out working direction and
2340 //which tabs to use from the alignment
2341 //it is public, because it is only used when getRunWidth is called from
2342 //outside of the class
2343 
getWorkingDirectionAndTabstops(FL_WORKING_DIRECTION & eWorkingDirection,FL_WHICH_TABSTOP & eUseTabStop) const2344 void fp_Line::getWorkingDirectionAndTabstops(FL_WORKING_DIRECTION &eWorkingDirection, FL_WHICH_TABSTOP &eUseTabStop) const
2345 {
2346 	fb_Alignment* pAlignment = m_pBlock->getAlignment();
2347 	UT_ASSERT(pAlignment);
2348 	FB_AlignmentType eAlignment = pAlignment->getType();
2349 
2350 	// find out the direction of the paragraph
2351 	UT_BidiCharType iDomDirection = m_pBlock->getDominantDirection();
2352 
2353 	eWorkingDirection = WORK_FORWARD;
2354 	eUseTabStop = USE_NEXT_TABSTOP;
2355 
2356 	// now from the current alignment work out which way we need to process the line
2357 	// and the corresponding starting positions
2358 
2359 	switch (eAlignment)
2360 	{
2361 		case FB_ALIGNMENT_LEFT:
2362 			if(iDomDirection == UT_BIDI_RTL)
2363 				eUseTabStop = USE_PREV_TABSTOP;
2364 			else
2365 				eUseTabStop = USE_NEXT_TABSTOP;
2366 
2367 			eWorkingDirection = WORK_FORWARD;
2368 			break;
2369 
2370 		case FB_ALIGNMENT_RIGHT:
2371 			if(iDomDirection == UT_BIDI_RTL)
2372 				eUseTabStop = USE_NEXT_TABSTOP;
2373 			else
2374 				eUseTabStop = USE_PREV_TABSTOP;
2375 
2376 			eWorkingDirection = WORK_BACKWARD;
2377 			break;
2378 
2379 		case FB_ALIGNMENT_CENTER:
2380 			eWorkingDirection = WORK_FORWARD;
2381 			eUseTabStop = USE_FIXED_TABWIDTH;
2382 			break;
2383 
2384 		case FB_ALIGNMENT_JUSTIFY:
2385 			if(iDomDirection == UT_BIDI_RTL)
2386 				eWorkingDirection = WORK_BACKWARD;
2387 			else
2388 				eWorkingDirection = WORK_FORWARD;
2389 
2390 			eUseTabStop = USE_NEXT_TABSTOP;
2391 			break;
2392 
2393 		default:
2394 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2395 	}
2396 	return;
2397 }
2398 
2399 // this function will calculate correct width for all runs, including
2400 // tab runs, from a visual processing index, it return point to the run
2401 // at the index for the callers convenience.
2402 
calculateWidthOfRun(UT_sint32 & iWidth,UT_uint32 iIndxVisual,FL_WORKING_DIRECTION eWorkingDirection,FL_WHICH_TABSTOP eUseTabStop)2403 fp_Run* fp_Line::calculateWidthOfRun(UT_sint32 &iWidth, UT_uint32 iIndxVisual, FL_WORKING_DIRECTION eWorkingDirection, FL_WHICH_TABSTOP eUseTabStop)
2404 {
2405 	const UT_sint32 iCountRuns		  = m_vecRuns.getItemCount();
2406 	UT_ASSERT(iCountRuns > static_cast<UT_sint32>(iIndxVisual));
2407 
2408 	//work out the real index based on working direction
2409 	UT_uint32 iIndx =0;
2410 	iIndx = eWorkingDirection == WORK_FORWARD ? iIndxVisual : iCountRuns - iIndxVisual - 1;
2411 
2412 	// of course, the loop is running in visual order, but the vector is
2413 	// in logical order
2414 	fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(iIndx));
2415 
2416 	// find out the direction of the paragraph
2417 	UT_BidiCharType iDomDirection = m_pBlock->getDominantDirection();
2418 
2419 	UT_sint32 iXreal = 0;
2420 	if(iDomDirection == UT_BIDI_RTL)
2421 	  iXreal = getMaxWidth() - iWidth;
2422 	else
2423 		iXreal = iWidth;
2424 
2425 	xxx_UT_DEBUGMSG(("fp_Line::calculateWidthOfRun (0x%x L 0x%x): \n"
2426 				 "		 iXreal %d, iXLreal %d, iIndxVisual %d, iCountRuns %d,\n"
2427 				 "		 eWorkingDirection %d, eUseTabStop %d,\n"
2428 						 ,pRun,this,iXreal, iXLreal, iIndxVisual, iCountRuns,
2429 						 eWorkingDirection, eUseTabStop
2430 				));
2431 
2432 	_calculateWidthOfRun(iXreal,
2433 						 pRun,
2434 						 iIndxVisual,
2435 						 iCountRuns,
2436 						 eWorkingDirection,
2437 						 eUseTabStop,
2438 						 iDomDirection
2439 						 );
2440 
2441 //xxx_UT_DEBUGMSG(("new iXreal %d, iXLreal %d\n", iXreal, iXLreal));
2442 
2443 	if(iDomDirection == UT_BIDI_RTL)
2444 	{
2445 	  iWidth = getMaxWidth() - iXreal;
2446 	}
2447 	else
2448 	{
2449 		iWidth = iXreal;
2450 	}
2451 
2452 	return pRun;
2453 }
2454 
2455 // private version of the above, which expect both the index and run prointer
2456 // to be passed to it.
_calculateWidthOfRun(UT_sint32 & iX,fp_Run * pRun,UT_uint32 iIndx,UT_uint32 iCountRuns,FL_WORKING_DIRECTION eWorkingDirection,FL_WHICH_TABSTOP eUseTabStop,UT_BidiCharType iDomDirection)2457 inline void fp_Line::_calculateWidthOfRun(	UT_sint32 &iX,
2458 									fp_Run * pRun,
2459 									UT_uint32 iIndx,
2460 									UT_uint32 iCountRuns,
2461 									FL_WORKING_DIRECTION eWorkingDirection,
2462 									FL_WHICH_TABSTOP eUseTabStop,
2463 									UT_BidiCharType iDomDirection
2464 									)
2465 {
2466 	if(!pRun)
2467 		return;
2468 
2469 	// If the run is to be hidden just return, since the positions
2470 	// should remain as they were, but it would be preferable if this
2471 	// situation was trapped higher up (thus the assert)
2472 	if(pRun->isHidden())
2473 	{
2474 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2475 		return;
2476 	}
2477 
2478 	switch(pRun->getType())
2479 	{
2480 		case FPRUN_TAB:
2481 		{
2482 			// a fixed width fraction of the font ascent which we will use for centered alignment
2483 			// i.e, width = font_ascent * iFixedWidthMlt / iFixedWidthDiv
2484 			const UT_uint32 iFixedWidthMlt = 2;
2485 			const UT_uint32 iFixedWidthDiv = 1;
2486 			UT_uint32 iWidth = 0;
2487 			fp_TabRun* pTabRun = static_cast<fp_TabRun*>(pRun);
2488 
2489 			// now we handle any Tab runs that are not meant to use a fixed width
2490 			if (eUseTabStop != USE_FIXED_TABWIDTH)
2491 			{
2492 				//if we are using the tabstops, we go through the hoops,
2493 				UT_sint32	iPos = 0;
2494 				eTabType	iTabType =FL_TAB_LEFT ;
2495 				eTabLeader	iTabLeader = FL_LEADER_NONE;
2496 				UT_DebugOnly<bool> bRes = false;
2497 				if(pTabRun->isTOCTab())
2498 				{
2499 					iTabLeader = getBlock()->getTOCTabLeader(10);
2500 					iTabType =  FL_TAB_LEFT;
2501 					iPos =  getBlock()->getTOCTabPosition(10);
2502 					bRes = true;
2503 				}
2504 				else if(pTabRun->isTOCTabListLabel())
2505 				{
2506 					iTabLeader = FL_LEADER_NONE;
2507 					iTabType =  FL_TAB_LEFT;
2508 					bRes =  findNextTabStop(iX, iPos, iTabType, iTabLeader);
2509 				}
2510 
2511 				// now find the tabstop for this tab, depending on whether we
2512 				// are to use next or previous tabstop
2513 				else if(eUseTabStop == USE_NEXT_TABSTOP)
2514 				{
2515 					if(iDomDirection == UT_BIDI_RTL)
2516 					{
2517 						UT_sint32 iStartPos = getContainer()->getWidth() - iX;
2518 						bRes = findNextTabStop(iStartPos, iPos, iTabType, iTabLeader);
2519 						iPos = getContainer()->getWidth() - iPos;
2520 					}
2521 					else
2522 						bRes = findNextTabStop(iX, iPos, iTabType, iTabLeader);
2523 				}
2524 				else
2525 				{
2526 					if(iDomDirection == UT_BIDI_RTL)
2527 					{
2528 						UT_sint32 iStartPos = getContainer()->getWidth() - iX;
2529 
2530 						bRes = findPrevTabStop(iStartPos, iPos, iTabType, iTabLeader);
2531 						iPos = getContainer()->getWidth() - iPos;
2532 					}
2533 					else
2534 						bRes = findPrevTabStop(iX, iPos, iTabType, iTabLeader);
2535 				}
2536 				UT_ASSERT(bRes);
2537 
2538 				fp_Run *pScanRun = NULL;
2539 				UT_sint32 iScanWidth = 0;
2540 				pTabRun->setLeader(iTabLeader);
2541 				pTabRun->setTabType(iTabType);
2542 				// we need to remember what the iX was
2543 				UT_sint32 iXprev;
2544 				iXprev = iX;
2545 
2546 				UT_BidiCharType iVisDirection = pTabRun->getVisDirection();
2547 
2548 				switch ( iTabType )
2549 				{
2550 				case FL_TAB_LEFT:
2551 					if(iVisDirection == UT_BIDI_LTR && iDomDirection == UT_BIDI_LTR)
2552 					{
2553 						iX = iPos;
2554 
2555 						iWidth = abs(iX - iXprev);
2556 						xxx_UT_DEBUGMSG(("left tab (ltr para), iX %d, iWidth %d\n", iX,iWidth));
2557 					}
2558 					else
2559 					{
2560 						iScanWidth = 0;
2561 						for ( UT_uint32 j = iIndx+1; j < iCountRuns; j++ )
2562 						{
2563 							UT_uint32 iJ;
2564 							iJ = eWorkingDirection == WORK_FORWARD ? j : iCountRuns - j - 1;
2565 
2566 							pScanRun = m_vecRuns.getNthItem(_getRunLogIndx(iJ));
2567 							if(!pScanRun || pScanRun->getType() == FPRUN_TAB)
2568 								break;
2569 
2570 							iScanWidth += pScanRun->getWidth();
2571 						}
2572 
2573 						if ( iScanWidth > abs(iPos - iX))
2574 						{
2575 							iWidth = 0;
2576 						}
2577 						else
2578 						{
2579 							iX += iPos - iX - eWorkingDirection * iScanWidth;
2580 							iWidth = abs(iX - iXprev);
2581 						}
2582 					}
2583 
2584 					break;
2585 
2586 					case FL_TAB_CENTER:
2587 					{
2588 						iScanWidth = 0;
2589 						for ( UT_uint32 j = iIndx+1; j < iCountRuns; j++ )
2590 						{
2591 							UT_uint32 iJ;
2592 							iJ = eWorkingDirection == WORK_FORWARD ? j : iCountRuns - j - 1;
2593 
2594 							pScanRun = m_vecRuns.getNthItem(_getRunLogIndx(iJ));
2595 
2596 							if(!pScanRun || pScanRun->getType() == FPRUN_TAB)
2597 								break;
2598 							iScanWidth += pScanRun->getWidth();
2599 						}
2600 
2601 						if ( iScanWidth / 2 > abs(iPos - iX))
2602 							iWidth = 0;
2603 						else
2604 						{
2605 							iX += iPos - iX - static_cast<UT_sint32>(eWorkingDirection * iScanWidth / 2);
2606 							iWidth = abs(iX - iXprev);
2607 						}
2608 						break;
2609 					}
2610 
2611 					case FL_TAB_RIGHT:
2612 						if(iVisDirection == UT_BIDI_RTL && iDomDirection == UT_BIDI_RTL)
2613 						{
2614 							iX = iPos;
2615 							iWidth = abs(iX - iXprev);
2616 							xxx_UT_DEBUGMSG(("right tab (rtl para), iX %d, width %d\n", iX,pTabRun->getWidth()));
2617 						}
2618 						else
2619 						{
2620 							xxx_UT_DEBUGMSG(("right tab (ltr para), ii %d\n",ii));
2621 							iScanWidth = 0;
2622 							for ( UT_uint32 j = iIndx+1; j < iCountRuns; j++ )
2623 							{
2624 								UT_uint32 iJ;
2625 								iJ = eWorkingDirection == WORK_FORWARD ? j : iCountRuns - j - 1;
2626 								xxx_UT_DEBUGMSG(("iJ %d\n", iJ));
2627 
2628 								pScanRun = m_vecRuns.getNthItem(_getRunLogIndx(iJ));
2629 
2630 								if(!pScanRun || pScanRun->getType() == FPRUN_TAB)
2631 									break;
2632 								iScanWidth += pScanRun->getWidth();
2633 							}
2634 
2635 							if ( iScanWidth > abs(iPos - iX))
2636 							{
2637 								iWidth = 0;
2638 							}
2639 							else
2640 							{
2641 								iX += iPos - iX - static_cast<UT_sint32>(eWorkingDirection * iScanWidth);
2642 								iWidth = abs(iX - iXprev);
2643 							}
2644 
2645 						}
2646 						break;
2647 
2648 					case FL_TAB_DECIMAL:
2649 					{
2650 						UT_UCSChar *pDecimalStr =NULL;
2651 						UT_uint32	runLen = 0;
2652 
2653 #if 1
2654 // localeconv is ANSI C and C++, but in case we might run into trouble
2655 // this will make it easire to undo temporarily (we need to do this though)
2656 						// find what char represents a decimal point
2657 						lconv *loc = localeconv();
2658 						if ( ! UT_UCS4_cloneString_char(&pDecimalStr, loc->decimal_point) )
2659 						{
2660 							// Out of memory. Now what?
2661 						}
2662 #else
2663 						if ( ! UT_UCS_cloneString_char(&pDecimalStr, '.') )
2664 						{
2665 							// Out of memory. Now what?
2666 						}
2667 #endif
2668 						iScanWidth = 0;
2669 						for ( UT_uint32 j = iIndx+1; j < iCountRuns; j++ )
2670 						{
2671 							UT_uint32 iJ;
2672 							iJ = eWorkingDirection == WORK_FORWARD ? j : iCountRuns - j - 1;
2673 
2674 							pScanRun = m_vecRuns.getNthItem(_getRunLogIndx(iJ));
2675 
2676 							if(!pScanRun || pScanRun->getType() == FPRUN_TAB)
2677 								break;
2678 
2679 							bool foundDecimal = false;
2680 							if(pScanRun->getType() == FPRUN_TEXT)
2681 							{
2682 								UT_sint32 decimalBlockOffset = (static_cast<fp_TextRun *>(pScanRun))->findCharacter(0, pDecimalStr[0]);
2683 								if(decimalBlockOffset != -1)
2684 								{
2685 									foundDecimal = true;
2686 									UT_uint32 u_decimalBlockOffset = static_cast<UT_uint32>(decimalBlockOffset);
2687 									UT_ASSERT(pScanRun->getBlockOffset() <= u_decimalBlockOffset); // runLen is unsigned
2688 									runLen = u_decimalBlockOffset - pScanRun->getBlockOffset();
2689 								}
2690 							}
2691 
2692 							xxx_UT_DEBUGMSG(("%s(%d): foundDecimal=%d len=%d iScanWidth=%d \n",
2693 								__FILE__, __LINE__, foundDecimal, pScanRun->getLength()-runLen, iScanWidth));
2694 							if ( foundDecimal )
2695 							{
2696 								UT_ASSERT(pScanRun->getType() == FPRUN_TEXT);
2697 								iScanWidth += static_cast<fp_TextRun *>(pScanRun)->simpleRecalcWidth(runLen);
2698 								break; // we found our decimal, don't search any further
2699 							}
2700 							else
2701 							{
2702 								iScanWidth += pScanRun->getWidth();
2703 							}
2704 						}
2705 						if ( iScanWidth > abs(iPos - iX))
2706 						{
2707 
2708 							// out of space before the decimal point;
2709 							// tab collapses to nothing
2710 							iWidth = 0;
2711 						}
2712 						else {
2713 							iX = iPos - eWorkingDirection * iScanWidth;
2714 							iWidth = abs(iX - iXprev);
2715 						}
2716 						FREEP(pDecimalStr);
2717 						break;
2718 					}
2719 
2720 					case FL_TAB_BAR:
2721 					{
2722 						iX = iPos;
2723 						iWidth = abs(iX - iXprev);
2724 					}
2725 					break;
2726 
2727 					default:
2728 						UT_ASSERT(UT_NOT_IMPLEMENTED);
2729 				}; //switch
2730 
2731 				// if working backwards, set the new X coordinance
2732 				// and decide if line needs erasing
2733 			}
2734 			else //this is not a Tab run, or we are using fixed width tabs
2735 			{
2736 				iWidth = pRun->getAscent()*iFixedWidthMlt / iFixedWidthDiv;
2737 				iX += iWidth;
2738 
2739 				xxx_UT_DEBUGMSG(("run[%d] (type %d) width=%d\n", i,pRun->getType(),iWidth));
2740 			}
2741 
2742 			pTabRun->setTabWidth(iWidth);
2743 			return;
2744 		}
2745 
2746 		case FPRUN_TEXT:
2747 		{
2748 			pRun->recalcWidth();
2749 			//and fall through to default
2750 		}
2751 
2752 		default:
2753 		{
2754 			if(eWorkingDirection == WORK_FORWARD)
2755 			{
2756 				iX += pRun->getWidth();
2757 			}
2758 			else
2759 			{
2760 				iX -= pRun->getWidth();
2761 			}
2762 					xxx_UT_DEBUGMSG(("fp_Line::calculateWidthOfRun (non-tab [0x%x, type %d, dir %d]): width %d\n",
2763 							pRun,pRun->getType(),pRun->getDirection(),pRun->getWidth()));
2764 			return;
2765 		}
2766 
2767 	}//switch run type
2768 
2769 }
2770 
layout(void)2771 void fp_Line::layout(void)
2772 {
2773 	/*
2774 		This would be very simple if it was not for the Tabs :-); this is what
2775 		we will do:
2776 
2777 		(1) determine alignment for this line; if the alignment is right
2778 			we will have to work out the width of the runs in reverse
2779 			visual order, because we know only the physical position of the
2780 			end of the line;
2781 
2782 		(2) in bidi mode we determine the direction of paragraph, since it
2783 			dictates what the alignment of the last line of a justified
2784 			praragraph will be.
2785 
2786 		(3) if the direction of our paragraph corresponds to the alignment
2787 			(i.e., left to ltr) we will process the tab stops the normal way,
2788 			i.e, we will look for the next tabstop for each tab run. If the
2789 			alignment is contrary to the direction of paragraph we will lookup
2790 			the previous tabstop instead, since we cannot shav the text in
2791 			the normal direction.
2792 	*/
2793 
2794 	xxx_UT_DEBUGMSG(("fp_Line::layout (0x%x)\n",this));
2795 
2796 	// first of all, work out the height
2797 	recalcHeight();
2798 	calcLeftBorderThick();
2799 	calcRightBorderThick();
2800 	UT_sint32 iCountRuns		  = m_vecRuns.getItemCount();
2801 	// I think we cannot return before the call to recalcHeight above, since we
2802 	// could be called in response to all runs being removed, and that potentially
2803 	// changes the line height; anything from here down has to do with runs though
2804 	// so if we have none, we can return
2805 	if(iCountRuns <= 0)
2806 		return;
2807 
2808 	// get current alignment; note that we cannot initialize the alignment
2809 	// at this stage, (and chances are we will not need to anyway), because
2810 	// we have to first calculate the widths of our tabs
2811 	fb_Alignment* pAlignment = m_pBlock->getAlignment();
2812 	UT_return_if_fail(pAlignment);
2813 	FB_AlignmentType eAlignment 	  = pAlignment->getType();
2814 
2815 	//we have to remember the old X coordinances of our runs
2816 	//to be able to decide latter whether and where from to erase
2817 	//(this is a real nuisance, but since it takes two passes to do the layout
2818 	//I do not see a way to avoid this)
2819 	//we will use a static buffer for this initialised to a decent size and
2820 	//reallocated as needed
2821 #ifdef DEBUG
2822 	const UT_uint32 iDefinesLine = __LINE__;
2823 
2824 	UT_uint32 iRealocCount = 0;
2825 #endif
2826 	while(static_cast<UT_sint32>(s_iOldXsSize) < iCountRuns + 1)
2827 	{
2828 		// always make sure there is one space available past the last run
2829 		// we will set that to 0 and it will help us to handle the justified
2830 		// alignment spliting runs while we are working on them (see notes after
2831 		// the main loop)
2832 
2833 		// UT_DEBUGMSG(("fp_Line::layout: static buffer pOldXs too small\n"
2834 					 // "		(original size %d, new size %d)\n"
2835 					 // "		IF THIS MESSAGE APPEARS TOO OFTEN, INCREASE \"STATIC_BUFFER_INITIAL\"\n"
2836 					 // "		(line %d in %s)\n",
2837 					 // iOldXsSize, iOldXsSize+STATIC_BUFFER_INCREMENT, iDefinesLine + 2, __FILE__));
2838 
2839 		delete[] s_pOldXs;
2840 		s_iOldXsSize *= 2;
2841 		s_pOldXs = new UT_sint32[s_iOldXsSize];
2842 #ifdef DEBUG
2843 		iRealocCount++;
2844 		if(iRealocCount > 1)
2845 			UT_DEBUGMSG(("fp_Line::layout: static buffer required repeated reallocation\n"
2846 						 "		 IF THIS MESSAGE APPEARS INCREASE \"STATIC_BUFFER_INCREMENT\"\n"
2847 						 "		 (line %d in %s)\n", iDefinesLine+1, __FILE__));
2848 
2849 #endif
2850 	}
2851 
2852 	UT_ASSERT(s_pOldXs);
2853 
2854 	UT_sint32 iStartX = getLeftThick();
2855 
2856 	// find out the direction of the paragraph
2857 	UT_BidiCharType iDomDirection = m_pBlock->getDominantDirection();
2858 
2859 	// a variable to keep the processing direction
2860 	// NB: it is important that WORK_FORWARD is set to 1 and
2861 	// WORK_BACKWARD to -1; this gives us a simple way to convert
2862 	// addition to substraction by mulplying by the direction
2863 	FL_WORKING_DIRECTION eWorkingDirection = WORK_FORWARD;
2864 
2865 	// a variable that will tell us how to interpret the tabstops
2866 	FL_WHICH_TABSTOP eUseTabStop = USE_NEXT_TABSTOP;
2867 
2868 	// now from the current alignment work out which way we need to process the line
2869 	// and the corresponding starting positions
2870 
2871 	switch (eAlignment)
2872 	{
2873 		case FB_ALIGNMENT_LEFT:
2874 			if(iDomDirection == UT_BIDI_RTL)
2875 				eUseTabStop = USE_PREV_TABSTOP;
2876 			else
2877 				eUseTabStop = USE_NEXT_TABSTOP;
2878 
2879 			eWorkingDirection = WORK_FORWARD;
2880 			break;
2881 
2882 		case FB_ALIGNMENT_RIGHT:
2883 			if(iDomDirection == UT_BIDI_RTL)
2884 				eUseTabStop = USE_NEXT_TABSTOP;
2885 			else
2886 				eUseTabStop = USE_PREV_TABSTOP;
2887 
2888 			eWorkingDirection = WORK_BACKWARD;
2889 			iStartX = getAvailableWidth();
2890 			break;
2891 
2892 		case FB_ALIGNMENT_CENTER:
2893 			eWorkingDirection = WORK_FORWARD;
2894 			eUseTabStop = USE_FIXED_TABWIDTH;
2895 			// we will pretend the line starts at pos 0, work out the width
2896 			// and then shift it by the necessary amount to the right
2897 			iStartX = 0;
2898 			break;
2899 
2900 		case FB_ALIGNMENT_JUSTIFY:
2901 			if(iDomDirection == UT_BIDI_RTL)
2902 			{
2903 				eWorkingDirection = WORK_BACKWARD;
2904 				iStartX = getMaxWidth();
2905 			}
2906 			else
2907 			{
2908 				eWorkingDirection = WORK_FORWARD;
2909 			}
2910 			eUseTabStop = USE_NEXT_TABSTOP;
2911 			break;
2912 
2913 		default:
2914 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2915 	}
2916 
2917 	//now variables to keep track of our progress along the line
2918 	UT_sint32 iX			= iStartX;
2919 	//variables to keep information about how to erase the line once we are
2920 	//in position to do so
2921 	bool bLineErased		= false;
2922 	UT_sint32 iIndxToEraseFrom = -1;
2923 
2924 #if 0 //def DEBUG
2925 
2926 	//some extra but lengthy debug stuff
2927 	char *al;
2928 	char left[] = "left";
2929 	char right[]= "right";
2930 	char cent[] = "center";
2931 	char just[] = "justified";
2932 
2933 	switch (eAlignment)
2934 	{
2935 		case FB_ALIGNMENT_LEFT:
2936 			al = left;
2937 			break;
2938 
2939 		case FB_ALIGNMENT_RIGHT:
2940 			al = right;
2941 			break;
2942 
2943 		case FB_ALIGNMENT_CENTER:
2944 			al = cent;
2945 			break;
2946 
2947 		case FB_ALIGNMENT_JUSTIFY:
2948 			al = just;
2949 			break;
2950 
2951 		default:
2952 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2953 	}
2954 
2955 	char *d;
2956 	char fwd[] = "forward";
2957 	char bck[] = "backward";
2958 
2959 	if(eWorkingDirection == WORK_FORWARD)
2960 		d = fwd;
2961 	else
2962 		d = bck;
2963 
2964 	char *t;
2965 	char next[] = "next";
2966 	char prev[] = "prev";
2967 	char fxd[] = "fixed width";
2968 
2969 	switch (eUseTabStop)
2970 	{
2971 		case USE_NEXT_TABSTOP:
2972 			t = next;
2973 			break;
2974 		case USE_PREV_TABSTOP:
2975 			t = prev;
2976 			break;
2977 		case USE_FIXED_TABWIDTH:
2978 			t = fxd;
2979 			break;
2980 		default:
2981 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2982 	}
2983 
2984 	UT_DEBUGMSG(("fp_Line::layout(), this = 0x%x\n"
2985 				 "		 alignment [%s], working direction [%s], using tabstops [%s]\n"
2986 				 "		 iStartX	= %d, \n"
2987 				 "		 iCountRuns = %d\n",
2988 				 this, al, d, t, iStartX, iCountRuns
2989 	));
2990 
2991 #endif //end of the debug stuff
2992 
2993 
2994 	// now we work our way through the runs on this line
2995 	xxx_UT_DEBUGMSG(("fp_Line::layout ------------------- \n"));
2996 
2997 	UT_sint32 ii = 0;
2998 	for (; ii<iCountRuns; ++ii)
2999 	{
3000 		//work out the real index based on working direction
3001 		UT_uint32 iIndx;
3002 		iIndx = eWorkingDirection == WORK_FORWARD ? ii : iCountRuns - ii - 1;
3003 
3004 		// of course, the loop is running in visual order, but the vector is
3005 		// in logical order
3006 		fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(iIndx));
3007 
3008 
3009 		// if this tab is to be hidden, we must treat it as if its
3010 		// width was 0
3011 		if(pRun->isHidden())
3012 			continue;
3013 
3014 		// if we are working from the left, we want to set the
3015 		// X coordinance now; if from the right we will do it
3016 		// after we have got to the width worked out
3017 		// also, decide whether erasure is needed
3018 		if(eWorkingDirection == WORK_FORWARD)
3019 		{
3020 			s_pOldXs[iIndx] = pRun->getX();
3021 				pRun->Run_setX(iX,FP_CLEARSCREEN_NEVER);
3022 		}
3023 		xxx_UT_DEBUGMSG(("fp_Line::layout: iX %d, ii %d, iCountRuns %d\n"
3024 					 "		 run type %d\n",
3025 					iX, ii, iCountRuns, pRun->getType()));
3026 		_calculateWidthOfRun(iX,
3027 							 pRun,
3028 							 ii,
3029 							 iCountRuns,
3030 							 eWorkingDirection,
3031 							 eUseTabStop,
3032 							 iDomDirection
3033 							);
3034 
3035 		// if working backwards, set the new X coordinance
3036 		// and decide if line needs erasing
3037 		if(eWorkingDirection == WORK_BACKWARD)
3038 		{
3039 			s_pOldXs[iIndx] = pRun->getX();
3040 			pRun->Run_setX(iX,FP_CLEARSCREEN_NEVER);
3041 		}
3042 	} //for
3043 
3044 	// this is to simplify handling justified alignment -- see below
3045 	s_pOldXs[ii] = 0;
3046 
3047 
3048 	///////////////////////////////////////////////////////////////////
3049 	//	now we are ready to deal with the alignment
3050 	//
3051 	pAlignment->initialize(this);
3052 	iStartX = pAlignment->getStartPosition();
3053 
3054 	// now we have to get the iCountRuns value afresh, because if the alignment
3055 	// is justified then it is possible that the call to pAlignment->initialize()
3056 	// will split the previous set of runs into more ... (took me many frustrated
3057 	// hours to work this out) -- this happens on loading a document where each
3058 	// line is initially just a single run in the non-bidi build
3059 	//
3060 	// This also means that the pOldX array may be of no
3061 	// use, but then we will only need to worry about pOldXs just after the last
3062 	// run, since as long as the first new run kicks in, the rest will follow
3063 
3064 	iCountRuns		  = m_vecRuns.getItemCount();
3065 
3066 	xxx_UT_DEBUGMSG(("fp_Line::layout(): original run count %d, new count %d\n",
3067 				ii, iCountRuns));
3068 	switch(eAlignment)
3069 	{
3070 		case FB_ALIGNMENT_LEFT:
3071 		case FB_ALIGNMENT_RIGHT:
3072 			{
3073 				for (UT_sint32 k = 0; k < iCountRuns; k++)
3074 				{
3075 					fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(k));
3076 					UT_ASSERT(pRun);
3077 
3078 					// if this tab is to be hidden, we must treated as if its
3079 					// width was 0
3080 					if(pRun->isHidden())
3081 						continue;
3082 
3083 					//eClearScreen = iStartX == pOldXs[k] ?
3084 					//FP_CLEARSCREEN_NEVER : FP_CLEARSCREEN_FORCE;
3085 
3086 					// if we are in LTR context, the first visual run that
3087 					// shifts is the one to erase from; in RTL context
3088 					// it is the last visual run that shifts
3089 
3090 					if(!bLineErased && iStartX != s_pOldXs[k])
3091 					{
3092 						if(iDomDirection == UT_BIDI_LTR)
3093 							bLineErased = true;
3094 
3095 						iIndxToEraseFrom = k;
3096 					}
3097 
3098 					pRun->Run_setX(iStartX,FP_CLEARSCREEN_NEVER);
3099 					iStartX += pRun->getWidth();
3100 				}
3101 				xxx_UT_DEBUGMSG(("Final width of line %d maxwidth %d \n",iStartX,getMaxWidth()));
3102 			}
3103 		break;
3104 		case FB_ALIGNMENT_JUSTIFY:
3105 			{
3106 				// now we need to shift the x-coordinances to reflect the new widths
3107 				// of the spaces
3108 				UT_sint32 k;
3109 #if 0
3110 				// if we are working backwards, we have to
3111 				// ignore trailing spaces on the line ...
3112 				if(eWorkingDirection == WORK_BACKWARD)
3113 				{
3114 					// work from first visual run to the right ...
3115 					for (k = 0; k < iCountRuns; k++)
3116 					{
3117 						fp_Run* pRun = static_cast<fp_Run*>(m_vecRuns.getNthItem(_getRunLogIndx(k)));
3118 						UT_ASSERT(pRun);
3119 
3120 						if(!pRun->doesContainNonBlankData())
3121 						{
3122 							iStartX += pRun->getWidth();
3123 						}
3124 						else
3125 						{
3126 							iStartX += pRun->findTrailingSpaceDistance();
3127 							break;
3128 						}
3129 					}
3130 				}
3131 #endif
3132 				for (k = 0; k < iCountRuns; k++)
3133 				{
3134 					UT_uint32 iK = (eWorkingDirection == WORK_FORWARD) ? k : iCountRuns - k - 1;
3135 					fp_Run* pRun = static_cast<fp_Run*>(m_vecRuns.getNthItem(_getRunLogIndx(iK)));
3136 					UT_ASSERT(pRun);
3137 
3138 					// if this tab is to be hidden, we must treated as if its
3139 					// width was 0
3140 					if(pRun->isHidden())
3141 						continue;
3142 
3143 					if(eWorkingDirection == WORK_BACKWARD)
3144 					{
3145 						iStartX -= pRun->getWidth();
3146 						//eClearScreen = iStartX == pOldXs[iK] ? FP_CLEARSCREEN_NEVER : FP_CLEARSCREEN_FORCE;
3147 						if(!bLineErased && iStartX != s_pOldXs[iK])
3148 						{
3149 							if(iDomDirection == UT_BIDI_LTR)
3150 								bLineErased = true;
3151 
3152 							iIndxToEraseFrom = iK;
3153 						}
3154 
3155 						pRun->Run_setX(iStartX, FP_CLEARSCREEN_NEVER);
3156 					}
3157 					else
3158 					{
3159 						//eClearScreen = iStartX == pOldXs[iK] ? FP_CLEARSCREEN_NEVER : FP_CLEARSCREEN_FORCE;
3160 						if(!bLineErased && iStartX != s_pOldXs[iK])
3161 						{
3162 							if(iDomDirection == UT_BIDI_LTR)
3163 								bLineErased = true;
3164 
3165 							iIndxToEraseFrom = iK;
3166 						}
3167 						xxx_UT_DEBUGMSG(("Run %x has width %d \n",pRun,pRun->getWidth()));
3168 						pRun->Run_setX(iStartX, FP_CLEARSCREEN_NEVER);
3169 						iStartX += pRun->getWidth();
3170 					}
3171 				}
3172 				xxx_UT_DEBUGMSG(("Final width of line %d maxwidth %d \n",iStartX,getMaxWidth()));
3173 		}
3174 		break;
3175 		case FB_ALIGNMENT_CENTER:
3176 			{
3177 				//if the line is centered we will have to shift the iX of each run
3178 				//since we worked on the assumption that the line starts at 0
3179 				//only now are we in the position to enquire of the alignment what
3180 				//the real starting position should be
3181 
3182 				for (UT_sint32 k = 0; k < iCountRuns; k++)
3183 				{
3184 					fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(k));
3185 					UT_ASSERT(pRun);
3186 
3187 					// if this tab is to be hidden, we must treated as if its
3188 					// width was 0
3189 					if(pRun->isHidden())
3190 						continue;
3191 
3192 					UT_sint32 iCurX = pRun->getX();
3193 					//eClearScreen = iCurX + iStartX == pOldXs[k] ? FP_CLEARSCREEN_NEVER : FP_CLEARSCREEN_FORCE;
3194 					if(!bLineErased && iCurX + iStartX != s_pOldXs[k])
3195 					{
3196 						if(iDomDirection == UT_BIDI_LTR)
3197 							bLineErased = true;
3198 
3199 						iIndxToEraseFrom = k;
3200 					}
3201 
3202 					pRun->Run_setX(iCurX + iStartX, FP_CLEARSCREEN_NEVER);
3203 				}
3204 			}
3205 		break;
3206 		default:
3207 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3208 	} //switch eAlignment
3209 
3210 	// if the line needs erasure, then either bLineErased is set or
3211 	if(iIndxToEraseFrom >= 0)
3212 	{
3213 		xxx_UT_DEBUGMSG(("fp_Line::layout (0x%x): clearling line from indx %d\n",
3214 						 this, iIndxToEraseFrom));
3215 		clearScreenFromRunToEnd((UT_uint32)iIndxToEraseFrom);
3216 	}
3217 	else
3218 	{
3219 		xxx_UT_DEBUGMSG(("fp_Line::layout (0x%x): nothing to clear\n", this));
3220 	}
3221 
3222 }
3223 
containsFootnoteReference(void)3224 bool fp_Line::containsFootnoteReference(void)
3225 {
3226 	fp_Run * pRun = NULL;
3227 	UT_sint32 i =0;
3228 	bool bFound = false;
3229 	for(i=0; (i< countRuns()) && !bFound; i++)
3230 	{
3231 		pRun = getRunFromIndex(static_cast<UT_uint32>(i));
3232 		if(pRun->getType() == FPRUN_FIELD)
3233 		{
3234 			fp_FieldRun * pFRun = static_cast<fp_FieldRun *>(pRun);
3235 			if(pFRun->getFieldType() == FPFIELD_footnote_ref)
3236 			{
3237 				bFound = true;
3238 				break;
3239 			}
3240 		}
3241 	}
3242 	return bFound;
3243 }
3244 
getFootnoteContainers(UT_GenericVector<fp_FootnoteContainer * > * pvecFoots)3245 bool fp_Line::getFootnoteContainers(UT_GenericVector<fp_FootnoteContainer*> * pvecFoots)
3246 {
3247 	fp_Run * pRun = NULL;
3248 	UT_uint32 i =0;
3249 	bool bFound = false;
3250 	fp_FootnoteContainer * pFC = NULL;
3251 	PT_DocPosition posStart = getBlock()->getPosition();
3252 	PT_DocPosition posEnd = posStart + getLastRun()->getBlockOffset() + getLastRun()->getLength();
3253 	posStart += getFirstRun()->getBlockOffset();
3254 	for(i=0; (i< static_cast<UT_uint32>(countRuns())); i++)
3255 	{
3256 		pRun = getRunFromIndex(i);
3257 		if(pRun->getType() == FPRUN_FIELD)
3258 		{
3259 			fp_FieldRun * pFRun = static_cast<fp_FieldRun *>(pRun);
3260 			if(pFRun->getFieldType() == FPFIELD_footnote_ref)
3261 			{
3262 				fp_FieldFootnoteRefRun * pFNRun = static_cast<fp_FieldFootnoteRefRun *>(pFRun);
3263 				fl_FootnoteLayout * pFL = getBlock()->getDocLayout()->findFootnoteLayout(pFNRun->getPID());
3264 
3265 				UT_ASSERT(pFL);
3266 				xxx_UT_DEBUGMSG(("Pos of footnote %d start of run %d end of run %d \n",pFL->getDocPosition(),posStart,posEnd));
3267 				if(pFL && pFL->getDocPosition()>= posStart && pFL->getDocPosition() <= posEnd)
3268 				{
3269 					pFC = static_cast<fp_FootnoteContainer *>(pFL->getFirstContainer());
3270 					bFound = true;
3271 					pvecFoots->addItem(pFC);
3272 				}
3273 			}
3274 		}
3275 	}
3276 	return bFound;
3277 }
3278 
3279 
3280 
containsAnnotations(void)3281 bool fp_Line::containsAnnotations(void)
3282 {
3283 	fp_Run * pRun = NULL;
3284 	UT_sint32 i =0;
3285 	bool bFound = false;
3286 	for(i=0; (i< countRuns()) && !bFound; i++)
3287 	{
3288 		pRun = getRunFromIndex(static_cast<UT_uint32>(i));
3289 		if(pRun->getType() == FPRUN_HYPERLINK)
3290 		{
3291 			fp_HyperlinkRun * pHRun = static_cast<fp_HyperlinkRun *>(pRun);
3292 			if(pHRun->getHyperlinkType() == HYPERLINK_ANNOTATION )
3293 			{
3294 			        fp_AnnotationRun * pARun = static_cast<fp_AnnotationRun *>(pHRun);
3295 				UT_uint32 iPID = pARun->getPID();
3296 				if(iPID > 0)
3297 				{
3298 				     bFound = true;
3299 				     break;
3300 				}
3301 			}
3302 		}
3303 	}
3304 	return bFound;
3305 }
3306 
getAnnotationContainers(UT_GenericVector<fp_AnnotationContainer * > * pvecAnns)3307 bool fp_Line::getAnnotationContainers(UT_GenericVector<fp_AnnotationContainer*> * pvecAnns)
3308 {
3309 	fp_Run * pRun = NULL;
3310 	UT_uint32 i =0;
3311 	bool bFound = false;
3312 	fp_AnnotationContainer * pAC = NULL;
3313 	PT_DocPosition posStart = getBlock()->getPosition();
3314 	PT_DocPosition posEnd = posStart + getLastRun()->getBlockOffset() + getLastRun()->getLength();
3315 	posStart += getFirstRun()->getBlockOffset();
3316 	for(i=0; (i< static_cast<UT_uint32>(countRuns())); i++)
3317 	{
3318 		pRun = getRunFromIndex(i);
3319 		if(pRun->getType() == FPRUN_HYPERLINK)
3320 		{
3321 			fp_HyperlinkRun * pHRun = static_cast<fp_HyperlinkRun *>(pRun);
3322 			if(pHRun->getHyperlinkType() == HYPERLINK_ANNOTATION )
3323 			{
3324 			        fp_AnnotationRun * pARun = static_cast<fp_AnnotationRun *>(pHRun);
3325 				UT_uint32 iPID = pARun->getPID();
3326 				if(iPID > 0)
3327 				{
3328 				      fl_AnnotationLayout * pAL = getBlock()->getDocLayout()->findAnnotationLayout(pARun->getPID());
3329 
3330 				      UT_ASSERT(pAL);
3331 				      xxx_UT_DEBUGMSG(("Pos of Annotation %d start of run %d end of run %d \n",pAL->getDocPosition(),posStart,posEnd));
3332 				      if(pAL && pAL->getDocPosition()>= posStart && pAL->getDocPosition() <= posEnd)
3333 				      {
3334 					   pAC = static_cast<fp_AnnotationContainer *>(pAL->getFirstContainer());
3335 					   bFound = true;
3336 					   pvecAnns->addItem(pAC);
3337 				      }
3338 				}
3339 			}
3340 		}
3341 	}
3342 	return bFound;
3343 }
3344 
setX(UT_sint32 iX,bool bDontClearIfNeeded)3345 void fp_Line::setX(UT_sint32 iX, bool bDontClearIfNeeded)
3346 {
3347 	if (m_iX == iX)
3348 	{
3349 		return;
3350 	}
3351 	if(!bDontClearIfNeeded)
3352 	{
3353 		clearScreen();
3354 	}
3355 	xxx_UT_DEBUGMSG(("SetX Line %x X %d \n",this,iX));
3356 	if(iX < m_iX)
3357 	  {
3358 	    UT_DEBUGMSG(("m_iX decreaed in value old %d new %d \n",m_iX,iX));
3359 	  }
3360 	m_iX = iX;
3361 }
3362 
setY(UT_sint32 iY)3363 void fp_Line::setY(UT_sint32 iY)
3364 {
3365 	if (m_iY == iY)
3366 	{
3367 		return;
3368 	}
3369 	if((m_iY !=INITIAL_OFFSET) && (m_iY != 0)  && isWrapped())
3370 	{
3371 	    setReformat();
3372 	}
3373 	clearScreen();
3374 	m_iY = iY;
3375 }
3376 
getMarginBefore(void) const3377 UT_sint32 fp_Line::getMarginBefore(void) const
3378 {
3379 	if (isFirstLineInBlock() && getBlock()->getPrev())
3380 	{
3381 		fl_ContainerLayout * pPrevC = getBlock()->getPrev();
3382 		UT_sint32 iBottomMargin = 0;
3383 		bool bLoop = true;
3384 		while(bLoop)
3385 		{
3386 			if(pPrevC->getContainerType() == FL_CONTAINER_BLOCK)
3387 			{
3388 				bLoop = false;
3389 				iBottomMargin = static_cast<fl_BlockLayout *>(pPrevC)->getBottomMargin();
3390 			}
3391 			else if(pPrevC->getContainerType() == FL_CONTAINER_TABLE)
3392 			{
3393 				bLoop = false;
3394 				iBottomMargin = static_cast<fl_TableLayout *>(pPrevC)->getBottomOffset();
3395 			}
3396 			else
3397 			{
3398 				if(pPrevC->getPrev())
3399 				{
3400 					pPrevC = pPrevC->getPrev();
3401 				}
3402 				else
3403 				{
3404 				    bLoop = false;
3405 				    return 0;
3406 				}
3407 			}
3408 		}
3409 		UT_sint32 iNextTopMargin = getBlock()->getTopMargin();
3410 
3411 		UT_sint32 iMargin = UT_MAX(iBottomMargin, iNextTopMargin);
3412 
3413 		return iMargin;
3414 	}
3415 
3416 	return 0;
3417 }
3418 
getMarginAfter(void) const3419 UT_sint32 fp_Line::getMarginAfter(void) const
3420 {
3421 	if (isLastLineInBlock() && getBlock()->getNext())
3422 	{
3423 		fl_ContainerLayout * pNext = getBlock()->getNext();
3424 		if (!pNext)
3425 			return 0;
3426 
3427 		UT_sint32 iBottomMargin = getBlock()->getBottomMargin();
3428 
3429 		UT_sint32 iNextTopMargin = 0;
3430 		bool bLoop = true;
3431 		while(bLoop)
3432 		{
3433 			if(pNext->getContainerType() == FL_CONTAINER_BLOCK)
3434 			{
3435 				iNextTopMargin = static_cast<fl_BlockLayout *>(pNext)->getTopMargin();
3436 				bLoop = false;
3437 			}
3438 			else if (pNext->getContainerType() == FL_CONTAINER_TABLE)
3439 			{
3440 				iNextTopMargin = 0;
3441 				bLoop = false;
3442 			}
3443 			else
3444 			{
3445 				if(pNext->getNext())
3446 				{
3447 					pNext = pNext->getNext();
3448 				}
3449 				else
3450 				{
3451 					bLoop = false;
3452 				}
3453 			}
3454 		}
3455 		UT_sint32 iMargin = UT_MAX(iBottomMargin, iNextTopMargin);
3456 		return iMargin + m_iAdditionalMarginAfter;
3457 	}
3458 
3459 	return m_iAdditionalMarginAfter;
3460 }
3461 
recalculateFields(UT_uint32 iUpdateCount)3462 bool fp_Line::recalculateFields(UT_uint32 iUpdateCount)
3463 {
3464 	bool bResult = false;
3465 
3466 	UT_sint32 iNumRuns = m_vecRuns.getItemCount();
3467 	for (UT_sint32 i = 0; i < iNumRuns; i++)
3468 	{
3469 		fp_Run* pRun = m_vecRuns.getNthItem(i);
3470 
3471 		if (pRun->getType() == FPRUN_FIELD)
3472 		{
3473 			fp_FieldRun* pFieldRun = static_cast<fp_FieldRun*>(pRun);
3474 			if(iUpdateCount && (iUpdateCount % pFieldRun->needsFrequentUpdates()))
3475 				continue;
3476 			bool bSizeChanged = pFieldRun->calculateValue();
3477 
3478 			bResult = bResult || bSizeChanged;
3479 		}
3480 	}
3481 
3482 	return bResult;
3483 }
3484 
getLastRun(void) const3485 fp_Run* fp_Line::getLastRun(void) const
3486 {
3487 	const UT_sint32 i = m_vecRuns.getItemCount();
3488 	if(i <= 0)
3489 	{
3490 		fp_Run* pRun = getBlock()->getFirstRun();
3491 		return pRun;
3492 	}
3493 	else
3494 	{
3495 		return (m_vecRuns.getLastItem());
3496 	}
3497 }
3498 
getLastTextRun(void) const3499 fp_Run* fp_Line::getLastTextRun(void) const
3500 {
3501 	const UT_sint32 i = m_vecRuns.getItemCount();
3502 	fp_Run * pRun = NULL;
3503 	if(i <= 0)
3504 	{
3505 		pRun = getBlock()->getFirstRun();
3506 		return pRun;
3507 	}
3508 	else
3509 	{
3510 		pRun = m_vecRuns.getLastItem();
3511 		while(pRun != NULL && pRun->getType() != FPRUN_TEXT)
3512 		{
3513 			pRun = pRun->getPrevRun();
3514 		}
3515 		if(pRun == NULL)
3516 		{
3517 			pRun = getBlock()->getFirstRun();
3518 		}
3519 		return pRun;
3520 	}
3521 }
3522 
findNextTabStop(UT_sint32 iStartX,UT_sint32 & iPosition,eTabType & iType,eTabLeader & iLeader)3523 bool	fp_Line::findNextTabStop(UT_sint32 iStartX, UT_sint32& iPosition, eTabType & iType, eTabLeader & iLeader )
3524 {
3525 	UT_sint32	iTabStopPosition = 0;
3526 	eTabType	iTabStopType = FL_TAB_NONE;
3527 	eTabLeader	iTabStopLeader = FL_LEADER_NONE;
3528 
3529 	UT_DebugOnly<bool> bRes = m_pBlock->findNextTabStop(iStartX + getX(),
3530 							    getX() + getMaxWidth(),
3531 							    iTabStopPosition, iTabStopType,
3532 							    iTabStopLeader);
3533 	UT_ASSERT(bRes);
3534 
3535 	iTabStopPosition -= getX();
3536 
3537 	//has to be <=
3538 	if (iTabStopPosition <= getMaxWidth())
3539 	{
3540 		iPosition = iTabStopPosition;
3541 		iType = iTabStopType;
3542 		iLeader = iTabStopLeader;
3543 
3544 		return true;
3545 	}
3546 	else
3547 	{
3548 		UT_DEBUGMSG(("fp_Line::findNextTabStop: iStartX %d, getMaxWidth %d\n"
3549 					 "			iPosition %d, iTabStopPosition %d, iType %d, iLeader %d\n",
3550 			     iStartX, getMaxWidth(),iPosition, iTabStopPosition,static_cast<UT_sint32>(iType), static_cast<UT_sint32>(iLeader)));
3551 		return false;
3552 	}
3553 }
3554 
findPrevTabStop(UT_sint32 iStartX,UT_sint32 & iPosition,eTabType & iType,eTabLeader & iLeader)3555 bool	fp_Line::findPrevTabStop(UT_sint32 iStartX, UT_sint32& iPosition, eTabType & iType, eTabLeader & iLeader )
3556 {
3557 	UT_sint32	iTabStopPosition = 0;
3558 	eTabType	iTabStopType = FL_TAB_NONE;
3559 	eTabLeader	iTabStopLeader = FL_LEADER_NONE;
3560 
3561 	UT_DebugOnly<bool> bRes = m_pBlock->findPrevTabStop(iStartX + getX(),
3562 							    getX() + getMaxWidth(),
3563 							    iTabStopPosition,
3564 							    iTabStopType, iTabStopLeader);
3565 	UT_ASSERT(bRes);
3566 
3567 	iTabStopPosition -= getX();
3568 
3569 	if (iTabStopPosition <= getMaxWidth())
3570 	{
3571 		iPosition = iTabStopPosition;
3572 		iType = iTabStopType;
3573 		iLeader = iTabStopLeader;
3574 
3575 		return true;
3576 	}
3577 	else
3578 	{
3579 		UT_DEBUGMSG(("fp_Line::findPrevTabStop: iStartX %d, m_iMaxWidth %d\n"
3580 					 "			iPosition %d, iTabStopPosition %d, iType %d, iLeader %d\n",
3581 			     iStartX, getMaxWidth(),iPosition, iTabStopPosition, static_cast<UT_sint32>(iType), static_cast<UT_sint32>(iLeader)));
3582 		return false;
3583 	}
3584 }
3585 
recalcMaxWidth(bool bDontClearIfNeeded)3586 void fp_Line::recalcMaxWidth(bool bDontClearIfNeeded)
3587 {
3588 	if(m_pBlock == NULL)
3589 	{
3590 		return;
3591 	}
3592         calcLeftBorderThick();
3593 	UT_sint32 iX = m_pBlock->getLeftMargin();
3594 	UT_sint32 iMaxWidth = getContainer()->getWidth();
3595 
3596 	UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
3597 
3598 	if (isFirstLineInBlock())
3599 	{
3600 		if(iBlockDir == UT_BIDI_LTR)
3601 			iX += m_pBlock->getTextIndent();
3602 	}
3603 	setSameYAsPrevious(false);
3604 	setWrapped(false);
3605 	setX(iX,bDontClearIfNeeded);
3606 
3607 	UT_ASSERT(iMaxWidth > 0);
3608 
3609 	fl_DocSectionLayout * pSL =  getBlock()->getDocSectionLayout();
3610 	UT_ASSERT(pSL);
3611 	if(pSL->getNumColumns() > 1)
3612 	{
3613 		if(getContainer()->getContainerType() == FP_CONTAINER_COLUMN ||
3614 			getContainer()->getContainerType() == FP_CONTAINER_COLUMN_SHADOW ||
3615 			getContainer()->getContainerType() == FP_CONTAINER_HDRFTR ||
3616 			getContainer()->getContainerType() == FP_CONTAINER_TOC ||
3617 			getContainer()->getContainerType() == FP_CONTAINER_FOOTNOTE||
3618 			getContainer()->getContainerType() == FP_CONTAINER_ANNOTATION||
3619 			getContainer()->getContainerType() == FP_CONTAINER_ENDNOTE)
3620 		{
3621 			m_iClearToPos = iMaxWidth + pSL->getColumnGap();
3622 			m_iClearLeftOffset = pSL->getColumnGap() - getGraphics()->tlu(1);
3623 		}
3624 		else if(getContainer()->getContainerType() == FP_CONTAINER_CELL)
3625 		{
3626 			fp_CellContainer * pCell = static_cast<fp_CellContainer *>(getContainer());
3627 			m_iClearToPos = static_cast<UT_sint32>(iMaxWidth + pCell->getRightPad());
3628 //			m_iClearLeftOffset = pCell->getCellX(this) - pCell->getLeftPos() - getGraphics()->tlu(1);
3629 			m_iClearLeftOffset =  0;
3630 		}
3631 		else if(getContainer()->getContainerType() == FP_CONTAINER_FRAME)
3632 		{
3633 			m_iClearToPos = iMaxWidth;
3634 			m_iClearLeftOffset = 0;
3635 		}
3636 		else
3637 		{
3638 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3639 			m_iClearToPos = iMaxWidth;
3640 			m_iClearLeftOffset = pSL->getLeftMargin() - getGraphics()->tlu(1);
3641 		}
3642 	}
3643 	else
3644 	{
3645 		if(getContainer()->getContainerType() == FP_CONTAINER_COLUMN ||
3646 			getContainer()->getContainerType() == FP_CONTAINER_COLUMN_SHADOW ||
3647 			getContainer()->getContainerType() == FP_CONTAINER_HDRFTR ||
3648 			getContainer()->getContainerType() == FP_CONTAINER_TOC||
3649 			getContainer()->getContainerType() == FP_CONTAINER_FOOTNOTE||
3650 			getContainer()->getContainerType() == FP_CONTAINER_ANNOTATION||
3651 			getContainer()->getContainerType() == FP_CONTAINER_ENDNOTE)
3652 		{
3653 			m_iClearToPos = iMaxWidth + pSL->getRightMargin() - getGraphics()->tlu(2);
3654 			m_iClearLeftOffset = pSL->getLeftMargin() - getGraphics()->tlu(1);
3655 		}
3656 		else if(getContainer()->getContainerType() == FP_CONTAINER_FRAME)
3657 		{
3658 			m_iClearToPos = iMaxWidth;
3659 			m_iClearLeftOffset = 0;
3660 		}
3661 		else if(getContainer()->getContainerType() == FP_CONTAINER_CELL)
3662 		{
3663 			fp_CellContainer * pCell = static_cast<fp_CellContainer *>(getContainer());
3664 			m_iClearToPos = static_cast<UT_sint32>(iMaxWidth + pCell->getRightPad());
3665 //			m_iClearLeftOffset =  pCell->getCellX(this) - pCell->getLeftPos() - getGraphics()->tlu(1);
3666 			m_iClearLeftOffset =  0;
3667 		}
3668 		else
3669 		{
3670 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3671 			m_iClearToPos = iMaxWidth;
3672 			m_iClearLeftOffset = pSL->getLeftMargin() - getGraphics()->tlu(1);
3673 		}
3674 	}
3675 
3676 	if (m_iClearLeftOffset < 0)
3677 	{
3678 		m_iClearLeftOffset = 0;
3679 	}
3680 	if(hasBordersOrShading())
3681 	{
3682 		m_iClearToPos = getRightEdge();
3683 		m_iClearLeftOffset = 0;
3684 	}
3685 	if (getPage() && (getPage()->getWidth() - m_iMaxWidth < m_iClearLeftOffset))
3686 	{
3687 		m_iClearLeftOffset = getPage()->getWidth() - m_iMaxWidth;
3688 		UT_ASSERT(m_iClearLeftOffset >= 0);
3689 	}
3690 
3691 
3692 	iMaxWidth -= m_pBlock->getRightMargin();
3693 	iMaxWidth -= m_pBlock->getLeftMargin();
3694 	m_iClearToPos -= m_pBlock->getLeftMargin();
3695 	if (isFirstLineInBlock())
3696 	{
3697 		iMaxWidth -= m_pBlock->getTextIndent();
3698 	}
3699 
3700 	// Check that there's actually room for content -- assert is not good enough,
3701 	// we need to handle this; there are docs around that exhibit this (e.g. 6722)
3702 	// UT_ASSERT(iMaxWidth > 0);
3703 	if(iMaxWidth <= 0)
3704 	{
3705 		// we will ignore the block indents ...
3706 		// (we should probably change the block indents)
3707 		iX = 0;
3708 		iMaxWidth = getContainer()->getWidth();
3709 	}
3710 
3711 	if(getPage())
3712 	{
3713 		UT_ASSERT(iMaxWidth <= getPage()->getWidth());
3714 	}
3715 	if(iMaxWidth < 60)
3716 	{
3717 		iMaxWidth = 60;
3718 	}
3719 	setMaxWidth(iMaxWidth);
3720 }
3721 
getNextContainerInSection(void) const3722 fp_Container*	fp_Line::getNextContainerInSection(void) const
3723 {
3724 	if (getNext())
3725 	{
3726 		return static_cast<fp_Container *>(getNext());
3727 	}
3728 
3729 	fl_ContainerLayout* pNextBlock = m_pBlock->getNext();
3730 	while(pNextBlock &&
3731 		  ((pNextBlock->getContainerType() == FL_CONTAINER_ENDNOTE) ||
3732 		   (pNextBlock->getContainerType() == FL_CONTAINER_FRAME) ||
3733 		  (pNextBlock->isHidden() == FP_HIDDEN_FOLDED)))
3734 	{
3735 		pNextBlock = pNextBlock->getNext();
3736 	}
3737 	if (pNextBlock)
3738 	{
3739 		return static_cast<fp_Container *>(pNextBlock->getFirstContainer());
3740 	}
3741 	return NULL;
3742 }
3743 
getPrevContainerInSection(void) const3744 fp_Container*	fp_Line::getPrevContainerInSection(void) const
3745 {
3746 	if (getPrev())
3747 	{
3748 		return static_cast<fp_Container *>(getPrev());
3749 	}
3750 
3751 	fl_ContainerLayout* pPrev =  static_cast<fl_ContainerLayout *>(m_pBlock->getPrev());
3752 	while(pPrev &&
3753 		  ((pPrev->getContainerType() == FL_CONTAINER_ENDNOTE) ||
3754 		   (pPrev->getContainerType() == FL_CONTAINER_FRAME) ||
3755 		   (pPrev->isHidden() == FP_HIDDEN_FOLDED)))
3756 	{
3757 		pPrev = pPrev->getPrev();
3758 	}
3759 	if(pPrev)
3760 	{
3761 		fp_Container * pPrevCon = static_cast<fp_Container *>(pPrev->getLastContainer());
3762 //
3763 // Have to handle broken tables in the previous layout..
3764 //
3765 		if(pPrevCon && pPrevCon->getContainerType() == FP_CONTAINER_TABLE)
3766 		{
3767 			fp_TableContainer * pTab = static_cast<fp_TableContainer *>(pPrevCon);
3768 			fp_TableContainer * pLLast = pTab;
3769 			fp_TableContainer * pNext = static_cast<fp_TableContainer *>(pTab->getNext());
3770 			while(pNext)
3771 			{
3772 				pLLast = pNext;
3773 				pNext = static_cast<fp_TableContainer *>(pNext->getNext());
3774 			}
3775 			pPrevCon = static_cast<fp_Container *>(pLLast);
3776 		}
3777 		return pPrevCon;
3778 	}
3779 
3780 
3781 	return NULL;
3782 }
3783 
containsForcedColumnBreak(void) const3784 bool	fp_Line::containsForcedColumnBreak(void) const
3785 {
3786 	if(!isEmpty())
3787 	{
3788 		fp_Run* pRun = getLastRun();
3789 		if (pRun->getType() == FPRUN_FORCEDCOLUMNBREAK)
3790 		{
3791 			return true;
3792 		}
3793 		if(pRun->getPrevRun() && (pRun->getPrevRun()->getType() == FPRUN_FORCEDCOLUMNBREAK))
3794 		{
3795 			return true;
3796 		}
3797 	}
3798 
3799 	return false;
3800 }
3801 
containsForcedPageBreak(void) const3802 bool fp_Line::containsForcedPageBreak(void) const
3803 {
3804 	if (!isEmpty())
3805 	{
3806 		fp_Run* pRun = getLastRun();
3807 		if (pRun->getType() == FPRUN_FORCEDPAGEBREAK)
3808 		{
3809 			return true;
3810 		}
3811 		if(pRun->getPrevRun() && (pRun->getPrevRun()->getType() == FPRUN_FORCEDPAGEBREAK))
3812 		{
3813 			return true;
3814 		}
3815 	}
3816 	return false;
3817 }
3818 
3819 /*!
3820     Call to this function must be followed by calling fp_Line::layout() since coalescing
3821     runs might require shaping of the resulting run, which in turn might result in lost of
3822     justification information (this is due to the fact that the data used by the external
3823     shaping engine can be opaque to us, so we are not able to combine it for the two
3824     original runs -- we have to ask the external engine to recalculate it for the whole
3825     combined run).
3826 
3827     At the moment we simply call coalesceRuns() just before doing layout() in fb_LineBreaker().
3828 */
coalesceRuns(void)3829 void fp_Line::coalesceRuns(void)
3830 {
3831 	xxx_UT_DEBUGMSG(("coalesceRuns (line 0x%x)\n", this));
3832 	UT_sint32 count = m_vecRuns.getItemCount();
3833 	for (UT_sint32 i=0; i < static_cast<UT_sint32>(count-1); i++)
3834 	{
3835 		fp_Run* pRun = m_vecRuns.getNthItem(static_cast<UT_uint32>(i));
3836 		if (pRun->getType() == FPRUN_TEXT)
3837 		{
3838 			fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
3839 			xxx_UT_DEBUGMSG(("Looking at %d Text run \n",i));
3840 			//	pTR->printText();
3841 			if (pTR->canMergeWithNext())
3842 			{
3843 			        xxx_UT_DEBUGMSG(("Can merge \n"));
3844 				//pTR->printText();
3845 			        fp_Run * pNext = pRun->getNextRun();
3846 				//
3847 				// Look if we have a redundant fmtMark.
3848 				// If so remove it
3849 				//
3850 				if(pNext->getType() == FPRUN_FMTMARK)
3851 				{
3852 				    pRun->setNextRun(pNext->getNextRun(), false);
3853 				    pNext->getNextRun()->setPrevRun(pRun, false);
3854 				    removeRun(pNext, false);
3855 				    delete pNext;
3856 				    count--;
3857 				    continue;
3858 				}
3859 				pTR->mergeWithNext();
3860 				count--;
3861 				i--; //test the newly merged run with the next
3862 			}
3863 		}
3864 
3865 	}
3866 }
3867 
calculateWidthOfLine(void)3868 UT_sint32 fp_Line::calculateWidthOfLine(void)
3869 {
3870 	const UT_sint32 iCountRuns = m_vecRuns.getItemCount();
3871 	UT_sint32 iX = 0;
3872 
3873 	// first calc the width of the line
3874 	for (UT_sint32 i = 0; i < iCountRuns; ++i)
3875 	{
3876 		const fp_Run* pRun = m_vecRuns.getNthItem(i);
3877 
3878 		if(pRun->isHidden())
3879 			continue;
3880 
3881 		iX += pRun->getWidth();
3882 	}
3883 	// this is a wrong assert, since line can include trailing spaces
3884 	// that are out of the margins.
3885 	//UT_ASSERT(iX <= m_iMaxWidth);
3886 
3887 	m_iWidth = iX;
3888 //	UT_ASSERT(m_iWidth > 0);
3889 	return iX;
3890 }
3891 
calculateWidthOfTrailingSpaces(void)3892 UT_sint32 fp_Line::calculateWidthOfTrailingSpaces(void)
3893 {
3894 	// need to move back until we find the first non blank character and
3895 	// return the distance back to this character.
3896 
3897 	UT_ASSERT(!isEmpty());
3898 
3899 	UT_sint32 iTrailingBlank = 0;
3900 
3901 
3902 	UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
3903 	UT_sint32 i;
3904 	UT_sint32 iCountRuns = m_vecRuns.getItemCount();
3905 
3906 	for (i=iCountRuns -1 ; i >= 0; i--)
3907 	{
3908 		// work from the run on the visual end of the line
3909 		UT_sint32 k = iBlockDir == UT_BIDI_LTR ? i : iCountRuns - i - 1;
3910 		fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(k));
3911 
3912 		if(pRun->isHidden())
3913 			continue;
3914 
3915 		if(!pRun->doesContainNonBlankData())
3916 		{
3917 			iTrailingBlank += pRun->getWidth();
3918 		}
3919 		else
3920 		{
3921 			iTrailingBlank += pRun->findTrailingSpaceDistance();
3922 			break;
3923 		}
3924 	}
3925 
3926 	return iTrailingBlank;
3927 }
3928 
countJustificationPoints(void)3929 UT_uint32 fp_Line::countJustificationPoints(void)
3930 {
3931 	UT_sint32 iCountRuns = m_vecRuns.getItemCount();
3932 	UT_sint32 i;
3933 	UT_uint32 iSpaceCount = 0;
3934 	bool bStartFound = false;
3935 
3936 	UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
3937 
3938 	// first calc the width of the line
3939 	for (i=iCountRuns -1 ; i >= 0; i--)
3940 	{
3941 		// work from the run on the visual end of the line
3942 		UT_sint32 k = iBlockDir == UT_BIDI_LTR ? i : iCountRuns - i - 1;
3943 		fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(k));
3944 
3945 		if (pRun->getType() == FPRUN_TAB)
3946 		{
3947 			// when we hit a tab, we stop this, since tabs "swallow" justification of the
3948 			// runs that preceed them (i.e., endpoint of the tab is given and cannot be
3949 			// moved)
3950 			break;
3951 		}
3952 		else if (pRun->getType() == FPRUN_TEXT)
3953 		{
3954 			fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
3955 			UT_sint32 iPointCount = pTR->countJustificationPoints(!bStartFound);
3956 			if(bStartFound)
3957 			{
3958 				iSpaceCount += abs(iPointCount);
3959 			}
3960 			else
3961 			{
3962 				if(iPointCount >= 0)
3963 				{
3964 					// we found our first non-blank run; the point
3965 					iSpaceCount += iPointCount;
3966 					bStartFound = true;
3967 				}
3968 
3969 			}
3970 		}
3971 		else if (pRun->getType () == FPRUN_FORCEDLINEBREAK || pRun->getType () == FPRUN_FORCEDPAGEBREAK ||
3972 				 pRun->getType () == FPRUN_FORCEDCOLUMNBREAK)
3973 		{
3974 			// why do we count these types of run as justifiable spaces? Tomas, Apr 8, 2004
3975 			iSpaceCount++;
3976 		}
3977 		else if (   pRun->getType () == FPRUN_DIRECTIONMARKER || pRun->getType () == FPRUN_FMTMARK
3978 				 || pRun->getType () == FPRUN_BOOKMARK || pRun->getType () == FPRUN_HYPERLINK)
3979 		{
3980 			// these runs do not expand under justification, but neither do they contain non-blank data
3981 			continue;
3982 		}
3983 		else
3984 		{
3985 			bStartFound = true;
3986 		}
3987 	}
3988 
3989 	return iSpaceCount;
3990 }
3991 
3992 
isLastCharacter(UT_UCSChar Character) const3993 bool fp_Line::isLastCharacter(UT_UCSChar Character) const
3994 {
3995 	UT_ASSERT(!isEmpty());
3996 
3997 	fp_Run *pRun = getLastRun();
3998 
3999 	if (pRun->getType() == FPRUN_TEXT)
4000 	{
4001 		fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
4002 
4003 		return pTR->isLastCharacter(Character);
4004 	}
4005 
4006 	return false;
4007 }
4008 
resetJustification(bool bPermanent)4009 void fp_Line::resetJustification(bool bPermanent)
4010 {
4011 	UT_sint32 count = m_vecRuns.getItemCount();
4012 	for (UT_sint32 i=0; i<count; i++)
4013 	{
4014 		fp_Run* pRun = m_vecRuns.getNthItem(i);
4015 
4016 		if (pRun->getType() == FPRUN_TEXT)
4017 		{
4018 			fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
4019 
4020 			pTR->resetJustification(bPermanent);
4021 		}
4022 	}
4023 }
4024 
4025 
justify(UT_sint32 iAmount)4026 void fp_Line::justify(UT_sint32 iAmount)
4027 {
4028 	if(iAmount > 0)
4029 	{
4030 		// because the justification means that the spaces are wider than the OS
4031 		// will draw them, we cannot have runs merged across the spaces
4032 
4033 		// also, we have to do the spliting  _before_ we can count justification points,
4034 		// otherwise we get problems if the last non blank run ends in spaces and
4035 		// is followed by some space-only runs; in that case the trailing spaces in
4036 		// the non-blank run get counted in when they should not -- this should not cost us
4037 		// too much, since it is unlikely that there is going to be a justified line with
4038 		// no spaces on it
4039 
4040 #if 0
4041 		// to avoid spliting the runs at spaces, saving memory and
4042 		// processing time, we now improved fp_TextRun::_draw(), so
4043 		// that it is able to skip over spaces
4044 
4045 		_splitRunsAtSpaces();
4046 #endif
4047 
4048 		UT_uint32 iSpaceCount = countJustificationPoints();
4049 		xxx_UT_DEBUGMSG(("fp_Line::distributeJustificationAmongstSpaces: iSpaceCount %d\n", iSpaceCount));
4050 
4051 		if(iSpaceCount)
4052 		{
4053 			bool bFoundStart = false;
4054 
4055 			UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
4056 			UT_sint32 count = m_vecRuns.getItemCount();
4057 			UT_ASSERT(count);
4058 
4059 			xxx_UT_DEBUGMSG(("DOM: must split iAmount %d between iSpaceCount %d spaces for count %d runs\n", iAmount, iSpaceCount, count));
4060 
4061 			for (UT_sint32 i=count-1; i >= 0 && iSpaceCount > 0; i--)
4062 			{
4063 				// work from the run on the visual end of the line
4064 				UT_sint32 k = iBlockDir == UT_BIDI_LTR ? i : count  - i - 1;
4065 				fp_Run* pRun = m_vecRuns.getNthItem(_getRunLogIndx(k));
4066 
4067 				if (pRun->getType() == FPRUN_TAB)
4068 				{
4069 					UT_ASSERT(iSpaceCount == 0);
4070 					UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
4071 					break;
4072 				}
4073 				else if (pRun->getType() == FPRUN_TEXT)
4074 				{
4075 					fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
4076 
4077 					UT_sint32 iSpacesInText = pTR->countJustificationPoints(!bFoundStart);
4078 
4079 					if(!bFoundStart && iSpacesInText >= 0)
4080 						bFoundStart = true;
4081 
4082 					if(bFoundStart && iSpacesInText)
4083 					{
4084 						UT_uint32 iMySpaces = abs(iSpacesInText);
4085 						UT_sint32 iJustifyAmountForRun;
4086 
4087 						if (iSpaceCount-1 > 0)
4088 							iJustifyAmountForRun = static_cast<int>(static_cast<double>(iAmount) / (iSpaceCount) * iMySpaces);
4089 						else
4090 							iJustifyAmountForRun = iAmount;
4091 
4092 						// I am not sure why this was here, but it breaks justification
4093 						// at least on win32 with Uniscribe we can have runs that contain
4094 						// only a space and that do count toward justification. Tomas
4095 						// if (iSpaceCount == 1) iJustifyAmountForRun = 0;
4096 						pTR->justify(iJustifyAmountForRun, iMySpaces);
4097 
4098 						iAmount -= iJustifyAmountForRun;
4099 						iSpaceCount -= iMySpaces;
4100 					}
4101 					else if(!bFoundStart && iSpacesInText)
4102 					{
4103 						// trailing space, need to do this so that the
4104 						// trailing spaces do not get included when
4105 						// this run is merged with previous one
4106 
4107 						pTR->justify(0, 0);
4108 					}
4109 				}
4110 			}
4111 		}
4112 	}
4113 }
4114 
4115 /*
4116     I was going split the line from the end up to the last visual tab,
4117 	but in the bidi build this would be extremely expensive because the
4118 	calculation of visual coordinace for the run requires that after every
4119 	split we would recalculated the bidi map, and that is not worth it
4120 */
4121 
_splitRunsAtSpaces(void)4122 void fp_Line::_splitRunsAtSpaces(void)
4123 {
4124 	UT_sint32 count = m_vecRuns.getItemCount();
4125 	if(!count)
4126 		return;
4127 
4128 	UT_sint32 countOrig = count;
4129 
4130 	for (UT_sint32 i = 0; i < count; i++)
4131 	{
4132 		fp_Run* pRun = m_vecRuns.getNthItem(i);
4133 
4134 		if (pRun->getType() == FPRUN_TEXT)
4135 		{
4136 			fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
4137 			UT_sint32 iSpacePosition;
4138 
4139 			iSpacePosition = pTR->findCharacter(0, UCS_SPACE);
4140 
4141 			if ((iSpacePosition > 0) &&
4142 				(static_cast<UT_uint32>(iSpacePosition) < pTR->getBlockOffset() + pTR->getLength() - 1))
4143 			{
4144 				addDirectionUsed(pRun->getDirection(),false);
4145 				pTR->split(iSpacePosition + 1);
4146 				count++;
4147 			}
4148 		}
4149 	}
4150 
4151 	fp_Run* pRun = getLastRun();
4152 
4153 	if (pRun->getType() == FPRUN_TEXT)
4154 	{
4155 		fp_TextRun* pTR = static_cast<fp_TextRun *>(pRun);
4156 		UT_sint32 iSpacePosition = pTR->findCharacter(0, UCS_SPACE);
4157 
4158 		if ((iSpacePosition > 0) &&
4159 			(static_cast<UT_uint32>(iSpacePosition) < pTR->getBlockOffset() + pTR->getLength() - 1))
4160 		{
4161 			addDirectionUsed(pRun->getDirection(),false);
4162 			pTR->split(iSpacePosition + 1);
4163 		}
4164 	}
4165 
4166 	count = m_vecRuns.getItemCount();
4167 	if(count != countOrig)
4168 	{
4169 		m_bMapDirty = true;
4170 		_createMapOfRuns();
4171 	}
4172 }
4173 
4174 /*!
4175 	Creates a map for conversion from visual to logical position of runs on the line.
4176 	\param void
4177 */
_createMapOfRuns()4178 UT_sint32 fp_Line::_createMapOfRuns()
4179 {
4180 	UT_sint32 i=0;
4181 
4182 #ifdef USE_STATIC_MAP
4183 	if((s_pMapOwner != this) || (m_bMapDirty))
4184 	{
4185 		//claim the ownership of the map and mark it not dirty
4186 		s_pMapOwner = this;
4187 		m_bMapDirty = false;
4188 
4189 #else //if using non-static map, we only check for dirtiness
4190 	if(m_bMapDirty)
4191 	{
4192 		m_bMapDirty = false;
4193 #endif
4194 		UT_sint32 count = m_vecRuns.getItemCount();
4195 		if(!count)
4196 			return UT_OK;  // do not even try to map a line with no runs
4197 
4198 #if 0
4199 		if(count == 1)	 //if there is just one run, then make sure that it maps on itself and return
4200 		{
4201 			s_pMapOfRunsL2V[0] = 0;
4202 			s_pMapOfRunsV2L[0] = 0;
4203 			return UT_OK;
4204 		}
4205 #endif
4206 		if (count >= s_iMapOfRunsSize) //the MapOfRuns member is too small, reallocate
4207 		{
4208 			delete[] s_pMapOfRunsL2V;
4209 			delete[] s_pMapOfRunsV2L;
4210 			delete[] s_pPseudoString;
4211 			delete[] s_pEmbeddingLevels;
4212 
4213 			s_iMapOfRunsSize = count + 20; //allow for 20 extra runs, so that we do not have to
4214 										   //do this immediately again
4215 			s_pMapOfRunsL2V = new UT_uint32[s_iMapOfRunsSize];
4216 			s_pMapOfRunsV2L = new UT_uint32[s_iMapOfRunsSize];
4217 			s_pPseudoString    = new UT_UCS4Char[s_iMapOfRunsSize];
4218 			s_pEmbeddingLevels =  new UT_Byte[s_iMapOfRunsSize];
4219 
4220 
4221 			UT_ASSERT(s_pMapOfRunsL2V && s_pMapOfRunsV2L && s_pPseudoString && s_pEmbeddingLevels);
4222 		}
4223 
4224 		//make sure that the map is not exessively long;
4225 		if ((count < RUNS_MAP_SIZE) && (s_iMapOfRunsSize > 2* RUNS_MAP_SIZE))
4226 		{
4227 			delete[] s_pMapOfRunsL2V;
4228 			delete[] s_pMapOfRunsV2L;
4229 			delete[] s_pPseudoString;
4230 			delete[] s_pEmbeddingLevels;
4231 
4232 			s_iMapOfRunsSize = RUNS_MAP_SIZE;
4233 
4234 			s_pMapOfRunsL2V = new UT_uint32[s_iMapOfRunsSize];
4235 			s_pMapOfRunsV2L = new UT_uint32[s_iMapOfRunsSize];
4236 			s_pPseudoString    = new UT_UCS4Char[RUNS_MAP_SIZE];
4237 			s_pEmbeddingLevels =  new UT_Byte[RUNS_MAP_SIZE];
4238 
4239 
4240 			UT_ASSERT(s_pMapOfRunsL2V && s_pMapOfRunsV2L && s_pPseudoString && s_pEmbeddingLevels);
4241 		}
4242 
4243 		FV_View * pView = getSectionLayout()->getDocLayout()->getView();
4244 
4245 		if((pView && pView->getBidiOrder() == FV_Order_Logical_LTR) || !m_iRunsRTLcount)
4246 		{
4247 			xxx_UT_DEBUGMSG(("_createMapOfRuns: ltr line only (line 0x%x)\n", this));
4248 			for (i = 0; i < count; i++)
4249 			{
4250 				s_pMapOfRunsL2V[i] = i;
4251 				s_pMapOfRunsV2L[i] = i;
4252 				m_vecRuns.getNthItem(i)->setVisDirection(UT_BIDI_LTR);
4253 			}
4254 			return UT_OK;
4255 		}
4256 		else
4257 
4258 		//if this is unidirectional rtl text, we just fill the map sequentially
4259 		//from back to start
4260 		if((pView && pView->getBidiOrder() == FV_Order_Logical_RTL) || !m_iRunsLTRcount)
4261 		{
4262 			xxx_UT_DEBUGMSG(("_createMapOfRuns: rtl line only (line 0x%x)\n", this));
4263 			for(i = 0; i < count/2; i++)
4264 			{
4265 				s_pMapOfRunsL2V[i]= count - i - 1;
4266 				s_pMapOfRunsV2L[i]= count - i - 1;
4267 				s_pMapOfRunsL2V[count - i - 1] = i;
4268 				s_pMapOfRunsV2L[count - i - 1] = i;
4269 				m_vecRuns.getNthItem(i)->setVisDirection(UT_BIDI_RTL);
4270 				m_vecRuns.getNthItem(count - i - 1)->setVisDirection(UT_BIDI_RTL);
4271 			}
4272 
4273 			if(count % 2)	//the run in the middle
4274 			{
4275 				s_pMapOfRunsL2V[count/2] = count/2;
4276 				s_pMapOfRunsV2L[count/2] = count/2;
4277 				m_vecRuns.getNthItem(count/2)->setVisDirection(UT_BIDI_RTL);
4278 
4279 			}
4280 
4281 		}
4282 		else
4283 		{
4284 			/*
4285 				This is a genuine bidi line, so we have to go the full way.
4286 			*/
4287 			xxx_UT_DEBUGMSG(("_createMapOfRuns: bidi line (%d ltr runs, %d rtl runs, line 0x%x)\n", m_iRunsLTRcount, m_iRunsRTLcount, this));
4288 
4289 			// create a pseudo line string
4290 			/*
4291 				The fribidi library takes as its input a Unicode string, which
4292 				it then analyses. Rather than trying to construct a string for
4293 				the entire line, we create a short one in which each run
4294 				is represented by a single character of a same direction as
4295 				that of the entire run.
4296 			*/
4297 			UT_sint32 iRunDirection;
4298 
4299 			for(i = 0; i < count; i++)
4300 			{
4301 				iRunDirection = m_vecRuns.getNthItem(i)->getDirection();
4302 				switch(iRunDirection)
4303 				{
4304 					case UT_BIDI_LTR : s_pPseudoString[i] = static_cast<UT_UCS4Char>('a'); break;
4305 					case UT_BIDI_RTL : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x05d0); break;
4306 					//case UT_BIDI_WL
4307 					//case UT_BIDI_WR
4308 					case UT_BIDI_EN  : s_pPseudoString[i] = static_cast<UT_UCS4Char>('0'); break;
4309 					case UT_BIDI_ES  : s_pPseudoString[i] = static_cast<UT_UCS4Char>('/'); break;
4310 					case UT_BIDI_ET  : s_pPseudoString[i] = static_cast<UT_UCS4Char>('#'); break;
4311 					case UT_BIDI_AN  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x0660); break;
4312 					case UT_BIDI_CS  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(','); break;
4313 					case UT_BIDI_BS  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x000A); break;
4314 					case UT_BIDI_SS  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x000B); break;
4315 					case UT_BIDI_WS  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(' '); break;
4316 					case UT_BIDI_AL  : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x062D); break;
4317 					case UT_BIDI_NSM : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x0300); break;
4318 					case UT_BIDI_LRE : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x202A); break;
4319 					case UT_BIDI_RLE : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x202B); break;
4320 					case UT_BIDI_LRO : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x202D); break;
4321 					case UT_BIDI_RLO : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x202E); break;
4322 					case UT_BIDI_PDF : s_pPseudoString[i] = static_cast<UT_UCS4Char>(0x202C); break;
4323 					case UT_BIDI_ON  : s_pPseudoString[i] = static_cast<UT_UCS4Char>('!'); break;
4324 
4325 				}
4326 				xxx_UT_DEBUGMSG(("fp_Line::_createMapOfRuns: pseudo char 0x%x\n",s_pPseudoString[i]));
4327 			}
4328 
4329 			UT_BidiCharType iBlockDir = m_pBlock->getDominantDirection();
4330 
4331 			/*bool bRet =*/ UT_bidiMapLog2Vis(s_pPseudoString, count, iBlockDir,
4332 										  s_pMapOfRunsL2V, s_pMapOfRunsV2L, s_pEmbeddingLevels);
4333 
4334 			 //the only other thing that remains is to pass the visual
4335 			 //directions down to the runs.
4336 			 for (i=0; i<count;i++)
4337 			 {
4338 				m_vecRuns.getNthItem(i)->setVisDirection(s_pEmbeddingLevels[i]%2 ? UT_BIDI_RTL : UT_BIDI_LTR);
4339 				xxx_UT_DEBUGMSG(("L2V %d, V2L %d, emb. %d [run 0x%x]\n", s_pMapOfRunsL2V[i],s_pMapOfRunsV2L[i],s_pEmbeddingLevels[i],m_vecRuns.getNthItem(i)));
4340 			 }
4341 		}//if/else only rtl
4342 	}
4343 
4344 	return(UT_OK);
4345 }
4346 
4347 /* the following two functions convert the position of a run from logical to visual
4348    and vice versa */
4349 
4350 UT_uint32 fp_Line::_getRunLogIndx(UT_sint32 indx)
4351 {
4352 #ifdef DEBUG
4353 	UT_sint32 iCount = m_vecRuns.getItemCount();
4354 	if(iCount <= indx)
4355 		UT_DEBUGMSG(("fp_Line::_getRunLogIndx: indx %d, iCount %d\n", indx,iCount));
4356 #endif
4357 	UT_ASSERT((m_vecRuns.getItemCount() > indx));
4358 
4359 	if(!m_iRunsRTLcount)
4360 		return(indx);
4361 
4362 	_createMapOfRuns();
4363 	return(s_pMapOfRunsV2L[indx]);
4364 }
4365 
4366 
4367 UT_uint32 fp_Line::_getRunVisIndx(UT_sint32 indx)
4368 {
4369 	UT_ASSERT(m_vecRuns.getItemCount() > indx);
4370 
4371 	if(!m_iRunsRTLcount)
4372 		return(indx);
4373 
4374 	_createMapOfRuns();
4375 	return(s_pMapOfRunsL2V[indx]);
4376 }
4377 
4378 UT_uint32	fp_Line::getVisIndx(fp_Run* pRun)
4379 {
4380 	UT_sint32 i = m_vecRuns.findItem(pRun);
4381 	UT_ASSERT(i >= 0);
4382 	return _getRunVisIndx(static_cast<UT_uint32>(i));
4383 }
4384 
4385 fp_Run *	fp_Line::getRunAtVisPos(UT_sint32 i)
4386 {
4387 	if(i >= m_vecRuns.getItemCount())
4388 		return NULL;
4389 	return m_vecRuns.getNthItem(_getRunLogIndx(i));
4390 }
4391 
4392 fp_Run * fp_Line::getLastVisRun()
4393 {
4394 	if(!m_iRunsRTLcount)
4395 		return(getLastRun());
4396 
4397 	_createMapOfRuns();
4398 	UT_sint32 count = m_vecRuns.getItemCount();
4399 	UT_ASSERT(count > 0);
4400 	return m_vecRuns.getNthItem(s_pMapOfRunsV2L[count - 1]);
4401 }
4402 
4403 fp_Run * fp_Line::getFirstVisRun()
4404 {
4405 	if(!m_iRunsRTLcount)
4406 		return(0);
4407 
4408 	_createMapOfRuns();
4409 	return m_vecRuns.getNthItem(s_pMapOfRunsV2L[0]);
4410 }
4411 
4412 
4413 
4414 ////////////////////////////////////////////////////////////////////
4415 //
4416 // the following three functions are used to keep track of rtl and
4417 // ltr runs on the line; this allows us to avoid the fullblown
4418 // bidi algorithm for ltr-only and rtl-only lines
4419 //
4420 // the parameter bRefreshMap specifies whether the map of runs should
4421 // be recalculated; if you call any of these functions in a loop
4422 // and do not need the refreshed map inside of that loop, set it to
4423 // false and then after the loop set m_bMapDirty true and run
4424 // _createMapOfRuns (when outside of fp_Line, make sure that only
4425 // the last call gets true)
4426 
4427 void fp_Line::addDirectionUsed(UT_BidiCharType dir, bool bRefreshMap)
4428 {
4429 	if(UT_BIDI_IS_RTL(dir))
4430 	{
4431 		m_iRunsRTLcount++;
4432 		xxx_UT_DEBUGMSG(("fp_Line::addDirectionUsed: increased RTL run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsRTLcount, this, bRefreshMap));
4433 	}
4434 	else if(!UT_BIDI_IS_NEUTRAL(dir))
4435 	{
4436 		m_iRunsLTRcount++;
4437 		xxx_UT_DEBUGMSG(("fp_Line::addDirectionUsed: increased LTR run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsLTRcount, this, bRefreshMap));
4438 	}
4439 
4440 	if(bRefreshMap && (dir != static_cast<UT_BidiCharType>(UT_BIDI_UNSET)))
4441 	{
4442 		m_bMapDirty = true;
4443 		//_createMapOfRuns();
4444 	}
4445 }
4446 
4447 void fp_Line::removeDirectionUsed(UT_BidiCharType dir, bool bRefreshMap)
4448 {
4449 	if(UT_BIDI_IS_RTL(dir))
4450 	{
4451 		m_iRunsRTLcount--;
4452 		xxx_UT_DEBUGMSG(("fp_Line::removeDirectionUsed: increased RTL run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsRTLcount, this, bRefreshMap));
4453 	}
4454 	else if(!UT_BIDI_IS_NEUTRAL(dir))
4455 	{
4456 		m_iRunsLTRcount--;
4457 		xxx_UT_DEBUGMSG(("fp_Line::removeDirectionUsed: increased LTR run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsLTRcount, this, bRefreshMap));
4458 	}
4459 
4460 	if(bRefreshMap && (dir != static_cast<UT_BidiCharType>(UT_BIDI_UNSET)))
4461 	{
4462 		m_bMapDirty = true;
4463 		//_createMapOfRuns();
4464 	}
4465 }
4466 
4467 void fp_Line::changeDirectionUsed(UT_BidiCharType oldDir, UT_BidiCharType newDir, bool bRefreshMap)
4468 {
4469 	if(oldDir == newDir)
4470 		return;
4471 
4472 	if(UT_BIDI_IS_RTL(newDir))
4473 	{
4474 		m_iRunsRTLcount++;
4475 		xxx_UT_DEBUGMSG(("fp_Line::changeDirectionUsed: increased RTL run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsRTLcount, this, bRefreshMap));
4476 	}
4477 	else if(!UT_BIDI_IS_NEUTRAL(newDir))
4478 	{
4479 		m_iRunsLTRcount++;
4480 		xxx_UT_DEBUGMSG(("fp_Line::changeDirectionUsed: increased LTR run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsLTRcount, this, bRefreshMap));
4481 	}
4482 
4483 	if(UT_BIDI_IS_RTL(oldDir))
4484 	{
4485 		m_iRunsRTLcount--;
4486 		xxx_UT_DEBUGMSG(("fp_Line::changeDirectionUsed: increased RTL run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsRTLcount, this, bRefreshMap));
4487 	}
4488 	else if(!UT_BIDI_IS_NEUTRAL(oldDir))
4489 	{
4490 		m_iRunsLTRcount--;
4491 		xxx_UT_DEBUGMSG(("fp_Line::changeDirectionUsed: increased LTR run count [%d, this=0x%x, bRefresh=%d]\n", m_iRunsLTRcount, this, bRefreshMap));
4492 	}
4493 
4494 
4495 	if(bRefreshMap && (newDir != static_cast<UT_BidiCharType>(UT_BIDI_UNSET)))
4496 	{
4497 		m_bMapDirty = true;
4498 		_createMapOfRuns();
4499 	}
4500 }
4501 
4502 /*!
4503     Scan through the runs on this line, checking for footnote anchor
4504     fields.  Return true if so.
4505 */
4506 void fp_Line::_updateContainsFootnoteRef(void)
4507 {
4508 	m_bContainsFootnoteRef = false;
4509 
4510 	UT_sint32 count = m_vecRuns.getItemCount();
4511 	for (UT_sint32 i = 0; i < count; i++)
4512 	{
4513 		const fp_Run * r = static_cast<const fp_Run *>(m_vecRuns.getNthItem(i));
4514 		if (r->getType() == FPRUN_FIELD)
4515 		{
4516 			const fp_FieldRun * fr = static_cast<const fp_FieldRun*>(r);
4517 			if (fr->getFieldType() == FPFIELD_endnote_ref)
4518 				m_bContainsFootnoteRef = true;
4519 		}
4520 	}
4521 }
4522 
4523 UT_sint32 fp_Line::getDrawingWidth() const
4524 {
4525 	if(isLastLineInBlock())
4526 	{
4527 		const fp_Run * pRun = getLastRun();
4528 		UT_return_val_if_fail(pRun && pRun->getType() == FPRUN_ENDOFPARAGRAPH, m_iWidth);
4529 		return (m_iWidth + (static_cast<const fp_EndOfParagraphRun*>(pRun))->getDrawingWidth());
4530 	}
4531 	else
4532 	{
4533 		return m_iWidth;
4534 	}
4535 }
4536