1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiWord
3 * Copyright (C) 1998,1999 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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29
30 #include "fp_TextRun.h"
31 #include "fl_DocLayout.h"
32 #include "fl_BlockLayout.h"
33 #include "fp_Line.h"
34 #include "pp_Property.h"
35 #include "pp_AttrProp.h"
36 #include "gr_Graphics.h"
37 #include "pd_Document.h"
38 #include "pd_Style.h"
39 #include "pd_Iterator.h"
40 #include "gr_DrawArgs.h"
41 #include "fv_View.h"
42 #include "fb_Alignment.h"
43 #include "fl_TableLayout.h"
44 #include "xap_App.h"
45 #include "xap_Frame.h"
46 #include "ut_debugmsg.h"
47 #include "ut_assert.h"
48 #include "ut_string.h"
49 #include "ut_growbuf.h"
50 #include "ut_units.h"
51 #include "ut_types.h"
52 #include "xap_EncodingManager.h"
53
54 #include "ut_OverstrikingChars.h"
55 #include "ut_Language.h"
56 #include "ap_Prefs.h"
57 #include "gr_Painter.h"
58
59 /*****************************************************************/
60
61 //inicialise the static members of the class
62 bool fp_TextRun::s_bBidiOS = false;
63 UT_uint32 fp_TextRun::s_iClassInstanceCount = 0;
64
fp_TextRun(fl_BlockLayout * pBL,UT_uint32 iOffsetFirst,UT_uint32 iLen,bool bLookupProperties)65 fp_TextRun::fp_TextRun(fl_BlockLayout* pBL,
66 UT_uint32 iOffsetFirst,
67 UT_uint32 iLen,
68 bool bLookupProperties)
69 : fp_Run(pBL,iOffsetFirst, iLen, FPRUN_TEXT),
70 m_TextTransform(GR_ShapingInfo::NONE),
71 m_fPosition(TEXT_POSITION_NORMAL),
72 #ifdef ENABLE_SPELL
73 m_bSpellSquiggled(false),
74 m_bGrammarSquiggled(false),
75 #endif
76 m_pLanguage(NULL),
77 m_bIsOverhanging(false),
78 m_bKeepWidths(false),
79 m_pItem(NULL),
80 m_pRenderInfo(NULL)
81 {
82 _setField(NULL);
83
84 // we will use this as an indication that the direction
85 // property has not been yet set normal values are -1,0,1
86 // (neutral, ltr, rtl)
87 _setDirection(UT_BIDI_UNSET);
88
89 // no override by default
90 m_iDirOverride = UT_BIDI_UNSET;
91
92 if (bLookupProperties)
93 {
94 lookupProperties();
95 }
96
97 markDrawBufferDirty();
98
99 if(!s_iClassInstanceCount)
100 {
101 s_bBidiOS = (XAP_App::getApp()->theOSHasBidiSupport() == XAP_App::BIDI_SUPPORT_FULL);
102 UT_DEBUGMSG(("fp_TextRun size is %zd\n",sizeof(fp_TextRun) ));
103 }
104
105 s_iClassInstanceCount++;
106 }
107
~fp_TextRun()108 fp_TextRun::~fp_TextRun()
109 {
110 xxx_UT_DEBUGMSG(("!!!!!!!! Text run %x deleted \n",this));
111 DELETEP(m_pRenderInfo);
112 DELETEP(m_pItem);
113 }
114
hasLayoutProperties(void) const115 bool fp_TextRun::hasLayoutProperties(void) const
116 {
117 return true;
118 }
119
_lookupProperties(const PP_AttrProp * pSpanAP,const PP_AttrProp * pBlockAP,const PP_AttrProp * pSectionAP,GR_Graphics * pG)120 void fp_TextRun::_lookupProperties(const PP_AttrProp * pSpanAP,
121 const PP_AttrProp * pBlockAP,
122 const PP_AttrProp * pSectionAP,
123 GR_Graphics * pG)
124 {
125 // we should only need this if the props have changed
126 //clearScreen();
127 bool bChanged = false;
128 bool bDontClear = false;
129 if(pG == NULL)
130 {
131 pG = getGraphics();
132 bDontClear = true;
133 }
134 if(pG != getGraphics() || isPrinting())
135 {
136 bDontClear = true;
137 }
138 if(_getFont() == NULL)
139 {
140 bDontClear = true;
141 }
142 xxx_UT_DEBUGMSG(("Lookup props in text run \n"));
143 fd_Field * fd = NULL;
144 static_cast<fl_Layout *>(getBlock())->getField(getBlockOffset(),fd);
145 _setField(fd);
146 // look for fonts in this DocLayout's font cache
147 FL_DocLayout * pLayout = getBlock()->getDocLayout();
148
149 PD_Document * pDoc = getBlock()->getDocument();
150
151 const PP_PropertyTypeColor *p_color = static_cast<const PP_PropertyTypeColor *>(PP_evalPropertyType("color",pSpanAP,pBlockAP,pSectionAP, Property_type_color, pDoc, true));
152 UT_ASSERT(p_color);
153 _setColorFG(p_color->getColor());
154
155 const gchar* pszStyle = NULL;
156 if(pSpanAP && pSpanAP->getAttribute(PT_STYLE_ATTRIBUTE_NAME, pszStyle))
157 {
158 PD_Style *pStyle = NULL;
159 pDoc->getStyle(static_cast<const char*>(pszStyle), &pStyle);
160 if(pStyle) pStyle->used(1);
161 }
162
163
164 const gchar *pszFontStyle = PP_evalProperty("font-style",pSpanAP,pBlockAP,pSectionAP, pDoc, true);
165 m_bIsOverhanging = (pszFontStyle && !strcmp(pszFontStyle, "italic"));
166
167 const gchar *pszDecor = PP_evalProperty("text-decoration",pSpanAP,pBlockAP,pSectionAP, pDoc, true);
168
169 /*
170 TODO map line width to a property, not a hard-coded value
171 */
172 static const UT_sint32 iLineWidth = UT_convertToLogicalUnits("0.8pt");
173 bChanged |= _setLineWidth(iLineWidth);
174
175 UT_uint32 oldDecors = _getDecorations();
176 _setDecorations(0);
177
178 gchar* p;
179 if (!(p = g_strdup(pszDecor)))
180 {
181 // TODO outofmem
182 }
183 UT_ASSERT(p || !pszDecor);
184 gchar* q = strtok(p, " ");
185
186 while (q)
187 {
188 if (0 == strcmp(q, "underline"))
189 {
190 _orDecorations(TEXT_DECOR_UNDERLINE);
191 }
192 else if (0 == strcmp(q, "overline"))
193 {
194 _orDecorations(TEXT_DECOR_OVERLINE);
195 }
196 else if (0 == strcmp(q, "line-through"))
197 {
198 _orDecorations(TEXT_DECOR_LINETHROUGH);
199 }
200 else if (0 == strcmp(q, "topline"))
201 {
202 _orDecorations(TEXT_DECOR_TOPLINE);
203 }
204 else if (0 == strcmp(q, "bottomline"))
205 {
206 _orDecorations(TEXT_DECOR_BOTTOMLINE);
207 }
208 q = strtok(NULL, " ");
209 }
210
211 g_free(p);
212
213 bChanged |= (_getDecorations() != oldDecors);
214
215 const gchar * pszPosition = PP_evalProperty("text-position",pSpanAP,pBlockAP,pSectionAP, pDoc, true);
216
217 UT_Byte oldPos = m_fPosition;
218
219 if (0 == strcmp(pszPosition, "superscript"))
220 {
221 m_fPosition = TEXT_POSITION_SUPERSCRIPT;
222 }
223 else if (0 == strcmp(pszPosition, "subscript"))
224 {
225 m_fPosition = TEXT_POSITION_SUBSCRIPT;
226 }
227 else m_fPosition = TEXT_POSITION_NORMAL;
228
229 bChanged |= (oldPos != m_fPosition);
230
231 const GR_Font * pFont = pLayout->findFont(pSpanAP,pBlockAP,pSectionAP,pG);
232 xxx_UT_DEBUGMSG(("Old font %x new font %x \n",_getFont(),pFont));
233 if (_getFont() != pFont)
234 {
235 _setFont(pFont);
236 pG->setFont(_getFont());
237 _setAscent(pG->getFontAscent(pFont));
238 _setDescent(pG->getFontDescent(pFont));
239 _setHeight(pG->getFontHeight(pFont));
240
241 // change of font can mean different glyph coverage; we have
242 // to recalculate the entire drawbuffer
243 //
244 // If we zoom the font changes but the charwidths don't. This preserves
245 // full justfication after a zoom.
246 //
247 if(!m_bKeepWidths)
248 {
249 markDrawBufferDirty();
250 markWidthDirty();
251
252 if(m_pRenderInfo)
253 m_pRenderInfo->m_eShapingResult = GRSR_Unknown;
254
255 bChanged = true;
256 }
257 }
258 else
259 {
260 pG->setFont(_getFont());
261 }
262 //set the language member
263 UT_Language lls;
264 const gchar * pszLanguage = PP_evalProperty("lang",pSpanAP,pBlockAP,pSectionAP, pDoc, true);
265
266 // NB: m_pLanguage is a pointer into static tables inside UT_Language class and as
267 // such has a guaranteed life-span same as the application; hence no g_strdup here and
268 // no strcmp later
269 const gchar * pszOldLanguage = m_pLanguage;
270 m_pLanguage = lls.getCodeFromCode(pszLanguage);
271 xxx_UT_DEBUGMSG(("!!!!!!!! Language of run set to %s pointer %x run %x \n",getLanguage(),m_pLanguage,this));
272 if(pszOldLanguage && (m_pLanguage != pszOldLanguage))
273 {
274 #ifdef ENABLE_SPELL
275 UT_uint32 reason = 0;
276 if( getBlock()->getDocLayout()->getAutoSpellCheck())
277 {
278 reason = (UT_uint32) FL_DocLayout::bgcrSpelling;
279 }
280 if( getBlock()->getDocLayout()->getAutoGrammarCheck())
281 {
282 reason = reason | (UT_uint32) FL_DocLayout::bgcrGrammar;
283 }
284 getBlock()->getDocLayout()->queueBlockForBackgroundCheck(reason, getBlock());
285 #endif
286 bChanged = true;
287 }
288
289
290 UT_BidiCharType iOldOverride = m_iDirOverride;
291 UT_BidiCharType iNewOverride;
292 const gchar *pszDirection = PP_evalProperty("dir-override",pSpanAP,pBlockAP,pSectionAP, pDoc, true);
293 // the way MS Word handles bidi is peculiar and requires that we allow
294 // temporarily a non-standard value for the dir-override property
295 // called "nobidi"
296 if(!pszDirection)
297 iNewOverride = UT_BIDI_UNSET;
298 else if(!strcmp(pszDirection, "ltr"))
299 iNewOverride = UT_BIDI_LTR;
300 else if(!strcmp(pszDirection, "rtl"))
301 iNewOverride = UT_BIDI_RTL;
302 else
303 iNewOverride = UT_BIDI_UNSET;
304
305
306 bChanged |= (iOldOverride != iNewOverride);
307
308 /*
309 OK, if the previous direction override was strong, and the new one is not (i.e., if
310 we are called because the user removed an override from us) we have to split this
311 run into chunks with uniform Unicode directional property
312
313 if the previous direction override was not strong, and the current one is, we have
314 to break this run's neighbours
315 */
316 if(iNewOverride == static_cast<UT_BidiCharType>(UT_BIDI_UNSET) &&
317 iOldOverride != static_cast<UT_BidiCharType>(UT_BIDI_UNSET))
318 {
319 // we have to do this without applying the new override otherwise the
320 // LTR and RTL run counters of the present line will be messed up;
321 // breakMeAtDirBoundaries will take care of applying the new override
322 breakMeAtDirBoundaries(iNewOverride);
323 }
324 else if(iNewOverride != static_cast<UT_BidiCharType>(UT_BIDI_UNSET) &&
325 iOldOverride == static_cast<UT_BidiCharType>(UT_BIDI_UNSET))
326 {
327 // first we have to apply the new override
328 setDirection(UT_BIDI_UNSET, iNewOverride);
329 // now do the breaking
330 breakNeighborsAtDirBoundaries();
331 }
332 else
333 setDirection(UT_BIDI_UNSET, iNewOverride);
334
335 const gchar *pszTextTransform = PP_evalProperty("text-transform",pSpanAP,pBlockAP,
336 pSectionAP, pDoc, true);
337
338 GR_ShapingInfo::TextTransform oldTextTransform = getTextTransform();
339 if(pszTextTransform && strcmp(pszTextTransform,"none") != 0)
340 {
341 if (strcmp(pszTextTransform,"capitalize") == 0)
342 setTextTransform(GR_ShapingInfo::CAPITALIZE);
343 else if (strcmp(pszTextTransform,"uppercase") == 0)
344 setTextTransform(GR_ShapingInfo::UPPERCASE);
345 else if (strcmp(pszTextTransform,"lowercase") == 0)
346 setTextTransform(GR_ShapingInfo::LOWERCASE);
347 }
348
349 bChanged |= (oldTextTransform != getTextTransform());
350
351 if(bChanged && !bDontClear)
352 clearScreen();
353 xxx_UT_DEBUGMSG(("fp_TextRun::lookupProperties: bChanged %d\n", static_cast<UT_uint32>(bChanged)));
354 }
355
356 /*!
357 * This method append the text in this run to the growbuf supplied in the
358 * parameter.
359 */
appendTextToBuf(UT_GrowBuf & buf) const360 void fp_TextRun::appendTextToBuf(UT_GrowBuf & buf) const
361 {
362 UT_GrowBuf myBuf;
363 getBlock()->getBlockBuf(&myBuf);
364 UT_uint32 len = getLength();
365 buf.append(myBuf.getPointer(getBlockOffset()),len);
366 }
367
368
369 #if DEBUG
printText(void)370 void fp_TextRun::printText(void)
371 {
372 // do not assert, the pointer might be legitimately null
373 //UT_ASSERT(m_pRenderInfo);
374
375 // if(!m_pRenderInfo || m_pRenderInfo->getType() != GRRI_XP)
376 if(!m_pRenderInfo)
377 return;
378 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
379 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
380
381 text.setUpperLimit(text.getPosition() + getLength() - 1);
382
383 UT_ASSERT_HARMLESS( text.getStatus() == UTIter_OK );
384 UT_UTF8String sTmp;
385 while(text.getStatus() == UTIter_OK)
386 {
387 UT_UCS4Char c = text.getChar();
388 xxx_UT_DEBUGMSG(("| %d |",c));
389 if(c >= ' ' && c <128)
390 sTmp += static_cast<char>(c);
391 ++text;
392 }
393
394 UT_uint32 offset = getBlockOffset();
395 UT_uint32 len = getLength();
396 UT_DEBUGMSG(("Run offset %d len %d Text |%s| \n",offset,len,sTmp.utf8_str()));
397 }
398 #endif
canBreakAfter(void) const399 bool fp_TextRun::canBreakAfter(void) const
400 {
401 if (getNextRun() && getNextRun()->getType () != FPRUN_TEXT)
402 return getNextRun()->canBreakBefore();
403 else if (!getNextRun())
404 return true;
405 else if (getLength() > 0)
406 {
407 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
408 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
409 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
410
411 // in order to allow proper decision on breaking at the end of run, we
412 // set the upper limit one character pass the end of this run -- we
413 // know there is a text run following us
414 text.setUpperLimit(text.getPosition() + getLength());
415
416 UT_return_val_if_fail(m_pRenderInfo, false);
417 m_pRenderInfo->m_pText = &text;
418 m_pRenderInfo->m_iOffset = getLength() - 1;
419 m_pRenderInfo->m_iLength = getLength();
420
421 UT_sint32 iNext;
422 if (getGraphics()->canBreak(*m_pRenderInfo, iNext, true))
423 return true;
424 }
425
426 return false;
427 }
428
canBreakBefore(void) const429 bool fp_TextRun::canBreakBefore(void) const
430 {
431 if (getLength() > 0)
432 {
433 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
434 getBlockOffset() + fl_BLOCK_STRUX_OFFSET );
435
436 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
437
438 // in order to allow proper decision on breaking at the end of run, we will try to
439 // set the upper limit one character pass the end of this run
440
441 if(getNextRun())
442 text.setUpperLimit(text.getPosition() + getLength());
443 else
444 text.setUpperLimit(text.getPosition() + getLength() - 1);
445
446 UT_return_val_if_fail(m_pRenderInfo, false);
447 m_pRenderInfo->m_pText = &text;
448 m_pRenderInfo->m_iOffset = 0;
449 m_pRenderInfo->m_iLength = getLength();
450 UT_sint32 iNext;
451
452 if (getGraphics()->canBreak(*m_pRenderInfo, iNext, false))
453 {
454 return true;
455 }
456 }
457 else
458 {
459 if (getNextRun())
460 {
461 return getNextRun()->canBreakBefore();
462 }
463 else
464 {
465 return true;
466 }
467 }
468
469 return false;
470 }
471
alwaysFits(void) const472 bool fp_TextRun::alwaysFits(void) const
473 {
474 if (getLength() > 0)
475 {
476 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
477 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
478
479 for (UT_uint32 i=0; i< getLength() && text.getStatus() == UTIter_OK; i++, ++text)
480 {
481 if (text.getChar() != UCS_SPACE)
482 {
483 return false;
484 }
485 }
486
487 return false;
488 }
489
490 // could assert here -- this should never happen, I think
491 return true;
492 }
493
findFirstNonBlankSplitPoint(fp_RunSplitInfo &)494 bool fp_TextRun::findFirstNonBlankSplitPoint(fp_RunSplitInfo& /*si*/ )
495 {
496 //
497 // What is this code trying to achieve?
498 // Why do we want to keep around for future reference?
499 //
500 return false;
501 #if 0 // if turning this back on, replace the while loop with PD_StruxIterator
502 UT_GrowBuf * pgbCharWidths = getBlock()->getCharWidths()->getCharWidths();
503 UT_sint32 iRightWidth = getWidth();
504 UT_GrowBufElement* pCharWidths = pgbCharWidths->getPointer(0);
505 if(pCharWidths == NULL)
506 {
507 return false;
508 }
509 UT_sint32 iLeftWidth = 0;
510
511 si.iOffset = -1;
512
513 const UT_UCSChar* pSpan;
514 UT_uint32 lenSpan;
515 UT_uint32 offset = getBlockOffset();
516 UT_uint32 len = getLength();
517 bool bContinue = true;
518 bool bFound = false;
519 while (bContinue)
520 {
521 bContinue = getBlock()->getSpanPtr(offset, &pSpan, &lenSpan);
522
523 if(!bContinue)
524 {
525 // this block has got a text run but no span in the PT,
526 // this is clearly a bug
527 UT_ASSERT( UT_SHOULD_NOT_HAPPEN );
528 return false;
529 }
530
531 UT_ASSERT(lenSpan>0);
532
533 if (lenSpan > len)
534 {
535 lenSpan = len;
536 }
537
538 for (UT_uint32 i=0; i<lenSpan; i++)
539 {
540 UT_sint32 iCW = pCharWidths[i + offset] > 0 ? pCharWidths[i + offset] : 0;
541 iLeftWidth += iCW;
542 iRightWidth -= iCW;
543 if (
544 (!XAP_EncodingManager::get_instance()->can_break_at(pSpan[i])
545 && ((i + offset) != (getBlockOffset() + getLength() - 1))
546 )
547 )
548 {
549 si.iLeftWidth = iLeftWidth-iCW;
550 si.iRightWidth = iRightWidth + iCW;
551 si.iOffset = i + offset -1;
552 if((i + offset - 1) < 0)
553 {
554 si.iOffset = 0;
555 }
556 bFound = true;
557 break;
558 }
559 }
560 bContinue = false;
561 }
562 return bFound;
563 #endif
564 }
565
566 /*
567 Determine best split point in Run
568 \param iMaxLeftWidth Width to split at
569 \retval si Split information (left width, right width, and position)
570 \param bForce Force a split at first opportunity (max width)
571 \return True if split point was found in this Run, otherwise false.
572
573 NB: the offset returned is the offset of the character the after which the break occurs,
574 not at which the break occurs
575 */
findMaxLeftFitSplitPoint(UT_sint32 iMaxLeftWidth,fp_RunSplitInfo & si,bool bForce)576 bool fp_TextRun::findMaxLeftFitSplitPoint(UT_sint32 iMaxLeftWidth, fp_RunSplitInfo& si, bool bForce)
577 {
578 UT_return_val_if_fail(m_pRenderInfo, false);
579
580 // this approach suffers from rounding errors if the graphics class keeps data
581 // internally in units other than layout; it might be better to recalculate the two
582 // portions onece we found the split point
583 UT_sint32 iLeftWidth = 0;
584 UT_sint32 iRightWidth = getWidth();
585
586 si.iOffset = -1;
587
588 UT_uint32 offset = getBlockOffset();
589
590 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
591 offset + fl_BLOCK_STRUX_OFFSET);
592
593 m_pRenderInfo->m_pText = &text;
594 // in order to allow proper decision on breaking at the end of run, we will try to
595 // set the upper limit one character pass the end of this run
596
597 if(getNextRun() && getNextRun()->getType() == FPRUN_TEXT)
598 text.setUpperLimit(text.getPosition() + getLength());
599 else
600 text.setUpperLimit(text.getPosition() + getLength() - 1);
601
602 UT_uint32 iPosStart = text.getPosition();
603
604 //bool bReverse = (getVisDirection() == UT_BIDI_RTL);
605 UT_sint32 iNext = -1;
606
607 for(UT_uint32 i = 0; i < getLength() && text.getStatus() == UTIter_OK; i++, ++text)
608 {
609 // getTextWidth() takes LOGICAL offset
610 m_pRenderInfo->m_iOffset = i;
611 m_pRenderInfo->m_iLength = 1;
612 UT_sint32 iCW2 = getGraphics()->getTextWidth(*m_pRenderInfo);
613 iLeftWidth += iCW2;
614 iRightWidth -= iCW2;
615
616 UT_UCS4Char c = text.getChar();
617 bool bCanBreak = false;
618 // GR_Graphics::canBreak can be expensive, so we only call it
619 // when necessary
620 if(!bForce && iNext != (UT_sint32)i)
621 {
622 // need to reposition the iterator
623 UT_uint32 iPos = text.getPosition();
624 text.setPosition(iPosStart);
625
626 m_pRenderInfo->m_iLength = getLength();
627 m_pRenderInfo->m_iOffset = i;
628 bCanBreak = getGraphics()->canBreak(*m_pRenderInfo, iNext, true);
629
630 text.setPosition(iPos);
631 }
632
633 if (bForce || iNext == (UT_sint32)i || bCanBreak)
634 // && ((i + offset) != (getBlockOffset() + getLength() - 1))
635 {
636 if (iLeftWidth <= iMaxLeftWidth)
637 {
638 si.iLeftWidth = iLeftWidth;
639 si.iRightWidth = iRightWidth;
640 si.iOffset = i + offset;
641 }
642 else
643 {
644 // Ignore trailing space when chosing break points
645 if(c == UCS_SPACE)
646 {
647 // calculate with of previous continuous space
648 UT_sint32 iSpaceW = 0;
649 PD_StruxIterator text2(getBlock()->getStruxDocHandle(),
650 offset + fl_BLOCK_STRUX_OFFSET + i);
651
652 UT_sint32 j = i;
653 while(j >= 0
654 && text2.getStatus() == UTIter_OK
655 && text2.getChar() == UCS_SPACE)
656 {
657 // getTextWidth() takes LOGICAL offset
658 m_pRenderInfo->m_iOffset = j;
659 m_pRenderInfo->m_iLength = 1;
660 iSpaceW += getGraphics()->getTextWidth(*m_pRenderInfo);
661 j--;
662 --text2;
663 }
664
665 if(iLeftWidth - iSpaceW <= iMaxLeftWidth)
666 {
667 si.iLeftWidth = iLeftWidth;
668 si.iRightWidth = iRightWidth;
669 si.iOffset = i + offset;
670 }
671 }
672
673 // no matter how we got here, we are now done ...
674 break;
675 }
676 xxx_UT_DEBUGMSG(("Candidate Slit point is %d \n", si.iLeftWidth));
677 }
678 else if(iNext > 0)
679 {
680 // this is the case when we cannot break at the present
681 // offset, but the graphics let us know what the next
682 // legal break offset is; we just scroll through the
683 // characters in between; i-th char has been processed
684 // already
685 UT_uint32 iAdvance = iNext - i - 1;
686 m_pRenderInfo->m_iOffset = i + 1;
687 m_pRenderInfo->m_iLength = iAdvance;
688 UT_sint32 iCW = getGraphics()->getTextWidth(*m_pRenderInfo);
689 iLeftWidth += iCW;
690 iRightWidth -= iCW;
691
692 // advance iterator and index by number of chars processed
693 i += iAdvance;
694 text += iAdvance;
695 UT_return_val_if_fail(text.getStatus()==UTIter_OK, false);
696 }
697 else if(iNext == -2)
698 {
699 // this is the case where the graphics let us know that there are no more
700 // breakpoints in this run
701 break;
702 }
703 }
704
705 if ((si.iOffset == -1) || (si.iLeftWidth == getWidth()))
706 {
707 // there were no split points which fit.
708 return false;
709 }
710
711 return true;
712 }
713
mapXYToPosition(UT_sint32 x,UT_sint32 y,PT_DocPosition & pos,bool & bBOL,bool & bEOL,bool &)714 void fp_TextRun::mapXYToPosition(UT_sint32 x, UT_sint32 y,
715 PT_DocPosition& pos,
716 bool& bBOL, bool& bEOL, bool & /*isTOC*/)
717 {
718 UT_BidiCharType iVisDirection = getVisDirection();
719 UT_BidiCharType iDomDirection = getBlock()->getDominantDirection();
720
721 if (x <= 0)
722 {
723 if(iVisDirection == UT_BIDI_RTL)
724 {
725 pos = getBlock()->getPosition() + getBlockOffset() + getLength();
726 if(iDomDirection == UT_BIDI_RTL)
727 {
728 bEOL = true;
729 bBOL = false;
730 }
731 else
732 {
733 bEOL = false;
734 bBOL = true;
735 }
736 }
737 else
738 {
739 pos = getBlock()->getPosition() + getBlockOffset();
740 // don't set bBOL to false here
741 bEOL = false;
742 }
743 return;
744 }
745
746 if (x >= getWidth())
747 {
748 if(iVisDirection == UT_BIDI_RTL)
749 {
750 pos = getBlock()->getPosition() + getBlockOffset();
751
752 if(iDomDirection == UT_BIDI_RTL)
753 {
754 bEOL = false;
755 bBOL = true;
756 }
757 else
758 {
759 bEOL = true;
760 bBOL = false;
761 }
762 }
763 else
764 {
765 pos = getBlock()->getPosition() + getBlockOffset() + getLength();
766 // Setting bEOL fixes bug 1149. But bEOL has been set in the
767 // past - probably somewhere else, so this is not necessarily
768 // the correct place to do it. 2001.02.25 jskov
769 bEOL = true;
770 }
771 return;
772 }
773
774 // this is a hack for the xp calculation; should really be moved
775 // into GR_Graphics::XYToPosition()
776 if(!m_pRenderInfo || _getRefreshDrawBuffer() == GRSR_Unknown)
777 {
778 _refreshDrawBuffer();
779 }
780 UT_return_if_fail(m_pRenderInfo);
781
782 if(m_pRenderInfo->getType() == GRRI_XP)
783 {
784 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) * m_pRenderInfo;
785
786 if(!RI.m_pWidths)
787 return;
788
789 // catch the case of a click directly on the left half of the
790 // first character in the run
791 UT_uint32 k = iVisDirection == UT_BIDI_RTL ? getLength() - 1 : 0;
792 UT_sint32 iCW2 = RI.m_pWidths[k] > 0 ? RI.m_pWidths[k] : 0;
793
794 if (x < (iCW2 / 2))
795 {
796 pos = getBlock()->getPosition() + getOffsetFirstVis();
797
798 // if this character is RTL then clicking on the left side
799 // means the user wants to postion the caret _after_ this char
800 if(iVisDirection == UT_BIDI_RTL)
801 pos++;
802
803 bBOL = false;
804 bEOL = false;
805 pos += adjustCaretPosition(pos,true);
806 return;
807 }
808
809 UT_sint32 iWidth = 0;
810 // for (UT_uint32 i=getBlockOffset(); i<(getBlockOffset() + getLength()); i++)
811 for (UT_uint32 i = 0; i < getLength(); i++)
812 {
813 // i represents VISUAL offset, CharWidths array now also uses logical
814 // order of indexing
815
816 // UT_uint32 iLog = getOffsetLog(i);
817 // UT_uint32 iCW = pCharWidths[iLog] > 0 ? pCharWidths[iLog] : 0;
818 UT_uint32 iCW = RI.m_pWidths[i] > 0 ? RI.m_pWidths[i] : 0;
819
820 iWidth += iCW;
821
822 if (iWidth > x)
823 {
824 if ((iWidth - x) <= (RI.m_pWidths[i] / 2))
825 {
826 i++;
827 }
828
829 // NOTE: this allows inserted text to be coalesced in the PT
830 bEOL = true;
831
832 // i is visual,
833 UT_uint32 iLog = i;
834
835 if(iVisDirection == UT_BIDI_RTL)
836 iLog = getLength() - i;
837
838 pos = getBlock()->getPosition() + getBlockOffset() + iLog;
839 pos += adjustCaretPosition(pos,true);
840 return;
841 }
842 }
843 }
844 else
845 {
846 #ifdef WITH_CAIRO
847 // This is really for the benefit of the Pango graphics, which requires the raw
848 // text for almost anything; we do not need this on win32, and since this this
849 // called all the time, do not want it in here unless necessary
850 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
851 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
852 UT_return_if_fail(text.getStatus() == UTIter_OK);
853 m_pRenderInfo->m_pText = &text;
854 m_pRenderInfo->m_iLength = getLength();
855 #endif
856
857 bBOL = false;
858 bEOL = false;
859 pos = getGraphics()->XYToPosition(*m_pRenderInfo, x, y);
860 pos += getBlock()->getPosition() + getBlockOffset();
861
862 #ifdef WITH_CAIRO
863 // reset this, so we have no stale pointers there
864 m_pRenderInfo->m_pText = NULL;
865 #endif
866 pos = adjustCaretPosition(pos,true);
867 return;
868 }
869
870 xxx_UT_DEBUGMSG(("fp_TextRun::mapXYToPosition: x %d, m_iWidth %d\n", x,getWidth()));
871 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
872 }
873
findPointCoords(UT_uint32 iOffset,UT_sint32 & x,UT_sint32 & y,UT_sint32 & x2,UT_sint32 & y2,UT_sint32 & height,bool & bDirection)874 void fp_TextRun::findPointCoords(UT_uint32 iOffset, UT_sint32& x, UT_sint32& y, UT_sint32& x2, UT_sint32& y2, UT_sint32& height, bool& bDirection)
875 {
876 UT_sint32 xoff;
877 UT_sint32 yoff;
878 UT_sint32 xoff2;
879 UT_sint32 yoff2;
880 UT_sint32 xdiff = 0;
881 xxx_UT_DEBUGMSG(("findPointCoords: Text Run offset %d \n",iOffset));
882 if(!m_pRenderInfo || _getRefreshDrawBuffer() == GRSR_Unknown)
883 {
884 // this can happen immediately after run is inserted at the
885 // end of a paragraph.
886 _refreshDrawBuffer();
887 }
888 UT_return_if_fail(m_pRenderInfo);
889
890 UT_return_if_fail(getLine());
891
892 // UT_uint32 docPos = getBlockOffset() + getBlock()->getPosition() +iOffset;
893 //docPos = adjustCaretPosition(docPos,true);
894 //iOffset = docPos - getBlockOffset() + getBlock()->getPosition();
895 getLine()->getOffsets(this, xoff, yoff);
896 if (getLine()->getY() == INITIAL_OFFSET)
897 {
898 UT_DEBUGMSG(("Line position requested prior to line being placed\n"));
899 if (getLine()->getPrev())
900 {
901 yoff += getLine()->getPrev()->getY() + getLine()->getHeight() - INITIAL_OFFSET;
902 }
903 }
904
905 if (m_fPosition == TEXT_POSITION_SUPERSCRIPT)
906 {
907 yoff -= getAscent() * 1/2;
908 }
909 else if (m_fPosition == TEXT_POSITION_SUBSCRIPT)
910 {
911 yoff += getDescent() /* * 3/2 */;
912 }
913
914 if(m_pRenderInfo->getType() == GRRI_XP)
915 {
916 GR_XPRenderInfo & RI = (GR_XPRenderInfo &) * m_pRenderInfo;
917
918 UT_return_if_fail(RI.m_pWidths);
919
920 //UT_uint32 offset = UT_MIN(iOffset, getBlockOffset() + getLength());
921 UT_uint32 offset = UT_MIN(iOffset - getBlockOffset(), getLength());
922
923 UT_sint32 iDirection = getVisDirection();
924
925 // for (UT_uint32 i=getBlockOffset(); i<offset; i++)
926 for (UT_uint32 i=0; i<offset; i++)
927 {
928 UT_uint32 k = iDirection == UT_BIDI_RTL ? getLength() - i - 1: i;
929 UT_uint32 iCW = RI.m_pWidths[k] > 0 ? RI.m_pWidths[k] : 0;
930 xdiff += iCW;
931 }
932
933 UT_sint32 iNextDir = iDirection == UT_BIDI_RTL ? UT_BIDI_LTR : UT_BIDI_RTL; //if this is last run we will anticipate the next to have *different* direction
934 fp_Run * pRun = 0; //will use 0 as indicator that there is no need to deal with the second caret
935
936 if(offset == getLength()) //this is the end of the run
937 {
938 pRun = getNextRun();
939
940 if(pRun)
941 {
942 iNextDir = pRun->getVisDirection();
943 pRun->getLine()->getOffsets(pRun, xoff2, yoff2);
944 // if the next run is the end of paragraph marker,
945 // we need to derive yoff2 from the offset of this
946 // run instead of the marker
947 if(pRun->getType() == FPRUN_ENDOFPARAGRAPH)
948 yoff2 = yoff;
949 }
950 }
951
952 if(iDirection == UT_BIDI_RTL) //#TF rtl run
953 {
954 x = xoff + getWidth() - xdiff; //we want the caret right of the char
955 }
956 else
957 {
958 x = xoff + xdiff;
959 }
960
961 if(pRun && (iNextDir != iDirection)) //followed by run of different direction, have to split caret
962 {
963 x2 = (iNextDir == UT_BIDI_LTR) ? xoff2 : xoff2 + pRun->getWidth();
964 y2 = yoff2;
965 }
966 else
967 {
968 x2 = x;
969 y2 = yoff;
970 }
971
972 bDirection = (iDirection != UT_BIDI_LTR);
973 y = yoff;
974 height = getHeight();
975 xxx_UT_DEBUGMSG(("findPointCoords: TextRun yoff %d \n",yoff));
976 }
977 else
978 {
979 y = y2 = yoff;
980 height = getHeight();
981 bDirection = (getVisDirection() != UT_BIDI_LTR);
982
983 // I am not sure why we have to subtract the 1 here, but we do -- we need to make
984 // sure we do not pass -1 down the line
985 m_pRenderInfo->m_iOffset = iOffset - getBlockOffset() - 1;
986 m_pRenderInfo->m_iLength = getLength();
987
988 #ifdef WITH_CAIRO
989 // This is really for the benefit of the Pango graphics, which requires the raw
990 // text for almost anything; we do not need this on win32, and since this this
991 // called all the time, do not want it in
992 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
993 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
994 UT_return_if_fail(text.getStatus() == UTIter_OK);
995 m_pRenderInfo->m_pText = &text;
996 #endif
997
998 getGraphics()->positionToXY(*m_pRenderInfo, x, y, x2, y2, height, bDirection);
999 x += xoff;
1000 x2 += xoff;
1001
1002 #ifdef WITH_CAIRO
1003 // reset this, so we have no stale pointers there
1004 m_pRenderInfo->m_pText = NULL;
1005 #endif
1006 }
1007 }
1008
canMergeWithNext(void)1009 bool fp_TextRun::canMergeWithNext(void)
1010 {
1011 bool bNextIsFmt = false;
1012 if (!getNextRun() ||
1013 !getLine() ||
1014 getNextRun()->getType() != FPRUN_TEXT ||
1015 !getNextRun()->getLine() ||
1016 getLength()+ getNextRun()->getLength() > 16000) // sanity check, see bugs 8542 and 13709
1017 {
1018 if(getNextRun()->getType() == FPRUN_FMTMARK)
1019 {
1020 bNextIsFmt = true;
1021 }
1022 else
1023 {
1024 return false;
1025 }
1026 }
1027 fp_TextRun* pNext = NULL;
1028 //
1029 // This code looks to see if we have a redundant fmtmark. If so
1030 // we remove it later.
1031 //
1032 if(bNextIsFmt)
1033 {
1034 fp_Run * pNextNext = getNextRun()->getNextRun();
1035 if(pNextNext && pNextNext->getType() == FPRUN_TEXT)
1036 {
1037 xxx_UT_DEBUGMSG(("Looking if we can merge through fmtMArk \n"));
1038 #ifdef DEBUG
1039 // printText();
1040 #endif
1041 pNext = static_cast<fp_TextRun *>(pNextNext);
1042 }
1043 else
1044 return false;
1045 }
1046 else
1047 pNext = static_cast<fp_TextRun*>(getNextRun());
1048
1049 if (
1050 (pNext->getBlockOffset() != (getBlockOffset() + getLength()))
1051 || (pNext->_getDecorations() != _getDecorations())
1052 || (pNext->_getFont() != _getFont())
1053 || (getHeight() != pNext->getHeight())
1054 || (pNext->getField() != getField())
1055 || (pNext->m_pLanguage != m_pLanguage) //this is not a bug; see m_pLanguage in
1056 //fp_TextRun.h before modifying this line
1057 || (pNext->_getColorFG() != _getColorFG())
1058 || (pNext->_getColorHL() != _getColorHL())
1059 || (pNext->_getColorHL().isTransparent() != _getColorHL().isTransparent())
1060 || (pNext->m_fPosition != m_fPosition)
1061 || (pNext->getVisDirection() != getVisDirection())
1062 // we also want to test the override, because we do not want runs that have the same
1063 // visual direction but different override merged
1064 || (pNext->m_iDirOverride != m_iDirOverride)
1065 // cannot merge two runs within different hyperlinks, but than
1066 // this can never happen, since between them alwasy will be at
1067 // least two hyperlink runs.
1068
1069 // cannot merge different scripts
1070 || (m_pRenderInfo && pNext->m_pRenderInfo
1071 && !m_pRenderInfo->canAppend(*(pNext->m_pRenderInfo)))
1072
1073 /* the revision evaluation is a bit more complex*/
1074 || (( getRevisions() != pNext->getRevisions()) // non-identical and one is null
1075 && (!getRevisions() || !pNext->getRevisions()))
1076 || (( getRevisions() && pNext->getRevisions())
1077 && !(*getRevisions() == *(pNext->getRevisions()))) //
1078 //non-null but different
1079 || (pNext->getVisibility() != getVisibility())
1080 // Different authors
1081 || (pNext->getAuthorNum() != getAuthorNum())
1082 // The merge must make just one item
1083 || (!isOneItem(pNext))
1084 #if 0
1085 // I do not think this should happen at all
1086 || ((pNext->m_bRenderInfo->isJustified() && m_bRenderInfo->isJustified())
1087 && (pNext->m_iSpaceWidthBeforeJustification != m_iSpaceWidthBeforeJustification))
1088 #endif
1089 )
1090 {
1091 xxx_UT_DEBUGMSG(("Falied to merge full test! \n"));
1092 #ifdef DEBUG
1093 // printText();
1094 #endif
1095 return false;
1096 }
1097
1098 //
1099 // Don't coalese past word boundaries
1100 // This improves lots of flicker issues
1101 //
1102 #if 0
1103 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
1104 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
1105 text.setPosition(getLength()-1);
1106 if(UT_UCS4_isspace(text.getChar()))
1107 {
1108 UT_DEBUGMSG(("Failed to merge space! length %d char |%d| \n",getLength(),text.getChar()));
1109 #ifdef DEBUG
1110 // printText();
1111 #endif
1112 return false;
1113 }
1114 #endif
1115 return true;
1116 }
1117
mergeWithNext(void)1118 void fp_TextRun::mergeWithNext(void)
1119 {
1120 UT_ASSERT(getNextRun() && (getNextRun()->getType() == FPRUN_TEXT));
1121 UT_ASSERT(getLine());
1122 UT_ASSERT(getNextRun()->getLine());
1123
1124 fp_TextRun* pNext = static_cast<fp_TextRun*>(getNextRun());
1125
1126 UT_ASSERT(pNext->getBlockOffset() == (getBlockOffset() + getLength()));
1127 UT_ASSERT(pNext->_getFont() == _getFont());
1128 UT_ASSERT(pNext->_getDecorations() == _getDecorations());
1129 UT_ASSERT(getAscent() == pNext->getAscent());
1130 UT_ASSERT(getDescent() == pNext->getDescent());
1131 UT_ASSERT(getHeight() == pNext->getHeight());
1132 UT_ASSERT(_getLineWidth() == pNext->_getLineWidth());
1133 UT_ASSERT(m_pLanguage == pNext->m_pLanguage); //this is not a bug; see m_pLanguage in
1134 //fp_TextRun.h before modifying this line
1135 UT_ASSERT(m_fPosition == pNext->m_fPosition);
1136 UT_ASSERT(m_iDirOverride == pNext->m_iDirOverride); //#TF
1137 //UT_ASSERT(m_iSpaceWidthBeforeJustification == pNext->m_iSpaceWidthBeforeJustification);
1138
1139 _setField(pNext->getField());
1140
1141 xxx_UT_DEBUGMSG(("fp_TextRun::mergeWithNext\n"));
1142 // first of all, make sure the X coordinance of the merged run is correct
1143
1144 if(getX() > pNext->getX())
1145 _setX(pNext->getX());
1146
1147 // can only adjust width after the justification has been handled
1148 _setWidth(getWidth() + pNext->getWidth());
1149 _setLength(getLength() + pNext->getLength());
1150 DELETEP(m_pRenderInfo);
1151 m_pRenderInfo = NULL;
1152 itemize();
1153 _setDirty(isDirty() || pNext->isDirty());
1154
1155 setNextRun(pNext->getNextRun(), false);
1156 if (getNextRun())
1157 {
1158 // do not mark anything dirty
1159 getNextRun()->setPrevRun(this, false);
1160 }
1161
1162 pNext->getLine()->removeRun(pNext, false);
1163 lookupProperties();
1164 setMustClearScreen();
1165 markDrawBufferDirty();
1166
1167 delete pNext;
1168
1169 }
1170
split(UT_uint32 iSplitOffset,UT_sint32 iLenSkip)1171 bool fp_TextRun::split(UT_uint32 iSplitOffset, UT_sint32 iLenSkip)
1172 {
1173 xxx_UT_DEBUGMSG(("fp_TextRun::split: iSplitOffset=%d\n", iSplitOffset));
1174 UT_ASSERT(iSplitOffset >= getBlockOffset());
1175 UT_ASSERT(iSplitOffset < (getBlockOffset() + getLength()));
1176
1177 UT_BidiCharType iVisDirection = getVisDirection();
1178 UT_sint32 iNewLen = static_cast<UT_sint32>(getLength()) - (static_cast<UT_sint32>(iSplitOffset) - static_cast<UT_sint32>(getBlockOffset()));
1179 UT_return_val_if_fail(iNewLen >= 0,false);
1180
1181 fp_TextRun* pNew = new fp_TextRun(getBlock(), iSplitOffset+static_cast<UT_uint32>(iLenSkip),static_cast<UT_uint32>(iNewLen), false);
1182
1183
1184 UT_ASSERT(pNew);
1185
1186 pNew->_setFont(this->_getFont());
1187
1188 pNew->_setDecorations(this->_getDecorations());
1189 pNew->_setColorFG(_getColorFG());
1190 pNew->_setColorHL(_getColorHL());
1191 pNew->_setField(this->getField());
1192 pNew->m_fPosition = this->m_fPosition;
1193 pNew->setTextTransform(this->getTextTransform());
1194
1195 pNew->_setAscent(this->getAscent());
1196 pNew->_setDescent(this->getDescent());
1197 pNew->_setHeight(this->getHeight());
1198 pNew->_setLineWidth(this->_getLineWidth());
1199 pNew->_setDirty(true);
1200 pNew->m_pLanguage = this->m_pLanguage;
1201 xxx_UT_DEBUGMSG(("!!!!--- Run %x gets Language pointer %x \n",pNew,pNew->m_pLanguage));
1202 pNew->_setDirection(this->_getDirection()); //#TF
1203 pNew->m_iDirOverride = this->m_iDirOverride;
1204 // set the visual direction to same as that of the old run
1205 pNew->setVisDirection(iVisDirection);
1206
1207 pNew->_setHyperlink(this->getHyperlink());
1208 pNew->setAuthorNum(this->getAuthorNum());
1209 // when revisions are present, this gets bit trickier
1210 if(getRevisions() != NULL)
1211 {
1212 // the revisions object cannot be shared, we have to
1213 // recreate one
1214 // TODO -- this is not a very efficient way of doing
1215 // this, it would be preferable to design copy constructors
1216 // for PP_Revision and PP_RevisionAttr and use the copy
1217 // constructor, but for no this will do
1218 pNew->_setRevisions(new PP_RevisionAttr(getRevisions()->getXMLstring()));
1219 }
1220
1221 pNew->setVisibility(this->getVisibility());
1222
1223 // do not force recalculation of the draw buffer and widths
1224 pNew->setPrevRun(this, false);
1225 pNew->setNextRun(this->getNextRun(), false);
1226 if (getNextRun())
1227 {
1228 // do not mark anything dirty, just the next run since the clearscreen
1229 // from this split has cleared a bit of the next run.
1230
1231 getNextRun()->setPrevRun(pNew, false);
1232 getNextRun()->markAsDirty();
1233 }
1234 setNextRun(pNew, false);
1235
1236 // reitemize this run and blow away all the old render info. It has to be
1237 // recalculated.
1238
1239 setLength(iSplitOffset - getBlockOffset(), false);
1240 DELETEP(m_pRenderInfo);
1241 itemize();
1242 lookupProperties();
1243 // Reitemize the new run
1244 pNew->itemize();
1245
1246 if(getLine())
1247 getLine()->insertRunAfter(pNew, this);
1248
1249 //have to recalculate these
1250
1251 recalcWidth();
1252 pNew->recalcWidth();
1253
1254 //bool bDomDirection = getBlock()->getDominantDirection();
1255
1256 if(iVisDirection == UT_BIDI_LTR)
1257 {
1258 pNew->_setX(getX() + getWidth());
1259 }
1260 else
1261 {
1262 pNew->_setX(getX());
1263 _setX(getX() + pNew->getWidth());
1264 }
1265
1266 pNew->_setY(getY());
1267 return true;
1268 }
1269
1270
getDirection() const1271 UT_BidiCharType fp_TextRun:: getDirection() const
1272 { return m_iDirOverride == static_cast<UT_BidiCharType>(UT_BIDI_UNSET) ? _getDirection() : m_iDirOverride;}
1273
1274
simpleRecalcWidth(UT_sint32 iLength)1275 UT_sint32 fp_TextRun::simpleRecalcWidth(UT_sint32 iLength)
1276 {
1277
1278 if(iLength == Calculate_full_width)
1279 {
1280 iLength = getLength();
1281 }
1282 UT_ASSERT(iLength >= 0);
1283
1284 UT_ASSERT(static_cast<UT_uint32>(iLength) <= getLength());
1285 if(static_cast<UT_uint32>(iLength) > getLength())
1286 iLength = static_cast<UT_sint32>(getLength());
1287
1288 if (iLength == 0)
1289 return 0;
1290
1291 _refreshDrawBuffer();
1292 UT_return_val_if_fail(m_pRenderInfo,0);
1293
1294 m_pRenderInfo->m_iOffset = 0;
1295 m_pRenderInfo->m_iLength = getLength();
1296 UT_sint32 iWidth = getGraphics()->getTextWidth(*m_pRenderInfo);
1297
1298 return iWidth;
1299 }
1300
1301 /*!
1302 measures widths of individual characters in our draw buffer,
1303 stores them in the block's width cache and recalculates overall width.
1304 */
measureCharWidths()1305 void fp_TextRun::measureCharWidths()
1306 {
1307 _setWidth(0);
1308 UT_return_if_fail(m_pRenderInfo);
1309
1310 m_pRenderInfo->m_iVisDir = getVisDirection();
1311 m_pRenderInfo->m_iOffset = getBlockOffset();
1312 m_pRenderInfo->m_iLength = getLength();
1313 m_pRenderInfo->m_pFont = _getFont();
1314
1315 getGraphics()->setFont(_getFont());
1316 getGraphics()->measureRenderedCharWidths(*m_pRenderInfo);
1317
1318 _addupCharWidths();
1319 _setRecalcWidth(false);
1320 }
1321
1322 /*!
1323 Recalculates the width of our run, updating the block's width
1324 cache as required.
1325
1326 \return returns true if the width of this run changed
1327 */
_recalcWidth(void)1328 bool fp_TextRun::_recalcWidth(void)
1329 {
1330 // _refreshDrawBuffer() takes care of recalculating width since
1331 // the width and the content of the buffer are linked. if the
1332 // buffer is uptodate, but width is dirty, we just need to add up
1333 // the widths in the block cache
1334 //
1335 // NB: the order of the calls is important, because invalidation
1336 // of draw buffer automatically means invalidation of width;
1337 // however, width can be dirty when the buffer is clean
1338 // (e.g. after a call to updateOnDelete())
1339
1340 UT_sint32 iWidth = getWidth();
1341
1342 if(_refreshDrawBuffer())
1343 {
1344 if(iWidth != getWidth())
1345 return true;
1346 else
1347 return false;
1348 }
1349
1350 if(_getRecalcWidth())
1351 {
1352 return _addupCharWidths();
1353 }
1354
1355 return false;
1356 }
1357
1358 // this function is just like recalcWidth, except it does not change the character width
1359 // information kept by the block, but assumes that information is correct.
_addupCharWidths(void)1360 bool fp_TextRun::_addupCharWidths(void)
1361 {
1362 UT_sint32 iWidth = 0;
1363
1364 if(m_pRenderInfo == NULL)
1365 return false;
1366 #ifdef DEBUG
1367 xxx_UT_DEBUGMSG(("_addupCharWidths() \n"));
1368 //printText();
1369 #endif
1370
1371 m_pRenderInfo->m_iOffset = 0;
1372 m_pRenderInfo->m_iLength = getLength();
1373 m_pRenderInfo->m_pFont = _getFont();
1374
1375 iWidth = getGraphics()->getTextWidth(*m_pRenderInfo);
1376
1377 if(iWidth != getWidth())
1378 {
1379 _setWidth(iWidth);
1380 return true;
1381 }
1382
1383 return false;
1384 }
1385
_clearScreen(bool)1386 void fp_TextRun::_clearScreen(bool /* bFullLineHeightRect */)
1387 {
1388 // UT_ASSERT(!isDirty());
1389 UT_ASSERT(getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN));
1390 UT_sint32 iExtra = 0;
1391 if(getWidth() == 0)
1392 {
1393 //
1394 // Can't clear if the width is 0
1395 //
1396 return;
1397 }
1398 if(!getLine()->isEmpty() && getLine()->getLastVisRun() == this) //#TF must be last visual run
1399 {
1400 // Last run on the line so clear to end.
1401 if(isSelectionDraw())
1402 {
1403 const UT_Rect *pRect = getGraphics()->getClipRect();
1404 if(pRect)
1405 {
1406 UT_Rect r = *pRect;
1407 r.width += getGraphics()->tlu(5);
1408 iExtra += getGraphics()->tlu(5);
1409 getGraphics()->setClipRect(&r);
1410 }
1411 }
1412 else
1413 {
1414 iExtra = getLine()->getMaxWidth() - getX() - getWidth();
1415 if(iExtra <= 0)
1416 {
1417 iExtra = getGraphics()->tlu(1);
1418 }
1419 }
1420 }
1421
1422 getGraphics()->setFont(_getFont());
1423 /*
1424 TODO this should not be hard-coded. We need to figure out
1425 what the appropriate background color for this run is, and
1426 use that. Note that it could vary on a run-by-run basis,
1427 since document facilities allow the background color to be
1428 changed, for things such as table cells.
1429 */
1430 // we need to use here page color, not highlight color, otherwise
1431 // we endup with higlighted margin
1432 //UT_RGBColor clrNormalBackground(m_colorHL.m_red, m_colorHL.m_grn, m_colorHL.m_blu);
1433 UT_RGBColor clrNormalBackground(_getColorPG());
1434 if (getField())
1435 {
1436 clrNormalBackground -= _getView()->getColorFieldOffset();
1437 }
1438 getGraphics()->setColor(clrNormalBackground);
1439
1440 UT_sint32 xoff = 0, yoff = 0;
1441 getLine()->getScreenOffsets(this, xoff, yoff);
1442
1443 //
1444 // Handle case where character extend behind the left side
1445 // like italic Times New Roman f
1446 //
1447 fp_Line * thisLine = getLine();
1448 fp_Run * pPrev = getPrevRun();
1449 fp_Run * pNext = getNextRun();
1450 // UT_sint32 leftClear = getAscent()/2;
1451 UT_sint32 leftClear = getDescent();
1452 if(isSelectionDraw())
1453 {
1454 leftClear = 0;
1455 }
1456 UT_sint32 rightClear = getDescent() + iExtra;
1457
1458 UT_sint32 iCumWidth = leftClear;
1459 if(thisLine != NULL)
1460 {
1461 // TODO -- this needs to be done in vis. space !!!
1462
1463 while(pPrev != NULL && pPrev->getLine() == thisLine &&
1464 (pPrev->getLength() == 0 || iCumWidth > 0))
1465 {
1466 // only substract the width of this run, if it is already on screen
1467 // (newly inserted runs that are not on screen have TmpWidth == 0)
1468 if(pPrev->getTmpWidth())
1469 iCumWidth -= pPrev->getWidth();
1470
1471 if(!isSelectionDraw())
1472 {
1473 pPrev->markAsDirty();
1474 }
1475 pPrev = pPrev->getPrevRun();
1476 }
1477
1478 iCumWidth = rightClear;
1479 // UT_sint32 iEx = getGraphics()->tlu(2);
1480 while(pNext != NULL && pNext->getLine() == thisLine &&
1481 (pNext->getLength() == 0 || iCumWidth > 0))
1482 {
1483 if(pNext->getTmpWidth())
1484 iCumWidth -= pNext->getWidth();
1485
1486 if(!isSelectionDraw())
1487 {
1488 pNext->markAsDirty();
1489 }
1490 pNext = pNext->getNextRun();
1491 }
1492 }
1493 Fill(getGraphics(),xoff - leftClear, yoff, getWidth() + leftClear + rightClear,
1494 getLine()->getHeight());
1495 xxx_UT_DEBUGMSG(("leftClear = %d total width = %d xoff %d height %d \n",
1496 leftClear,getWidth()+leftClear+rightClear,xoff,getLine()->getHeight()));
1497
1498 }
getLanguage() const1499 const gchar * fp_TextRun::getLanguage() const
1500 {
1501 return m_pLanguage;
1502 }
1503
1504
_draw(dg_DrawArgs * pDA)1505 void fp_TextRun::_draw(dg_DrawArgs* pDA)
1506 {
1507 /*
1508 Upon entry to this function, pDA->yoff is the BASELINE of this run, NOT
1509 the top.
1510 */
1511
1512 if(getLength() == 0)
1513 return;
1514
1515 GR_Graphics * pG = pDA->pG;
1516
1517 GR_Painter painter(pG);
1518 //
1519 // If a refresh was performed, we lose our full justification. If this is done
1520 // and we have full justification we abort, reformat the paragraph and redraw.
1521 //
1522 /*bool bRefresh =*/ _refreshDrawBuffer();
1523
1524 xxx_UT_DEBUGMSG(("fp_TextRun::_draw (0x%x): m_iVisDirection %d, _getDirection() %d\n",
1525 this, m_iVisDirection, _getDirection()));
1526
1527 #if 0
1528 // BAD, BAD HACK, please do not re-enable this.
1529 //
1530 // This causes bad pixed dirt with characters that fill the entire glyph box. We
1531 // really cannot, under any circumstances, adjust the y coords -- the y coordinance is
1532 // the base coordinace of the text, from which everything else derives (if we adust
1533 // it, it means that the text gets drawn at wrong position !).
1534 //
1535 // Also, the comment that accompanies this hack makes no sense: how does adjusting y
1536 // coordinance remove final character dirt?
1537 // Tomas, Christmas Eve, 2004 (I know, I should get life)
1538 UT_sint32 yTopOfRun = pDA->yoff - getAscent() - pG->tlu(1); // Hack to remove
1539 UT_sint32 yTopOfSel = yTopOfRun + pG->tlu(1); // final character dirt
1540 #else
1541 UT_sint32 yTopOfRun = pDA->yoff - getAscent();
1542 // UT_sint32 yTopOfRun = pDA->yoff - pG->getFontAscent(_getFont());
1543 UT_sint32 yTopOfSel = yTopOfRun;
1544 #endif
1545 xxx_UT_DEBUGMSG(("_draw Text: yoff %d \n",pDA->yoff));
1546 xxx_UT_DEBUGMSG(("_draw Text: getAscent %d fontAscent-1 %d fontAscent-2 %d \n",getAscent(),pG->getFontAscent(_getFont()),pG->getFontAscent()));
1547 /*
1548 TODO We should add more possibilities for text placement here.
1549 It shouldn't be too hard. Just adjust the math a little.
1550 See bug 1297
1551 */
1552 //
1553 // This makes sure the widths don't change underneath us after a zoom.
1554 //
1555 m_bKeepWidths = true;
1556 UT_sint32 iWidth = getWidth();
1557 xxx_UT_DEBUGMSG(("textRun Xoff %d width %d \n",pDA->xoff,iWidth));
1558 #if DEBUG
1559 //printText();
1560 #endif
1561 //
1562 // This code makes sure we don't fill past the right edge of text.
1563 // Full Justified text often as a space at the right edge of the text.
1564 // This space is counted in the width even though the text is aligned
1565 // to the correct edge.
1566 //
1567 UT_Rect * pLRec = getLine()->getScreenRect();
1568 if (pLRec == NULL)
1569 return;
1570 if((pDA->xoff + iWidth) > (pLRec->left + pLRec->width))
1571 {
1572 iWidth -= (pDA->xoff + iWidth) - (pLRec->left + pLRec->width);
1573 }
1574 delete pLRec;
1575 Fill(pG,pDA->xoff,yTopOfSel + getAscent() - getLine()->getAscent(),
1576 iWidth,
1577 getLine()->getHeight());
1578 m_bKeepWidths = false;
1579
1580 if (m_fPosition == TEXT_POSITION_SUPERSCRIPT)
1581 {
1582 yTopOfRun -= getAscent() * 1/2;
1583 }
1584 else if (m_fPosition == TEXT_POSITION_SUBSCRIPT)
1585 {
1586 yTopOfRun += getDescent() /* * 3/2 */;
1587 }
1588
1589
1590 ////////////////////////////////////////////////////////////////////
1591 //
1592 // Here we handle run background ..
1593 //
1594 //
1595 // the old way of doing things was very inefficient; for each chunk of this
1596 // run that had a different background we first drew the background rectangle,
1597 // then drew the text over it, and then moved onto the next chunk. This is very
1598 // involved since to draw the text in pieces we have to calculate the screen
1599 // offset of each chunk using character widths.
1600 //
1601 // This is is what we will do instead:
1602 // (1) draw the background using the background colour for the whole run in
1603 // a single go
1604 // (2) draw any selection background where needed over the basic background
1605 // (3) draw the whole text in a single go over the composite background
1606
1607 UT_RGBColor clrNormalBackground(_getColorHL());
1608 UT_RGBColor clrSelBackground = _getView()->getColorSelBackground();
1609
1610 if (getField())
1611 {
1612 UT_RGBColor color_offset = _getView()->getColorFieldOffset();
1613 clrNormalBackground -= color_offset;
1614 clrSelBackground -= color_offset;
1615 }
1616 // calculate selection rectangles ...
1617 UT_uint32 iBase = getBlock()->getPosition();
1618 UT_uint32 iRunBase = iBase + getBlockOffset();
1619 bool bIsInTOC = getBlock()->isContainedByTOC();
1620 FV_View* pView = getBlock()->getDocLayout()->getView();
1621 UT_uint32 iSelAnchor = pView->getSelectionAnchor();
1622 UT_uint32 iPoint = pView->getPoint();
1623
1624 UT_uint32 iSel1 = UT_MIN(iSelAnchor, iPoint);
1625 UT_uint32 iSel2 = UT_MAX(iSelAnchor, iPoint);
1626 //
1627 // Handle fully selected cells
1628 //
1629 if(pView->getSelectionMode() > FV_SelectionMode_Multiple)
1630 {
1631 fl_ContainerLayout * pCL = getBlock()->myContainingLayout();
1632 if(pCL->getContainerType() == FL_CONTAINER_CELL)
1633 {
1634 fl_CellLayout * pCell = static_cast<fl_CellLayout *>(pCL);
1635 if(pCell->isCellSelected())
1636 {
1637 iSel1 = iRunBase;
1638 iSel2 = iRunBase+getLength();
1639 }
1640 else
1641 {
1642 iSel1 = iRunBase-1;
1643 iSel2 = iSel1;
1644 }
1645 }
1646 else
1647 {
1648 iSel1 = iRunBase-1;
1649 iSel2 = iSel1;
1650 }
1651 }
1652 UT_ASSERT(iSel1 <= iSel2);
1653
1654 // we shall remember the nature of the selection, so we do not
1655 // have to consider it later again; for each segment we will
1656 // remember if it is selected or not, where in the run it starts,
1657 // and how wide it is
1658 UT_uint32 iSegmentCount = 1;
1659 UT_uint32 iSegmentOffset[4]; //the fourth segment is only would-be ...
1660 bool bSegmentSelected[3];
1661 UT_uint32 iSegmentWidth[3];
1662 UT_Rect rSegment;
1663
1664 iSegmentOffset[0] = 0;
1665 iSegmentOffset[1] = iSegmentOffset[3] = getLength();
1666 bSegmentSelected[0] = false;
1667 iSegmentWidth[0] = iWidth;
1668
1669 if (/* pView->getFocus()!=AV_FOCUS_NONE && */ !bIsInTOC && (iSel1 != iSel2) && pG->queryProperties(GR_Graphics::DGP_SCREEN))
1670 {
1671 if (iSel1 <= iRunBase)
1672 {
1673 if (iSel2 > iRunBase)
1674 {
1675 if (iSel2 >= (iRunBase + getLength()))
1676 {
1677 // the whole run is selected
1678 _fillRect(clrSelBackground, pDA->xoff, yTopOfSel, getBlockOffset(), getLength(), rSegment,pG);
1679 bSegmentSelected[0] = true;
1680 }
1681 else
1682 {
1683 // the first part is selected, the second part is not
1684 _fillRect(clrSelBackground, pDA->xoff, yTopOfSel, getBlockOffset(), iSel2 - iRunBase, rSegment,pG);
1685 iSegmentCount = 2;
1686 bSegmentSelected[0] = true;
1687 bSegmentSelected[1] = false;
1688 iSegmentOffset[1] = iSel2 - iRunBase;
1689 iSegmentOffset[2] = getLength();
1690 iSegmentWidth[0] = rSegment.width;
1691 iSegmentWidth[1] = iWidth - rSegment.width;
1692 }
1693 }
1694 }
1695 else if (iSel1 < (iRunBase + getLength()))
1696 {
1697 if (iSel2 >= (iRunBase + getLength()))
1698 {
1699 // the second part is selected
1700 _fillRect(clrSelBackground, pDA->xoff, yTopOfSel, iSel1 - iBase, getLength() - (iSel1 - iRunBase), rSegment,pG);
1701
1702 iSegmentCount = 2;
1703 bSegmentSelected[0] = false;
1704 bSegmentSelected[1] = true;
1705 iSegmentOffset[1] = iSel1 - iRunBase;
1706 iSegmentOffset[2] = getLength();
1707 iSegmentWidth[0] = iWidth - rSegment.width;
1708 iSegmentWidth[1] = rSegment.width;
1709 }
1710 else
1711 {
1712 // a midle section is selected
1713 _fillRect(clrSelBackground, pDA->xoff, yTopOfSel, iSel1 - iBase, iSel2 - iSel1, rSegment,pG);
1714
1715 iSegmentCount = 3;
1716 bSegmentSelected[0] = false;
1717 bSegmentSelected[1] = true;
1718 bSegmentSelected[2] = false;
1719 iSegmentOffset[1] = iSel1 - iRunBase;
1720 iSegmentOffset[2] = iSel2 - iRunBase;
1721 iSegmentWidth[1] = rSegment.width;
1722
1723 if(getVisDirection() == UT_BIDI_LTR)
1724 {
1725 iSegmentWidth[0] = rSegment.left - pDA->xoff;
1726 iSegmentWidth[2] = iWidth - (rSegment.width + iSegmentWidth[0]);
1727 }
1728 else
1729 {
1730 iSegmentWidth[2] = rSegment.left - pDA->xoff;
1731 iSegmentWidth[0] = iWidth - (rSegment.width + iSegmentWidth[2]);
1732 }
1733 }
1734 }
1735 }
1736 if(isInSelectedTOC())
1737 {
1738 _fillRect(clrSelBackground, pDA->xoff, yTopOfSel, getBlockOffset(), getLength(), rSegment,pG);
1739 iSel1 = iRunBase;
1740 iSel2 = iRunBase+getLength();
1741 bSegmentSelected[0] = true;
1742 }
1743 // fill our GR_RenderInfo with needed data ...
1744 UT_return_if_fail(m_pRenderInfo);
1745
1746 UT_uint32 iLen = getLength();
1747 m_pRenderInfo->m_iLength = iLen;
1748
1749
1750 // if iLen is 0, there is nothing to draw; this sometimes happens,
1751 // and is probably legal ...
1752 UT_return_if_fail(m_pRenderInfo->m_iLength);
1753
1754 m_pRenderInfo->m_xoff = pDA->xoff;
1755 m_pRenderInfo->m_yoff = yTopOfRun;
1756
1757 m_pRenderInfo->m_pGraphics = pG;
1758
1759 // HACK for the built-in shaping engine
1760 if(m_pRenderInfo->getType() == GRRI_XP)
1761 {
1762 // the built in shaping engine replaces second part of each
1763 // ligature with a placeholder which gets striped before the
1764 // drawing. As a result, the offsets of the segments we
1765 // calculated above need to be adjusted
1766 GR_XPRenderInfo * pRI = (GR_XPRenderInfo *) m_pRenderInfo;
1767 pRI->m_pSegmentOffset = reinterpret_cast<UT_sint32 *>(&iSegmentOffset[0]);
1768 pRI->m_iSegmentCount = iSegmentCount;
1769 }
1770
1771 // this is needed by the built-in shaping engine
1772 // the construction is not very expensive but once that shaper is
1773 // deprecated, we might be able to get rid of it
1774 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
1775 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
1776
1777 UT_sint32 iIterPos = text.getPosition(); // need to remember for drawing selections
1778
1779 m_pRenderInfo->m_pText = &text;
1780 m_pRenderInfo->m_pFont = _getFont();
1781
1782 /*
1783 if the text on either side of this run is in italics, there is a good
1784 chance that the background covered some of the text; to fix this we
1785 call _drawLastChar and _drawFirstChar for those runs, but we will not do
1786 so undiscriminately -- we need to do this only if the prev or next
1787 runs are overhanging (i.e., italicized) and if we are in a screen-like
1788 context (screen-like context is one in which drawing a white rectangle
1789 over a piece of text results in the text being erased; apart from
1790 the sreen, a Windows printer can behave like this).
1791 */
1792
1793 if(pG->queryProperties(GR_Graphics::DGP_SCREEN) && pG->queryProperties(GR_Graphics::DGP_OPAQUEOVERLAY))
1794 {
1795 fp_Run * pNext = getNextVisual();
1796 fp_Run * pPrev = getPrevVisual();
1797
1798 if(pNext && pNext->getType() == FPRUN_TEXT)
1799 {
1800 fp_TextRun * pT = static_cast<fp_TextRun*>(pNext);
1801 UT_sint32 ytemp = pDA->yoff+(pT->getY()-getY())-pT->getAscent()-pG->tlu(1);
1802 if (pT->m_fPosition == TEXT_POSITION_SUPERSCRIPT)
1803 {
1804 ytemp -= pT->getAscent() * 1/2;
1805 }
1806 else if (pT->m_fPosition == TEXT_POSITION_SUBSCRIPT)
1807 {
1808 ytemp += pT->getDescent() /* * 3/2 */;
1809 }
1810 if(!isSelectionDraw())
1811 {
1812 if(pT->m_bIsOverhanging)
1813 {
1814 UT_uint32 iDocOffset = pT->getBlock()->getPosition() + pT->getBlockOffset();
1815 bool bSel = (iSel1 <= iDocOffset && iSel2 > iDocOffset);
1816
1817 UT_ASSERT( pT->m_pRenderInfo );
1818 if(pT->m_pRenderInfo)
1819 {
1820 pT->m_pRenderInfo->m_xoff = pDA->xoff + iWidth;
1821 pT->m_pRenderInfo->m_yoff = ytemp;
1822
1823 pT->_drawFirstChar(bSel);
1824 }
1825 }
1826 }
1827
1828 }
1829
1830 if(pPrev && pPrev->getType() == FPRUN_TEXT)
1831 {
1832 fp_TextRun * pT = static_cast<fp_TextRun*>(pPrev);
1833 UT_sint32 ytemp = pDA->yoff+(pT->getY()-getY())-pT->getAscent()-pG->tlu(1);
1834 if (pT->m_fPosition == TEXT_POSITION_SUPERSCRIPT)
1835 {
1836 ytemp -= pT->getAscent() * 1/2;
1837 }
1838 else if (pT->m_fPosition == TEXT_POSITION_SUBSCRIPT)
1839 {
1840 ytemp += pT->getDescent() /* * 3/2 */;
1841 }
1842 if(!isSelectionDraw())
1843 {
1844 if(pT->m_bIsOverhanging)
1845 {
1846 UT_uint32 iDocOffset = pT->getBlock()->getPosition() + pT->getBlockOffset() + pT->getLength() - 1;
1847 bool bSel = (iSel1 <= iDocOffset && iSel2 > iDocOffset);
1848
1849 UT_ASSERT( pT->m_pRenderInfo );
1850 if(pT->m_pRenderInfo)
1851 {
1852 pT->m_pRenderInfo->m_xoff = pDA->xoff;
1853 pT->m_pRenderInfo->m_yoff = ytemp;
1854
1855 pT->_drawLastChar(bSel);
1856 }
1857
1858 }
1859 }
1860 }
1861 }
1862
1863 // now draw the string
1864 m_pRenderInfo->m_iOffset = 0;
1865 m_pRenderInfo->m_iLength = getLength();
1866 m_pRenderInfo->m_pFont = _getFont();
1867
1868 pG->prepareToRenderChars(*m_pRenderInfo);
1869 pG->setFont(_getFont());
1870
1871 // if there is a selection, we will need to draw the text in
1872 // segments due to different colour of the foreground
1873 UT_sint32 iX = pDA->xoff;
1874
1875 UT_BidiCharType iVisDir = getVisDirection();
1876 if(iVisDir == UT_BIDI_RTL)
1877 {
1878 // iX += getWidth();
1879 iX += iWidth;
1880 }
1881 UT_ASSERT(iSegmentCount > 0);
1882 for(UT_uint32 iSegment = 0; iSegment < iSegmentCount; iSegment++)
1883 {
1884 if(bSegmentSelected[iSegment])
1885 {
1886 pG->setColor(_getView()->getColorSelForeground());
1887 }
1888 else
1889 {
1890 pG->setColor(getFGColor());
1891 }
1892
1893 UT_uint32 iMyOffset = iVisDir == UT_BIDI_RTL ?
1894 iLen-iSegmentOffset[iSegment+1] :
1895 iSegmentOffset[iSegment];
1896
1897 if(iVisDir == UT_BIDI_RTL)
1898 iX -= iSegmentWidth[iSegment];
1899
1900 // reset the iterator
1901 text.setPosition(iIterPos);
1902 m_pRenderInfo->m_iOffset = iMyOffset;
1903 m_pRenderInfo->m_iLength = iSegmentOffset[iSegment+1]-iSegmentOffset[iSegment];
1904 m_pRenderInfo->m_xoff = iX;
1905 m_pRenderInfo->m_yoff = yTopOfRun;
1906 xxx_UT_DEBUGMSG((" _drawText yTopOfRun %d \n",yTopOfRun));
1907 xxx_UT_DEBUGMSG(("_drawText segment %d off %d length %d width %d \n",iSegment,iMyOffset,m_pRenderInfo->m_iLength ,iSegmentWidth[iSegment]));
1908 painter.renderChars(*m_pRenderInfo);
1909
1910 #if 0
1911 //DEBUG
1912 const GR_Font * f = _getFont();
1913 UT_uint32 _ascent, _descent, _height;
1914
1915 _ascent = pG->getFontAscent(f);
1916 _descent = pG->getFontDescent(f);
1917 _height = pG->getFontHeight(f);
1918
1919 UT_DEBUGMSG(("_drawText font %s ascent = %u height = %u descent = %u\n", f->hashKey().c_str(),
1920 _ascent, _height, _descent));
1921 painter.drawLine(iX, pDA->yoff - _ascent, iX + iSegmentWidth[iSegment], pDA->yoff - _ascent);
1922 painter.drawLine(iX, pDA->yoff, iX + iSegmentWidth[iSegment], pDA->yoff);
1923 painter.drawLine(iX, pDA->yoff + _descent, iX + iSegmentWidth[iSegment], pDA->yoff + _descent);
1924 //end DEBUG
1925 #endif
1926 if(iVisDir == UT_BIDI_LTR)
1927 iX += iSegmentWidth[iSegment];
1928 }
1929
1930 xxx_UT_DEBUGMSG(("_draw text yoff %d yTopOfRun %d \n",pDA->yoff,yTopOfRun));
1931 drawDecors(pDA->xoff, yTopOfRun,pG);
1932
1933 if(pView->getShowPara())
1934 {
1935 _drawInvisibles(pDA->xoff, yTopOfRun);
1936 }
1937
1938 #ifdef ENABLE_SPELL
1939 // TODO: draw this underneath (ie, before) the text and decorations
1940 if(pG->queryProperties(GR_Graphics::DGP_SCREEN))
1941 {
1942 m_bSpellSquiggled = false;
1943 getBlock()->findSpellSquigglesForRun(this);
1944 m_bGrammarSquiggled = false;
1945 getBlock()->findGrammarSquigglesForRun(this);
1946 }
1947 #endif
1948 }
1949
_fillRect(UT_RGBColor & clr,UT_sint32 xoff,UT_sint32 yoff,UT_uint32 iPos1,UT_uint32 iLen,UT_Rect & r,GR_Graphics *)1950 void fp_TextRun::_fillRect(UT_RGBColor& clr,
1951 UT_sint32 xoff,
1952 UT_sint32 yoff,
1953 UT_uint32 iPos1,
1954 UT_uint32 iLen,
1955 UT_Rect &r,
1956 GR_Graphics * /*pG*/)
1957 {
1958 /*
1959 Upon entry to this function, yoff is the TOP of the run,
1960 NOT the baseline.
1961 */
1962 // we also need to support this in printing
1963 // NO! we do not and must not -- this is only used to draw selections
1964 // and field background and we do not want to see these printed
1965 if (getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN))
1966 {
1967 //UT_Rect r;
1968 xxx_UT_DEBUGMSG(("Doing _fillRect red %d blue %d green %d\n",clr.m_red,clr.m_blu,clr.m_grn));
1969 _getPartRect(&r, xoff, yoff, iPos1, iLen);
1970 r.height = getLine()->getHeight();
1971 r.top = r.top + getAscent() - getLine()->getAscent();
1972 GR_Painter painter(getGraphics());
1973 painter.fillRect(clr, r.left, r.top, r.width, r.height);
1974 }
1975 }
1976
_getPartRect(UT_Rect * pRect,UT_sint32 xoff,UT_sint32 yoff,UT_uint32 iStart,UT_uint32 iLen)1977 void fp_TextRun::_getPartRect(UT_Rect* pRect,
1978 UT_sint32 xoff,
1979 UT_sint32 yoff,
1980 UT_uint32 iStart,
1981 UT_uint32 iLen)
1982 {
1983 /*
1984 Upon entry to this function, yoff is the TOP of the run,
1985 NOT the baseline.
1986 */
1987 pRect->top = yoff;
1988 pRect->height = getHeight();
1989 pRect->width = 0;
1990
1991 // that's enough for zero-length run
1992 if (getLength() == 0)
1993 {
1994 pRect->left = xoff;
1995 return;
1996 }
1997
1998 pRect->left = 0;//#TF need 0 because of BiDi, need to calculate the width of the non-selected
1999 //section first rather than the abs pos of the left corner
2000
2001 if(!m_pRenderInfo || _getRefreshDrawBuffer() == GRSR_Unknown)
2002 {
2003 _refreshDrawBuffer();
2004 }
2005
2006 UT_return_if_fail(m_pRenderInfo);
2007
2008 if(iStart > getBlockOffset())
2009 {
2010 m_pRenderInfo->m_iOffset = 0;
2011 m_pRenderInfo->m_iLength = iStart - getBlockOffset();
2012 pRect->left = getGraphics()->getTextWidth(*m_pRenderInfo);
2013 }
2014
2015 if(getVisDirection() == UT_BIDI_LTR)
2016 {
2017 pRect->left += xoff; //if this is ltr then adding xoff is all that is needed
2018 }
2019
2020 m_pRenderInfo->m_iOffset = iStart - getBlockOffset();
2021 m_pRenderInfo->m_iLength = iLen;
2022 pRect->width = getGraphics()->getTextWidth(*m_pRenderInfo);
2023 UT_ASSERT(pRect->left < 10000000);
2024 UT_ASSERT(pRect->width >= 0);
2025
2026 //in case of rtl we are now in the position to calculate the position of the the left corner
2027 if(getVisDirection() == UT_BIDI_RTL)
2028 pRect->left = xoff + getWidth() - pRect->left - pRect->width;
2029
2030 //
2031 // This code makes sure we don't fill past the right edge of text.
2032 // Full Justified text often as a space at the right edge of the text.
2033 // This space is counted in the width even though the text is aligned
2034 // to the correct edge.
2035 //
2036 if(getLine())
2037 {
2038 UT_Rect * pLRec = getLine()->getScreenRect();
2039 if(!pLRec)
2040 return;
2041 if(getLine()->getContainer() && ((getLine()->getContainer()->getContainerType() == FP_CONTAINER_CELL) ||
2042 (getLine()->getContainer()->getContainerType() == FP_CONTAINER_FRAME)))
2043 return;
2044 if((pRect->left + pRect->width) > (pLRec->left + pLRec->width))
2045 {
2046 pRect->width -= (pRect->left + pRect->width) - (pLRec->left + pLRec->width);
2047 }
2048 delete pLRec;
2049 }
2050 UT_ASSERT(pRect->left < 10000000);
2051 UT_ASSERT(pRect->width >= 0);
2052 xxx_UT_DEBUGMSG(("part Rect left %d width %d \n",pRect->left,pRect->width));
2053 }
2054
getPreviousInterestingRunForCapitalization(fp_Run * self)2055 static fp_Run* getPreviousInterestingRunForCapitalization(fp_Run* self) {
2056 if (self == NULL)
2057 return NULL;
2058
2059 if (self->getType() == FPRUN_FMTMARK)
2060 return getPreviousInterestingRunForCapitalization(self->getPrevRun());
2061
2062 return self;
2063 }
2064
2065 /*!
2066 Determines if the draw buffer (the run's cache of the text it
2067 draws on screen) is uptodate or not and recalculates it as
2068 required. If the contents of the buffer change, the block's width
2069 cache is updated and overall width recalculated.
2070
2071 \return returns true if the buffer was modified
2072 */
_refreshDrawBuffer()2073 bool fp_TextRun::_refreshDrawBuffer()
2074 {
2075 // see if there is an overlap between the dirtiness of the present
2076 // buffer and the shaping requirenments of the text it represents
2077
2078 UT_uint32 iLen = getLength();
2079 GRShapingResult eRefresh = _getRefreshDrawBuffer();
2080
2081 bool bRefresh = true;
2082
2083 if(m_pRenderInfo)
2084 {
2085 bRefresh = ((UT_uint32)eRefresh & (UT_uint32)m_pRenderInfo->m_eShapingResult) != 0;
2086 }
2087
2088 if(iLen && bRefresh)
2089 {
2090 UT_return_val_if_fail(m_pItem, false);
2091
2092 UT_BidiCharType iVisDir = getVisDirection();
2093 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2094 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2095
2096 bool lastWasSpace = false;
2097
2098 if (getTextTransform() == GR_ShapingInfo::CAPITALIZE) {
2099 fp_Run* prevRun = getPreviousInterestingRunForCapitalization(this->getPrevRun());
2100 if (prevRun == NULL) {
2101 lastWasSpace = true;
2102 }
2103 else if (prevRun->getType() != FPRUN_TEXT) {
2104 lastWasSpace = true;
2105 }
2106 else if (prevRun->getType() == FPRUN_TEXT) {
2107 UT_GrowBuf buf;
2108 static_cast<fp_TextRun*>(prevRun)->appendTextToBuf(buf);
2109
2110 if (buf.getLength() != 0) {
2111 UT_GrowBufElement* elem = buf.getPointer(buf.getLength() - 1);
2112 lastWasSpace = g_unichar_isspace(*elem);
2113 }
2114 }
2115 }
2116
2117 GR_ShapingInfo si(text,iLen, m_pLanguage, iVisDir,
2118 m_pRenderInfo ? m_pRenderInfo->m_eShapingResult : GRSR_Unknown,
2119 _getFont(), m_pItem, getTextTransform(), lastWasSpace);
2120 getGraphics()->shape(si, m_pRenderInfo);
2121
2122 UT_ASSERT(m_pRenderInfo && m_pRenderInfo->m_eShapingResult != GRSR_Error );
2123
2124 // if we are on a non-bidi OS, we have to reverse any RTL runs
2125 // if we are on bidi OS, we have to reverse RTL runs that have direction
2126 // override set to LTR, to preempty to OS reversal of such
2127 // text
2128 if(m_pRenderInfo->getType() == GRRI_XP)
2129 {
2130 GR_XPRenderInfo * pRI = (GR_XPRenderInfo *) m_pRenderInfo;
2131
2132 if((!s_bBidiOS && iVisDir == UT_BIDI_RTL)
2133 || (s_bBidiOS && m_iDirOverride == UT_BIDI_RTL && _getDirection() == UT_BIDI_LTR)
2134 || (s_bBidiOS && m_iDirOverride == UT_BIDI_LTR && _getDirection() == UT_BIDI_RTL))
2135 UT_UCS4_strnrev(pRI->m_pChars, iLen);
2136 }
2137
2138 // mark the draw buffer clean ...
2139 _setRefreshDrawBuffer(GRSR_BufferClean);
2140
2141 // now remeasure our characters
2142 measureCharWidths();
2143 return true;
2144 } //if(m_bRefreshDrawBuffer)
2145
2146 // mark the draw buffer clean ...
2147 _setRefreshDrawBuffer(GRSR_BufferClean);
2148 return false;
2149 }
2150
2151
2152 /*
2153 this function expect to have m_pRenderInfo->m_x/yoff set
2154 xoff is the right edge of this run !!!
2155 */
_drawLastChar(bool)2156 void fp_TextRun::_drawLastChar(bool /*bSelection*/)
2157 {
2158 //
2159 // We appear to no longer need this code. Symptom would be if the last
2160 // character in a run is blanked out. Removing this code fixes bug 6113
2161 //
2162 // I'm keeping this code behind an #if 0 in case we need it. If after
2163 // a few month we don't, this method and calls of it should be removed.
2164 // MES 28/8/2004
2165 //
2166 return;
2167 #if 0
2168 UT_return_if_fail(m_pRenderInfo);
2169
2170 if(!getLength())
2171 return;
2172 // return;
2173 // have to set font (and colour!), since we were called from a run
2174 // using different font
2175 GR_Graphics * pG = getGraphics();
2176 UT_return_if_fail(pG);
2177
2178 pG->setFont(_getFont());
2179
2180 UT_RGBColor pForeCol(255,255,255);
2181 UT_RGBColor cWhite(255,255,255);
2182 //
2183 // Draw character in white then foreground to minimize "bolding" the
2184 // character.
2185 //
2186 if(bSelection)
2187 {
2188 pForeCol= _getView()->getColorSelForeground();
2189 }
2190 else
2191 pForeCol = getFGColor();
2192
2193 GR_Painter painter(pG);
2194
2195 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2196 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2197
2198 m_pRenderInfo->m_pText = &text;
2199
2200 // getTextWidth() takes LOGICAL offset
2201 m_pRenderInfo->m_iOffset = getLength() - 1;
2202 text.setPosition(getBlockOffset() + fl_BLOCK_STRUX_OFFSET + getLength() - 1);
2203
2204 m_pRenderInfo->m_iLength = 1;
2205 m_pRenderInfo->m_xoff -= getGraphics()->getTextWidth(*m_pRenderInfo);
2206 m_pRenderInfo->m_pFont = _getFont();
2207
2208 // renderChars() takes VISUAL offset
2209 UT_BidiCharType iVisDirection = getVisDirection();
2210 UT_uint32 iVisOffset = iVisDirection == UT_BIDI_LTR ? getLength() - 1 : 0;
2211 m_pRenderInfo->m_iOffset = iVisOffset;
2212 pG->prepareToRenderChars(*m_pRenderInfo);
2213 pG->setColor(cWhite);
2214 painter.renderChars(*m_pRenderInfo);
2215 pG->setColor(pForeCol);
2216 painter.renderChars(*m_pRenderInfo);
2217 #endif
2218 }
2219
2220 /*
2221 this function expect to have m_pRenderInfo->m_x/yoff set
2222 */
_drawFirstChar(bool bSelection)2223 void fp_TextRun::_drawFirstChar(bool bSelection)
2224 {
2225 UT_return_if_fail(m_pRenderInfo);
2226
2227 if(!getLength())
2228 return;
2229
2230 // have to sent font (and colour!), since we were called from a
2231 // run using different font
2232 GR_Graphics * pG = getGraphics();
2233 UT_return_if_fail(pG);
2234
2235 pG->setFont(_getFont());
2236
2237 GR_Painter painter(pG);
2238
2239 if(bSelection)
2240 {
2241 pG->setColor(_getView()->getColorSelForeground());
2242 }
2243 else
2244 pG->setColor(getFGColor());
2245
2246 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2247 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2248
2249 m_pRenderInfo->m_pText = &text;
2250 UT_BidiCharType iVisDirection = getVisDirection();
2251 UT_uint32 iVisOffset = iVisDirection == UT_BIDI_LTR ? 0 : getLength() - 1;
2252
2253 if(!s_bBidiOS)
2254 {
2255 // m_pSpanBuff is in visual order, so we just draw the last char
2256 m_pRenderInfo->m_iOffset = 0;
2257 }
2258 else
2259 {
2260 // UT_uint32 iPos = getVisDirection() == UT_BIDI_RTL ? getLength() - 1 : 0;
2261 UT_uint32 iPos = 0;
2262 m_pRenderInfo->m_iOffset = iPos;
2263 text.setPosition(getBlockOffset() + fl_BLOCK_STRUX_OFFSET + iPos);
2264 }
2265
2266 m_pRenderInfo->m_iLength = 1;
2267
2268 m_pRenderInfo->m_iOffset = iVisOffset;
2269 m_pRenderInfo->m_pFont = _getFont();
2270
2271 pG->prepareToRenderChars(*m_pRenderInfo);
2272 painter.renderChars(*m_pRenderInfo);
2273 #ifdef ENABLE_SPELL
2274 if(pG->queryProperties(GR_Graphics::DGP_SCREEN))
2275 {
2276 m_bSpellSquiggled = false;
2277 getBlock()->findSpellSquigglesForRun(this);
2278 m_bGrammarSquiggled = false;
2279 getBlock()->findGrammarSquigglesForRun(this);
2280 }
2281 #endif
2282 }
2283
_drawInvisibleSpaces(UT_sint32 xoff,UT_sint32 yoff)2284 void fp_TextRun::_drawInvisibleSpaces(UT_sint32 xoff, UT_sint32 yoff)
2285 {
2286 bool bRTL = getVisDirection() == UT_BIDI_RTL;
2287
2288 UT_sint32 iWidth = bRTL ? getWidth() : 0;
2289 UT_uint32 iLen = getLength();
2290 UT_sint32 iLineWidth = 1+ (UT_MAX(10,getAscent())-10)/8;
2291 UT_sint32 iRectSize = iLineWidth * 3 / 2;
2292 UT_uint32 iWidthOffset = 0;
2293 UT_uint32 iY = yoff + getAscent() * 2 / 3;
2294
2295 FV_View* pView = getBlock()->getDocLayout()->getView();
2296
2297 GR_Painter painter(getGraphics());
2298 UT_return_if_fail(m_pRenderInfo);
2299
2300 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2301 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2302
2303 for (UT_uint32 i=0; i < iLen && text.getStatus() == UTIter_OK; ++i, ++text)
2304 {
2305 m_pRenderInfo->m_iOffset = iWidthOffset;
2306 m_pRenderInfo->m_iLength = 1;
2307 UT_sint32 iCharWidth = getGraphics()->getTextWidth(*m_pRenderInfo);
2308
2309 if(text.getChar() == UCS_SPACE)
2310 {
2311 UT_uint32 x;
2312 if(bRTL)
2313 x = xoff + iWidth - (iCharWidth + iRectSize)/2;
2314 else
2315 x = xoff + iWidth + (iCharWidth - iRectSize)/2;
2316
2317 painter.fillRect(pView->getColorShowPara(), x, iY, iRectSize, iRectSize);
2318 }
2319 UT_uint32 iCW = iCharWidth > 0 && iCharWidth < GR_OC_MAX_WIDTH ? iCharWidth : 0;
2320
2321 if(bRTL)
2322 iWidth -= iCW;
2323 else
2324 iWidth += iCW;
2325
2326 ++iWidthOffset;
2327 }
2328 }
2329
_drawInvisibles(UT_sint32 xoff,UT_sint32 yoff)2330 void fp_TextRun::_drawInvisibles(UT_sint32 xoff, UT_sint32 yoff)
2331 {
2332 if (!(getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN)))
2333 return;
2334
2335 _drawInvisibleSpaces(xoff,yoff);
2336 }
2337
2338 #ifdef ENABLE_SPELL
_drawSquiggle(UT_sint32 top,UT_sint32 left,UT_sint32 right,FL_SQUIGGLE_TYPE iSquiggle)2339 void fp_TextRun::_drawSquiggle(UT_sint32 top, UT_sint32 left, UT_sint32 right, FL_SQUIGGLE_TYPE iSquiggle)
2340 {
2341 if(_getView())
2342 {
2343 XAP_Frame * pFrame = static_cast<XAP_Frame*>(_getView()->getParentData());
2344 if(pFrame && pFrame->isMenuScrollHidden())
2345 {
2346 return;
2347 }
2348 }
2349 if (!(getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN)))
2350 {
2351 return;
2352 }
2353
2354 GR_Painter painter(getGraphics());
2355 if(iSquiggle == FL_SQUIGGLE_SPELL)
2356 {
2357 m_bSpellSquiggled = true;
2358 }
2359 if(iSquiggle == FL_SQUIGGLE_GRAMMAR)
2360 {
2361 m_bGrammarSquiggled = true;
2362 }
2363
2364 UT_sint32 nPoints = 0;
2365
2366 if(iSquiggle == FL_SQUIGGLE_SPELL)
2367 {
2368 /* Do /\/\/\/\ */
2369
2370 nPoints = getGraphics()->tdu((right - left + getGraphics()->tlu(3))/2);
2371 }
2372 else
2373 {
2374 // Do _|-|_|-|
2375 nPoints = getGraphics()->tdu((right - left + getGraphics()->tlu(3)));
2376 }
2377 if(nPoints < 1)
2378 return;
2379 /*
2380 NB: This array gets recopied inside the polyLine implementation
2381 to move the coordinates into a platform-specific point
2382 structure. They're all x, y but different widths. Bummer.
2383 */
2384 UT_Point * points, scratchpoints[100];
2385 if (static_cast<unsigned>(nPoints) < (sizeof(scratchpoints)/sizeof(scratchpoints[0])))
2386 {
2387 points = scratchpoints;
2388 }
2389 else
2390 {
2391 points = new UT_Point[nPoints];
2392 }
2393 UT_ASSERT(points);
2394
2395 points[0].x = left;
2396 points[0].y = top;
2397
2398 bool bTop = false;
2399 if(iSquiggle == FL_SQUIGGLE_SPELL)
2400 {
2401 for (UT_sint32 i = 1; i < nPoints; i++, bTop = !bTop)
2402 {
2403 points[i].x = points[i-1].x + getGraphics()->tlu(2);
2404 points[i].y = (bTop ? top : top + getGraphics()->tlu(2));
2405 }
2406
2407 if (points[nPoints-1].x > right)
2408 {
2409 points[nPoints-1].x = right;
2410 points[nPoints-1].y = top + getGraphics()->tlu(1);
2411 }
2412 }
2413 else
2414 {
2415
2416 UT_return_if_fail(nPoints >= 2); //can be 1 for overstriking chars
2417 points[0].x = left;
2418 points[0].y = top + getGraphics()->tlu(2);
2419 UT_sint32 i =0;
2420 for (i = 1; i < nPoints-2; i +=2)
2421 {
2422 points[i].x = points[i-1].x + getGraphics()->tlu(2);
2423 points[i].y = (bTop ? top : top + getGraphics()->tlu(2)) ;
2424 points[i+1].x = points[i].x;
2425 points[i+1].y = (bTop ? top + getGraphics()->tlu(2) : top);
2426 bTop = ! bTop;
2427 }
2428 if(i == (nPoints-2))
2429 {
2430 points[i].x = points[i-1].x + getGraphics()->tlu(2);
2431 points[i].y = (bTop ? top : top + getGraphics()->tlu(2)) ;
2432 points[i+1].x = points[i].x;
2433 points[i+1].y = (bTop ? top + getGraphics()->tlu(2) : top);
2434 bTop = ! bTop;
2435 }
2436 else if( i == (nPoints-1))
2437 {
2438 points[nPoints-1].x = right;
2439 points[i].y = (bTop ? top : top + getGraphics()->tlu(2)) ;
2440 }
2441 if (points[nPoints-1].x > right)
2442 {
2443 points[nPoints-1].x = right;
2444 points[i].y = (bTop ? top : top + getGraphics()->tlu(2)) ;
2445 }
2446
2447 }
2448 getGraphics()->setLineProperties(getGraphics()->tluD(1.0),
2449 GR_Graphics::JOIN_MITER,
2450 GR_Graphics::CAP_PROJECTING,
2451 GR_Graphics::LINE_SOLID);
2452
2453 painter.polyLine(points, nPoints);
2454
2455 if (points != scratchpoints) delete[] points;
2456 }
2457
drawSquiggle(UT_uint32 iOffset,UT_uint32 iLen,FL_SQUIGGLE_TYPE iSquiggle)2458 void fp_TextRun::drawSquiggle(UT_uint32 iOffset, UT_uint32 iLen,FL_SQUIGGLE_TYPE iSquiggle )
2459 {
2460 // UT_ASSERT(iLen > 0);
2461 if (iLen == 0)
2462 {
2463 // I think this is safe, although it begs the question, why did we get called if iLen is zero? TODO
2464 return;
2465 }
2466 if(getLine())
2467 {
2468 getLine()->setScreenCleared(false);
2469 }
2470 UT_sint32 xoff = 0, yoff = 0;
2471 UT_sint32 iAscent = getLine()->getAscent();
2472 UT_sint32 iDescent = getLine()->getDescent();
2473 if(iOffset < getBlockOffset())
2474 {
2475 iOffset = getBlockOffset();
2476 }
2477 // we'd prefer squiggle to leave one pixel below the baseline,
2478 // but we need to force all three pixels inside the descent
2479 // we cannot afford the 1pixel gap, it leave dirt on screen -- Tomas
2480 UT_sint32 iGap = (iDescent > 3) ?/*1*/0 : (iDescent - 3);
2481 if(iSquiggle == FL_SQUIGGLE_GRAMMAR)
2482 {
2483 // iGap += getGraphics()->tlu(2);
2484 }
2485 getGraphics()->setColor(_getView()->getColorSquiggle(iSquiggle));
2486
2487 getLine()->getScreenOffsets(this, xoff, yoff);
2488
2489 UT_Rect r;
2490 _getPartRect( &r, xoff, yoff, iOffset, iLen);
2491 if(r.width > getWidth())
2492 {
2493 r.width = getWidth();
2494 }
2495 _drawSquiggle(r.top + iAscent + iGap + getGraphics()->tlu(1), r.left, r.left + r.width,iSquiggle);
2496 xxx_UT_DEBUGMSG(("Done draw sqiggle for run in block %x \n",getBlock()));
2497 }
2498 #endif
2499
findCharacter(UT_uint32 startPosition,UT_UCSChar Character) const2500 UT_sint32 fp_TextRun::findCharacter(UT_uint32 startPosition, UT_UCSChar Character) const
2501 {
2502 // NOTE: startPosition is run-relative
2503 // NOTE: return value is block-relative (don't ask me why)
2504 if ((getLength() > 0) && (startPosition < getLength()))
2505 {
2506 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2507 getBlockOffset() + fl_BLOCK_STRUX_OFFSET + startPosition);
2508
2509 for(UT_uint32 i = startPosition; i < getLength() && text.getStatus() == UTIter_OK;
2510 i++, ++text)
2511 {
2512 if(text.getChar() == Character)
2513 return i + getBlockOffset();
2514 }
2515 }
2516
2517 // not found
2518 return -1;
2519 }
2520
getCharacter(UT_uint32 run_offset,UT_UCSChar & Character) const2521 bool fp_TextRun::getCharacter(UT_uint32 run_offset, UT_UCSChar &Character) const
2522 {
2523 if(getLength() == 0)
2524 return false;
2525
2526 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2527 getBlockOffset() + fl_BLOCK_STRUX_OFFSET + run_offset);
2528
2529 UT_return_val_if_fail(text.getStatus() == UTIter_OK, false);
2530
2531 Character = text.getChar();
2532
2533 xxx_UT_DEBUGMSG(("fp_TextRun::getCharacter offset %d, char 0x%04x\n",
2534 run_offset, Character));
2535 return true;
2536 }
2537
isLastCharacter(UT_UCSChar Character) const2538 bool fp_TextRun::isLastCharacter(UT_UCSChar Character) const
2539 {
2540 UT_UCSChar c;
2541
2542 if (getCharacter(getLength() - 1, c))
2543 return c == Character;
2544
2545 // not found
2546
2547 return false;
2548 }
2549
isFirstCharacter(UT_UCSChar Character) const2550 bool fp_TextRun::isFirstCharacter(UT_UCSChar Character) const
2551 {
2552 UT_UCSChar c;
2553
2554 if (getCharacter(0, c))
2555 return c == Character;
2556
2557 // not found
2558
2559 return false;
2560 }
2561
2562
doesContainNonBlankData(void) const2563 bool fp_TextRun::doesContainNonBlankData(void) const
2564 {
2565 if(getLength() > 0)
2566 {
2567 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2568 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2569
2570 for(UT_uint32 i = 0; i < getLength() && text.getStatus() == UTIter_OK; i++, ++text)
2571 {
2572 if(text.getChar() != UCS_SPACE)
2573 return true;
2574 }
2575 }
2576
2577 // Only spaces found;
2578 return false;
2579 }
2580
isSuperscript(void) const2581 inline bool fp_TextRun::isSuperscript(void) const
2582 {
2583 return (m_fPosition == TEXT_POSITION_SUPERSCRIPT);
2584 }
2585
isSubscript(void) const2586 inline bool fp_TextRun::isSubscript(void) const
2587 {
2588 return (m_fPosition == TEXT_POSITION_SUBSCRIPT);
2589 }
2590
findTrailingSpaceDistance(void) const2591 UT_sint32 fp_TextRun::findTrailingSpaceDistance(void) const
2592 {
2593 UT_return_val_if_fail(m_pRenderInfo, 0);
2594
2595 UT_sint32 iTrailingDistance = 0;
2596 if(getLength() > 0)
2597 {
2598 UT_sint32 i;
2599
2600 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2601 getBlockOffset() + fl_BLOCK_STRUX_OFFSET + getLength() - 1);
2602
2603 // HACK
2604 //fp_TextRun * pThis = const_cast<fp_TextRun*>(this);
2605 //bool bReverse = (pThis->getVisDirection() == UT_BIDI_RTL);
2606
2607 for (i = getLength() - 1; i >= 0 && text.getStatus() == UTIter_OK; i--, --text)
2608 {
2609 // getTextWidth() takes LOGICAL offset
2610
2611 if(UCS_SPACE == text.getChar())
2612 {
2613 xxx_UT_DEBUGMSG(("For i %d char is |%c| trail %d \n",i,c,iTrailingDistance));
2614 m_pRenderInfo->m_iOffset = i;
2615 m_pRenderInfo->m_iLength = 1;
2616 iTrailingDistance += getGraphics()->getTextWidth(*m_pRenderInfo);
2617 }
2618 else
2619 {
2620 break;
2621 }
2622 }
2623
2624 }
2625 xxx_UT_DEBUGMSG(("findTrailingSpaceDistance result %d \n",iTrailingDistance));
2626
2627 return iTrailingDistance;
2628 }
2629
resetJustification(bool bPermanent)2630 void fp_TextRun::resetJustification(bool bPermanent)
2631 {
2632 if(!m_pRenderInfo || (_getRefreshDrawBuffer() == GRSR_Unknown) || bPermanent)
2633 {
2634 _refreshDrawBuffer();
2635 }
2636
2637 UT_return_if_fail(m_pRenderInfo);
2638 if(getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN))
2639 {
2640 // _clearScreen(); // This causes excessive flicker editing Justified text
2641 }
2642 UT_sint32 iWidth = getWidth();
2643 xxx_UT_DEBUGMSG(("reset Justification of run %x \n", this));
2644
2645 m_pRenderInfo->m_iLength = getLength();
2646 UT_sint32 iAccumDiff = getGraphics()->resetJustification(*m_pRenderInfo, bPermanent);
2647
2648 if(iAccumDiff != 0)
2649 {
2650 _setRecalcWidth(true); // not sure this is needed
2651 _setWidth(iWidth + iAccumDiff);
2652 }
2653 }
2654
2655 /*!
2656 This metod distributes an extra width needed to make the line
2657 justified betten the spaces in this run
2658
2659 \param UT_sint32 iAmount : the extra width to distribute
2660 \param UT_uint32 iSpacesInRun : the number of spaces in this run
2661
2662 */
justify(UT_sint32 iAmount,UT_uint32 iSpacesInRun)2663 void fp_TextRun::justify(UT_sint32 iAmount, UT_uint32 iSpacesInRun)
2664 {
2665 UT_return_if_fail(m_pRenderInfo);
2666 UT_uint32 len = getLength();
2667
2668 if(!iAmount)
2669 {
2670 // this can happend near the start of the line (the line is
2671 // processed from back to front) due to rounding errors in
2672 // the algorithm; we simply mark the run as one that does not
2673 // use justification
2674
2675 // not needed, since we are always called after ::resetJustification()
2676 // resetJustification();
2677 return;
2678 }
2679
2680 if(iSpacesInRun && len > 0)
2681 {
2682 m_pRenderInfo->m_iLength = len;
2683
2684 _setWidth(getWidth() + iAmount);
2685
2686 #ifdef WITH_CAIRO
2687 // Because Pango does not yet support justification, we need to iterate over the raw
2688 // text counting spaces; this is not required by the default graphics class, nor
2689 // Uniscribe and for performance reasons it is therefore only conditionally compiled
2690 // in; once Pango supports justification, this will not be needed, so this whole lot
2691 // will be again removed
2692 UT_uint32 iPosStart = getBlockOffset() + fl_BLOCK_STRUX_OFFSET;
2693 PD_StruxIterator text(getBlock()->getStruxDocHandle(),iPosStart);
2694 text.setUpperLimit(text.getPosition() + len - 1);
2695 m_pRenderInfo->m_pText = & text;
2696 UT_ASSERT(len == getLength());
2697 // m_pRenderInfo->m_iLength = getLength();
2698 #else
2699 m_pRenderInfo->m_pText = NULL;
2700 #endif
2701
2702 m_pRenderInfo->m_iJustificationPoints = iSpacesInRun;
2703 m_pRenderInfo->m_iJustificationAmount = iAmount;
2704 getGraphics()->justify(*m_pRenderInfo);
2705
2706 #ifdef WITH_CAIRO
2707 // do not leave stale pointer behind
2708 m_pRenderInfo->m_pText = NULL;
2709 #endif
2710 }
2711 }
2712
2713
2714 /*
2715 if this run contains only spaces, we will return the count
2716 as a negative value, this will save us having to call
2717 doesContainNonBlankData() in a couple of loops in fp_Line
2718 */
2719
countJustificationPoints(bool bLast) const2720 UT_sint32 fp_TextRun::countJustificationPoints(bool bLast) const
2721 {
2722 UT_return_val_if_fail(m_pRenderInfo, 0);
2723 m_pRenderInfo->m_iLength = getLength();
2724
2725 UT_return_val_if_fail(m_pRenderInfo->m_iLength > 0, 0);
2726
2727 #ifdef WITH_CAIRO
2728 // Because Pango does not yet support justification, we need to iterate over the raw
2729 // text counting spaces; this is not required by the default graphics class, nor
2730 // Uniscribe and for performance reasons it is therefore only conditionally compiled
2731 // in; once Pango supports justification, this will not be needed, so this whole lot
2732 // will be again removed
2733 UT_uint32 iPosStart = getBlockOffset() + fl_BLOCK_STRUX_OFFSET;
2734 PD_StruxIterator text(getBlock()->getStruxDocHandle(),iPosStart);
2735 text.setUpperLimit(text.getPosition() + getLength() - 1);
2736 m_pRenderInfo->m_pText = & text;
2737 m_pRenderInfo->m_iLength = getLength();
2738 #else
2739 m_pRenderInfo->m_pText = NULL;
2740 #endif
2741
2742 m_pRenderInfo->m_bLastOnLine = bLast;
2743 UT_sint32 iCount = getGraphics()->countJustificationPoints(*m_pRenderInfo);
2744
2745 #ifdef WITH_CAIRO
2746 m_pRenderInfo->m_pText = NULL;
2747 #endif
2748
2749 return iCount;
2750 }
2751
_canContainPoint(void) const2752 bool fp_TextRun::_canContainPoint(void) const
2753 {
2754 if (getField())
2755 {
2756 return false;
2757 }
2758 else
2759 {
2760 return true;
2761 }
2762 }
2763
2764 //fill str of size iMax with the text of the run, return 0 on success,
2765 //size of str required if not not enough space, -1 otherwise
2766 //it further returns the len copied (or desired to be copied) into pStr in iMax
2767
getStr(UT_UCSChar * pStr,UT_uint32 & iMax)2768 UT_sint32 fp_TextRun::getStr(UT_UCSChar * pStr, UT_uint32 &iMax)
2769 {
2770 UT_uint32 len = getLength();
2771
2772 if(iMax <= len)
2773 {
2774 iMax = len;
2775 return(len);//if there is not enough space in the passed string
2776 //return size needed
2777 }
2778
2779 if (len > 0)
2780 {
2781 UT_uint32 i;
2782 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2783 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2784
2785 for(i = 0; i < getLength() && text.getStatus() == UTIter_OK; i++, ++text)
2786 pStr[i] = text.getChar();
2787
2788 pStr[i] = 0;
2789 iMax = getLength();
2790 return(0);
2791 }
2792 else //this run is empty, fill pStr with 00 and return 0
2793 {
2794 *pStr = 0;
2795 iMax = 0;
2796 return 0;
2797 }
2798 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2799 return -1;
2800 }
2801
2802 /*!
2803 * Returns true if this run plus the next can be combined to make
2804 * one contiguous item
2805 */
isOneItem(fp_Run * pNext)2806 bool fp_TextRun::isOneItem(fp_Run * pNext)
2807 {
2808 GR_Itemization I;
2809 bool b = getBlock()->itemizeSpan(getBlockOffset(), getLength()+pNext->getLength(),I);
2810 UT_return_val_if_fail(b,false);
2811 if(I.getItemCount() <= 2)
2812 {
2813 //
2814 // Now look to see if there is roman text mixed with
2815 // Unicode. Can easily happen with numbers or smart quotes
2816 //
2817 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2818 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2819
2820 text.setUpperLimit(text.getPosition() + getLength()+ pNext->getLength() - 1);
2821 UT_ASSERT_HARMLESS( text.getStatus() == UTIter_OK );
2822 bool bFoundRoman = false;
2823 bool bFoundUnicode = false;
2824 while(text.getStatus() == UTIter_OK)
2825 {
2826 UT_UCS4Char c = text.getChar();
2827 if(c != ' ' && c <256)
2828 {
2829 bFoundRoman = true;
2830 }
2831 else if(c!= ' ' && !UT_isSmartQuotedCharacter(c))
2832 {
2833 bFoundUnicode = true;
2834 }
2835 ++text;
2836 }
2837 if(bFoundRoman && bFoundUnicode)
2838 {
2839 return false;
2840 }
2841 return true;
2842 }
2843 return false;
2844 }
itemize(void)2845 void fp_TextRun::itemize(void)
2846 {
2847 GR_Itemization I;
2848 bool b = getBlock()->itemizeSpan(getBlockOffset(), getLength(),I);
2849 UT_return_if_fail(b);
2850 //
2851 // Should only be one item per run
2852 //
2853 GR_Item * pItem = I.getNthItem(0);
2854 UT_return_if_fail(pItem);
2855 setItem(pItem->makeCopy());
2856 }
2857
setItem(GR_Item * i)2858 void fp_TextRun::setItem(GR_Item * i)
2859 {
2860 DELETEP(m_pItem);
2861 m_pItem =i;
2862 if(m_pRenderInfo)
2863 {
2864 m_pRenderInfo->m_pItem = m_pItem;
2865 }
2866 }
2867
setDirection(UT_BidiCharType dir,UT_BidiCharType dirOverride)2868 void fp_TextRun::setDirection(UT_BidiCharType dir, UT_BidiCharType dirOverride)
2869 {
2870 if( !getLength()
2871 || ( dir == static_cast<UT_BidiCharType>(UT_BIDI_UNSET)
2872 && _getDirection() != static_cast<UT_BidiCharType>(UT_BIDI_UNSET)
2873 && dirOverride == m_iDirOverride
2874 )
2875 )
2876 return; //ignore 0-length runs, let them be treated on basis of the app defaults
2877
2878 UT_BidiCharType prevDir = m_iDirOverride == static_cast<UT_BidiCharType>(UT_BIDI_UNSET) ? _getDirection() : m_iDirOverride;
2879 if(dir == static_cast<UT_BidiCharType>(UT_BIDI_UNSET))
2880 {
2881 // only do this once
2882 if(_getDirection() == static_cast<UT_BidiCharType>(UT_BIDI_UNSET))
2883 {
2884 // here we used to check the first character; we can no longer do that,
2885 // because the latest versions of USP create items that are not homogenous and
2886 // can contain strong chars prefixed by weak chars. So if the first char is
2887 // not strong, we try the rest of the run to see if it might contain any
2888 // strong chars
2889 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
2890 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
2891
2892 text.setUpperLimit(text.getPosition() + getLength() - 1);
2893
2894 UT_ASSERT_HARMLESS( text.getStatus() == UTIter_OK );
2895
2896 UT_BidiCharType t = UT_BIDI_UNSET;
2897
2898 while(text.getStatus() == UTIter_OK)
2899 {
2900 UT_UCS4Char c = text.getChar();
2901
2902 t = UT_bidiGetCharType(c);
2903
2904 if(UT_BIDI_IS_STRONG(t))
2905 break;
2906
2907 ++text;
2908 }
2909
2910 _setDirection(t);
2911 }
2912 }
2913 else //meaningfull value received
2914 {
2915 _setDirection(dir);
2916 }
2917
2918 if(dirOverride != static_cast<UT_BidiCharType>(UT_BIDI_IGNORE))
2919 {
2920
2921 m_iDirOverride = dirOverride;
2922
2923 // if we set dir override to a strong value, set also visual direction
2924 // if we set it to UNSET, and the new direction is srong, then we set
2925 // it to that direction, if it is weak, we have to make the line
2926 // to calculate it
2927
2928 if(dirOverride != static_cast<UT_BidiCharType>(UT_BIDI_UNSET))
2929 setVisDirection(dirOverride);
2930 }
2931
2932
2933 /*
2934 if this run belongs to a line we have to notify the line that
2935 that it now contains a run of this direction, if it does not belong
2936 to a line this will be taken care of by the fp_Line:: member function
2937 used to add the run to the line (generally, we set it here if this
2938 is a run that is being typed in and it gets set in the member
2939 functions when the run is loaded from a document on the disk.)
2940 */
2941
2942 UT_BidiCharType curDir = m_iDirOverride == static_cast<UT_BidiCharType>(UT_BIDI_UNSET) ? _getDirection() : m_iDirOverride;
2943
2944 UT_ASSERT(curDir != static_cast<UT_BidiCharType>(UT_BIDI_UNSET));
2945
2946 if(curDir != prevDir)
2947 {
2948 // TODO -- not sure this is necessary
2949 clearScreen();
2950 markDrawBufferDirty();
2951
2952 if(getLine())
2953 {
2954 getLine()->changeDirectionUsed(prevDir,curDir,true);
2955 //getLine()->setNeedsRedraw();
2956 }
2957 }
2958 else
2959 {
2960 if(!UT_BIDI_IS_STRONG(curDir) && getLine())
2961 {
2962 getLine()->setMapOfRunsDirty();
2963 clearScreen();
2964 markDrawBufferDirty();
2965 }
2966 }
2967
2968 }
2969
2970 /*
2971 NB !!!
2972 This function will set the m_iDirOverride member and change the properties
2973 in the piece table correspondingly; however, note that if dir ==
2974 UT_BIDI_UNSET, this function must immediately return.
2975
2976 Because of this specialised behaviour and since it does not do any updates, etc.,
2977 its usability is limited -- its main purpose is to allow to set this property
2978 in response to inserting a Unicode direction token in fl_BlockLayout _immediately_
2979 after the run is created. For all other purposes one of the standard
2980 edit methods should be used.
2981 */
setDirOverride(UT_BidiCharType dir)2982 void fp_TextRun::setDirOverride(UT_BidiCharType dir)
2983 {
2984 if(dir == static_cast<UT_BidiCharType>(UT_BIDI_UNSET) || dir == static_cast<UT_BidiCharType>(m_iDirOverride))
2985 return;
2986
2987 const gchar * prop[] = {NULL, NULL, 0};
2988 const gchar direction[] = "dir-override";
2989 const gchar rtl[] = "rtl";
2990 const gchar ltr[] = "ltr";
2991
2992 prop[0] = static_cast<const gchar*>(&direction[0]);
2993
2994 switch(dir)
2995 {
2996 case UT_BIDI_LTR:
2997 prop[1] = static_cast<const gchar*>(<r[0]);
2998 break;
2999 case UT_BIDI_RTL:
3000 prop[1] = static_cast<const gchar*>(&rtl[0]);
3001 break;
3002 default:
3003 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
3004 };
3005
3006 m_iDirOverride = dir;
3007
3008 UT_uint32 offset = getBlock()->getPosition() + getBlockOffset();
3009 getBlock()->getDocument()->changeSpanFmt(PTC_AddFmt,offset,offset + getLength(),NULL,prop);
3010
3011 UT_DEBUGMSG(("fp_TextRun::setDirOverride: offset=%d, len=%d, dir=\"%s\"\n", offset,getLength(),prop[1]));
3012 }
3013
3014 /*! A word of explanaiton of the break*AtDirBoundaries() functions.
3015 In order to reduce our memory use, we merge runs that resolve to
3016 the same embeding level. For example, if we have the sequence 'RTL
3017 white_space RTL', we will merge it into one run that gets treated
3018 as RTL. However, if we insert a new character into this combined
3019 run, or on its left or right, this might result in the embeding
3020 level of the white_space segment changing. In order to handle
3021 this, we have to break the present run into segments that contain
3022 characters of the same type, do the bidi processing, and then we
3023 can again merge anything that is on the same embeding level. The
3024 two functions below are responsible for the breaking, and are
3025 invoked from inside the fl_BlockLayout class.
3026 */
breakNeighborsAtDirBoundaries()3027 void fp_TextRun::breakNeighborsAtDirBoundaries()
3028 {
3029 #ifndef NO_BIDI_SUPPORT
3030 UT_BidiCharType iPrevType, iType = UT_BIDI_UNSET;
3031 UT_BidiCharType iDirection = getDirection();
3032
3033 fp_TextRun *pNext = NULL, *pPrev = NULL, *pOtherHalf;
3034 PT_BlockOffset curOffset = 0;
3035
3036 if( getPrevRun()
3037 && getPrevRun()->getType() == FPRUN_TEXT
3038 && getPrevRun()->getVisDirection() != iDirection)
3039 {
3040 pPrev = static_cast<fp_TextRun*>(getPrevRun());
3041 curOffset = pPrev->getBlockOffset() + pPrev->getLength() - 1;
3042 }
3043
3044
3045 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
3046 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
3047
3048 while(pPrev)
3049 {
3050 UT_UCS4Char c = text[curOffset + fl_BLOCK_STRUX_OFFSET];
3051 UT_return_if_fail(text.getStatus() == UTIter_OK);
3052
3053 iPrevType = iType = UT_bidiGetCharType(c);
3054
3055 if(pPrev->getLength() > 1)
3056 {
3057 while(curOffset > pPrev->getBlockOffset() && !UT_BIDI_IS_STRONG(iType))
3058 {
3059 curOffset--;
3060 c = text[curOffset + fl_BLOCK_STRUX_OFFSET];
3061 UT_return_if_fail(text.getStatus() == UTIter_OK);
3062
3063 iType = UT_bidiGetCharType(c);
3064 if(iType != iPrevType)
3065 {
3066 pPrev->split(curOffset+1);
3067
3068 //now we want to reset the direction of the second half
3069 UT_ASSERT(pPrev->getNextRun()->getType() == FPRUN_TEXT);
3070 pOtherHalf = static_cast<fp_TextRun*>(pPrev->getNextRun());
3071 pOtherHalf->setDirection(iPrevType, pOtherHalf->getDirOverride());
3072 iPrevType = iType;
3073 // we do not want to break here, since pPrev still points to the
3074 // left part, so we can carry on leftwards
3075 }
3076 }
3077 }
3078
3079 if(UT_BIDI_IS_STRONG(iPrevType))
3080 break;
3081
3082 // if we got this far, the whole previous run is weak, so we want to
3083 // reset its direction and proceed with the run before it
3084 pPrev->setDirection(iPrevType, pPrev->getDirOverride());
3085
3086 if(pPrev->getPrevRun() && pPrev->getPrevRun()->getType() == FPRUN_TEXT)
3087 {
3088 pPrev = static_cast<fp_TextRun*>(pPrev->getPrevRun());
3089 curOffset = pPrev->getBlockOffset() + pPrev->getLength() - 1;
3090 }
3091 else
3092 break;
3093
3094 }
3095
3096 // now do the same thing with the following run
3097 if( getNextRun()
3098 && getNextRun()->getType() == FPRUN_TEXT
3099 && getNextRun()->getVisDirection() != iDirection)
3100 {
3101 pNext = static_cast<fp_TextRun*>(getNextRun());
3102 curOffset = pNext->getBlockOffset();
3103 }
3104
3105 while(pNext)
3106 {
3107 UT_UCS4Char c = text[curOffset + fl_BLOCK_STRUX_OFFSET];
3108 UT_return_if_fail(text.getStatus() == UTIter_OK);
3109
3110 iPrevType = iType = UT_bidiGetCharType(c);
3111 bool bDirSet = false;
3112
3113 if(pNext->getLength() > 1)
3114 {
3115 while(curOffset < pNext->getBlockOffset() + pNext->getLength() - 1
3116 && !UT_BIDI_IS_STRONG(iType))
3117 {
3118 curOffset++;
3119 c = text[curOffset + fl_BLOCK_STRUX_OFFSET];
3120 iType = UT_bidiGetCharType(c);
3121
3122 if(iType != iPrevType)
3123 {
3124 pNext->split(curOffset);
3125 pNext->setDirection(iPrevType, pNext->getDirOverride());
3126
3127 // now set direction of the second half
3128 UT_ASSERT(pNext->getNextRun()->getType() == FPRUN_TEXT);
3129 pOtherHalf = static_cast<fp_TextRun*>(pNext->getNextRun());
3130
3131 pOtherHalf->setDirection(iType, pOtherHalf->getDirOverride());
3132 bDirSet = true;
3133 iPrevType = iType; // not needed
3134
3135 // since pNext points now to the left half, the right-ward processing
3136 // cannot continue, but insteds we need to proceed with the new run
3137 // on the right
3138 break;
3139 }
3140 }
3141
3142 }
3143
3144 if(UT_BIDI_IS_STRONG(iPrevType))
3145 break;
3146
3147 // if we got this far, the whole next run is weak, so we want to
3148 // reset its direction, unless we split it, in which case it has already
3149 // been set
3150 // then proceed with the run after it
3151 if(!bDirSet)
3152 pNext->setDirection(iPrevType, pNext->getDirOverride());
3153
3154 if(pNext->getNextRun() && pNext->getNextRun()->getType() == FPRUN_TEXT)
3155 {
3156 pNext = static_cast<fp_TextRun*>(pNext->getNextRun());
3157 curOffset = pNext->getBlockOffset();
3158 }
3159 else
3160 break;
3161
3162 }
3163 #endif
3164 }
3165
breakMeAtDirBoundaries(UT_BidiCharType iNewOverride)3166 void fp_TextRun::breakMeAtDirBoundaries(UT_BidiCharType iNewOverride)
3167 {
3168 #ifndef NO_BIDI_SUPPORT
3169 // we cannot use the draw buffer here because in case of ligatures it might
3170 // contain characters of misleading directional properties
3171 fp_TextRun * pRun = this;
3172 UT_uint32 iLen = getLength(); // need to remember this, since
3173 //
3174 // getLength() will change if we split
3175
3176 if(!iLen)
3177 return;
3178
3179 PT_BlockOffset currOffset = getBlockOffset();
3180 UT_BidiCharType iPrevType, iType = UT_BIDI_UNSET;
3181
3182 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
3183 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
3184
3185 UT_UCS4Char c = text[currOffset + fl_BLOCK_STRUX_OFFSET];
3186 UT_return_if_fail(text.getStatus() == UTIter_OK);
3187
3188 iPrevType = iType = UT_bidiGetCharType(c);
3189
3190 if(iLen == 1)
3191 {
3192 // there is obviously nothing to break, but we need to ensure
3193 // that the run has its direction set correctly (e.g., if
3194 // run contained one strong character and one white space, and
3195 // the strong char was deleted, we need to set direction to
3196 // what is appropriate for the whitespace)
3197 setDirection(iType, UT_BIDI_IGNORE);
3198 return;
3199 }
3200
3201 while(currOffset < (getBlockOffset() + iLen))
3202 {
3203 while(iPrevType == iType && (currOffset < (getBlockOffset() + iLen - 1)))
3204 {
3205 currOffset++;
3206 c = text[currOffset + fl_BLOCK_STRUX_OFFSET];
3207 UT_return_if_fail(text.getStatus() == UTIter_OK);
3208
3209 iType = UT_bidiGetCharType(c);
3210 }
3211
3212 // if we reached the end of the origianl run, then stop
3213 if(currOffset > (getBlockOffset() + iLen - 1) || iType == iPrevType)
3214 {
3215 pRun->setDirection(iPrevType, iNewOverride);
3216 break;
3217 }
3218
3219 // so we know where the continuos fragment ends ...
3220 pRun->split(currOffset);
3221 pRun->setDirection(iPrevType, iNewOverride);
3222 UT_ASSERT(pRun->getNextRun() && pRun->getNextRun()->getType() == FPRUN_TEXT);
3223 pRun = static_cast<fp_TextRun*>(pRun->getNextRun());
3224 iPrevType = iType;
3225 }
3226 #endif
3227 }
3228
3229
3230 /*!
3231 The following function allows us to respond to deletion of part of
3232 the text represented by this run in a smart way (smart here means
3233 avoiding recalculating the draw buffer when we do not have to)
3234
3235 \param offset: run offset at which deletion starts
3236 \param iLen: length of the deleted section, can reach past the
3237 end of the run
3238 */
updateOnDelete(UT_uint32 offset,UT_uint32 iLenToDelete)3239 void fp_TextRun::updateOnDelete(UT_uint32 offset, UT_uint32 iLenToDelete)
3240 {
3241 // do not try to delete after the end of the run ...
3242 UT_return_if_fail(offset < getLength());
3243
3244 // calculate actual length of deletion in this run
3245 UT_sint32 iLen = UT_MIN(static_cast<UT_sint32>(iLenToDelete), static_cast<UT_sint32>(getLength()) - static_cast<UT_sint32>(offset));
3246
3247 // do not try to delete nothing ...
3248 if(iLen == 0)
3249 return;
3250
3251 UT_sint32 iLenOrig = getLength();
3252
3253 // construct a text iterator to speed things up
3254 PD_StruxIterator text(getBlock()->getStruxDocHandle(),
3255 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
3256
3257 if((iLenOrig - iLen) == 0)
3258 {
3259 // this whole run will be deleted ...
3260 goto set_length;
3261 }
3262 xxx_UT_DEBUGMSG(("Doing updateOnDelete %x length of run %d amount to delete %d iLen %d \n",this,getLength(),iLenToDelete,iLen));
3263 if(m_pRenderInfo)
3264 {
3265 m_pRenderInfo->m_iLength = iLenOrig;
3266 m_pRenderInfo->m_iVisDir = getVisDirection();
3267 m_pRenderInfo->m_eState = _getRefreshDrawBuffer();
3268 m_pRenderInfo->m_pText = &text;
3269 if(!m_pRenderInfo->cut(offset,iLen))
3270 {
3271 // mark draw buffer dirty ...
3272 orDrawBufferDirty(GRSR_Unknown);
3273 }
3274 }
3275 if(!m_pRenderInfo)
3276 {
3277 // mark draw buffer dirty ...
3278 orDrawBufferDirty(GRSR_Unknown);
3279 }
3280
3281
3282 set_length:
3283 // now set length without marking width and draw buffer dirty
3284 setLength(iLenOrig - iLen, false);
3285
3286 // mark width dirty manually
3287 markWidthDirty();
3288
3289 // if deletion started at offset 0, the previous run will be
3290 // effected if it contains context sensitive characters ...
3291 if(!offset && getPrevRun())
3292 {
3293 fp_Run * pRun = getPrevRun();
3294
3295 // ignore fmt marks, hyperlinks and bookmarks ...
3296 while(pRun &&
3297 ( pRun->getType() == FPRUN_FMTMARK
3298 || pRun->getType() == FPRUN_HYPERLINK
3299 || pRun->getType() == FPRUN_BOOKMARK))
3300 {
3301 pRun = pRun->getPrevRun();
3302 }
3303
3304 if(pRun)
3305 {
3306 if(pRun->getType() == FPRUN_TEXT)
3307 {
3308 fp_TextRun * pT = static_cast<fp_TextRun*>(pRun);
3309
3310 if(pT->m_pRenderInfo && pT->m_pRenderInfo->m_eShapingResult == GRSR_ContextSensitive)
3311 {
3312 pT->orDrawBufferDirty(GRSR_ContextSensitive);
3313 }
3314 else if(!pT->m_pRenderInfo)
3315 {
3316 pT->orDrawBufferDirty(GRSR_Unknown);
3317 }
3318
3319 }
3320 else
3321 pRun->orDrawBufferDirty(GRSR_ContextSensitive);
3322 }
3323 }
3324
3325 if((static_cast<UT_sint32>(offset) + iLen == iLenOrig) && getNextRun())
3326 {
3327 fp_Run * pRun = getNextRun();
3328
3329 // ignore fmt marks, hyperlinks and bookmarks ...
3330 while(pRun &&
3331 ( pRun->getType() == FPRUN_FMTMARK
3332 || pRun->getType() == FPRUN_HYPERLINK
3333 || pRun->getType() == FPRUN_BOOKMARK))
3334 {
3335 pRun = pRun->getNextRun();
3336 }
3337
3338 if(pRun)
3339 {
3340 if(pRun->getType() == FPRUN_TEXT)
3341 {
3342 fp_TextRun * pT = static_cast<fp_TextRun*>(pRun);
3343
3344 if(pT->m_pRenderInfo && pT->m_pRenderInfo->m_eShapingResult == GRSR_ContextSensitive)
3345 {
3346 pT->orDrawBufferDirty(GRSR_ContextSensitive);
3347 }
3348 else if(!pT->m_pRenderInfo)
3349 {
3350 pT->orDrawBufferDirty(GRSR_Unknown);
3351 }
3352 }
3353 else
3354 pRun->orDrawBufferDirty(GRSR_ContextSensitive);
3355 }
3356 }
3357 }
3358
adjustCaretPosition(UT_uint32 iDocumentPosition,bool bForward)3359 UT_uint32 fp_TextRun::adjustCaretPosition(UT_uint32 iDocumentPosition, bool bForward)
3360 {
3361 UT_uint32 iRunOffset = getBlockOffset() + getBlock()->getPosition();
3362
3363 UT_return_val_if_fail( iDocumentPosition >= iRunOffset && iDocumentPosition <= iRunOffset + getLength() &&
3364 m_pRenderInfo,
3365 iDocumentPosition);
3366
3367 PD_StruxIterator * text = new PD_StruxIterator(getBlock()->getStruxDocHandle(),
3368 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
3369
3370 UT_return_val_if_fail(text->getStatus() == UTIter_OK, iDocumentPosition);
3371 xxx_UT_DEBUGMSG(("sdh %p text->getPosition() %d getLength() %d \n",getBlock()->getStruxDocHandle(),text->getPosition(),getLength()));
3372 text->setUpperLimit(text->getPosition() + getLength() - 1);
3373 xxx_UT_DEBUGMSG(("text->getUpperLimit() %d \n",text->getUpperLimit()));
3374 // DELETEP(m_pRenderInfo->m_pText);
3375 m_pRenderInfo->m_pText = text;
3376 m_pRenderInfo->m_iOffset = iDocumentPosition - iRunOffset;
3377 m_pRenderInfo->m_iLength = getLength();
3378 if(!getGraphics()->needsSpecialCaretPositioning(*m_pRenderInfo))
3379 {
3380 DELETEP(text);
3381 m_pRenderInfo->m_pText = NULL;
3382 return iDocumentPosition;
3383 }
3384 UT_uint32 adjustedPos = iRunOffset + getGraphics()->adjustCaretPosition(*m_pRenderInfo, bForward);
3385 DELETEP(text);
3386 m_pRenderInfo->m_pText = NULL;
3387 if((adjustedPos - iRunOffset) > getLength())
3388 adjustedPos = iRunOffset + getLength();
3389 _refreshDrawBuffer();
3390 return adjustedPos;
3391 }
3392
adjustDeletePosition(UT_uint32 & iDocumentPosition,UT_uint32 & iCount)3393 void fp_TextRun::adjustDeletePosition(UT_uint32 &iDocumentPosition, UT_uint32 &iCount)
3394 {
3395 UT_uint32 iRunOffset = getBlockOffset() + getBlock()->getPosition();
3396
3397 UT_return_if_fail( iDocumentPosition >= iRunOffset && iDocumentPosition < iRunOffset + getLength() &&
3398 m_pRenderInfo);
3399
3400 PD_StruxIterator * text = new PD_StruxIterator(getBlock()->getStruxDocHandle(),
3401 getBlockOffset() + fl_BLOCK_STRUX_OFFSET);
3402
3403 UT_return_if_fail(text->getStatus() == UTIter_OK);
3404 xxx_UT_DEBUGMSG(("sdh %p text->getPosition() %d getLength() %d \n",getBlock()->getStruxDocHandle(),text->getPosition(),getLength()));
3405 text->setUpperLimit(text->getPosition() + getLength() - 1);
3406 xxx_UT_DEBUGMSG(("text->getUpperLimit() %d \n",text->getUpperLimit()));
3407
3408 m_pRenderInfo->m_pText = text;
3409 m_pRenderInfo->m_iOffset = iDocumentPosition - iRunOffset;
3410 m_pRenderInfo->m_iLength = iCount;
3411 if(!getGraphics()->needsSpecialCaretPositioning(*m_pRenderInfo))
3412 {
3413 DELETEP(text);
3414 m_pRenderInfo->m_pText = NULL;
3415 return;
3416 }
3417 getGraphics()->adjustDeletePosition(*m_pRenderInfo);
3418
3419 iDocumentPosition = iRunOffset + m_pRenderInfo->m_iOffset;
3420 iCount = m_pRenderInfo->m_iLength;
3421 DELETEP(text);
3422 m_pRenderInfo->m_pText = NULL;
3423 }
3424
3425