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