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*>(&ltr[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