1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3  * Copyright (C) 1998,1999 AbiSource, Inc.
4  * BIDI 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 <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <math.h>
31 
32 #include "fl_BlockLayout.h"
33 #include "pf_Frag_Strux.h"
34 #ifdef ENABLE_SPELL
35 #include "fl_Squiggles.h"
36 #endif
37 #include "fl_Layout.h"
38 #include "fl_DocLayout.h"
39 #include "fl_SectionLayout.h"
40 #include "fl_FootnoteLayout.h"
41 #include "fl_TableLayout.h"
42 #include "fl_AutoNum.h"
43 #include "fl_TOCLayout.h"
44 #include "fb_LineBreaker.h"
45 #include "fb_Alignment.h"
46 #include "fp_Column.h"
47 #include "fp_FootnoteContainer.h"
48 #include "fp_TableContainer.h"
49 #include "fp_Line.h"
50 #include "fp_Run.h"
51 #include "fp_TextRun.h"
52 #include "fp_FieldListLabelRun.h"
53 #include "fp_DirectionMarkerRun.h"
54 #include "fp_FrameContainer.h"
55 #include "pd_Document.h"
56 #include "fd_Field.h"
57 #include "pd_Style.h"
58 #include "pp_Property.h"
59 #include "pp_AttrProp.h"
60 #include "pt_Types.h"
61 #include "gr_Graphics.h"
62 
63 #ifdef ENABLE_SPELL
64 #include "spell_manager.h"
65 
66 #if 1
67 // todo: work around to remove the INPUTWORDLEN restriction for pspell
68 #include "ispell_def.h"
69 #endif
70 #endif
71 
72 #include "px_CR_FmtMark.h"
73 #include "px_CR_FmtMarkChange.h"
74 #include "px_CR_Object.h"
75 #include "px_CR_ObjectChange.h"
76 #include "px_CR_Span.h"
77 #include "px_CR_SpanChange.h"
78 #include "px_CR_Strux.h"
79 #include "px_CR_StruxChange.h"
80 #include "pd_Iterator.h"
81 #include "fv_View.h"
82 #include "xap_App.h"
83 #include "xap_Clipboard.h"
84 #include "ut_png.h"
85 #include "ut_sleep.h"
86 #include "fg_Graphic.h"
87 #include "ap_Prefs.h"
88 #include "ap_Prefs_SchemeIds.h"
89 #include "ut_rand.h"
90 #include "fp_FieldTOCNum.h"
91 #include "ut_debugmsg.h"
92 #include "ut_assert.h"
93 #include "ut_string.h"
94 #include "fp_MathRun.h"
95 #include "fp_EmbedRun.h"
96 
97 #include "xap_EncodingManager.h"
98 
99 #define BIG_NUM_BLOCKBL 1000000
100 
101 
102 static void s_border_properties (const char * border_color,
103 								 const char * border_style,
104 								 const char * border_width,
105 								 const char * color,
106 								 const char * spacing, PP_PropertyMap::Line & line);
107 
108 
109 
110 #ifdef ENABLE_SPELL
111 SpellChecker *
_getSpellChecker(UT_uint32 blockPos) const112 fl_BlockLayout::_getSpellChecker (UT_uint32 blockPos) const
113 {
114 	// the idea behind the static's here is to cache the dictionary, so
115 	// we do not have to do dictionary lookup all the time; rather, we
116 	// will cache the AP's and checker, and if the AP's have not
117 	// changed we will reuse the previous dictionary
118 	// this works because when the PT is asked to change formatting,
119 	// it will create a new AP with the new attr/props, rather than
120 	// add them to the existing AP for the section of the document, so
121 	// that identical AP's always imply identical formatting, and thus
122 	// language
123 	//
124 	// Unfortunately, this does not work as intented because if the APs do not contain
125 	// explicit lang property and the default language for document changes, we need to
126 	// get a different checker. We therefore have to evaluate the property on all
127 	// occasions and remember the language, not the APs (bug #9562)
128 
129 	static SpellChecker * checker = NULL;
130 	static char szPrevLang[8] = {0};
131 
132 	const PP_AttrProp * pSpanAP = NULL;
133 	const PP_AttrProp * pBlockAP = NULL;
134 
135 	getSpanAP(blockPos, false, pSpanAP);
136 	getAP(pBlockAP);
137 
138 	const char * pszLang = static_cast<const char *>(PP_evalProperty("lang",pSpanAP,pBlockAP,NULL,m_pDoc,true));
139 	if(!pszLang || !*pszLang)
140 	{
141 		// we just (dumbly) default to the last dictionary
142 		checker = SpellManager::instance().lastDictionary();
143 		return checker;
144 	}
145 
146 	if(!szPrevLang[0] || strcmp(pszLang,szPrevLang))
147 	{
148 		checker = SpellManager::instance().requestDictionary(pszLang);
149 
150 		strncpy(szPrevLang, pszLang, sizeof(szPrevLang));
151 		UT_uint32 iEnd = UT_MIN(sizeof(szPrevLang)-1, strlen(pszLang));
152 		szPrevLang[iEnd] = 0;
153 	}
154 
155 	return checker;
156 }
157 
158 bool
_spellCheckWord(const UT_UCSChar * word,UT_uint32 len,UT_uint32 blockPos) const159 fl_BlockLayout::_spellCheckWord(const UT_UCSChar * word,
160 								UT_uint32 len, UT_uint32 blockPos) const
161 {
162 	SpellChecker * checker = _getSpellChecker (blockPos);
163 
164 	if (!checker)
165 	{
166 		// no checker found, don't mark as wrong
167 		return true;
168 	}
169 
170 	if (checker->checkWord (word, len) == SpellChecker::LOOKUP_SUCCEEDED)
171 		return true;
172 	return false;
173 }
174 #endif // ENABLE_SPELL
175 
176 
177 //////////////////////////////////////////////////////////////////
178 //////////////////////////////////////////////////////////////////
179 
fl_BlockLayout(pf_Frag_Strux * sdh,fl_ContainerLayout * pPrev,fl_SectionLayout * pSectionLayout,PT_AttrPropIndex indexAP,bool bIsHdrFtr)180 fl_BlockLayout::fl_BlockLayout(pf_Frag_Strux* sdh,
181 							   fl_ContainerLayout* pPrev,
182 							   fl_SectionLayout* pSectionLayout,
183 							   PT_AttrPropIndex indexAP, bool bIsHdrFtr)
184 	: fl_ContainerLayout(pSectionLayout,sdh,indexAP,PTX_Block,FL_CONTAINER_BLOCK),
185 	  m_uBackgroundCheckReasons(0),
186 	  m_iNeedsReformat(0),
187 	  m_bNeedsRedraw(false),
188 	  m_bIsHdrFtr(bIsHdrFtr),
189 	  m_pFirstRun(NULL),
190 	  m_pSectionLayout(pSectionLayout),
191 	  m_pAlignment(NULL),
192 	  m_bKeepTogether(false),
193 	  m_bKeepWithNext(false),
194 	  m_bStartList(false), m_bStopList(false),
195 	  m_bListLabelCreated(false),
196 #ifdef ENABLE_SPELL
197  	  m_pSpellSquiggles(NULL),
198 	  m_pGrammarSquiggles(NULL),
199 	  m_nextToSpell(0),
200 	  m_prevToSpell(0),
201 #endif
202 	  m_bListItem(false),
203 	  m_szStyle(NULL),
204 	  m_bIsCollapsed(true),
205 	  m_iDomDirection(UT_BIDI_UNSET),
206 	  m_iDirOverride(UT_BIDI_UNSET),
207 	  m_bIsTOC(false),
208 	  m_bStyleInTOC(false),
209 	  m_iTOCLevel(0),
210 	  m_bSameYAsPrevious(false),
211 	  m_iAccumulatedHeight(0),
212 	  m_pVertContainer(NULL),
213 	  m_iLinePosInContainer(0),
214 	  m_bForceSectionBreak(false),
215 	  m_bPrevListLabel(false),
216 	  m_iAdditionalMarginAfter(0),
217 	  m_ShadingForeColor(0,0,0),
218 	  m_ShadingBackColor(0,0,0),
219 	  m_iPattern(0),
220 	  m_bCanMergeBordersWithNext(true),
221 	  m_bHasBorders(false)
222 {
223 	xxx_UT_DEBUGMSG(("BlockLayout %x created sdh %x \n",this,getStruxDocHandle()));
224 	setPrev(pPrev);
225 	UT_ASSERT(myContainingLayout() != NULL);
226 
227 	// insert us into the list
228 	if (pPrev)
229 		pPrev->_insertIntoList(this);
230 	else
231 	{
232 		setNext(myContainingLayout()->getFirstLayout());
233 		if (myContainingLayout()->getFirstLayout())
234 			myContainingLayout()->getFirstLayout()->setPrev(this);
235 	}
236 
237 	if(m_pSectionLayout && m_pSectionLayout->getType() == FL_SECTION_HDRFTR)
238 	{
239 		m_bIsHdrFtr = true;
240 	}
241 	m_pLayout = m_pSectionLayout->getDocLayout();
242 	m_pDoc = m_pLayout->getDocument();
243 	UT_ASSERT(m_pDoc);
244 	setAttrPropIndex(indexAP);
245 
246 	const PP_AttrProp * pAP = 0;
247 	getAP(pAP);
248     UT_ASSERT_HARMLESS(pAP);
249 
250 	if (pAP && !pAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, m_szStyle))
251 		{
252 			m_szStyle = NULL;
253 		}
254 	m_bIsTOC = (pSectionLayout->getContainerType() == FL_CONTAINER_TOC);
255 	if(m_bIsTOC)
256 	{
257 		fl_TOCLayout * pTOCL= static_cast<fl_TOCLayout *>(getSectionLayout());
258 		m_iTOCLevel = pTOCL->getCurrentLevel();
259 	}
260 	if (m_szStyle != NULL)
261 	{
262 		PD_Style * pStyle = NULL;
263 		m_pDoc->getStyle(static_cast<const char*>(m_szStyle), &pStyle);
264 		if(pStyle != NULL)
265 		{
266 			pStyle->used(1);
267 			UT_sint32 iLoop = 0;
268 			while((pStyle->getBasedOn()) != NULL && (iLoop < 10))
269 			{
270 				pStyle->getBasedOn()->used(1);
271 				pStyle = pStyle->getBasedOn();
272 				iLoop++;
273 			}
274 		}
275 	}
276 	lookupProperties();
277 	//
278 	// Since the Style doesn't change we need to look to see if this
279 	// block should added now. We need to wait until after the lookupProperties
280 	// to get the field list label inserted.
281 	//
282 	if(!m_bIsTOC)
283 	{
284 		if(!isNotTOCable())
285 		{
286 			m_bStyleInTOC = m_pLayout->addOrRemoveBlockFromTOC(this);
287 		}
288 	}
289 
290 	if(!isHdrFtr() || (static_cast<fl_HdrFtrSectionLayout *>(getSectionLayout())->getDocSectionLayout() != NULL))
291 	{
292 		_insertEndOfParagraphRun();
293 	}
294 
295 #ifdef ENABLE_SPELL
296 	m_pSpellSquiggles = new fl_SpellSquiggles(this);
297 	m_pGrammarSquiggles = new fl_GrammarSquiggles(this);
298 	UT_ASSERT(m_pSpellSquiggles);
299 	UT_ASSERT(m_pGrammarSquiggles);
300 #endif
301 	setUpdatableField(false);
302 	updateEnclosingBlockIfNeeded();
303 	if (hasBorders())
304 	{
305 		if (pPrev && pPrev->getContainerType() == FL_CONTAINER_BLOCK)
306 		{
307 			fl_BlockLayout* pBPrev = static_cast<fl_BlockLayout *>(pPrev);
308 			if (pBPrev->hasBorders())
309 			{
310 				pBPrev->setLineHeightBlockWithBorders(-1);
311 			}
312 		}
313 	}
314 }
315 
fl_TabStop()316 fl_TabStop::fl_TabStop()
317 {
318 	iPosition = 0;
319 	iType = FL_TAB_NONE;
320 	iLeader = FL_LEADER_NONE;
321 }
322 
compare_tabs(const void * p1,const void * p2)323 static int compare_tabs(const void* p1, const void* p2)
324 {
325 	const fl_TabStop * const * ppTab1 = reinterpret_cast<const fl_TabStop * const *>(p1);
326 	const fl_TabStop * const * ppTab2 = reinterpret_cast<const fl_TabStop * const *>(p2);
327 
328 	if ((*ppTab1)->getPosition() < (*ppTab2)->getPosition())
329 	{
330 		return -1;
331 	}
332 
333 	if ((*ppTab1)->getPosition() > (*ppTab2)->getPosition())
334 	{
335 		return 1;
336 	}
337 
338 	return 0;
339 }
340 
buildTabStops(const char * pszTabStops,UT_GenericVector<fl_TabStop * > & m_vecTabs)341 void buildTabStops(const char* pszTabStops, UT_GenericVector<fl_TabStop*> &m_vecTabs)
342 {
343 	// no matter what, clear prior tabstops
344 	UT_uint32 iCount = m_vecTabs.getItemCount();
345 	UT_uint32 i;
346 
347 	for (i=0; i<iCount; i++)
348 	{
349 		fl_TabStop* pTab = m_vecTabs.getNthItem(i);
350 
351 		delete pTab;
352 	}
353 
354 	m_vecTabs.clear();
355 
356 	if (pszTabStops && pszTabStops[0])
357 	{
358 		eTabType	iType = FL_TAB_NONE;
359 		eTabLeader	iLeader = FL_LEADER_NONE;
360 		UT_sint32	iPosition = 0;
361 
362 		const char* pStart = pszTabStops;
363 		while (*pStart)
364 		{
365 			const char* pEnd = pStart;
366 			while (*pEnd && (*pEnd != ','))
367 			{
368 				pEnd++;
369 			}
370 
371 			const char* p1 = pStart;
372 			while ((p1 < pEnd) && (*p1 != '/'))
373 			{
374 				p1++;
375 			}
376 
377 			if (
378 				(p1 == pEnd)
379 				|| ((p1+1) == pEnd)
380 				)
381 			{
382 				iType = FL_TAB_LEFT;
383 			}
384 			else
385 			{
386 				switch (p1[1])
387 				{
388 				case 'R':
389 					iType = FL_TAB_RIGHT;
390 					break;
391 				case 'C':
392 					iType = FL_TAB_CENTER;
393 					break;
394 				case 'D':
395 					iType = FL_TAB_DECIMAL;
396 					break;
397 				case 'B':
398 					iType = FL_TAB_BAR;
399 					break;
400 				case 'L':
401 					iType = FL_TAB_LEFT;
402 					break;
403 				default:
404 					iType = FL_TAB_LEFT;
405 					UT_DEBUGMSG(("tabstop: unknown tab stop type [%c]\n", p1[1]));
406 					break;
407 				}
408 
409 				// tab leaders
410 				if ( p1 +2 != pEnd && p1[2] >= '0' && p1[2] <= ((static_cast<UT_sint32>(__FL_LEADER_MAX))+'0') )
411 					iLeader = static_cast<eTabLeader>(p1[2]-'0');
412 			}
413 
414 			char pszPosition[32];
415 			UT_uint32 iPosLen = p1 - pStart;
416 
417 			UT_ASSERT(iPosLen < sizeof pszPosition);
418 
419 			memcpy(pszPosition, pStart, iPosLen);
420 			pszPosition[iPosLen] = 0;
421 
422 			iPosition = UT_convertToLogicalUnits(pszPosition);
423 
424 			UT_ASSERT(iType > 0);
425 			/*
426 			  The following assert is probably bogus, since tabs are
427 			  column-relative, rather than block-relative.
428 			*/
429 //			UT_ASSERT(iPosition >= 0);
430 
431 			fl_TabStop* pTabStop = new fl_TabStop();
432 			pTabStop->setPosition(iPosition);
433 			pTabStop->setType(iType);
434 			pTabStop->setLeader(iLeader);
435 			pTabStop->setOffset(pStart - pszTabStops);
436 
437 			m_vecTabs.addItem(pTabStop);
438 
439 			pStart = pEnd;
440 			if (*pStart)
441 			{
442 				pStart++;	// skip past delimiter
443 
444 				while (*pStart == UCS_SPACE)
445 				{
446 					pStart++;
447 				}
448 			}
449 		}
450 
451 		m_vecTabs.qsort(compare_tabs);
452 	}
453 }
454 
455 /*!
456  * This method is used to reset the colorization such as what occurs
457  * when showAuthors state is changed.
458  */
refreshRunProperties(void) const459 void fl_BlockLayout::refreshRunProperties(void) const
460 {
461 	fp_Run * pRun = getFirstRun();
462 	while(pRun)
463 	{
464 		pRun->lookupProperties();
465 		pRun = pRun->getNextRun();
466 	}
467 }
468 
469 /*!
470     this function is only to be called by fl_ContainerLayout::lookupMarginProperties()
471     all other code must call lookupMarginProperties() instead
472 
473     This function looks up the block margins and handles the values appropriately to the
474     type of current view mode
475 */
_lookupMarginProperties(const PP_AttrProp * pBlockAP)476 void fl_BlockLayout::_lookupMarginProperties(const PP_AttrProp* pBlockAP)
477 {
478 	UT_return_if_fail(pBlockAP);
479 
480 	UT_ASSERT(myContainingLayout() != NULL);
481  	FV_View * pView = getView();
482 	UT_return_if_fail( pView );
483 
484 	GR_Graphics* pG = m_pLayout->getGraphics();
485 
486 	UT_sint32 iTopMargin = m_iTopMargin;
487 	UT_sint32 iBottomMargin = m_iBottomMargin;
488 	UT_sint32 iLeftMargin = m_iLeftMargin;
489 	UT_sint32 iRightMargin = m_iRightMargin;
490 	UT_sint32 iTextIndent = getTextIndent();
491 
492 	struct MarginAndIndent_t
493 	{
494 		const char* szProp;
495 		UT_sint32*	pVar;
496 	}
497 	const rgProps[] =
498 	{
499 		{ "margin-top", 	&m_iTopMargin    },
500 		{ "margin-bottom",	&m_iBottomMargin },
501 		{ "margin-left",	&m_iLeftMargin,  },
502 		{ "margin-right",	&m_iRightMargin, },
503 		{ "text-indent",	&m_iTextIndent,  }
504 	};
505 	for (UT_uint32 iRg = 0; iRg < G_N_ELEMENTS(rgProps); ++iRg)
506 	{
507 		const MarginAndIndent_t& mai = rgProps[iRg];
508 		const PP_PropertyTypeSize * pProp =
509 			static_cast<const PP_PropertyTypeSize *>(getPropertyType(static_cast<const gchar*>(mai.szProp),
510 																	 Property_type_size));
511 
512 		*mai.pVar	= UT_convertSizeToLayoutUnits(pProp->getValue(), pProp->getDim());
513 		xxx_UT_DEBUGMSG(("para prop %s layout size %d \n",mai.szProp,*mai.pVar));
514 	}
515 
516 	if((pView->getViewMode() == VIEW_NORMAL) || ((pView->getViewMode() == VIEW_WEB) && !pG->queryProperties(GR_Graphics::DGP_PAPER)))
517 	{
518 		if(m_iLeftMargin < 0)
519 		{
520 			m_iLeftMargin = 0;
521 		}
522 
523 		if(getTextIndent() < 0)
524 		{
525 			// shuv the whole thing to the left
526 			m_iLeftMargin -= getTextIndent();
527 		}
528 
529 		// igonre right margin
530 		m_iRightMargin = 0;
531 	}
532 
533 	// NOTE : Parsing spacing strings:
534 	// NOTE : - if spacing string ends with "+", it's marked as an "At Least" measurement
535 	// NOTE : - if spacing has a unit in it, it's an "Exact" measurement
536 	// NOTE : - if spacing is a unitless number, it's just a "Multiple"
537     // 	UT_uint32 nLen = strlen(pszSpacing);
538 	// this assumed that only spacing 1 can be represented by a single charcter
539 	// but that is not very safe assumption, for there should be nothing stoping
540 	// us to use 2 or 3 in place of 2.0 or 3.0, so I commented this this out
541 	// Tomas 21/1/2002
542 	const char * pszSpacing = getProperty("line-height");
543 	const char * pPlusFound = strrchr(pszSpacing, '+');
544 	eSpacingPolicy spacingPolicy = m_eSpacingPolicy;
545 	double dLineSpacing = m_dLineSpacing;
546 
547 	if (pPlusFound && *(pPlusFound + 1) == 0)
548 	{
549 		m_eSpacingPolicy = spacing_ATLEAST;
550 
551 		// need to strip the plus first
552 		int posPlus = pPlusFound - pszSpacing;
553 		UT_ASSERT(posPlus>=0);
554 		UT_ASSERT(posPlus<100);
555 
556 		UT_String pTmp(pszSpacing);
557 		pTmp[posPlus] = 0;
558 
559 		m_dLineSpacing = UT_convertToLogicalUnits(pTmp.c_str());
560 	}
561 	else if (UT_hasDimensionComponent(pszSpacing))
562 	{
563 		m_eSpacingPolicy = spacing_EXACT;
564 		m_dLineSpacing = UT_convertToLogicalUnits(pszSpacing);
565 
566 	}
567 	else
568 	{
569 		m_eSpacingPolicy = spacing_MULTIPLE;
570 		m_dLineSpacing =
571 			UT_convertDimensionless(pszSpacing);
572 	}
573 
574 	if((pView->getViewMode() == VIEW_NORMAL) || ((pView->getViewMode() == VIEW_WEB) && !pG->queryProperties(GR_Graphics::DGP_PAPER)))
575 	{
576 		// flatten the text; we will indicate more than single spacing by using 1.2, which
577 		// is enough for the text to be noticeably spaced, but not enough for it to take
578 		// too much space
579 		m_eSpacingPolicy = spacing_MULTIPLE;
580 
581 		double dSpacing1 = UT_convertDimensionless("1.2");
582 		if(m_dLineSpacing > dSpacing1)
583 			m_dLineSpacing = UT_convertDimensionless("1.2");
584 	}
585 
586 
587 	UT_sint32 i = 0;
588 	for(i=0; i< getNumFrames();i++)
589 	{
590 		fl_FrameLayout * pFrame = getNthFrameLayout(i);
591 
592 		if(pFrame->isHidden() > FP_VISIBLE)
593 			continue;
594 
595 		if(pFrame->getContainerType() != FL_CONTAINER_FRAME)
596 		{
597 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
598 			continue;
599 		}
600 
601 		pFrame->lookupMarginProperties();
602 	}
603 
604 
605 	if(iTopMargin != m_iTopMargin || iBottomMargin != m_iBottomMargin ||
606 	   iLeftMargin != m_iLeftMargin || iRightMargin != m_iRightMargin || iTextIndent != getTextIndent() ||
607 	   spacingPolicy != m_eSpacingPolicy || dLineSpacing != m_dLineSpacing)
608 	{
609 		collapse();
610 	}
611 }
612 
613 /*!
614     this function is only to be called by fl_ContainerLayout::lookupProperties()
615     all other code must call lookupProperties() instead
616 */
_lookupProperties(const PP_AttrProp * pBlockAP)617 void fl_BlockLayout::_lookupProperties(const PP_AttrProp* pBlockAP)
618 {
619 	UT_return_if_fail(pBlockAP);
620 
621 	{
622 		// The EOP Run is an integral part of the block so also make
623 		// sure it does lookup.
624 
625 		fp_Line* pLine = static_cast<fp_Line *>(getLastContainer());
626 		if (pLine)
627 		{
628 			fp_Run* pRun = pLine->getLastRun();
629 			pRun->lookupProperties();
630 		}
631 	}
632 	UT_ASSERT(myContainingLayout() != NULL);
633 	UT_UTF8String sOldStyle("");
634 	if(m_szStyle)
635 	{
636 		sOldStyle = m_szStyle;
637 	}
638 	if(!pBlockAP)
639 	{
640 		m_szStyle = NULL;
641 	}
642 	else if (!pBlockAP->getAttribute(PT_STYLE_ATTRIBUTE_NAME, m_szStyle))
643 	{
644 		m_szStyle = NULL;
645 	}
646 	UT_UTF8String sNewStyle("");
647 	if(m_szStyle)
648 	{
649 		sNewStyle = m_szStyle;
650 	}
651 	// Now work out our dominant direction
652 	// First, test if this is not a block that is the wrapper around a
653 	// footnote text, if it is, we will get the direction from the
654 	// document section that contains the footnote
655 	const gchar * pszFntId = NULL;
656 	const gchar * pszDir = NULL;
657 
658 	if (pBlockAP && pBlockAP->getAttribute("footnote-id", pszFntId ))
659 	{
660 		if(pszFntId && *pszFntId)
661 		{
662 			UT_return_if_fail(m_pSectionLayout->getContainerType() == FL_CONTAINER_FOOTNOTE);
663 			fl_FootnoteLayout   * pFL = (fl_FootnoteLayout*) m_pSectionLayout;
664 			fl_DocSectionLayout * pDSL=	 pFL->getDocSectionLayout();
665 			UT_return_if_fail(pDSL);
666 
667 			const PP_AttrProp * pSectionAP = NULL;
668 			pDSL->getAP(pSectionAP);
669 
670 			pszDir = PP_evalProperty("dom-dir",NULL,NULL,pSectionAP,m_pDoc,false);
671 		}
672 	}
673 
674 	if(!pszDir)
675 	{
676 		pszDir = getProperty("dom-dir", true);
677 	}
678 
679 	UT_BidiCharType iOldDirection = m_iDomDirection;
680 
681  	FV_View * pView = getView();
682 
683 	if(pView && pView->getBidiOrder() != FV_Order_Visual)
684 	{
685 		if(pView->getBidiOrder() == FV_Order_Logical_LTR)
686 			m_iDomDirection = UT_BIDI_LTR;
687 		else
688 			m_iDomDirection = UT_BIDI_RTL;
689 	}
690 	else if(!strcmp(pszDir,"rtl"))
691 	{
692 		m_iDomDirection = UT_BIDI_RTL;
693 	}
694 	else
695 		m_iDomDirection = UT_BIDI_LTR;
696 
697 	// if the direction was previously set and the new dominant
698 	// direction is different, we have to split all runs in this
699 	// block at their direciton boundaries, because the base
700 	// direction influences the visual direciton of weak characters
701 	if(iOldDirection != static_cast<UT_BidiCharType>(UT_BIDI_UNSET) && iOldDirection != m_iDomDirection)
702 	{
703 		fp_Run * pRun = getFirstRun();
704 
705 		while(pRun)
706 		{
707 			if (pRun->getType() == FPRUN_TEXT)
708 			{
709 				fp_TextRun * pTextRun = static_cast<fp_TextRun*>(pRun);
710 
711 				//we get the next run in line prior to breaking this
712 				//one up, so that we do not break those already broken
713 				pRun = pRun->getNextRun();
714 				pTextRun->breakMeAtDirBoundaries(UT_BIDI_IGNORE);
715 			}
716 			else if(pRun->getType() == FPRUN_ENDOFPARAGRAPH)
717 			{
718 				// need to set the direction correctly
719 				pRun->setDirection(m_iDomDirection);
720 				pRun->setVisDirection(m_iDomDirection);
721 				pRun = pRun->getNextRun();
722 			}
723 			else if(pRun->getType() == FPRUN_FIELD)
724 			{
725 				fp_FieldRun * pFR = static_cast<fp_FieldRun*>(pRun);
726 				if(pFR->getFieldType() == FPFIELD_endnote_anch  ||
727 				   pFR->getFieldType() == FPFIELD_endnote_ref   ||
728 				   pFR->getFieldType() == FPFIELD_footnote_anch ||
729 				   pFR->getFieldType() == FPFIELD_footnote_ref)
730 				{
731 					// need to set the direction correctly
732 					pRun->setDirection(m_iDomDirection);
733 					pRun->setVisDirection(m_iDomDirection);
734 					pRun = pRun->getNextRun();
735 				}
736 
737 				pRun = pRun->getNextRun();
738 			}
739 			else
740 				pRun = pRun->getNextRun();
741 		}
742 
743 
744 	}
745 	{
746 		const PP_PropertyTypeInt *pOrphans = static_cast<const PP_PropertyTypeInt *>(getPropertyType("orphans", Property_type_int));
747 		UT_ASSERT_HARMLESS(pOrphans);
748 		if(pOrphans)
749 			m_iOrphansProperty = pOrphans->getValue();
750 
751 		const PP_PropertyTypeInt *pWidows = static_cast<const PP_PropertyTypeInt *>(getPropertyType("widows", Property_type_int));
752 		UT_ASSERT_HARMLESS(pWidows);
753 		if(pWidows)
754 			m_iWidowsProperty = pWidows->getValue();
755 
756 		if (m_iOrphansProperty < 1)
757 		{
758 			m_iOrphansProperty = 1;
759 		}
760 		if (m_iWidowsProperty < 1)
761 		{
762 			m_iWidowsProperty = 1;
763 		}
764 	}
765 
766 	{
767 		const char* pszKeepTogether = getProperty("keep-together");
768 		if (pszKeepTogether)
769 		{
770 			if (0 == strcmp("yes", pszKeepTogether))
771 			{
772 				m_bKeepTogether = true;
773 			}
774 			else
775 			{
776 				m_bKeepTogether = false;
777 			}
778 		}
779 
780 
781 		const char* pszKeepWithNext = getProperty("keep-with-next");
782 		if (pszKeepWithNext)
783 		{
784 			if (0 == strcmp("yes", pszKeepWithNext))
785 			{
786 				m_bKeepWithNext = true;
787 			}
788 			else
789 			{
790 				m_bKeepWithNext = false;
791 			}
792 		}
793 	}
794 
795 	GR_Graphics* pG = m_pLayout->getGraphics();
796 
797 	struct MarginAndIndent_t
798 	{
799 		const char* szProp;
800 		UT_sint32*	pVar;
801 	}
802 	const rgProps[] =
803 	{
804 		{ "margin-top", 	&m_iTopMargin    },
805 		{ "margin-bottom",	&m_iBottomMargin },
806 		{ "margin-left",	&m_iLeftMargin,  },
807 		{ "margin-right",	&m_iRightMargin, },
808 		{ "text-indent",	&m_iTextIndent,  }
809 	};
810 	for (UT_uint32 iRg = 0; iRg < G_N_ELEMENTS(rgProps); ++iRg)
811 	{
812 		const MarginAndIndent_t& mai = rgProps[iRg];
813 		const PP_PropertyTypeSize * pProp = static_cast<const PP_PropertyTypeSize *>(getPropertyType(static_cast<const gchar*>(mai.szProp), Property_type_size));
814 		*mai.pVar	= UT_convertSizeToLayoutUnits(pProp->getValue(), pProp->getDim());
815 		xxx_UT_DEBUGMSG(("para prop %s layout size %d \n",mai.szProp,*mai.pVar));
816 	}
817 
818 	if((pView->getViewMode() == VIEW_NORMAL) || ((pView->getViewMode() == VIEW_WEB) && !pG->queryProperties(GR_Graphics::DGP_PAPER)))
819 	{
820 		if(m_iLeftMargin < 0)
821 		{
822 			m_iLeftMargin = 0;
823 		}
824 
825 		if(getTextIndent() < 0)
826 		{
827 			// shuv the whole thing to the left
828 			m_iLeftMargin -= getTextIndent();
829 		}
830 
831 		// igonre right margin
832 		m_iRightMargin = 0;
833 	}
834 
835 	{
836 		const char* pszAlign = getProperty("text-align");
837 
838 		// we will only delete and reallocate the alignment if it is different
839 		// than the current one
840 		//DELETEP(m_pAlignment);
841 
842 		xxx_UT_DEBUGMSG(("block: _lookupProperties, text-align=%s, current %d\n", pszAlign, m_pAlignment?m_pAlignment->getType():0xffff));
843 
844 		if (0 == strcmp(pszAlign, "left"))
845 		{
846 			if(!m_pAlignment || m_pAlignment->getType() != FB_ALIGNMENT_LEFT)
847 			{
848 				DELETEP(m_pAlignment);
849 				m_pAlignment = new fb_Alignment_left;
850 			}
851 		}
852 		else if (0 == strcmp(pszAlign, "center"))
853 		{
854 			if(!m_pAlignment || m_pAlignment->getType() != FB_ALIGNMENT_CENTER)
855 			{
856 				DELETEP(m_pAlignment);
857 				m_pAlignment = new fb_Alignment_center;
858 			}
859 		}
860 		else if (0 == strcmp(pszAlign, "right"))
861 		{
862 			if(!m_pAlignment || m_pAlignment->getType() != FB_ALIGNMENT_RIGHT)
863 			{
864 				DELETEP(m_pAlignment);
865 				m_pAlignment = new fb_Alignment_right;
866 			}
867 		}
868 		else if (0 == strcmp(pszAlign, "justify"))
869 		{
870 			if(!m_pAlignment || m_pAlignment->getType() != FB_ALIGNMENT_JUSTIFY)
871 			{
872 				DELETEP(m_pAlignment);
873 				m_pAlignment = new fb_Alignment_justify;
874 			}
875 		}
876 		else
877 		{
878 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
879 			if (!m_pAlignment)
880 			{
881 				m_pAlignment = new fb_Alignment_left;
882 			}
883 		}
884 	}
885 
886 	// parse any new tabstops
887 	const char* pszTabStops = getProperty("tabstops");
888 	buildTabStops(pszTabStops, m_vecTabs);
889 
890 
891 #if 0
892 	UT_DEBUGMSG(("XXXX: [default-tab-interval:%s][yields %d][resolution %d][zoom %d]\n",
893 				 getProperty("default-tab-interval"),
894 				 UT_convertToLogicalUnits(getProperty("default-tab-interval")),
895 				 pG->getResolution(),
896 				 pG->getZoomPercentage()));
897 #endif
898 
899 	const PP_PropertyTypeSize * pProp = static_cast<const PP_PropertyTypeSize *>(getPropertyType("default-tab-interval", Property_type_size));
900 	// TODO: this should probably change the stored property instead
901 	m_iDefaultTabInterval = UT_convertSizeToLayoutUnits(pProp->getValue(), pProp->getDim());
902 	if (!m_iDefaultTabInterval)
903 	{
904 		m_iDefaultTabInterval = UT_convertToLogicalUnits("1pt");
905 	}
906 
907 	const char * pszSpacing = getProperty("line-height");
908 
909 	// NOTE : Parsing spacing strings:
910 	// NOTE : - if spacing string ends with "+", it's marked as an "At Least" measurement
911 	// NOTE : - if spacing has a unit in it, it's an "Exact" measurement
912 	// NOTE : - if spacing is a unitless number, it's just a "Multiple"
913     // 	UT_uint32 nLen = strlen(pszSpacing);
914 	// this assumed that only spacing 1 can be represented by a single charcter
915 	// but that is not very safe assumption, for there should be nothing stoping
916 	// us to use 2 or 3 in place of 2.0 or 3.0, so I commented this this out
917 	// Tomas 21/1/2002
918 	// if (nLen > 1)
919 	const char * pPlusFound = strrchr(pszSpacing, '+');
920 	if (pPlusFound && *(pPlusFound + 1) == 0)
921 	{
922 		m_eSpacingPolicy = spacing_ATLEAST;
923 
924 		// need to strip the plus first
925 		int posPlus = pPlusFound - pszSpacing;
926 		UT_ASSERT(posPlus>=0);
927 		UT_ASSERT(posPlus<100);
928 
929 		UT_String pTmp(pszSpacing);
930 		pTmp[posPlus] = 0;
931 
932 		m_dLineSpacing = UT_convertToLogicalUnits(pTmp.c_str());
933 	}
934 	else if (UT_hasDimensionComponent(pszSpacing))
935 	{
936 		m_eSpacingPolicy = spacing_EXACT;
937 		m_dLineSpacing = UT_convertToLogicalUnits(pszSpacing);
938 
939 	}
940 	else
941 	{
942 		m_eSpacingPolicy = spacing_MULTIPLE;
943 		m_dLineSpacing =
944 			UT_convertDimensionless(pszSpacing);
945 	}
946 
947 	if((pView->getViewMode() == VIEW_NORMAL) || ((pView->getViewMode() == VIEW_WEB) && !pG->queryProperties(GR_Graphics::DGP_PAPER)))
948 	{
949 		// flatten the text; we will indicate more than single spacing by using 1.2, which
950 		// is enough for the text to be noticeably spaced, but not enough for it to take
951 		// too much space
952 		m_eSpacingPolicy = spacing_MULTIPLE;
953 
954 		double dSpacing1 = UT_convertDimensionless("1.2");
955 		if(m_dLineSpacing > dSpacing1)
956 			m_dLineSpacing = UT_convertDimensionless("1.2");
957 	}
958 	//
959 	// Shading now
960 	//
961 	{
962 		const gchar * sPattern = NULL;
963 		const gchar * sShadingForeCol = NULL;
964 		const gchar * sShadingBackCol = NULL;
965 		sPattern = getProperty("shading-pattern",true);
966 		if(sPattern)
967 		{
968 			m_iPattern = atoi(sPattern);
969 		}
970 		else
971 		{
972 			m_iPattern = 0;
973 		}
974 		sShadingForeCol = getProperty("shading-foreground-color",true);
975 		if(sShadingForeCol)
976 		{
977 			m_ShadingForeColor.setColor(sShadingForeCol);
978 		}
979 		else
980 		{
981 			m_ShadingForeColor.setColor("white");
982 		}
983 		sShadingBackCol = getProperty("shading-background-color",true);
984 		if(sShadingBackCol)
985 		{
986 			m_ShadingBackColor.setColor(sShadingBackCol);
987 		}
988 		else
989 		{
990 			m_ShadingBackColor.setColor("white");
991 		}
992 
993 	}
994 	//
995 	// Borders now
996 	//
997 	{
998 		m_bHasBorders = false;
999 		m_lineBottom.m_t_linestyle = PP_PropertyMap::linestyle_none;
1000 		m_lineTop.m_t_linestyle =  PP_PropertyMap::linestyle_none;
1001 		m_lineLeft.m_t_linestyle =  PP_PropertyMap::linestyle_none;
1002 		m_lineRight.m_t_linestyle =  PP_PropertyMap::linestyle_none;
1003 		m_bCanMergeBordersWithNext = true;
1004 		const gchar * pszCanMergeBorders = NULL;
1005 		pszCanMergeBorders = getProperty("border-merge");
1006 		if(pszCanMergeBorders && !strcmp(pszCanMergeBorders,"false"))
1007 		{
1008 			m_bCanMergeBordersWithNext = false;
1009 		}
1010 		const gchar * pszBorderColor = NULL;
1011 		const gchar * pszBorderStyle = NULL;
1012 		const gchar * pszBorderWidth = NULL;
1013 		const gchar * pszBorderSpacing = NULL;
1014 		//
1015 		// Default color
1016 		//
1017 		const gchar * pszColor= NULL;
1018 
1019 		pszBorderColor = getProperty ("bot-color");
1020 		pBlockAP->getProperty ("bot-style",pszBorderStyle);
1021 		pszBorderWidth = getProperty ("bot-thickness");
1022 		pszBorderSpacing= getProperty ("bot-space");
1023 		if(pBlockAP && pBlockAP->getProperty ("bot-style",pszBorderStyle) && pszBorderStyle)
1024 		{
1025 			s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, pszBorderSpacing,m_lineBottom);
1026 			m_bHasBorders |= (m_lineBottom.m_t_linestyle > 1);
1027 		}
1028 		pszBorderColor = NULL;
1029 		pszBorderStyle = NULL;
1030 		pszBorderWidth = NULL;
1031 		pszBorderSpacing = NULL;
1032 
1033 		pszBorderColor = getProperty ("left-color");
1034 		pszBorderWidth = getProperty ("left-thickness");
1035 		pszBorderSpacing = getProperty ("left-space");
1036 
1037 		if(pBlockAP && pBlockAP->getProperty ("left-style",pszBorderStyle) && pszBorderStyle)
1038 		{
1039  			s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, pszBorderSpacing,m_lineLeft);
1040 			m_bHasBorders |= (m_lineLeft.m_t_linestyle > 1);
1041 		}
1042 		pszBorderColor = NULL;
1043 		pszBorderStyle = NULL;
1044 		pszBorderWidth = NULL;
1045 		pszBorderSpacing = NULL;
1046 
1047 		pszBorderColor = getProperty ("right-color");
1048 		pszBorderStyle = getProperty ("right-style");
1049 		pszBorderWidth = getProperty ("right-thickness");
1050 		pszBorderSpacing = getProperty ("right-space");
1051 
1052 		if(pBlockAP && pBlockAP->getProperty ("right-style",pszBorderStyle) && pszBorderStyle)
1053 		{
1054 			s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, pszBorderSpacing,m_lineRight);
1055 			m_bHasBorders |= (m_lineRight.m_t_linestyle > 1);
1056 		}
1057 		pszBorderColor = NULL;
1058 		pszBorderStyle = NULL;
1059 		pszBorderWidth = NULL;
1060 		pszBorderSpacing = NULL;
1061 
1062 		pszBorderColor = getProperty ("top-color");
1063 		pszBorderWidth = getProperty ("top-thickness");
1064 		pszBorderSpacing = getProperty ("top-space");
1065 
1066 		if(pBlockAP && pBlockAP->getProperty ("top-style",pszBorderStyle) && pszBorderStyle)
1067 		{
1068 			s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, pszBorderSpacing,m_lineTop);
1069 			m_bHasBorders |= (m_lineTop.m_t_linestyle > 1);
1070 		}
1071 	}
1072 	//
1073 	// No numbering in headers/footers
1074 	//
1075 	if(getSectionLayout() && (getSectionLayout()->getType()== FL_SECTION_HDRFTR))
1076 	{
1077 		return;
1078 	}
1079 
1080 	//const PP_AttrProp * pBlockAP = NULL;
1081 	//getAttrProp(&pBlockAP);
1082 	const gchar * szLid=NULL;
1083 	const gchar * szPid=NULL;
1084 	const gchar * szLevel=NULL;
1085 	UT_uint32 id,parent_id;
1086 
1087 	if (!pBlockAP || !pBlockAP->getAttribute(PT_LISTID_ATTRIBUTE_NAME, szLid))
1088 		szLid = NULL;
1089 	if (szLid)
1090 	{
1091 		id = atoi(szLid);
1092 
1093 	}
1094 	else
1095 		id = 0;
1096 
1097 
1098 	if (!pBlockAP || !pBlockAP->getAttribute(PT_PARENTID_ATTRIBUTE_NAME, szPid))
1099 		szPid = NULL;
1100 	if (szPid)
1101 		parent_id = atoi(szPid);
1102 	else
1103 		parent_id = 0;
1104 
1105 	if (!pBlockAP || !pBlockAP->getAttribute(PT_LEVEL_ATTRIBUTE_NAME, szLevel))
1106 		szLevel = NULL;
1107 
1108 	fl_BlockLayout * prevBlockInList = NULL;
1109 	fl_BlockLayout * nextBlockInList = NULL;
1110 	fl_AutoNum * pAutoNum;
1111 
1112 	if ((m_pAutoNum) && (id) && (m_pAutoNum->getID() != id))
1113 	{
1114 		// We have stopped or started a multi-level list
1115 		// this struxdochandle may already have been removed if there is another
1116 		// view on this document. So check first
1117 
1118 		if(m_pAutoNum->isItem(getStruxDocHandle()))
1119 		{
1120 		   m_pAutoNum->removeItem(getStruxDocHandle());
1121 		}
1122 		m_pAutoNum = NULL;
1123 		UT_DEBUGMSG(("Started/Stopped Multi-Level\n"));
1124 	}
1125 
1126 	if (id == 0 && (m_pAutoNum))
1127 	{
1128 		// We have stopped a final list item.
1129 		m_bStopList = true;
1130 		m_pAutoNum->markAsDirty();
1131 		if(m_pAutoNum->isItem(getStruxDocHandle()))
1132 			m_pAutoNum->removeItem(getStruxDocHandle());
1133 		m_bListItem = false;
1134 		_deleteListLabel();
1135 
1136 		if (m_pAutoNum->isEmpty())
1137 		{
1138 			m_pDoc->removeList(m_pAutoNum,getStruxDocHandle());
1139 			DELETEP(m_pAutoNum);
1140 		}
1141 		else
1142 		{
1143 			m_pAutoNum->update(0);
1144 		}
1145 		m_bStopList = false;
1146 		m_pAutoNum = NULL;
1147 		UT_DEBUGMSG(("Stopped List\n"));
1148 	}
1149 
1150 	if (id != 0 && !m_pAutoNum)
1151 	{
1152 		pAutoNum = m_pDoc->getListByID(id);
1153 		//
1154 		// Create new list if none exists
1155 		//
1156 		if(pAutoNum == NULL)
1157 		{
1158 			const gchar * pszStart = getProperty("start-value",true);
1159 			const gchar * lDelim =  getProperty("list-delim",true);
1160 			const gchar * lDecimal =  getProperty("list-decimal",true);
1161 			UT_uint32 start = atoi(pszStart);
1162 			const gchar * style = NULL;
1163 			style = getProperty("list-style",true);
1164 			if(!style)
1165 			{
1166 				pBlockAP->getAttribute(PT_STYLE_ATTRIBUTE_NAME,style);
1167 			}
1168 			UT_ASSERT(style);
1169 			FL_ListType lType = getListTypeFromStyle( style);
1170 			pAutoNum = new fl_AutoNum(id, parent_id, lType, start, lDelim, lDecimal, m_pDoc, getView());
1171 			UT_DEBUGMSG(("SEVIOR: Created new list id = %d\n",id));
1172 			m_pDoc->addList(pAutoNum);
1173 		}
1174 		UT_ASSERT(pAutoNum);
1175 		m_pAutoNum = pAutoNum;
1176 		m_bListItem = true;
1177 
1178 		prevBlockInList = getPreviousList(id);
1179 		nextBlockInList = getNextList(id);
1180 
1181 		if (prevBlockInList)
1182 			m_pAutoNum->insertItem(getStruxDocHandle(), prevBlockInList->getStruxDocHandle());
1183 		else if (nextBlockInList)
1184 			m_pAutoNum->prependItem(getStruxDocHandle(),nextBlockInList->getStruxDocHandle());
1185 		else
1186 		{
1187 			if (pAutoNum->getParent())
1188 				prevBlockInList = getParentItem();
1189 			else
1190 				prevBlockInList = NULL;
1191 			pf_Frag_Strux* pItem = getStruxDocHandle();
1192 			pf_Frag_Strux* ppItem;
1193 			if(prevBlockInList != NULL )
1194 			{
1195 				ppItem = prevBlockInList->getStruxDocHandle();
1196 			}
1197 			else
1198 			{
1199 				ppItem = NULL;
1200 			}
1201 			m_pAutoNum->insertFirstItem(pItem,ppItem,0);
1202 			m_bStartList = true;
1203 		}
1204 
1205 		xxx_UT_DEBUGMSG(("Added Item to List\n"));
1206 	}
1207 
1208 	// Add this in for loading - see if better way to fix.
1209 	// if (m_bListItem && !m_bListLabelCreated && m_pFirstRun)
1210 	//	_createListLabel();
1211 
1212 	xxx_UT_DEBUGMSG(("BlockLayout %x Folded Level %d sdh %x \n",this,getFoldedLevel(),getStruxDocHandle()));
1213 
1214 	//
1215 	// Look after TOC handling now.
1216 	//
1217 	if(!m_bIsTOC && !(sNewStyle == sOldStyle))
1218 	{
1219 		if(!isNotTOCable())
1220 		{
1221 			if(m_bStyleInTOC)
1222 			{
1223 				m_pLayout->removeBlockFromTOC(this); // remove old one
1224 			}
1225 			m_bStyleInTOC = m_pLayout->addOrRemoveBlockFromTOC(this);
1226 		}
1227 	}
1228 	// later we will need to add here revision handling ...
1229 }
1230 
getPattern(void) const1231 UT_sint32 fl_BlockLayout::getPattern(void) const
1232 {
1233 	return m_iPattern;
1234 }
1235 
getShadingingForeColor(void) const1236 const UT_RGBColor fl_BlockLayout::getShadingingForeColor(void) const
1237 {
1238 	return m_ShadingForeColor;
1239 }
1240 
getShadingingBackColor(void) const1241 const UT_RGBColor fl_BlockLayout::getShadingingBackColor(void) const
1242 {
1243 	return m_ShadingBackColor;
1244 }
1245 
canMergeBordersWithPrev(void) const1246 bool fl_BlockLayout::canMergeBordersWithPrev(void) const
1247 {
1248 	if(!getPrev())
1249 		return false;
1250 	if(getPrev()->getContainerType() !=  FL_CONTAINER_BLOCK)
1251 		return false;
1252 	const fl_BlockLayout * pPrev = static_cast<const fl_BlockLayout *>(getPrev());
1253 	if((pPrev->getBottom() == getBottom()) &&
1254 	   (pPrev->getTop() == getTop()) &&
1255 	   (pPrev->getLeft() == getLeft()) &&
1256 	   (pPrev->getRight() == getRight()) &&
1257 	   (pPrev->getLeftMargin() == getLeftMargin()) &&
1258 	   (pPrev->getRightMargin() == getRightMargin()) &&
1259 	   (pPrev->getTextIndent() == getTextIndent()) &&
1260 	   (pPrev->m_bCanMergeBordersWithNext))
1261 		{
1262 			return true;
1263 		}
1264 	return false;
1265 }
1266 
1267 
canMergeBordersWithNext(void) const1268 bool fl_BlockLayout::canMergeBordersWithNext(void) const
1269 {
1270 	if(!getNext())
1271 		return false;
1272 	if(getNext()->getContainerType() !=  FL_CONTAINER_BLOCK)
1273 		return false;
1274 	fl_BlockLayout * pNext = static_cast<fl_BlockLayout *>(getNext());
1275 	if((pNext->getBottom() == getBottom()) &&
1276 	   (pNext->getTop() == getTop()) &&
1277 	   (pNext->getLeft() == getLeft()) &&
1278 	   (pNext->getRight() == getRight()) &&
1279 	   (pNext->getLeftMargin() == getLeftMargin()) &&
1280 	   (pNext->getRightMargin() == getRightMargin()) &&
1281 	   (pNext->getTextIndent() == getTextIndent()) &&
1282 	   m_bCanMergeBordersWithNext)
1283 		{
1284 			return true;
1285 		}
1286 	return false;
1287 }
1288 
hasBorders(void) const1289 bool fl_BlockLayout::hasBorders(void) const
1290 {
1291 	return m_bHasBorders;
1292 }
1293 
1294 // Recalculate the line heights of a block with borders.
1295 // If whichLine=1, recalculate only the height of the first line.
1296 // If whichLine=-1, recalculate only the height of the last line.
1297 // For other values of whichLine, recalculate the height for all the lines
setLineHeightBlockWithBorders(int whichLine)1298 void fl_BlockLayout::setLineHeightBlockWithBorders(int whichLine)
1299 {
1300 	fp_Line * pLine = NULL;
1301 	switch(whichLine)
1302 	{
1303 	case 1:
1304 		pLine = static_cast<fp_Line *>(getFirstContainer());
1305 		if(pLine)
1306 		{
1307 			pLine->setAlongTopBorder(false);
1308 			pLine->setAlongBotBorder(false);
1309 			pLine->calcBorderThickness();
1310 			pLine->recalcHeight();
1311 			if(pLine->isWrapped())
1312 			{
1313 				pLine = static_cast<fp_Line *>(pLine->getNext());
1314 				while(pLine && pLine->isSameYAsPrevious())
1315 				{
1316 					pLine->setAlongTopBorder(false);
1317 					pLine->setAlongBotBorder(false);
1318 					pLine->calcBorderThickness();
1319 					pLine->recalcHeight();
1320 				}
1321 			}
1322 		}
1323 		break;
1324 	case -1:
1325 		pLine = static_cast<fp_Line *>(getLastContainer());
1326 		if(pLine)
1327 		{
1328 			pLine->setAlongTopBorder(false);
1329 			pLine->setAlongBotBorder(false);
1330 			pLine->calcBorderThickness();
1331 			pLine->recalcHeight();
1332 			if(pLine->isSameYAsPrevious())
1333 			{
1334 				do
1335 				{
1336 					pLine = static_cast<fp_Line *>(pLine->getPrev());
1337 					if(pLine)
1338 					{
1339 						pLine->setAlongTopBorder(false);
1340 						pLine->setAlongBotBorder(false);
1341 						pLine->calcBorderThickness();
1342 						pLine->recalcHeight();
1343 					}
1344 				}
1345 				while(pLine && pLine->isSameYAsPrevious());
1346 			}
1347 		}
1348 		break;
1349 	default:
1350 		pLine = static_cast<fp_Line *>(getFirstContainer());
1351 		while(pLine)
1352 		{
1353 			pLine->setAlongTopBorder(false);
1354 			pLine->setAlongBotBorder(false);
1355 			pLine->calcBorderThickness();
1356 			pLine->recalcHeight();
1357 			pLine = static_cast<fp_Line *>(pLine->getNext());
1358 		}
1359 	}
1360 }
1361 
~fl_BlockLayout()1362 fl_BlockLayout::~fl_BlockLayout()
1363 {
1364 #ifdef ENABLE_SPELL
1365 	dequeueFromSpellCheck();
1366 	DELETEP(m_pSpellSquiggles);
1367 	DELETEP(m_pGrammarSquiggles);
1368 #endif
1369 	purgeLayout();
1370 	UT_VECTOR_PURGEALL(fl_TabStop *, m_vecTabs);
1371 	DELETEP(m_pAlignment);
1372 	//	if (m_pAutoNum)
1373 //		{
1374 //			m_pAutoNum->removeItem(getStruxDocHandle());
1375 //			if (m_pAutoNum->isEmpty())
1376 //				DELETEP(m_pAutoNum);
1377 //		}
1378 	if(!m_bIsTOC)
1379 	{
1380 		if(!isNotTOCable())
1381 		{
1382 			m_pLayout->removeBlockFromTOC(this);
1383 		}
1384 	}
1385 
1386 	UT_ASSERT_HARMLESS(m_pLayout != NULL);
1387 	if(m_pLayout)
1388 	{
1389 		m_pLayout->notifyBlockIsBeingDeleted(this);
1390 #ifdef ENABLE_SPELL
1391 		m_pLayout->dequeueBlockForBackgroundCheck(this);
1392 #endif
1393 	}
1394 
1395 	m_pDoc = NULL;
1396 	m_pLayout = NULL;
1397 	xxx_UT_DEBUGMSG(("~fl_BlockLayout: Deleting block %x sdh %x \n",this,getStruxDocHandle()));
1398 }
1399 
getStyle(UT_UTF8String & sStyle) const1400 void fl_BlockLayout::getStyle(UT_UTF8String & sStyle) const
1401 {
1402 	sStyle = m_szStyle;
1403 }
1404 
1405 /*!
1406  * This method returns true if the block is contained with a section embedded
1407  * in a block, like a footnote or a table or frame with text wrapping.
1408  */
isEmbeddedType(void) const1409 bool fl_BlockLayout::isEmbeddedType(void) const
1410 {
1411 	fl_ContainerLayout * pCL = myContainingLayout();
1412 	if(pCL && (((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) ) || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION )) )
1413 	{
1414 		return true;
1415 	}
1416 	return false;
1417 }
1418 
1419 
1420 /*!
1421  * This method returns true if the block is contained with a section embedded
1422  * that should not be included in TOC like, footnote,endnotes,HdrFtr's
1423  * and other TOC's.
1424  */
isNotTOCable(void) const1425 bool fl_BlockLayout::isNotTOCable(void) const
1426 {
1427 	fl_ContainerLayout * pCL = myContainingLayout();
1428 	if(pCL && (pCL->getContainerType() == FL_CONTAINER_FOOTNOTE
1429 			   || pCL->getContainerType() == FL_CONTAINER_ENDNOTE
1430 			   || pCL->getContainerType() == FL_CONTAINER_ANNOTATION
1431 			   || pCL->getContainerType() == FL_CONTAINER_HDRFTR
1432 			   || pCL->getContainerType() == FL_CONTAINER_TOC
1433 			   || pCL->getContainerType() == FL_CONTAINER_SHADOW
1434 			   ) )
1435 	{
1436 		return true;
1437 	}
1438 	if(pCL == NULL)
1439 	{
1440 		return true;
1441 	}
1442 	if(pCL->getContainerType() == FL_CONTAINER_CELL)
1443 	{
1444 		pCL = pCL->myContainingLayout(); // should be a table
1445 		if(pCL == NULL)
1446 		{
1447 			return true;
1448 		}
1449 		pCL = pCL->myContainingLayout(); // is it a Hdrftr?
1450 		if(pCL && (pCL->getContainerType() == FL_CONTAINER_HDRFTR
1451 			   || pCL->getContainerType() == FL_CONTAINER_SHADOW
1452 			   ) )
1453 		{
1454 			return true;
1455 		}
1456 	}
1457 	return false;
1458 }
1459 
1460 /*!
1461  * This method returns the offset of the next embedded strux within the
1462  * the block. (Like a footnote or endnote)
1463  * It returns -1 if none is found.
1464  * Also returns the id of the embedded strux.
1465  */
getEmbeddedOffset(UT_sint32 offset,fl_ContainerLayout * & pEmbedCL) const1466 UT_sint32 fl_BlockLayout::getEmbeddedOffset(UT_sint32 offset, fl_ContainerLayout *& pEmbedCL) const
1467 {
1468 	UT_sint32 iEmbed = -1;
1469 	PT_DocPosition posOff = static_cast<PT_DocPosition>(offset);
1470 	pf_Frag_Strux* sdhEmbed;
1471 	pEmbedCL = NULL;
1472 	iEmbed = m_pDoc->getEmbeddedOffset(getStruxDocHandle(), posOff, sdhEmbed);
1473 	if( iEmbed < 0)
1474 	{
1475 		return iEmbed;
1476 	}
1477 	fl_ContainerLayout* sfhEmbed = NULL;
1478 	bool bFound = false;
1479 	sfhEmbed = m_pDoc->getNthFmtHandle(sdhEmbed,m_pLayout->getLID());
1480 	if(	sfhEmbed == NULL)
1481 	{
1482 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1483 		return -1;
1484 	}
1485 	pEmbedCL = sfhEmbed;
1486 	if(pEmbedCL->getDocSectionLayout() == getDocSectionLayout())
1487 	{
1488 		bFound = true;
1489 	}
1490 	else
1491 	{
1492 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1493 		pEmbedCL = NULL;
1494 		return -1;
1495 	}
1496 	if(bFound)
1497 	{
1498 		if(pEmbedCL->getContainerType() == FL_CONTAINER_TOC)
1499 		{
1500 			pEmbedCL = NULL;
1501 			return -1;
1502 		}
1503 		return iEmbed;
1504 	}
1505 	pEmbedCL = NULL;
1506 	return -1;
1507 }
1508 
1509 /*!
1510  * This method scans through the list of runs from the first position listed
1511  * and updates the offsets. This is used following an operation on an embedded
1512  * type section (Like a footnote). Also updates the char widths and the POB's
1513  * in the squiggles.
1514 \param posEmbedded the position of the embedded Section.
1515 \param iEmbeddedSize the size of the embedded Section.
1516  */
updateOffsets(PT_DocPosition posEmbedded,UT_uint32 iEmbeddedSize,UT_sint32 iSuggestDiff)1517 void fl_BlockLayout::updateOffsets(PT_DocPosition posEmbedded, UT_uint32 iEmbeddedSize, UT_sint32 iSuggestDiff)
1518 {
1519 	UT_UNUSED(iEmbeddedSize);
1520 	xxx_UT_DEBUGMSG(("In update Offsets posEmbedded %d EmbeddedSize %d shift %d \n",posEmbedded,iEmbeddedSize,iSuggestDiff));
1521 	fp_Run * pRun = getFirstRun();
1522 	PT_DocPosition posOfBlock = getPosition(true);
1523 	PT_DocPosition posAtStartOfBlock = getPosition();
1524 	fp_Run * pPrev = NULL;
1525 #if DEBUG
1526 	while(pRun)
1527 	{
1528 		xxx_UT_DEBUGMSG(("!!Initially run %p runType %d posindoc %d end run %d \n",pRun,pRun->getType(),posAtStartOfBlock+pRun->getBlockOffset(),posAtStartOfBlock+pRun->getBlockOffset()+pRun->getLength()));
1529 		pRun = pRun->getNextRun();
1530 	}
1531 	pRun = getFirstRun();
1532 #endif
1533 	while(pRun && (posAtStartOfBlock + pRun->getBlockOffset() < posEmbedded))
1534 	{
1535 		xxx_UT_DEBUGMSG(("Look at run %p runType %d posindoc %d \n",pRun,pRun->getType(),posAtStartOfBlock+pRun->getBlockOffset()));
1536 		pPrev = pRun;
1537 		pRun = pRun->getNextRun();
1538 
1539 	}
1540 	PT_DocPosition posRun = 0;
1541 	if(pRun == NULL)
1542 	{
1543 		if(pPrev == NULL)
1544 		{
1545 			UT_DEBUGMSG(("!!!YIKES NO RUN or PREV RUN!!! \n"));
1546 			return;
1547 		}
1548 		//
1549 		// Catch case of EOP actually containing posEmebedded
1550 		//
1551 		if((posOfBlock + pPrev->getBlockOffset() +1) < posEmbedded)
1552 		{
1553 			xxx_UT_DEBUGMSG(("!!! POSEMBEDDED past end of block!! \n"));
1554 			xxx_UT_DEBUGMSG(("End of block %d PosEmbedded %d \n",posOfBlock+pPrev->getBlockOffset()+1,posEmbedded));
1555 			return;
1556 		}
1557 		else
1558 		{
1559 			pRun = pPrev;
1560 			pPrev = pRun->getPrevRun();
1561 		}
1562 	}
1563 	else
1564 	{
1565 		posRun = posAtStartOfBlock + pRun->getBlockOffset();
1566 		if((posRun > posEmbedded) && pPrev)
1567 		{
1568 			posRun = posAtStartOfBlock + pPrev->getBlockOffset();
1569 			if(posRun < posEmbedded)
1570 			{
1571 				pRun = pPrev;
1572 				pPrev = pRun->getPrevRun();
1573 			}
1574 		}
1575 	}
1576 	//
1577 	// Position of pRun should be  <= posEmbedded
1578 	//
1579 	posRun = posAtStartOfBlock + pRun->getBlockOffset();
1580 	fp_Run * pNext = pRun->getNextRun();
1581 	if(pNext && (posRun + pRun->getLength() <= posEmbedded) && ((pNext->getBlockOffset() + posAtStartOfBlock) > posEmbedded))
1582 	{
1583 		//
1584 		// OK it's obvious here. Run previous is before posEmbedded next
1585 		// is after. Use it
1586 
1587 		pRun = pNext;
1588 	}
1589 	else if(posRun < posEmbedded)
1590 	{
1591 		UT_uint32 splitOffset = posEmbedded - posOfBlock -1;
1592 		if(splitOffset > pRun->getBlockOffset() && (pRun->getBlockOffset() + pRun->getLength() > splitOffset))
1593 		{
1594 			UT_ASSERT(pRun->getType() == FPRUN_TEXT);
1595 			fp_TextRun * pTRun = static_cast<fp_TextRun *>(pRun);
1596 			xxx_UT_DEBUGMSG(("updateOffsets: Split at offset %d \n",splitOffset));
1597 			bool bres = pTRun->split(splitOffset,0);
1598 			UT_UNUSED(bres);
1599 			UT_ASSERT(bres);
1600 			pRun = pTRun->getNextRun();
1601 			pPrev = pTRun;
1602 			xxx_UT_DEBUGMSG(("New Run %x created offset %d \n",pRun,pRun->getBlockOffset()));
1603 			xxx_UT_DEBUGMSG(("Old Run %x offset %d length\n",pPrev,pPrev->getBlockOffset(),pPrev->getLength()));
1604 		}
1605 		else
1606 		{
1607 			// Split point is actually after this run
1608 			UT_ASSERT(splitOffset == pRun->getBlockOffset());
1609 			pPrev = pRun;
1610 			pRun = pRun->getNextRun();
1611 			UT_ASSERT(pRun);
1612 			if(pRun == NULL)
1613 			{
1614 				pPrev = pRun;
1615 			}
1616 		}
1617 	}
1618 
1619 	//
1620 	// pRun is the first run that gets shifted
1621 	//
1622 	UT_return_if_fail(pRun);
1623 	posRun = posAtStartOfBlock + pRun->getBlockOffset();
1624 
1625 
1626 	if(iSuggestDiff !=  0)
1627 	{
1628 //
1629 // Now shift all the offsets in the runs.
1630 //
1631 #ifdef ENABLE_SPELL
1632 		UT_sint32 iFirstOffset = static_cast<UT_sint32>(pRun->getBlockOffset());
1633 #endif
1634 		while(pRun)
1635 		{
1636 			UT_sint32 iNew = static_cast<UT_sint32>(pRun->getBlockOffset()) + iSuggestDiff;
1637 			//
1638 			// Since suggestDiff can be < 0 we need to check this
1639 			//
1640 			// Check if this iNew is sane.
1641 			pPrev = pRun->getPrevRun();
1642 			if(pPrev && (static_cast<UT_sint32>(pPrev->getBlockOffset() + pPrev->getLength()) > iNew))
1643 			{
1644 				// Something went wrong. Try to recover
1645 				UT_DEBUGMSG(("Invalid updated offset %d Try to recover \n",iNew));
1646 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
1647 				if(pRun->getType() == FPRUN_FMTMARK)
1648 				{
1649 					iNew= static_cast<UT_sint32>(pPrev->getBlockOffset() + pPrev->getLength());
1650 				}
1651 				else
1652 				{
1653 					iNew= static_cast<UT_sint32>(pPrev->getBlockOffset() + pPrev->getLength()) + 1;
1654 				}
1655 			}
1656 			else if( (pPrev == NULL) && (iNew < 0))
1657 			{
1658 				// Something went wrong. Try to recover
1659 				UT_DEBUGMSG(("Invalid updated offset %d Try to recover \n",iNew));
1660 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
1661 				iNew = 0;
1662 			}
1663 			UT_ASSERT(iNew >= 0);
1664 			xxx_UT_DEBUGMSG(("Run %p Old offset %d New Offset %d \n",pRun,pRun->getBlockOffset(),iNew));
1665 			pRun->setBlockOffset(static_cast<UT_uint32>(iNew));
1666 			pRun = pRun->getNextRun();
1667 		}
1668 //
1669 // Now update the PartOfBlocks in the squiggles
1670 //
1671 #ifdef ENABLE_SPELL
1672 		getSpellSquiggles()->updatePOBs(iFirstOffset,iSuggestDiff);
1673 		getGrammarSquiggles()->updatePOBs(iFirstOffset,iSuggestDiff);
1674 #endif
1675 	}
1676 #if 1
1677 #if DEBUG
1678 	pRun = getFirstRun();
1679 	while(pRun)
1680 	{
1681 		if(pRun->getType() == FPRUN_TEXT)
1682 		{
1683 			fp_TextRun * pTRun = static_cast<fp_TextRun *>(pRun);
1684 			pTRun->printText();
1685 		}
1686 		UT_DEBUGMSG(("update offsets!!!!--- Run %p offset %d Type %d \n",pRun,pRun->getBlockOffset(),pRun->getType()));
1687 		pRun = pRun->getNextRun();
1688 	}
1689 #endif
1690 #endif
1691 	setNeedsReformat(this);
1692 	updateEnclosingBlockIfNeeded();
1693 }
1694 
1695 /*!
1696  * This method updates the enclosing Block which contains the embedded Section
1697  * which in turn contains this Block. If this is not a block in an embedded
1698  * section type, we just return and do nothing.
1699  */
updateEnclosingBlockIfNeeded(void)1700 void fl_BlockLayout::updateEnclosingBlockIfNeeded(void)
1701 {
1702 	UT_return_if_fail (m_pLayout);
1703 
1704 	if(!isEmbeddedType())
1705 	{
1706 		xxx_UT_DEBUGMSG(("Block %x is Not enclosed - returning \n"));
1707 		return;
1708 	}
1709 	fl_ContainerLayout * pCL = myContainingLayout();
1710 	UT_ASSERT((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION) );
1711 	fl_EmbedLayout * pFL = static_cast<fl_EmbedLayout *>(pCL);
1712 	if(!pFL->isEndFootnoteIn())
1713 	{
1714 		return;
1715 	}
1716 	pf_Frag_Strux* sdhStart = pCL->getStruxDocHandle();
1717 	pf_Frag_Strux* sdhEnd = NULL;
1718 	if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
1719 	{
1720 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndFootnote, &sdhEnd);
1721 	}
1722 	else if(pCL->getContainerType() == FL_CONTAINER_ENDNOTE)
1723 	{
1724 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndEndnote, &sdhEnd);
1725 	}
1726 	else if(pCL->getContainerType() == FL_CONTAINER_ANNOTATION)
1727 	{
1728 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndAnnotation, &sdhEnd);
1729 	}
1730 
1731 	UT_return_if_fail(sdhEnd != NULL);
1732 	PT_DocPosition posStart = getDocument()->getStruxPosition(sdhStart);
1733 	PT_DocPosition posEnd = getDocument()->getStruxPosition(sdhEnd);
1734 	UT_uint32 iSize = posEnd - posStart + 1;
1735 	fl_ContainerLayout*  psfh = NULL;
1736 	getDocument()->getStruxOfTypeFromPosition(m_pLayout->getLID(),posStart,PTX_Block, &psfh);
1737 	fl_BlockLayout * pBL = static_cast<fl_BlockLayout*>(psfh);
1738 	UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);
1739 	UT_ASSERT(iSize > 1);
1740     UT_sint32 iOldSize = pFL->getOldSize();
1741     pFL->setOldSize(iSize);
1742 	pBL->updateOffsets(posStart,iSize,iSize-iOldSize);
1743 }
1744 
1745 /*!
1746  * Get the enclosing block of this if this block is in a footnote-type strux
1747  * Return NULL is not an embedded type
1748 */
getEnclosingBlock(void) const1749 fl_BlockLayout * fl_BlockLayout::getEnclosingBlock(void) const
1750 {
1751 	UT_return_val_if_fail (m_pLayout,NULL);
1752 
1753 	if(!isEmbeddedType())
1754 	{
1755 		xxx_UT_DEBUGMSG(("Block %x is Not enclosed - returning \n"));
1756 		return NULL;
1757 	}
1758 	fl_ContainerLayout * pCL = myContainingLayout();
1759 	UT_ASSERT((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION) );
1760 	fl_EmbedLayout * pFL = static_cast<fl_EmbedLayout *>(pCL);
1761 	if(!pFL->isEndFootnoteIn())
1762 	{
1763 		return NULL;
1764 	}
1765 	pf_Frag_Strux* sdhStart = pCL->getStruxDocHandle();
1766 	pf_Frag_Strux* sdhEnd = NULL;
1767 	if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
1768 	{
1769 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndFootnote, &sdhEnd);
1770 	}
1771 	else if(pCL->getContainerType() == FL_CONTAINER_ENDNOTE)
1772 	{
1773 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndEndnote, &sdhEnd);
1774 	}
1775 	else if(pCL->getContainerType() == FL_CONTAINER_ANNOTATION)
1776 	{
1777 		getDocument()->getNextStruxOfType(sdhStart,PTX_EndAnnotation, &sdhEnd);
1778 	}
1779 
1780 	UT_return_val_if_fail(sdhEnd != NULL,NULL);
1781 	PT_DocPosition posStart = getDocument()->getStruxPosition(sdhStart);
1782 	fl_ContainerLayout*  psfh = NULL;
1783 	getDocument()->getStruxOfTypeFromPosition(m_pLayout->getLID(),posStart,PTX_Block, &psfh);
1784 	fl_BlockLayout * pBL = static_cast<fl_BlockLayout *>(psfh);
1785 	UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);
1786 	return pBL;
1787 }
1788 
1789 /*!
1790  * This method returns the DocSectionLayout that this block is associated with
1791  */
getDocSectionLayout(void) const1792 fl_DocSectionLayout * fl_BlockLayout::getDocSectionLayout(void) const
1793 {
1794 	fl_DocSectionLayout * pDSL = NULL;
1795 	if(getSectionLayout()->getType() == FL_SECTION_DOC)
1796 	{
1797 		pDSL = static_cast<fl_DocSectionLayout *>( m_pSectionLayout);
1798 		return pDSL;
1799 	}
1800 	else if	(getSectionLayout()->getType() == FL_SECTION_TOC)
1801 	{
1802 		pDSL = static_cast<fl_TOCLayout *>(getSectionLayout())->getDocSectionLayout();
1803 		return pDSL;
1804 	}
1805 	else if	(getSectionLayout()->getType() == FL_SECTION_FOOTNOTE)
1806 	{
1807 		pDSL = static_cast<fl_FootnoteLayout *>(getSectionLayout())->getDocSectionLayout();
1808 		return pDSL;
1809 	}
1810 	else if	(getSectionLayout()->getType() == FL_SECTION_ENDNOTE)
1811 	{
1812 		pDSL = static_cast<fl_EndnoteLayout *>(getSectionLayout())->getDocSectionLayout();
1813 		return pDSL;
1814 	}
1815 	else if	(getSectionLayout()->getType() == FL_SECTION_ANNOTATION)
1816 	{
1817 		pDSL = static_cast<fl_AnnotationLayout *>(getSectionLayout())->getDocSectionLayout();
1818 		return pDSL;
1819 	}
1820 	else if (getSectionLayout()->getType() == FL_SECTION_HDRFTR)
1821 	{
1822 		pDSL = static_cast<fl_HdrFtrSectionLayout *>(getSectionLayout())->getDocSectionLayout();
1823 		return pDSL;
1824 	}
1825 	else if (getSectionLayout()->getType() == FL_SECTION_SHADOW)
1826 	{
1827 		pDSL = static_cast<fl_HdrFtrShadow *>( getSectionLayout())->getHdrFtrSectionLayout()->getDocSectionLayout();
1828 		return pDSL;
1829 	}
1830 	else if (getSectionLayout()->getType() == FL_SECTION_CELL)
1831 	{
1832 		pDSL = static_cast<fl_ContainerLayout *>(getSectionLayout())->getDocSectionLayout();
1833 		return pDSL;
1834 	}
1835 	else if (getSectionLayout()->getType() == FL_SECTION_FRAME)
1836 	{
1837 		pDSL = static_cast<fl_ContainerLayout *>(getSectionLayout())->getDocSectionLayout();
1838 		return pDSL;
1839 	}
1840 	UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1841 	return NULL;
1842 }
1843 
1844 
findLineWithFootnotePID(UT_uint32 pid) const1845 fp_Line * fl_BlockLayout::findLineWithFootnotePID(UT_uint32 pid) const
1846 {
1847 	fp_Line * pLine = static_cast<fp_Line *>(getFirstContainer());
1848 	UT_GenericVector<fp_FootnoteContainer *> vecFoots;
1849 	bool bFound = false;
1850 	while(pLine && !bFound)
1851 	{
1852 		vecFoots.clear();
1853 		if(pLine->getFootnoteContainers(&vecFoots))
1854 		{
1855 			UT_sint32 i = 0;
1856 			for(i=0; i< vecFoots.getItemCount(); i++)
1857 			{
1858 				fp_FootnoteContainer * pFC = vecFoots.getNthItem(i);
1859 				fl_FootnoteLayout * pFL = static_cast<fl_FootnoteLayout *>(pFC->getSectionLayout());
1860 				if(pFL->getFootnotePID() == pid)
1861 				{
1862 					bFound = true;
1863 					break;
1864 				}
1865 			}
1866 		}
1867 		pLine = static_cast<fp_Line *>(pLine->getNext());
1868 	}
1869 	if(bFound)
1870 	{
1871 		return pLine;
1872 	}
1873 	return NULL;
1874 }
1875 
getTOCNumType(void) const1876 FootnoteType fl_BlockLayout::getTOCNumType(void) const
1877 {
1878 	UT_ASSERT(m_bIsTOC);
1879 	fl_TOCLayout * pTOCL = static_cast<fl_TOCLayout *>(getSectionLayout());
1880 	UT_ASSERT(pTOCL->getContainerType() == FL_CONTAINER_TOC);
1881 	return pTOCL->getNumType(m_iTOCLevel);
1882 }
1883 
getTOCTabLeader(UT_sint32 iOff) const1884 eTabLeader fl_BlockLayout::getTOCTabLeader(UT_sint32 iOff) const
1885 {
1886 	UT_ASSERT(m_bIsTOC);
1887 	fl_TOCLayout * pTOCL = static_cast<fl_TOCLayout *>(getSectionLayout());
1888 	UT_ASSERT(pTOCL->getContainerType() == FL_CONTAINER_TOC);
1889 	if(iOff > 1)
1890 	{
1891 		return pTOCL->getTabLeader(m_iTOCLevel);
1892 	}
1893 	return FL_LEADER_NONE;
1894 }
1895 
getTOCTabPosition(UT_sint32 iOff) const1896 UT_sint32 fl_BlockLayout::getTOCTabPosition(UT_sint32 iOff) const
1897 {
1898 	UT_ASSERT(m_bIsTOC);
1899 	fl_TOCLayout * pTOCL = static_cast<fl_TOCLayout *>(getSectionLayout());
1900 	UT_ASSERT(pTOCL->getContainerType() == FL_CONTAINER_TOC);
1901 	if(iOff > 1)
1902 	{
1903 		return pTOCL->getTabPosition(m_iTOCLevel,this);
1904 	}
1905 	return 0;
1906 }
1907 
getMaxNonBreakableRun(void) const1908 UT_sint32 fl_BlockLayout::getMaxNonBreakableRun(void) const
1909 {
1910 	fp_Run * pRun = getFirstRun();
1911 	UT_sint32 iMax = 6; // this is the pixel width of a typical 12 point char
1912 	if(pRun)
1913 	{
1914 #if 0
1915 		if(pRun->getGraphics())
1916 		{
1917 			GR_Font *pFont = pRun->getGraphics()->getGUIFont();
1918 			if(pFont)
1919 			{
1920 				iMax = pRun->getGraphics()->measureUnRemappedChar(static_cast<UT_UCSChar>('i'));
1921 			}
1922 		}
1923 #endif
1924 	}
1925 	while(pRun)
1926 	{
1927 		if(pRun->getType() == FPRUN_IMAGE)
1928 		{
1929 			iMax = UT_MAX(iMax,pRun->getWidth());
1930 		}
1931 		pRun = pRun->getNextRun();
1932 	}
1933 	return iMax;
1934 }
1935 
isHdrFtr(void) const1936 bool fl_BlockLayout::isHdrFtr(void) const
1937 {
1938 	if(getSectionLayout()!= NULL)
1939 	{
1940 		return (getSectionLayout()->getType() == FL_SECTION_HDRFTR);
1941 	}
1942 	else
1943 		return m_bIsHdrFtr;
1944 }
1945 
clearScreen(GR_Graphics *) const1946 void fl_BlockLayout::clearScreen(GR_Graphics* /* pG */) const
1947 {
1948 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
1949 	if(isHdrFtr())
1950 	{
1951 		return;
1952 	}
1953 	while (pLine)
1954 	{
1955 		// I have commented this assert out, since due to the call from doclistener_deleteStrux
1956 		// clearScreen can be called _after_ the contents of this paragraph have been cleared
1957 		// Tomas 28/02/2002
1958 		//UT_ASSERT(!pLine->isEmpty());
1959 		if(!pLine->isEmpty())
1960 			pLine->clearScreen();
1961 		pLine = static_cast<fp_Line *>(pLine->getNext());
1962 	}
1963 }
1964 
_mergeRuns(fp_Run * pFirstRunToMerge,fp_Run * pLastRunToMerge) const1965 void fl_BlockLayout::_mergeRuns(fp_Run* pFirstRunToMerge, fp_Run* pLastRunToMerge) const
1966 {
1967 	UT_ASSERT(pFirstRunToMerge != pLastRunToMerge);
1968 	UT_ASSERT(pFirstRunToMerge->getType() == FPRUN_TEXT);
1969 	UT_ASSERT(pLastRunToMerge->getType() == FPRUN_TEXT);
1970 
1971 	_assertRunListIntegrity();
1972 
1973 	fp_TextRun* pFirst = static_cast<fp_TextRun*>(pFirstRunToMerge);
1974 
1975 	bool bDone = false;
1976 	while (!bDone)
1977 	{
1978 		if (pFirst->getNextRun() == pLastRunToMerge)
1979 		{
1980 			bDone = true;
1981 		}
1982 
1983 		pFirst->mergeWithNext();
1984 	}
1985 
1986 	_assertRunListIntegrity();
1987 }
1988 
coalesceRuns(void) const1989 void fl_BlockLayout::coalesceRuns(void) const
1990 {
1991 	_assertRunListIntegrity();
1992 
1993 #if 1
1994 	xxx_UT_DEBUGMSG(("fl_BlockLayout::coalesceRuns\n"));
1995 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
1996 	while (pLine)
1997 	{
1998 		pLine->coalesceRuns();
1999 		pLine = static_cast<fp_Line *>(pLine->getNext());
2000 	}
2001 #else
2002 	fp_Run* pFirstRunInChain = NULL;
2003 	UT_uint32 iNumRunsInChain = 0;
2004 
2005 	fp_Run* pCurrentRun = m_pFirstRun;
2006 	fp_Run* pLastRun = NULL;
2007 
2008 	while (pCurrentRun)
2009 	{
2010 		if (pCurrentRun->getType() == FPRUN_TEXT)
2011 		{
2012 			if (pFirstRunInChain)
2013 			{
2014 				if (
2015 					(pCurrentRun->getLine() == pFirstRunInChain->getLine())
2016 					&& (pCurrentRun->getAP() == pFirstRunInChain->getAP())
2017 					&& ((!pLastRun)
2018 						|| (
2019 							(pCurrentRun->getBlockOffset() == (pLastRun->getBlockOffset() + pLastRun->getLength()))
2020 							)
2021 						)
2022 					)
2023 				{
2024 					iNumRunsInChain++;
2025 				}
2026 				else
2027 				{
2028 					if (iNumRunsInChain > 1)
2029 					{
2030 						_mergeRuns(pFirstRunInChain, pLastRun);
2031 					}
2032 
2033 					pFirstRunInChain = pCurrentRun;
2034 					iNumRunsInChain = 1;
2035 				}
2036 			}
2037 			else
2038 			{
2039 				pFirstRunInChain = pCurrentRun;
2040 				iNumRunsInChain = 1;
2041 			}
2042 		}
2043 		else
2044 		{
2045 			if (iNumRunsInChain > 1)
2046 			{
2047 				_mergeRuns(pFirstRunInChain, pLastRun);
2048 			}
2049 
2050 			iNumRunsInChain = 0;
2051 			pFirstRunInChain = NULL;
2052 		}
2053 
2054 		pLastRun = pCurrentRun;
2055 		pCurrentRun = pCurrentRun->getNextRun();
2056 	}
2057 
2058 	if (iNumRunsInChain > 1)
2059 	{
2060 		_mergeRuns(pFirstRunInChain, pLastRun);
2061 	}
2062 #endif
2063 
2064 	_assertRunListIntegrity();
2065 }
2066 
collapse(void)2067 void fl_BlockLayout::collapse(void)
2068 {
2069 	xxx_UT_DEBUGMSG(("Collapsing Block %x No containers %d \n",this,findLineInBlock(static_cast<fp_Line *>(getLastContainer()))));
2070 	fp_Run* pRun = m_pFirstRun;
2071 	while (pRun)
2072 	{
2073 		pRun->setLine(NULL);
2074 
2075 		pRun = pRun->getNextRun();
2076 	}
2077 
2078 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
2079 	while (pLine)
2080 	{
2081 		fl_DocSectionLayout * pDSL = getDocSectionLayout();
2082 		if(!pDSL->isCollapsing())
2083 		{
2084 			_removeLine(pLine,true,false);
2085 		}
2086 		else
2087 		{
2088 			_removeLine(pLine,false,false);
2089 		}
2090 		pLine = static_cast<fp_Line *>(getFirstContainer());
2091 	}
2092 	xxx_UT_DEBUGMSG(("Block collapsed in collapsed %x \n",this));
2093 	m_bIsCollapsed = true;
2094 	m_iNeedsReformat = 0;
2095 	UT_ASSERT_HARMLESS(getFirstContainer() == NULL);
2096 	UT_ASSERT_HARMLESS(getLastContainer() == NULL);
2097 }
2098 
purgeLayout(void)2099 void fl_BlockLayout::purgeLayout(void)
2100 {
2101 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
2102 	while (pLine)
2103 	{
2104 		_purgeLine(pLine);
2105 		pLine = static_cast<fp_Line *>(getFirstContainer());
2106 	}
2107 
2108 	UT_ASSERT(getFirstContainer() == NULL);
2109 	UT_ASSERT(getLastContainer() == NULL);
2110 
2111 	while (m_pFirstRun)
2112 	{
2113 		fp_Run* pNext = m_pFirstRun->getNextRun();
2114 		m_pFirstRun->setBlock(NULL);
2115 		delete m_pFirstRun;
2116 		m_pFirstRun = pNext;
2117 	}
2118 }
2119 
_removeLine(fp_Line * pLine,bool bRemoveFromContainer,bool bReCalc)2120 void fl_BlockLayout::_removeLine(fp_Line* pLine, bool bRemoveFromContainer, bool bReCalc)
2121 {
2122 	if(!pLine->canDelete())
2123 	{
2124 		m_pLayout->setRebuiltBlock(this);
2125 	}
2126 	if (getFirstContainer() == static_cast<fp_Container *>(pLine))
2127 	{
2128 		setFirstContainer(static_cast<fp_Container *>(getFirstContainer()->getNext()));
2129 
2130 		// we have to call recalcMaxWidth so that the new line has the correct
2131 		// x offset and width
2132 
2133 		if(!getDocSectionLayout()->isCollapsing() && getFirstContainer() && bReCalc)
2134 			getFirstContainer()->recalcMaxWidth();
2135 	}
2136 
2137 	if (getLastContainer() == static_cast<fp_Container *>(pLine))
2138 	{
2139 		setLastContainer(static_cast<fp_Container *>(getLastContainer()->getPrev()));
2140 	}
2141 
2142 	if(pLine->getContainer() && bRemoveFromContainer)
2143 	{
2144 		fp_VerticalContainer * pVert = static_cast<fp_VerticalContainer *>(pLine->getContainer());
2145 		pVert->removeContainer(pLine);
2146 		pLine->setContainer(NULL);
2147 	}
2148 	pLine->remove();
2149 	pLine->setBlock(NULL);
2150 	xxx_UT_DEBUGMSG(("Removed line %x \n",pLine));
2151 	UT_ASSERT(findLineInBlock(pLine) == -1);
2152 
2153 	delete pLine;
2154 #if DEBUG
2155 	if(getFirstContainer())
2156 	{
2157 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
2158 	}
2159 #endif
2160 
2161 // if the block has borders we may need to change the last line height
2162 	if (hasBorders())
2163 	{
2164 		setLineHeightBlockWithBorders(-1);
2165 	}
2166 }
2167 
_purgeLine(fp_Line * pLine)2168 void fl_BlockLayout::_purgeLine(fp_Line* pLine)
2169 {
2170 	if (getLastContainer() == static_cast<fp_Container *>(pLine))
2171 	{
2172 		setLastContainer(static_cast<fp_Container *>(getLastContainer()->getPrev()));
2173 	}
2174 
2175 	if (getFirstContainer() == static_cast<fp_Container *>(pLine))
2176 	{
2177 		setFirstContainer(static_cast<fp_Container *>(getFirstContainer()->getNext()));
2178 	}
2179 	pLine->setBlock(NULL);
2180 	pLine->remove();
2181 
2182 	delete pLine;
2183 #if DEBUG
2184 	if(getFirstContainer())
2185 	{
2186 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
2187 	}
2188 #endif
2189 }
2190 
2191 
_removeAllEmptyLines(void)2192 void fl_BlockLayout::_removeAllEmptyLines(void)
2193 {
2194 	fp_Line* pLine;
2195 
2196 	pLine = static_cast<fp_Line *>(getFirstContainer());
2197 	while (pLine)
2198 	{
2199 		if (pLine->isEmpty())
2200 		{
2201 			fp_Line * pNext = static_cast<fp_Line *>(pLine->getNext());
2202 			_removeLine(pLine, true,true);
2203 			pLine = pNext;
2204 		}
2205 		else
2206 		{
2207 			pLine = static_cast<fp_Line *>(pLine->getNext());
2208 		}
2209 	}
2210 }
2211 
2212 /*!
2213   Truncate layout from the specified Run
2214   \param pTruncRun First Run to be truncated
2215   \return True
2216 
2217   This will remove all Runs starting from pTruncRun to the last Run on
2218   the block from their lines (and delete them from the display).
2219 
2220   \note The Run list may be inconsistent when this function is
2221 		called, so no assertion.
2222   */
_truncateLayout(fp_Run * pTruncRun)2223 bool fl_BlockLayout::_truncateLayout(fp_Run* pTruncRun)
2224 {
2225 	// Special case, nothing to do
2226 	if (!pTruncRun)
2227 	{
2228 		return true;
2229 	}
2230 
2231 	if (m_pFirstRun == pTruncRun)
2232 	{
2233 		m_pFirstRun = NULL;
2234 	}
2235 	fp_Run * pRun = NULL;
2236 	// Remove runs from screen. No need for HdrFtr's though
2237 	if(!isHdrFtr())
2238 	{
2239 		fp_Line * pLine = pTruncRun->getLine();
2240 		if(pLine != NULL)
2241 		{
2242 			pLine->clearScreenFromRunToEnd(pTruncRun);
2243 			pLine = static_cast<fp_Line *>(pLine->getNext());
2244 			while(pLine)
2245 			{
2246 				pLine->clearScreen();
2247 				pLine= static_cast<fp_Line *>(pLine->getNext());
2248 			}
2249 		}
2250 		else
2251 		{
2252 			pRun = pTruncRun;
2253 			while (pRun)
2254 			{
2255 				pRun->clearScreen();
2256 				pRun = pRun->getNextRun();
2257 			}
2258 		}
2259 	}
2260 
2261 	// Remove runs from lines
2262 	pRun = pTruncRun;
2263 	while (pRun)
2264 	{
2265 		fp_Line* pLine = pRun->getLine();
2266 
2267 		if (pLine)
2268 			pLine->removeRun(pRun, true);
2269 
2270 		pRun = pRun->getNextRun();
2271 	}
2272 
2273 	_removeAllEmptyLines();
2274 
2275 	return true;
2276 }
2277 
2278 /*!
2279   Move all Runs in the block onto a new line.
2280   This is only called during block creation when there are no existing
2281   lines in the block.
2282 */
_stuffAllRunsOnALine(void)2283 void fl_BlockLayout::_stuffAllRunsOnALine(void)
2284 {
2285 	UT_ASSERT(getFirstContainer() == NULL);
2286 	fp_Line* pLine = static_cast<fp_Line *>(getNewContainer());
2287 	UT_return_if_fail(pLine);
2288 	if(pLine->getContainer() == NULL)
2289 	{
2290 		fp_VerticalContainer * pContainer = NULL;
2291 		if(m_pSectionLayout->getFirstContainer())
2292 		{
2293 			// TODO assert something here about what's in that container
2294 			pContainer = static_cast<fp_VerticalContainer *>(m_pSectionLayout->getFirstContainer());
2295 		}
2296 		else
2297 		{
2298 			pContainer = static_cast<fp_VerticalContainer *>(m_pSectionLayout->getNewContainer());
2299 			UT_ASSERT(pContainer->getWidth() >0);
2300 		}
2301 
2302 		pContainer->insertContainer(static_cast<fp_Container *>(pLine));
2303 	}
2304 	fp_Run* pTempRun = m_pFirstRun;
2305 	while (pTempRun)
2306 	{
2307 		pTempRun->lookupProperties();
2308 		pLine->addRun(pTempRun);
2309 
2310 		if(pTempRun->getType() == FPRUN_TEXT && !UT_BIDI_IS_STRONG(pTempRun->getDirection()))
2311 		{
2312 			// if the runs is not of a strong type, we have to ensure its visual direction gets
2313 			// recalculated and buffer refreshed ...
2314 			pTempRun->setVisDirection(UT_BIDI_UNSET);
2315 		}
2316 
2317 		pTempRun = pTempRun->getNextRun();
2318 	}
2319 	UT_ASSERT(pLine->getContainer());
2320 	xxx_UT_DEBUGMSG(("fl_BlockLayout: Containing container for line is %x \n",pLine->getContainer()));
2321 	pLine->recalcMaxWidth();
2322 }
2323 
2324 /*!
2325   Add end-of-paragraph Run to block
2326 
2327   This function adds the EOP Run to the block.	The presence of the
2328   EOP is an invariant (except for when merging / splitting blocks) and
2329   ensures that the cursor can always be placed on the last line of a
2330   block.  If there are multiple lines, the first N-1 lines will have a
2331   forced break of some kind which can also hold the cursor.
2332 */
2333 void
_insertEndOfParagraphRun(void)2334 fl_BlockLayout::_insertEndOfParagraphRun(void)
2335 {
2336 	UT_ASSERT(!m_pFirstRun);
2337 
2338 	fp_EndOfParagraphRun* pEOPRun = new fp_EndOfParagraphRun(this, 0, 0);
2339 	m_pFirstRun = pEOPRun;
2340 
2341 	m_bNeedsRedraw = true;
2342 
2343 	// FIXME:jskov Why don't the header/footer need the line?
2344 	//if (getSectionLayout()
2345 	//	&& (getSectionLayout()->getType()== FL_SECTION_HDRFTR))
2346 	//{
2347 	//	return;
2348 	//}
2349 
2350 	if (!getFirstContainer())
2351 	{
2352 		getNewContainer();
2353 		m_bIsCollapsed = false;
2354 	}
2355 	fp_Line * pFirst = static_cast<fp_Line *>(getFirstContainer());
2356 	UT_ASSERT(pFirst && pFirst->countRuns() == 0);
2357 
2358 	pFirst->addRun(m_pFirstRun);
2359 	// only do the line layout if this block is not hidden ...
2360  	FV_View * pView = getView();
2361 
2362 	bool bShowHidden = pView && pView->getShowPara();
2363 	FPVisibility eHidden = isHidden();
2364 	bool bHidden = ((eHidden == FP_HIDDEN_TEXT && !bShowHidden)
2365 		              || eHidden == FP_HIDDEN_REVISION
2366 					|| eHidden == FP_HIDDEN_FOLDED
2367 		              || eHidden == FP_HIDDEN_REVISION_AND_TEXT);
2368 	if(!bHidden)
2369 		pFirst->layout();
2370 	// Run list should be valid now.
2371 	_assertRunListIntegrity();
2372 }
2373 
2374 /*!
2375   Remove end-of-paragraph Run from block
2376 
2377   This function does the opposite of the _insertEndOfParagraphRun
2378   function.
2379 
2380   \note It should <b>only</b> be called by functions that do really
2381 		low-level handling of blocks and only on newly created blocks.
2382 */
2383 void
_purgeEndOfParagraphRun(void)2384 fl_BlockLayout::_purgeEndOfParagraphRun(void)
2385 {
2386 	UT_ASSERT(m_pFirstRun &&
2387 			  FPRUN_ENDOFPARAGRAPH == m_pFirstRun->getType());
2388 	fp_Line * pFirstLine = static_cast<fp_Line *>(getFirstContainer());
2389 	UT_ASSERT(pFirstLine && pFirstLine->countRuns() == 1);
2390 
2391 	// Run list should be valid when called (but not at exit!)
2392 	_assertRunListIntegrity();
2393 
2394 	pFirstLine->removeRun(m_pFirstRun);
2395 	delete m_pFirstRun;
2396 	m_pFirstRun = NULL;
2397 
2398 	pFirstLine->remove();
2399 	delete pFirstLine;
2400 	setFirstContainer(NULL);
2401 	setLastContainer(NULL);
2402 }
2403 
2404 /*!
2405   Split the line the Run resides on
2406   \param pRun The Run to split the line after
2407 
2408   There is never added any Runs as it happened in the past to ensure
2409   that both lines can hold the point. This is because the caller
2410   always does that now.
2411 */
2412 void
_breakLineAfterRun(fp_Run * pRun)2413 fl_BlockLayout::_breakLineAfterRun(fp_Run* pRun)
2414 {
2415 	_assertRunListIntegrity();
2416 
2417 	// When loading a document, there may not have been created
2418 	// lines yet. Get a first one created and hope for the best...
2419 	// Sevior: Ah here is one source of the multi-level list bug we
2420 	// need a last line from the previous block before we call this.
2421 	if (getPrev() != NULL && getPrev()->getLastContainer() == NULL)
2422 	{
2423 		xxx_UT_DEBUGMSG(("In _breakLineAfterRun no LastLine \n"));
2424 		xxx_UT_DEBUGMSG(("getPrev = %d this = %d \n", getPrev(), this));
2425 		//UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2426 	}
2427 
2428 	// Add a line for the Run if necessary
2429 	if (getFirstContainer() == NULL)
2430 		_stuffAllRunsOnALine();
2431 
2432 	// Create the new line
2433 	fp_Line* pNewLine = new fp_Line(getSectionLayout());
2434 	UT_ASSERT(pNewLine);
2435 	// Insert it after the current line
2436 	fp_Line* pLine = pRun->getLine();
2437 	pNewLine->setPrev(pLine);
2438 	pNewLine->setNext(pLine->getNext());
2439 
2440 	if(pLine->getNext())
2441 		pLine->getNext()->setPrev(pNewLine);
2442 
2443 	pLine->setNext(pNewLine);
2444 	// Update LastContainer if necessary
2445 	if (getLastContainer() == static_cast<fp_Container *>(pLine))
2446 		setLastContainer(pNewLine);
2447 	// Set the block
2448 	pNewLine->setBlock(this);
2449 	// Add the line to the container
2450 	static_cast<fp_VerticalContainer *>(pLine->getContainer())->insertContainerAfter(static_cast<fp_Container *>(pNewLine),
2451 																				     static_cast<fp_Container *>(pLine));
2452 
2453 	// Now add Runs following pRun on the same line to the new
2454 	// line.
2455 	fp_Run* pCurrentRun = pRun->getNextRun();
2456 	while (pCurrentRun && pCurrentRun->getLine() == pLine)
2457 	{
2458 		pLine->removeRun(pCurrentRun, true);
2459 		pNewLine->addRun(pCurrentRun);
2460 		pCurrentRun = pCurrentRun->getNextRun();
2461 	}
2462 
2463 	// Update the layout information in the lines.
2464 	pLine->layout();
2465 	pNewLine->layout();
2466 #if DEBUG
2467 	if(getFirstContainer())
2468 	{
2469 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
2470 	}
2471 #endif
2472 	_assertRunListIntegrity();
2473 }
2474 
2475 /*!
2476  * This method is called at the end of the layout method in
2477  * fp_VerticalContainer. It places the frames pointed to within the block at
2478  * the appropriate place on the appropriate page. Since we don't know where
2479  * this is until the lines in the block are placed in a column we have to
2480  * wait until both the column and lines are placed on the page.
2481  *
2482  * pLastLine is the last line placed inthe column. If the frame should be
2483  * placed after this line we don't place any frames that should be below
2484  * this line now. In this case we wait until pLastLine is below the frame.
2485  *
2486  * If pLastLine is NULL we place all the frames in this block on the screen.
2487  *
2488  */
setFramesOnPage(fp_Line * pLastLine)2489 bool fl_BlockLayout::setFramesOnPage(fp_Line * pLastLine)
2490 {
2491 	FV_View *pView = getView();
2492 	GR_Graphics * pG = m_pLayout->getGraphics();
2493 	FL_DocLayout *pDL = getDocLayout();
2494 	UT_return_val_if_fail( pView && pG, false );
2495 
2496 	if(getNumFrames() == 0)
2497 	{
2498 		return true;
2499 	}
2500 
2501 	UT_sint32 i = 0;
2502 	for(i=0; i< getNumFrames();i++)
2503 	{
2504 		fl_FrameLayout * pFrame = getNthFrameLayout(i);
2505 
2506 		if(pFrame->isHidden() > FP_VISIBLE)
2507 			continue;
2508 
2509 		if(pFrame->getContainerType() != FL_CONTAINER_FRAME)
2510 		{
2511 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2512 			continue;
2513 		}
2514 		if(pFrame->getFramePositionTo() == FL_FRAME_POSITIONED_TO_BLOCK)
2515 		{
2516 			UT_sint32 xFpos = pFrame->getFrameXpos();
2517 			UT_sint32 yFpos = pFrame->getFrameYpos();
2518 			UT_DEBUGMSG(("xFpos %d yFpos %d \n",xFpos,yFpos));
2519 			// Now scan through the lines until we find a line below
2520 			// yFpos
2521 
2522 			fp_Line * pFirstLine = static_cast<fp_Line *>(getFirstContainer());
2523 			fp_Line * pCon = pFirstLine;
2524 			if(pCon == NULL)
2525 			{
2526 				return false;
2527 			}
2528 
2529 			UT_sint32 yoff = pCon->getHeight() + pCon->getMarginAfter();
2530 			while ((pCon != pLastLine) && (yoff < yFpos) && (pCon->getNext()))
2531 			{
2532 				pCon = static_cast<fp_Line *>(pCon->getNext());
2533 				if (!pCon->isSameYAsPrevious())
2534 				{
2535 					yoff += pCon->getHeight();
2536 					yoff += pCon->getMarginAfter();
2537 				}
2538 			}
2539 			if((pCon == pLastLine) && (pCon != static_cast<fp_Line *>(getLastContainer())) && (yoff < yFpos))
2540 			{
2541 				// Frame is not within the container so far filled.
2542 				// try later
2543 				continue;
2544 			}
2545 
2546 			//
2547 			// Do this if we've found a line below the frame
2548 			//
2549 			if(pCon && pCon != pLastLine && yoff >= yFpos)
2550 			{
2551 				yoff -= pCon->getHeight();
2552 				yoff -= pCon->getMarginAfter();
2553 				xxx_UT_DEBUGMSG(("Final yoff %d \n",yoff));
2554 			}
2555 			//
2556 			// OK at this point pCon is the first line above our frame.
2557 			// The Frame should be placed on the same page as this line
2558 			//
2559 			fp_Page * pPage = pCon->getPage();
2560 			UT_sint32 Xref = pCon->getX();
2561 			UT_sint32 Yref = pCon->getY();
2562 			if(pPage == NULL || Yref <= -9999999)
2563 			{
2564 				return false;
2565 			}
2566 			//
2567 			// OK now calculate the offset from the first line to this page.
2568 			//
2569 			UT_sint32 yLineOff,xLineOff;
2570 			fp_VerticalContainer * pVCon = NULL;
2571 			pVCon = (static_cast<fp_VerticalContainer *>(pCon->getContainer()));
2572 			pVCon->getOffsets(pCon, xLineOff, yLineOff);
2573 			UT_DEBUGMSG(("xLineOff %d yLineOff %d in block \n",xLineOff,yLineOff));
2574 			xFpos += xLineOff - Xref; // Never use the x-position
2575                                               // of the Line!!!
2576 			yFpos += yLineOff - yoff;
2577 
2578 			// OK, we have the X and Y positions of the frame relative to
2579 			// the page.
2580 
2581 			fp_FrameContainer * pFrameCon = getNthFrameContainer(i);
2582 			//
2583 			// The frame container may not yet be created.
2584 			//
2585 			if(pFrameCon)
2586 			{
2587 				pFrameCon->setX(xFpos);
2588 				pFrameCon->setY(yFpos);
2589 				UT_return_val_if_fail(pPage,false);
2590 				if(pPage->findFrameContainer(pFrameCon) < 0)
2591 				{
2592 					pPage->insertFrameContainer(pFrameCon);
2593 					UT_sint32 iPrefPage = getDocLayout()->findPage(pPage);
2594 					pFrameCon->setPreferedPageNo(iPrefPage);
2595 				}
2596 			}
2597 		}
2598 		else if(pFrame->getFramePositionTo() == FL_FRAME_POSITIONED_TO_COLUMN)
2599 		{
2600 			fp_FrameContainer * pFrameCon = getNthFrameContainer(i);
2601 			//
2602 			// The frame container may not yet be created.
2603 			//
2604 			if(pFrameCon)
2605 			{
2606 				UT_sint32 iPrefPage = pFrameCon->getPreferedPageNo();
2607 				UT_sint32 iPrefColumn = pFrameCon->getPreferedColumnNo();
2608 				bool b_PrefColumnChanged = false;
2609 				if (iPrefColumn < 0)
2610 				{
2611 					iPrefColumn = 0;
2612 					b_PrefColumnChanged = true;
2613 				}
2614 				fl_DocSectionLayout *pSection = getDocSectionLayout();
2615 				UT_sint32 numColumns = getDocSectionLayout()->getNumColumns();
2616 				//
2617 				// Handle case of block spanning two pages
2618 				//
2619 				fp_Page * pPage = NULL;
2620 				fp_Container * pCol = NULL;
2621 				fp_Line * pLFirst = static_cast<fp_Line *>(getFirstContainer());
2622 				UT_return_val_if_fail(pLFirst,false);
2623 				fp_Page * pPageFirst = pLFirst->getPage();
2624 				UT_return_val_if_fail(pPageFirst,false);
2625 				fp_Line * pLLast = static_cast<fp_Line *>(getLastContainer());
2626 				UT_return_val_if_fail(pLLast,false);
2627 				fp_Page * pPageLast = pLLast->getPage();
2628 				if (pDL->isLayoutFilling())
2629 				{
2630 					fp_Page * pPageFinal = pDL->getLastPage();
2631 					if (!pPageLast && (pDL->findPage(pPageFinal) <= iPrefPage))
2632 					{
2633 						if (pDL->findPage(pPageFinal) == iPrefPage)
2634 						{
2635 							UT_sint32 j=0;
2636 							UT_sint32 k=0;
2637 							bool b_sectionFound = false;
2638 							for(j=0;j<pPageFinal->countColumnLeaders() && !b_sectionFound;j++)
2639 							{
2640 								if (pPageFinal->getNthColumnLeader(j)->getDocSectionLayout()==pSection)
2641 								{
2642 									b_sectionFound = true;
2643 									fp_Container * pCol2 = pPageFinal->getNthColumnLeader(j);
2644 									while(k < iPrefColumn && pCol2)
2645 									{
2646 										pCol2 = static_cast <fp_Container *> (pCol2->getNext());
2647 										k++;
2648 									}
2649 								}
2650 							}
2651 							if (k < iPrefColumn)
2652 							{
2653 							// The good column has not yet been drawn
2654 								continue;
2655 							}
2656 						}
2657 						else
2658 						{
2659 							// The good column has not yet been drawn
2660 							continue;
2661 						}
2662 					}
2663 					else
2664 					{
2665 						if (pDL->findPage(pPageFirst) > iPrefPage)
2666 						{
2667 							if((iPrefPage >= 0) && (iPrefPage > pDL->findPage(pPageFirst) - 3))
2668 							{
2669 								pPage = pDL->getNthPage(iPrefPage);
2670 							}
2671 							else
2672 							{
2673 								pPage = pPageFirst;
2674 							}
2675 						}
2676 						else if (pPageLast && pDL->findPage(pPageLast) < iPrefPage)
2677 						{
2678 							if(iPrefPage < pDL->findPage(pPageLast) + 3)
2679 							{
2680 								// The frame will be inserted when iPrefPage is created
2681 								// Add it to a temporary list for now
2682 								pDL->addFramesToBeInserted(pFrameCon);
2683 								pPage = NULL;
2684 							}
2685 							else
2686 							{
2687 								pPage = pPageLast;
2688 							}
2689 						}
2690 						else
2691 						{
2692 							pPage = pDL->getNthPage(iPrefPage);
2693 						}
2694 					}
2695 					if (pPage) // pPage might be NULL
2696 					{
2697 						if (numColumns > iPrefColumn)
2698 						{
2699 							pCol = pPage->getNthColumn(iPrefColumn,pSection);
2700 						}
2701 						else
2702 						{
2703 							pCol = pPage->getNthColumn(numColumns-1,pSection);
2704 							b_PrefColumnChanged = true;
2705 						}
2706 					}
2707 				}
2708 				else
2709 				{
2710 					UT_return_val_if_fail(pPageLast,false);
2711 					UT_sint32 iPageFirst = getDocLayout()->findPage(pPageFirst);
2712 					UT_sint32 iPageLast = getDocLayout()->findPage(pPageLast);
2713 					if(pPageFirst != pPageLast)
2714 					{
2715 						if(iPrefPage == iPageFirst)
2716 						{
2717 							pPage = pPageFirst;
2718 							fp_Column * firstColumn = static_cast <fp_Column *> (pLFirst->getColumn());
2719 							if ((firstColumn->getColumnIndex() <= iPrefColumn) &&
2720 								(numColumns > iPrefColumn))
2721 							{
2722 								pCol = pPage->getNthColumn(iPrefColumn,pSection);
2723 							}
2724 							else
2725  							{
2726 								pCol = pLFirst->getColumn();
2727 								b_PrefColumnChanged = true;
2728 							}
2729 						}
2730 						else if(iPrefPage == iPageLast)
2731 						{
2732 							pPage = pPageLast;
2733 							fp_Column * lastColumn = static_cast <fp_Column *> (pLLast->getColumn());
2734 							if (lastColumn->getColumnIndex() >= iPrefColumn)
2735 							{
2736 								pCol = pPage->getNthColumn(iPrefColumn,pSection);
2737 							}
2738 							else
2739 							{
2740 								pCol = pLLast->getColumn();
2741 								b_PrefColumnChanged = true;
2742 							}
2743 
2744 						}
2745 						else if((iPrefPage >= iPageFirst) && (iPrefPage <= iPageLast))
2746 						{
2747 							pPage = pDL->getNthPage(iPrefPage);
2748 							if (numColumns > iPrefColumn)
2749 							{
2750 								pCol = pPage->getNthColumn(iPrefColumn,pSection);
2751 							}
2752 							else
2753 							{
2754 								pCol = pPage->getNthColumn(numColumns-1,pSection);
2755 								b_PrefColumnChanged = true;
2756 							}
2757 						}
2758 						else
2759 						{
2760 							pPage = pPageFirst;
2761 							pCol = pLFirst->getColumn();
2762 						}
2763 					}
2764 					else
2765 					{
2766 						pPage = pPageFirst;
2767 						fp_Column * firstColumn = static_cast <fp_Column *> (pLFirst->getColumn());
2768 						fp_Column * lastColumn = static_cast <fp_Column *> (pLLast->getColumn());
2769 						if (iPrefColumn < firstColumn->getColumnIndex())
2770 						{
2771 							pCol = pLFirst->getColumn();
2772 							b_PrefColumnChanged = true;
2773 						}
2774 						else if (iPrefColumn > lastColumn->getColumnIndex())
2775 						{
2776 							pCol = pLLast->getColumn();
2777 							b_PrefColumnChanged = true;
2778 						}
2779 						else
2780 						{
2781 							pCol = pPage->getNthColumn(iPrefColumn,pSection);
2782 						}
2783 					}
2784 
2785 					UT_sint32 iGuessedPage = getDocLayout()->findPage(pPage);
2786 
2787 					if((iPrefPage > -1) && (iPrefPage > iGuessedPage-3) && (iPrefPage < iGuessedPage+3))
2788 					{
2789 						fp_Page *pPrefPage = getDocLayout()->getNthPage(iPrefPage);
2790 						if(pPrefPage && (pPage != pPrefPage))
2791 						{
2792 							pPage = pPrefPage;
2793 						}
2794 						if (numColumns > iPrefColumn)
2795 						{
2796 							pCol = pPage->getNthColumn(iPrefColumn,pSection);
2797 							b_PrefColumnChanged = false;
2798 						}
2799 						else
2800 						{
2801 							pCol = pPage->getNthColumn(numColumns-1,pSection);
2802 							b_PrefColumnChanged = true;
2803 						}
2804 					}
2805 				}
2806 
2807 				if (pCol == NULL) // this may happen if pPage is NULL
2808 					return false;
2809 				pFrameCon->setX(pFrame->getFrameXColpos()+pCol->getX());
2810 				pFrameCon->setY(pFrame->getFrameYColpos()+pCol->getY());
2811 				UT_return_val_if_fail(pPage,false);
2812 				if(pPage && pPage->findFrameContainer(pFrameCon) < 0)
2813 				{
2814 					pPage->insertFrameContainer(pFrameCon);
2815 					iPrefPage = pDL->findPage(pPage);
2816 					pFrameCon->setPreferedPageNo(iPrefPage);
2817 				}
2818 				if (b_PrefColumnChanged)
2819 				{
2820 					fp_Column * pColumn = static_cast <fp_Column *> (pCol);
2821 					pFrameCon->setPreferedColumnNo(pColumn->getColumnIndex());
2822 				}
2823 			}
2824 		}
2825 		else if(pFrame->getFramePositionTo() == FL_FRAME_POSITIONED_TO_PAGE)
2826 		{
2827 			fp_FrameContainer * pFrameCon = getNthFrameContainer(i);
2828 			//
2829 			// The frame container may not yet be created.
2830 			//
2831 			fp_Page * pPage = NULL;
2832 			if(pFrameCon)
2833 			{
2834 				//
2835 				// Handle case of block spanning two pages
2836 				//
2837 				UT_sint32 iPrefPage = pFrameCon->getPreferedPageNo();
2838 				fp_Line * pLFirst = static_cast<fp_Line *>(getFirstContainer());
2839 				UT_return_val_if_fail(pLFirst,false);
2840 				fp_Line * pLLast = static_cast<fp_Line *>(getLastContainer());
2841 				UT_return_val_if_fail(pLLast,false);
2842 				fp_Page * pPageFirst = pLFirst->getPage();
2843 				UT_return_val_if_fail(pPageFirst,false);
2844 				fp_Page * pPageLast = pLLast->getPage();
2845 				if (pDL->isLayoutFilling())
2846 				{
2847 					if (!pPageLast && (pDL->findPage(pDL->getLastPage()) < iPrefPage))
2848 					{
2849 						// The good page has not yet been drawn
2850 						continue;
2851 					}
2852 					else
2853 					{
2854 						if (pDL->findPage(pPageFirst) > iPrefPage)
2855 						{
2856 							if((iPrefPage >= 0) && (iPrefPage > pDL->findPage(pPageFirst) - 3))
2857 							{
2858 								pPage = pDL->getNthPage(iPrefPage);
2859 							}
2860 							else
2861 							{
2862 								pPage = pPageFirst;
2863 							}
2864 						}
2865 						else if (pPageLast && pDL->findPage(pPageLast) < iPrefPage)
2866 						{
2867 							if(iPrefPage < pDL->findPage(pPageLast) + 3)
2868 							{
2869 								// The frame will be inserted when iPrefPage is created
2870 								// Add it to a temporary list for now
2871 								pDL->addFramesToBeInserted(pFrameCon);
2872 								pPage = NULL;
2873 							}
2874 							else
2875 							{
2876 								pPage = pPageLast;
2877 							}
2878 						}
2879 						else
2880 						{
2881 							pPage = pDL->getNthPage(iPrefPage);
2882 						}
2883 					}
2884 				}
2885 				else
2886 				{
2887 					UT_return_val_if_fail(pPageLast,false);
2888 					pPage = pPageLast;
2889 					if(pPageFirst != pPageLast)
2890 					{
2891 						UT_sint32 idLast = abs(pLLast->getY() - pFrame->getFrameYColpos());
2892 						UT_sint32 idFirst =  abs(pLFirst->getY() - pFrame->getFrameYColpos());
2893 						if(idFirst < idLast)
2894 						{
2895 							pPage = pPageFirst;
2896 						}
2897 					}
2898 
2899 					UT_sint32 iGuessedPage = getDocLayout()->findPage(pPage);
2900 					if((iPrefPage > -1) && (iPrefPage > iGuessedPage-3) && (iPrefPage < iGuessedPage+3))
2901 					{
2902 						pPage = getDocLayout()->getNthPage(iPrefPage);
2903 					}
2904 				}
2905 				pFrameCon->setX(pFrame->getFrameXPagepos());
2906 				pFrameCon->setY(pFrame->getFrameYPagepos());
2907 				if(pPage && pPage->findFrameContainer(pFrameCon) < 0)
2908 				{
2909 					pPage->insertFrameContainer(pFrameCon);
2910 					iPrefPage = pDL->findPage(pPage);
2911 					pFrameCon->setPreferedPageNo(iPrefPage);
2912 				}
2913 			}
2914 		}
2915 		else
2916 		{
2917 			UT_DEBUGMSG(("Not implemented Yet \n"));
2918 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2919 		}
2920 
2921 	}
2922 	return true;
2923 }
2924 /*!
2925  * This returns the distance from the first line in the block to the
2926  * line presented here.
2927  */
getXYOffsetToLine(UT_sint32 & xoff,UT_sint32 & yoff,fp_Line * pLine) const2928 bool fl_BlockLayout::getXYOffsetToLine(UT_sint32 & xoff, UT_sint32 & yoff, fp_Line * pLine) const
2929 {
2930 	if(pLine == NULL)
2931 	{
2932 		return false;
2933 	}
2934 	xoff = 0;
2935 	yoff = 0;
2936 	fp_Line * pCon = static_cast<fp_Line *>(getFirstContainer());
2937 	while(pCon && (pCon != pLine))
2938 	{
2939 		if (!pCon->isSameYAsPrevious())
2940 		{
2941 			yoff += pCon->getHeight();
2942 			yoff += pCon->getMarginAfter();
2943 		}
2944 		pCon = static_cast<fp_Line *>(pCon->getNext());
2945 	}
2946 	if(pCon != pLine)
2947 	{
2948 		return false;
2949 	}
2950 	return true;
2951 }
2952 /*!
2953  * Calculate the height of the all the text contained by this block
2954  */
getHeightOfBlock(bool b_withMargins)2955 UT_sint32 fl_BlockLayout::getHeightOfBlock(bool b_withMargins)
2956 {
2957 	UT_sint32 iHeight = 0;
2958 	fp_Line * pCon = static_cast<fp_Line *>(getFirstContainer());
2959 	while(pCon)
2960 	{
2961 		if(!pCon->isSameYAsPrevious())
2962 		{
2963 			iHeight += pCon->getHeight();
2964 			if (b_withMargins)
2965 			{
2966 				iHeight += pCon->getMarginBefore();
2967 				iHeight += pCon->getMarginAfter();
2968 			}
2969 		}
2970 		pCon = static_cast<fp_Line *>(pCon->getNext());
2971 	}
2972 	return iHeight;
2973 }
2974 
2975 /*!
2976  * Force a sectionBreak by setting StartHeight to a ridiculus value
2977  */
forceSectionBreak(void)2978 void fl_BlockLayout::forceSectionBreak(void)
2979 {
2980 	m_bForceSectionBreak = true;
2981 }
2982 
2983 /*!
2984  * Minimum width of a line we'll try to fit a wrapped line within
2985  */
getMinWrapWidth(void) const2986 UT_sint32 fl_BlockLayout::getMinWrapWidth(void) const
2987 {
2988 	return 4*20*4;
2989 }
2990 
2991 /*!
2992  * Reformat a block from the line given.
2993  */
formatWrappedFromHere(fp_Line * pLine,fp_Page * pPage)2994 void fl_BlockLayout::formatWrappedFromHere(fp_Line * pLine, fp_Page * pPage)
2995 {
2996 	//
2997 	// Check line is in this block. It might haave been removed.
2998 	//
2999 	fp_Line *pCLine = static_cast<fp_Line *>(getFirstContainer());
3000 	bool bFound = false;
3001 	while(pCLine)
3002 	{
3003 		if(pCLine == pLine)
3004 		{
3005 			bFound = true;
3006 			break;
3007 		}
3008 		pCLine = static_cast<fp_Line *>(pCLine->getNext());
3009 	}
3010 	if(!bFound)
3011 	{
3012 		_removeAllEmptyLines(); // try again
3013 		return;
3014 	}
3015 	fp_Run * pRun2 = pLine->getLastRun();
3016 	if(pLine->getHeight() == 0)
3017 	{
3018 		pLine->recalcHeight(pRun2);
3019 	}
3020 	pRun2 = pRun2->getNextRun();
3021 	m_pVertContainer = static_cast<fp_VerticalContainer *>(pLine->getContainer());
3022 	m_iLinePosInContainer = m_pVertContainer->findCon(pLine)+1;
3023 	if(m_iLinePosInContainer < 0)
3024 	{
3025 		m_iLinePosInContainer = 0;
3026 	}
3027 	UT_Rect * pRec = pLine->getScreenRect();
3028 	m_iAccumulatedHeight = pRec->top;
3029 	UT_Rect * pVertRect = m_pVertContainer->getScreenRect();
3030 	UT_sint32 iYBotScreen = pVertRect->top + pVertRect->height;
3031 	xxx_UT_DEBUGMSG(("Initial m_iAccumulatedHeight %d iYBotScreen %d \n",m_iAccumulatedHeight,iYBotScreen));
3032 	delete pVertRect;
3033 	m_iAdditionalMarginAfter = 0;
3034 	UT_Rect rec;
3035 	rec.height = pRec->height;
3036 	rec.width = pRec->width;
3037 	rec.top = pRec->top;
3038 	rec.left = pRec->left;
3039 	delete pRec;
3040 	m_bSameYAsPrevious = pLine->isSameYAsPrevious();
3041 	UT_sint32 iHeight = pLine->getHeight() + pLine->getMarginAfter();
3042 	//
3043 	// Stuff remaining content on the line
3044 	//
3045 	while(pRun2)
3046 	{
3047 		pLine->addRun(pRun2);
3048 		pRun2 = pRun2->getNextRun();
3049 	}
3050 	//
3051 	// Remove all the lines after this
3052 	//
3053 	fp_Line * pDumLine = static_cast<fp_Line *>(pLine->getNext());
3054 	while(pDumLine)
3055 	{
3056 		fp_Line * pLineToDelete = pDumLine;
3057 		pDumLine = static_cast<fp_Line *>(pDumLine->getNext());
3058 		pLineToDelete->setBlock(NULL);
3059 // delete this and remove from container
3060 		_removeLine(pLineToDelete,true,false);
3061 	}
3062 	//
3063 	// OK our line is the last line left
3064 	//
3065 	setLastContainer(pLine);
3066 	//
3067 	// OK now we have to adjust the X and max width of pLine to fit around
3068 	// the wrapped objects. We do this by looping though the wrapped objects
3069 	// on the page
3070 	//
3071 	UT_sint32 iX = getLeftMargin();
3072 	UT_sint32 iMaxW = m_pVertContainer->getWidth();
3073 	iMaxW -=  getLeftMargin();
3074 	iMaxW -= getRightMargin();
3075 	if(pLine == static_cast<fp_Line *>(getFirstContainer()))
3076 	{
3077 		UT_BidiCharType iBlockDir = getDominantDirection();
3078 		if(iBlockDir == UT_BIDI_LTR)
3079 		{
3080 			iMaxW -= getTextIndent();
3081 			iX += getTextIndent();
3082 		}
3083 	}
3084 	//
3085 	// OK Now adjust the left pos to make it bump up against either the
3086 	// the left side of the container or the previous line.
3087 	//
3088 	fp_Line * pPrev = static_cast<fp_Line *>(pLine->getPrev());
3089 	UT_sint32 iWidth = 0;
3090 	if(pPrev)
3091 	{
3092 		if(pLine->isSameYAsPrevious() && (pPrev->getY() == pLine->getY()))
3093 		{
3094 			iX = pPrev->getX() + pPrev->getMaxWidth();
3095 			iWidth = iMaxW - iX;
3096 		}
3097 		else
3098 		{
3099 			iWidth = iMaxW;
3100 			pLine->setSameYAsPrevious(false);
3101 		}
3102 	}
3103 	else
3104 	{
3105 		iWidth = iMaxW;
3106 		pLine->setSameYAsPrevious(false);
3107 	}
3108 	UT_sint32 xoff = rec.left - pLine->getX();
3109 	if(iWidth < getMinWrapWidth())
3110 	{
3111 		xxx_UT_DEBUGMSG(("!!!!!!! ttttOOOO NAAARRRROOOWWWW iMaxX %d iX %d \n",iMaxX,iX));
3112 		//
3113 		// Can't fit on this line.
3114 		// transfer to new wrapped line and delete the old one
3115 		//
3116 		m_iAccumulatedHeight += iHeight;
3117 		iX = getLeftMargin();
3118 		bool bFirst = false;
3119 		if(pLine == static_cast<fp_Line *>(getFirstContainer()))
3120 		{
3121 			bFirst = true;
3122 			UT_BidiCharType iBlockDir = getDominantDirection();
3123 			if(iBlockDir == UT_BIDI_LTR)
3124 			{
3125 				iX += getTextIndent();
3126 			}
3127 		}
3128 		m_bSameYAsPrevious = false;
3129 		fp_Line * pNew = NULL;
3130 		if(m_iAccumulatedHeight <= iYBotScreen)
3131 		{
3132 			pNew = getNextWrappedLine(iX,iHeight,pPage);
3133 		}
3134 		else
3135 		{
3136 			pNew = static_cast<fp_Line *>(getNewContainer());
3137 		}
3138 		while(pNew && (pNew->getPrev() != pLine))
3139 		{
3140 			pNew = static_cast<fp_Line *>(pNew->getPrev());
3141 		}
3142 		fp_Run * pRun = pLine->getFirstRun();
3143 		while(pRun)
3144 		{
3145 			pNew->addRun(pRun);
3146 			pRun= pRun->getNextRun();
3147 		}
3148 		fp_Container * pPrevLine = pLine->getPrevContainerInSection();
3149 		if(pPrevLine && pPrevLine->getContainerType() == FP_CONTAINER_LINE)
3150 		{
3151 			static_cast<fp_Line *>(pPrevLine)->setAdditionalMargin(m_iAdditionalMarginAfter);
3152 		}
3153 		if(pPrevLine && pPrevLine->getContainerType() == FP_CONTAINER_TABLE )
3154 		{
3155 			static_cast<fp_TableContainer *>(pPrevLine)->setAdditionalMargin(m_iAdditionalMarginAfter);
3156 		}
3157 		_removeLine(pLine,true,false);
3158 		pLine = pNew;
3159 		if(bFirst)
3160 		{
3161 			setFirstContainer(pLine);
3162 		}
3163 	}
3164 	else
3165 	{
3166 		UT_sint32 iMinLeft = BIG_NUM_BLOCKBL;
3167 		UT_sint32 iMinWidth = BIG_NUM_BLOCKBL;
3168 		UT_sint32 iMinRight = BIG_NUM_BLOCKBL;
3169 		getLeftRightForWrapping(iX, rec.height,iMinLeft,iMinRight,iMinWidth);
3170 		iX = iMinLeft - xoff;
3171 		pLine->setX(iX);
3172 		if(iMinWidth < getMinWrapWidth())
3173 		{
3174 			//
3175 			// Can't fit on this line.
3176 			// transfer to new wrapped line and delete the old one
3177 			//
3178 			xxx_UT_DEBUGMSG(("Line too narrow in formatwrapped %x block %d \n",pLine,this));
3179 			iX = getLeftMargin();
3180 			bool bFirst = false;
3181 			if(pLine == static_cast<fp_Line *>(getFirstContainer()))
3182 			{
3183 				bFirst = true;
3184 				UT_BidiCharType iBlockDir = getDominantDirection();
3185 				if(iBlockDir == UT_BIDI_LTR)
3186 				{
3187 					iX += getTextIndent();
3188 				}
3189 			}
3190 			m_iAccumulatedHeight += iHeight;
3191 			m_bSameYAsPrevious = false;
3192 			fp_Line * pNew = NULL;
3193 			if(m_iAccumulatedHeight <= iYBotScreen)
3194 			{
3195 				pNew = getNextWrappedLine(iX,iHeight,pPage);
3196 			}
3197 			else
3198 			{
3199 				pNew = static_cast<fp_Line *>(getNewContainer());
3200 			}
3201 			while(pNew && static_cast<fp_Line *>(pNew->getPrev()) != pLine)
3202 			{
3203 				pNew = static_cast<fp_Line *>(pNew->getPrev());
3204 			}
3205 			fp_Run * pRun = pLine->getFirstRun();
3206 			while(pRun)
3207 			{
3208 				pNew->addRun(pRun);
3209 				pRun= pRun->getNextRun();
3210 			}
3211 			fp_Container * pPrevLine = pLine->getPrevContainerInSection();
3212 			if(pPrevLine && pPrevLine->getContainerType() == FP_CONTAINER_LINE )
3213 			{
3214 				static_cast<fp_Line *>(pPrevLine)->setAdditionalMargin(m_iAdditionalMarginAfter);
3215 			}
3216 			if(pPrevLine && pPrevLine->getContainerType() == FP_CONTAINER_TABLE )
3217 			{
3218 				static_cast<fp_TableContainer *>(pPrevLine)->setAdditionalMargin(m_iAdditionalMarginAfter);
3219 			}
3220 			_removeLine(pLine,true,false);
3221 			pLine = pNew;
3222 			if(bFirst)
3223 			{
3224 				pLine->setPrev(NULL);
3225 				setFirstContainer(pLine);
3226 			}
3227 		}
3228 		else
3229 		{
3230 			m_bSameYAsPrevious = true;
3231 			UT_DEBUGMSG(("Max width 1 set to %d \n",iMinWidth));
3232 			pLine->setMaxWidth(iMinWidth);
3233 		}
3234 	}
3235 	//
3236 	// OK, Now we have one long line with all our remaining content.
3237 	// Break it to fit in the container and around the wrapped objects
3238 	//
3239 		// Reformat paragraph
3240 	m_Breaker.breakParagraph(this, pLine,pPage);
3241 	xxx_UT_DEBUGMSG(("Format wrapped text in blobk %x \n",this));
3242 
3243 	pLine = static_cast<fp_Line *>(getFirstContainer());
3244 	while(pLine)
3245 	{
3246 		pLine->recalcHeight();
3247 		pLine = static_cast<fp_Line *>(pLine->getNext());
3248 	}
3249 	UT_ASSERT(getLastContainer());
3250 	if(!m_pLayout->isLayoutFilling())
3251     {
3252 		m_iNeedsReformat = -1;
3253 	}
3254 	if(m_pAlignment && m_pAlignment->getType() == FB_ALIGNMENT_JUSTIFY)
3255 	{
3256 		fp_Line* pLastLine = static_cast<fp_Line *>(getLastContainer());
3257 		pLastLine->resetJustification(true); // permanent reset
3258 	}
3259 #if DEBUG
3260 	if(getFirstContainer())
3261 	{
3262 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
3263 	}
3264 #endif
3265 	return;
3266 }
3267 
3268 /*!
3269  * Given the x-position (iX) and the height of the line (iHeight) this
3270  * Method returns the width of the line that fits at the current screen
3271  * position.
3272  *
3273  * The dimensions of all the parameters are logical units.
3274  *
3275  * (input) (iX)The position relative the container holding the line of the left
3276  *         edge of the line.
3277  * (input) (iHeight) The assumed height of the line.
3278  *
3279  * (output) The width of the line that fits between the image is iMinWidth
3280  * (output) iMinLeft is the left-most edge of the line that fits
3281  * (output) iMinRight is the right-most edge of the line that fits
3282  */
getLeftRightForWrapping(UT_sint32 iX,UT_sint32 iHeight,UT_sint32 & iMinLeft,UT_sint32 & iMinRight,UT_sint32 & iMinWidth)3283 void fl_BlockLayout::getLeftRightForWrapping(UT_sint32 iX, UT_sint32 iHeight,
3284 											  UT_sint32 & iMinLeft,
3285 											  UT_sint32 & iMinRight,
3286 											  UT_sint32 & iMinWidth)
3287 {
3288 	UT_sint32 iMaxW = m_pVertContainer->getWidth();
3289 	UT_sint32 iMinR = m_pVertContainer->getWidth();
3290 	UT_sint32 iXDiff = getLeftMargin();
3291 	GR_Graphics * pG = m_pLayout->getGraphics();
3292 	UT_ASSERT(iHeight > 0);
3293 	if(iHeight == 0)
3294 	{
3295 		if(getLastContainer())
3296 		{
3297 			iHeight = getLastContainer()->getHeight();
3298 		}
3299 		if(iHeight == 0)
3300 		{
3301 			iHeight = m_pLayout->getGraphics()->tlu(2);
3302 		}
3303 	}
3304 	iMaxW -=  getLeftMargin();
3305 	iMaxW -= getRightMargin();
3306 	if (getFirstContainer() == NULL)
3307 	{
3308 		UT_BidiCharType iBlockDir = getDominantDirection();
3309 		if(iBlockDir == UT_BIDI_LTR)
3310 		{
3311 			iMaxW -= getTextIndent();
3312 			iXDiff += getTextIndent();
3313 		}
3314 	}
3315 	UT_sint32 xoff,yoff;
3316 	fp_Page * pPage = m_pVertContainer->getPage();
3317 	pPage->getScreenOffsets(m_pVertContainer,xoff,yoff);
3318  	fp_FrameContainer * pFC = NULL;
3319 	UT_sint32 iExpand = 0;
3320 
3321 	UT_sint32 i = 0;
3322 	UT_sint32 iScreenX = iX + xoff;
3323 	UT_Rect projRec;
3324 	bool bIsTight = false;
3325 	iMinLeft = BIG_NUM_BLOCKBL;
3326 	iMinWidth = BIG_NUM_BLOCKBL;
3327 	iMinRight = BIG_NUM_BLOCKBL;
3328 	for(i=0; i< static_cast<UT_sint32>(pPage->countAboveFrameContainers());i++)
3329 	{
3330 		projRec.left = iScreenX;
3331 		projRec.height = iHeight;
3332 		projRec.width = iMaxW;
3333 		projRec.top = m_iAccumulatedHeight;
3334 		m_iAdditionalMarginAfter = 0;
3335 		pFC = pPage->getNthAboveFrameContainer(i);
3336 		if(!pFC->isWrappingSet())
3337 		{
3338 			continue;
3339 		}
3340 		bIsTight = pFC->isTightWrapped();
3341 		UT_Rect * pRec = pFC->getScreenRect();
3342 		xxx_UT_DEBUGMSG(("Frame Left %d Line Left %d \n",pRec->left,iScreenX));
3343 		fl_FrameLayout * pFL = static_cast<fl_FrameLayout *>(pFC->getSectionLayout());
3344 		iExpand = pFL->getBoundingSpace() + 2;
3345 		pRec->height += 2*iExpand;
3346 		pRec->width += 2*iExpand;
3347 		pRec->left -= iExpand;
3348 		pRec->top -= iExpand;
3349 		if(projRec.intersectsRect(pRec))
3350 		{
3351 			if(!pFC->overlapsRect(projRec)  && bIsTight)
3352 			{
3353 				delete pRec;
3354 				continue;
3355 			}
3356 			if((!pFC->isLeftWrapped() && ((pRec->left - getMinWrapWidth() <= projRec.left +pG->tlu(1)) && (pRec->left + pRec->width) > projRec.left)) || pFC->isRightWrapped())
3357 			{
3358 				UT_sint32 iRightP = 0;
3359 				if(bIsTight)
3360 				{
3361 					//
3362 					// Project back into image over the transparent region
3363 					//
3364 					iRightP = pFC->getRightPad(m_iAccumulatedHeight,iHeight) - iExpand;
3365 					xxx_UT_DEBUGMSG(("Projecnt Right %d \n",iRightP));
3366 				}
3367 				projRec.left = pRec->left + pRec->width + iRightP + pG->tlu(1);
3368 				if(projRec.left < iMinLeft)
3369 				{
3370 					iMinLeft = projRec.left;
3371 				}
3372 
3373 			}
3374 			else if(((pRec->left >= (projRec.left -iExpand -pG->tlu(1))) && (projRec.left + projRec.width + getMinWrapWidth() > (pRec->left -iExpand - pG->tlu(1)))) || pFC->isLeftWrapped())
3375 			{
3376 				UT_sint32 iLeftP = 0;
3377 				if(bIsTight)
3378 				{
3379 					//
3380 					// Project into the image over the transparent region
3381 					//
3382 					iLeftP = pFC->getLeftPad(m_iAccumulatedHeight,iHeight) - iExpand;
3383 					xxx_UT_DEBUGMSG(("Project into (1) image with distance %d \n",iLeftP));
3384 				}
3385 				UT_sint32 diff = pRec->left - iLeftP -pG->tlu(1);
3386 				if(diff < iMinRight)
3387 				{
3388 					iMinRight = diff;
3389 				}
3390 			}
3391 		}
3392 		delete pRec;
3393 	}
3394 	if(iMinLeft == BIG_NUM_BLOCKBL)
3395 	{
3396 		iMinLeft = iScreenX;
3397 	}
3398 	if(iMinRight == BIG_NUM_BLOCKBL)
3399 	{
3400 		iMinRight = iMinR + xoff;
3401 	}
3402 	iMinWidth = iMinRight - iMinLeft;
3403 	if(iMinWidth < 0)
3404 	{
3405 		//
3406 		// Look to see if there is some space between iMinLeft and the right
3407 		// margin
3408 		//
3409 		if(iMinR + xoff - iMinLeft > getMinWrapWidth())
3410 	   	{
3411 			//
3412 			// OK we have some overlapping images. We'll take the right-most
3413 			// edge of the available frames.
3414 			//
3415 			UT_sint32 iRightEdge = 0;
3416 			fp_FrameContainer * pRightC = NULL;
3417 			for(i=0; i< static_cast<UT_sint32>(pPage->countAboveFrameContainers());i++)
3418 			{
3419 				projRec.left = iScreenX;
3420 				projRec.height = iHeight;
3421 				projRec.width = iMaxW;
3422 				projRec.top = m_iAccumulatedHeight;
3423 				m_iAdditionalMarginAfter = 0;
3424 				pFC = pPage->getNthAboveFrameContainer(i);
3425 				if(!pFC->isWrappingSet())
3426 				{
3427 					continue;
3428 				}
3429 				bIsTight = pFC->isTightWrapped();
3430 				UT_Rect * pRec = pFC->getScreenRect();
3431 				fl_FrameLayout * pFL = static_cast<fl_FrameLayout *>(pFC->getSectionLayout());
3432 				iExpand = pFL->getBoundingSpace() + 2;
3433 				pRec->height += 2*iExpand;
3434 				pRec->width += 2*iExpand;
3435 				pRec->left -= iExpand;
3436 				pRec->top -= iExpand;
3437 				if(projRec.intersectsRect(pRec))
3438 				{
3439 					if(!pFC->overlapsRect(projRec)  && bIsTight)
3440 					{
3441 						delete pRec;
3442 						continue;
3443 					}
3444 					if((pRec->left + pRec->width) > iRightEdge)
3445 					{
3446 						iRightEdge = pRec->left + pRec->width;
3447 						pRightC = pFC;
3448 					}
3449 				}
3450 				delete pRec;
3451 			}
3452 			if(pRightC != NULL)
3453 			{
3454 				UT_sint32 iRightP = 0;
3455 				if(pRightC->isTightWrapped())
3456 				{
3457 					//
3458 					// Project back into image over the transparent region
3459 					//
3460 					iRightP = pRightC->getRightPad(m_iAccumulatedHeight,iHeight) - iExpand;
3461 					xxx_UT_DEBUGMSG(("Projecnt Right %d \n",iRightP));
3462 				}
3463 				UT_Rect * pRec = pRightC->getScreenRect();
3464 				iMinLeft = pRec->left + pRec->width + iRightP + pG->tlu(1);
3465 				iMinRight = iMinR + xoff;
3466 				iMinWidth = iMinRight - iMinLeft;
3467 			}
3468 
3469 		}
3470 	}
3471 }
3472 
3473 
3474 /*!
3475  * Create a new line that will fit between positioned objects on the page.
3476  * iX       is the position of the last X coordinate of the previous
3477  *          Line relative to it's container.
3478             The X location of wrapped line will be greater than this.
3479   * iHeight  is the assumed height of the line (at first approximation this
3480             is the height of the previous line).
3481  * pPage    Pointer to the page with the positioned objects.
3482  */
getNextWrappedLine(UT_sint32 iX,UT_sint32 iHeight,fp_Page * pPage)3483 fp_Line *  fl_BlockLayout::getNextWrappedLine(UT_sint32 iX,
3484 											  UT_sint32 iHeight,
3485 											  fp_Page * pPage)
3486 {
3487 	UT_sint32 iMinWidth = BIG_NUM_BLOCKBL;
3488 	UT_sint32 iMinLeft = BIG_NUM_BLOCKBL;
3489 	UT_sint32 iMinRight = BIG_NUM_BLOCKBL;
3490 	fp_Line * pLine = NULL;
3491 	UT_sint32 iXDiff = getLeftMargin();
3492 	UT_sint32 iMinR = m_pVertContainer->getWidth();
3493 	UT_Rect * pVertRect = m_pVertContainer->getScreenRect();
3494 	UT_sint32 iYBotScreen = pVertRect->top + pVertRect->height;
3495 	xxx_UT_DEBUGMSG(("Initial m_iAccumulatedHeight %d iYBotScreen %d \n",m_iAccumulatedHeight,iYBotScreen));
3496 	delete pVertRect;
3497 	if(m_iAccumulatedHeight > iYBotScreen)
3498 	{
3499 			pLine = static_cast<fp_Line *>(getNewContainer());
3500 			m_iAccumulatedHeight += iHeight;
3501 			pLine->setSameYAsPrevious(false);
3502 			m_bSameYAsPrevious = false;
3503 			return pLine;
3504 	}
3505 
3506 	iMinR -= getRightMargin();
3507 	UT_sint32 xoff,yoff;
3508 	pPage->getScreenOffsets(m_pVertContainer,xoff,yoff);
3509 	iMinR += xoff;
3510 	UT_sint32 iMaxW = m_pVertContainer->getWidth();
3511 	iMaxW -=  getLeftMargin();
3512 	iMaxW -= getRightMargin();
3513 	fp_Line * pPrevLine = static_cast<fp_Line *>(getLastContainer());
3514 	if (getFirstContainer() == NULL)
3515 	{
3516 		UT_BidiCharType iBlockDir = getDominantDirection();
3517 		if(iBlockDir == UT_BIDI_LTR)
3518 		{
3519 			iMaxW -= getTextIndent();
3520 			iXDiff += getTextIndent();
3521 		}
3522 	}
3523 	if((iMinR - iX -xoff) < getMinWrapWidth())
3524 	{
3525 		xxx_UT_DEBUGMSG(("!!!!!!! ttttOOOO NAAARRRROOOWWWW iMaxW %d iX %d \n",iMaxW,iX));
3526 		iX = iXDiff;
3527 		m_iAccumulatedHeight += iHeight;
3528 		m_iAdditionalMarginAfter += iHeight;
3529 		m_bSameYAsPrevious = false;
3530 	}
3531 	else
3532 	{
3533 		getLeftRightForWrapping(iX, iHeight,iMinLeft,iMinRight,iMinWidth);
3534 		if(iMinWidth <  getMinWrapWidth())
3535 		{
3536 			iX = getLeftMargin();
3537 			if (getFirstContainer() == NULL)
3538 			{
3539 				UT_BidiCharType iBlockDir = getDominantDirection();
3540 				if(iBlockDir == UT_BIDI_LTR)
3541 					iX += getTextIndent();
3542 			}
3543 			m_iAccumulatedHeight += iHeight;
3544 			m_iAdditionalMarginAfter += iHeight;
3545 			m_bSameYAsPrevious = false;
3546 		}
3547 		else
3548 		{
3549 			pLine = new fp_Line(getSectionLayout());
3550 			fp_Line* pOldLastLine = static_cast<fp_Line *>(getLastContainer());
3551 
3552 			if(pOldLastLine == NULL)
3553 			{
3554 				setFirstContainer(pLine);
3555 				setLastContainer(pLine);
3556 				pLine->setBlock(this);
3557    				m_pVertContainer->insertConAt(pLine,m_iLinePosInContainer);
3558 				m_iLinePosInContainer++;
3559 	   			pLine->setContainer(m_pVertContainer);
3560 				xxx_UT_DEBUGMSG(("Max width 2 set to %d \n",iMinWidth));
3561 				pLine->setMaxWidth(iMinWidth);
3562 				pLine->setX(iMinLeft-xoff);
3563 				pLine->setSameYAsPrevious(false);
3564 				pLine->setWrapped((iMaxW != iMinWidth));
3565 				m_bSameYAsPrevious = true;
3566 			}
3567 			else
3568 			{
3569 				pLine->setPrev(getLastContainer());
3570 				getLastContainer()->setNext(pLine);
3571 				setLastContainer(pLine);
3572 
3573 				fp_VerticalContainer * pContainer = static_cast<fp_VerticalContainer *>(pOldLastLine->getContainer());
3574 				pLine->setWrapped((iMaxW != iMinWidth));
3575 				pLine->setBlock(this);
3576 				if(pContainer)
3577 				{
3578    					pContainer->insertContainerAfter(static_cast<fp_Container *>(pLine), static_cast<fp_Container *>(pOldLastLine));
3579 					m_iLinePosInContainer = pContainer->findCon(pLine)+1;
3580 					pLine->setContainer(pContainer);
3581 				}
3582 				xxx_UT_DEBUGMSG(("Max width 3 set to %d \n",iMinWidth));
3583 				pLine->setMaxWidth(iMinWidth);
3584 				pLine->setX(iMinLeft-xoff);
3585 				pLine->setSameYAsPrevious(m_bSameYAsPrevious);
3586 				m_bSameYAsPrevious = true;
3587 			}
3588 			xxx_UT_DEBUGMSG(("-1- New line %x has X %d Max width %d wrapped %d sameY %d \n",pLine,pLine->getX(),pLine->getMaxWidth(),pLine->isWrapped(),pLine->isSameYAsPrevious()));
3589 			pLine->setHeight(iHeight);
3590 #if DEBUG
3591 			if(getFirstContainer())
3592 			{
3593 				UT_ASSERT(getFirstContainer()->getPrev() == NULL);
3594 			}
3595 #endif
3596 			UT_ASSERT(findLineInBlock(pLine) >= 0);
3597 			pPrevLine->setAdditionalMargin(m_iAdditionalMarginAfter);
3598 			return pLine;
3599 		}
3600 	}
3601 	bool bStop = false;
3602 	while(!bStop)
3603     {
3604 		getLeftRightForWrapping(iX, iHeight,iMinLeft,iMinRight,iMinWidth);
3605 		fp_Line* pOldLastLine = static_cast<fp_Line *>(getLastContainer());
3606 		if(iMinWidth >  getMinWrapWidth())
3607 		{
3608 			fp_Line* pLine2 = new fp_Line(getSectionLayout());
3609 			if(pOldLastLine == NULL)
3610 			{
3611 				xxx_UT_DEBUGMSG(("Old Lastline NULL?????? \n"));
3612 				setFirstContainer(pLine2);
3613 				setLastContainer(pLine2);
3614 				pLine2->setBlock(this);
3615    				m_pVertContainer->insertConAt(pLine2,m_iLinePosInContainer);
3616 				m_iLinePosInContainer++;
3617 				pLine2->setContainer(m_pVertContainer);
3618 				xxx_UT_DEBUGMSG(("Max width 4 set to %d \n",iMinWidth));
3619 				pLine2->setMaxWidth(iMinWidth);
3620 				pLine2->setX(iMinLeft-xoff);
3621 				pLine2->setSameYAsPrevious(false);
3622 				pLine2->setWrapped((iMaxW != iMinWidth));
3623 				m_bSameYAsPrevious = true;
3624 			}
3625 			else
3626 		    {
3627 				pLine2->setPrev(getLastContainer());
3628 				getLastContainer()->setNext(pLine2);
3629 				setLastContainer(pLine2);
3630 
3631 				fp_VerticalContainer * pContainer = static_cast<fp_VerticalContainer *>(pOldLastLine->getContainer());
3632 				pLine2->setWrapped((iMaxW != iMinWidth));
3633 				pLine2->setBlock(this);
3634 				if(pContainer)
3635 				{
3636 			   		pContainer->insertContainerAfter(static_cast<fp_Container *>(pLine2), static_cast<fp_Container *>(pOldLastLine));
3637 					m_iLinePosInContainer = pContainer->findCon(pLine2)+1;
3638 					pLine2->setContainer(pContainer);
3639 				}
3640 				xxx_UT_DEBUGMSG(("Max width 5 set to %d \n",iMinWidth));
3641 				pLine2->setMaxWidth(iMinWidth);
3642 				pLine2->setX(iMinLeft-xoff);
3643 				pLine2->setSameYAsPrevious(m_bSameYAsPrevious);
3644 				m_bSameYAsPrevious = true;
3645 			}
3646 			xxx_UT_DEBUGMSG(("-2- New line %x has X %d Max width %d wrapped %d sameY %d \n",pLine2,pLine2->getX(),pLine2->getMaxWidth(),pLine2->isWrapped(),pLine2->isSameYAsPrevious()));
3647 			pLine2->setHeight(iHeight);
3648 			UT_ASSERT(findLineInBlock(pLine2) >= 0);
3649 			pPrevLine->setAdditionalMargin(m_iAdditionalMarginAfter);
3650 			return pLine2;
3651 		}
3652 		xxx_UT_DEBUGMSG(("Max width 6 set to %d \n",20));
3653 #if 0
3654 		pLine->setMaxWidth(61);
3655 		pLine->setX(iMinLeft-xoff);
3656 		pLine->setBlock(this);
3657 		pLine->setSameYAsPrevious(false);
3658 		pLine->setWrapped((iMaxW != iMinWidth));
3659 		pOldLastLine = static_cast<fp_Line *>(getLastContainer());
3660 		if(pOldLastLine)
3661 		{
3662 			pLine->setPrev(getLastContainer());
3663 			getLastContainer()->setNext(pLine);
3664 			setLastContainer(pLine);
3665 			fp_VerticalContainer * pContainer = static_cast<fp_VerticalContainer *>(pOldLastLine->getContainer());
3666 			if(pContainer)
3667 			{
3668    				pContainer->insertContainerAfter(static_cast<fp_Container *>(pLine), static_cast<fp_Container *>(pOldLastLine));
3669 				m_iLinePosInContainer = pContainer->findCon(pLine)+1;
3670 				pLine->setContainer(pContainer);
3671 			}
3672 		}
3673 		else
3674 		{
3675 			setFirstContainer(pLine);
3676 			setLastContainer(pLine);
3677    			m_pVertContainer->insertConAt(pLine,m_iLinePosInContainer);
3678 			m_iLinePosInContainer++;
3679 			pLine->setContainer(m_pVertContainer);
3680 		}
3681 #endif
3682 		m_bSameYAsPrevious = false;
3683 		delete pLine;
3684 		iX = getLeftMargin();
3685 		m_iAccumulatedHeight += iHeight;
3686 		m_iAdditionalMarginAfter += iHeight;
3687 	}
3688 	xxx_UT_DEBUGMSG(("-3- New line %x has X %d Max width %d wrapped %d sameY %d \n",pLine,pLine->getX(),pLine->getMaxWidth(),pLine->isWrapped(),pLine->isSameYAsPrevious()));
3689 	pLine->setHeight(iHeight);
3690 #if DEBUG
3691 	if(getFirstContainer())
3692 	{
3693 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
3694 	}
3695 #endif
3696 	UT_ASSERT(findLineInBlock(pLine) >= 0);
3697 	pPrevLine->setAdditionalMargin(m_iAdditionalMarginAfter);
3698 	return pLine;
3699 }
3700 
3701 
formatAll(void)3702 void fl_BlockLayout::formatAll(void)
3703 {
3704 	m_iNeedsReformat = 0;
3705 	format();
3706 }
3707 
3708 /*!
3709   Format paragraph: split the content into lines which
3710   will fit in the container.  */
format()3711 void fl_BlockLayout::format()
3712 {
3713 	if((isHidden() >= FP_HIDDEN_FOLDED) || (m_pSectionLayout->isHidden() >= FP_HIDDEN_FOLDED))
3714 	{
3715 		xxx_UT_DEBUGMSG(("Don't format coz I'm hidden! \n"));
3716 		return;
3717 	}
3718 #if 0
3719 	if(m_pLayout->isLayoutFilling())
3720 	{
3721 		if(!m_bIsTOC)
3722 		{
3723 			if(!isNotTOCable())
3724 			{
3725 				m_bStyleInTOC = m_pLayout->addOrRemoveBlockFromTOC(this);
3726 			}
3727 		}
3728 	}
3729 #endif
3730 	bool bJustifyStuff = false;
3731 	xxx_UT_DEBUGMSG(("Format block %x needsreformat %d m_pFirstRun %x \n",this,m_iNeedsReformat,m_pFirstRun));
3732 	fl_ContainerLayout * pCL2 = myContainingLayout();
3733 	while(pCL2 && (pCL2->getContainerType() != FL_CONTAINER_DOCSECTION) && (pCL2->getContainerType() != FL_CONTAINER_SHADOW))
3734 	{
3735 		pCL2 = pCL2->myContainingLayout();
3736 	}
3737 	bool isInShadow = false;
3738 	if(pCL2 && (pCL2->getContainerType() == FL_CONTAINER_SHADOW))
3739 	{
3740 		xxx_UT_DEBUGMSG(("Formatting a block in a shadow \n"));
3741 		xxx_UT_DEBUGMSG(("m_pSectionLayout Type is %d \n",m_pSectionLayout->getContainerType()));
3742 		isInShadow = true;
3743 	}
3744 	//
3745 	// If block hasn't changed don't format it.
3746 	//
3747 	if((m_iNeedsReformat == -1) && !m_bIsCollapsed)
3748 	{
3749 		return;
3750 	}
3751 	//
3752 	// Should be ablke to get away with just formatting from the first line
3753 	// containing m_iNeedsReformat
3754 	//
3755 	if(m_pAlignment && m_pAlignment->getType() == FB_ALIGNMENT_JUSTIFY)
3756 	{
3757 		m_iNeedsReformat = 0;
3758 		bJustifyStuff = true;
3759 	}
3760 
3761 	//
3762 	// Save the old height of the block. We compare to the new height after
3763 	// the format.
3764 	//
3765 	UT_sint32 iOldHeight = getHeightOfBlock();
3766 	xxx_UT_DEBUGMSG(("Old Height of block %d \n",iOldHeight));
3767 	//
3768 	// Need this to find where to break section in the document.
3769 	//
3770 	fp_Page * pPrevP = NULL;
3771 	//
3772 	// Sevior says...
3773 	// Two choices of code here. "1" is mroe agressive and less likely
3774 	// to lead to infinite loops.
3775 	// On the down side it appears to cause pages with difficult to wrap
3776 	// sets of images to bump all content off the page.
3777 	//
3778 	// If think the latter is fixable elsewhere so will try for the "1"
3779 	// branch for now
3780 #if 1
3781 	fp_Container * pPrevCon = getFirstContainer();
3782 	if(pPrevCon)
3783 	{
3784 		pPrevP = pPrevCon->getPage();
3785 	}
3786 	else
3787 	{
3788 		fl_BlockLayout *pPrevB = getPrevBlockInDocument();
3789 		while(pPrevB)
3790 		{
3791 			pPrevCon = pPrevB->getFirstContainer();
3792 			if(pPrevCon)
3793 			{
3794 				pPrevP = pPrevCon->getPage();
3795 				break;
3796 			}
3797 			pPrevB = pPrevB->getPrevBlockInDocument();
3798 		}
3799 	}
3800 #else
3801 	fl_ContainerLayout * pPrevCL = getPrev();
3802 	while(pPrevCL && pPrevCL->getContainerType() != FL_CONTAINER_BLOCK)
3803 	{
3804 		pPrevCL = pPrevCL->getPrev();
3805 	}
3806 	if(pPrevCL)
3807 	{
3808 		fp_Container * pPrevCon = pPrevCL->getFirstContainer();
3809 		if(pPrevCon)
3810 		{
3811 			pPrevP = pPrevCon->getPage();
3812 		}
3813 	}
3814 #endif
3815 	xxx_UT_DEBUGMSG(("fl_BlockLayout - format \n"));
3816 	_assertRunListIntegrity();
3817 	fp_Run *pRunToStartAt = NULL;
3818 
3819 	// TODO -- is this really needed?
3820 	// is should not be, since _lookupProperties is explicitely called
3821 	// by our listeners when the format changes
3822 	// please do not uncomment this as a quick bugfix to some other
3823 	// problem, and if you do uncomment it, please explain why - Tomas
3824 	// lookupProperties();
3825 
3826 	// Some fields like clock, character count etc need to be constantly updated
3827 	// This is best done in the background updater which examines every block
3828 	// in the document. To save scanning every run in the entire document we
3829 	// set a bool in blocks with these sort of fields.
3830 	//
3831 	setUpdatableField(false);
3832 	//
3833 	// Save old line widths
3834 	//
3835 	UT_GenericVector<UT_sint32> vecOldLineWidths;
3836 	xxx_UT_DEBUGMSG(("formatBlock 3: pPage %x \n",pPrevP));
3837 	if (m_pFirstRun)
3838 	{
3839 		if(m_iNeedsReformat > 0)
3840 		{
3841 			// only a part of this block need reformat, find the run that
3842 			// contains this offset
3843 			fp_Run * pR = m_pFirstRun;
3844 			while(pR && (pR->getBlockOffset() + pR->getLength()) <= static_cast<UT_uint32>(m_iNeedsReformat))
3845 				pR = pR->getNextRun();
3846 
3847 			UT_ASSERT( pR );
3848 			pRunToStartAt = pR;
3849 		}
3850 		else
3851 			pRunToStartAt = m_pFirstRun;
3852 
3853 
3854 		//
3855 		// Reset justification before we recalc width of runs
3856 		//
3857 		fp_Run* pRun = m_pFirstRun;
3858 		//
3859 		// Save old X position and width
3860 		//
3861 		fp_Line * pOldLine = NULL;
3862 		while(pRun)
3863 		{
3864 			if(pOldLine != pRun->getLine())
3865 			{
3866 				pOldLine = pRun->getLine();
3867 				if(pOldLine)
3868 				{
3869 					vecOldLineWidths.addItem(pOldLine->getWidth());
3870 				}
3871 			}
3872 			if(pRun->getLine())
3873 			{
3874 				pRun->setTmpX(pRun->getX());
3875 				pRun->setTmpY(pRun->getY());
3876 				pRun->setTmpWidth(pRun->getWidth());
3877 				pRun->setTmpLine(pRun->getLine());
3878 			}
3879 			else
3880 			{
3881 				pRun->setTmpX(0);
3882 				pRun->setTmpY(0);
3883 				pRun->setTmpWidth(0);
3884 				pRun->setTmpLine(NULL);
3885 			}
3886 			pRun = pRun->getNextRun();
3887 		}
3888 
3889 		fp_Line* pLine2 = static_cast<fp_Line *>(getFirstContainer());
3890 		while(pLine2 && 	bJustifyStuff)
3891 		{
3892 			pLine2->resetJustification(!bJustifyStuff); // temporary reset
3893 			pLine2 = static_cast<fp_Line *>(pLine2->getNext());
3894 		}
3895 
3896 		// Recalculate widths of Runs if necessary.
3897 		bool bDoit = false; // was false. Same kludge from
3898 		pRun = m_pFirstRun;
3899 		// sevior. Kludge very expensive,
3900 		// proper fix required. Tomas
3901 
3902 		while (pRun)
3903 		{
3904 			if(pRun->getType() == FPRUN_FIELD)
3905 			{
3906 				fp_FieldRun * pFRun = static_cast<fp_FieldRun *>( pRun);
3907 				if(pFRun->needsFrequentUpdates())
3908 				{
3909 					setUpdatableField(true);
3910 				}
3911 			}
3912 			if(pRun->getType() == FPRUN_HYPERLINK)
3913 			{
3914 				fp_HyperlinkRun * pHRun = static_cast<fp_HyperlinkRun *>(pRun);
3915 				if(pHRun->getHyperlinkType() == HYPERLINK_ANNOTATION)
3916 				{
3917 					setUpdatableField(true);
3918 				}
3919 				if(pHRun->getHyperlinkType() == HYPERLINK_RDFANCHOR)
3920 				{
3921 					setUpdatableField(true);
3922 				}
3923 			}
3924 			if(pRun == pRunToStartAt)
3925 				bDoit = true;
3926 			if(bJustifyStuff || (bDoit && (pRun->getType() != FPRUN_ENDOFPARAGRAPH)))
3927 			{
3928 				pRun->recalcWidth();
3929 				xxx_UT_DEBUGMSG(("Run %x has width %d \n",pRun,pRun->getWidth()));
3930 			}
3931 			if(pRun->getType() == FPRUN_ENDOFPARAGRAPH)
3932 			{
3933 				pRun->lookupProperties();
3934 			}
3935 			pRun = pRun->getNextRun();
3936 		}
3937 
3938 		// Create the first line if necessary.
3939 		if (!getFirstContainer())
3940 		{
3941 			collapse(); // remove all old content
3942 			_stuffAllRunsOnALine();
3943 			fp_Line * pLine = static_cast<fp_Line *>(getFirstContainer());
3944 			pLine->resetJustification(true);
3945 		}
3946 		recalculateFields(0);
3947 
3948 		// Reformat paragraph
3949 		m_Breaker.breakParagraph(this, NULL,NULL);
3950 	}
3951 	else
3952 	{
3953 		UT_DEBUGMSG(("NO block content. Insert an EOP \n"));
3954 		// No paragraph content. Just insert the EOP Run.
3955 		_removeAllEmptyLines();
3956 		_insertEndOfParagraphRun();
3957 	}
3958 
3959 	if ((m_pAutoNum && isListLabelInBlock() == true)
3960 		&& (m_bListLabelCreated == false))
3961 	{
3962 		m_bListLabelCreated =true;
3963 	}
3964 	_assertRunListIntegrity();
3965 	//
3966 	// Now coalesceRuns
3967 	//
3968 	coalesceRuns();
3969 	if(!bJustifyStuff && m_pAlignment && (m_pAlignment->getType() != FB_ALIGNMENT_LEFT))
3970 	{
3971 		//
3972 		// If the width of the line changes for center or right justification
3973 		// we need to clear the whole line.
3974 		//
3975 		fp_Line * pLine =  static_cast<fp_Line *>(getFirstContainer());
3976 		UT_sint32 iCurLine = 0;
3977 		while(pLine && (pLine->getContainerType() == FP_CONTAINER_LINE) && (vecOldLineWidths.getItemCount() > 0))
3978 		{
3979 			UT_sint32 iOldWidth = vecOldLineWidths.getNthItem(iCurLine);
3980 			pLine->calculateWidthOfLine();
3981 			if(iOldWidth != pLine->getWidth())
3982 			{
3983 				pLine->clearScreen();
3984 			}
3985 			pLine = static_cast<fp_Line *>(pLine->getNext());
3986 			iCurLine++;
3987 			if(iCurLine >= vecOldLineWidths.getItemCount())
3988 				break;
3989 		}
3990 	}
3991 	fp_Line* pLastLine = static_cast<fp_Line *>(getLastContainer());
3992 	if(pLastLine && pLastLine->getContainerType() == FP_CONTAINER_LINE)
3993 	{
3994 		if(bJustifyStuff)
3995 		{
3996 			pLastLine->resetJustification(bJustifyStuff); // permanent reset
3997 			pLastLine->layout();
3998 		}
3999 	}
4000 	fp_Run * pRun = m_pFirstRun;
4001 	//
4002 	// Compare old positions and width. Clear those that don't match.
4003 	//
4004 	while(pRun)
4005 	{
4006 		pRun->clearIfNeeded();
4007 		pRun = pRun->getNextRun();
4008 	}
4009 
4010 #if 0
4011 	// we need to coalesce runs *before* we do justification (coalescing might require
4012 	// that the whole run is reshaped, and that can lead to loss of the justification
4013 	// information for the run).
4014 
4015     	// was previously after breakParagraph. Idea is to make this a less
4016 		// frequent occurance. So the paragraph get's lines coalessed
4017         // whenever the height changes. So we don't do this on every key press
4018         // but on average the paragraph gets coalessed.
4019 
4020 		// the down-side of this is that on the active line we keep
4021 		// spliting/merging if the editing position is not at either
4022 		// end; the up-side is that at any given time our document
4023 		// is represented by the minimal number of runs necessary,
4024 		// which not only means that we use less memory, but more
4025 		// importantly, we draw faster since any line with uniform
4026 		// formatting is drawn by a single call to OS text drawing
4027 		// routine
4028 		coalesceRuns();
4029 #endif
4030 
4031 	m_bIsCollapsed = false;
4032 	xxx_UT_DEBUGMSG(("Block Uncollapsed in format \n"));
4033 	//
4034 	// Only break section if the height of the block changes.
4035 	//
4036 	UT_sint32 iNewHeight = 0;
4037 	if (!m_bForceSectionBreak)
4038 	{
4039 		iNewHeight = getHeightOfBlock();
4040 	}
4041 	xxx_UT_DEBUGMSG(("New height of block %d \n",iNewHeight));
4042 	if((m_bForceSectionBreak) || (iOldHeight != iNewHeight))
4043 	{
4044 		m_bForceSectionBreak = false;
4045 		if(getSectionLayout()->getContainerType() != FL_CONTAINER_DOCSECTION)
4046 		{
4047 			getSectionLayout()->setNeedsReformat(this);
4048 			if(getSectionLayout()->getContainerType() == FL_CONTAINER_CELL)
4049 			{
4050 				//
4051 				// Can speed up things by doing an immediate format on the
4052 				// the cell.
4053 				//
4054 				fl_CellLayout * pCL = static_cast<fl_CellLayout *>(getSectionLayout());
4055 				if(!pCL->isDoingFormat())
4056 				{
4057 					getSectionLayout()->format();
4058 				}
4059 			}
4060 		}
4061 		if(!isInShadow &&  (getSectionLayout()->getContainerType() != FL_CONTAINER_FRAME))
4062 		{
4063 			getDocSectionLayout()->setNeedsSectionBreak(true,pPrevP);
4064 		}
4065 	}
4066 
4067 	// Paragraph has been reformatted.
4068 	if(!m_pLayout->isLayoutFilling())
4069     {
4070 		m_iNeedsReformat = -1;
4071 		getDocSectionLayout()->clearNeedsReformat(this);
4072 	}
4073 	else
4074 	{
4075 		m_iNeedsReformat = 0;
4076 	}
4077 	return;	// TODO return code
4078 }
4079 
findLineInBlock(fp_Line * pLine) const4080 UT_sint32 fl_BlockLayout::findLineInBlock(fp_Line * pLine) const
4081 {
4082 	fp_Line * pTmpLine = static_cast<fp_Line *>(getFirstContainer());
4083 	UT_sint32 i = 0;
4084 	while(pTmpLine && pTmpLine != pLine)
4085 	{
4086 		i++;
4087 		pTmpLine = static_cast<fp_Line *>(pTmpLine->getNext());
4088 	}
4089 	if(pTmpLine == NULL)
4090 	{
4091 		return -1;
4092 	}
4093 	return i;
4094 }
4095 
markAllRunsDirty(void)4096 void fl_BlockLayout::markAllRunsDirty(void)
4097 {
4098 	fp_Run * pRun = m_pFirstRun;
4099 	while(pRun)
4100 	{
4101 		pRun->markAsDirty();
4102 		pRun = pRun->getNextRun();
4103 	}
4104 	fp_Line  * pLine = static_cast<fp_Line *>(getFirstContainer());
4105 	while(pLine)
4106 	{
4107 		pLine->setNeedsRedraw();
4108 		pLine = static_cast<fp_Line *>(pLine->getNext());
4109 	}
4110 }
4111 
redrawUpdate()4112 void fl_BlockLayout::redrawUpdate()
4113 {
4114 //
4115 // This can happen from the new deleteStrux code
4116 //
4117 	bool bFirstLineOn = false;
4118 	bool bLineOff = false;
4119 
4120 	xxx_UT_DEBUGMSG(("redrawUpdate Called \n"));
4121 	// TODO -- is this really needed ??
4122 	// we should not need to lookup properties on redraw,
4123 	// lookupProperties() gets explicitely called by our listeners
4124 	// when format changes.
4125 	// please do not uncomment this as a quick bugfix to some other
4126 	// problem, and if you do uncomment it, please explain why - Tomas
4127 	// lookupProperties();
4128 	if(isHdrFtr())
4129 		return;
4130 
4131 	if(needsReformat())
4132 	{
4133 		xxx_UT_DEBUGMSG(("redrawUpdate Called doing format \n"));
4134 		format();
4135 		if(m_pAlignment && m_pAlignment->getType() == FB_ALIGNMENT_JUSTIFY)
4136 		{
4137 			markAllRunsDirty();
4138 			fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
4139 			while (pLine)
4140 			{
4141 				xxx_UT_DEBUGMSG(("Drawing line in redraw update after format %x \n",pLine));
4142 				pLine->draw(m_pFirstRun->getGraphics());
4143 				pLine = static_cast<fp_Line *>(pLine->getNext());
4144 			}
4145 			m_bNeedsRedraw = false;
4146 			return;
4147 		}
4148 	}
4149 
4150 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
4151 	while (pLine)
4152 	{
4153 		if (pLine->needsRedraw())
4154 		{
4155 			bLineOff = pLine->redrawUpdate();
4156 			bFirstLineOn |= bLineOff;
4157 		}
4158 
4159 		if(bFirstLineOn && !bLineOff)
4160 		{
4161 			// we are past all visible lines
4162 			break;
4163 		}
4164 
4165 		pLine = static_cast<fp_Line *>(pLine->getNext());
4166 	}
4167 
4168 	m_bNeedsRedraw = false;
4169 
4170 	//	lookupProperties();
4171 }
4172 
getNewContainer(fp_Container *)4173 fp_Container* fl_BlockLayout::getNewContainer(fp_Container * /* pCon*/)
4174 {
4175 	fp_Line* pLine = new fp_Line(getSectionLayout());
4176 	// TODO: Handle out-of-memory
4177 	UT_ASSERT(pLine);
4178 	fp_TableContainer * pPrevTable = NULL;
4179 	fp_TOCContainer * pPrevTOC = NULL;
4180 	pLine->setBlock(this);
4181 	pLine->setNext(NULL);
4182 	fp_VerticalContainer* pContainer = NULL;
4183 	if (getLastContainer())
4184 	{
4185 		fp_Line* pOldLastLine = static_cast<fp_Line *>(getLastContainer());
4186 
4187 		UT_ASSERT(getFirstContainer());
4188 		UT_ASSERT(!getLastContainer()->getNext());
4189 
4190 		pLine->setPrev(getLastContainer());
4191 		getLastContainer()->setNext(pLine);
4192 		setLastContainer(pLine);
4193 
4194 		pContainer = static_cast<fp_VerticalContainer *>(pOldLastLine->getContainer());
4195 		pContainer->insertContainerAfter(static_cast<fp_Container *>(pLine), static_cast<fp_Container *>(pOldLastLine));
4196 	}
4197 	else
4198 	{
4199 		UT_ASSERT(!getFirstContainer());
4200 		setFirstContainer(pLine);
4201 		setLastContainer(getFirstContainer());
4202 		pLine->setPrev(NULL);
4203 
4204 		fp_Line* pPrevLine = NULL;
4205 		if(getPrev())
4206 		{
4207 			if(getPrev()->getLastContainer() == NULL)
4208 			{
4209 				// Previous block exists but doesn't have a last line.
4210 				// This is a BUG. Try a work around for now. TODO Fix this elsewhere
4211 				UT_DEBUGMSG(("BUG!!! Previous block exists with no last line. This should not happen \n"));
4212 				//	getPrev()->format();
4213 			}
4214 		}
4215 		if (getPrev() && getPrev()->getLastContainer())
4216 		{
4217 			fp_Container * pPrevCon = static_cast<fp_Container *>(getPrev()->getLastContainer());
4218 			if(pPrevCon->getContainerType() == FP_CONTAINER_LINE)
4219 			{
4220 				pContainer = static_cast<fp_VerticalContainer *>(pPrevCon->getContainer());
4221 				pPrevLine = static_cast<fp_Line *>(pPrevCon);
4222 				UT_ASSERT(pContainer);
4223 				UT_ASSERT(pContainer->getWidth() >0);
4224 			}
4225 			else
4226 			{
4227 				fp_Container * ppPrev = static_cast<fp_Container *>(pPrevCon);
4228 				if(ppPrev && ((ppPrev->getContainerType() == FP_CONTAINER_ENDNOTE) || (ppPrev->getContainerType() == FP_CONTAINER_FOOTNOTE)  || (ppPrev->getContainerType() == FP_CONTAINER_ANNOTATION) || (ppPrev->getContainerType() == FP_CONTAINER_FRAME) ))
4229 				{
4230 					fl_ContainerLayout * pCL = static_cast<fl_ContainerLayout *>(ppPrev->getSectionLayout());
4231 					while(pCL && ((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION)|| (pCL->getContainerType() == FL_CONTAINER_FRAME)))
4232 					{
4233 						pCL = pCL->getPrev();
4234 					}
4235 					if(pCL)
4236 					{
4237 						ppPrev = pCL->getLastContainer();
4238 					}
4239 					else
4240 					{
4241 						ppPrev = NULL;
4242 					}
4243 				}
4244 				if(ppPrev && (ppPrev->getContainerType() == FP_CONTAINER_LINE))
4245 				{
4246 					pPrevLine = static_cast<fp_Line *>(ppPrev);
4247 					pContainer = static_cast<fp_VerticalContainer *>(pPrevLine->getContainer());
4248 					UT_ASSERT(pContainer);
4249 					UT_ASSERT(pContainer->getWidth() >0);
4250 				}
4251 				else if(ppPrev && (ppPrev->getContainerType() == FP_CONTAINER_TABLE))
4252 				{
4253 					pContainer = (fp_VerticalContainer *) ppPrev->getContainer();
4254 					pPrevLine = NULL;
4255 					pPrevTable = (fp_TableContainer*)ppPrev;
4256 				}
4257 				else if(ppPrev && (ppPrev->getContainerType() == FP_CONTAINER_TOC))
4258 				{
4259 					pContainer = (fp_VerticalContainer *) ppPrev->getContainer();
4260 					pPrevLine = NULL;
4261 					pPrevTOC = (fp_TOCContainer*)ppPrev;
4262 				}
4263 				else
4264 				{
4265 					pPrevLine = NULL;
4266 					pContainer = NULL;
4267 				}
4268 			}
4269 		}
4270 		else
4271 		{
4272 			//
4273 			// Skip any footnotes or endnotes
4274 			//
4275 			fl_ContainerLayout * pCL = getNext();
4276 			while(pCL && ((pCL->getContainerType() == FL_CONTAINER_ENDNOTE)
4277 						  || (pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
4278 						  || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION))
4279 				  )
4280 			{
4281 				pCL = pCL->getNext();
4282 			}
4283 			if (pCL && pCL->getFirstContainer() && pCL->getFirstContainer()->getContainer())
4284 			{
4285 				pContainer = static_cast<fp_VerticalContainer *>(pCL->getFirstContainer()->getContainer());
4286 				UT_return_val_if_fail(pContainer, NULL);
4287 				UT_ASSERT_HARMLESS(pContainer->getWidth() >0);
4288 			}
4289 			else if (myContainingLayout()->getFirstContainer())
4290 			{
4291 			// TODO assert something here about what's in that container
4292 				pContainer = static_cast<fp_VerticalContainer *>(myContainingLayout()->getFirstContainer());
4293 				UT_return_val_if_fail(pContainer, NULL);
4294 				UT_ASSERT_HARMLESS(pContainer->getWidth() >0);
4295 			}
4296 			else
4297 			{
4298 				pContainer = static_cast<fp_VerticalContainer *>(myContainingLayout()->getNewContainer());
4299 				UT_return_val_if_fail(pContainer, NULL);
4300 				UT_ASSERT_HARMLESS(pContainer->getWidth() >0);
4301 			}
4302 		}
4303 		if(pContainer == NULL)
4304 		{
4305 			pContainer = static_cast<fp_VerticalContainer *>(m_pSectionLayout->getNewContainer());
4306 			UT_return_val_if_fail(pContainer, NULL);
4307 			UT_ASSERT_HARMLESS(pContainer->getWidth() >0);
4308 		}
4309 
4310 		if ((pPrevLine==NULL) && (pPrevTable== NULL) && (pPrevTOC == NULL))
4311 		{
4312 			pContainer->insertContainer(static_cast<fp_Container *>(pLine));
4313 		}
4314 		else if((pPrevLine==NULL) &&(NULL!=pPrevTable))
4315 		{
4316 			pContainer->insertContainerAfter((fp_Container *)pLine, (fp_Container *) pPrevTable);
4317 		}
4318 		else if((pPrevLine==NULL) &&(NULL!=pPrevTOC))
4319 		{
4320 			pContainer->insertContainerAfter((fp_Container *)pLine, (fp_Container *) pPrevTOC);
4321 		}
4322 		else
4323 		{
4324 			pContainer->insertContainerAfter(static_cast<fp_Container *>(pLine), static_cast<fp_Container *>(pPrevLine));
4325 		}
4326 	}
4327 	UT_ASSERT(pLine->getContainer());
4328 #if DEBUG
4329 	if(getFirstContainer())
4330 	{
4331 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
4332 	}
4333 #endif
4334 	UT_ASSERT(findLineInBlock(pLine) >= 0);
4335 	pLine->recalcMaxWidth(true);
4336 	return static_cast<fp_Container *>(pLine);
4337 }
4338 
setNeedsReformat(fl_ContainerLayout * pCL,UT_uint32 offset)4339 void fl_BlockLayout::setNeedsReformat(fl_ContainerLayout * pCL,UT_uint32 offset)
4340 {
4341 	// _lesser_ value is the one that matter here, Tomas, Nov 28, 2003
4342 	if(m_iNeedsReformat < 0 || static_cast<UT_sint32>(offset) < m_iNeedsReformat)
4343 		m_iNeedsReformat = offset;
4344   	getSectionLayout()->setNeedsReformat(pCL);
4345 	setNeedsRedraw();
4346 }
4347 
clearPrint(void) const4348 void fl_BlockLayout::clearPrint(void) const
4349 {
4350 	fp_Run * pRun = getFirstRun();
4351 	while(pRun)
4352 	{
4353 		pRun->clearPrint();
4354 		pRun = pRun->getNextRun();
4355 	}
4356 }
4357 
setNeedsRedraw(void)4358 void fl_BlockLayout::setNeedsRedraw(void)
4359 {
4360 	m_bNeedsRedraw = true;
4361 	getSectionLayout()->setNeedsRedraw();
4362 }
4363 
getProperty(const gchar * pszName,bool bExpandStyles) const4364 const char* fl_BlockLayout::getProperty(const gchar * pszName, bool bExpandStyles) const
4365 {
4366 	const PP_AttrProp * pSpanAP = NULL;
4367 	const PP_AttrProp * pBlockAP = NULL;
4368 	const PP_AttrProp * pSectionAP = NULL;
4369 
4370 	getAP(pBlockAP);
4371 
4372 	// at the moment this is only needed in the bidi build, where dom-dir property
4373 	// can be inherited from the section; however, it the future this might need to
4374 	// be added for the normal build too.
4375 	m_pSectionLayout->getAP(pSectionAP);
4376 
4377 	return PP_evalProperty(pszName,pSpanAP,pBlockAP,pSectionAP,m_pDoc,bExpandStyles);
4378 }
4379 
4380 /*!
4381  * This method returns the length of the Block, including the initial strux.
4382  * so if "i" is the position of the block strux, i+getLength() will be the
4383  * position of the strux (whatever it might be), following this block.
4384  * The length includes any embedded struxes (like footnotes and endnotes).
4385  */
getLength() const4386 UT_sint32 fl_BlockLayout::getLength() const
4387 {
4388 	PT_DocPosition posThis = getPosition(true);
4389 	pf_Frag_Strux* nextSDH =NULL;
4390 	m_pDoc->getNextStrux(getStruxDocHandle(),&nextSDH);
4391 	if(nextSDH == NULL)
4392 	{
4393 		//
4394 		// Here if we reach EOD.
4395 		//
4396 		PT_DocPosition docEnd;
4397 		m_pDoc->getBounds(true, docEnd);
4398 		UT_sint32 length = static_cast<UT_sint32>(docEnd) - static_cast<UT_sint32>(posThis);
4399 		return length;
4400 	}
4401 	PT_DocPosition posNext = m_pDoc->getStruxPosition(nextSDH);
4402 	//
4403 	// OK Look to see if we've got a TOC/ENDTOC at the end. If so subtract
4404 	// it's length.
4405 	//
4406 	pf_Frag * pf = m_pDoc->getFragFromPosition(posNext-1);
4407 	if(pf->getType() == pf_Frag::PFT_Strux)
4408 	{
4409 		pf_Frag_Strux * pfsTemp = static_cast<pf_Frag_Strux *>(pf);
4410 		if (pfsTemp->getStruxType() == PTX_EndTOC)	// did we find it
4411 		{
4412 			posNext -= 2;
4413 		}
4414 	}
4415 
4416 	UT_sint32 length = static_cast<UT_sint32>(posNext) - static_cast<UT_sint32>(posThis);
4417 	return length;
4418 }
4419 
getPropertyType(const gchar * pszName,tProperty_type Type,bool bExpandStyles) const4420 const PP_PropertyType * fl_BlockLayout::getPropertyType(const gchar * pszName, tProperty_type Type, bool bExpandStyles) const
4421 {
4422 	const PP_AttrProp * pSpanAP = NULL;
4423 	const PP_AttrProp * pBlockAP = NULL;
4424 	const PP_AttrProp * pSectionAP = NULL;
4425 
4426 	getAP(pBlockAP);
4427 
4428 	return PP_evalPropertyType(pszName,pSpanAP,pBlockAP,pSectionAP,Type,m_pDoc,bExpandStyles);
4429 }
4430 
4431 /*!
4432  Get block's position in document
4433  \param bActualBlockPos When true return block's position. When false
4434 						return position of first run in block
4435  \return Position of block (or first run in block)
4436  \fixme Split in two functions if called most often with FALSE
4437 */
getPosition(bool bActualBlockPos) const4438 PT_DocPosition fl_BlockLayout::getPosition(bool bActualBlockPos) const
4439 {
4440 	PT_DocPosition pos = m_pDoc->getStruxPosition(getStruxDocHandle());
4441 
4442 	// it's usually more useful to know where the runs start
4443 	if (!bActualBlockPos)
4444 		pos += fl_BLOCK_STRUX_OFFSET;
4445 
4446 	return pos;
4447 }
4448 
getLineSpacing(double & dSpacing,eSpacingPolicy & eSpacing) const4449 void fl_BlockLayout::getLineSpacing(double& dSpacing, eSpacingPolicy& eSpacing) const
4450 {
4451 	dSpacing = m_dLineSpacing;
4452 	eSpacing = m_eSpacingPolicy;
4453 }
4454 
getBlockBuf(UT_GrowBuf * pgb) const4455 bool	fl_BlockLayout::getBlockBuf(UT_GrowBuf * pgb) const
4456 {
4457 	return m_pDoc->getBlockBuf(getStruxDocHandle(), pgb);
4458 }
4459 
4460 
4461 /*!
4462   Compute insertion point (caret) coordinates and size
4463   \param iPos Document position of cursor
4464   \param bEOL Set if EOL position is wanted
4465   \retval x X position (LTR)
4466   \retval y Y position (LTR)
4467   \retval x2 X position (RTL)
4468   \retval y2 Y position (RTL)
4469   \retval height Height of carret
4470   \retval bDirection Editing direction (true = LTR, false = RTL)
4471   \return The Run containing (or next to) the carret, or NULL if the block
4472 		  has no formatting information.
4473   \fixme bDirection should be an enum type
4474 */
4475 fp_Run*
findPointCoords(PT_DocPosition iPos,bool bEOL,UT_sint32 & x,UT_sint32 & y,UT_sint32 & x2,UT_sint32 & y2,UT_sint32 & height,bool & bDirection) const4476 fl_BlockLayout::findPointCoords(PT_DocPosition iPos,
4477 								bool bEOL,
4478 								UT_sint32& x,  UT_sint32& y,
4479 								UT_sint32& x2, UT_sint32& y2,
4480 								UT_sint32& height,
4481 								bool& bDirection) const
4482 {
4483 	if (!getFirstContainer() || !m_pFirstRun)
4484 	{
4485 		// when we have no formatting information, can't find anything
4486 		return NULL;
4487 	}
4488 
4489 	// find the run which has this offset inside it.
4490 	PT_DocPosition dPos = getPosition();
4491 	const UT_uint32 iRelOffset = iPos - dPos;
4492 
4493 	// By default, the Run just before the one we find is the one we
4494 	// want the coords from. This is because insertion is done with
4495 	// the properties of the Run before the point.
4496 	// In some situations, we need to override that and use the coords
4497 	// of the found Run - this flag tells us when to do what.
4498 	bool bCoordOfPrevRun = true;
4499 
4500 	// Some of the special cases below fix up pRun/bCoordOfPrevRun
4501 	// so we can use the first exit point. We could just as well
4502 	// fiddle bEOL in those cases, but using this variable makes
4503 	// the intention more clear (IMO).
4504 	bool bUseFirstExit = false;
4505 
4506 	// Find first Run past (or at) the requested offset. By scanning
4507 	// in this manner, we do a mimimum of computation to find the
4508 	// approximate location.
4509 	fp_Run* pRun = m_pFirstRun;
4510 	while (pRun->getNextRun() && pRun->getBlockOffset() < iRelOffset)
4511 	{
4512 		pRun = pRun->getNextRun();
4513 	}
4514 	// Now scan farther if necessary - the block may contain Runs
4515 	// with zero length. This is only a problem when empty Runs
4516 	// appear for no good reason (i.e., an empty Run on an empty
4517 	// line should be OK).
4518 	//
4519 	// The original test for block offset + len < iRelOffset was no
4520 	// good as that condition is always false by the time we get here.
4521  	// The test would need to be for length == 0
4522 	// however, testing for 0 length makes us skip over fmt marks,
4523 	// which we do not want (I wonder if this is really needed at all)
4524 	while (pRun->getNextRun() && pRun->getLength() == 0 && pRun->getType() != FPRUN_FMTMARK)
4525 	{
4526 		pRun = pRun->getNextRun();
4527 	}
4528 
4529 	// We may have scanned past the last Run in the block. Back up.
4530 	if (!pRun)
4531 	{
4532 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
4533 		pRun = static_cast<fp_Line *>(getLastContainer())->getLastRun();
4534 		bCoordOfPrevRun = false;
4535 	}
4536 
4537 	// Step one back if previous Run holds the offset (the
4538 	// above loops scan past what we're looking for since it's
4539 	// faster).
4540 	fp_Run* pPrevRun = pRun->getPrevRun();
4541 
4542 	if (pPrevRun &&
4543 		pPrevRun->getBlockOffset() + pPrevRun->getLength() > iRelOffset)
4544 	{
4545 		pRun = pPrevRun;
4546 		bCoordOfPrevRun = false;
4547 	}
4548 
4549 	// Since the requested offset may be a page break (or similar
4550 	// Runs) which cannot contain the point, now work backwards
4551 	// while looking for a Run which can contain the point.
4552 	if(pRun && !pRun->canContainPoint())
4553 	{
4554 		fp_Run * pOldRun = pRun;
4555 
4556 		while (pRun && !pRun->canContainPoint())
4557 		{
4558 			pRun = pRun->getPrevRun();
4559 			bCoordOfPrevRun = false;
4560 		}
4561 
4562 		if(!pRun)
4563 		{
4564 			//look the other way
4565 			pRun = pOldRun;
4566 
4567 			while (pRun && !pRun->canContainPoint())
4568 			{
4569 				pRun = pRun->getNextRun();
4570 				bCoordOfPrevRun = false;
4571 			}
4572 		}
4573 	}
4574 
4575 	// Assert if there have been no Runs which can hold the point
4576 	// between the beginning of the block and the requested
4577 	// offset.
4578 	UT_ASSERT(NULL != pRun);
4579 	if (!pRun){
4580 		x = x2 = y = y2 = height = 0;
4581 		return NULL;
4582 	}
4583 
4584 	// This covers a special case (I) when bEOL.  Consider this
4585 	// line (| is the right margin, E end of document):
4586 	//
4587 	// 1:  abcdefgh|
4588 	// 2:  iE
4589 	//
4590 	// When EOL position for display line 1 is requested, it's
4591 	// done with either the offset of h or i (fall through to code
4592 	// below first exit point). EOL position for display line 2 is
4593 	// requested with offset of E (matches third sub-expresion).
4594 	// (This check is rather non-intuitive - step through the code
4595 	// for the different permutations to see why it's correct).
4596 	if (bEOL && pRun->getBlockOffset() < iRelOffset &&
4597 		pRun->getBlockOffset() + pRun->getLength() >= iRelOffset)
4598 	{
4599 		bCoordOfPrevRun = false;
4600 		bUseFirstExit = true;
4601 	}
4602 
4603 	// If not bEOL, we're done: either we have actually found the
4604 	// Run containing the offset, or we have found the first
4605 	// suitable Run before the requested offset.
4606 	//
4607 	// This is the exit point most calls will use (being the first
4608 	// exit point, we should be OK performance wise).
4609 	if (bUseFirstExit || !bEOL)
4610 	{
4611 		if (bCoordOfPrevRun && pRun->letPointPass())
4612 		{
4613 			// This looks a little weird. What it does is first try to
4614 			// go one Run back only, if allowed.  If that fails, use
4615 			// the original Run.
4616 			pPrevRun = pRun->getPrevRun();
4617 			if (!pPrevRun
4618 				|| !pPrevRun->letPointPass()
4619 				|| !pPrevRun->canContainPoint())
4620 			{
4621 				pPrevRun = pRun;
4622 			}
4623 			else
4624 			{
4625 				// If the code gets one Run back, keep going back
4626 				// until finding a Run that is valid for point
4627 				// coordinate calculations.
4628 				while (pPrevRun &&
4629 					   (!pPrevRun->letPointPass()
4630 					   || !pPrevRun->canContainPoint()))
4631 				{
4632 					pPrevRun = pPrevRun->getPrevRun();
4633 				}
4634 
4635 				// If this fails, go with the original Run.
4636 				if (!pPrevRun)
4637 				{
4638 					pPrevRun = pRun;
4639 				}
4640 			}
4641 
4642 
4643 			// One final check: only allow the point to move to a
4644 			// different line if bEOL.
4645 			if (!bEOL && pRun->getLine() != pPrevRun->getLine())
4646 			{
4647 				pPrevRun = pRun;
4648 			}
4649 			if(getFirstRun()->getLine())
4650 			{
4651 				pPrevRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4652 			}
4653 			else
4654 			{
4655 				height = 0;
4656 			}
4657 		}
4658 		else
4659 		{
4660 			if(getFirstRun()->getLine())
4661 			{
4662 				pRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4663 			}
4664 			else
4665 			{
4666 				height = 0;
4667 			}
4668 		}
4669 
4670 		return pRun;
4671 	}
4672 
4673 	// Runs with layout information (page/column break) are not
4674 	// visible (or rather, cannot contain the point). They look
4675 	// like this (P is a page break, E is end of document):
4676 	//
4677 	// 1:  abcdefghP
4678 	// <page/column alignment>
4679 	// 2:  E
4680 	//
4681 	// When we have to find EOL position for display line 2, the
4682 	// arguments (the offset) is the same as in case (I) above
4683 	// (i.e., the argument is the offset of P). Thus this special
4684 	// check.
4685 	pPrevRun = pRun->getPrevRun();
4686 	if (!pPrevRun || !pPrevRun->letPointPass())
4687 	{
4688 		if(getFirstRun()->getLine())
4689 		{
4690 			pRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4691 		}
4692 		else
4693 		{
4694 			height = 0;
4695 		}
4696 		return pRun;
4697 	}
4698 
4699 	// Now search for the Run at the end of the line.  For a
4700 	// soft-broken block (soft-break due to margin constraints),
4701 	// this may be on the previous display line. It may also be
4702 	// pRun (if the offset was past the last Run of this display
4703 	// line). Consider this line (| is the right margin, N the
4704 	// line break or paragraph end):
4705 	//
4706 	// 1:  abcdefgh|
4707 	// 2:  ijklN
4708 	//
4709 	// For normal cursor movement (bEOL=false), IP (*) will move
4710 	// from *h to *i (or vice versa), skipping the h* position.
4711 	// When bEOL=true (user presses End key, or selects EOL with
4712 	// mouse) IP on display line 1 will be at h*, even though the
4713 	// requested offset is actually that of i.
4714 	//
4715 	while (pPrevRun && !pPrevRun->canContainPoint())
4716 	{
4717 		pPrevRun = pPrevRun->getPrevRun();
4718 	}
4719 
4720 	// If we went past the head of the list, it means that the
4721 	// originally found Run is the only one on this display line.
4722 	if (!pPrevRun)
4723 	{
4724 		if(getFirstRun()->getLine())
4725 		{
4726 			pRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4727 		}
4728 		else
4729 		{
4730 			height = 0;
4731 		}
4732 		return pRun;
4733 	}
4734 
4735 
4736 	// If the Runs are on the same line, assume pRun to be farther
4737 	// right than pPrevRun.
4738 	if (pPrevRun->getLine() == pRun->getLine())
4739 	{
4740 		if(getFirstContainer())
4741 		{
4742 			pRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4743 		}
4744 		else
4745 		{
4746 			height = 0;
4747 		}
4748 		return pRun;
4749 	}
4750 
4751 	// Only case left is that of a soft-broken line.
4752 
4753 	// Always return position _and_ Run of the previous line. Old
4754 	// implementation returned pRun, but this will cause the
4755 	// cursor to wander if End is pressed multiple times.
4756 	if(getFirstRun()->getLine())
4757 	{
4758 		pPrevRun->findPointCoords(iRelOffset, x, y, x2, y2, height, bDirection);
4759 	}
4760 	else
4761 	{
4762 		height = 0;
4763 	}
4764 	return pPrevRun;
4765 }
4766 
findPrevLineInDocument(fp_Line * pLine) const4767 fp_Line* fl_BlockLayout::findPrevLineInDocument(fp_Line* pLine) const
4768 {
4769 	if (pLine->getPrev())
4770 	{
4771 		return static_cast<fp_Line *>(pLine->getPrev());
4772 	}
4773 	else
4774 	{
4775 		if (getPrev())
4776 		{
4777 			return static_cast<fp_Line *>(getPrev()->getLastContainer());
4778 		}
4779 		else
4780 		{
4781 			fl_SectionLayout* pSL = static_cast<fl_SectionLayout *>(m_pSectionLayout->getPrev());
4782 
4783 			if (!pSL)
4784 			{
4785 				// at EOD, so just bail
4786 				return NULL;
4787 			}
4788 
4789 			// is this cast safe? Could not some other layout class be returned?
4790 			// if this assert fails, then this code needs to be fixed up. Tomas
4791 			UT_ASSERT_HARMLESS( pSL->getLastLayout() && pSL->getLastLayout()->getContainerType() == FL_CONTAINER_BLOCK );
4792 			fl_BlockLayout* pBlock = static_cast<fl_BlockLayout *>(pSL->getLastLayout());
4793 			UT_return_val_if_fail(pBlock, NULL);
4794 			return static_cast<fp_Line *>(pBlock->getLastContainer());
4795 		}
4796 	}
4797 
4798 	UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
4799 	return NULL;
4800 }
4801 
findNextLineInDocument(fp_Line * pLine) const4802 fp_Line* fl_BlockLayout::findNextLineInDocument(fp_Line* pLine) const
4803 {
4804 	if (pLine->getNext())
4805 	{
4806 		return static_cast<fp_Line *>(pLine->getNext());
4807 	}
4808 
4809 	if (getNext())
4810 	{
4811 		// grab the first line from the next block
4812 		return static_cast<fp_Line *>(getNext()->getFirstContainer());
4813 	}
4814 	else
4815 	{
4816 		// there is no next line in this section, try the next
4817 		const fl_SectionLayout* pSL = static_cast<const fl_SectionLayout*>(m_pSectionLayout->getNext());
4818 
4819 		if (!pSL)
4820 		{
4821 			// at EOD, so just bail
4822 			return NULL;
4823 		}
4824 
4825 		// is this cast safe? Could not some other layout class be returned?
4826 		// if this assert fails, then this code needs to be fixed up. Tomas
4827 		UT_ASSERT_HARMLESS( pSL->getLastLayout() && pSL->getLastLayout()->getContainerType() == FL_CONTAINER_BLOCK );
4828 
4829 		const fl_BlockLayout* pBlock = static_cast<const fl_BlockLayout*>(pSL->getFirstLayout());
4830 		UT_return_val_if_fail(pBlock, NULL);
4831 		return static_cast<fp_Line *>(pBlock->getFirstContainer());
4832 	}
4833 
4834 	UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
4835 	return NULL;
4836 }
4837 
4838 /*****************************************************************/
4839 
4840 
4841 #ifdef ENABLE_SPELL
4842 /*!
4843  Recalculate boundries for pending word
4844  \param iOffset Offset of change
4845  \param chg Size of change, negative is removal, zero is for
4846 			recalculating the pending word.
4847 
4848  On entry, the block is already changed and any pending word is junk.
4849  On exit, there's either a single unchecked pending word, or nothing.
4850 */
4851 void
_recalcPendingWord(UT_uint32 iOffset,UT_sint32 chg) const4852 fl_BlockLayout::_recalcPendingWord(UT_uint32 iOffset, UT_sint32 chg) const
4853 {
4854 	xxx_UT_DEBUGMSG(("fl_BlockLayout::_recalcPendingWord(%d, %d)\n",
4855 					 iOffset, chg));
4856 
4857 	UT_GrowBuf pgb(1024);
4858 	bool bRes = getBlockBuf(&pgb);
4859 	UT_UNUSED(bRes);
4860 	UT_ASSERT(bRes);
4861 
4862 	const UT_UCSChar* pBlockText = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
4863 	if (pBlockText == NULL)
4864 	{
4865 		return;
4866 	}
4867 
4868 	UT_uint32 iFirst = iOffset;
4869 
4870 	if (iFirst > pgb.getLength() - 1)
4871 		iFirst = pgb.getLength() - 1;
4872 
4873 	UT_uint32 iAbs = static_cast<UT_uint32>((chg >= 0) ? chg : -chg);
4874 	UT_sint32 iLen = ((chg > 0) ? iAbs : 0);
4875 
4876 	// We expand this region outward until we get a word delimiter on
4877 	// each side.
4878 
4879 	// First, look towards the start of the buffer
4880 	while ((iFirst > 1)
4881 		   && !isWordDelimiter(pBlockText[iFirst-1], pBlockText[iFirst] ,pBlockText[iFirst-2], iFirst-1))
4882 	{
4883 		iFirst--;
4884 	}
4885 
4886 	if(iFirst == 1 && !isWordDelimiter(pBlockText[0], pBlockText[1], UCS_UNKPUNK, iFirst))
4887 	{
4888 		iFirst--;
4889 	}
4890 
4891 	UT_ASSERT(iOffset>=iFirst);
4892 	iLen += (iOffset-iFirst);
4893 
4894 	// Then look towards the end of the buffer
4895 	UT_uint32 iBlockSize = pgb.getLength();
4896 	while ((iFirst + iLen < iBlockSize))
4897 	{
4898 		UT_UCSChar followChar, prevChar;
4899 		followChar = ((iFirst + iLen + 1) < iBlockSize)  ?	pBlockText[iFirst + iLen + 1]  : UCS_UNKPUNK;
4900 		prevChar = (iFirst == 0) ? UCS_UNKPUNK : pBlockText[iFirst + iLen - 1];
4901 
4902 		if (isWordDelimiter(pBlockText[iFirst + iLen], followChar, prevChar, iFirst+iLen)) break;
4903 		iLen++;
4904 	}
4905 
4906 	// Now we figure out what to do with this expanded span
4907 	if (chg > 0)
4908 	{
4909 		// Insertion - look for any completed words by finding the
4910 		// first word delimiter from the end.
4911 		UT_uint32 iLast = iOffset + chg;
4912 		UT_UCSChar followChar = UCS_UNKPUNK, currentChar, prevChar = iLast > 2 ? pBlockText[iLast - 2] : UCS_UNKPUNK;
4913 		while (iLast > iFirst)
4914 		{
4915 			currentChar = pBlockText[--iLast];
4916 			prevChar = iLast > 0 ? pBlockText[iLast - 1] : UCS_UNKPUNK;
4917 			if (isWordDelimiter(currentChar, followChar,prevChar,iLast)) break;
4918 			followChar = currentChar;
4919 		}
4920 
4921 		if (iLast > (iFirst + 1))
4922 		{
4923 			// Delimiter was found in the block - that means
4924 			// there is one or more words between iFirst
4925 			// and iLast we want to check.
4926 			_checkMultiWord(iFirst, iLast, false);
4927 		}
4928 
4929 		// We still have the word at the end pending though.
4930 		iLen -= (iLast - iFirst);
4931 		iFirst = iLast;
4932 	}
4933 	else
4934 	{
4935 		// Deletion or update - everything's already set up, so just
4936 		// fall through
4937 		UT_ASSERT(chg <= 0);
4938 	}
4939 
4940 	// Skip any word delimiters; handling the case where a word
4941 	// is split by space - without this check, the space would
4942 	// become part of the pending word.
4943 	UT_uint32 eor = pgb.getLength();
4944 	while (iLen > 0 && iFirst < eor)
4945 	{
4946 		UT_UCSChar currentChar = pBlockText[iFirst];
4947 		UT_UCSChar followChar = (((iFirst + 1) < eor) ?
4948 								 pBlockText[iFirst + + 1]  : UCS_UNKPUNK);
4949 		UT_UCSChar prevChar = iFirst > 0 ? pBlockText[iFirst - 1] : UCS_UNKPUNK;
4950 
4951 		if (!isWordDelimiter(currentChar, followChar, prevChar,iFirst)) break;
4952 		iFirst++;
4953 		iLen--;
4954 	}
4955 
4956 	// Is there a pending word left? If so, record the details.
4957 	if (iLen)
4958 	{
4959 		fl_PartOfBlockPtr pPending;
4960 
4961 		if (m_pLayout->isPendingWordForSpell())
4962 		{
4963 			pPending = m_pLayout->getPendingWordForSpell();
4964 			UT_ASSERT(pPending);
4965 		}
4966 
4967 		if (!pPending)
4968 		{
4969 			pPending = fl_PartOfBlockPtr(new fl_PartOfBlock());
4970 			UT_ASSERT(pPending);
4971 		}
4972 
4973 		if (pPending)
4974 		{
4975 			pPending->setOffset(iFirst);
4976 			pPending->setPTLength(iLen); // not sure about this ...
4977 			m_pLayout->setPendingWordForSpell(this, pPending);
4978 		}
4979 	}
4980 	else
4981 	{
4982 		// No pending word any more
4983 		m_pLayout->setPendingWordForSpell(NULL, NULL);
4984 	}
4985 }
4986 
4987 /*****************************************************************/
4988 /*****************************************************************/
4989 
4990 /*!
4991  Check spelling of entire block
4992 
4993  Destructively recheck the entire block. Called from timer context, so
4994  we need to toggle IP.
4995 
4996  TODO - the IP toggling does not work very well, particularly just
4997  after a document was loaded. Long paragraphs do a slow blink of the
4998  IP and short paragraphs fast one virtually freezing the IP. The
4999  overall effect is rather erratic. I do not see, though, a good way of
5000  fixing this, particularly concering short blocks.
5001 */
checkSpelling(void)5002 bool fl_BlockLayout::checkSpelling(void)
5003 {
5004 
5005 	xxx_UT_DEBUGMSG(("fl_BlockLayout::checkSpelling: this 0x%08x isOnScreen(): %d\n", this,static_cast<UT_uint32>(isOnScreen())));
5006 	// Don't spell check non-formatted blocks!
5007 	if(m_pFirstRun == NULL)
5008 		return false;
5009 	if(m_pFirstRun->getLine() == NULL)
5010 		return false;
5011 
5012 	// we only want to do the cursor magic if the cursor is in this block
5013 	bool bIsCursorInBlock = false;
5014 	FV_View* pView = getView();
5015 	fp_Run* pLastRun = m_pFirstRun;
5016 
5017 	while(pLastRun && pLastRun->getNextRun())
5018 		pLastRun = pLastRun->getNextRun();
5019 
5020 
5021 	if(pView && pLastRun)
5022 	{
5023 		UT_uint32 iBlPosStart = static_cast<UT_uint32>(getPosition());
5024 		UT_uint32 iBlPosEnd   = iBlPosStart + pLastRun->getBlockOffset() + pLastRun->getLength();
5025 		UT_uint32 iPos   = static_cast<UT_uint32>(pView->getPoint());
5026 
5027 		bIsCursorInBlock = ((iPos >= iBlPosStart) && (iPos <= iBlPosEnd));
5028 	}
5029 
5030 	// Remove any existing squiggles from the screen...
5031 	bool bUpdateScreen = m_pSpellSquiggles->deleteAll();
5032 
5033 	// Now start checking
5034 	bUpdateScreen |= _checkMultiWord(0, -1, bIsCursorInBlock);
5035 	if( bUpdateScreen && pView)
5036 	{
5037 		markAllRunsDirty();
5038 		setNeedsRedraw();
5039 	}
5040 	return true;
5041 }
5042 
5043 /*!
5044  Spell-check region of block with potentially multiple words
5045  \param pBlockText Text of block
5046  \param iStart Start of region to check
5047  \param eor End of region to check (or -1 to check to the end)
5048  \param bToggleIP Toggle IP if true
5049 */
5050 bool
_checkMultiWord(UT_sint32 iStart,UT_sint32 eor,bool bToggleIP) const5051 fl_BlockLayout::_checkMultiWord(UT_sint32 iStart,
5052 								UT_sint32 eor,
5053 								bool bToggleIP) const
5054 {
5055 	xxx_UT_DEBUGMSG(("fl_BlockLayout::_checkMultiWord\n"));
5056 
5057 	bool bScreenUpdated = false;
5058 
5059 	fl_BlockSpellIterator wordIterator(this, iStart);
5060 
5061 	const UT_UCSChar* pWord;
5062 	UT_sint32 iLength, iBlockPos, iPTLength;
5063 
5064 	while (wordIterator.nextWordForSpellChecking(pWord, iLength, iBlockPos, iPTLength))
5065 	{
5066 		// When past the provided end position, break out
5067 		if (eor > 0 && iBlockPos > eor) break;
5068 
5069 		fl_PartOfBlockPtr pPOB(new fl_PartOfBlock(iBlockPos, iPTLength));
5070 		UT_ASSERT(pPOB);
5071 
5072 #if 0 // TODO: turn this code on someday
5073 		FV_View* pView = getView();
5074 		XAP_App * pApp = XAP_App::getApp();
5075 		XAP_Prefs *pPrefs = pApp->getPrefs();
5076 		UT_ASSERT(pPrefs);
5077 
5078 		bool b;
5079 
5080 		// possibly auto-replace the squiggled word with a suggestion
5081 		if (pPrefs->getPrefsValueBool(static_cast<gchar*>(AP_PREF_KEY_SpellAutoReplace), &b))
5082 		{
5083 			if (b && !bIsIgnored)
5084 			{
5085 				// todo: better cursor movement
5086 				pView->cmdContextSuggest(1, this, pPOB);
5087 				pView->moveInsPtTo(FV_DOCPOS_EOW_MOVE);
5088 				DELETEP(pPOB);
5089 			}
5090 		}
5091 #endif
5092 
5093 		if (pPOB)
5094 		{
5095 			bool bwrong = false;
5096 			bwrong = _doCheckWord(pPOB, pWord, iLength, true, bToggleIP);
5097 #if 0
5098 			if(bwrong)
5099 			{
5100 				UT_DEBUGMSG(("Found misspelt word in block %x \n",this));
5101 			}
5102 #endif
5103 			bScreenUpdated |= bwrong;
5104 		}
5105 	}
5106 
5107 	return bScreenUpdated;
5108 }
5109 
5110 /*!
5111  Validate a word and spell-check it
5112  \param pPOB Block region to squiggle if appropriate
5113  \param pWord Pointer to the word
5114  \param iLength length of the word in pWord
5115  \param bAddSquiggle True if pPOB should be added to squiggle list
5116  \return True if display was updated, otherwise false
5117 
5118  If the word bounded by pPOB is not squiggled, the pPOB is deleted.
5119 
5120  I added the iLength parameter, because we need to store the PieceTable length in pPOB and
5121  as this is about the only function that need to know the word length, there is no point
5122  to store it in the POB (which gets queued in the layout). Tomas, May 9, 2005
5123  */
5124 
5125 bool
_doCheckWord(const fl_PartOfBlockPtr & pPOB,const UT_UCSChar * pWord,UT_sint32 iLength,bool bAddSquiggle,bool bClearScreen) const5126 fl_BlockLayout::_doCheckWord(const fl_PartOfBlockPtr& pPOB,
5127 							 const UT_UCSChar* pWord,
5128 							 UT_sint32 iLength,
5129 							 bool bAddSquiggle /* = true */,
5130 							 bool bClearScreen /* = true */) const
5131 {
5132 	UT_sint32 iBlockPos = pPOB->getOffset();
5133 
5134 	do {
5135 		// Spell check the word, return if correct
5136 		if (_spellCheckWord(pWord, iLength, iBlockPos))
5137 			break;
5138 
5139 		// Find out if the word is in the document's list of ignored
5140 		// words
5141 		pPOB->setIsIgnored(_getSpellChecker(iBlockPos)->isIgnored(pWord, iLength));
5142 
5143 		// Word not correct or recognized, so squiggle it
5144 		if (bAddSquiggle)
5145 		{
5146 			m_pSpellSquiggles->add(pPOB);
5147 		}
5148 
5149 		if(bClearScreen)
5150 		{
5151 			m_pSpellSquiggles->clear(pPOB);
5152 		}
5153 
5154 		// Display was updated
5155 		return true;
5156 
5157 	} while (0);
5158 
5159 	return false;
5160 }
5161 
5162 
5163 /*!
5164  Spell-check word in the block region
5165  \param pPOB Block region bounding the word
5166  \return True if display was updated, false otherwise
5167  Consume word in pPOB -- either squiggle or delete it
5168 
5169  FIXME:jsk Make callers use fl_BlockSpellIterator so we don't have to
5170  check validity? Should just be provided the starting offset...
5171 */
5172 bool
checkWord(const fl_PartOfBlockPtr & pPOB) const5173 fl_BlockLayout::checkWord(const fl_PartOfBlockPtr & pPOB) const
5174 {
5175 	xxx_UT_DEBUGMSG(("fl_BlockLayout::checkWord\n"));
5176 
5177 	UT_ASSERT(pPOB);
5178 	if (!pPOB)
5179 		return false;
5180 
5181     // Just use the initial offset from the provided pPOB - the word's
5182     // exact location/length is not known (since what we're provided
5183     // is just the editing limits).
5184     fl_BlockSpellIterator wordIterator(this, pPOB->getOffset());
5185 
5186 	const UT_UCSChar* pWord;
5187 	UT_sint32 iLength, iBlockPos, iPTLength;
5188 
5189     // The word iterator may be unable to find a word within the
5190     // editing limits provided by the pPOB - so check that before
5191     // continuing.
5192     if (wordIterator.nextWordForSpellChecking(pWord,
5193                                               iLength, iBlockPos, iPTLength)
5194         && (iBlockPos+iLength <= pPOB->getOffset()+pPOB->getPTLength()))
5195     {
5196         fl_PartOfBlockPtr pNewPOB(new fl_PartOfBlock(iBlockPos, iPTLength));
5197         UT_ASSERT(pNewPOB);
5198 
5199         return _doCheckWord(pNewPOB, pWord, iLength );
5200     }
5201 
5202 	return false;
5203 }
5204 #endif
5205 /*****************************************************************/
5206 /*****************************************************************/
5207 
doclistener_populateSpan(const PX_ChangeRecord_Span * pcrs,PT_BlockOffset blockOffset,UT_uint32 len)5208 bool fl_BlockLayout::doclistener_populateSpan(const PX_ChangeRecord_Span * pcrs, PT_BlockOffset blockOffset, UT_uint32 len)
5209 {
5210 	_assertRunListIntegrity();
5211 
5212 	PT_BufIndex bi = pcrs->getBufIndex();
5213 	if(getPrev()!= NULL && getPrev()->getLastContainer()==NULL)
5214 	{
5215 		xxx_UT_DEBUGMSG(("In fl_BlockLayout::doclistener_populateSpan  no LastLine \n"));
5216 		xxx_UT_DEBUGMSG(("getPrev = %d this = %d \n",getPrev(),this));
5217 		//			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
5218 	}
5219 	const UT_UCSChar* pChars = m_pDoc->getPointer(bi);
5220 	xxx_UT_DEBUGMSG(("fl_BlockLayout:: populateSpan BlockOffset %d NO chars %d \n",blockOffset,len));
5221 	/*
5222 	  walk through the characters provided and find any
5223 	  control characters.  Then, each control character gets
5224 	  handled specially.  Normal characters get grouped into
5225 	  runs as usual.
5226 	*/
5227 	UT_uint32	iNormalBase = 0;
5228 	bool		bNormal = false;
5229 	UT_uint32 i;
5230 	for (i=0; i<len; i++)
5231 	{
5232 		xxx_UT_DEBUGMSG(("fl_BlockLayout: char %d %c \n",i,static_cast<char>(pChars[i])));
5233 		switch (pChars[i])
5234 		{
5235 			// see similar control characters in fl_DocLayout.cpp
5236 		case UCS_FF:	// form feed, forced page break
5237 		case UCS_VTAB:	// vertical tab, forced column break
5238 		case UCS_LF:	// newline
5239 		case UCS_FIELDSTART: // zero length line to mark field start
5240 		case UCS_FIELDEND: // zero length line to mark field end
5241 		case UCS_BOOKMARKSTART:
5242 		case UCS_BOOKMARKEND:
5243 		case UCS_TAB:	// tab
5244 		case UCS_LRO:	// explicit direction overrides
5245 		case UCS_RLO:
5246 		case UCS_PDF:
5247 		case UCS_LRE:
5248 		case UCS_RLE:
5249 		case UCS_LRM:
5250 		case UCS_RLM:
5251 
5252 			if (bNormal)
5253 			{
5254 				_doInsertTextSpan(iNormalBase + blockOffset, i - iNormalBase);
5255 				bNormal = false;
5256 			}
5257 
5258 			/*
5259 			  Now, depending upon the kind of control char we found,
5260 			  we add a control run which corresponds to it.
5261 			*/
5262 			switch (pChars[i])
5263 			{
5264 			case UCS_FF:
5265 				_doInsertForcedPageBreakRun(i + blockOffset);
5266 				break;
5267 
5268 			case UCS_VTAB:
5269 				_doInsertForcedColumnBreakRun(i + blockOffset);
5270 				break;
5271 
5272 			case UCS_LF:
5273 				_doInsertForcedLineBreakRun(i + blockOffset);
5274 				break;
5275 
5276 			case UCS_FIELDSTART:
5277 				_doInsertFieldStartRun(i + blockOffset);
5278 				break;
5279 
5280 			case UCS_FIELDEND:
5281 				_doInsertFieldEndRun(i + blockOffset);
5282 				break;
5283 
5284 			case UCS_BOOKMARKSTART:
5285 			case UCS_BOOKMARKEND:
5286 				_doInsertBookmarkRun(i + blockOffset);
5287 				break;
5288 
5289 			case UCS_TAB:
5290 				_doInsertTabRun(i + blockOffset);
5291 				break;
5292 
5293 			case UCS_LRO:
5294 			case UCS_RLO:
5295 			case UCS_LRE:
5296 			case UCS_RLE:
5297 			case UCS_PDF:
5298 				// these should have been removed by
5299 				// pd_Document::append/insert functions
5300 				UT_ASSERT( UT_SHOULD_NOT_HAPPEN );
5301 				break;
5302 
5303 			case UCS_LRM:
5304 			case UCS_RLM:
5305 				_doInsertDirectionMarkerRun(i + blockOffset,pChars[i]);
5306 				break;
5307 
5308 			default:
5309 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
5310 				break;
5311 			}
5312 			break;
5313 
5314 		default:
5315 			if (!bNormal)
5316 			{
5317 				bNormal = true;
5318 				iNormalBase = i;
5319 			}
5320 			break;
5321 		}
5322 	}
5323 
5324 	UT_ASSERT(i == len);
5325 
5326 	if (bNormal && (iNormalBase < i))
5327 	{
5328 		_doInsertTextSpan(iNormalBase + blockOffset, i - iNormalBase);
5329 	}
5330 
5331 	_assertRunListIntegrity();
5332 
5333 	// This is needed because fl_BlockLayout::format() can be triggered by a timer
5334 	// half-way through populating a block. If that happens the format clears the flag
5335 	// and any runs that get inserted in subsequent populate calls are not correctly
5336 	// positioned. It might be desirable to have a mechanism to ignore format() calls
5337 	// while populating (for example storing sdh in a static member on populateStrux)
5338 	// but calling setNeedsReformat() costs us little and will do OK for now.
5339 	// Tomas, Apr 23, 2004
5340 	setNeedsReformat(this,blockOffset);
5341 	updateEnclosingBlockIfNeeded();
5342 	if(isHidden() == FP_HIDDEN_FOLDED)
5343 	{
5344 		collapse();
5345 	}
5346 	return true;
5347 }
5348 
itemizeSpan(PT_BlockOffset blockOffset,UT_uint32 len,GR_Itemization & I)5349 bool   fl_BlockLayout::itemizeSpan(PT_BlockOffset blockOffset, UT_uint32 len,GR_Itemization & I)
5350 {
5351 	UT_return_val_if_fail( m_pLayout, false );
5352 	PD_StruxIterator text(getStruxDocHandle(),
5353 						  blockOffset + fl_BLOCK_STRUX_OFFSET,
5354 						  blockOffset + fl_BLOCK_STRUX_OFFSET + len - 1);
5355 	I.setDirOverride(m_iDirOverride);
5356 	I.setEmbedingLevel(m_iDomDirection);
5357 
5358 	bool bShowControls = false;
5359 	FV_View* pView = getView();
5360 	if(pView && pView->getShowPara())
5361 		bShowControls = true;
5362 
5363 	I.setShowControlChars(bShowControls);
5364 
5365 	const PP_AttrProp * pSpanAP = NULL;
5366 	const PP_AttrProp * pBlockAP = NULL;
5367 	getSpanAP(blockOffset, false, pSpanAP);
5368 	getAP(pBlockAP);
5369 	const char * szLang = static_cast<const char *>(PP_evalProperty("lang",
5370 																	pSpanAP,
5371 																	pBlockAP,
5372 																	NULL,
5373 																	m_pDoc,
5374 																	true));
5375 
5376 	const GR_Font * pFont = m_pLayout->findFont(pSpanAP,
5377 												pBlockAP,
5378 												NULL,
5379 												m_pLayout->getGraphics());
5380 
5381 	xxx_UT_DEBUGMSG(("Got [%s], %s\n", pFont->getFamily(), szLang));
5382 	I.setLang(szLang);
5383 	I.setFont(pFont);
5384 
5385 	m_pLayout->getGraphics()->itemize(text, I);
5386 	return true;
5387 }
5388 
_doInsertTextSpan(PT_BlockOffset blockOffset,UT_uint32 len)5389 bool	fl_BlockLayout::_doInsertTextSpan(PT_BlockOffset blockOffset, UT_uint32 len)
5390 {
5391 
5392 	xxx_UT_DEBUGMSG(("_doInsertTextSpan: Initial offset %d, len %d bl_Length %d \n", blockOffset, len,getLength()));
5393 	GR_Itemization I;
5394 	bool b= itemizeSpan(blockOffset, len,I);
5395 	UT_return_val_if_fail( b, false );
5396 
5397 	for(UT_sint32 i = 0; i < I.getItemCount() - 1; ++i)
5398 	{
5399 		UT_uint32 iRunOffset = I.getNthOffset(i);
5400 		UT_uint32 iRunLength = I.getNthLength(i);
5401 
5402 		// because of bug 8542 we do not allow runs longer than 32000 chars, so
5403 		// if it is longer, just split it (we do not care where we split it,
5404 		// this is a contingency measure only). Lowered to 16000 because of
5405 		// bug 13709.
5406 		while(iRunLength)
5407 		{
5408 			UT_uint32 iRunSegment = UT_MIN(iRunLength, 16000);
5409 
5410 			fp_TextRun* pNewRun = new fp_TextRun(this, blockOffset + iRunOffset, iRunSegment);
5411 			iRunOffset += iRunSegment;
5412 			iRunLength -= iRunSegment;
5413 
5414 			UT_return_val_if_fail(pNewRun && pNewRun->getType() == FPRUN_TEXT, false);
5415 			pNewRun->setDirOverride(m_iDirOverride);
5416 
5417 			GR_Item * pItem = I.getNthItem(i)->makeCopy();
5418 			UT_ASSERT( pItem );
5419 			pNewRun->setItem(pItem);
5420 
5421 			if(!_doInsertRun(pNewRun))
5422 				return false;
5423 		}
5424 
5425 	}
5426 
5427 	return true;
5428 }
5429 
_doInsertForcedLineBreakRun(PT_BlockOffset blockOffset)5430 bool	fl_BlockLayout::_doInsertForcedLineBreakRun(PT_BlockOffset blockOffset)
5431 {
5432 	fp_Run* pNewRun = NULL;
5433 	if(isContainedByTOC())
5434 	{
5435 		pNewRun = new fp_DummyRun(this,blockOffset);
5436 	}
5437 	else
5438 	{
5439 		pNewRun = new fp_ForcedLineBreakRun(this, blockOffset, 1);
5440 	}
5441 	UT_ASSERT(pNewRun); // TODO check for outofmem
5442 
5443 	bool bResult = _doInsertRun(pNewRun);
5444 	if (bResult && !isContainedByTOC())
5445 		_breakLineAfterRun(pNewRun);
5446 
5447 	return bResult;
5448 }
5449 
_doInsertDirectionMarkerRun(PT_BlockOffset blockOffset,UT_UCS4Char iM)5450 bool    fl_BlockLayout::_doInsertDirectionMarkerRun(PT_BlockOffset blockOffset, UT_UCS4Char iM)
5451 {
5452 	xxx_UT_DEBUGMSG(("fl_BlockLayout::_doInsertDirectionMarkerRun: offset %d, marker 0x%04x\n",
5453 				 blockOffset, iM));
5454 
5455 	fp_Run * pNewRun = new fp_DirectionMarkerRun(this, blockOffset, iM);
5456 	UT_ASSERT( pNewRun );
5457 
5458 	bool bResult = _doInsertRun(pNewRun);
5459 #if 0
5460 	if (bResult)
5461 		_breakLineAfterRun(pNewRun);
5462 #endif
5463 	return bResult;
5464 }
5465 
5466 #if 0
5467 bool	fl_BlockLayout::_deleteBookmarkRun(PT_BlockOffset blockOffset)
5468 {
5469 	UT_DEBUGMSG(("fl_BlockLayout::_deleteBookmarkRun: blockOffset %d\n",blockOffset));
5470 	_assertRunListIntegrity();
5471 
5472 	fp_BookmarkRun *pB1;
5473 
5474 	fp_Run* pRun = m_pFirstRun;
5475 
5476 	/*
5477 		we have to deal with FmtMarks, which are special case since they
5478 		have width 0 and so can share block offset with our book mark
5479 	*/
5480 	while (pRun->getNextRun() && (pRun->getBlockOffset() != blockOffset || pRun->getType() == FPRUN_FMTMARK))
5481 	{
5482 		pRun = pRun->getNextRun();
5483 	}
5484 
5485 	UT_ASSERT(pRun && pRun->getType() == FPRUN_BOOKMARK);
5486 	if(!pRun || pRun->getType() != FPRUN_BOOKMARK)
5487 		return false;
5488 
5489 	pB1 = static_cast<fp_BookmarkRun *>(pRun);
5490 
5491 	// Remove Run from line
5492 	fp_Line* pLine = pB1->getLine();
5493 	UT_ASSERT(pLine);
5494 	if(pLine)
5495 	{
5496 		pLine->removeRun(pB1, true);
5497 	}
5498 	// Unlink Run and delete it
5499 	if (m_pFirstRun == pB1)
5500 	{
5501 		m_pFirstRun = pB1->getNextRun();
5502 	}
5503 
5504 	pRun = pB1->getNextRun();
5505 	pB1->unlinkFromRunList();
5506 	delete pB1;
5507 
5508 	fp_Run * pLastRun = static_cast<fp_Line *>(getLastContainer())->getLastRun();
5509 	while(pRun )
5510 	{
5511 		pRun->setBlockOffset(pRun->getBlockOffset() - 1);
5512 		if(pRun == pLastRun)
5513 			break;
5514 		pRun = pRun->getNextRun();
5515 	}
5516 
5517 	xxx_UT_DEBUGMSG(("fl_BlockLayout::_deleteBookmarkRun: assert integrity (1)\n"));
5518 	_assertRunListIntegrity();
5519 
5520 	return true;
5521 }
5522 #endif
5523 
_doInsertBookmarkRun(PT_BlockOffset blockOffset)5524 bool	fl_BlockLayout::_doInsertBookmarkRun(PT_BlockOffset blockOffset)
5525 {
5526 	fp_Run * pNewRun;
5527 
5528 	if(!isContainedByTOC())
5529 	{
5530 		pNewRun = new fp_BookmarkRun(this, blockOffset, 1);
5531 	}
5532 	else
5533 	{
5534 		pNewRun = new fp_DummyRun(this,blockOffset);
5535 	}
5536 
5537 	UT_ASSERT(pNewRun);
5538 	bool bResult = _doInsertRun(pNewRun);
5539 #if 0
5540 	if (bResult)
5541 	{
5542 		_breakLineAfterRun(pNewRun);
5543 	}
5544 #endif
5545 	return bResult;
5546 
5547 }
5548 
_finishInsertHyperlinkedNewRun(PT_BlockOffset,fp_HyperlinkRun * pNewRun)5549 void    fl_BlockLayout::_finishInsertHyperlinkedNewRun( PT_BlockOffset /*blockOffset*/,
5550 														fp_HyperlinkRun* pNewRun )
5551 {
5552 	// if this is the start of the hyperlink, we need to mark all the runs
5553 	// till the end of it
5554 	// if this is because of an insert operation, the end run is already
5555 	// in place, because we insert them in that order; if it is because of
5556 	// append, there is no end run, but then this is the last run; the other
5557 	// runs will get marked as they get appended (inside fp_Run::insertRun...)
5558 	// any hyperlink run will not get its m_pHyperlink set, so that
5559 	// runs that follow it would not be marked
5560 
5561 	if(pNewRun->isStartOfHyperlink())
5562 	{
5563 		fp_Run * pRun = pNewRun->getNextRun();
5564 		UT_ASSERT(pRun);
5565 		// when loading a document the opening hyperlink run is initially followed
5566 		// by ENDOFPARAGRAPH run; we do not want to set this one
5567 		while(pRun && pRun->getType() != FPRUN_HYPERLINK && pRun->getType() != FPRUN_ENDOFPARAGRAPH)
5568 		{
5569 			pRun->setHyperlink(pNewRun);
5570 			pRun = pRun->getNextRun();
5571 		}
5572 	}
5573 	else
5574 	{
5575 		//
5576 		// clear out any hyperlinks
5577 		//
5578 		fp_Run * pRun = pNewRun->getNextRun();
5579 		while(pRun && (pRun->getType() != FPRUN_HYPERLINK && pRun->getType() != FPRUN_ENDOFPARAGRAPH))
5580 		{
5581 			pRun->setHyperlink(NULL);
5582 			pRun = pRun->getNextRun();
5583 		}
5584 	}
5585 	//_breakLineAfterRun(pNewRun);
5586 }
5587 
_doInsertHyperlinkRun(PT_BlockOffset blockOffset)5588 bool	fl_BlockLayout::_doInsertHyperlinkRun(PT_BlockOffset blockOffset)
5589 {
5590 	bool bResult = false;
5591 
5592 	if(!isContainedByTOC())
5593 	{
5594 		fp_HyperlinkRun * pNewRun =  new fp_HyperlinkRun(this, blockOffset, 1);
5595 		UT_ASSERT(pNewRun);
5596 		bResult = _doInsertRun(pNewRun);
5597 
5598 		if (bResult)
5599 		{
5600 			_finishInsertHyperlinkedNewRun( blockOffset, pNewRun );
5601 		}
5602 	}
5603 	else
5604 	{
5605 		fp_Run * pNewRun = new fp_DummyRun(this,blockOffset);
5606 		UT_ASSERT(pNewRun);
5607 		bResult = _doInsertRun(pNewRun);
5608 	}
5609 
5610 
5611 	return bResult;
5612 
5613 }
5614 
5615 
_doInsertAnnotationRun(PT_BlockOffset blockOffset)5616 bool	fl_BlockLayout::_doInsertAnnotationRun(PT_BlockOffset blockOffset)
5617 {
5618 	bool bResult = false;
5619 
5620 	if(!isContainedByTOC())
5621 	{
5622 		fp_AnnotationRun * pNewRun =  new fp_AnnotationRun(this, blockOffset, 1);
5623 		UT_ASSERT(pNewRun);
5624 		bResult = _doInsertRun(pNewRun);
5625 
5626 		if (bResult)
5627 		{
5628 			_finishInsertHyperlinkedNewRun( blockOffset, pNewRun );
5629 		}
5630 	}
5631 	else
5632 	{
5633 		fp_Run * pNewRun = new fp_DummyRun(this,blockOffset);
5634 		UT_ASSERT(pNewRun);
5635 		bResult = _doInsertRun(pNewRun);
5636 	}
5637 
5638 
5639 	return bResult;
5640 
5641 }
5642 
5643 
5644 /**
5645  * Note that _doInsertHyperlinkRun(), _doInsertAnnotationRun,
5646  * and _doInsertRDFAnchorRun() all work on contained information.
5647  * Each of these methods use setHyperlink() on the runs.
5648  */
_doInsertRDFAnchorRun(PT_BlockOffset blockOffset)5649 bool fl_BlockLayout::_doInsertRDFAnchorRun(PT_BlockOffset blockOffset)
5650 {
5651 	bool bResult = false;
5652 
5653 	if( isContainedByTOC() )
5654 	{
5655 		fp_Run * pNewRun = new fp_DummyRun(this,blockOffset);
5656 		UT_ASSERT(pNewRun);
5657 		bResult = _doInsertRun(pNewRun);
5658 	}
5659 	else
5660 	{
5661 		fp_RDFAnchorRun * pNewRun = new fp_RDFAnchorRun(this, blockOffset, 1);
5662 		UT_ASSERT(pNewRun);
5663 		bResult = _doInsertRun(pNewRun);
5664 
5665 		if (bResult)
5666 		{
5667 			_finishInsertHyperlinkedNewRun( blockOffset, pNewRun );
5668 		}
5669 	}
5670 
5671 	return bResult;
5672 
5673 }
5674 
5675 
_doInsertFieldStartRun(PT_BlockOffset blockOffset)5676 bool	fl_BlockLayout::_doInsertFieldStartRun(PT_BlockOffset blockOffset)
5677 {
5678 	fp_Run* pNewRun = new fp_FieldStartRun(this,blockOffset, 1);
5679 	UT_ASSERT(pNewRun); // TODO check for outofmem
5680 
5681 	bool bResult = _doInsertRun(pNewRun);
5682 	if (bResult)
5683 		_breakLineAfterRun(pNewRun);
5684 
5685 	return bResult;
5686 }
5687 
_doInsertFieldEndRun(PT_BlockOffset blockOffset)5688 bool	fl_BlockLayout::_doInsertFieldEndRun(PT_BlockOffset blockOffset)
5689 {
5690 	fp_Run* pNewRun = new fp_FieldEndRun(this, blockOffset, 1);
5691 	UT_ASSERT(pNewRun); // TODO check for outofmem
5692 
5693 	bool bResult = _doInsertRun(pNewRun);
5694 	if (bResult)
5695 		_breakLineAfterRun(pNewRun);
5696 
5697 	return bResult;
5698 }
5699 
5700 /*!
5701  * Returns true if this run is at the last position of the block.
5702  */
isLastRunInBlock(fp_Run * pRun) const5703 bool fl_BlockLayout::isLastRunInBlock(fp_Run * pRun) const
5704 {
5705 	if(((UT_sint32)pRun->getBlockOffset()+2) == getLength())
5706 	{
5707 		return true;
5708 	}
5709 	return false;
5710 }
5711 
_doInsertForcedPageBreakRun(PT_BlockOffset blockOffset)5712 bool	fl_BlockLayout::_doInsertForcedPageBreakRun(PT_BlockOffset blockOffset)
5713 {
5714 	fp_Run* pNewRun = NULL;
5715 	if(isContainedByTOC())
5716 	{
5717 		pNewRun = new fp_DummyRun(this,blockOffset);
5718 	}
5719 	else
5720 	{
5721 		pNewRun = new fp_ForcedPageBreakRun(this,blockOffset, 1);
5722 	}
5723 	UT_ASSERT(pNewRun); // TODO check for outofmem
5724 	if(getPrev()!= NULL && getPrev()->getLastContainer()==NULL)
5725 	{
5726 		UT_DEBUGMSG(("In fl_BlockLayout::_doInsertForcedPageBreakRun  no LastLine \n"));
5727 		UT_DEBUGMSG(("getPrev = %p this = %p \n",getPrev(),this));
5728 		//UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
5729 	}
5730 
5731 	bool bResult = _doInsertRun(pNewRun);
5732 	//
5733 	// only do this if this run is the last run in the block. Otherwise we terrible UI
5734 	// whre the first line of th enext page cannot have it's own style!
5735 	//
5736 	if (bResult && !isLastRunInBlock(pNewRun))
5737 		_breakLineAfterRun(pNewRun);
5738 
5739 	return bResult;
5740 }
5741 
_doInsertForcedColumnBreakRun(PT_BlockOffset blockOffset)5742 bool	fl_BlockLayout::_doInsertForcedColumnBreakRun(PT_BlockOffset blockOffset)
5743 {
5744 	fp_Run* pNewRun = NULL;
5745 	if(isContainedByTOC())
5746 	{
5747 		pNewRun = new fp_DummyRun(this,blockOffset);
5748 	}
5749 	else
5750 	{
5751 		pNewRun = new fp_ForcedColumnBreakRun(this,blockOffset, 1);
5752 	}
5753 	UT_ASSERT(pNewRun); // TODO check for outofmem
5754 
5755 	bool bResult = _doInsertRun(pNewRun);
5756 	if (bResult && !isLastRunInBlock(pNewRun) )
5757 		_breakLineAfterRun(pNewRun);
5758 
5759 	return bResult;
5760 }
5761 
_doInsertTabRun(PT_BlockOffset blockOffset)5762 bool	fl_BlockLayout::_doInsertTabRun(PT_BlockOffset blockOffset)
5763 {
5764 	fp_Run * pNewRun = NULL;
5765 	if(!isContainedByTOC() || !m_bPrevListLabel)
5766 	{
5767 		pNewRun = new fp_TabRun(this,blockOffset, 1);
5768 	}
5769 	else
5770 	{
5771 		xxx_UT_DEBUGMSG(("Insert dummy in place of TAB at %d \n",blockOffset));
5772 		pNewRun = new fp_DummyRun(this,blockOffset);
5773 	}
5774 	UT_ASSERT(pNewRun); // TODO check for outofmem
5775 
5776 	return _doInsertRun(pNewRun);
5777 }
5778 
getTextIndent(void) const5779 UT_sint32	fl_BlockLayout::getTextIndent(void) const
5780 {
5781 	fl_ContainerLayout * pCL = myContainingLayout();
5782 	if(pCL && (pCL->getContainerType() == FL_CONTAINER_ANNOTATION) && ((pCL->getFirstLayout() == NULL) || (pCL->getFirstLayout() == this)))
5783 	{
5784 			fl_AnnotationLayout * pAL = static_cast<fl_AnnotationLayout *>(pCL);
5785 			fp_AnnotationRun * pAR = pAL->getAnnotationRun();
5786 			if(pAR)
5787 			{
5788 				    if(pAR->getRealWidth() == 0)
5789 						pAR->recalcValue();
5790 					return m_iTextIndent+pAR->getRealWidth();
5791 			}
5792 	}
5793 	return m_iTextIndent;
5794 }
5795 
_doInsertMathRun(PT_BlockOffset blockOffset,PT_AttrPropIndex indexAP,pf_Frag_Object * oh)5796 bool	fl_BlockLayout::_doInsertMathRun(PT_BlockOffset blockOffset,PT_AttrPropIndex indexAP, pf_Frag_Object* oh)
5797 {
5798 	if(isContainedByTOC())
5799 	{
5800 		fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5801 		xxx_UT_DEBUGMSG(("Inserting a dummy run instead of mathrun at %d \n",blockOffset));
5802 		return _doInsertRun(pDumRun);
5803 	}
5804 
5805 	fp_Run * pNewRun = NULL;
5806 	pNewRun = new fp_MathRun(this,blockOffset,indexAP,oh);
5807 	UT_ASSERT(pNewRun); // TODO check for outofmem
5808 
5809 	return _doInsertRun(pNewRun);
5810 }
5811 
5812 
_doInsertEmbedRun(PT_BlockOffset blockOffset,PT_AttrPropIndex indexAP,pf_Frag_Object * oh)5813 bool	fl_BlockLayout::_doInsertEmbedRun(PT_BlockOffset blockOffset,PT_AttrPropIndex indexAP, pf_Frag_Object* oh)
5814 {
5815 	if(isContainedByTOC())
5816 	{
5817 		fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5818 		xxx_UT_DEBUGMSG(("Inserting a dummy run instead of embedrun at %d \n",blockOffset));
5819 		return _doInsertRun(pDumRun);
5820 	}
5821 
5822 	fp_Run * pNewRun = NULL;
5823 	pNewRun = new fp_EmbedRun(this,blockOffset,indexAP,oh);
5824 	UT_ASSERT(pNewRun); // TODO check for outofmem
5825 
5826 	return _doInsertRun(pNewRun);
5827 }
5828 
_doInsertTOCTabRun(PT_BlockOffset blockOffset)5829 bool	fl_BlockLayout::_doInsertTOCTabRun(PT_BlockOffset blockOffset)
5830 {
5831 	fp_Run* pNewRun = new fp_TabRun(this,blockOffset, 1);
5832 	UT_ASSERT(pNewRun); // TODO check for outofmem
5833 	static_cast<fp_TabRun *>(pNewRun)->setTOCTab();
5834 	return _doInsertRun(pNewRun);
5835 }
5836 
5837 /*!
5838  * Special TAB that follows a TOCListLabel. It has zero length since it's
5839  * not in the document.
5840  */
_doInsertTOCListTabRun(PT_BlockOffset blockOffset)5841 bool	fl_BlockLayout::_doInsertTOCListTabRun(PT_BlockOffset blockOffset)
5842 {
5843 	fp_Run* pNewRun = new fp_TabRun(this,blockOffset, 0);
5844 	UT_ASSERT(pNewRun); // TODO check for outofmem
5845 	static_cast<fp_TabRun *>(pNewRun)->setTOCTabListLabel();
5846 	fp_Run * pRun = m_pFirstRun;
5847 	pRun->insertIntoRunListBeforeThis(*pNewRun);
5848 	m_pFirstRun = pNewRun;
5849 	pNewRun->markWidthDirty();
5850 	if(pRun->getLine())
5851 	{
5852 		pRun->getLine()->insertRunBefore(pNewRun, pRun);
5853 	}
5854 	return true;
5855 }
5856 
_doInsertImageRun(PT_BlockOffset blockOffset,FG_Graphic * pFG,pf_Frag_Object * oh)5857 bool	fl_BlockLayout::_doInsertImageRun(PT_BlockOffset blockOffset, FG_Graphic* pFG, pf_Frag_Object* oh)
5858 {
5859 	if(isContainedByTOC())
5860 	{
5861 		fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5862 		xxx_UT_DEBUGMSG(("Inserting a dummy run instead of image at %d \n",blockOffset));
5863 		return _doInsertRun(pDumRun);
5864 	}
5865 
5866 	fp_ImageRun* pNewRun = new fp_ImageRun(this, blockOffset, 1, pFG,oh);
5867 	UT_ASSERT(pNewRun); // TODO check for outofmem
5868 
5869 	return _doInsertRun(pNewRun);
5870 }
5871 
_doInsertFieldRun(PT_BlockOffset blockOffset,const PX_ChangeRecord_Object * pcro)5872 bool	fl_BlockLayout::_doInsertFieldRun(PT_BlockOffset blockOffset, const PX_ChangeRecord_Object * pcro)
5873 {
5874 	// Get the field type.
5875 	const PP_AttrProp * pSpanAP = NULL;
5876 
5877 #if 0
5878 	// this is unnecessarily involved, just use the index from the pcro
5879 	getSpanAttrProp(blockOffset, false, &pSpanAP);
5880 	UT_ASSERT(pSpanAP);
5881 #else
5882 	UT_return_val_if_fail(pcro, false);
5883 	PT_AttrPropIndex iAP = pcro->getIndexAP();
5884 	m_pLayout->getDocument()->getAttrProp(iAP, &pSpanAP);
5885 #endif
5886 
5887 	const gchar* pszType = NULL;
5888 	pSpanAP->getAttribute("type", pszType);
5889 
5890 	// Create the field run.
5891 
5892 	fp_FieldRun* pNewRun;
5893 
5894 	if (!pszType)
5895 		{
5896 			UT_ASSERT (pszType);
5897 			pNewRun = new fp_FieldRun(this, blockOffset,1);
5898 		}
5899 	else if(strcmp(pszType, "list_label") == 0)
5900 	{
5901 		if(!isContainedByTOC())
5902 		{
5903 			pNewRun = new fp_FieldListLabelRun(this,   blockOffset, 1);
5904 		}
5905 		else
5906 		{
5907 			fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5908 			xxx_UT_DEBUGMSG(("Inserting a dummy run instead of listlabel at %d \n",blockOffset));
5909 			_doInsertRun(pDumRun);
5910 			recalculateFields(0);
5911 			m_bPrevListLabel = true;
5912 			//
5913 			// Might have to put in code here to detect if there is already
5914 			// a tab run ahead of the list label. If so we replace it
5915 			// with a dummyrun
5916 			// fp_Run * pNextRun = pDumRun->getNextRun();
5917 			return true;
5918 		}
5919 	}
5920 	else if(strcmp(pszType, "footnote_ref") == 0)
5921 	{
5922 		if(isContainedByTOC())
5923 		{
5924 			fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5925 			xxx_UT_DEBUGMSG(("Inserting a dummy run instead of footnote_ref at %d \n",blockOffset));
5926 			return _doInsertRun(pDumRun);
5927 		}
5928 
5929 		xxx_UT_DEBUGMSG(("Footnoet ref run created at %d \n",blockOffset));
5930 		pNewRun = new fp_FieldFootnoteRefRun(this,   blockOffset, 1);
5931 	}
5932 	else if(strcmp(pszType, "footnote_anchor") == 0)
5933 	{
5934 		if(isContainedByTOC())
5935 		{
5936 			fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5937 			xxx_UT_DEBUGMSG(("Inserting a dummy run instead of footnote_anchor at %d \n",blockOffset));
5938 			return _doInsertRun(pDumRun);
5939 		}
5940 		pNewRun = new fp_FieldFootnoteAnchorRun(this,   blockOffset, 1);
5941 	}
5942 	else if(strcmp(pszType, "endnote_ref") == 0)
5943 	{
5944 		if(isContainedByTOC())
5945 		{
5946 			fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5947 			xxx_UT_DEBUGMSG(("Inserting a dummy run instead of endnote_ref at %d \n",blockOffset));
5948 			return _doInsertRun(pDumRun);
5949 		}
5950 
5951 		xxx_UT_DEBUGMSG(("Endnote ref run created at %d \n",blockOffset));
5952 		pNewRun = new fp_FieldEndnoteRefRun(this,   blockOffset, 1);
5953 	}
5954 	else if(strcmp(pszType, "endnote_anchor") == 0)
5955 	{
5956 		if(isContainedByTOC())
5957 		{
5958 			fp_Run * pDumRun = new fp_DummyRun(this,blockOffset);
5959 			xxx_UT_DEBUGMSG(("Inserting a dummy run instead of endnote_anchor at %d \n",blockOffset));
5960 			return _doInsertRun(pDumRun);
5961 		}
5962 		pNewRun = new fp_FieldEndnoteAnchorRun(this,   blockOffset, 1);
5963 	}
5964 	else if(strcmp(pszType, "time") == 0)
5965 	{
5966 		pNewRun = new fp_FieldTimeRun(this,   blockOffset, 1);
5967 	}
5968 	else if(strcmp(pszType, "page_number") == 0)
5969 	{
5970 		pNewRun = new fp_FieldPageNumberRun(this,   blockOffset, 1);
5971 	}
5972 	else if(strcmp(pszType, "page_ref") == 0)
5973 	{
5974 		pNewRun = new fp_FieldPageReferenceRun(this,   blockOffset, 1);
5975 	}
5976 	else if(strcmp(pszType, "page_count") == 0)
5977 	{
5978 		pNewRun = new fp_FieldPageCountRun(this,   blockOffset, 1);
5979 	}
5980 	else if(strcmp(pszType, "date") == 0)
5981 	{
5982 		pNewRun = new fp_FieldDateRun(this,   blockOffset, 1);
5983 	}
5984 	else if(strcmp(pszType, "date_mmddyy") == 0)
5985 	{
5986 		pNewRun = new fp_FieldMMDDYYRun(this,   blockOffset, 1);
5987 	}
5988 	else if(strcmp(pszType, "date_ddmmyy") == 0)
5989 	{
5990 		pNewRun = new fp_FieldDDMMYYRun(this,   blockOffset, 1);
5991 	}
5992 	else if(strcmp(pszType, "date_mdy") == 0)
5993 	{
5994 		pNewRun = new fp_FieldMonthDayYearRun(this,   blockOffset, 1);
5995 	}
5996 	else if(strcmp(pszType, "date_mthdy") == 0)
5997 	{
5998 		pNewRun = new fp_FieldMthDayYearRun(this,   blockOffset, 1);
5999 	}
6000 	else if(strcmp(pszType, "date_dfl") == 0)
6001 	{
6002 		pNewRun = new fp_FieldDefaultDateRun(this,   blockOffset, 1);
6003 	}
6004 	else if(strcmp(pszType, "date_ntdfl") == 0)
6005 	{
6006 		pNewRun = new fp_FieldDefaultDateNoTimeRun(this,   blockOffset, 1);
6007 	}
6008 	else if(strcmp(pszType, "date_wkday") == 0)
6009 	{
6010 		pNewRun = new fp_FieldWkdayRun(this,   blockOffset, 1);
6011 	}
6012 	else if(strcmp(pszType, "date_doy") == 0)
6013 	{
6014 		pNewRun = new fp_FieldDOYRun(this,   blockOffset, 1);
6015 	}
6016 	else if(strcmp(pszType, "time_miltime") == 0)
6017 	{
6018 		pNewRun = new fp_FieldMilTimeRun(this,   blockOffset, 1);
6019 	}
6020 	else if(strcmp(pszType, "time_ampm") == 0)
6021 	{
6022 		pNewRun = new fp_FieldAMPMRun(this,   blockOffset, 1);
6023 	}
6024 	else if(strcmp(pszType, "time_zone") == 0)
6025 	{
6026 		pNewRun = new fp_FieldTimeZoneRun(this,   blockOffset, 1);
6027 	}
6028 	else if(strcmp(pszType, "time_epoch") == 0)
6029 	{
6030 		pNewRun = new fp_FieldTimeEpochRun(this,   blockOffset, 1);
6031 	}
6032 	else if(strcmp(pszType, "datetime_custom") == 0)
6033 	{
6034 		pNewRun = new fp_FieldDateTimeCustomRun(this,   blockOffset, 1);
6035 	}
6036 	else if(strcmp(pszType, "word_count") == 0)
6037 	{
6038 		pNewRun = new fp_FieldWordCountRun(this,   blockOffset, 1);
6039 	}
6040 	else if(strcmp(pszType, "char_count") == 0)
6041 	{
6042 		pNewRun = new fp_FieldCharCountRun(this,   blockOffset, 1);
6043 	}
6044 	else if(strcmp(pszType, "line_count") == 0)
6045 	{
6046 		pNewRun = new fp_FieldLineCountRun(this,   blockOffset, 1);
6047 	}
6048 	else if(strcmp(pszType, "para_count") == 0)
6049 	{
6050 		pNewRun = new fp_FieldParaCountRun(this,   blockOffset, 1);
6051 	}
6052 	else if(strcmp(pszType, "nbsp_count") == 0)
6053 	{
6054 		pNewRun = new fp_FieldNonBlankCharCountRun(this,   blockOffset, 1);
6055 	}
6056 	else if(strcmp(pszType, "file_name") == 0)
6057 	{
6058 		pNewRun = new fp_FieldFileNameRun(this,   blockOffset, 1);
6059 	}
6060 	else if(strcmp(pszType, "short_file_name") == 0)
6061 	{
6062 		pNewRun = new fp_FieldShortFileNameRun(this,   blockOffset, 1);
6063 	}
6064 	else if(strcmp(pszType, "app_ver") == 0)
6065 	{
6066 		pNewRun = new fp_FieldBuildVersionRun(this,   blockOffset, 1);
6067 	}
6068 	else if(strcmp(pszType, "app_id") == 0)
6069 	{
6070 		pNewRun = new fp_FieldBuildIdRun(this,   blockOffset, 1);
6071 	}
6072 	else if(strcmp(pszType, "app_options") == 0)
6073 	  {
6074 		pNewRun = new fp_FieldBuildOptionsRun(this,   blockOffset, 1);
6075 	  }
6076 	else if(strcmp(pszType, "app_target") == 0)
6077 	  {
6078 		pNewRun = new fp_FieldBuildTargetRun(this,   blockOffset, 1);
6079 	  }
6080 	else if(strcmp(pszType, "app_compiledate") == 0)
6081 	  {
6082 		pNewRun = new fp_FieldBuildCompileDateRun(this,   blockOffset, 1);
6083 	  }
6084 	else if(strcmp(pszType, "app_compiletime") == 0)
6085 	  {
6086 		pNewRun = new fp_FieldBuildCompileTimeRun(this,   blockOffset, 1);
6087 	  }
6088 	else if(strcmp(pszType, "mail_merge") == 0)
6089 	  {
6090 	    pNewRun = new fp_FieldMailMergeRun(this,   blockOffset, 1);
6091 	  }
6092 	else if(strcmp(pszType, "meta_title") == 0)
6093 	  {
6094 	    pNewRun = new fp_FieldMetaTitleRun(this,   blockOffset, 1);
6095 	  }
6096 	else if(strcmp(pszType, "meta_creator") == 0)
6097 	  {
6098 	    pNewRun = new fp_FieldMetaCreatorRun(this,   blockOffset, 1);
6099 	  }
6100 	else if(strcmp(pszType, "meta_subject") == 0)
6101 	  {
6102 	    pNewRun = new fp_FieldMetaSubjectRun(this,   blockOffset, 1);
6103 	  }
6104 	else if(strcmp(pszType, "meta_publisher") == 0)
6105 	  {
6106 	    pNewRun = new fp_FieldMetaPublisherRun(this,   blockOffset, 1);
6107 	  }
6108 	else if(strcmp(pszType, "meta_contributor") == 0)
6109 	  {
6110 	    pNewRun = new fp_FieldMetaContributorRun(this,   blockOffset, 1);
6111 	  }
6112 	else if(strcmp(pszType, "meta_date") == 0)
6113 	  {
6114 	    pNewRun = new fp_FieldMetaDateRun(this,   blockOffset, 1);
6115 	  }
6116         else if(strcmp(pszType, "meta_date_last_changed") == 0)
6117 	  {
6118 	    pNewRun = new fp_FieldMetaDateLastChangedRun(this,   blockOffset, 1);
6119 	  }
6120 	else if(strcmp(pszType, "meta_type") == 0)
6121 	  {
6122 	    pNewRun = new fp_FieldMetaTypeRun(this,   blockOffset, 1);
6123 	  }
6124 	else if(strcmp(pszType, "meta_language") == 0)
6125 	  {
6126 	    pNewRun = new fp_FieldMetaLanguageRun(this,   blockOffset, 1);
6127 	  }
6128 	else if(strcmp(pszType, "meta_coverage") == 0)
6129 	  {
6130 	    pNewRun = new fp_FieldMetaCoverageRun(this,   blockOffset, 1);
6131 	  }
6132 	else if(strcmp(pszType, "meta_rights") == 0)
6133 	  {
6134 	    pNewRun = new fp_FieldMetaRightsRun(this,   blockOffset, 1);
6135 	  }
6136 	else if(strcmp(pszType, "meta_keywords") == 0)
6137 	  {
6138 	    pNewRun = new fp_FieldMetaKeywordsRun(this,   blockOffset, 1);
6139 	  }
6140 	else if(strcmp(pszType, "meta_description") == 0)
6141 	  {
6142 	    pNewRun = new fp_FieldMetaDescriptionRun(this,   blockOffset, 1);
6143 	  }
6144 	else if(strcmp(pszType, "sum_rows") == 0)
6145 	  {
6146 	    pNewRun = new fp_FieldTableSumRows(this,   blockOffset, 1);
6147 	  }
6148 	else if(strcmp(pszType, "sum_cols") == 0)
6149 	  {
6150 	    pNewRun = new fp_FieldTableSumCols(this,   blockOffset, 1);
6151 	  }
6152 	else
6153 	{
6154 		UT_ASSERT_NOT_REACHED ();
6155 		//
6156 		// New Piece Table Field Leave it for that code..
6157 		//
6158 		pNewRun = new fp_FieldRun(this,   blockOffset, 1);
6159 	}
6160 
6161 	UT_ASSERT(pNewRun); // TODO check for outofmem
6162 
6163 	// TODO -- is this really needed ???
6164 	// should not be, since we called lookupProperties in the
6165 	// constructor - Tomas
6166 	// pNewRun->lookupProperties();
6167 	pNewRun->calculateValue();
6168 
6169 	_doInsertRun(pNewRun);
6170 	//	recalculateFields(0); MES Do this in the format following
6171 	return true;
6172 }
6173 
6174 
_doInsertFieldTOCRun(PT_BlockOffset blockOffset)6175 bool	fl_BlockLayout::_doInsertFieldTOCRun(PT_BlockOffset blockOffset)
6176 {
6177 	fp_FieldRun* pNewRun;
6178 	pNewRun = new fp_FieldTOCNumRun(this,   blockOffset, 1);
6179 	_doInsertRun(pNewRun);
6180 	return true;
6181 }
6182 
6183 /*!
6184  * TOC List label run. It has zero length since it's not in the document.
6185  */
_doInsertTOCListLabelRun(PT_BlockOffset blockOffset)6186 bool	fl_BlockLayout::_doInsertTOCListLabelRun(PT_BlockOffset blockOffset)
6187 {
6188 	fp_FieldRun* pNewRun;
6189 	pNewRun = new fp_FieldTOCListLabelRun(this,   blockOffset, 0);
6190 	fp_Run * pRun = m_pFirstRun;
6191 	pRun->insertIntoRunListBeforeThis(*pNewRun);
6192 	m_pFirstRun = pNewRun;
6193 	pNewRun->markWidthDirty();
6194 	if(pRun->getLine())
6195 	{
6196 		pRun->getLine()->insertRunBefore(pNewRun, pRun);
6197 	}
6198 	return true;
6199 }
6200 
6201 
_doInsertTOCHeadingRun(PT_BlockOffset blockOffset)6202 bool	fl_BlockLayout::_doInsertTOCHeadingRun(PT_BlockOffset blockOffset)
6203 {
6204 	fp_FieldRun* pNewRun;
6205 	pNewRun = new fp_FieldTOCHeadingRun(this,   blockOffset, 1);
6206 	fp_Run * pRun = m_pFirstRun;
6207 	pRun->insertIntoRunListBeforeThis(*pNewRun);
6208 	m_pFirstRun = pNewRun;
6209 	pNewRun->markWidthDirty();
6210 	if(pRun->getLine())
6211 	{
6212 		pRun->getLine()->insertRunBefore(pNewRun, pRun);
6213 	}
6214 	return true;
6215 }
6216 
_doInsertRun(fp_Run * pNewRun)6217 bool	fl_BlockLayout::_doInsertRun(fp_Run* pNewRun)
6218 {
6219 	PT_BlockOffset blockOffset = pNewRun->getBlockOffset();
6220 	UT_uint32 len = pNewRun->getLength();
6221 	xxx_UT_DEBUGMSG(("_doInsertRun: New run has offset %d Length %d \n",blockOffset,len));
6222 	_assertRunListIntegrity();
6223 
6224 	bool bInserted = false;
6225 	fp_Run* pRun = m_pFirstRun;
6226 	while (pRun)
6227 	{
6228 		UT_uint32 iRunBlockOffset = pRun->getBlockOffset();
6229 		UT_uint32 iRunLength = pRun->getLength();
6230 		xxx_UT_DEBUGMSG(("_doInsertRun: Target offset %d CurRun Offset %d Length %d  Type %d \n",blockOffset,iRunBlockOffset,iRunLength,pRun->getType()));
6231 		if ( (iRunBlockOffset + iRunLength) <= blockOffset )
6232 		{
6233 			// nothing to do.  the insert occurred AFTER this run
6234 		}
6235 		else if ((iRunBlockOffset > blockOffset) && bInserted)
6236 		{
6237 
6238 			// the insert is occuring BEFORE this run, so we just move the run offset
6239 				pRun->setBlockOffset(iRunBlockOffset + len);
6240 		}
6241 		else if((iRunBlockOffset > blockOffset) && !bInserted)
6242 //
6243 // Run should be inserted before this run
6244 //
6245 		{
6246 			pRun->setBlockOffset(iRunBlockOffset + len);
6247 			pRun->insertIntoRunListBeforeThis(*pNewRun);
6248 
6249 			if(m_pFirstRun == pRun)
6250 			{
6251 				m_pFirstRun = pNewRun;
6252 			}
6253 			bInserted = true;
6254 			if(pRun->getLine())
6255 			{
6256 				pRun->getLine()->insertRunBefore(pNewRun, pRun);
6257 #if DEBUG
6258 				pRun->getLine()->assertLineListIntegrity();
6259 #endif
6260 			}
6261 		}
6262 		else if (iRunBlockOffset == blockOffset && !bInserted)
6263 		{
6264 			UT_ASSERT(!bInserted);
6265 
6266 			bInserted = true;
6267 
6268 			// the insert is right before this run.
6269 			pRun->setBlockOffset(iRunBlockOffset + len);
6270 			pRun->insertIntoRunListBeforeThis(*pNewRun);
6271 
6272 			if (m_pFirstRun == pRun)
6273 			{
6274 				m_pFirstRun = pNewRun;
6275 			}
6276 			if(pRun->getLine())
6277 			{
6278 				pRun->getLine()->insertRunBefore(pNewRun, pRun);
6279 #if DEBUG
6280 				pRun->getLine()->assertLineListIntegrity();
6281 #endif
6282 			}
6283 		}
6284 //
6285 // Here if the run run starts before the target offset and finishes after it.
6286 // We need to split this run.
6287 //
6288 		else if(!bInserted)
6289 		{
6290 			UT_ASSERT(!bInserted);
6291 
6292 			UT_ASSERT((blockOffset >= pRun->getBlockOffset()) &&
6293 					  (blockOffset <
6294 					   (pRun->getBlockOffset() + pRun->getLength())));
6295 			UT_ASSERT(pRun->getType() == FPRUN_TEXT);	// only textual runs can be split anyway
6296 
6297 			fp_TextRun* pTextRun = static_cast<fp_TextRun*>(pRun);
6298 			//
6299 			// Have to take account of the length of the new run in the
6300 			// block.
6301 			//
6302 			pTextRun->split(blockOffset,pNewRun->getLength());
6303 
6304 			UT_ASSERT(pRun->getNextRun());
6305 			UT_ASSERT(pRun->getNextRun()->getBlockOffset() == (blockOffset+pNewRun->getLength()));
6306 
6307 			UT_ASSERT(pTextRun->getNextRun());
6308 			UT_ASSERT(pTextRun->getNextRun()->getType() == FPRUN_TEXT);
6309 
6310 			bInserted = true;
6311 
6312 			pRun = pRun->getNextRun();
6313 
6314 			iRunBlockOffset = pRun->getBlockOffset();
6315 			iRunLength = pRun->getLength();
6316 
6317 			UT_ASSERT(iRunBlockOffset == (blockOffset+pNewRun->getLength()));
6318 
6319 			// the insert is right before this run.
6320 
6321 			pRun->insertIntoRunListBeforeThis(*pNewRun);
6322 
6323 			if(pRun->getLine())
6324 			{
6325 				pRun->getLine()->insertRunBefore(pNewRun, pRun);
6326 #if DEBUG
6327 				pRun->getLine()->assertLineListIntegrity();
6328 #endif
6329 			}
6330 
6331 //			pOtherHalfOfSplitRun->recalcWidth();
6332 		}
6333 		pRun = pRun->getNextRun();
6334 	}
6335 
6336 	if (!bInserted)
6337 	{
6338 		pRun = m_pFirstRun;
6339 		fp_Run * pLastRun = NULL;
6340 		UT_uint32 offset = 0;
6341 		while (pRun)
6342 		{
6343 			pLastRun = pRun;
6344 			offset += pRun->getLength();
6345 			pRun = pRun->getNextRun();
6346 		}
6347 		if (pLastRun)
6348 		{
6349 			if((pNewRun->getType() !=FPRUN_ENDOFPARAGRAPH) && (pLastRun->getType()== FPRUN_ENDOFPARAGRAPH))
6350 			{
6351 				pLastRun->insertIntoRunListBeforeThis(*pNewRun);
6352 				pLastRun->setBlockOffset(pNewRun->getBlockOffset()+pNewRun->getLength());
6353 				if(pLastRun->getLine())
6354 				{
6355 					pLastRun->getLine()->insertRunBefore(pNewRun, pLastRun);
6356 #if DEBUG
6357 					pLastRun->getLine()->assertLineListIntegrity();
6358 #endif
6359 				}
6360 			}
6361 			else
6362 			{
6363 				pLastRun->insertIntoRunListAfterThis(*pNewRun);
6364 				if (getLastContainer())
6365 				{
6366 					static_cast<fp_Line *>(getLastContainer())->addRun(pNewRun);
6367 #if DEBUG
6368 					static_cast<fp_Line *>(getLastContainer())->assertLineListIntegrity();
6369 #endif
6370 				}
6371 			}
6372 		}
6373 		else
6374 		{
6375 			m_pFirstRun = pNewRun;
6376 			if (getLastContainer())
6377 			{
6378 				static_cast<fp_Line *>(getLastContainer())->addRun(pNewRun);
6379 #if DEBUG
6380 				static_cast<fp_Line *>(getLastContainer())->assertLineListIntegrity();
6381 #endif
6382 			}
6383 		}
6384 
6385 	}
6386 
6387 	/*
6388 	  if we inserted a text run, and its direction is strong, then we might need to do
6389 	  some more work. Since a strong run can change the visual direction of adjucent
6390 	  weak characters, we need to ensure that any weak characters on either side
6391 	  are in runs of their own.
6392 	*/
6393 	UT_BidiCharType iDirection = pNewRun->getDirection();
6394 	if(FRIBIDI_IS_STRONG(iDirection) && pNewRun->getType() == FPRUN_TEXT)
6395 	{
6396 		static_cast<fp_TextRun*>(pNewRun)->breakNeighborsAtDirBoundaries();
6397 	}
6398 
6399 	pNewRun->markWidthDirty();
6400 	_assertRunListIntegrity();
6401 
6402 #if 0
6403 	// now that the run is in place and the context has been set, we
6404 	// calculate character widths
6405 
6406 	// actually, we are not in position to calculate widths at this
6407 	// point, because the insertion of this run invalidated the draw
6408 	// buffers of un unspecified number of runs on either side, and in
6409 	// order for the width calculation to be correct, the widths of
6410 	// the runs that precede it would need to be recalculated first,
6411 	// otherwise we get wrong results with ligatures (when our run
6412 	// starts with a ligature placeholder, its with gets set to 1/2 of
6413 	// the width of the previous glyph; this assumes that the previous
6414 	// glyph is already the ligature glyph which it is not)
6415 	// There seems to be no reason why we would need to calculate the widths
6416 	// here, so we will leave it for now, and when the widths are needed,
6417 	// i.e., when we attempt to draw, we will have all the right
6418 	// values in place
6419 	if (pNewRun->getType() == FPRUN_TEXT)
6420 	{
6421 		fp_TextRun* pNewTextRun = static_cast<fp_TextRun*>(pNewRun);
6422 		pNewTextRun->recalcWidth();
6423 	}
6424 #endif
6425 	return true;
6426 }
6427 
6428 /*!
6429  * This method will append the text in the block to the UTF8 string supplied
6430  */
appendUTF8String(UT_UTF8String & sText) const6431 void fl_BlockLayout::appendUTF8String(UT_UTF8String & sText) const
6432 {
6433 	UT_GrowBuf buf;
6434 	appendTextToBuf(buf);
6435 	const UT_UCS4Char * pBuff = reinterpret_cast<const UT_UCS4Char *>(buf.getPointer(0));
6436 	if((buf.getLength() > 0) && (pBuff != NULL))
6437 	{
6438 		sText.appendUCS4(pBuff,buf.getLength());
6439 	}
6440 }
6441 
6442 /*!
6443  * This method extracts all the text from the current block and appends it
6444  * to the supplied growbuf.
6445  */
appendTextToBuf(UT_GrowBuf & buf) const6446 void fl_BlockLayout::appendTextToBuf(UT_GrowBuf & buf) const
6447 {
6448 	fp_Run * pRun = m_pFirstRun;
6449 	while(pRun)
6450     {
6451 		if(pRun->getType() == 	FPRUN_TEXT)
6452 		{
6453 			fp_TextRun * pTRun = static_cast<fp_TextRun *>(pRun);
6454 			pTRun->appendTextToBuf(buf);
6455 		}
6456 		pRun = pRun->getNextRun();
6457 	}
6458 }
doclistener_insertSpan(const PX_ChangeRecord_Span * pcrs)6459 bool fl_BlockLayout::doclistener_insertSpan(const PX_ChangeRecord_Span * pcrs)
6460 {
6461 	UT_return_val_if_fail( m_pLayout, false );
6462 
6463 	_assertRunListIntegrity();
6464 
6465 	UT_ASSERT(pcrs->getType()==PX_ChangeRecord::PXT_InsertSpan);
6466 	//UT_ASSERT(pcrs->getPosition() >= getPosition());		/* valid assert, but very expensive */
6467 
6468 	PT_BlockOffset blockOffset = pcrs->getBlockOffset();
6469 	UT_uint32 len = pcrs->getLength();
6470 	UT_ASSERT(len>0);
6471 
6472 	PT_BufIndex bi = pcrs->getBufIndex();
6473 	const UT_UCSChar* pChars = m_pDoc->getPointer(bi);
6474 
6475 	/*
6476 	  walk through the characters provided and find any
6477 	  control characters.  Then, each control character gets
6478 	  handled specially.  Normal characters get grouped into
6479 	  runs as usual.
6480 	*/
6481 	UT_uint32	iNormalBase = 0;
6482 	bool	bNormal = false;
6483 	UT_uint32 i;
6484 	UT_uint32 _sqlist[100], *sqlist = _sqlist;
6485 	UT_uint32 sqcount = 0;
6486 	//
6487 	// Need this to find where to break section in the document.
6488 	//
6489 	fl_ContainerLayout * pPrevCL = getPrev();
6490 	fp_Page * pPrevP = NULL;
6491 	if(pPrevCL)
6492 	{
6493 		fp_Container * pPrevCon = pPrevCL->getFirstContainer();
6494 		if(pPrevCon)
6495 		{
6496 			pPrevP = pPrevCon->getPage();
6497 		}
6498 	}
6499 
6500 	if (sizeof(_sqlist) / sizeof(_sqlist[0])  < len)
6501 	{
6502 		sqlist = new UT_uint32[len];
6503 	}
6504 	xxx_UT_DEBUGMSG(("fl_BlockLayout::doclistener_insertSpan(), len=%d, pos %d \n", len, getPosition()+blockOffset));
6505 	for (i=0; i<len; i++)
6506 	{
6507 		xxx_UT_DEBUGMSG(("fl_BlockLayout: char %d %c \n",i,static_cast<char>(pChars[i])));
6508 		switch (pChars[i])
6509 		{
6510 		case UCS_FF:	// form feed, forced page break
6511 		case UCS_VTAB:	// vertical tab, forced column break
6512 		case UCS_LF:	// newline
6513 		case UCS_FIELDSTART: // zero length line to mark field start
6514 		case UCS_FIELDEND: // zero length line to mark field end
6515 		case UCS_BOOKMARKSTART:
6516 		case UCS_BOOKMARKEND:
6517 		case UCS_TAB:	// tab
6518 		case UCS_LRO:	// explicit direction overrides
6519 		case UCS_RLO:
6520 		case UCS_LRE:
6521 		case UCS_RLE:
6522 		case UCS_PDF:
6523 		case UCS_LRM:
6524 		case UCS_RLM:
6525 
6526 			if (bNormal)
6527 			{
6528 				_doInsertTextSpan(blockOffset + iNormalBase, i - iNormalBase);
6529 				bNormal = false;
6530 			}
6531 
6532 			/*
6533 			  Now, depending upon the kind of control char we found,
6534 			  we add a control run which corresponds to it.
6535 			*/
6536 			switch (pChars[i])
6537 			{
6538 			case UCS_FF:
6539 				getDocSectionLayout()->setNeedsSectionBreak(true,pPrevP);
6540 				_doInsertForcedPageBreakRun(i + blockOffset);
6541 				break;
6542 
6543 			case UCS_VTAB:
6544 				getDocSectionLayout()->setNeedsSectionBreak(true,pPrevP);
6545 				_doInsertForcedColumnBreakRun(i + blockOffset);
6546 				break;
6547 
6548 			case UCS_LF:
6549 				getDocSectionLayout()->setNeedsSectionBreak(true,pPrevP);
6550 				_doInsertForcedLineBreakRun(i + blockOffset);
6551 				break;
6552 
6553 			case UCS_FIELDSTART:
6554 				_doInsertFieldStartRun(i + blockOffset);
6555 				break;
6556 
6557 			case UCS_FIELDEND:
6558 				_doInsertFieldEndRun(i + blockOffset);
6559 				break;
6560 
6561 			case UCS_BOOKMARKSTART:
6562 			case UCS_BOOKMARKEND:
6563 				_doInsertBookmarkRun(i + blockOffset);
6564 				break;
6565 
6566 			case UCS_TAB:
6567 				_doInsertTabRun(i + blockOffset);
6568 				break;
6569 
6570 			case UCS_LRO:
6571 			case UCS_RLO:
6572 			case UCS_LRE:
6573 			case UCS_RLE:
6574 			case UCS_PDF:
6575 				// these should have been removed by
6576 				// pd_Document::append/insert functions
6577 				UT_ASSERT( UT_SHOULD_NOT_HAPPEN );
6578 				break;
6579 
6580 			case UCS_LRM:
6581 			case UCS_RLM:
6582 				_doInsertDirectionMarkerRun(i + blockOffset,pChars[i]);
6583 				break;
6584 
6585 			default:
6586 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
6587 				break;
6588 			}
6589 			break;
6590 
6591 		default:
6592 			if ((i != len-1)  &&  UT_isSmartQuotableCharacter(pChars[i]))
6593 			{
6594 				// accumulate smart quote candidates and deal with them
6595 				// as a bunch below after the final text insertion has
6596 				// been dealt with
6597 				sqlist[sqcount++] = blockOffset + i;
6598 			}
6599 			if (!bNormal)
6600 			{
6601 				bNormal = true;
6602 				iNormalBase = i;
6603 			}
6604 			break;
6605 		}
6606 	}
6607 
6608 	UT_ASSERT(i == len);
6609 
6610 	if (bNormal && (iNormalBase < i))
6611 	{
6612 		xxx_UT_DEBUGMSG(("insertSpan: BlockOffset %d iNormalBase %d i %d \n",blockOffset,iNormalBase,i));
6613 		_doInsertTextSpan(blockOffset + iNormalBase, i - iNormalBase);
6614 	}
6615 	m_iNeedsReformat = blockOffset;
6616 	format();
6617 	updateEnclosingBlockIfNeeded();
6618 
6619 #ifdef ENABLE_SPELL
6620 	m_pSpellSquiggles->textInserted(blockOffset, len);
6621 	m_pGrammarSquiggles->textInserted(blockOffset, len);
6622 	xxx_UT_DEBUGMSG(("Set pending block for grammar - insertSpan \n"));
6623 	m_pLayout->setPendingBlockForGrammar(this);
6624 #endif
6625 	FV_View* pView = getView();
6626 	if (pView && (pView->isActive() || pView->isPreview()))
6627 	{
6628 		pView->_setPoint(pcrs->getPosition() + len);
6629 //		if(!isHdrFtr())
6630 //			pView->notifyListeners(AV_CHG_FMTCHAR); // TODO verify that this is necessary.
6631 	}
6632 	else if(pView && pView->getPoint() > pcrs->getPosition())
6633 		pView->_setPoint(pView->getPoint() + len);
6634 
6635 	if(pView)
6636 		pView->updateCarets(pcrs->getPosition(),len);
6637 
6638 	if (m_pLayout->hasBackgroundCheckReason(FL_DocLayout::bgcrSmartQuotes))
6639 	{
6640 		fl_BlockLayout *sq_bl = m_pLayout->getPendingBlockForSmartQuote();
6641 		UT_uint32 sq_of = m_pLayout->getOffsetForSmartQuote();
6642 		m_pLayout->setPendingSmartQuote(NULL, 0);
6643 		//
6644 		// Don't do Smart quotes during an undo or during a paste
6645 		//
6646 		if(!m_pDoc->isDoingTheDo() && !m_pDoc->isDoingPaste())
6647 		{
6648 			if (sq_bl)
6649 			{
6650 				m_pLayout->considerSmartQuoteCandidateAt(sq_bl, sq_of);
6651 			}
6652 			if (sqcount)
6653 			{
6654 				m_pDoc->beginUserAtomicGlob();
6655 				for (UT_uint32 sdex=0; sdex<sqcount; ++sdex)
6656 				{
6657 					m_pLayout->considerSmartQuoteCandidateAt(this, sqlist[sdex]);
6658 				}
6659 				m_pDoc->endUserAtomicGlob();
6660 			}
6661 			if (UT_isSmartQuotableCharacter(pChars[len - 1]))
6662 			{
6663 				m_pLayout->setPendingSmartQuote(this, blockOffset + len - 1);
6664 			}
6665 		}
6666 	}
6667 	if (sqlist != _sqlist) delete[] sqlist;
6668 
6669 	_assertRunListIntegrity();
6670 #if 0
6671 #if DEBUG
6672 	fp_Run * ppRun = getFirstRun();
6673 	while(ppRun)
6674 	{
6675 		if(ppRun->getType() == FPRUN_TEXT)
6676 		{
6677 			fp_TextRun * pTRun = static_cast<fp_TextRun *>(ppRun);
6678 			pTRun->printText();
6679 		}
6680 		ppRun = ppRun->getNextRun();
6681 	}
6682 #endif
6683 #endif
6684 	//
6685 	// OK Now do the insertSpan for any TOC's that shadow this block.
6686 	//
6687 	if(!isNotTOCable() && !m_bIsTOC && m_bStyleInTOC)
6688 	{
6689 		UT_GenericVector<fl_BlockLayout *> vecBlocksInTOCs;
6690 		if(m_pLayout->getMatchingBlocksFromTOCs(this, &vecBlocksInTOCs))
6691 		{
6692 			for(UT_sint32 j=0; j<vecBlocksInTOCs.getItemCount();j++)
6693 			{
6694 				fl_BlockLayout * pBL = vecBlocksInTOCs.getNthItem(j);
6695 				pBL->doclistener_insertSpan(pcrs);
6696 			}
6697 		}
6698 		else
6699 		{
6700 			m_bStyleInTOC = false;
6701 		}
6702 	}
6703   	return true;
6704 }
6705 
6706 #ifndef NDEBUG
6707 /*!
6708   Assert integrity of the Run list
6709   Assert the following properties:
6710    - Offsets are correct
6711    - No adjacent FmtMark Runs
6712    - Only FmtMark Runs have length zero
6713    - List ends in an EOP Run
6714 */
6715 void
_assertRunListIntegrityImpl(void) const6716 fl_BlockLayout::_assertRunListIntegrityImpl(void) const
6717 {
6718 	UT_return_if_fail( m_pLayout );
6719 
6720 	fp_Run* pRun = m_pFirstRun;
6721 	UT_uint32 iOffset = 0;
6722 	if(m_pFirstRun)
6723 	{
6724 		//
6725 		// Dummy Runs are allowed at the first positions
6726 		// of a TOC
6727 		//
6728 		if(m_pFirstRun->getPrevRun())
6729 		{
6730 			UT_ASSERT(m_pFirstRun->getPrevRun()->getType() == FPRUN_DUMMY);
6731 		}
6732 	}
6733 #if 0
6734 	//
6735 	// This can legitmately be non zero while deleting a block with an
6736 	// embedded footnote
6737 	//
6738 	UT_ASSERT(m_pFirstRun->getBlockOffset() == 0);
6739 	// Verify that offset of this block is correct.
6740 #endif
6741 	UT_sint32 icnt = -1;
6742 	//	PT_DocPosition posAtStartOfBlock = getPosition();
6743 	while (pRun)
6744 	{
6745 		icnt++;
6746 		xxx_UT_DEBUGMSG(("!!Assert run %d runType %d posindoc %d end run %d \n",icnt,pRun->getType(),posAtStartOfBlock+pRun->getBlockOffset(),posAtStartOfBlock+pRun->getBlockOffset()+pRun->getLength()));
6747 		xxx_UT_DEBUGMSG(("run %d %p Type %d offset %d length %d \n",icnt,pRun,pRun->getType(),pRun->getBlockOffset(), pRun->getLength()));
6748 #if 0
6749 //
6750 // FIXME: Invent a clever way to account for embedded hidden stuff
6751 //        in blocks (like footnotes).
6752 //        Maybe detect this sort of anomaly can verify it matches
6753 //        what is in the piecetable
6754 		UT_ASSERT( iOffset == pRun->getBlockOffset() );
6755 #endif
6756 		iOffset += pRun->getLength();
6757 
6758 		// Verify that we don't have two adjacent FmtMarks.
6759 		//		UT_ASSERT( ((pRun->getType() != FPRUN_FMTMARK)
6760 		//			|| !pRun->getNextRun()
6761 		//			|| (pRun->getNextRun()->getType() != FPRUN_FMTMARK)) );
6762 
6763 		// Verify that the Run has a non-zero length (or is a FmtMark)
6764 		UT_ASSERT( (FPRUN_FMTMARK == pRun->getType()) ||
6765 				   (((FPRUN_TAB == pRun->getType())
6766 					 || (FPRUN_FIELD == pRun->getType()))
6767 					  && isContainedByTOC())
6768 				   || (pRun->getLength() > 0) );
6769 
6770 		// Verify that if there is no next Run, this Run is the EOP Run.
6771 		// Or we're in the middle of loading a document.
6772 //
6773 // FIXME; Take this code out when things work.
6774 //
6775 		if(pRun->getNextRun() || (FPRUN_ENDOFPARAGRAPH == pRun->getType()) )
6776 		{
6777 		}
6778 		else
6779 		{
6780 			m_pLayout->getDocument()->miniDump(getStruxDocHandle(),8);
6781 		}
6782 		UT_ASSERT( pRun->getNextRun()
6783 				   || (FPRUN_ENDOFPARAGRAPH == pRun->getType()) );
6784 		pRun = pRun->getNextRun();
6785 	}
6786 }
6787 #endif /* !NDEBUG */
6788 
6789 inline void
_assertRunListIntegrity(void) const6790 fl_BlockLayout::_assertRunListIntegrity(void) const
6791 {
6792 #ifndef NDEBUG
6793 	_assertRunListIntegrityImpl();
6794 #endif
6795 }
6796 
6797 
_delete(PT_BlockOffset blockOffset,UT_uint32 len)6798 bool fl_BlockLayout::_delete(PT_BlockOffset blockOffset, UT_uint32 len)
6799 {
6800 	_assertRunListIntegrity();
6801 	xxx_UT_DEBUGMSG(("_delete fl_BlockLayout offset %d len %d \n",blockOffset,len));
6802 	// runs to do with bidi post-processing
6803 	fp_TextRun * pTR_del1 = NULL;
6804 	fp_TextRun * pTR_del2 = NULL;
6805 	fp_TextRun * pTR_next = NULL;
6806 	fp_TextRun * pTR_prev = NULL;
6807 
6808 	fp_Run* pRun = m_pFirstRun;
6809 	while (pRun)
6810 	{
6811 		UT_uint32 iRunBlockOffset = pRun->getBlockOffset();
6812 		UT_uint32 iRunLength = pRun->getLength();
6813 		xxx_UT_DEBUGMSG(("_delete run %x type %d offset %d len %d \n",pRun,pRun->getType(),iRunBlockOffset,iRunLength));
6814 		fp_Run* pNextRun = pRun->getNextRun(); // remember where we're going, since this run may get axed
6815 
6816 		if ( (iRunBlockOffset + iRunLength) <= blockOffset )
6817 		{
6818 			// nothing to do.  the delete occurred AFTER this run
6819 		}
6820 		else if (iRunBlockOffset >= (blockOffset + len))
6821 		{
6822 			// the delete occurred entirely before this run.
6823 			xxx_UT_DEBUGMSG(("_delete Run %x New Offset offset %d len %d \n",pRun,iRunBlockOffset - len,iRunLength));
6824 
6825 			pRun->setBlockOffset(iRunBlockOffset - len);
6826 		}
6827 		else
6828 		{
6829 //
6830 // Force a whole page redraw if we delete a page or column break
6831 //
6832 			if(pRun->getType() == FPRUN_FORCEDCOLUMNBREAK ||
6833 			   pRun->getType() == FPRUN_FORCEDPAGEBREAK)
6834 			{
6835 				fp_Container * pCon = static_cast<fp_Container *>(pRun->getLine());
6836 				fp_Page * pPage = pCon->getPage();
6837 				if(pPage)
6838 				{
6839 					pPage->markAllDirty();
6840 				}
6841 			}
6842 			if (blockOffset >= iRunBlockOffset)
6843 			{
6844 				if ((blockOffset + len) < (iRunBlockOffset + iRunLength))
6845 				{
6846 					// the deleted section is entirely within this run
6847 					if(pRun->getType()== FPRUN_DIRECTIONMARKER)
6848 					{
6849 						if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6850 						{
6851 							pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6852 						}
6853 
6854 						if(pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6855 						{
6856 							pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6857 						}
6858 					}
6859 					else if(pRun->getType()== FPRUN_TEXT)
6860 					{
6861 						// there should always be something left of
6862 						// this run
6863 						pTR_del1 = static_cast<fp_TextRun*>(pRun);
6864 
6865 						if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6866 						{
6867 							pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6868 						}
6869 
6870 						if(pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6871 						{
6872 							pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6873 						}
6874 					}
6875 
6876 					//pRun->setLength(iRunLength - len);
6877 					pRun->updateOnDelete(blockOffset - iRunBlockOffset, len);
6878 					UT_ASSERT((pRun->getLength() == 0) || (pRun->getType() == FPRUN_TEXT)); // only textual runs could have a partial deletion
6879 				}
6880 				else
6881 				{
6882 					// the deleted section crosses over the end of the
6883 					// run, but not the start; it can however, lead to
6884 					// deletion of an entire run
6885 					UT_ASSERT(iRunBlockOffset + iRunLength - blockOffset > 0);
6886 
6887 					if(pRun->getType()== FPRUN_DIRECTIONMARKER)
6888 					{
6889 						if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6890 						{
6891 							pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6892 						}
6893 
6894 						if(pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6895 						{
6896 							pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6897 						}
6898 					}
6899 					else if(pRun->getType()== FPRUN_TEXT)
6900 					{
6901 						// if the block offset is same as the run
6902 						// offset and deleted length is greater or
6903 						// equal to the run length, this whole run is
6904 						// going and we must do no further processing
6905 						// on it ...
6906 						if(!((iRunBlockOffset == blockOffset) && (iRunLength <= len)))
6907 							pTR_del1 = static_cast<fp_TextRun*>(pRun);
6908 
6909 						if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6910 						{
6911 							pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6912 						}
6913 
6914 						if(pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6915 						{
6916 							pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6917 						}
6918 					}
6919 
6920 					//pRun->setLength(iRunLength - iDeleted);
6921 					pRun->updateOnDelete(blockOffset - iRunBlockOffset, len);
6922 					UT_ASSERT((pRun->getLength() == 0) || (pRun->getType() == FPRUN_TEXT)); // only textual runs could have a partial deletion
6923 				}
6924 			}
6925 			else
6926 			{
6927 				// the deleted section crosses over the start of the
6928 				// run and possibly also the end; unless this is the
6929 				// first run in the block, then we have already deleted
6930 				// something
6931 				if(pRun->getType()== FPRUN_DIRECTIONMARKER)
6932 				{
6933 					if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6934 					{
6935 						pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6936 					}
6937 
6938 					if(pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6939 					{
6940 						pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6941 					}
6942 				}
6943 				else if(pRun->getType()== FPRUN_TEXT)
6944 				{
6945 					if(!pTR_del1 && pRun->getPrevRun() && pRun->getPrevRun()->getType()== FPRUN_TEXT)
6946 					{
6947 						pTR_prev = static_cast<fp_TextRun*>(pRun->getPrevRun());
6948 					}
6949 
6950 					if(pRun->getNextRun() && pRun->getNextRun()->getType()== FPRUN_TEXT)
6951 					{
6952 						pTR_next = static_cast<fp_TextRun*>(pRun->getNextRun());
6953 					}
6954 
6955 				}
6956 
6957 				if ((blockOffset + len) < (iRunBlockOffset + iRunLength))
6958 				{
6959 					if(pTR_del1)
6960 					{
6961 						pTR_del2 = static_cast<fp_TextRun*>(pRun);
6962 					}
6963 					else
6964 					{
6965 						pTR_del1 = static_cast<fp_TextRun*>(pRun);
6966 					}
6967 
6968 					int iDeleted = blockOffset + len - iRunBlockOffset;
6969 					UT_ASSERT(iDeleted > 0);
6970 					pRun->setBlockOffset(iRunBlockOffset - (len - iDeleted));
6971 					//pRun->setLength(iRunLength - iDeleted);
6972 					pRun->updateOnDelete(0, iDeleted);
6973 					UT_ASSERT((pRun->getLength() == 0) || (pRun->getType() == FPRUN_TEXT)); // only textual runs could have a partial deletion
6974 				}
6975 				else
6976 				{
6977 					/* the deletion spans the entire run. time to delete it */
6978 					//pRun->setLength(0);
6979 					pRun->updateOnDelete(0, iRunLength);
6980 				}
6981 			}
6982 
6983 			if ((pRun->getLength() == 0) && (pRun->getType() != FPRUN_FMTMARK))
6984 			{
6985 				// Remove Run from line
6986 				// first, however, make sure that if this is our
6987 				// pTR_next run, we get the run after it in its place
6988 				if(pTR_next == pRun)
6989 				{
6990 					if(pRun->getNextRun() && pRun->getNextRun()->getType() == FPRUN_TEXT)
6991 					{
6992 						pTR_next =  static_cast<fp_TextRun*>(pRun->getNextRun());
6993 					}
6994 					else
6995 					{
6996 						pTR_next = NULL;
6997 					}
6998 				}
6999 
7000 				fp_Line* pLine = pRun->getLine();
7001 				if(pLine)
7002 				{
7003 					pLine->removeRun(pRun, true);
7004 				}
7005 				// Unlink Run and delete it
7006 				if (m_pFirstRun == pRun)
7007 				{
7008 					m_pFirstRun = pRun->getNextRun();
7009 				}
7010 				pRun->unlinkFromRunList();
7011 
7012 				// make sure that we do not do any bidi
7013 				// post-processing on the delete run ...
7014 				if(pTR_del1 == pRun)
7015 					pTR_del1 = NULL;
7016 
7017 				if(pTR_del2 == pRun)
7018 					pTR_del2 = NULL;
7019 
7020 				if(pTR_prev == pRun)
7021 					pTR_prev = NULL;
7022 
7023 				DELETEP(pRun);
7024 
7025 				if (!m_pFirstRun)
7026 				{
7027 					// When deleting content in a block, the EOP Run
7028 					// should always remain.
7029 					UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
7030 					_insertEndOfParagraphRun();
7031 				}
7032 			}
7033 		}
7034 
7035 		pRun = pNextRun;
7036 	}
7037 
7038 	// now that we have done the deleting, we have to do some bidi
7039 	// post processing, since the deletion might have seriously
7040 	// impacted the visual order of the line; we have to break all
7041 	// the text runs affected by this, plus the run before and after,
7042 	// so that the bidi algorithm can be properly applied
7043 	if(pTR_del1)
7044 		pTR_del1->breakMeAtDirBoundaries(UT_BIDI_IGNORE);
7045 
7046 	if(pTR_del2)
7047 		pTR_del2->breakMeAtDirBoundaries(UT_BIDI_IGNORE);
7048 
7049 	if(pTR_prev)
7050 		pTR_prev->breakMeAtDirBoundaries(UT_BIDI_IGNORE);
7051 
7052 	if(pTR_next)
7053 		pTR_next->breakMeAtDirBoundaries(UT_BIDI_IGNORE);
7054 
7055 	_assertRunListIntegrity();
7056 
7057 	return true;
7058 }
7059 
doclistener_deleteSpan(const PX_ChangeRecord_Span * pcrs)7060 bool fl_BlockLayout::doclistener_deleteSpan(const PX_ChangeRecord_Span * pcrs)
7061 {
7062 	UT_return_val_if_fail( m_pLayout, false );
7063 	_assertRunListIntegrity();
7064 
7065 	UT_ASSERT(pcrs->getType()==PX_ChangeRecord::PXT_DeleteSpan);
7066 
7067 	PT_BlockOffset blockOffset = pcrs->getBlockOffset();
7068 	UT_uint32 len = pcrs->getLength();
7069 	UT_ASSERT(len>0);
7070 	xxx_UT_DEBUGMSG(("fl_BlockLayout:: deleteSpan offset %d len %d \n",blockOffset,len));
7071 	_delete(blockOffset, len);
7072 
7073 #ifdef ENABLE_SPELL
7074 	m_pSpellSquiggles->textDeleted(blockOffset, len);
7075 	m_pGrammarSquiggles->textDeleted(blockOffset, len);
7076 	xxx_UT_DEBUGMSG(("Set pending block for grammar - deleteSpan \n"));
7077 	m_pLayout->setPendingBlockForGrammar(this);
7078 #endif
7079 
7080 	FV_View* pView = getView();
7081 	if (pView && (pView->isActive() || pView->isPreview()))
7082 	{
7083 		pView->_resetSelection();
7084 		pView->_setPoint(pcrs->getPosition());
7085 	}
7086 	else if(pView && pView->getPoint() > pcrs->getPosition())
7087 	{
7088 		if(pView->getPoint() <= pcrs->getPosition() + len)
7089 			pView->_setPoint(pcrs->getPosition());
7090 		else pView->_setPoint(pView->getPoint() - len);
7091 	}
7092 	if(pView)
7093 		pView->updateCarets(pcrs->getPosition(),-len);
7094 
7095 	_assertRunListIntegrity();
7096 	m_iNeedsReformat = blockOffset;
7097 	format();
7098 	updateEnclosingBlockIfNeeded();
7099 	//
7100 	// OK Now do the deleteSpan for any TOC's that shadow this block.
7101 	//
7102 	if(!isNotTOCable() && !m_bIsTOC && m_bStyleInTOC)
7103 	{
7104 		UT_GenericVector<fl_BlockLayout *> vecBlocksInTOCs;
7105 		if( m_pLayout->getMatchingBlocksFromTOCs(this, &vecBlocksInTOCs))
7106 		{
7107 			UT_sint32 i = 0;
7108 			for(i=0; i<vecBlocksInTOCs.getItemCount();i++)
7109 			{
7110 				fl_BlockLayout * pBL = vecBlocksInTOCs.getNthItem(i);
7111 				pBL->doclistener_deleteSpan(pcrs);
7112 			}
7113 		}
7114 		else
7115 		{
7116 			m_bStyleInTOC = false;
7117 		}
7118 	}
7119 
7120 	return true;
7121 }
7122 
7123 /*!
7124   Change runs in the given span
7125   \param pcrsc Specifies the span
7126 
7127   This function makes all fp_Run objects within the given span lookup
7128   new properties and recalculate their width. Runs at the ends of the
7129   span that extend over the span border will be split, so runs fall
7130   entirely inside or outside of the span.
7131 */
doclistener_changeSpan(const PX_ChangeRecord_SpanChange * pcrsc)7132 bool fl_BlockLayout::doclistener_changeSpan(const PX_ChangeRecord_SpanChange * pcrsc)
7133 {
7134 	_assertRunListIntegrity();
7135 
7136 	UT_ASSERT(pcrsc->getType()==PX_ChangeRecord::PXT_ChangeSpan);
7137 
7138 	PT_BlockOffset blockOffset = pcrsc->getBlockOffset();
7139 	UT_uint32 len = pcrsc->getLength();
7140 	UT_ASSERT(len > 0);
7141 	UT_GenericVector<fp_Line *> vecLines;
7142 	vecLines.clear();
7143 	// First look for the first run inside the span
7144 	fp_Run* pRun = m_pFirstRun;
7145 	fp_Run* pPrevRun = NULL;
7146 	while (pRun && pRun->getBlockOffset() < blockOffset)
7147 	{
7148 		pPrevRun = pRun;
7149 		pRun = pRun->getNextRun();
7150 	}
7151 
7152 	// If pRun is now at the blockOffset, the span falls on an
7153 	// existing separation between runs. If not, we have to split the
7154 	// run (pPrevRun) which is extending over the border.
7155 	if (!pRun || (pRun->getBlockOffset() != blockOffset))
7156 	{
7157 		// Need to split previous Run.
7158 		// Note: That should be a fp_TextRun. If not, we'll keep going
7159 		// using the first run fully inside the span - but keep the
7160 		// assertion for alert in debug builds.
7161 		UT_return_val_if_fail(pPrevRun,false);
7162 		UT_ASSERT(FPRUN_TEXT == pPrevRun->getType());
7163 		if (FPRUN_TEXT == pPrevRun->getType())
7164 		{
7165 			fp_TextRun* pTextRun = static_cast<fp_TextRun*>(pPrevRun);
7166 			pTextRun->split(blockOffset,0);
7167 		}
7168 		pRun = pPrevRun->getNextRun();
7169 	}
7170 
7171 	// When we get here, we have a clean separation on the left
7172 	// between what's outside and what's inside of the span. pRun is
7173 	// the first run inside the span.
7174 	UT_ASSERT(!pRun || (blockOffset == pRun->getBlockOffset()));
7175 	// Now start forcing the runs to update
7176 	while (pRun)
7177 	{
7178 		// If the run is on the right of the span, we're done
7179 		if ((pRun->getBlockOffset() >= (blockOffset + len)))
7180 			break;
7181 
7182 		// If the run extends beyond the span, split it.
7183 		if ((pRun->getBlockOffset() + pRun->getLength()) > (blockOffset + len))
7184 		{
7185 			// Note: That should be a fp_TextRun. If not, we'll just
7186 			// have to update the entire run - but keep the assertion
7187 			// for alert in debug builds.
7188 			UT_ASSERT(FPRUN_TEXT == pRun->getType());
7189 			if (FPRUN_TEXT == pRun->getType())
7190 			{
7191 				fp_TextRun* pTextRun = static_cast<fp_TextRun*>(pRun);
7192 				pTextRun->split(blockOffset+len,0);
7193 			}
7194 		}
7195 
7196 		// FIXME:jskov Here we want to call a changeSpanMember
7197 		// function in the Run which decides how to behave. That way
7198 		// we don't forget new Run types as they get added, and
7199 		// show-paragraphs mode can be handled properly.
7200 
7201 		// Make the run update its properties and recalculate width as
7202 		// necessary.
7203 		if (pRun->getType() == FPRUN_TEXT)
7204 		{
7205 			fp_TextRun* pTextRun = static_cast<fp_TextRun*>(pRun);
7206 			pTextRun->lookupProperties();
7207 			// I moved markWidthDirty() inside fp_TextRun::_lookupProperties(),
7208 			// since there we are in proper position to determine if the
7209 			// width needs redoing. Tomas, Nov 28, 2003
7210 			// pTextRun->markWidthDirty();
7211 		}
7212 		else if (pRun->getType() == FPRUN_TAB)
7213 		{
7214 			pRun->lookupProperties();
7215 		}
7216 		else if (pRun->getType() == FPRUN_IMAGE)
7217 		{
7218 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
7219 		}
7220 		// TODO: do we need to call lookupProperties for other run types.
7221 		fp_Line * pLine = pRun->getLine();
7222 		if((pLine!= NULL) && (vecLines.findItem(pLine) < 0))
7223 		{
7224 			vecLines.addItem(pLine);
7225 		}
7226 		pRun = pRun->getNextRun();
7227 	}
7228 	//
7229 	// maybe able to remove this once the rest of bug 5240 is fixed.
7230 	//
7231 	UT_sint32 i =0;
7232    	for(i=0; i< vecLines.getItemCount(); i++)
7233 	{
7234 		fp_Line * pLine = vecLines.getNthItem(i);
7235 		pLine->clearScreen();
7236 	}
7237 	m_iNeedsReformat = blockOffset;
7238     format();
7239 	updateEnclosingBlockIfNeeded();
7240 	_assertRunListIntegrity();
7241 
7242 #ifdef ENABLE_SPELL
7243 	// need to handle the case where we have a revisions based delete
7244 	if(pcrsc->isRevisionDelete())
7245 	{
7246 		m_pSpellSquiggles->textRevised(blockOffset, 0);
7247 		m_pGrammarSquiggles->textRevised(blockOffset, 0);
7248 	}
7249 #endif
7250 
7251 	return true;
7252 }
7253 
7254 /*!
7255   Delete strux Run
7256   \param pcrx Change record for the operation
7257   \return true if succeeded, false if not
7258   This function will merge the content of this strux to the previous
7259   strux.
7260 */
7261 bool
doclistener_deleteStrux(const PX_ChangeRecord_Strux * pcrx)7262 fl_BlockLayout::doclistener_deleteStrux(const PX_ChangeRecord_Strux* pcrx)
7263 {
7264 	UT_DEBUGMSG(("doclistener_deleteStrux\n"));
7265 
7266 	_assertRunListIntegrity();
7267 
7268 	UT_ASSERT(pcrx->getType()==PX_ChangeRecord::PXT_DeleteStrux);
7269 	UT_ASSERT(pcrx->getStruxType()==PTX_Block);
7270 
7271 	// First see if the block in a list. If so remove it!
7272 	if(m_pAutoNum != NULL)
7273 	{
7274 		if( m_pAutoNum->isItem(getStruxDocHandle()) == true)
7275 		{
7276 			// This nifty method handles all the details
7277 			m_pAutoNum->removeItem(getStruxDocHandle());
7278 		}
7279 	}
7280 //
7281 // Do this before all the required info is deleted.
7282 //
7283 	updateEnclosingBlockIfNeeded();
7284 	bool isInFrame = (getSectionLayout()->getContainerType() == FL_CONTAINER_FRAME);
7285 	fp_Container * pCon = getFirstContainer();
7286 	if(!isInFrame)
7287 	{
7288 		if(pCon)
7289 		{
7290 			fp_Page * pPage = pCon->getPage();
7291 			getDocSectionLayout()->setNeedsSectionBreak(true,pPage);
7292 		}
7293 		else
7294 		{
7295 			getDocSectionLayout()->setNeedsSectionBreak(true,NULL);
7296 		}
7297 	}
7298 	if(getPrev())
7299 	{
7300 		getPrev()->setNeedsReformat(this);
7301 		getPrev()->setNeedsRedraw();
7302 	}
7303 	setNeedsReformat(this);
7304 	// Erase the old version.  Or this what I added when adding the
7305 	// EOP stuff. Only, I don't remember why I did it, and it's wrong:
7306 	// the strux is deleted only after its content has been deleted -
7307 	// so the call might try to clear empty lines. jskov 2001.04.23
7308 
7309 	// Sevior put this back 2001.6.3. I don't understand why there was ever any
7310 	// question about it's neccessity.
7311 
7312 	clearScreen(m_pLayout->getGraphics());
7313 
7314 	// If there is a previous strux, we merge the Runs from this strux
7315 	// into it - including the EOP Run, so delete that in the previous
7316 	// strux.
7317 	// If there is no previous strux (this being the first strux in
7318 	// the document) this will be empty - but the EOP Run needs to be
7319 	// deleted.
7320 	//
7321 	// This is not exactly the case; for example the first block in the footnote section
7322 	// has not previous, yet it is not empty -- it contains at least the footnote reference.
7323 
7324 	fp_Line* pLastLine = NULL;
7325 	fl_BlockLayout * pPrevBL = NULL;
7326 
7327 	fl_ContainerLayout *pCL = getPrev();
7328 	while(pCL && pCL->getContainerType() != FL_CONTAINER_BLOCK)
7329 	{
7330 //
7331 // Attach to the block before the other container type ,
7332 // because this block has to get merged with it
7333 //
7334 		pCL = pCL->getPrev();
7335 	}
7336 
7337 	// this is safe cast because we either have block or NULL
7338 	pPrevBL = static_cast<fl_BlockLayout*>(pCL);
7339 
7340 	//
7341 	// Deal with embedded containers if any in this block.
7342 	//
7343 	shuffleEmbeddedIfNeeded(pPrevBL, 0);
7344 	//
7345 	// The idea here is to append the runs of the deleted block, if
7346 	// any, at the end of the previous block. We must make sure to take
7347 	// of embedded footnotes/endnotes. We need to calculate the offset
7348 	// before we deletes the EOP run. The offset may not be contiguous
7349 	// because of embedded footnotes/endnotes
7350 	//
7351 	UT_uint32 offset = 0;
7352 	if (pPrevBL)
7353 	{
7354 		// Find the EOP Run.
7355 		pLastLine = static_cast<fp_Line *>(pPrevBL->getLastContainer());
7356 		fp_Run* pNukeRun = pPrevBL->m_pFirstRun;
7357 		fp_Run * pPrevRun = pPrevBL->m_pFirstRun;
7358 		while(pNukeRun->getNextRun() != NULL)
7359 		{
7360 			pPrevRun = pNukeRun;
7361 			UT_ASSERT(FPRUN_ENDOFPARAGRAPH != pPrevRun->getType());
7362 			pNukeRun  = pPrevRun->getNextRun();
7363 		}
7364 		UT_ASSERT(FPRUN_ENDOFPARAGRAPH == pNukeRun->getType());
7365 		//
7366 		// The idea here is to append the runs of the deleted block, if
7367 		// any, at the end of the previous block. We must make sure to take
7368 		// account of embedded footnotes/endnotes.
7369 		// We need to calculate the offset
7370 		// before we delete the EOP run.
7371 		//
7372 		if(FPRUN_ENDOFPARAGRAPH == pNukeRun->getType())
7373 		{
7374 			offset = pNukeRun->getBlockOffset();
7375 		}
7376 		else
7377 		{
7378 			offset =  pNukeRun->getBlockOffset() + pNukeRun->getLength();
7379 		}
7380 
7381 		// Detach from the line
7382 		fp_Line* pLine = pNukeRun->getLine();
7383 		UT_ASSERT(pLine && pLine == pLastLine);
7384 		if(pLine)
7385 		{
7386 			pLine->removeRun(pNukeRun);
7387 		}
7388 		// Unlink and delete it
7389 		if (pPrevRun && (pPrevRun != pNukeRun))
7390 		{
7391 			pPrevRun->setNextRun(NULL);
7392 		}
7393 		else
7394 		{
7395 			pPrevBL->m_pFirstRun = NULL;
7396 		}
7397 		delete pNukeRun;
7398 	}
7399 	else
7400 	{
7401 		// Delete end-of-paragraph Run in this strux
7402 		UT_ASSERT(m_pFirstRun
7403 				  && (FPRUN_ENDOFPARAGRAPH == m_pFirstRun->getType()));
7404 
7405 		fp_Run* pNukeRun = m_pFirstRun;
7406 
7407 		// Detach from the line
7408 		fp_Line* pLine = pNukeRun->getLine();
7409 		UT_ASSERT(pLine);
7410 		if(pLine)
7411 		{
7412 			pLine->removeRun(pNukeRun);
7413 		}
7414 
7415 		// Unlink and delete it
7416 		m_pFirstRun = NULL;
7417 		delete pNukeRun;
7418 
7419 	}
7420 
7421 	// We use the offset we calculated earlier.
7422 
7423 	if (m_pFirstRun)
7424 	{
7425 		// Figure out where the merge point is
7426 		fp_Run * pRun = pPrevBL->m_pFirstRun;
7427 		fp_Run * pLastRun = NULL;
7428 		while (pRun)
7429 		{
7430 			pLastRun = pRun;
7431 			pRun = pRun->getNextRun();
7432 		}
7433 		// Link them together
7434 		if (pLastRun)
7435 		{
7436 			pLastRun->setNextRun(m_pFirstRun);
7437 			if(m_pFirstRun)
7438 			{
7439 				m_pFirstRun->setPrevRun(pLastRun);
7440 			}
7441 		}
7442 		else
7443 		{
7444 			pPrevBL->m_pFirstRun = m_pFirstRun;
7445 		}
7446 		UT_DEBUGMSG(("deleteStrux: offset = %d \n",offset));
7447 
7448 		// Tell all the new runs where they live
7449 		pRun = m_pFirstRun;
7450 		while (pRun)
7451 		{
7452 			pRun->setBlockOffset(pRun->getBlockOffset() + offset);
7453 			pRun->setBlock(pPrevBL);
7454 
7455 			// Detach from their line
7456 			fp_Line* pLine = pRun->getLine();
7457 			UT_ASSERT(pLine);
7458 			if(pLine)
7459 			{
7460 				pLine->removeRun(pRun);
7461 			}
7462 			if(pLastLine)
7463 			{
7464 				pLastLine->addRun(pRun);
7465 			}
7466 			pRun = pRun->getNextRun();
7467 		}
7468 
7469 		// Runs are no longer attached to this block
7470 		m_pFirstRun = NULL;
7471 	}
7472 	//
7473 	// Transfer any frames from this block to the previous block in the
7474 	// the document.
7475 	//
7476 	fl_BlockLayout * pPrevForFrames = pPrevBL;
7477 	if(pPrevForFrames == NULL)
7478 	{
7479 		pPrevForFrames = getPrevBlockInDocument();
7480 	}
7481 	if(pPrevForFrames)
7482 	{
7483 		if(getNumFrames() > 0)
7484 		{
7485 			fl_FrameLayout * pFrame = NULL;
7486 			UT_sint32 i = 0;
7487 			UT_sint32 count = getNumFrames();
7488 			for(i= 0; i < count; i++)
7489 		  	{
7490 				pFrame = getNthFrameLayout(0);
7491 				removeFrame(pFrame);
7492 				pPrevForFrames->addFrame(pFrame);
7493 			}
7494 		}
7495 	}
7496 	// Get rid of everything else about the block
7497 	purgeLayout();
7498 	//
7499 	// Update it's TOC entry
7500 	//
7501 	if(m_pLayout->isBlockInTOC(this))
7502 	{
7503 		m_pLayout->removeBlockFromTOC(this);
7504 	}
7505 	// Unlink this block
7506 	if(getNext() && getNext()->getNext() &&  getNext()->getNext()->getContainerType() == FL_CONTAINER_TOC)
7507 		{
7508 			xxx_UT_DEBUGMSG(("Next container is TOC \n"));
7509 		}
7510 	fl_SectionLayout* pSL = static_cast<fl_SectionLayout *>(myContainingLayout());
7511 	UT_ASSERT(pSL);
7512 	if(pSL)
7513 	{
7514 		pSL->remove(this);
7515 	}
7516 	if (pPrevBL)
7517 	{
7518 //
7519 // Now fix up the previous block. Calling this format fixes bug 2702
7520 //
7521 		fp_Run * pPrevBLRun =pPrevBL->getFirstRun();
7522 		while(pPrevBLRun)
7523 		{
7524 			pPrevBLRun->lookupProperties();
7525 			pPrevBLRun = pPrevBLRun->getNextRun();
7526 		}
7527 		pPrevBL->format();
7528 
7529 #ifdef ENABLE_SPELL
7530 		// This call will dequeue the block from background checking
7531 		// if necessary
7532 		m_pSpellSquiggles->join(offset, pPrevBL);
7533 		m_pGrammarSquiggles->join(offset, pPrevBL);
7534 #endif
7535 		pPrevBL->setNeedsReformat(pPrevBL);
7536 		//
7537 		// Update if it's TOC entry by removing then restoring
7538 		//
7539 		if(m_pLayout->isBlockInTOC(pPrevBL))
7540 		{
7541 			m_pLayout->removeBlockFromTOC(pPrevBL);
7542 			m_pLayout->addOrRemoveBlockFromTOC(pPrevBL);
7543 		}
7544 	}
7545 	else
7546 	{
7547 #ifdef ENABLE_SPELL
7548 		// In case we've never checked this one
7549 		m_pLayout->dequeueBlockForBackgroundCheck(this);
7550 #endif
7551 	}
7552 	if(pSL)
7553 	{
7554 		FV_View* pView = pSL->getDocLayout()->getView();
7555 		if (pView->isHdrFtrEdit() && (!pView->getEditShadow() ||
7556 									  !pView->getEditShadow()->getLastLayout()))
7557 			pView->clearHdrFtrEdit();
7558 
7559 		if (pView && (pView->isActive() || pView->isPreview()))
7560 		{
7561 			pView->_setPoint(pcrx->getPosition());
7562 		}
7563 		else if(pView && pView->getPoint() > pcrx->getPosition())
7564 		{
7565 			pView->_setPoint(pView->getPoint() - 1);
7566 		}
7567 		if(pView)
7568 			pView->updateCarets(pcrx->getPosition(),-1);
7569 		_assertRunListIntegrity();
7570 	}
7571 
7572 	delete this;			// FIXME: whoa!  this construct is VERY dangerous.
7573 
7574 	return true;
7575 }
7576 
doclistener_changeStrux(const PX_ChangeRecord_StruxChange * pcrxc)7577 bool fl_BlockLayout::doclistener_changeStrux(const PX_ChangeRecord_StruxChange * pcrxc)
7578 {
7579 
7580 	_assertRunListIntegrity();
7581 
7582 	UT_ASSERT(pcrxc->getType()==PX_ChangeRecord::PXT_ChangeStrux);
7583 
7584 	// Check if the block has borders. If this changes we might have to update other blocks
7585 
7586 	bool b_bordersMergedWithPrev = false, b_bordersMergedWithNext = false;
7587 	if (hasBorders())
7588 	{
7589 		b_bordersMergedWithNext = canMergeBordersWithNext();
7590 		b_bordersMergedWithPrev = canMergeBordersWithPrev();
7591 	}
7592 
7593 	// erase the old version
7594 	if(!isHdrFtr())
7595 	{
7596 		clearScreen(m_pLayout->getGraphics());
7597 	}
7598 	if(getPrev())
7599 	{
7600 		getPrev()->setNeedsReformat(getPrev());
7601 	}
7602 	collapse();
7603 	setAttrPropIndex(pcrxc->getIndexAP());
7604 	xxx_UT_DEBUGMSG(("SEVIOR: In changeStrux in fl_BlockLayout %x \n",this));
7605 //
7606 // Not sure if we'll ever need this. We don't need this now I'll comment it out.
7607 //	const gchar * szOldStyle = m_szStyle;
7608 	UT_BidiCharType iOldDomDirection = m_iDomDirection;
7609 
7610 	lookupProperties();
7611 	xxx_UT_DEBUGMSG(("SEVIOR: Old Style = %s new style = %s \n",szOldStyle,m_szStyle));
7612 //
7613 // Not sure why we need this IF - Sevior
7614 //	if ((szOldStyle != m_szStyle) &&
7615 //		(!szOldStyle || !m_szStyle || !!(strcmp(szOldStyle, m_szStyle))))
7616 	{
7617 		/*
7618 		  A block-level style change means that we also need to update
7619 		  all the run-level properties.
7620 		*/
7621 		fp_Run* pRun = m_pFirstRun;
7622 
7623 		xxx_UT_DEBUGMSG(("SEVIOR: Doing a style change \n"));
7624 		while (pRun)
7625 		{
7626 			pRun->lookupProperties();
7627 			pRun->recalcWidth();
7628 
7629 			pRun = pRun->getNextRun();
7630 		}
7631 	}
7632 
7633 	fp_Line* pLine = static_cast<fp_Line *>(getFirstContainer());
7634 	while (pLine)
7635 	{
7636 		pLine->recalcHeight();	// line-height
7637 		pLine->recalcMaxWidth();
7638 
7639 		if(m_iDomDirection != iOldDomDirection)
7640 		{
7641 			xxx_UT_DEBUGMSG(("block listener: change of direction\n"));
7642 			pLine->setMapOfRunsDirty();
7643 		}
7644 
7645 		pLine = static_cast<fp_Line *>(pLine->getNext());
7646 	}
7647 
7648 	format();
7649 #if 0
7650 //	This was...
7651 	if(m_pDoc->isDoingPaste())
7652 	{
7653 		format();
7654 	}
7655 
7656 
7657 	// if we were on screen we need to reformat immediately, since the ruler will be
7658 	// calling the findPointCoords() chain and if we are collapsed (as
7659 	// we are now) and contain the point, it will fail
7660 	if(bWasOnScreen)
7661 		format();
7662 	else
7663 		setNeedsReformat(this);
7664 #endif
7665 	updateEnclosingBlockIfNeeded();
7666 	//
7667 	// Need this to find where to break section in the document.
7668 	//
7669 	fl_ContainerLayout * pPrevCL = getPrevBlockInDocument();
7670 	fp_Page * pPrevP = NULL;
7671 	if(pPrevCL)
7672 	{
7673 		fp_Container * pPrevCon = pPrevCL->getFirstContainer();
7674 		if(pPrevCon)
7675 		{
7676 			pPrevP = pPrevCon->getPage();
7677 		}
7678 	}
7679 	getDocSectionLayout()->setNeedsSectionBreak(true,pPrevP);
7680 
7681 	_assertRunListIntegrity();
7682 
7683 	if (hasBorders() || b_bordersMergedWithPrev || b_bordersMergedWithNext)
7684 	{
7685 		bool b_bordersMergedWithNextUpdate=canMergeBordersWithNext();
7686 		bool b_bordersMergedWithPrevUpdate=canMergeBordersWithPrev();
7687 		if ((b_bordersMergedWithPrev && !b_bordersMergedWithPrevUpdate) ||
7688 			(!b_bordersMergedWithPrev && b_bordersMergedWithPrevUpdate))
7689 		{
7690 			fl_BlockLayout * pPrev = static_cast<fl_BlockLayout *>(getPrev());
7691 			if (pPrev)
7692 			{
7693 				pPrev->setLineHeightBlockWithBorders(-1);
7694 			}
7695 		}
7696 		if ((b_bordersMergedWithNext && !b_bordersMergedWithNextUpdate) ||
7697 			(!b_bordersMergedWithNext && b_bordersMergedWithNextUpdate))
7698 		{
7699 			fl_BlockLayout * pNext = static_cast<fl_BlockLayout *>(getNext());
7700 			if (pNext)
7701 			{
7702 				pNext->setLineHeightBlockWithBorders(1);
7703 			}
7704 		}
7705 	}
7706 
7707 
7708 
7709 	return true;
7710 }
7711 
doclistener_insertFirstBlock(const PX_ChangeRecord_Strux * pcrx,pf_Frag_Strux * sdh,PL_ListenerId lid,void (* pfnBindHandles)(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew))7712 bool fl_BlockLayout::doclistener_insertFirstBlock(const PX_ChangeRecord_Strux * pcrx,
7713 												  pf_Frag_Strux* sdh,
7714 												  PL_ListenerId lid,
7715 												  void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
7716 																		  PL_ListenerId lid,
7717 																		  fl_ContainerLayout* sfhNew))
7718 {
7719 	//	Exchange handles with the piece table
7720 	fl_ContainerLayout* sfhNew = this;
7721 	//
7722 	// Don't bind to shadows!
7723 	//
7724 	if(pfnBindHandles)
7725 	{
7726 		pfnBindHandles(sdh,lid,sfhNew);
7727 	}
7728 	setNeedsReformat(this);
7729 	updateEnclosingBlockIfNeeded();
7730 
7731 	FV_View* pView = getView();
7732 	if (pView && (pView->isActive() || pView->isPreview()))
7733 		pView->_setPoint(pcrx->getPosition());
7734 	else if (pView && ((pView->getPoint() == 0) || pView->getPoint() > pcrx->getPosition()) ) pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
7735 	if(pView)
7736 		pView->updateCarets(pcrx->getPosition(),1);
7737 
7738 	// Run list should be valid now.
7739 	_assertRunListIntegrity();
7740 
7741 	return true;
7742 }
doclistener_insertBlock(const PX_ChangeRecord_Strux * pcrx,pf_Frag_Strux * sdh,PL_ListenerId lid,void (* pfnBindHandles)(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew))7743 bool fl_BlockLayout::doclistener_insertBlock(const PX_ChangeRecord_Strux * pcrx,
7744 											 pf_Frag_Strux* sdh,
7745 											 PL_ListenerId lid,
7746 											 void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
7747 																	 PL_ListenerId lid,
7748 																	 fl_ContainerLayout* sfhNew))
7749 {
7750 	_assertRunListIntegrity();
7751 
7752 	UT_ASSERT(pcrx->getType()==PX_ChangeRecord::PXT_InsertStrux);
7753 	UT_ASSERT(pcrx->getStruxType()==PTX_Block);
7754 
7755 	fl_SectionLayout* pSL = static_cast<fl_SectionLayout *>(myContainingLayout());
7756 	UT_return_val_if_fail(pSL,false);
7757 	fl_BlockLayout* pNewBL = static_cast<fl_BlockLayout *>(pSL->insert(sdh, this, pcrx->getIndexAP(),FL_CONTAINER_BLOCK));
7758 	if(isHdrFtr())
7759 		pNewBL->setHdrFtr();
7760 	if (!pNewBL)
7761 	{
7762 		UT_DEBUGMSG(("no memory for BlockLayout\n"));
7763 		return false;
7764 	}
7765 	xxx_UT_DEBUGMSG(("Inserting block %x it's sectionLayout type is %d \n",pNewBL,pNewBL->getSectionLayout()->getContainerType()));
7766 	//xxx_UT_DEBUGMSG(("Inserting block at pos %d \n",getPosition(true)));
7767 	//xxx_UT_DEBUGMSG(("shd of strux block = %x of new block is %x \n",getStruxDocHandle(),pNewBL->getStruxDocHandle()));
7768 	// The newly returned block will contain a line and EOP. Delete those
7769 	// since the code below expects an empty block
7770 	pNewBL->_purgeEndOfParagraphRun();
7771 
7772 	// Must call the bind function to complete the exchange
7773 	// of handles with the document (piece table) *** before ***
7774 	// anything tries to call down into the document (like all
7775 	// of the view listeners).
7776 
7777 	fl_ContainerLayout* sfhNew = pNewBL;
7778 	//
7779 	// Don't Bind to shadows
7780 	//
7781 	if(pfnBindHandles)
7782 	{
7783 		pfnBindHandles(sdh,lid,sfhNew);
7784 	}
7785 
7786 	/*
7787 	  The idea here is to divide the runs of the existing block
7788 	  into two equivalence classes.  This may involve
7789 	  splitting an existing run.
7790 
7791 	  All runs and lines remaining in the existing block are
7792 	  fine, although the last run should be redrawn.
7793 
7794 	  All runs in the new block need their offsets fixed, and
7795 	  that entire block needs to be formatted from scratch.
7796 
7797 	  TODO is the above commentary still correct ??
7798 	*/
7799 
7800 	// figure out where the breakpoint is
7801 	PT_BlockOffset blockOffset = (pcrx->getPosition() - getPosition());
7802 	//
7803 	// OK Now we have to deal with any embedded containerlayout associated with
7804     // this block.
7805 	// If they are before the insert point they must be moved to be immediately
7806 	// after this block (and hence before the new block)
7807 	//
7808 	shuffleEmbeddedIfNeeded(this,blockOffset);
7809 
7810 	fp_Run* pFirstNewRun = NULL;
7811 	fp_Run* pLastRun = NULL;
7812 	fp_Run* pRun;
7813 	xxx_UT_DEBUGMSG(("BlockOffset %d \n",blockOffset));
7814 	for (pRun=m_pFirstRun; (pRun && !pFirstNewRun);
7815 		 pLastRun=pRun, pRun=pRun->getNextRun())
7816 	{
7817 		// We have passed the point. Why didn't previous Run claim to
7818 		// hold the offset? Make the best of it in non-debug
7819 		// builds. But keep the assert to get us information...
7820 		xxx_UT_DEBUGMSG(("pRun %x pRun->next %x pRun->blockOffset %d pRun->getLength %d \n",pRun,pRun->getNextRun(),pRun->getBlockOffset(),pRun->getLength()));
7821 		if (pRun->getBlockOffset() > blockOffset)
7822 		{
7823 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
7824 			pFirstNewRun = pRun;
7825 			break;
7826 		}
7827 
7828 		// FIXME: Room for optimization improvement here - always scan
7829 		// FIXME: past the point by only comparing getBlockOffset.
7830 
7831 		if (pRun->getBlockOffset() <= blockOffset &&
7832 			(pRun->getBlockOffset() + pRun->getLength()) > blockOffset)
7833 		{
7834 			// We found the Run. Now handle splitting.
7835 
7836 			if (pRun->getBlockOffset() == blockOffset)
7837 			{
7838 				// Split between this Run and the previous one
7839 				pFirstNewRun = pRun;
7840 			}
7841 			else
7842 			{
7843 				// Need to split current Run
7844 				UT_ASSERT(pRun->getType() == FPRUN_TEXT);
7845 
7846 				// split here
7847 				fp_TextRun* pTextRun = static_cast<fp_TextRun*>(pRun);
7848 				pTextRun->split(blockOffset,0);
7849 				pFirstNewRun = pRun->getNextRun();
7850 			}
7851 			break;
7852 		}
7853 	}
7854 
7855 	while(pFirstNewRun && (pFirstNewRun->getType() == FPRUN_FMTMARK))
7856 	{
7857 		// Since a FmtMark has length zero, both it and the next run
7858 		// have the same blockOffset.  We always want to be to the
7859 		// right of the FmtMark, so we take the next one.
7860 		pFirstNewRun = pFirstNewRun->getNextRun();
7861 	}
7862 	UT_sint32 iEOPOffset = -1;
7863 	if (pFirstNewRun)
7864 	{
7865 		if(pFirstNewRun->getBlockOffset() == blockOffset)
7866 		{
7867 			iEOPOffset = pFirstNewRun->getBlockOffset();
7868 		}
7869 		if (pFirstNewRun->getPrevRun())
7870 		{
7871 			// Break doubly-linked list of runs into two distinct lists.
7872 			// But remember the last Run in this block.
7873 
7874 			pLastRun = pFirstNewRun->getPrevRun();
7875 			pFirstNewRun->getPrevRun()->setNextRun(NULL);
7876 			pFirstNewRun->setPrevRun(NULL);
7877 		}
7878 		else
7879 			pLastRun = NULL;
7880 	}
7881 	// else the old value of pLastRun is what we want.
7882 
7883 	// pFirstNewRun can be NULL at this point.	It means that the
7884 	// entire set of runs in this block must remain with this block --
7885 	// and the newly created block will be empty.
7886 	//
7887 	// Also, note if pFirstNewRun == m_pFirstRun then we will be moving
7888 	// the entire set of runs to the newly created block -- and leave
7889 	// the current block empty.
7890 
7891 	// Move remaining runs to new block
7892 	pNewBL->m_pFirstRun = pFirstNewRun;
7893 
7894 	// And update their positions
7895 	for (pRun=pFirstNewRun; (pRun); pRun=pRun->getNextRun())
7896 	{
7897 		pRun->setBlockOffset(pRun->getBlockOffset() - blockOffset);
7898 		pRun->setBlock(pNewBL);
7899 		// TODO [2] the following 2 steps seem expensive considering
7900 		// TODO we already knew width information before divided the
7901 		// TODO char widths data between the two clocks.  see [1].
7902 		pRun->recalcWidth();
7903 	}
7904 
7905 	// Explicitly truncate rest of this block's layout
7906 	_truncateLayout(pFirstNewRun);
7907 
7908 	// Now make sure this block still has an EOP Run.
7909 	if (m_pFirstRun) {
7910 		UT_return_val_if_fail(pLastRun,false);
7911 		// Create a new end-of-paragraph run and add it to the block.
7912 		fp_EndOfParagraphRun* pNewRun =
7913 			new fp_EndOfParagraphRun(this,   0, 0);
7914 		pLastRun->setNextRun(pNewRun);
7915 		pNewRun->setPrevRun(pLastRun);
7916 		if(iEOPOffset < 0)
7917 		{
7918 			pNewRun->setBlockOffset(pLastRun->getBlockOffset()
7919 									+ pLastRun->getLength());
7920 		}
7921 		else
7922 		{
7923 			pNewRun->setBlockOffset(iEOPOffset);
7924 		}
7925 		if(pLastRun->getLine())
7926 			pLastRun->getLine()->addRun(pNewRun);
7927 		coalesceRuns();
7928 	}
7929 	else
7930 	{
7931 		_insertEndOfParagraphRun();
7932 	}
7933 	setNeedsReformat(this);
7934 	pNewBL->collapse(); // remove all previous lines
7935 	// Throw all the runs onto one jumbo line in the new block
7936 	pNewBL->_stuffAllRunsOnALine();
7937 	if (pNewBL->m_pFirstRun)
7938 		pNewBL->coalesceRuns();
7939 	else
7940 		pNewBL->_insertEndOfParagraphRun();
7941 	pNewBL->setNeedsReformat(pNewBL);
7942 	updateEnclosingBlockIfNeeded();
7943 
7944 	//
7945 	// Now transfer the frames of this block to the newly created one
7946 	//
7947 	if(getNumFrames() > 0)
7948 	{
7949 		FL_DocLayout *pDL = getDocLayout();
7950 		fp_Line * pLine = pLastRun->getLine();
7951 		fp_Container * pCon = pLine->getColumn();
7952 		UT_sint32 pLineX = 0;
7953 		UT_sint32 pLineY = 0;
7954 		UT_sint32 pLinePage = 0;
7955 		if (pLine && pCon)
7956 		{
7957 			pLineX = pLine->getX() + pCon->getX() +pCon->getWidth();
7958 			pLineY = pLine->getY() + pCon->getY();
7959 			pLinePage = pDL->findPage(pLine->getPage());
7960 		}
7961 		fl_FrameLayout * pFL = NULL;
7962 		fp_FrameContainer * pFrame = NULL;
7963 		bool b_evalHeightOfFirstBlock = false;
7964 		UT_sint32 extraHeight = 0;
7965 		UT_sint32 i = 0;
7966 		UT_sint32 k = 0;
7967 		UT_sint32 count = getNumFrames();
7968 		for(i= 0; i < count; i++)
7969 		{
7970 			pFL = getNthFrameLayout(k);
7971 			pFrame = static_cast <fp_FrameContainer *> (pFL->getFirstContainer());
7972 			UT_sint32 pFrameX = 0;
7973 			UT_sint32 pFrameY = 0;
7974 			UT_sint32 pFramePage = 0;
7975 			if (pFrame)
7976 			{
7977 				pFrameX = pFrame->getX();
7978 				pFrameY = pFrame->getY();
7979 				pFramePage = pDL->findPage(pFrame->getPage());
7980 			}
7981 			if (!pFrame || (pFramePage > pLinePage) || (pFrameY > pLineY) || (pFrameX > pLineX))
7982 			{
7983 				UT_DEBUGMSG(("Frame %p associated to block %p (2nd)\n",pFL,pNewBL));
7984 				removeFrame(pFL);
7985 				pNewBL->addFrame(pFL);
7986 				if((pFL->getFramePositionTo() == FL_FRAME_POSITIONED_TO_BLOCK) &&
7987 				   (!m_pDoc->isDoingTheDo()))
7988 				{
7989 					const PP_AttrProp* pAP = NULL;
7990 					const gchar * pszYPos = NULL;
7991 					double ypos = 0.;
7992 					pFL->getAP(pAP);
7993 					if(!pAP || !pAP->getProperty("ypos",pszYPos))
7994 					{
7995 						pszYPos = "0.0in";
7996 					}
7997 					if (!b_evalHeightOfFirstBlock)
7998 					{
7999 						fp_Line * ppLine = pLine;
8000 						while(ppLine)
8001 						{
8002 							extraHeight += ppLine->getHeight();
8003 							ppLine = static_cast <fp_Line *> (ppLine->getPrev());
8004 						}
8005 						fp_Line * pLastLine = static_cast <fp_Line *> (getLastContainer());
8006 						if (pLastLine)
8007 							extraHeight += pLastLine->getMarginAfter();
8008 						b_evalHeightOfFirstBlock = true;
8009 					}
8010 					ypos = UT_convertToInches(pszYPos) - double(extraHeight)/UT_LAYOUT_RESOLUTION;
8011 					UT_String sValY = UT_formatDimensionString(DIM_IN,ypos);
8012 					const gchar * frameProperties[] = {
8013 						"ypos",
8014 						sValY.c_str(),
8015 						NULL
8016 					};
8017 					PT_DocPosition posStart = pFL->getPosition(true)+1;
8018 					PT_DocPosition posEnd = posStart;
8019 					UT_DebugOnly<bool> bRet = m_pDoc->changeStruxFmt(PTC_AddFmt,posStart,posEnd,NULL,
8020 																	 frameProperties,PTX_SectionFrame);
8021 					UT_ASSERT(bRet);
8022 				}
8023 			}
8024 			else
8025 			{
8026 				UT_DEBUGMSG(("Frame %p associated to block %p (1st)\n",pFL,this));
8027 				//Frame stays in first block. Need to change the PieceTable
8028 				if(!m_pDoc->isDoingTheDo())
8029 				{
8030 					pDL->relocateFrame(pFL,this);
8031 				}
8032 				else
8033 				{
8034 					k++;
8035 					continue;
8036 				}
8037 			}
8038 		}
8039 	}
8040 
8041 
8042 #ifdef ENABLE_SPELL
8043 	// Split squiggles between this and the new block
8044 	m_pSpellSquiggles->split(blockOffset, pNewBL);
8045 	m_pGrammarSquiggles->split(blockOffset, pNewBL);
8046 	m_pLayout->setPendingBlockForGrammar(pNewBL);
8047 #endif
8048 
8049 	FV_View* pView = getView();
8050 	if (pView && (pView->isActive() || pView->isPreview()))
8051 		pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
8052 	else if(pView && pView->getPoint() > pcrx->getPosition())
8053 		pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
8054 	if(pView)
8055 		pView->updateCarets(pcrx->getPosition(),1);
8056 
8057 	_assertRunListIntegrity();
8058 	xxx_UT_DEBUGMSG(("Prev Block = %x type %d Next block = %x type %d \n",pNewBL->getPrev(),pNewBL->getContainerType(),pNewBL->getNext(),pNewBL->getContainerType()));
8059 	return true;
8060 }
8061 
8062 /*!
8063  * This method shuffles any emebedded containers in the block to be placed
8064  * after the supplied block.
8065  *
8066  * If they are before the insert point they must be moved to be immediately
8067  * after this block (and hence before the new block)
8068  */
shuffleEmbeddedIfNeeded(fl_BlockLayout * pBlock,UT_uint32 blockOffset)8069 void fl_BlockLayout::shuffleEmbeddedIfNeeded(fl_BlockLayout * pBlock, UT_uint32 blockOffset)
8070 {
8071 	if(pBlock == NULL)
8072 	{
8073 		return;
8074 	}
8075 	UT_sint32 iEmbed = 0;
8076 	bool bStop = false;
8077 	fl_ContainerLayout * pEmbedCL = NULL;
8078 	while(!bStop)
8079 	{
8080 		iEmbed = pBlock->getEmbeddedOffset(iEmbed, pEmbedCL);
8081 		if(iEmbed < 0)
8082 		{
8083 			bStop = true;
8084 			break;
8085 		}
8086 		if(pEmbedCL == NULL)
8087 		{
8088 			bStop = true;
8089 			break;
8090 		}
8091 		if((blockOffset > 0) && (iEmbed < static_cast<UT_sint32>(blockOffset)))
8092 		{
8093 			iEmbed++;
8094 			continue;
8095 		}
8096 		//
8097 		// Move pEmbedCL to be just after this block.
8098 		//
8099 		// Outer pointers
8100 		//
8101 		fl_ContainerLayout * pBLNext = pBlock->getNext();
8102 		if(pEmbedCL->getPrev() && (pEmbedCL->getPrev() != pBlock))
8103 			{
8104 				pEmbedCL->getPrev()->setNext(pEmbedCL->getNext());
8105 			}
8106 		if(pEmbedCL->getNext() && pBLNext != pEmbedCL)
8107 			{
8108 				pEmbedCL->getNext()->setPrev(pEmbedCL->getPrev());
8109 			}
8110 		//
8111 		// New pointers for EmbedCL
8112 		pEmbedCL->setPrev(static_cast<fl_ContainerLayout *>(pBlock));
8113 		if(pBLNext != pEmbedCL)
8114 			{
8115 				pEmbedCL->setNext(pBlock->getNext());
8116 			}
8117 		//
8118 		// New pointer here
8119 		if(pBlock->getNext() && (pBlock->getNext() != pEmbedCL))
8120 			{
8121 				pBlock->getNext()->setPrev(pEmbedCL);
8122 			}
8123 		pBlock->setNext(pEmbedCL);
8124 		//
8125 		// Now add in the length of the container
8126 		//
8127 		pf_Frag_Strux* sdhStart = pEmbedCL->getStruxDocHandle();
8128 		pf_Frag_Strux* sdhEnd = NULL;
8129 		if(pEmbedCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
8130 		{
8131 			getDocument()->getNextStruxOfType(sdhStart,PTX_EndFootnote, &sdhEnd);
8132 		}
8133 		else if(pEmbedCL->getContainerType() == FL_CONTAINER_ENDNOTE)
8134 		{
8135 			getDocument()->getNextStruxOfType(sdhStart,PTX_EndEndnote, &sdhEnd);
8136 		}
8137 		else if(pEmbedCL->getContainerType() == FL_CONTAINER_ANNOTATION)
8138 		{
8139 			getDocument()->getNextStruxOfType(sdhStart,PTX_EndAnnotation, &sdhEnd);
8140 		}
8141 		else if( pEmbedCL->getContainerType() == FL_CONTAINER_TOC)
8142 		{
8143 			getDocument()->getNextStruxOfType(sdhStart,PTX_EndTOC, &sdhEnd);
8144 		}
8145 		UT_return_if_fail(sdhEnd != NULL);
8146 		PT_DocPosition posStart = getDocument()->getStruxPosition(sdhStart);
8147 		PT_DocPosition posEnd = getDocument()->getStruxPosition(sdhEnd);
8148 		UT_uint32 iSize = posEnd - posStart + 1;
8149 		iEmbed += iSize;
8150 		getDocSectionLayout()->setNeedsSectionBreak(true,NULL);
8151 
8152 	}
8153 }
8154 
doclistener_insertSection(const PX_ChangeRecord_Strux * pcrx,SectionType iType,pf_Frag_Strux * sdh,PL_ListenerId lid,void (* pfnBindHandles)(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew))8155 bool fl_BlockLayout::doclistener_insertSection(const PX_ChangeRecord_Strux * pcrx,
8156 											   SectionType iType,
8157 											   pf_Frag_Strux* sdh,
8158 											   PL_ListenerId lid,
8159 											   void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
8160 																	   PL_ListenerId lid,
8161 																	   fl_ContainerLayout* sfhNew))
8162 {
8163 	UT_ASSERT(iType == FL_SECTION_DOC || iType == FL_SECTION_HDRFTR
8164 			  || iType == FL_SECTION_TOC
8165 			  || iType == FL_SECTION_FOOTNOTE
8166 			  || iType == FL_SECTION_ENDNOTE
8167 			  || iType == FL_SECTION_ANNOTATION);
8168 
8169 	_assertRunListIntegrity();
8170 
8171 	// Insert a section at the location given in the change record.
8172 	// Everything from this point forward (to the next section) needs
8173 	// to be re-parented to this new section.  We also need to verify
8174 	// that this insertion point is at the end of the block (and that
8175 	// another block follows).	This is because a section cannot
8176 	// contain content.
8177 
8178 	UT_ASSERT(pcrx);
8179 	UT_ASSERT(pcrx->getType() == PX_ChangeRecord::PXT_InsertStrux);
8180 	UT_ASSERT(iType != FL_SECTION_DOC || pcrx->getStruxType() == PTX_Section);
8181 	UT_ASSERT(iType != FL_SECTION_HDRFTR || pcrx->getStruxType() == PTX_SectionHdrFtr);
8182 	UT_ASSERT(iType != FL_SECTION_FOOTNOTE || pcrx->getStruxType() == PTX_SectionFootnote);
8183 	UT_ASSERT(iType != FL_SECTION_ANNOTATION || pcrx->getStruxType() == PTX_SectionAnnotation);
8184 	getDocSectionLayout()->setNeedsSectionBreak(true,NULL);
8185 
8186 //
8187 // Not true always. eg Undo on a delete header/footer. We should detect this
8188 // and deal with it.
8189 //
8190 	PT_DocPosition pos1;
8191 //
8192 // This is to clean the fragments
8193 //
8194 	m_pDoc->getBounds(true,pos1);
8195 	fl_DocSectionLayout* pDSL = NULL;
8196 	if(m_pSectionLayout->getType() == FL_SECTION_DOC)
8197 		pDSL =	static_cast<fl_DocSectionLayout *>(m_pSectionLayout);
8198 
8199 	xxx_UT_DEBUGMSG(("SectionLayout for block is %x block is %x \n",m_pSectionLayout,this));
8200 	fl_SectionLayout* pSL = NULL;
8201 	const gchar* pszNewID = NULL;
8202 
8203 	UT_DEBUGMSG(("Insert section at pos %d sdh of section =%p sdh of block =%p \n",getPosition(true),sdh,getStruxDocHandle()));
8204 
8205 	switch (iType)
8206 	{
8207 	case FL_SECTION_DOC:
8208 		pSL = new fl_DocSectionLayout
8209 			(m_pLayout, sdh, pcrx->getIndexAP(), FL_SECTION_DOC);
8210 		if (!pSL)
8211 		{
8212 			UT_DEBUGMSG(("no memory for SectionLayout"));
8213 			return false;
8214 		}
8215 
8216 		m_pLayout->insertSectionAfter(pDSL, static_cast<fl_DocSectionLayout*>(pSL));
8217 		break;
8218 	case FL_SECTION_HDRFTR:
8219 	{
8220 		pSL = new fl_HdrFtrSectionLayout(FL_HDRFTR_NONE,m_pLayout,NULL, sdh, pcrx->getIndexAP());
8221 		if (!pSL)
8222 		{
8223 			UT_DEBUGMSG(("no memory for SectionLayout"));
8224 			return false;
8225 		}
8226 
8227 		fl_HdrFtrSectionLayout * pHFSL = static_cast<fl_HdrFtrSectionLayout *>(pSL);
8228 		m_pLayout->addHdrFtrSection(pHFSL);
8229 //
8230 // Need to find the DocSectionLayout associated with this.
8231 //
8232 		const PP_AttrProp* pHFAP = NULL;
8233 		PT_AttrPropIndex indexAP = pcrx->getIndexAP();
8234 		bool bres = (m_pDoc->getAttrProp(indexAP, &pHFAP) && pHFAP);
8235 		UT_UNUSED(bres);
8236 		UT_ASSERT(bres);
8237 		pHFAP->getAttribute("id", pszNewID);
8238 //
8239 // pszHFID may not be defined yet. If not we can't do this stuff. If it is defined
8240 // this step is essential
8241 //
8242 		if(pszNewID)
8243 		{
8244 		  // plam mystery code
8245 			// plam, MES here, I need this code for inserting headers/footers.
8246 		  UT_DEBUGMSG(("new id: tell plam if you see this message\n"));
8247 //		  UT_ASSERT(0);
8248 			fl_DocSectionLayout* pDocSL = m_pLayout->findSectionForHdrFtr(static_cast<const char*>(pszNewID));
8249 			UT_return_val_if_fail( pDocSL, false );
8250 //
8251 // Determine if this is a header or a footer.
8252 //
8253 			const gchar* pszSectionType = NULL;
8254 			pHFAP->getAttribute("type", pszSectionType);
8255 
8256 			HdrFtrType hfType = FL_HDRFTR_NONE;
8257 			if (pszSectionType && *pszSectionType)
8258 			{
8259 				if(strcmp(pszSectionType,"header") == 0)
8260 					hfType = FL_HDRFTR_HEADER;
8261 				else if (strcmp(pszSectionType,"header-even") == 0)
8262 					hfType = FL_HDRFTR_HEADER_EVEN;
8263 				else if (strcmp(pszSectionType,"header-first") == 0)
8264 					hfType = FL_HDRFTR_HEADER_FIRST;
8265 				else if (strcmp(pszSectionType,"header-last") == 0)
8266 					hfType = FL_HDRFTR_HEADER_LAST;
8267 				else if (strcmp(pszSectionType,"footer") == 0)
8268 					hfType = FL_HDRFTR_FOOTER;
8269 				else if (strcmp(pszSectionType,"footer-even") == 0)
8270 					hfType = FL_HDRFTR_FOOTER_EVEN;
8271 				else if (strcmp(pszSectionType,"footer-first") == 0)
8272 					hfType = FL_HDRFTR_FOOTER_FIRST;
8273 				else if (strcmp(pszSectionType,"footer-last") == 0)
8274 					hfType = FL_HDRFTR_FOOTER_LAST;
8275 
8276 				if(hfType != FL_HDRFTR_NONE)
8277 				{
8278 					pHFSL->setDocSectionLayout(pDocSL);
8279 					pHFSL->setHdrFtr(hfType);
8280 					//
8281 					// Set the pointers to this header/footer
8282 					//
8283 					pDocSL->setHdrFtr(hfType, pHFSL);
8284 				}
8285 			}
8286 		}
8287 		else
8288 		{
8289 			UT_DEBUGMSG(("NO ID found with insertSection HdrFtr \n"));
8290 		}
8291 		break;
8292 	}
8293 	case FL_SECTION_ENDNOTE:
8294 	case FL_SECTION_ANNOTATION:
8295 	case FL_SECTION_FOOTNOTE:
8296 	{
8297 		// Most of the time, we would insert a new section
8298 		// after the previous section.
8299 		// But, here we insert our FootnoteLayout after this(?)
8300 		// BlockLayout. -PL
8301 		PT_AttrPropIndex indexAP = pcrx->getIndexAP();
8302 		if(iType == FL_SECTION_FOOTNOTE)
8303 		{
8304 			pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,indexAP, FL_CONTAINER_FOOTNOTE));
8305 		}
8306 		else if (iType == FL_SECTION_ENDNOTE)
8307 		{
8308 			pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,indexAP, FL_CONTAINER_ENDNOTE));
8309 		}
8310 		else if (iType == FL_SECTION_ANNOTATION)
8311 		{
8312 			pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,indexAP, FL_CONTAINER_ANNOTATION));
8313 		}
8314 //
8315 // Need to find the DocSectionLayout associated with this.
8316 //
8317 		const PP_AttrProp* pAP = NULL;
8318 		bool bres = (m_pDoc->getAttrProp(indexAP, &pAP) && pAP);
8319 		UT_UNUSED(bres);
8320 		UT_ASSERT(bres);
8321 		pAP->getAttribute("id", pszNewID);
8322 		break;
8323 	}
8324 	case FL_SECTION_TOC:
8325 	{
8326 		// Most of the time, we would insert a new section
8327 		// after the previous section.
8328 		// But, here we insert our TOCLayout after this(?)
8329 		PT_AttrPropIndex indexAP = pcrx->getIndexAP();
8330 		pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,indexAP, FL_CONTAINER_TOC));
8331 
8332 		// Must call the bind function to complete the exchange of handles
8333 		// with the document (piece table) *** before *** anything tries
8334 		// to call down into the document (like all of the view
8335 		// listeners).
8336 
8337 		fl_ContainerLayout* sfhNew = pSL;
8338 		//
8339 		// Don't bind to shadows
8340 		//
8341 		if(pfnBindHandles)
8342 		{
8343 			pfnBindHandles(sdh,lid,sfhNew);
8344 		}
8345 		//
8346 		// That's all we need to do except update the view pointers I guess..
8347 		//
8348 		FV_View* pView = getView();
8349 		if (pView && (pView->isActive() || pView->isPreview()))
8350 		{
8351 			pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
8352 		}
8353 		else if(pView && pView->getPoint() > pcrx->getPosition())
8354 		{
8355 			//
8356 			// For EndTOC
8357 			//
8358 			pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
8359 		}
8360 		if(pView)
8361 			pView->updateCarets(pcrx->getPosition(),2);
8362 		return true;
8363 	}
8364 	default:
8365 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
8366 		break;
8367 	}
8368 
8369 	PT_DocPosition posSL = m_pDoc->getStruxPosition(pSL->getStruxDocHandle());
8370 	PT_DocPosition posThis = m_pDoc->getStruxPosition(getStruxDocHandle());
8371 
8372 	// Must call the bind function to complete the exchange of handles
8373 	// with the document (piece table) *** before *** anything tries
8374 	// to call down into the document (like all of the view
8375 	// listeners).
8376 
8377 	fl_ContainerLayout* sfhNew = pSL;
8378 	//
8379 	// Don't bind to shadows
8380 	//
8381 	if(pfnBindHandles)
8382 	{
8383 		pfnBindHandles(sdh,lid,sfhNew);
8384 	}
8385 
8386 	fl_SectionLayout* pOldSL = m_pSectionLayout;
8387 
8388 	if ((iType == FL_SECTION_FOOTNOTE) || (iType == FL_SECTION_ENDNOTE) || (iType == FL_SECTION_ANNOTATION))
8389 	{
8390 //
8391 // Now update the position pointer in the view
8392 //
8393 		FV_View* pView = getView();
8394 		if (pView && (pView->isActive() || pView->isPreview()))
8395 		{
8396 			pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
8397 		}
8398 		else if(pView && pView->getPoint() > pcrx->getPosition())
8399 		{
8400 			pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
8401 		}
8402 		if(pView)
8403 			pView->updateCarets(pcrx->getPosition(),2);
8404 		return true;
8405 	}
8406 //
8407 // Now move all the blocks following into the new section
8408 //
8409 	fl_ContainerLayout* pCL = NULL;
8410 	if(posSL < posThis)
8411 	{
8412 		pCL = this;
8413 	}
8414 	else
8415 	{
8416 		pCL = getNext();
8417 	}
8418 	//
8419 	// BUT!!! Don't move the immediate Footnotes or Endnotes
8420 	//
8421 	fl_ContainerLayout * pLastCL = NULL;
8422 	if(pCL)
8423 	{
8424 		pLastCL = pCL->getPrev();
8425 	}
8426 	while(pCL && ((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
8427 				  || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE)
8428 				  || (pCL->getContainerType() == FL_CONTAINER_ANNOTATION)))
8429 	{
8430 		pLastCL = pCL;
8431 		pCL = pCL->getNext();
8432 	}
8433 	fl_BlockLayout * pBL = NULL;
8434 	while (pCL)
8435 	{
8436 		//
8437 		// When inserting a HEADER/FOOTER dont move footnotes/endnotes into
8438 		// Header/Footer
8439 		//
8440 		if((iType== FL_SECTION_HDRFTR) && (pCL->getContainerType() == FL_CONTAINER_FOOTNOTE
8441 										 || pCL->getContainerType() == FL_CONTAINER_ENDNOTE
8442 										 || pCL->getContainerType() == FL_CONTAINER_ANNOTATION
8443 										 || pCL->getContainerType() == FL_CONTAINER_TOC
8444 										 ||   pCL->getContainerType() == FL_CONTAINER_FRAME))
8445 		{
8446 			pCL = pCL->getNext();
8447 			continue;
8448 		}
8449 
8450 		fl_ContainerLayout* pNext = pCL->getNext();
8451 		pBL = NULL;
8452 		pCL->collapse();
8453 		if(pCL->getContainerType()==FL_CONTAINER_BLOCK)
8454 		{
8455 			pBL = static_cast<fl_BlockLayout *>(pCL);
8456 		}
8457 		if(pBL && pBL->isHdrFtr())
8458 		{
8459 			fl_HdrFtrSectionLayout * pHF = static_cast<fl_HdrFtrSectionLayout *>(pBL->getSectionLayout());
8460 			pHF->collapseBlock(pBL);
8461 		}
8462 		pOldSL->remove(pCL);
8463 		pSL->add(pCL);
8464 		if(pBL)
8465 		{
8466 			pBL->setSectionLayout( pSL);
8467 			pBL->m_iNeedsReformat = 0;
8468 		}
8469 		if(pSL->getType() == FL_SECTION_DOC)
8470 		{
8471 			fl_DocSectionLayout * pDDSL = static_cast<fl_DocSectionLayout *>(pSL);
8472 			if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
8473 			{
8474 				static_cast<fl_FootnoteLayout *>(pCL)->
8475 					setDocSectionLayout(pDDSL);
8476 			}
8477 			if(pCL->getContainerType() == FL_CONTAINER_ENDNOTE)
8478 			{
8479 				static_cast<fl_EndnoteLayout *>(pCL)->
8480 					setDocSectionLayout(pDDSL);
8481 			}
8482 			if(pCL->getContainerType() == FL_CONTAINER_ANNOTATION)
8483 			{
8484 				static_cast<fl_EndnoteLayout *>(pCL)->
8485 					setDocSectionLayout(pDDSL);
8486 			}
8487 		}
8488 		pCL = pNext;
8489 	}
8490 
8491 //
8492 // Terminate blocklist here. This Block is the last in this section.
8493 //
8494 	if (pLastCL)
8495 	{
8496 		pLastCL->setNext(NULL);
8497 		pOldSL->setLastLayout(pLastCL);
8498 	}
8499 //
8500 // OK we have to redo all the containers now.
8501 //
8502 	if(pSL->getType() == FL_SECTION_DOC)
8503 	{
8504 		fl_DocSectionLayout * pFirstDSL = static_cast<fl_DocSectionLayout *>(pOldSL);
8505 		pDSL = pFirstDSL;
8506 		while(pDSL != NULL)
8507 		{
8508 			pDSL->collapse();
8509 			pDSL = pDSL->getNextDocSection();
8510 		}
8511 		pDSL = pFirstDSL;
8512 		while(pDSL != NULL)
8513 		{
8514 			pDSL->updateDocSection();
8515 			pDSL = pDSL->getNextDocSection();
8516 		}
8517 	}
8518 
8519 //
8520 // In the case of Header/Footer sections we must now format this stuff to create
8521 // the shadows.
8522 //
8523 	if(iType == FL_SECTION_HDRFTR || iType == FL_SECTION_FOOTNOTE || iType == FL_SECTION_ANNOTATION)
8524 	{
8525 		if(pszNewID)
8526 		{
8527 			pSL->format();
8528 			pSL->redrawUpdate();
8529 		}
8530 		else
8531 			return true;
8532 	}
8533 	updateEnclosingBlockIfNeeded();
8534 
8535 	FV_View* pView = getView();
8536 	if (pView && (pView->isActive() || pView->isPreview()))
8537 	{
8538 		pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
8539 	}
8540 	else if(pView && pView->getPoint() > pcrx->getPosition())
8541 	{
8542 		pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
8543 	}
8544 	if(pView)
8545 		pView->updateCarets(pcrx->getPosition(),2);
8546 
8547 	_assertRunListIntegrity();
8548 #if DEBUG
8549 	if(getFirstContainer())
8550 	{
8551 		UT_ASSERT(getFirstContainer()->getPrev() == NULL);
8552 	}
8553 #endif
8554 	return true;
8555 }
8556 
8557 /*!
8558  * Insert a table into the list of blocks
8559  */
doclistener_insertTable(const PX_ChangeRecord_Strux * pcrx,SectionType iType,pf_Frag_Strux * sdh,PL_ListenerId lid,void (* pfnBindHandles)(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew))8560 fl_SectionLayout * fl_BlockLayout::doclistener_insertTable(const PX_ChangeRecord_Strux * pcrx,
8561 														   SectionType iType,
8562 											   pf_Frag_Strux* sdh,
8563 											   PL_ListenerId lid,
8564 											   void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
8565 																	   PL_ListenerId lid,
8566 																	   fl_ContainerLayout* sfhNew))
8567 {
8568 	UT_UNUSED(iType);
8569 	UT_ASSERT(iType == FL_SECTION_TABLE);
8570 	_assertRunListIntegrity();
8571 
8572 	// Insert a section at the location given in the change record.
8573 	// Everything from this point forward (to the next section) needs
8574 	// to be re-parented to this new section.  We also need to verify
8575 	// that this insertion point is at the end of the block (and that
8576 	// another block follows).	This is because a section cannot
8577 	// contain content.
8578 
8579 	UT_ASSERT(pcrx);
8580 	UT_ASSERT(pcrx->getType() == PX_ChangeRecord::PXT_InsertStrux);
8581 //
8582 // Not true always. eg Undo on a delete header/footer. We should detect this
8583 // and deal with it.
8584 //
8585 	PT_DocPosition pos1;
8586 //
8587 // This is to clean the fragments
8588 //
8589 	m_pDoc->getBounds(true,pos1);
8590 
8591 	fl_SectionLayout* pSL = NULL;
8592 
8593 	pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,pcrx->getIndexAP(), FL_CONTAINER_TABLE));
8594 
8595 		// Must call the bind function to complete the exchange of handles
8596 		// with the document (piece table) *** before *** anything tries
8597 		// to call down into the document (like all of the view
8598 		// listeners).
8599 
8600 	fl_ContainerLayout* sfhNew = pSL;
8601 	//
8602 	// Don't bind to shadows
8603 	//
8604 	if(pfnBindHandles)
8605 	{
8606 		pfnBindHandles(sdh,lid,sfhNew);
8607 	}
8608 
8609 //
8610 // increment the insertion point in the view.
8611 //
8612 	FV_View* pView = getView();
8613 	if (pView && (pView->isActive() || pView->isPreview()))
8614 	{
8615 		pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
8616 	}
8617 	else if(pView && pView->getPoint() > pcrx->getPosition())
8618 	{
8619 		pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
8620 	}
8621 	if(pView)
8622 		pView->updateCarets(pcrx->getPosition(),1);
8623 //
8624 // OK that's it!
8625 //
8626 	updateEnclosingBlockIfNeeded();
8627 
8628 	return pSL;
8629 }
8630 
8631 /*!
8632  * Insert a Frame after this block.
8633  */
doclistener_insertFrame(const PX_ChangeRecord_Strux * pcrx,SectionType iType,pf_Frag_Strux * sdh,PL_ListenerId lid,void (* pfnBindHandles)(pf_Frag_Strux * sdhNew,PL_ListenerId lid,fl_ContainerLayout * sfhNew))8634 fl_SectionLayout * fl_BlockLayout::doclistener_insertFrame(const PX_ChangeRecord_Strux * pcrx,
8635 														   SectionType iType,
8636 											   pf_Frag_Strux* sdh,
8637 											   PL_ListenerId lid,
8638 											   void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
8639 																	   PL_ListenerId lid,
8640 																	   fl_ContainerLayout* sfhNew))
8641 {
8642 	UT_UNUSED(iType);
8643 	UT_ASSERT(iType == FL_SECTION_FRAME);
8644 	_assertRunListIntegrity();
8645 
8646 	// Insert a section at the location given in the change record.
8647 	// Everything from this point forward (to the next section) needs
8648 	// to be re-parented to this new section.  We also need to verify
8649 	// that this insertion point is at the end of the block (and that
8650 	// another block follows).	This is because a section cannot
8651 	// contain content.
8652 
8653 	UT_ASSERT(pcrx);
8654 	UT_ASSERT(pcrx->getType() == PX_ChangeRecord::PXT_InsertStrux);
8655 //
8656 // Not true always. eg Undo on a delete header/footer. We should detect this
8657 // and deal with it.
8658 //
8659 	PT_DocPosition pos1;
8660 //
8661 // This is to clean the fragments
8662 //
8663 	m_pDoc->getBounds(true,pos1);
8664 
8665 	fl_SectionLayout* pSL = NULL;
8666 
8667 	pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(getSectionLayout())->insert(sdh,this,pcrx->getIndexAP(), FL_CONTAINER_FRAME));
8668 
8669 		// Must call the bind function to complete the exchange of handles
8670 		// with the document (piece table) *** before *** anything tries
8671 		// to call down into the document (like all of the view
8672 		// listeners).
8673 
8674 	fl_ContainerLayout* sfhNew = pSL;
8675 	//
8676 	// Don't bind to shadows
8677 	//
8678 	if(pfnBindHandles)
8679 	{
8680 		pfnBindHandles(sdh,lid,sfhNew);
8681 	}
8682 
8683 	// Create a Physical Container for this frame
8684 
8685 	static_cast<fl_FrameLayout *>(pSL)->format();
8686   	getDocSectionLayout()->completeBreakSection();
8687 //
8688 // increment the insertion point in the view.
8689 //
8690 	FV_View* pView = getView();
8691 	if (pView && (pView->isActive() || pView->isPreview()))
8692 	{
8693 		pView->_setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
8694 	}
8695 	else if(pView && pView->getPoint() > pcrx->getPosition())
8696 	{
8697 		pView->_setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
8698 	}
8699 	if(pView)
8700 		pView->updateCarets(pcrx->getPosition(),1);
8701 //
8702 // OK that's it!
8703 //
8704 	updateEnclosingBlockIfNeeded();
8705 
8706 	return pSL;
8707 }
8708 
8709 #ifdef ENABLE_SPELL
8710 /*!
8711  Draw squiggles intersecting with Run
8712  \param pRun Run
8713 
8714  For all misspelled words in this run, call the run->drawSquiggle()
8715  method.
8716 */
8717 void
findSpellSquigglesForRun(fp_Run * pRun) const8718 fl_BlockLayout::findSpellSquigglesForRun(fp_Run* pRun) const
8719 {
8720 	xxx_UT_DEBUGMSG(("fl_BlockLayout::findSpellSquigglesForRun\n"));
8721 
8722 	UT_ASSERT(pRun->getType() == FPRUN_TEXT);
8723 	fp_TextRun* pTextRun = (static_cast<fp_TextRun*>(pRun));
8724 
8725 	UT_sint32 runBlockOffset = pRun->getBlockOffset();
8726 	UT_sint32 runBlockEnd = runBlockOffset + pRun->getLength();
8727 	UT_sint32 iFirst, iLast;
8728 	if (m_pSpellSquiggles->findRange(runBlockOffset, runBlockEnd, iFirst, iLast))
8729 	{
8730 		UT_sint32 iStart = 0, iEnd;
8731 		fl_PartOfBlockPtr pPOB;
8732 		UT_sint32 i = iFirst;
8733 
8734 		// The first POB may only be partially within the region. Clip
8735 		// it if necessary.
8736 		pPOB = m_pSpellSquiggles->getNth(i++);
8737 		if (!pPOB->getIsIgnored())
8738 		{
8739 			iStart = pPOB->getOffset();
8740 			iEnd =	iStart + pPOB->getPTLength();
8741 			if (iStart < runBlockOffset) iStart = runBlockOffset;
8742 
8743 			// Only draw if there's more than one POB. If there's only
8744 			// one POB, it may also need clipping at the end (let the
8745 			// code below handle it).
8746 			if (iFirst != iLast)
8747 			{
8748 				pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_SPELL);
8749 			}
8750 		}
8751 		// The ones in the middle don't need clipping.
8752 		for (; i < iLast; i++)
8753 		{
8754 			pPOB = m_pSpellSquiggles->getNth(i);
8755 			if (pPOB->getIsIgnored()) continue;
8756 
8757 			iStart = pPOB->getOffset();
8758 			iEnd =	iStart + pPOB->getPTLength();
8759 			pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_SPELL);
8760 		}
8761 		// The last POB may only be partially within the region. Clip
8762 		// it if necessary. Note the load with iLast instead of i.
8763 		pPOB = m_pSpellSquiggles->getNth(iLast);
8764 		if (!pPOB->getIsIgnored())
8765 		{
8766 			// Only load start if this POB is different from the first
8767 			// one.
8768 			if (iFirst != iLast)
8769 				iStart = pPOB->getOffset();
8770 			iEnd =	pPOB->getOffset() + pPOB->getPTLength();
8771 			if (iEnd > runBlockEnd) iEnd = runBlockEnd;
8772 			pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_SPELL);
8773 		}
8774 	}
8775 }
8776 
8777 /*!
8778  * Draw all the grammar squiggles in the Block.
8779  */
drawGrammarSquiggles(void) const8780 void fl_BlockLayout::drawGrammarSquiggles(void) const
8781 {
8782 	fp_Run * pRun = getFirstRun();
8783 	while(pRun)
8784 	{
8785 		if(pRun->getType() == FPRUN_TEXT)
8786 		{
8787 			findGrammarSquigglesForRun(pRun);
8788 		}
8789 		pRun = pRun->getNextRun();
8790 	}
8791 }
8792 
8793 /*!
8794  Draw grammar squiggles intersecting with Run
8795  \param pRun Run
8796 
8797  For all incorrect grammar in this run, call the run->drawSquiggle()
8798  method.
8799 */
8800 void
findGrammarSquigglesForRun(fp_Run * pRun) const8801 fl_BlockLayout::findGrammarSquigglesForRun(fp_Run* pRun) const
8802 {
8803 	xxx_UT_DEBUGMSG(("fl_BlockLayout::findSpellSquigglesForRun\n"));
8804 
8805 	UT_ASSERT(pRun->getType() == FPRUN_TEXT);
8806 	fp_TextRun* pTextRun = (static_cast<fp_TextRun*>(pRun));
8807 
8808 	UT_sint32 runBlockOffset = pRun->getBlockOffset();
8809 	UT_sint32 runBlockEnd = runBlockOffset + pRun->getLength();
8810 	UT_sint32 iFirst, iLast;
8811 	if (m_pGrammarSquiggles->findRange(runBlockOffset, runBlockEnd, iFirst, iLast,true))
8812 	{
8813 		UT_sint32 iStart = 0, iEnd;
8814 		fl_PartOfBlockPtr pPOB;
8815 		UT_sint32 i = iFirst;
8816 
8817 		// The first POB may only be partially within the region. Clip
8818 		// it if necessary.
8819 		pPOB = m_pGrammarSquiggles->getNth(i++);
8820 		if (!pPOB->getIsIgnored() && !pPOB->isInvisible())
8821 		{
8822 			iStart = pPOB->getOffset();
8823 			iEnd =	iStart + pPOB->getPTLength();
8824 			if (iStart < runBlockOffset) iStart = runBlockOffset;
8825 
8826 			// Only draw if there's more than one POB. If there's only
8827 			// one POB, it may also need clipping at the end (let the
8828 			// code below handle it).
8829 			//			if (iFirst != iLast)
8830 			{
8831 				pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_GRAMMAR);
8832 			}
8833 		}
8834 		// The ones in the middle don't need clipping.
8835 		for (; i < iLast; i++)
8836 		{
8837 			pPOB = m_pGrammarSquiggles->getNth(i);
8838 			if (pPOB->getIsIgnored() || pPOB->isInvisible()) continue;
8839 
8840 			iStart = pPOB->getOffset();
8841 			iEnd =	iStart + pPOB->getPTLength();
8842 			pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_GRAMMAR);
8843 		}
8844 		// The last POB may only be partially within the region. Clip
8845 		// it if necessary. Note the load with iLast instead of i.
8846 		pPOB = m_pGrammarSquiggles->getNth(iLast);
8847 		if (!pPOB->getIsIgnored() && !pPOB->isInvisible())
8848 		{
8849 			// Only load start if this POB is different from the first
8850 			// one.
8851 			if (iFirst != iLast)
8852 				iStart = pPOB->getOffset();
8853 			if(iStart < (UT_sint32)pTextRun->getBlockOffset())
8854 				iStart = pTextRun->getBlockOffset();
8855 			iEnd =	pPOB->getOffset() + pPOB->getPTLength();
8856 			if (iEnd > runBlockEnd) iEnd = runBlockEnd;
8857 			pTextRun->drawSquiggle(iStart, iEnd - iStart,FL_SQUIGGLE_GRAMMAR);
8858 		}
8859 	}
8860 }
8861 #endif
8862 
8863 //////////////////////////////////////////////////////////////////
8864 // Object-related stuff
8865 //////////////////////////////////////////////////////////////////
8866 
doclistener_populateObject(PT_BlockOffset blockOffset,const PX_ChangeRecord_Object * pcro)8867 bool fl_BlockLayout::doclistener_populateObject(PT_BlockOffset blockOffset,
8868 												const PX_ChangeRecord_Object * pcro)
8869 {
8870 	_assertRunListIntegrity();
8871 
8872 	switch (pcro->getObjectType())
8873 	{
8874 	case PTO_Image:
8875 	{
8876 		FG_Graphic* pFG = FG_Graphic::createFromChangeRecord(this, pcro);
8877 		if (pFG == NULL)
8878 			return false;
8879 
8880 		xxx_UT_DEBUGMSG(("Populate:InsertObject:Image:\n"));
8881 		_doInsertImageRun(blockOffset, pFG,pcro->getObjectHandle());
8882 		return true;
8883 	}
8884 
8885 	case PTO_Field:
8886 		xxx_UT_DEBUGMSG(("!!!Populate:InsertObject:Field: BlockOffset %d \n",blockOffset));
8887 		_doInsertFieldRun(blockOffset, pcro);
8888 		return true;
8889 
8890 	case PTO_Bookmark:
8891 		xxx_UT_DEBUGMSG(("Populate:InsertBookmark:\n"));
8892 		_doInsertBookmarkRun(blockOffset);
8893 		return true;
8894 
8895 	case PTO_Hyperlink:
8896 		xxx_UT_DEBUGMSG(("Populate:InsertHyperlink:\n"));
8897 		_doInsertHyperlinkRun(blockOffset);
8898 		return true;
8899 
8900 	case PTO_Annotation:
8901 		xxx_UT_DEBUGMSG(("Populate:InsertHyperlink:\n"));
8902 		_doInsertAnnotationRun(blockOffset);
8903 		return true;
8904 
8905 	case PTO_RDFAnchor:
8906 		xxx_UT_DEBUGMSG(("Populate:InsertHyperlink, RDFAnchor:\n"));
8907 		_doInsertRDFAnchorRun(blockOffset);
8908 		return true;
8909 
8910 	case PTO_Math:
8911 		xxx_UT_DEBUGMSG(("Populate:InsertMathML:\n"));
8912 		_doInsertMathRun(blockOffset,pcro->getIndexAP(),pcro->getObjectHandle());
8913 		return true;
8914 
8915 
8916 	case PTO_Embed:
8917 		UT_DEBUGMSG(("Populate Embed:\n"));
8918 		_doInsertEmbedRun(blockOffset,pcro->getIndexAP(),pcro->getObjectHandle());
8919 		return true;
8920 
8921 	default:
8922 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
8923 		return false;
8924 	}
8925 
8926 	_assertRunListIntegrity();
8927 	updateEnclosingBlockIfNeeded();
8928 	if(isHidden() == FP_HIDDEN_FOLDED)
8929 	{
8930 		collapse();
8931 	}
8932 }
8933 
doclistener_insertObject(const PX_ChangeRecord_Object * pcro)8934 bool fl_BlockLayout::doclistener_insertObject(const PX_ChangeRecord_Object * pcro)
8935 {
8936 	_assertRunListIntegrity();
8937 
8938 	PT_BlockOffset blockOffset = 0;
8939 
8940 	switch (pcro->getObjectType())
8941 	{
8942 	case PTO_Image:
8943 	{
8944 		UT_DEBUGMSG(("Edit:InsertObject:Image:\n"));
8945 		blockOffset = pcro->getBlockOffset();
8946 
8947 		FG_Graphic* pFG = FG_Graphic::createFromChangeRecord(this, pcro);
8948 		if (pFG == NULL)
8949 			return false;
8950 
8951 		_doInsertImageRun(blockOffset, pFG,pcro->getObjectHandle());
8952 		break;
8953 	}
8954 
8955 	case PTO_Field:
8956 	{
8957 		UT_DEBUGMSG(("Edit:InsertObject:Field:\n"));
8958 		blockOffset = pcro->getBlockOffset();
8959 		_doInsertFieldRun(blockOffset, pcro);
8960 		break;
8961 	}
8962 
8963 	case PTO_Bookmark:
8964 	{
8965 		UT_DEBUGMSG(("Edit:InsertObject:Bookmark:\n"));
8966 		blockOffset = pcro->getBlockOffset();
8967 		_doInsertBookmarkRun(blockOffset);
8968 		break;
8969 
8970 	}
8971 
8972 	case PTO_Hyperlink:
8973 	{
8974 		UT_DEBUGMSG(("Edit:InsertObject:Hyperlink:\n"));
8975 		blockOffset = pcro->getBlockOffset();
8976 		_doInsertHyperlinkRun(blockOffset);
8977 		break;
8978 
8979 	}
8980 
8981 
8982 	case PTO_Annotation:
8983 	{
8984 		UT_DEBUGMSG(("Edit:InsertObject:Hyperlink:\n"));
8985 		blockOffset = pcro->getBlockOffset();
8986 		_doInsertAnnotationRun(blockOffset);
8987 		break;
8988 
8989 	}
8990 
8991     case PTO_RDFAnchor:
8992 	{
8993 		UT_DEBUGMSG(("Edit:InsertObject:Hyperlink, RDFAnchor:\n"));
8994 		blockOffset = pcro->getBlockOffset();
8995 		_doInsertRDFAnchorRun(blockOffset);
8996 		break;
8997 
8998 	}
8999 
9000 	case PTO_Math:
9001 	{
9002 		UT_DEBUGMSG(("Edit:InsertObject:Math:\n"));
9003 		blockOffset = pcro->getBlockOffset();
9004 		_doInsertMathRun(blockOffset,pcro->getIndexAP(),pcro->getObjectHandle());
9005 		break;
9006 
9007 	}
9008 
9009 
9010 	case PTO_Embed:
9011 	{
9012 		UT_DEBUGMSG(("Edit:InsertObject:Embed:\n"));
9013 		blockOffset = pcro->getBlockOffset();
9014 		_doInsertEmbedRun(blockOffset,pcro->getIndexAP(),pcro->getObjectHandle());
9015 		break;
9016 
9017 	}
9018 
9019 
9020 	default:
9021 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9022 		return false;
9023 	}
9024 	m_iNeedsReformat = blockOffset;
9025 	//
9026 	// Update the offsets before the format (Where stuff gets calculated)
9027 	//
9028 	updateEnclosingBlockIfNeeded();
9029 	format();
9030 
9031 	FV_View* pView = getView();
9032 	if (pView && (pView->isActive() || pView->isPreview()))
9033 		pView->_setPoint(pcro->getPosition() + 1);
9034 	else if(pView && pView->getPoint() > pcro->getPosition())
9035 		pView->_setPoint(pView->getPoint() + 1);
9036 	if(pView)
9037 		pView->updateCarets(pcro->getPosition(),1);
9038 
9039 #ifdef ENABLE_SPELL
9040 	// TODO: are objects always one wide?
9041 	m_pSpellSquiggles->textInserted(blockOffset, 1);
9042 	m_pGrammarSquiggles->textInserted(blockOffset, 1);
9043 #endif
9044 
9045 	_assertRunListIntegrity();
9046 	//
9047 	// OK Now do the insertSpan for any TOC's that shadow this block.
9048 	//
9049 	if(!isNotTOCable() && !m_bIsTOC && m_bStyleInTOC)
9050 	{
9051 		UT_GenericVector<fl_BlockLayout *> vecBlocksInTOCs;
9052 		if(m_pLayout->getMatchingBlocksFromTOCs(this, &vecBlocksInTOCs))
9053 		{
9054 			UT_sint32 i = 0;
9055 			for(i=0; i<vecBlocksInTOCs.getItemCount();i++)
9056 			{
9057 				fl_BlockLayout * pBL = vecBlocksInTOCs.getNthItem(i);
9058 				pBL->doclistener_insertObject(pcro);
9059 			}
9060 		}
9061 		else
9062 		{
9063 			m_bStyleInTOC = false;
9064 		}
9065 	}
9066 
9067 	return true;
9068 }
9069 
doclistener_deleteObject(const PX_ChangeRecord_Object * pcro)9070 bool fl_BlockLayout::doclistener_deleteObject(const PX_ChangeRecord_Object * pcro)
9071 {
9072 	_assertRunListIntegrity();
9073 
9074 	PT_BlockOffset blockOffset = 0;
9075 
9076 	switch (pcro->getObjectType())
9077 	{
9078 		case PTO_Image:
9079 		{
9080 			UT_DEBUGMSG(("Edit:DeleteObject:Image:\n"));
9081 			blockOffset = pcro->getBlockOffset();
9082 			_delete(blockOffset, 1);
9083 			break;
9084 		}
9085 		case PTO_Math:
9086 		{
9087 			UT_DEBUGMSG(("Edit:DeleteObject:Math:\n"));
9088 			blockOffset = pcro->getBlockOffset();
9089 			_delete(blockOffset, 1);
9090 			break;
9091 		}
9092 		case PTO_Embed:
9093 		{
9094 			UT_DEBUGMSG(("Edit:DeleteObject:Embed:\n"));
9095 			blockOffset = pcro->getBlockOffset();
9096 			_delete(blockOffset, 1);
9097 			break;
9098 		}
9099 
9100 		case PTO_Field:
9101 		{
9102 			xxx_UT_DEBUGMSG(("Edit:DeleteObject:Field:\n"));
9103 			blockOffset = pcro->getBlockOffset();
9104 			_delete(blockOffset, 1);
9105 			if(m_pAutoNum)
9106 			{
9107 				m_pAutoNum->markAsDirty();
9108 			}
9109 			break;
9110 		}
9111 
9112 		case PTO_Bookmark:
9113 		{
9114 			UT_DEBUGMSG(("Edit:DeleteObject:Bookmark:\n"));
9115 			blockOffset = pcro->getBlockOffset();
9116 			_delete(blockOffset,1);
9117 			break;
9118 		}
9119 
9120 		case PTO_Hyperlink:
9121 		{
9122 			UT_DEBUGMSG(("Edit:DeleteObject:Hyperlink:\n"));
9123 			blockOffset = pcro->getBlockOffset();
9124 			_delete(blockOffset,1);
9125 			break;
9126 		}
9127 
9128 
9129 		case PTO_Annotation:
9130 		{
9131 			UT_DEBUGMSG(("Edit:DeleteObject:Annotation:\n"));
9132 			blockOffset = pcro->getBlockOffset();
9133 			_delete(blockOffset,1);
9134 			break;
9135 		}
9136 
9137 		case PTO_RDFAnchor:
9138 		{
9139 			UT_DEBUGMSG(("Edit:DeleteObject:RDFAnchor:\n"));
9140 			blockOffset = pcro->getBlockOffset();
9141 			_delete(blockOffset,1);
9142 			break;
9143 		}
9144 
9145 		default:
9146 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9147 		return false;
9148 	}
9149 	updateEnclosingBlockIfNeeded();
9150 	m_iNeedsReformat = blockOffset;
9151 	format();
9152 
9153 	FV_View* pView = getView();
9154 	if (pView && (pView->isActive() || pView->isPreview()))
9155 	{
9156 		pView->_resetSelection();
9157 		pView->_setPoint(pcro->getPosition());
9158 	}
9159 	else if(pView && pView->getPoint() > pcro->getPosition())
9160 		pView->_setPoint(pView->getPoint() - 1);
9161 	if(pView)
9162 		pView->updateCarets(pcro->getPosition(),-1);
9163 
9164 #ifdef ENABLE_SPELL
9165 	// TODO: are objects always one wide?
9166 	if(m_pSpellSquiggles)
9167 		m_pSpellSquiggles->textDeleted(blockOffset, 1);
9168 	if(m_pGrammarSquiggles)
9169 		m_pGrammarSquiggles->textDeleted(blockOffset, 1);
9170 #endif
9171 
9172 	_assertRunListIntegrity();
9173 	//
9174 	// OK Now do the deleteObject for any TOC's that shadow this block.
9175 	//
9176 	if(!isNotTOCable() && !m_bIsTOC && m_bStyleInTOC && m_pLayout)
9177 	{
9178 		UT_GenericVector<fl_BlockLayout *> vecBlocksInTOCs;
9179 		if( m_pLayout->getMatchingBlocksFromTOCs(this, &vecBlocksInTOCs))
9180 		{
9181 			UT_sint32 i = 0;
9182 			for(i=0; i<vecBlocksInTOCs.getItemCount();i++)
9183 			{
9184 				fl_BlockLayout * pBL = vecBlocksInTOCs.getNthItem(i);
9185 				pBL->doclistener_deleteObject(pcro);
9186 			}
9187 		}
9188 		else
9189 		{
9190 			m_bStyleInTOC = false;
9191 		}
9192 	}
9193 
9194 	return true;
9195 }
9196 
doclistener_changeObject(const PX_ChangeRecord_ObjectChange * pcroc)9197 bool fl_BlockLayout::doclistener_changeObject(const PX_ChangeRecord_ObjectChange * pcroc)
9198 {
9199 
9200 	_assertRunListIntegrity();
9201 
9202 	PT_BlockOffset blockOffset = 0;
9203 	switch (pcroc->getObjectType())
9204 	{
9205 	case PTO_Bookmark:
9206 	case PTO_Hyperlink:
9207 	case PTO_Annotation:
9208 		return true;
9209 	case PTO_Image:
9210 	{
9211 		xxx_UT_DEBUGMSG(("Edit:ChangeObject:Image:\n"));
9212 		blockOffset = pcroc->getBlockOffset();
9213 		fp_Run* pRun = m_pFirstRun;
9214 		while (pRun)
9215 		{
9216 			if (pRun->getBlockOffset() == blockOffset)
9217 			{
9218 				if(pRun->getType()!= FPRUN_IMAGE)
9219 				{
9220 					UT_DEBUGMSG(("!!! run type NOT OBJECT, instead = %d !!!! \n",pRun->getType()));
9221 					while(pRun && pRun->getType() == FPRUN_FMTMARK)
9222 					{
9223 						pRun = pRun->getNextRun();
9224 					}
9225 				}
9226 				if(!pRun || pRun->getType() != FPRUN_IMAGE)
9227 				{
9228 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9229 					return false;
9230 				}
9231 				fp_ImageRun* pImageRun = static_cast<fp_ImageRun*>(pRun);
9232 				if(!isHdrFtr())
9233 				{
9234 					pImageRun->clearScreen();
9235 				}
9236 				pImageRun->lookupProperties();
9237 
9238 				goto done;
9239 			}
9240 			pRun = pRun->getNextRun();
9241 		}
9242 
9243 		return false;
9244 	}
9245 	case PTO_Field:
9246 	{
9247 		xxx_UT_DEBUGMSG(("Edit:ChangeObject:Field:\n"));
9248 		blockOffset = pcroc->getBlockOffset();
9249 		fp_Run* pRun = m_pFirstRun;
9250 		while (pRun)
9251 		{
9252 			if (pRun->getBlockOffset() == blockOffset && (pRun->getType()!= FPRUN_FMTMARK))
9253 			{
9254 				if(pRun->getType()!= FPRUN_FIELD)
9255 				{
9256 					UT_DEBUGMSG(("!!! run type NOT OBJECT, instead = %d !!!! \n",pRun->getType()));
9257 					while(pRun && pRun->getType() == FPRUN_FMTMARK)
9258 					{
9259 						pRun = pRun->getNextRun();
9260 					}
9261 				}
9262 				if(!pRun || pRun->getType() != FPRUN_FIELD)
9263 				{
9264 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9265 					return false;
9266 				}
9267 				fp_FieldRun* pFieldRun = static_cast<fp_FieldRun*>(pRun);
9268 				if(!isHdrFtr())
9269 				{
9270 					pFieldRun->clearScreen();
9271 				}
9272 				pFieldRun->lookupProperties();
9273 
9274 				goto done;
9275 			}
9276 			pRun = pRun->getNextRun();
9277 		}
9278 
9279 		return false;
9280 	}
9281 	case PTO_Math:
9282 	{
9283 		UT_DEBUGMSG(("Edit:ChangeObject:Math:\n"));
9284 		blockOffset = pcroc->getBlockOffset();
9285 		fp_Run* pRun = m_pFirstRun;
9286 		while (pRun)
9287 		{
9288 			if (pRun->getBlockOffset() == blockOffset && (pRun->getType()!= FPRUN_FMTMARK))
9289 			{
9290 				if(pRun->getType()!= FPRUN_MATH)
9291 				{
9292 					UT_DEBUGMSG(("!!! run type NOT OBJECT, instead = %d !!!! \n",pRun->getType()));
9293 					while(pRun && pRun->getType() == FPRUN_FMTMARK)
9294 					{
9295 						pRun = pRun->getNextRun();
9296 					}
9297 				}
9298 				if(!pRun || pRun->getType() != FPRUN_MATH)
9299 				{
9300 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9301 					return false;
9302 				}
9303 				fp_MathRun* pMathRun = static_cast<fp_MathRun*>(pRun);
9304 				if(!isHdrFtr())
9305 				{
9306 					pMathRun->clearScreen();
9307 				}
9308 				pMathRun->lookupProperties();
9309 
9310 				goto done;
9311 			}
9312 			pRun = pRun->getNextRun();
9313 		}
9314 
9315 		return false;
9316 	}
9317 
9318 	case PTO_Embed:
9319 	{
9320 		UT_DEBUGMSG(("Edit:ChangeObject:Embed:\n"));
9321 		blockOffset = pcroc->getBlockOffset();
9322 		fp_Run* pRun = m_pFirstRun;
9323 		while (pRun)
9324 		{
9325 			if (pRun->getBlockOffset() == blockOffset && (pRun->getType()!= FPRUN_FMTMARK))
9326 			{
9327 				if(pRun->getType()!= FPRUN_EMBED)
9328 				{
9329 					UT_DEBUGMSG(("!!! run type NOT OBJECT, instead = %d !!!! \n",pRun->getType()));
9330 					while(pRun && pRun->getType() == FPRUN_FMTMARK)
9331 					{
9332 						pRun = pRun->getNextRun();
9333 					}
9334 				}
9335 				if(!pRun || pRun->getType() != FPRUN_EMBED)
9336 				{
9337 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
9338 					return false;
9339 				}
9340 				fp_EmbedRun* pEmbedRun = static_cast<fp_EmbedRun*>(pRun);
9341 				if(!isHdrFtr())
9342 				{
9343 					pEmbedRun->clearScreen();
9344 				}
9345 				pEmbedRun->update ();
9346 				pEmbedRun->lookupProperties();
9347 
9348 				goto done;
9349 			}
9350 			pRun = pRun->getNextRun();
9351 		}
9352 
9353 		return false;
9354 	}
9355 
9356 	default:
9357 		UT_ASSERT_HARMLESS(0);
9358 		return false;
9359 	}
9360 
9361  done:
9362 	m_iNeedsReformat = blockOffset;
9363 	format();
9364 	_assertRunListIntegrity();
9365 
9366 	return true;
9367 }
9368 
recalculateFields(UT_uint32 iUpdateCount)9369 bool fl_BlockLayout::recalculateFields(UT_uint32 iUpdateCount)
9370 {
9371 	_assertRunListIntegrity();
9372 
9373 	bool bResult = false;
9374 	fp_Run* pRun = m_pFirstRun;
9375 	while (pRun)
9376 	{
9377 		if (pRun->getType() == FPRUN_FIELD)
9378 		{
9379 			fp_FieldRun* pFieldRun = static_cast<fp_FieldRun*>(pRun);
9380 			/*	TODO: Write list (fl_autonum, I think) code adding a member bool
9381 			 * indicating if the list structure has changed since the last field recalc
9382 			 * and setting it to true whenever such a change occurs (ie, adding an item,
9383 			 * deleting one, whatever).  Then here you can if(pFieldRun->getFieldType() ==
9384 			 * FPFIELD_list_label) * get the list to which it belongs, and get that list's
9385 			 * * m_bDirtyForFieldRecalc which is set true after any change * to the list
9386 			 * structure. Thus only recalc if needed.  Finally, after the loop, tell the
9387 			 * list to reset that member to false.  However, the possible down side to
9388 			 * this is that you need to recalc the entire list and not just the individual
9389 			 * fields, because otherwise you risk having an only partially recalced list
9390 			 * being left alone because it's marked clean... in retrospect I think you may
9391 			 * need to move this sort of optimization up to the DocSectionLayout redraw
9392 			 * code, rather than having it here at the block level where it might (not
9393 			 * 100% sure but might) be waaay overcalculated (having a 1-1 block-li
9394 			 * situation.  In fl_DocSectionLayout::redrawUpdate, you just make a special
9395 			 * case for if any block encountered has an autonum (as opposed to any old
9396 			 * field), and if so you do this (recalc the whole list and mark it no longer
9397 			 * dirty for recalc), and then subsequent blocks with part of the same autonum
9398 			 * will pass over recalculating it.  You may still need (or want) a new method
9399 			 * in BL to recalculateAutoNums, called separately from recalculateFields
9400 			 * (which ignores fields from autonums), to ease still recalculating
9401 			 * non-autonum fields from the DSL code.  */
9402 
9403 			xxx_UT_DEBUGMSG(("DOM: %d %d\n", pFieldRun==0, pFieldRun->needsFrequentUpdates()));
9404 
9405 			if((!iUpdateCount
9406 				|| !pFieldRun->needsFrequentUpdates()
9407 				|| !(iUpdateCount % pFieldRun->needsFrequentUpdates())))
9408 			{
9409 				const bool bSizeChanged = pFieldRun->calculateValue();
9410 				bResult |= bSizeChanged;
9411 			}
9412 		}
9413 		//
9414 		// See if Annotation or RDF has changed
9415 		//
9416 		if (pRun->getType() == FPRUN_HYPERLINK)
9417 		{
9418 				fp_HyperlinkRun * pHRun = pRun->getHyperlink();
9419 				if(pHRun && pHRun->getHyperlinkType() == HYPERLINK_ANNOTATION)
9420 				{
9421 					fp_AnnotationRun * pARun = static_cast<fp_AnnotationRun *>(pHRun);
9422 					UT_sint32 iWidth = pARun->getWidth();
9423 					pARun->recalcWidth();
9424 					if(iWidth != pARun->getWidth())
9425 					{
9426 						bResult |= true;
9427 					}
9428 				}
9429 				if(pHRun && pHRun->getHyperlinkType() == HYPERLINK_RDFANCHOR)
9430 				{
9431 					fp_RDFAnchorRun* pARun = static_cast<fp_RDFAnchorRun*>(pHRun);
9432 					UT_sint32 iWidth = pARun->getWidth();
9433 					pARun->recalcWidth();
9434 					if(iWidth != pARun->getWidth())
9435 					{
9436 						bResult |= true;
9437 					}
9438 				}
9439 		}
9440 
9441 		//				else if(pRun->isField() == true)
9442 		//	{
9443 		//		 bResult = pRun->getField()->update();
9444 		//}
9445 		pRun = pRun->getNextRun();
9446 	}
9447 
9448 	_assertRunListIntegrity();
9449 
9450 	return bResult;
9451 }
9452 
9453 
findNextTabStop(UT_sint32 iStartX,UT_sint32 iMaxX,UT_sint32 & iPosition,eTabType & iType,eTabLeader & iLeader) const9454 bool	fl_BlockLayout::findNextTabStop( UT_sint32 iStartX, UT_sint32 iMaxX, UT_sint32& iPosition,
9455 										 eTabType & iType, eTabLeader &iLeader ) const
9456 {
9457 #ifdef DEBUG
9458 	UT_sint32 iMinLeft = m_iLeftMargin;
9459   	if(getTextIndent() < 0)
9460 		iMinLeft += getTextIndent();
9461 	UT_ASSERT(iStartX >= iMinLeft);
9462 #endif
9463 
9464 	UT_uint32 iCountTabs = m_vecTabs.getItemCount();
9465 	UT_uint32 i;
9466 	if(isContainedByTOC())
9467     {
9468 		iCountTabs = 0;
9469 	}
9470 	iLeader = FL_LEADER_NONE;
9471 
9472 	for (i=0; i<iCountTabs; i++)
9473 	{
9474 		fl_TabStop* pTab = m_vecTabs.getNthItem(i);
9475 		UT_continue_if_fail(pTab);
9476 
9477 		if (pTab->getPosition() > iMaxX)
9478 		{
9479 			break;
9480 		}
9481 
9482 		if (pTab->getPosition() > iStartX)
9483 		{
9484 			if(m_iDomDirection == UT_BIDI_RTL)
9485 			{
9486 				if(m_iRightMargin > iStartX && m_iRightMargin < pTab->getPosition())
9487 				{
9488 					iPosition = m_iRightMargin;
9489 					iType = FL_TAB_RIGHT;
9490 					iLeader = FL_LEADER_NONE;
9491 				}
9492 				else
9493 				{
9494 					iPosition = pTab->getPosition();
9495 					iType = pTab->getType();
9496 					iLeader = pTab->getLeader();
9497 				}
9498 
9499 			}
9500 			else
9501 			{
9502 				if(m_iLeftMargin > iStartX && m_iLeftMargin < pTab->getPosition())
9503 				{
9504 					iPosition = m_iLeftMargin;
9505 					iType = FL_TAB_LEFT;
9506 					iLeader = FL_LEADER_NONE;
9507 				}
9508 				else
9509 				{
9510 					iPosition = pTab->getPosition();
9511 					iType = pTab->getType();
9512 					iLeader = pTab->getLeader();
9513 				}
9514 			}
9515 
9516 			return true;
9517 		}
9518 	}
9519 
9520 	// now, handle the default tabs
9521 
9522 	UT_sint32 iMin;
9523 
9524 	if(m_iDomDirection == UT_BIDI_RTL)
9525 		iMin = m_iRightMargin;
9526 	else
9527 		iMin = m_iLeftMargin;
9528 
9529 	if (iMin > iStartX)
9530 	{
9531 		iPosition = iMin;
9532 
9533 		if(m_iDomDirection == UT_BIDI_RTL)
9534 			iType = FL_TAB_RIGHT;
9535 		else
9536 			iType = FL_TAB_LEFT;
9537 
9538 		return true;
9539 	}
9540 
9541 	UT_ASSERT(m_iDefaultTabInterval > 0);
9542 
9543 	// mathematical approach
9544 	const UT_sint32 iPos = (iStartX / m_iDefaultTabInterval + 1) *
9545 		m_iDefaultTabInterval;
9546 
9547 	if(iPos > iMaxX)
9548 		iPosition = iMaxX;
9549 	else
9550 		iPosition = iPos;
9551 
9552 	if(m_iDomDirection == UT_BIDI_RTL)
9553 		iType = FL_TAB_RIGHT;
9554 	else
9555 		iType = FL_TAB_LEFT;
9556 
9557 	UT_ASSERT(iPos > iStartX);
9558 
9559 	return true;
9560 }
9561 
findPrevTabStop(UT_sint32 iStartX,UT_sint32 iMaxX,UT_sint32 & iPosition,eTabType & iType,eTabLeader & iLeader) const9562 bool	fl_BlockLayout::findPrevTabStop( UT_sint32 iStartX, UT_sint32 iMaxX, UT_sint32& iPosition,
9563 										 eTabType & iType, eTabLeader &iLeader ) const
9564 {
9565 #ifdef DEBUG
9566 	UT_sint32 iMinLeft = m_iLeftMargin;
9567 	if(getTextIndent() < 0)
9568 		iMinLeft += getTextIndent();
9569 
9570 	UT_ASSERT(iStartX >= iMinLeft);
9571 #endif
9572 
9573 	UT_uint32 iCountTabs = m_vecTabs.getItemCount();
9574 	UT_uint32 i;
9575 
9576 	iLeader = FL_LEADER_NONE;
9577 
9578 	for (i=0; i<iCountTabs; i++)
9579 	{
9580 		fl_TabStop* pTab = static_cast<fl_TabStop*>(m_vecTabs.getNthItem(i));
9581 		UT_continue_if_fail(pTab);
9582 
9583 		if (pTab->getPosition() > iMaxX)
9584 		{
9585 			break;
9586 		}
9587 
9588 		if (pTab->getPosition() > iStartX)
9589 		{
9590 			pTab = static_cast<fl_TabStop*>(m_vecTabs.getNthItem(i>0?i-1:0));
9591 			UT_continue_if_fail(pTab);
9592 
9593 			if(m_iDomDirection == UT_BIDI_RTL)
9594 			{
9595 				if(m_iRightMargin > pTab->getPosition() && m_iRightMargin < iStartX)
9596 				{
9597 					iPosition = m_iRightMargin;
9598 					iType = FL_TAB_RIGHT;
9599 					iLeader = FL_LEADER_NONE;
9600 				}
9601 				else
9602 				{
9603 					iPosition = pTab->getPosition();
9604 					iType = pTab->getType();
9605 					iLeader = pTab->getLeader();
9606 				}
9607 
9608 			}
9609 			else
9610 			{
9611 				if(m_iLeftMargin > pTab->getPosition() && m_iLeftMargin < iStartX)
9612 				{
9613 					iPosition = m_iLeftMargin;
9614 					iType = FL_TAB_LEFT;
9615 					iLeader = FL_LEADER_NONE;
9616 				}
9617 				else
9618 				{
9619 					iPosition = pTab->getPosition();
9620 					iType = pTab->getType();
9621 					iLeader = pTab->getLeader();
9622 				}
9623 			}
9624 			return true;
9625 		}
9626 	}
9627 
9628 	// the special case where there is no tabstop after the tab
9629 	// but there is something before it
9630 	if(iCountTabs > 0 && i == iCountTabs)
9631 	{
9632 			xxx_UT_DEBUGMSG(("found tabstop indx=%d\n", iCountTabs - 1));
9633 			fl_TabStop* pTab = static_cast<fl_TabStop*>(m_vecTabs.getNthItem(iCountTabs - 1));
9634 			UT_return_val_if_fail(pTab,false);
9635 
9636 			iPosition = pTab->getPosition();
9637 			iType = pTab->getType();
9638 			iLeader = pTab->getLeader();
9639 
9640 			return true;
9641 	}
9642 
9643 	// now, handle the default tabs
9644 
9645 	UT_sint32 iMin;
9646 
9647 	if(m_iDomDirection == UT_BIDI_RTL)
9648 		iMin = m_iRightMargin;
9649 	else
9650 		iMin = m_iLeftMargin;
9651 
9652 	if (iMin >= iStartX)
9653 	{
9654 		iPosition = iMin;
9655 
9656 		if(m_iDomDirection == UT_BIDI_RTL)
9657 			iType = FL_TAB_RIGHT;
9658 		else
9659 			iType = FL_TAB_LEFT;
9660 
9661 		return true;
9662 	}
9663 
9664 	UT_ASSERT(m_iDefaultTabInterval > 0);
9665 
9666 	// mathematical approach
9667 	// the -1 is to ensure we do not get iStartX
9668 	const UT_sint32 iPos = ((iStartX - 1)/ m_iDefaultTabInterval) *
9669 		m_iDefaultTabInterval;
9670 	iPosition = iPos;
9671 
9672 		if(m_iDomDirection == UT_BIDI_RTL)
9673 			iType = FL_TAB_RIGHT;
9674 		else
9675 			iType = FL_TAB_LEFT;
9676 
9677 	UT_ASSERT(iPos <= iStartX);
9678 
9679 	return true;
9680 }
9681 
s_EnumTabStops(void * myThis,UT_uint32 k,fl_TabStop * pTabInfo)9682 bool fl_BlockLayout::s_EnumTabStops( void * myThis, UT_uint32 k, fl_TabStop *pTabInfo)
9683 {
9684 	// a static function
9685 
9686 	const fl_BlockLayout * pBL = static_cast<const fl_BlockLayout*>(myThis);
9687 
9688 	UT_uint32 iCountTabs = pBL->m_vecTabs.getItemCount();
9689 	if (k >= iCountTabs)
9690 		return false;
9691 
9692 	fl_TabStop * pTab = static_cast<fl_TabStop *>(pBL->m_vecTabs.getNthItem(k));
9693 
9694 	*pTabInfo = *pTab;
9695 	return true;
9696 }
9697 
9698 
setSectionLayout(fl_SectionLayout * pSectionLayout)9699 void fl_BlockLayout::setSectionLayout(fl_SectionLayout* pSectionLayout)
9700 {
9701 	//	If we are setting the new section layout, this block
9702 	//	shouldn't already have a section.  If we are clearing
9703 	//	it, then it should already have a section.
9704 	if (pSectionLayout == NULL)
9705 	{
9706 		UT_ASSERT(m_pSectionLayout != NULL);
9707 	}
9708 	m_pSectionLayout = pSectionLayout;
9709 	if(pSectionLayout)
9710 		m_bIsHdrFtr = (pSectionLayout->getType() == FL_SECTION_HDRFTR);
9711 }
9712 
9713 //////////////////////////////////////////////////////////////////
9714 // FmtMark-related stuff
9715 //////////////////////////////////////////////////////////////////
9716 
9717 bool
doclistener_insertFmtMark(const PX_ChangeRecord_FmtMark * pcrfm)9718 fl_BlockLayout::doclistener_insertFmtMark(const PX_ChangeRecord_FmtMark* pcrfm)
9719 {
9720 	_assertRunListIntegrity();
9721 
9722 	PT_BlockOffset blockOffset = pcrfm->getBlockOffset();
9723 
9724 	xxx_UT_DEBUGMSG(("Edit:InsertFmtMark [blockOffset %ld]\n",blockOffset));
9725 
9726 	fp_FmtMarkRun * pNewRun = new fp_FmtMarkRun(this, blockOffset);
9727 	UT_ASSERT(pNewRun);
9728 	_doInsertRun(pNewRun);
9729 
9730 	// TODO is it necessary to force a reformat when inserting a
9731 	// FmtMark -- no fmt mark has no width, so it cannot change layout
9732 
9733 	FV_View* pView = getView();
9734 	if (pView && (pView->isActive() || pView->isPreview()))
9735 		pView->_setPoint(pcrfm->getPosition());
9736 	if(pView)
9737 		pView->updateCarets(pcrfm->getPosition(),0);
9738 
9739 	if (pView)
9740 	{
9741 		pView->_resetSelection();
9742 //		if(!isHdrFtr())
9743 //			pView->notifyListeners(AV_CHG_FMTCHAR);
9744 	}
9745 	m_iNeedsReformat = blockOffset;
9746 	format();
9747 	_assertRunListIntegrity();
9748 	return true;
9749 }
9750 
9751 bool
doclistener_deleteFmtMark(const PX_ChangeRecord_FmtMark * pcrfm)9752 fl_BlockLayout::doclistener_deleteFmtMark(const PX_ChangeRecord_FmtMark* pcrfm)
9753 {
9754 	UT_return_val_if_fail( m_pLayout, false );
9755 	_assertRunListIntegrity();
9756 
9757 	PT_BlockOffset blockOffset = pcrfm->getBlockOffset();
9758 
9759 	xxx_UT_DEBUGMSG(("Edit:DeleteFmtMark: [blockOffset %ld]\n",blockOffset));
9760 
9761 	// we can't use the regular _delete() since we are of length zero
9762 	_deleteFmtMark(blockOffset);
9763 
9764 	// TODO is it necessary to force a reformat when deleting a FmtMark
9765 	m_iNeedsReformat = blockOffset;
9766 	format();
9767 	updateEnclosingBlockIfNeeded();
9768 
9769 	FV_View* pView = getView();
9770 	PT_DocPosition posEOD =0;
9771 	m_pDoc->getBounds(true,posEOD);
9772 	if (pView && (pView->isActive() || pView->isPreview()))
9773 	{
9774 		pView->_resetSelection();
9775 		if(posEOD >= pcrfm->getPosition())
9776 		{
9777 			pView->_setPoint(pcrfm->getPosition());
9778 //			if(!isHdrFtr())
9779 //				pView->notifyListeners(AV_CHG_FMTCHAR);
9780 		}
9781 		else
9782 		{
9783 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
9784 		}
9785 		if(pView)
9786 			pView->updateCarets(pcrfm->getPosition(),0);
9787 	}
9788 
9789 	_assertRunListIntegrity();
9790 
9791 	return true;
9792 }
9793 
9794 /*!
9795   Delete FmtMarkRun
9796   \param blockOffset Offset of Run to delete
9797   \return True
9798 
9799   Deleting a FmtMarkRun is a special version of _delete() since a
9800   FmtMarkRun has a length of zero.
9801 
9802   \fixme FmtMarkRun should not have a length of zero - jskov
9803 */
9804 bool
_deleteFmtMark(PT_BlockOffset blockOffset)9805 fl_BlockLayout::_deleteFmtMark(PT_BlockOffset blockOffset)
9806 {
9807 	fp_Run* pRun = m_pFirstRun;
9808 	while (pRun)
9809 	{
9810 		UT_uint32 iRunBlockOffset = pRun->getBlockOffset();
9811 
9812 		// Remember where we're going, since this run may get axed
9813 		fp_Run* pNextRun = pRun->getNextRun();
9814 
9815 		if ( (iRunBlockOffset == blockOffset)
9816 			 && (pRun->getType() == FPRUN_FMTMARK) )
9817 		{
9818 			fp_Line* pLine = pRun->getLine();
9819 			UT_ASSERT(pLine);
9820 
9821 			// Remove Run from line
9822 			if(pLine)
9823 			{
9824 				pLine->removeRun(pRun);
9825 			}
9826 
9827 			// Unlink and delete it
9828 			if (m_pFirstRun == pRun)
9829 			{
9830 				m_pFirstRun = pRun->getNextRun();
9831 			}
9832 			pRun->unlinkFromRunList();
9833 			delete pRun;
9834 
9835 			if (!m_pFirstRun)
9836 			{
9837 				// By the time we get to deleting anything from a block,
9838 				// it should already have the necessary EOP in place.
9839 				UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
9840 				_insertEndOfParagraphRun();
9841 			}
9842 		}
9843 
9844 		pRun = pNextRun;
9845 	}
9846 
9847 	return true;
9848 }
9849 
doclistener_changeFmtMark(const PX_ChangeRecord_FmtMarkChange * pcrfmc)9850 bool fl_BlockLayout::doclistener_changeFmtMark(const PX_ChangeRecord_FmtMarkChange * pcrfmc)
9851 {
9852 	_assertRunListIntegrity();
9853 
9854 	PT_BlockOffset blockOffset = pcrfmc->getBlockOffset();
9855 
9856 	xxx_UT_DEBUGMSG(("Edit:ChangeFmtMark: [blockOffset %ld]\n",blockOffset));
9857 
9858 	fp_Run* pRun = m_pFirstRun;
9859 	while (pRun)
9860 	{
9861 		if ((pRun->getBlockOffset() > blockOffset) ||
9862 			((pRun->getBlockOffset() == blockOffset) && (pRun->getType() != FPRUN_FMTMARK)))
9863 		{
9864 			// The FmtMark run no longer exists. Exit function
9865 			// Since the runs and text fragments are coalesced separately, a FmtMark run may
9866 			// have been deleted even if the corresponding pf_Frag still exists
9867 			return true;
9868 		}
9869 
9870 		if (pRun->getBlockOffset() == blockOffset)
9871 		{
9872 			pRun->lookupProperties();
9873 			if(!isHdrFtr())
9874 			{
9875 				pRun->clearScreen();
9876 			}
9877 			break;
9878 		}
9879 
9880 		pRun = pRun->getNextRun();
9881 	}
9882 
9883 	// We need a reformat for blocks that only contain a format mark.
9884 	// ie. no next just a carrige return.
9885 
9886 	m_iNeedsReformat = blockOffset;
9887 	format();
9888 	updateEnclosingBlockIfNeeded();
9889 	FV_View* pView = getView();
9890 	if (pView && (pView->isActive() || pView->isPreview()))
9891 	{
9892 //		if(!isHdrFtr())
9893 //			pView->notifyListeners(AV_CHG_FMTCHAR);
9894 	}
9895 
9896 	_assertRunListIntegrity();
9897 
9898 	return true;
9899 }
9900 
9901 #ifdef ENABLE_SPELL
9902 /*!
9903  Recheck ignored words
9904 
9905  For all misspelled words in this run, call the run->drawSquiggle()
9906  method.
9907 */
9908 void
recheckIgnoredWords(void)9909 fl_BlockLayout::recheckIgnoredWords(void)
9910 {
9911 	// buffer to hold text
9912 	UT_GrowBuf pgb(1024);
9913 	bool bRes = getBlockBuf(&pgb);
9914 	UT_UNUSED(bRes);
9915 	UT_ASSERT(bRes);
9916 	const UT_UCSChar* pBlockText = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
9917 
9918 	bool bUpdate = m_pSpellSquiggles->recheckIgnoredWords(pBlockText);
9919 
9920 	// Update screen if any words squiggled
9921 	FV_View* pView = getView();
9922 	if (bUpdate && pView)
9923 	{
9924 		pView->updateScreen();
9925 	}
9926 }
9927 #endif
9928 
9929 ////////////////////////////////////////////////////////////////////////////
9930 //List Item Stuff
9931 ///////////////////////////////////////////////////////////////////////////
9932 
getListStyleString(FL_ListType iListType) const9933 gchar* fl_BlockLayout::getListStyleString( FL_ListType iListType) const
9934 {
9935 
9936 	gchar* style;
9937 
9938 	// These strings match piece table styles and should not be
9939 	// internationalized
9940 	UT_sint32 nlisttype = static_cast<UT_sint32>(iListType);
9941 	if(nlisttype < 0 || nlisttype >= static_cast<UT_sint32>(NOT_A_LIST))
9942 		style = static_cast<gchar *>(NULL);
9943 	else
9944 	{
9945 		fl_AutoLists al;
9946 		style = const_cast<gchar *>(al.getXmlList(nlisttype));
9947 	}
9948 	return style;
9949 }
9950 
getListTypeFromStyle(const gchar * style) const9951 FL_ListType fl_BlockLayout::getListTypeFromStyle( const gchar* style) const
9952 {
9953 	FL_ListType lType = NOT_A_LIST;
9954 	if(style == NULL)
9955 		return lType;
9956 	UT_uint32 j;
9957 	fl_AutoLists al;
9958 	UT_uint32 size_xml_lists = al.getXmlListsSize();
9959 	for(j=0; j < size_xml_lists; j++)
9960 	{
9961 		if( strcmp(style,al.getXmlList(j))==0)
9962 			break;
9963 	}
9964 	if(j < size_xml_lists)
9965 		lType = static_cast<FL_ListType>(j);
9966 	return lType;
9967 }
9968 
9969 
getFormatFromListType(FL_ListType iListType) const9970 char *	fl_BlockLayout::getFormatFromListType( FL_ListType iListType) const
9971 {
9972 	UT_sint32 nlisttype = static_cast<UT_sint32>(iListType);
9973 	char * pFormat = NULL;
9974 	if(nlisttype < 0 || nlisttype >= static_cast<UT_sint32>(NOT_A_LIST))
9975 		return pFormat;
9976 	fl_AutoLists al;
9977 	pFormat = const_cast<char *>(al.getFmtList(nlisttype));
9978 	return pFormat;
9979 }
9980 
decodeListType(char * listformat) const9981 FL_ListType fl_BlockLayout::decodeListType(char * listformat) const
9982 {
9983 	FL_ListType iType = NOT_A_LIST;
9984 	UT_uint32 j;
9985 	fl_AutoLists al;
9986 	UT_uint32 size_fmt_lists = al.getFmtListsSize();
9987 	for(j=0; j < size_fmt_lists; j++)
9988 	{
9989 		if( strstr(listformat,al.getFmtList(j))!=NULL)
9990 			break;
9991 	}
9992 	if(j < size_fmt_lists)
9993 		iType = static_cast<FL_ListType>(j);
9994 	return iType;
9995 }
9996 
getListType(void) const9997 FL_ListType fl_BlockLayout::getListType(void) const
9998 {
9999 	if(isListItem()==false)
10000 	{
10001 		return NOT_A_LIST;
10002 	}
10003 	else if(getAutoNum())
10004 	{
10005 		return getAutoNum()->getType();
10006 	}
10007 	else
10008 	{
10009 		return NOT_A_LIST;
10010 	}
10011 }
10012 
remItemFromList(void)10013 void fl_BlockLayout::remItemFromList(void)
10014 {
10015 	gchar lid[15], buf[5];
10016 	UT_uint32 id;
10017 	UT_DebugOnly<bool> bRet;
10018 	UT_GenericVector<const gchar*> vp;
10019 	if( m_bListLabelCreated == true)
10020 	{
10021 		m_bListLabelCreated = false;
10022 		UT_ASSERT(getView());
10023 
10024 
10025 		UT_uint32 currLevel = getLevel();
10026 		UT_ASSERT(currLevel > 0);
10027 		currLevel =0; // was currLevel--;
10028 		sprintf(buf, "%i", currLevel);
10029 		setStopping(false);
10030 		fl_BlockLayout * pNext = getNextBlockInDocument();
10031 		if (currLevel == 0)
10032 		{
10033 			id = 0;
10034 		}
10035 		else
10036 		{
10037 			id = getAutoNum()->getParent()->getID();
10038 			pNext = getPreviousList( id);
10039 		}
10040 		sprintf(lid, "%i", id);
10041 
10042 		setStopping(false);
10043 		format();
10044 		//
10045 		// Set formatiing to match the next paragraph if it exists
10046 		//
10047 		const gchar ** props = NULL;
10048 
10049 		if(pNext != NULL)
10050 		{
10051 			pNext->getListPropertyVector( &vp);
10052 			UT_sint32 countp = vp.getItemCount() + 1;
10053 			UT_sint32 i;
10054 			props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10055 			for(i=0; i<vp.getItemCount();i++)
10056 			{
10057 				if( i > 0 &&
10058 					strcmp(props[i-1],
10059 								  "text-indent")==0)
10060 				{
10061 					props[i] = "0.0000in";
10062 				}
10063 				else
10064 				{
10065 					props[i] = vp.getNthItem(i);
10066 				}
10067 			}
10068 			props[i] = static_cast<gchar *>(NULL);
10069 
10070 		}
10071 		else
10072 		{
10073 			getListPropertyVector( &vp);
10074 			UT_sint32 countp = vp.getItemCount() + 1;
10075 			UT_sint32 i;
10076 			props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10077 			for(i=0; i<vp.getItemCount();i++)
10078 			{
10079 				if( i > 0 &&
10080 					strcmp(props[i-1],
10081 								  "text-indent")==0)
10082 				{
10083 					props[i] = "0.0000in";
10084 				}
10085 				else
10086 				{
10087 					props[i] = vp.getNthItem(i);
10088 				}
10089 			}
10090 			props[i] = static_cast<gchar *>(NULL);
10091 		}
10092 		if (currLevel == 0)
10093 		{
10094 			const gchar * attribs[] = {	"listid", lid,
10095 										"level", buf, NULL, NULL };
10096 
10097 			bRet = m_pDoc->changeStruxFmt(PTC_AddFmt, getPosition(), getPosition(), attribs, props, PTX_Block);
10098 			UT_ASSERT(bRet);
10099 
10100 			m_bListItem = false;
10101 		}
10102 		else
10103 		{
10104 			const gchar * attribs[] = {	"listid", lid,
10105 											"level", buf,NULL,NULL };
10106 
10107 			bRet = m_pDoc->changeStruxFmt(PTC_AddFmt,getPosition(), getPosition(), attribs, props, PTX_Block);
10108 			UT_ASSERT(bRet);
10109 
10110 			m_pDoc->listUpdate(getStruxDocHandle());
10111 		}
10112 		//format();
10113 
10114 		//		pView->AV_View::notifyListeners(AV_CHG_FMTBLOCK);
10115 		// pView->_fixInsertionPointCoords();
10116 		FREEP(props);
10117 	}
10118 }
10119 
10120 /*!
10121  * Start a list with the paragraph definition container in the style defined by "style"
10122 \param const XML_CHar * style the name of the paragraph style for this block.
10123 */
StartList(const gchar * style,pf_Frag_Strux * prevSDH)10124 void	fl_BlockLayout::StartList( const gchar * style, pf_Frag_Strux* prevSDH)
10125 {
10126 	//
10127 	// Starts a new list at the current block with list style style all other
10128 	// attributes and properties are the default values
10129 	//
10130 	FL_ListType lType2;
10131 	PD_Style* pStyle = 0;
10132 	const gchar* szDelim     = 0;
10133 	const gchar* szDec       = 0;
10134 	const gchar* szStart     = 0;
10135 	const gchar* szAlign     = 0;
10136 	const gchar* szIndent    = 0;
10137 	const gchar* szFont      = 0;
10138 	const gchar* szListStyle = 0;
10139 	UT_uint32 startv, level, currID;
10140 
10141 	// TODO -- this mixture of float and double is a mess, we should
10142 	// either use double throughout or float
10143 	float fAlign, fIndent;
10144 
10145 	m_pDoc->getStyle(static_cast<const char *>(style), &pStyle);
10146 	if (pStyle)
10147 	{
10148 		xxx_UT_DEBUGMSG(("SEVIOR: Found list of style %s \n",style));
10149 		// Use the props in the style
10150 		pStyle->getProperty(static_cast<const gchar *>("list-delim"), szDelim);
10151 		pStyle->getProperty(static_cast<const gchar *>("list-decimal"), szDec);
10152 		pStyle->getProperty(static_cast<const gchar *>("start-value"), szStart);
10153 
10154 		if(m_iDomDirection == UT_BIDI_RTL)
10155 		   pStyle->getProperty(static_cast<const gchar *>("margin-right"), szAlign);
10156 	    else
10157 		   pStyle->getProperty(static_cast<const gchar *>("margin-left"), szAlign);
10158 
10159 		pStyle->getProperty(static_cast<const gchar *>("text-indent"), szIndent);
10160 		pStyle->getProperty(static_cast<const gchar *>("field-font"), szFont);
10161 		pStyle->getProperty(static_cast<const gchar *>("list-style"), szListStyle);
10162 		if (szStart)
10163 			startv = atoi(szStart);
10164 		else
10165 			startv = 1;
10166 
10167 		if (szAlign)
10168 			fAlign = static_cast<float>(UT_convertToInches(szAlign));
10169 		else
10170 			fAlign = static_cast<float>(LIST_DEFAULT_INDENT);
10171 		if (szIndent)
10172 			fIndent = static_cast<float>(UT_convertToInches(szIndent));
10173 		else
10174 			fIndent =  static_cast<float>(-LIST_DEFAULT_INDENT_LABEL);
10175 		double dLeft;
10176 		if(m_iDomDirection == UT_BIDI_LTR)
10177 			dLeft = UT_convertToInches(getProperty("margin-left",true));
10178 		else
10179 			dLeft = UT_convertToInches(getProperty("margin-right",true));
10180 
10181 		fAlign += static_cast<float>(dLeft);
10182 		if(!szListStyle)
10183 			szListStyle = style;
10184 		if(szDelim==NULL)
10185 			szDelim="%L";
10186 		if(szDec==NULL)
10187 			szDec=".";
10188 		if(!szFont)
10189 		{
10190 			szFont = "Times New Roman";
10191 			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
10192 		}
10193 	}
10194 	else
10195 	{
10196 		xxx_UT_DEBUGMSG(("SEVIOR: Could NOT find list of style %s \n",style));
10197 		szDelim = "%L";
10198 		startv = 1;
10199 		szDec = ".";
10200 		fAlign = static_cast<float>(LIST_DEFAULT_INDENT);
10201 		fIndent = static_cast<float>(-LIST_DEFAULT_INDENT_LABEL);
10202 		szListStyle = "Numbered List";
10203 	}
10204 
10205 	UT_uint32 count = m_pDoc->getListsCount();
10206 	UT_uint32 j = 0;
10207 	bool bFound = false;
10208 	fl_AutoNum * pPrev  = NULL;
10209 	if(prevSDH)
10210 	{
10211 		for(j=0; j< count && !bFound; j++)
10212 		{
10213 			pPrev = m_pDoc->getNthList(j);
10214 			if(pPrev->isItem(prevSDH))
10215 			{
10216 				bFound = true;
10217 			}
10218 		}
10219 	}
10220 	if(prevSDH == NULL || !bFound)
10221 	{
10222 		if (m_pAutoNum)
10223 		{
10224 			level = m_pAutoNum->getLevel();
10225 			currID = m_pAutoNum->getID();
10226 		}
10227 		else
10228 		{
10229 			level = 0;
10230 			currID = 0;
10231 		}
10232 		level++;
10233 		fAlign *= static_cast<float>(level);
10234 	}
10235 	else
10236 	{
10237 		currID = pPrev->getID();
10238 		level = pPrev->getLevel();
10239 		level++;
10240 	}
10241 
10242 	lType2 = getListTypeFromStyle(szListStyle);
10243 	StartList( lType2, startv,szDelim, szDec, szFont, fAlign, fIndent, currID,level);
10244 }
10245 
getListAttributesVector(UT_GenericVector<const gchar * > * va) const10246 void	fl_BlockLayout::getListAttributesVector(UT_GenericVector<const gchar*> * va) const
10247 {
10248 	//
10249 	// This function fills the vector va with list attributes
10250 	//
10251 	UT_uint32 count=0,level;
10252 	const gchar * style = NULL;
10253 	const gchar * lid = NULL;
10254 	static gchar  buf[5];
10255 
10256 	const PP_AttrProp * pBlockAP = NULL;
10257 	getAP(pBlockAP);
10258 	pBlockAP->getAttribute(PT_STYLE_ATTRIBUTE_NAME,style);
10259 	pBlockAP->getAttribute(static_cast<const gchar *>(PT_LISTID_ATTRIBUTE_NAME),lid);
10260 	if(getAutoNum())
10261 	{
10262 		level = getAutoNum()->getLevel();
10263 	}
10264 	else
10265 	{
10266 		level = 0;
10267 	}
10268 	sprintf(buf,"%i",level);
10269 	//	pBlockAP->getAttribute("level",buf);
10270 	if(lid != NULL)
10271 	{
10272 		va->addItem("listid");
10273 		va->addItem(lid);
10274 		count++;
10275 	}
10276 	if(buf != NULL)
10277 	{
10278 		va->addItem("level");
10279 		va->addItem(buf);
10280 		count++;
10281 	}
10282 	if(style != NULL)
10283 	{
10284 		va->addItem(PT_STYLE_ATTRIBUTE_NAME);
10285 		va->addItem(style);
10286 		count++;
10287 	}
10288 	if(count == 0)
10289 	{
10290 		va->addItem( NULL);
10291 	}
10292 }
10293 
10294 
getListPropertyVector(UT_GenericVector<const gchar * > * vp) const10295 void	fl_BlockLayout::getListPropertyVector(UT_GenericVector<const gchar*>* vp) const
10296 {
10297 	//
10298 	// This function fills the vector vp with list properties. All vector
10299 	// quantities are const gchar *
10300 	//
10301 	UT_uint32 count=0;
10302 	const gchar * pszStart = getProperty("start-value",true);
10303 	const gchar * lDelim =  getProperty("list-delim",true);
10304 	const gchar * lDecimal =  getProperty("list-decimal",true);
10305 
10306 	const gchar * pszAlign;
10307 	if(m_iDomDirection == UT_BIDI_RTL)
10308 		pszAlign =  getProperty("margin-right",true);
10309 	else
10310 		pszAlign =  getProperty("margin-left",true);
10311 
10312 	const gchar * pszIndent =  getProperty("text-indent",true);
10313 	const gchar * fFont =  getProperty("field-font",true);
10314 	const gchar * pszListStyle =  getProperty("list-style",true);
10315 	if(pszStart != NULL)
10316 	{
10317 		vp->addItem("start-value");
10318 		vp->addItem(pszStart);
10319 	}
10320 	if(pszAlign != NULL)
10321 	{
10322 		if(m_iDomDirection == UT_BIDI_RTL)
10323 			vp->addItem("margin-right");
10324 		else
10325 			vp->addItem("margin-left");
10326 
10327 		vp->addItem(pszAlign);
10328 
10329 		count++;
10330 	}
10331 	if(pszIndent != NULL)
10332 	{
10333 		vp->addItem("text-indent");
10334 		vp->addItem(pszIndent);
10335 		count++;
10336 	}
10337 	if(lDelim != NULL)
10338 	{
10339 		vp->addItem("list-delim");
10340 		vp->addItem(lDelim);
10341 		count++;
10342 	}
10343 	if(lDecimal != NULL)
10344 	{
10345 		vp->addItem("list-decimal");
10346 		vp->addItem(lDecimal);
10347 		count++;
10348 	}
10349 	if(fFont != NULL)
10350 	{
10351 		vp->addItem("field-font");
10352 		vp->addItem(fFont);
10353 		count++;
10354 	}
10355 	if(pszListStyle != NULL)
10356 	{
10357 		vp->addItem("list-style");
10358 		vp->addItem(pszListStyle);
10359 		count++;
10360 	}
10361 	if(count == 0)
10362 	{
10363 		vp->addItem( NULL);
10364 	}
10365 }
10366 
10367 
StartList(FL_ListType lType,UT_uint32 start,const gchar * lDelim,const gchar * lDecimal,const gchar * fFont,float Align,float indent,UT_uint32 iParentID,UT_uint32 curlevel)10368 void	fl_BlockLayout::StartList( FL_ListType lType, UT_uint32 start,const gchar * lDelim, const gchar * lDecimal, const gchar * fFont, float Align, float indent, UT_uint32 iParentID, UT_uint32 curlevel )
10369 {
10370 	//
10371 	// Starts a new list at the current block with all the options
10372 	//
10373 	gchar lid[15], pszAlign[20], pszIndent[20],buf[20],pid[20],pszStart[20];
10374 	gchar * style = getListStyleString(lType);
10375 	UT_DebugOnly<bool> bRet;
10376 	UT_uint32 id=0;
10377 	UT_GenericVector<const gchar*> vp,va;
10378 
10379 	fl_AutoNum * pAutoNum;
10380 	const PP_AttrProp * pBlockAP = NULL;
10381 	const gchar * szLid=NULL;
10382 	getAP(pBlockAP);
10383 	bool bGetPrevAuto = true;
10384 	if (!pBlockAP || !pBlockAP->getAttribute(PT_LISTID_ATTRIBUTE_NAME, szLid))
10385 		szLid = NULL;
10386 	if (szLid)
10387 		id = atoi(szLid);
10388 	else
10389 		{
10390 			id = 0;
10391 			bGetPrevAuto = false;
10392 		}
10393 
10394 
10395 	UT_ASSERT(getView());
10396 	if(bGetPrevAuto)
10397 	{
10398 		pAutoNum = m_pDoc->getListByID(id);
10399 		UT_DEBUGMSG(("SEVIOR: found autonum %p from id %d \n",pAutoNum,id));
10400 		if(pAutoNum != NULL)
10401 		{
10402 			m_pAutoNum = pAutoNum;
10403 			m_bListItem = true;
10404 			UT_DEBUGMSG(("Found list of id %d \n",id));
10405 			listUpdate();
10406 		}
10407 	}
10408 
10409 	UT_return_if_fail(m_pDoc);
10410 	id = m_pDoc->getUID(UT_UniqueId::List);
10411 
10412 	sprintf(lid, "%i", id);
10413 
10414 	sprintf(pid, "%i", iParentID);
10415 	sprintf(buf, "%i", curlevel);
10416 	sprintf(pszStart,"%i",start);
10417 
10418 	strncpy( pszAlign, UT_convertInchesToDimensionString(DIM_IN, Align, 0), sizeof(pszAlign));
10419 
10420 	strncpy( pszIndent, UT_convertInchesToDimensionString(DIM_IN, indent, 0), sizeof(pszIndent));
10421 
10422 	va.addItem("listid"); 		va.addItem(lid);
10423 	va.addItem("parentid");		va.addItem(pid);
10424 	va.addItem("level");		va.addItem(buf);
10425 
10426 	vp.addItem("start-value");	vp.addItem(pszStart);
10427 
10428 	if(m_iDomDirection == UT_BIDI_RTL)
10429 		vp.addItem("margin-right");
10430 	else
10431 	    vp.addItem("margin-left");
10432 
10433 	vp.addItem(pszAlign);
10434 
10435 	vp.addItem("text-indent");	vp.addItem(pszIndent);
10436 	vp.addItem("field-font"); 	vp.addItem(fFont);
10437 	vp.addItem("list-style"); 	vp.addItem(style);
10438 	vp.addItem("list-delim");	vp.addItem(lDelim);
10439 	vp.addItem("list-decimal");	vp.addItem(lDecimal);
10440 	xxx_UT_DEBUGMSG(("SEVIOR: Starting List with font %s \n",fFont));
10441 
10442 	pAutoNum = new fl_AutoNum(id, iParentID, lType, start, lDelim, lDecimal, m_pDoc, getView());
10443 	if (!pAutoNum)
10444 	{
10445 		// TODO Out of Mem.
10446 	}
10447 	m_pDoc->addList(pAutoNum);
10448 	pAutoNum->fixHierarchy();
10449 
10450 	UT_sint32 counta = va.getItemCount() + 1;
10451 	UT_sint32 countp = vp.getItemCount() + 1;
10452 	UT_sint32 i;
10453 	const gchar ** attribs = static_cast<const gchar **>(UT_calloc(counta, sizeof(gchar *)));
10454 	for(i=0; i<va.getItemCount();i++)
10455 	{
10456 		attribs[i] = va.getNthItem(i);
10457 	}
10458 	attribs[i] = static_cast<gchar *>(NULL);
10459 
10460 	const gchar ** props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10461 	for(i=0; i<vp.getItemCount();i++)
10462 	{
10463 		props[i] = vp.getNthItem(i);
10464 	}
10465 	props[i] = static_cast<gchar *>(NULL);
10466 	setStarting( false);
10467 
10468 	bRet = m_pDoc->changeStruxFmt(PTC_AddFmt, getPosition(), getPosition(), attribs, props, PTX_Block);
10469 	UT_ASSERT(bRet);
10470 
10471 	m_pDoc->listUpdate(getStruxDocHandle());
10472 	FREEP(attribs);
10473 	FREEP(props);
10474 }
10475 
10476 
StopListInBlock(void)10477 void	fl_BlockLayout::StopListInBlock(void)
10478 {
10479 	//
10480 	// Stops the list in the current block
10481 	//
10482 	static gchar lid[15], pszlevel[12];
10483 	UT_DebugOnly<bool> bRet;
10484 	UT_uint32 id, level;
10485 	UT_GenericVector<const gchar*> vp;
10486 	FV_View* pView = getView();
10487 	UT_ASSERT(pView);
10488 	bool bHasStopped = m_pDoc->hasListStopped();
10489 	if(getAutoNum()== NULL || bHasStopped)
10490 	{
10491 		return; // this block has already been processed
10492 	}
10493 	m_pDoc->setHasListStopped(true);
10494 	PT_DocPosition offset = pView->getPoint() - getPosition();
10495 	fl_BlockLayout * pPrev, * pNext;
10496 
10497 	if (getAutoNum()->getParent())
10498 	{
10499 		id = getAutoNum()->getParent()->getID();
10500 		level = getAutoNum()->getParent()->getLevel();
10501 	}
10502 	else
10503 	{
10504 		id = 0;
10505 		level = 0;
10506 	}
10507 
10508 	sprintf(lid, "%i", id);
10509 
10510 	setStopping(false);
10511 	//
10512 	// Set formatting to match the next paragraph if it exists
10513 	//
10514 	const gchar ** props = NULL;
10515 	const gchar * szAlign, * szIndent;
10516 	pPrev = getPrevBlockInDocument();
10517 	pNext = getNextBlockInDocument();
10518 
10519 	if (id != 0)
10520 	{
10521 		UT_ASSERT_HARMLESS(pPrev);	// TMN: Is an assert appropriate here?
10522 
10523 		//First, look for block in list
10524 		bool bmatch = false;
10525 		bmatch = static_cast<bool>(pPrev && pPrev->isListItem() && pPrev->getLevel() == level && pPrev->getAutoNum()->getID() == id);
10526 		while (pPrev && !bmatch)
10527 		{
10528 			pPrev = pPrev->getPrevBlockInDocument();
10529 			if (pPrev && pPrev->isListItem())
10530 				bmatch = static_cast<bool>(pPrev->getLevel() == level
10531 								&& pPrev->getAutoNum()->getID() == id);
10532 		}
10533 		while (pNext  && !bmatch)
10534 		{
10535 			pNext = pNext->getNextBlockInDocument();
10536 			if (pNext && pNext->isListItem())
10537 				bmatch = static_cast<bool>(pNext->getLevel() == level
10538 								&& pNext->getAutoNum()->getID() == id);
10539 		}
10540 
10541 		if (pPrev)
10542 			pPrev->getListPropertyVector( &vp);
10543 		else if (pNext)
10544 			pNext->getListPropertyVector( &vp);
10545 		else
10546 		{
10547 			// We have a problem
10548 			FL_ListType newType;
10549 			PD_Style * pStyle;
10550 			float fAlign, fIndent;
10551 			gchar align[30], indent[30];
10552 
10553 			newType = getAutoNum()->getParent()->getType();
10554 			m_pDoc->getStyle(static_cast<char *>(getListStyleString(newType)), &pStyle);
10555 			if (pStyle)
10556 			{
10557 				if(m_iDomDirection == UT_BIDI_RTL)
10558 					pStyle->getProperty(static_cast<const gchar *>("margin-right"), szAlign);
10559 				else
10560 					pStyle->getProperty(static_cast<const gchar *>("margin-left"), szAlign);
10561 
10562 				pStyle->getProperty(static_cast<const gchar *>("text-indent"), szIndent);
10563 				fAlign = static_cast<float>(UT_convertToInches(szAlign));
10564 				fAlign *= level;
10565 				strncpy( align,
10566 								UT_convertInchesToDimensionString(DIM_IN, fAlign, 0),
10567 								sizeof(align));
10568 				sprintf(indent, "%s", szIndent);
10569 			}
10570 			else
10571 			{
10572 				fAlign =  static_cast<float>(LIST_DEFAULT_INDENT) * level;
10573 				fIndent = static_cast<float>(-LIST_DEFAULT_INDENT_LABEL);
10574 				strncpy( align,
10575 								UT_convertInchesToDimensionString(DIM_IN, fAlign, 0),
10576 								sizeof(align));
10577 				strncpy( indent,
10578 								UT_convertInchesToDimensionString(DIM_IN, fIndent, 0),
10579 								sizeof(indent));
10580 			}
10581 
10582 			if(m_iDomDirection == UT_BIDI_RTL)
10583 				vp.addItem("margin-right");
10584 			else
10585 				vp.addItem("margin-left");
10586 
10587 			vp.addItem(align);
10588 			vp.addItem("text-indent");
10589 			vp.addItem(indent);
10590 		}
10591 	}
10592 	else
10593 	{
10594 		// Find the last non-list item and set alignment + indent
10595 		while (pPrev && pPrev->isListItem())
10596 			pPrev = pPrev->getPrevBlockInDocument();
10597 
10598 		while (pNext && pNext->isListItem())
10599 			pNext = pNext->getNextBlockInDocument();
10600 
10601 		if (pPrev)
10602 		{
10603 			if(m_iDomDirection == UT_BIDI_RTL)
10604 				szAlign = pPrev->getProperty("margin-right", true);
10605 			else
10606 				szAlign = pPrev->getProperty("margin-left", true);
10607 
10608 			szIndent =	pPrev->getProperty("text-indent", true);
10609 		}
10610 		else if (pNext)
10611 		{
10612 			if(m_iDomDirection == UT_BIDI_RTL)
10613 				szAlign = pNext->getProperty("margin-right", true);
10614 			else
10615 				szAlign = pNext->getProperty("margin-left", true);
10616 
10617 			szIndent = pNext->getProperty("text-indent", true);
10618 		}
10619 		else
10620 		{
10621 			szAlign = "0.0000in";
10622 			szIndent = "0.0000in";
10623 		}
10624 
10625 		if(m_iDomDirection == UT_BIDI_RTL)
10626 			vp.addItem("margin-right");
10627 		else
10628 			vp.addItem("margin-left");
10629 
10630 		vp.addItem(szAlign);
10631 		vp.addItem("text-indent");
10632 		vp.addItem(szIndent);
10633 	}
10634 	UT_sint32 countp = vp.getItemCount() + 1;
10635 	UT_sint32 i;
10636 	props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10637 	for (i = 0; i < vp.getItemCount(); i++)
10638 	{
10639 		props[i] = vp.getNthItem(i);
10640 	}
10641 	props[i] = NULL;
10642 	sprintf(pszlevel, "%i", level);
10643 
10644 	if (id == 0)
10645 	{
10646 		const gchar * pListAttrs[10];
10647 		pListAttrs[0] = "listid";
10648 		pListAttrs[1] = NULL;
10649 		pListAttrs[2] = "parentid";
10650 		pListAttrs[3] = NULL;
10651 		pListAttrs[4] = "level";
10652 		pListAttrs[5] = NULL;
10653 		pListAttrs[6] = "type";
10654 		pListAttrs[7] = NULL;
10655 		pListAttrs[8] = NULL;
10656 		pListAttrs[9] = NULL;
10657 
10658 		// we also need to explicitely clear the list formating
10659 		// properties, since their values are not necessarily part
10660 		// of the style definition, so that cloneWithEliminationIfEqual
10661 		// which we call later will not get rid off them
10662 		const gchar * pListProps[20];
10663 		pListProps[0] =  "start-value";
10664 		pListProps[1] =  NULL;
10665 		pListProps[2] =  "list-style";
10666 		pListProps[3] =  NULL;
10667 
10668 		if(m_iDomDirection == UT_BIDI_RTL)
10669 			pListProps[4] =  "margin-right";
10670 		else
10671 			pListProps[4] =  "margin-left";
10672 
10673 		pListProps[5] =  NULL;
10674 		pListProps[6] =  "text-indent";
10675 		pListProps[7] =  NULL;
10676 		pListProps[8] =  "field-color";
10677 		pListProps[9] =  NULL;
10678 		pListProps[10]=  "list-delim";
10679 		pListProps[11] =  NULL;
10680 		pListProps[12]=  "field-font";
10681 		pListProps[13] =  NULL;
10682 		pListProps[14]=  "list-decimal";
10683 		pListProps[15] =  NULL;
10684 		pListProps[16] =  "list-tag";
10685 		pListProps[17] =  NULL;
10686 		pListProps[18] =  NULL;
10687 		pListProps[19] =  NULL;
10688 //
10689 // Remove all the list related properties
10690 //
10691 
10692 		bRet = m_pDoc->changeStruxFmt(PTC_RemoveFmt, getPosition(), getPosition(), pListAttrs, pListProps, PTX_Block);
10693 		UT_ASSERT(bRet);
10694 
10695 		fp_Run * pRun = getFirstRun();
10696 		while(pRun->getNextRun())
10697 		{
10698 			pRun = pRun->getNextRun();
10699 		}
10700 		PT_DocPosition lastPos = getPosition(false) + pRun->getBlockOffset();
10701 		bRet = m_pDoc->changeSpanFmt(PTC_RemoveFmt, getPosition(false), lastPos, pListAttrs, pListProps);
10702 		UT_ASSERT(bRet);
10703 //
10704 // Set the indents to match.
10705 //
10706 		bRet = m_pDoc->changeStruxFmt(PTC_AddFmt, getPosition(), getPosition(), NULL, props, PTX_Block);
10707 		UT_ASSERT(bRet);
10708 
10709 		m_bListItem = false;
10710 	}
10711 	else
10712 	{
10713 		const gchar * attribs[] = {	"listid", NULL,"level",NULL, NULL,NULL };
10714 		attribs [1] = lid;
10715 		attribs [3] = pszlevel;
10716 
10717 		bRet = m_pDoc->changeStruxFmt(PTC_AddFmt,getPosition(), getPosition(), attribs, props, PTX_Block);
10718 		UT_ASSERT(bRet);
10719 
10720 		m_pDoc->listUpdate(getStruxDocHandle());
10721 	}
10722 	// format();
10723 	if (pView && (pView->isActive() || pView->isPreview()))
10724 	{
10725 		if(offset > 0 )
10726 			{
10727 				pView->_setPoint(pView->getPoint()+offset-2);
10728 				pView->updateCarets(0,offset-2);
10729 			}
10730 	}
10731 	FREEP(props);
10732 }
10733 
10734 /*!
10735  * Find the most recent block with the list ID given.
10736 \param UT_uint32 id the identifier of the list
10737 \returns fl_BlockLayout *
10738 */
getPreviousList(UT_uint32 id) const10739 fl_BlockLayout * fl_BlockLayout::getPreviousList(UT_uint32 id) const
10740 {
10741 	//
10742 	// Find the most recent list item that matches the id given
10743 	//
10744 	UT_ASSERT(m_pAutoNum);
10745 	fl_BlockLayout * pPrev = getPrevBlockInDocument();
10746 	bool bmatchid =  false;
10747 	fl_AutoNum * pAutoNum = NULL;
10748 
10749 	if (pPrev != NULL && (pPrev->getAutoNum() != NULL) && pPrev->isListItem())
10750 	{
10751 		bmatchid = static_cast<bool>(id == pPrev->getAutoNum()->getID());
10752 		if (pPrev->isFirstInList() && !bmatchid)
10753 		{
10754 			pAutoNum = pPrev->getAutoNum()->getParent();
10755 			while (pAutoNum && !bmatchid)
10756 			{
10757 				bmatchid = static_cast<bool>(id == pAutoNum->getID()
10758 								   && pAutoNum->isItem(pPrev->getStruxDocHandle()));
10759 				pAutoNum = pAutoNum->getParent();
10760 			}
10761 		}
10762 	}
10763 
10764 	while (pPrev != NULL && bmatchid == false)
10765 	{
10766 		pPrev = pPrev->getPrevBlockInDocument();
10767 		if (pPrev && (pPrev->getAutoNum() != NULL) && pPrev->isListItem())
10768 		{
10769 			bmatchid = static_cast<bool>(id == pPrev->getAutoNum()->getID());
10770 			if (pPrev->isFirstInList() && !bmatchid)
10771 			{
10772 				pAutoNum = pPrev->getAutoNum()->getParent();
10773 				while (pAutoNum && !bmatchid)
10774 				{
10775 					bmatchid = (bool)
10776 						(id == pAutoNum->getID()
10777 						 && pAutoNum->isItem(pPrev->getStruxDocHandle()));
10778 					pAutoNum = pAutoNum->getParent();
10779 				}
10780 			}
10781 		}
10782 	}
10783 
10784 	return pPrev;
10785 }
10786 
10787 
getNextList(UT_uint32 id) const10788 fl_BlockLayout * fl_BlockLayout::getNextList(UT_uint32 id) const
10789 {
10790 	//
10791 	// Find the next list  item that matches the id given
10792 	//
10793 	fl_BlockLayout * pNext = getNextBlockInDocument();
10794 	bool bmatchLevel =	false;
10795 	if( pNext != NULL && pNext->isListItem()&& (pNext->getAutoNum() != NULL))
10796 	{
10797 		bmatchLevel = static_cast<bool>(id == pNext->getAutoNum()->getID());
10798 	}
10799 	while(pNext != NULL && bmatchLevel == false)
10800 	{
10801 		pNext = pNext->getNextBlockInDocument();
10802 		if( pNext != NULL && pNext->isListItem() && (pNext->getAutoNum() != NULL))
10803 		{
10804 			bmatchLevel = static_cast<bool>(id == pNext->getAutoNum()->getID());
10805 		}
10806 	}
10807 	return pNext;
10808 }
10809 
10810 /*!
10811  * Find the most recent block with a list item.
10812 \returns fl_BlockLayout *
10813 */
getPreviousList(void) const10814 fl_BlockLayout * fl_BlockLayout::getPreviousList( void) const
10815 {
10816 	//
10817 	// Find the most recent block with a list
10818 	//
10819 	fl_BlockLayout * pPrev = getPrevBlockInDocument();
10820 	while(pPrev != NULL && !pPrev->isListItem())
10821 	{
10822 		pPrev = pPrev->getPrevBlockInDocument();
10823 	}
10824 	return pPrev;
10825 }
10826 
10827 /*!
10828  * Returns the most recent Block containing a list item of the closest match
10829  * of left-margin to this one.
10830  \returns fl_BlockLayout *
10831 */
getPreviousListOfSameMargin(void) const10832 fl_BlockLayout * fl_BlockLayout::getPreviousListOfSameMargin(void) const
10833 {
10834 
10835     const char * szAlign;
10836 	if(m_iDomDirection == UT_BIDI_RTL)
10837 		szAlign = getProperty("margin-right",true);
10838 	else
10839 		szAlign = getProperty("margin-left",true);
10840 
10841 	double dAlignMe = UT_convertToDimension(szAlign,DIM_IN);
10842 	//
10843 	// Find the most recent block with a list
10844 	//
10845 	fl_BlockLayout * pClosest = NULL;
10846 	float dClosest = 100000.;
10847 	bool bFound = false;
10848 	fl_BlockLayout * pPrev = getPrevBlockInDocument();
10849 	while(pPrev != NULL && !bFound)
10850 	{
10851 		if(pPrev->isListItem())
10852 		{
10853 			if(m_iDomDirection == UT_BIDI_RTL)
10854 				szAlign = pPrev->getProperty("margin-right",true);
10855 			else
10856 				szAlign = pPrev->getProperty("margin-left",true);
10857 
10858 			double dAlignThis = UT_convertToDimension(szAlign,DIM_IN);
10859 			float diff = static_cast<float>(fabs( static_cast<float>(dAlignThis)-dAlignMe));
10860 			if(diff < 0.01)
10861 			{
10862 				pClosest = pPrev;
10863 				bFound = true;
10864 			}
10865 			else
10866 			{
10867 				if(diff < dClosest)
10868 				{
10869 					pClosest = pPrev;
10870 					dClosest = diff;
10871 				}
10872 				pPrev = pPrev->getPrevBlockInDocument();
10873 			}
10874 		}
10875 		else
10876 		{
10877 			pPrev = pPrev->getPrevBlockInDocument();
10878 		}
10879 	}
10880 	return pClosest;
10881 }
10882 
getParentItem(void) const10883 fl_BlockLayout * fl_BlockLayout::getParentItem(void) const
10884 {
10885 	// TODO Again, more firendly.
10886 	UT_return_val_if_fail(m_pAutoNum, NULL);
10887 
10888 	fl_AutoNum * pParent = m_pAutoNum->getActiveParent();
10889 	if (pParent)
10890 		return getPreviousList(pParent->getID());
10891 	else
10892 		return NULL;
10893 }
10894 
10895 
prependList(fl_BlockLayout * nextList)10896 void  fl_BlockLayout::prependList( fl_BlockLayout * nextList)
10897 {
10898 	//
10899 	// Make the current block an element of the list before in the block nextList
10900 	//
10901 	UT_return_if_fail(nextList);
10902 	UT_GenericVector<const gchar*> va,vp;
10903 
10904 	nextList->getListPropertyVector( &vp);
10905 	nextList->getListAttributesVector( &va);
10906 	UT_sint32 counta = va.getItemCount() + 1;
10907 	UT_sint32 countp = vp.getItemCount() + 1;
10908 	UT_sint32 i;
10909 	const gchar ** attribs = static_cast<const gchar **>(UT_calloc(counta, sizeof(gchar *)));
10910 	for(i=0; i<va.getItemCount();i++)
10911 	{
10912 		attribs[i] = va.getNthItem(i);
10913 	}
10914 	attribs[i] = static_cast<gchar *>(NULL);
10915 
10916 	const gchar ** props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10917 	for(i=0; i<vp.getItemCount();i++)
10918 	{
10919 		props[i] = vp.getNthItem(i);
10920 	}
10921 	props[i] = static_cast<gchar *>(NULL);
10922 	m_bStartList =	false;
10923 	m_bStopList = false;
10924 	UT_ASSERT(getView());
10925 	m_bListLabelCreated = false;
10926 
10927 	m_pDoc->changeStruxFmt(PTC_AddFmt, getPosition(), getPosition(), attribs, props, PTX_Block);
10928 
10929 	m_bListItem = true;
10930 	m_pDoc->listUpdate(getStruxDocHandle());
10931 	FREEP(attribs);
10932 	FREEP(props);
10933 }
10934 
resumeList(fl_BlockLayout * prevList)10935 void  fl_BlockLayout::resumeList( fl_BlockLayout * prevList)
10936 {
10937 	//
10938 	// Make the current block the next element of the list in the block prevList
10939 	//
10940 	UT_return_if_fail(prevList);
10941 	UT_GenericVector<const gchar*> va,vp;
10942 //
10943 // Defensive code. This should not happen
10944 //
10945 	UT_ASSERT(prevList->getAutoNum());
10946 	if(prevList->getAutoNum() == NULL)
10947 		return;
10948 	prevList->getListPropertyVector( &vp);
10949 	prevList->getListAttributesVector( &va);
10950 	UT_sint32 counta = va.getItemCount() + 1;
10951 	UT_sint32 countp = vp.getItemCount() + 1;
10952 	UT_sint32 i;
10953 	const gchar ** attribs = static_cast<const gchar **>(UT_calloc(counta, sizeof(gchar *)));
10954 	for(i=0; i<va.getItemCount();i++)
10955 	{
10956 		attribs[i] = va.getNthItem(i);
10957 	}
10958 	attribs[i] = static_cast<gchar *>(NULL);
10959 
10960 	const gchar ** props = static_cast<const gchar **>(UT_calloc(countp, sizeof(gchar *)));
10961 	for(i=0; i<vp.getItemCount();i++)
10962 	{
10963 		props[i] = vp.getNthItem(i);
10964 	}
10965 	props[i] = static_cast<gchar *>(NULL);
10966 	m_bStartList =	false;
10967 	m_bStopList = false;
10968 	UT_ASSERT(getView());
10969 	m_bListLabelCreated = false;
10970 
10971 	m_pDoc->changeStruxFmt(PTC_AddFmt, getPosition(), getPosition(), attribs, props, PTX_Block);
10972 
10973 	m_bListItem = true;
10974 	m_pDoc->listUpdate(getStruxDocHandle());
10975 	FREEP(attribs);
10976 	FREEP(props);
10977 }
10978 
listUpdate(void)10979 void fl_BlockLayout::listUpdate(void)
10980 {
10981 	//
10982 	// Update the list on the screen to reflect changes made.
10983 	//
10984 	if(getSectionLayout() && (getSectionLayout()->getType()== FL_SECTION_HDRFTR))
10985 	{
10986 		m_pAutoNum = NULL;
10987 		return;
10988 	}
10989 	if (m_pAutoNum == NULL)
10990 		return;
10991 
10992 	if (m_bStartList == true)
10993 		m_pAutoNum->update(1);
10994 
10995 	if ((m_bListLabelCreated == false) && (m_bStopList == false))
10996 		_createListLabel();
10997 	//
10998 	// Need to recalculate the line location.
10999 	//
11000 	m_bForceSectionBreak = true;
11001 	format();
11002 
11003 }
11004 
transferListFlags(void)11005 void fl_BlockLayout::transferListFlags(void)
11006 {
11007 	//
11008 	// Transfer list flags from a block to the following list blocks
11009 	//
11010 	UT_return_if_fail(getNext());
11011 
11012 	if(getNext()->getContainerType() != FL_CONTAINER_BLOCK)
11013 	{
11014 		return;
11015 	}
11016 	if (getNextBlockInDocument()->isListItem()) // this is wrong. It should be next in the list.
11017 	{
11018 		UT_uint32 nId = getNext()->getAutoNum()->getID();
11019 		UT_uint32 cId=0, pId=0;
11020 		fl_BlockLayout * pPrev = getPreviousList();
11021 		if(pPrev && pPrev->getAutoNum() == NULL)
11022 		{
11023 			return;
11024 		}
11025 		if(pPrev != NULL)
11026 			pId = pPrev->getAutoNum()->getID();
11027 		if(isListItem())
11028 			cId = getAutoNum()->getID();
11029 		if(cId == nId)
11030 		{
11031 			if (!getNextBlockInDocument()->m_bStartList)
11032 				getNextBlockInDocument()->m_bStartList = m_bStartList;
11033 			if (!getNextBlockInDocument()->m_bStopList)
11034 				getNextBlockInDocument()->m_bStopList = m_bStopList;
11035 		}
11036 		else if ( pId == nId)
11037 		{
11038 			if (!getNextBlockInDocument()->m_bStartList)
11039 				getNextBlockInDocument()->m_bStartList = pPrev->m_bStartList;
11040 			if (!getNextBlockInDocument()->m_bStopList)
11041 				getNextBlockInDocument()->m_bStopList = pPrev->m_bStopList;
11042 		}
11043 	}
11044 }
11045 
isListLabelInBlock(void) const11046 bool  fl_BlockLayout::isListLabelInBlock( void) const
11047 {
11048 	fp_Run * pRun = m_pFirstRun;
11049 	bool bListLabel = false;
11050 	while( (pRun!= NULL) && (bListLabel == false))
11051 	{
11052 		if(pRun->getType() == FPRUN_FIELD)
11053 		{
11054 			fp_FieldRun* pFRun = static_cast<fp_FieldRun*>(pRun);
11055 			if(pFRun->getFieldType() == FPFIELD_list_label)
11056 				bListLabel = true;
11057 		}
11058 		pRun = pRun->getNextRun();
11059 	}
11060 	return bListLabel;
11061 }
11062 
isFirstInList(void) const11063 bool fl_BlockLayout::isFirstInList(void) const
11064 {
11065 	pf_Frag_Strux* sdh = fl_Layout::getStruxDocHandle();
11066 	if (!m_pAutoNum)
11067 		return false;
11068 	else
11069 		return static_cast<bool>(sdh == m_pAutoNum->getFirstItem());
11070 }
11071 
_createListLabel(void)11072 void fl_BlockLayout::_createListLabel(void)
11073 {
11074 	//
11075 	// Put the current list label into this block.
11076 	//
11077 	if(!m_pFirstRun)
11078 		return;
11079 	if (isListLabelInBlock() == true  || m_bListLabelCreated == true)
11080 	{
11081 		m_bListLabelCreated = true;
11082 		return;
11083 	}
11084 	PD_Document * pDoc = m_pLayout->getDocument();
11085 	//
11086 	// Let remote document create the list label
11087 	//
11088 	if(!pDoc->isOrigUUID())
11089 	{
11090 			return;
11091 	}
11092 	UT_ASSERT(m_pAutoNum);
11093 	xxx_UT_DEBUGMSG(("Doing create list label \n"));
11094 	FV_View* pView = getView();
11095 	PT_DocPosition offset =0;
11096 	if(pView)
11097 	{
11098 		offset = pView->getPoint() - getPosition();
11099 	}
11100 #if 1
11101 	const  gchar ** blockatt;
11102 	bool bHaveBlockAtt = pView->getCharFormat(&blockatt,true,getPosition());
11103 #endif
11104 #if 1
11105 	const gchar * tagatt[3] = {"list-tag",NULL,NULL};
11106 	gchar tagID[12];
11107 
11108 	UT_return_if_fail(m_pDoc);
11109 	UT_uint32 itag = m_pDoc->getUID(UT_UniqueId::List);
11110 
11111 	sprintf(tagID,"%d",itag);
11112 	tagatt[1] = static_cast<gchar *>(&tagID[0]);
11113 	m_pDoc->changeSpanFmt(PTC_AddFmt,getPosition(),getPosition(),NULL,const_cast<const gchar **>(tagatt));
11114 #endif
11115 
11116 	const gchar* attributes[] = {
11117 		"type","list_label",
11118 		NULL, NULL
11119 	};
11120 	UT_DebugOnly<bool> bResult = m_pDoc->insertObject(getPosition(), PTO_Field, attributes, NULL);
11121 	UT_ASSERT(bResult);
11122 	PT_DocPosition diff = 1;
11123 	if(m_pDoc->isDoingPaste() == false)
11124 	{
11125 		UT_UCSChar c = UCS_TAB;
11126 		const PP_AttrProp * pSpanAP = NULL;
11127 		getSpanAP(1, false, pSpanAP);
11128 		bResult = m_pDoc->insertSpan(getPosition()+1,&c,1,const_cast< PP_AttrProp *>(pSpanAP));
11129 		diff = 2;
11130 	}
11131 //
11132 // I don't think we need this.
11133 // We definately need this to preserve attributes on new list lines
11134 //
11135 //
11136 //	UT_uint32 i =0;
11137 //  	while(blockatt[i] != NULL)
11138 //  	{
11139 //  		UT_DEBUGMSG(("SEVIOR: Applying blockatt[i] %s at %d %d \n",blockatt[i],getPosition(),getPosition()+diff));
11140 //  		i++;
11141 //  	}
11142 
11143 	// FV_View::getCharFmt() can sometimes return a static temporary
11144 	if(bHaveBlockAtt)
11145 	{
11146 		m_pDoc->changeSpanFmt(PTC_AddFmt,getPosition(),getPosition()+diff,NULL,static_cast<const char **>(blockatt));
11147 		FREEP(blockatt);
11148 	}
11149 
11150 
11151 	if (pView && (pView->isActive() || pView->isPreview()))
11152 	{
11153 		pView->_setPoint(pView->getPoint()+offset);
11154 		pView->updateCarets(0,offset);
11155 	}
11156 	m_bListLabelCreated = true;
11157 }
11158 
deleteListLabel(void)11159 void fl_BlockLayout::deleteListLabel(void)
11160 {
11161 	_deleteListLabel();
11162 }
11163 
11164 
_deleteListLabel(void)11165 void fl_BlockLayout::_deleteListLabel(void)
11166 {
11167 	//
11168 	// Remove the current list label from the block. This code does not assume the
11169 	// label is at the first position in the block
11170 	//
11171 	PD_Document * pDoc = m_pLayout->getDocument();
11172 	//
11173 	// Let remote document create the list label
11174 	//
11175 	if(!pDoc->isOrigUUID())
11176 	{
11177 			return;
11178 	}
11179 	UT_uint32 posBlock = getPosition();
11180 	// Find List Label
11181 	fp_Run * pRun = getFirstRun();
11182 	bool bStop = false;
11183 	m_bListLabelCreated = false;
11184 	//
11185 	// Search within the block for the list label
11186 	//
11187 	while(bStop == false && pRun != NULL)
11188 	{
11189 		if(pRun->getType() == FPRUN_FIELD)
11190 		{
11191 			fp_FieldRun * pFRun = static_cast<fp_FieldRun *>(pRun);
11192 			if(pFRun->getFieldType() == FPFIELD_list_label)
11193 			{
11194 				bStop = true;
11195 				break;
11196 			}
11197 		}
11198 		pRun = pRun->getNextRun();
11199 		if(pRun == NULL)
11200 		{
11201 			bStop = true;
11202 		}
11203 	}
11204 	if(pRun != NULL)
11205 	{
11206 		UT_uint32 ioffset = pRun->getBlockOffset();
11207 		UT_uint32 npos = 1;
11208 		fp_Run * tRun = pRun->getNextRun();
11209 		if(tRun != NULL && tRun->getType()==FPRUN_TAB)
11210 		{
11211 			npos = 2;
11212 		}
11213 
11214 		UT_uint32 iRealDeleteCount;
11215 		pDoc->deleteSpan(posBlock+ioffset, posBlock+ioffset + npos,NULL,iRealDeleteCount);
11216 	}
11217 }
11218 
getListLabel(void) const11219 UT_UCSChar * fl_BlockLayout::getListLabel(void) const
11220 {
11221 	//	UT_ASSERT(m_pAutoNum);
11222 	//
11223 	// Return the calculated list label for the block
11224 	//
11225 	if(m_pAutoNum != NULL)
11226 		return const_cast<UT_UCSChar *>(m_pAutoNum->getLabel(getStruxDocHandle()));
11227 	else
11228 		return NULL;
11229 }
11230 
_addBlockToPrevList(fl_BlockLayout * prevBlockInList,UT_uint32 level)11231 inline void fl_BlockLayout::_addBlockToPrevList( fl_BlockLayout * prevBlockInList, UT_uint32 level)
11232 {
11233 	//
11234 	// Insert the current block to the list at the point after prevBlockInList
11235 	//
11236 	fl_AutoNum * pAutoNum;
11237 	bool bMatchList = false;
11238 
11239 	UT_return_if_fail(prevBlockInList);
11240 
11241 	pAutoNum = prevBlockInList->getAutoNum();
11242 	while(pAutoNum && !bMatchList)
11243 	{
11244 		if (pAutoNum->getLevel() == level)
11245 		{
11246 			bMatchList = true;
11247 			UT_DEBUGMSG(("Matched List. Returning.\n"));
11248 		}
11249 		else
11250 		{
11251 			pAutoNum = pAutoNum->getParent();
11252 			UT_DEBUGMSG(("Didn't match list. Going Up.\n"));
11253 		}
11254 	}
11255 	UT_DEBUGMSG(("Found List with Id: %d\n", pAutoNum->getID()));
11256 	m_pAutoNum = pAutoNum;
11257 	m_pAutoNum->insertItem(getStruxDocHandle(), prevBlockInList->getStruxDocHandle());
11258 }
11259 
11260 
_prependBlockToPrevList(fl_BlockLayout * nextBlockInList)11261 inline void fl_BlockLayout::_prependBlockToPrevList( fl_BlockLayout * nextBlockInList)
11262 {
11263 	//
11264 	// Insert the current block to the list at the point before nextBlockInList
11265 	//
11266 	UT_return_if_fail(nextBlockInList);
11267 	m_pAutoNum = nextBlockInList->getAutoNum();
11268 	m_pAutoNum->prependItem(getStruxDocHandle(), nextBlockInList->getStruxDocHandle());
11269 }
11270 
getLevel(void) const11271 UT_uint32 fl_BlockLayout::getLevel(void) const
11272 {
11273 	if (!m_pAutoNum)
11274 		return 0;
11275 	else return m_pAutoNum->getLevel();
11276 }
11277 
setStarting(bool bValue)11278 void fl_BlockLayout::setStarting( bool bValue )
11279 {
11280 	m_bStartList = bValue;
11281 }
11282 
setStopping(bool bValue)11283 void fl_BlockLayout::setStopping( bool bValue)
11284 {
11285 	m_bStopList = bValue;
11286 }
11287 
11288 /*!
11289  * This Method searches for the next piece of of the block that could
11290  * be used for texttotable conversions.
11291 \returns true if a valid piece of text was found and there is more, false otherwise
11292 \param buf reference to a growbug containing the text in the block
11293 \param startPos - start search from this position
11294 \param begPos - first character of the word
11295 \param endPos - Last character of the word
11296 \param sWord - UTF8 string containing the word
11297 \param delim: use tab (0), comma (1), space (2) or all (>2) as delimiters
11298 */
getNextTableElement(UT_GrowBuf * buf,PT_DocPosition startPos,PT_DocPosition & begPos,PT_DocPosition & endPos,UT_UTF8String & sWord,UT_uint32 iDelim) const11299 bool fl_BlockLayout::getNextTableElement(UT_GrowBuf * buf,
11300 										 PT_DocPosition startPos,
11301 										 PT_DocPosition & begPos,
11302 										 PT_DocPosition & endPos,
11303 										 UT_UTF8String & sWord,
11304 										 UT_uint32 iDelim) const
11305 {
11306 	UT_uint32 offset = startPos - getPosition(false);
11307 	UT_uint32 i = 0;
11308 	UT_UCS4Char curChar = 0;
11309 	if(offset >= buf->getLength())
11310 	{
11311 		begPos = 0;
11312 		endPos = 0;
11313 		return false;
11314 	}
11315 	UT_uint32 iMax = buf->getLength() - offset;
11316 	bool bFoundFootnote = false;
11317 	//
11318 	// skip initial spaces
11319 	for(i= 0; i < iMax; i++)
11320 	{
11321 		curChar = static_cast<UT_UCS4Char>(*buf->getPointer(offset+i));
11322 		xxx_UT_DEBUGMSG(("Pre CurChar %c pos %d \n",curChar,offset+i+begPos));
11323 		if(curChar == 7)
11324 		{
11325 			break; // don't split on fields
11326 		}
11327 		//
11328 		// Don't split on numbers
11329 		//
11330 		if(curChar >= static_cast<UT_uint32>('0') && curChar <= static_cast<UT_uint32>('9'))
11331 	    {
11332 			break;
11333 		}
11334 		if(!(curChar == UCS_SPACE))
11335 		{
11336 			break;
11337 		}
11338 	}
11339 	if( i == iMax)
11340 	{
11341 		begPos = 0;
11342 		endPos = 0;
11343 		return false;
11344 	}
11345 	begPos = getPosition(false) + offset + i;
11346 	for(; i< iMax; i++)
11347 	{
11348 		curChar = static_cast<UT_UCS4Char>(*buf->getPointer(offset+i));
11349 		xxx_UT_DEBUGMSG(("CurChar %c pos %d \n",curChar,offset+i+begPos));
11350 		if(curChar == 0)
11351 		{
11352 			PT_DocPosition pos = offset+i+begPos;
11353 			if(m_pDoc->isFootnoteAtPos(pos))
11354 			{
11355 				bFoundFootnote = true;
11356 				continue;
11357 			}
11358 			if(m_pDoc->isEndFootnoteAtPos(pos))
11359 			{
11360 				bFoundFootnote = false;
11361 				continue;
11362 			}
11363 		}
11364 		if(bFoundFootnote)
11365 		{
11366 			continue;
11367 		}
11368 		sWord += curChar;
11369 		if(curChar == 7)
11370 		{
11371 			continue; // don't split on fields
11372 		}
11373 		//
11374 		// Don't split on numbers
11375 		//
11376 		if(curChar >= static_cast<UT_uint32>('0') && curChar <= static_cast<UT_uint32>('9'))
11377 	    {
11378 			continue;
11379 		}
11380 		if(UT_isWordDelimiter(curChar,UCS_UNKPUNK,UCS_UNKPUNK))
11381 		{
11382 			if(((iDelim == 0) && (curChar == UCS_TAB)) ||
11383 			   ((iDelim == 1) && (curChar == ',')) ||
11384 			   ((iDelim == 2) && (curChar == UCS_SPACE)) ||
11385 			   ((iDelim >  2) && (curChar==',' || curChar== UCS_TAB || curChar== UCS_SPACE)))
11386 			{
11387 				break;
11388 			}
11389 		}
11390 	}
11391 	if(i< iMax)
11392 	{
11393 		endPos = getPosition(false) + offset + i;
11394 	}
11395 	else
11396 	{
11397 		endPos = getPosition(false) + offset + i;
11398 	}
11399 	xxx_UT_DEBUGMSG(("Split at %d \n",endPos));
11400 	return true;
11401 }
11402 
setDominantDirection(UT_BidiCharType iDirection)11403 void fl_BlockLayout::setDominantDirection(UT_BidiCharType iDirection)
11404 {
11405 	m_iDomDirection = iDirection;
11406 	gchar * prop[] = {NULL, NULL, 0};
11407 	gchar   ddir[] = "dom-dir";
11408 	gchar   rtl[]  = "rtl";
11409 	gchar   ltr[]  = "ltr";
11410 
11411 	prop[0] = static_cast<gchar *>(&ddir[0]);
11412 
11413 	if(m_iDomDirection == UT_BIDI_RTL)
11414 	{
11415 		prop[1] = static_cast<gchar *>(&rtl[0]);
11416 	}
11417 	else
11418 	{
11419 		prop[1] = static_cast<gchar *>(&ltr[0]);
11420 	}
11421 
11422 	PT_DocPosition offset = getPosition();
11423 	PT_DocPosition offset2 = offset;
11424 	//NB The casts in the following call are really necessary, it refuses to compile without them. #TF
11425 	getDocument()->changeStruxFmt(static_cast<PTChangeFmt>(PTC_AddFmt),
11426 								  offset, offset2,
11427 								  static_cast<const gchar **>(NULL),
11428 								  const_cast<const gchar **>(prop),
11429 								  static_cast<PTStruxType>(PTX_Block));
11430 	UT_DEBUGMSG(("Block::setDominantDirection: offset=%d\n", offset));
11431 }
11432 
11433 /*!
11434  Squiggle block being checked (for debugging)
11435 
11436  Trivial background checker which puts on and takes off squiggles from
11437  the entire block that's being checked.  This sort of messes up the
11438  spelling squiggles, but it's just a debug thing anyhow.  Enable it by
11439  setting a preference DebugFlash="1"
11440 */
11441 void
debugFlashing(void)11442 fl_BlockLayout::debugFlashing(void)
11443 {
11444 #if 0
11445 	xxx_UT_DEBUGMSG(("fl_BlockLayout::debugFlashing() was called\n"));
11446 
11447 	UT_GrowBuf pgb(1024);
11448 	bool bRes = getBlockBuf(&pgb);
11449 	UT_ASSERT(bRes);
11450 
11451 	UT_uint32 eor = pgb.getLength(); // end of region
11452 	FV_View* pView = getView();
11453 
11454 	fl_PartOfBlock* pPOB = new fl_PartOfBlock(0, eor);
11455 	UT_ASSERT(pPOB);
11456 	if (pPOB) {
11457 		m_pSpellSquiggles->add(pPOB);
11458 		m_pSpellSquiggles->clear(pPOB);
11459 
11460 		pView->updateScreen();
11461 		UT_usleep(250000);
11462 
11463 		//_deleteSquiggles(0, eor);
11464 
11465 		pView->updateScreen();
11466 	}
11467 
11468 	pView->updateScreen();
11469 #endif
11470 }
11471 
11472 
findRunAtOffset(UT_uint32 offset) const11473 fp_Run* fl_BlockLayout::findRunAtOffset(UT_uint32 offset) const
11474 {
11475 	fp_Run * pRun = getFirstRun();
11476 	UT_return_val_if_fail(pRun, NULL);
11477 
11478 	fp_Run * pRunResult = NULL;
11479 
11480 	while (pRun)
11481 	{
11482 		if(    pRun->getBlockOffset() <= offset
11483 		   && (pRun->getBlockOffset() + pRun->getLength()) > offset )
11484 		{
11485 			pRunResult = pRun;
11486 			break;
11487 		}
11488 
11489 		pRun = pRun->getNextRun();
11490 	}
11491 
11492 	return pRunResult;
11493 }
11494 
_canContainPoint() const11495 bool fl_BlockLayout::_canContainPoint() const
11496 {
11497 	return isContainedByTOC() == false;
11498 }
11499 
11500 /*!
11501     this function decides if character c is a word-delimiter, taking on board revisions
11502     markup
11503  */
11504 
isWordDelimiter(UT_UCS4Char c,UT_UCS4Char next,UT_UCS4Char prev,UT_uint32 iBlockPos) const11505 bool fl_BlockLayout::isWordDelimiter(UT_UCS4Char c, UT_UCS4Char next, UT_UCS4Char prev, UT_uint32 iBlockPos) const
11506 {
11507 	if(c == 0)
11508 		return true;
11509 	if(!UT_isWordDelimiter(c, next, prev))
11510 		return false;
11511 	// see if this character has not been deleted in revisions mode ...
11512 	fp_Run * pRun = findRunAtOffset(iBlockPos);
11513 
11514 	if(pRun== NULL && (next == 0))
11515 	{
11516 		return true;
11517 	}
11518 	if(pRun == NULL)
11519 	{
11520 		xxx_UT_DEBUGMSG(("No run where one is expected block %x iBlockPos %d \n",this,iBlockPos));
11521 		return false;
11522 	}
11523 	//	UT_return_val_if_fail( pRun, false );
11524 
11525 	// ignore hidden runs
11526 	if(pRun->getVisibility() != FP_VISIBLE)
11527 		return false;
11528 
11529 	if(!pRun->containsRevisions())
11530 		return true;
11531 
11532 	if(pRun->getRevisions()->getLastRevision()->getType() == PP_REVISION_DELETION)
11533 		return false;
11534 
11535 	return true;
11536 }
11537 
isSentenceSeparator(UT_UCS4Char c,UT_uint32 iBlockPos) const11538 bool fl_BlockLayout::isSentenceSeparator(UT_UCS4Char c, UT_uint32 iBlockPos) const
11539 {
11540 	if(!UT_UCS4_isSentenceSeparator(c))
11541 		return false;
11542 
11543 	// see if this character has not been deleted in revisions mode ...
11544 	fp_Run * pRun = findRunAtOffset(iBlockPos);
11545 	UT_return_val_if_fail( pRun, false );
11546 
11547 	// ignore hidden runs
11548 	if(pRun->getVisibility() != FP_VISIBLE)
11549 		return false;
11550 
11551 	if(!pRun->containsRevisions())
11552 		return true;
11553 
11554 	if(pRun->getRevisions()->getLastRevision()->getType() == PP_REVISION_DELETION)
11555 		return false;
11556 
11557 	return true;
11558 }
11559 
11560 
11561 
11562 
11563 #ifdef ENABLE_SPELL
enqueueToSpellCheckAfter(fl_BlockLayout * prev)11564 void fl_BlockLayout::enqueueToSpellCheckAfter(fl_BlockLayout *prev)
11565 {
11566 	if (prev != NULL) {
11567 		m_nextToSpell = prev->m_nextToSpell;
11568 		prev->m_nextToSpell = this;
11569 	}
11570 	else {
11571 		m_nextToSpell = m_pLayout->spellQueueHead();
11572 		m_pLayout->setSpellQueueHead(this);
11573 	}
11574 	if (m_nextToSpell != NULL) {
11575 		m_nextToSpell->m_prevToSpell = this;
11576 	}
11577 	else {
11578 		m_pLayout->setSpellQueueTail(this);
11579 	}
11580 	m_prevToSpell = prev;
11581 }
11582 
11583 
dequeueFromSpellCheck(void)11584 void fl_BlockLayout::dequeueFromSpellCheck(void)
11585 {
11586 	if (m_prevToSpell != NULL) {
11587 		m_prevToSpell->m_nextToSpell = m_nextToSpell;
11588 	}
11589 	else if(m_pLayout->spellQueueHead() == this) {
11590 		m_pLayout->setSpellQueueHead(m_nextToSpell);
11591 	}
11592 	if (m_nextToSpell != NULL) {
11593 		m_nextToSpell->m_prevToSpell = m_prevToSpell;
11594 	}
11595 	else if (m_pLayout->spellQueueTail() == this) {
11596 		m_pLayout->setSpellQueueTail(m_prevToSpell);
11597 	}
11598 	m_nextToSpell = m_prevToSpell = NULL;
11599 }
11600 
11601 /*!
11602   Constructor for iterator
11603 
11604   Use the iterator to find words for spell-checking in the block.
11605 
11606   \param pBL BlockLayout this iterator should work on
11607   \param iPos Position the iterator should start from
11608 */
fl_BlockSpellIterator(const fl_BlockLayout * pBL,UT_sint32 iPos)11609 fl_BlockSpellIterator::fl_BlockSpellIterator(const fl_BlockLayout* pBL, UT_sint32 iPos)
11610 	: m_pBL(pBL), m_iWordOffset(iPos), m_iStartIndex(iPos), m_iPrevStartIndex(iPos),
11611 	  m_pMutatedString(NULL),
11612 	  m_iSentenceStart(0), m_iSentenceEnd(0)
11613 {
11614 	m_pgb = new UT_GrowBuf(1024);
11615 	bool bRes = pBL->getBlockBuf(m_pgb);
11616 	UT_UNUSED(bRes);
11617 	UT_ASSERT(bRes);
11618 	m_pText = reinterpret_cast<UT_UCS4Char*>(m_pgb->getPointer(0));
11619 	m_iLength = m_pgb->getLength();
11620 }
11621 
11622 /*!
11623   Destructor for iterator
11624 */
~fl_BlockSpellIterator()11625 fl_BlockSpellIterator::~fl_BlockSpellIterator()
11626 {
11627 	DELETEP(m_pgb);
11628 	FREEP(m_pMutatedString);
11629 }
11630 
11631 /*!
11632   Get length of the block text
11633   \return Length of the block
11634 */
11635 UT_sint32
getBlockLength(void) const11636 fl_BlockSpellIterator::getBlockLength(void) const
11637 {
11638 	return m_iLength;
11639 }
11640 
11641 /*!
11642   Update block information for this iterator
11643 
11644   This method must be called whenever the block this iterator is
11645   associated with changes.
11646 */
11647 void
updateBlock(void)11648 fl_BlockSpellIterator::updateBlock(void)
11649 {
11650 	m_pgb->truncate(0);
11651 	bool bRes = m_pBL->getBlockBuf(m_pgb);
11652 	UT_UNUSED(bRes);
11653 	UT_ASSERT(bRes);
11654 	m_pText = reinterpret_cast<UT_UCS4Char*>(m_pgb->getPointer(0));
11655 
11656 	UT_sint32 iNewLen = m_pgb->getLength();
11657 	if (iNewLen <= m_iStartIndex)
11658 	{
11659 		m_iStartIndex = iNewLen;
11660 		m_iPrevStartIndex = iNewLen;
11661 	}
11662 
11663 	m_iLength = iNewLen;
11664 
11665 
11666 	m_iWordOffset = 0;
11667 	m_iWordLength = 0;
11668 }
11669 
11670 
11671 /*!
11672   Returns next word for spell checking in block
11673 
11674   The method finds the next word in the block for spell checking. It
11675   takes care of ignoring words as per user configuration and speller
11676   limitations. It also makes necessary tweaks to the word (such as
11677   right-quote to ASCII-quote conversion).
11678 
11679   If the block is changed between calls to this method, the
11680   updateBlock method must be called.
11681 
11682   \result pWord Pointer to word.
11683   \result iLength Length of word.
11684   \result iBlockPost The word's position in the block
11685   \return True if word was found, false otherwise.
11686 */
11687 
11688 typedef struct
11689 {
11690 	UT_uint32 iStart;
11691 	UT_uint32 iEnd;
11692 	bool      bIgnore;
11693 } _spell_type;
11694 
11695 
11696 /*!
11697     pWord -- pointer to next word
11698     iLength -- length of the word in the pWord buffer
11699     iBlockPos -- block offset of the word
11700     iPTLenth -- the lenth of the word in the Piece Table (can be > iLength)
11701 */
11702 bool
nextWordForSpellChecking(const UT_UCSChar * & pWord,UT_sint32 & iLength,UT_sint32 & iBlockPos,UT_sint32 & iPTLength)11703 fl_BlockSpellIterator::nextWordForSpellChecking(const UT_UCSChar*& pWord, UT_sint32& iLength,
11704 												UT_sint32& iBlockPos, UT_sint32& iPTLength)
11705 {
11706 	// For empty blocks, there will be no buffer
11707 	if (NULL == m_pText) return false;
11708 	UT_return_val_if_fail( m_pBL, false );
11709 
11710 	for (;;) {
11711 
11712 		bool bFound = false;
11713 		bool bWordStartFound = false;
11714 		m_iWordOffset = m_iStartIndex;
11715 
11716 		// Special case for first character in block - checked
11717 		// seperately to avoid in loop below
11718 		if (0 == m_iWordOffset)
11719 		{
11720 			UT_UCSChar followChar = (((m_iWordOffset + 1) < m_iLength)
11721 									 ?  m_pText[m_iWordOffset+1] : UCS_UNKPUNK);
11722 			if (!m_pBL->isWordDelimiter( m_pText[m_iWordOffset], followChar, UCS_UNKPUNK, m_iWordOffset))
11723 			{
11724 				bWordStartFound = true;
11725 			}
11726 			else
11727 			{
11728 				m_iWordOffset++;
11729 			}
11730 		}
11731 
11732 		// If start of word not found, keep looking (until the last
11733 		// character but one - avoids boundary checks for the
11734 		// followChar argument)
11735 		if (!bWordStartFound) {
11736 			while (m_iWordOffset < (m_iLength-1))
11737 			{
11738 				if (!m_pBL->isWordDelimiter( m_pText[m_iWordOffset],
11739 											 m_pText[m_iWordOffset+1],
11740 											 m_pText[m_iWordOffset-1],
11741 											 m_iWordOffset))
11742 				{
11743 					bWordStartFound = true;
11744 					break;
11745 				}
11746 				m_iWordOffset++;
11747 			}
11748 		}
11749 
11750 		// No word start has been found. We still have to check the
11751 		// last character in the block, but even if it is a word
11752 		// character, we don't spell-check one-character words, so
11753 		// there's no reason to make the effort. Just exit...
11754 		if (!bWordStartFound) {
11755 			return false;
11756 		}
11757 
11758 		// Now we have the starting position of the word in
11759 		// m_iWordOffset.
11760 
11761 		// Ignore some initial characters
11762 		if (_ignoreFirstWordCharacter(m_pText[m_iWordOffset]))
11763         {
11764 			m_iWordOffset++;
11765         }
11766 
11767 		// If we're at the end of the block after ignoring characters,
11768 		// nothing more to do
11769 		if (m_iWordOffset == m_iLength)
11770         {
11771             return false;
11772         }
11773 
11774 		// We're at the start of a word. Find end of word while
11775 		// keeping track of numerics and case of letters. Again, only
11776 		// check until the last but one character to avoid followChar
11777 		// boundary checks...
11778 		bool bAllUpperCase = true;
11779 		bool bHasNumeric = false;
11780 		UT_sint32 iWordEnd = m_iWordOffset;
11781 		if (0 == iWordEnd) {
11782 			// Special check for first letter in the block - which can
11783 			// never be a word delimiter (we skipped those in the
11784 			// first loop of this method, remember?) - so juct collect
11785 			// the property data
11786             bAllUpperCase &= UT_UCS4_isupper(m_pText[iWordEnd]);
11787             bHasNumeric |= UT_UCS4_isdigit(m_pText[iWordEnd]);
11788 
11789             iWordEnd++;
11790         }
11791 
11792 		while (!bFound && (iWordEnd < (m_iLength-1)))
11793 		{
11794 			if (m_pBL->isWordDelimiter( m_pText[iWordEnd],
11795 										m_pText[iWordEnd+1],
11796 										m_pText[iWordEnd-1],
11797 										iWordEnd))
11798 			{
11799 				bFound = true;
11800 			}
11801 			else
11802 			{
11803 				if (bAllUpperCase)
11804 				{
11805 					// Only check as long as all seen characters have
11806 					// been upper case. Most words will cause
11807 					// bAllUpperCase to go false pretty early, so we
11808 					// can save the lookup...
11809 					bAllUpperCase &= UT_UCS4_isupper(m_pText[iWordEnd]);
11810 				}
11811 				// It's not worth making this lookup conditional:
11812 				// majority of words do not contain digits, so the
11813 				// if-statement will just become an overhead...
11814 				bHasNumeric |= UT_UCS4_isdigit(m_pText[iWordEnd]);
11815 
11816 				iWordEnd++;
11817 			}
11818 		}
11819 
11820 		// Check last character in block if necessary
11821 		if (!bFound && iWordEnd != m_iLength)
11822 		{
11823 			UT_ASSERT(iWordEnd == (m_iLength-1));
11824 
11825 			if (m_pBL->isWordDelimiter(m_pText[iWordEnd],
11826 										  UCS_UNKPUNK,
11827 										  m_pText[iWordEnd-1],
11828 										  iWordEnd))
11829 			{
11830 				bFound = true;
11831 			}
11832 			else
11833 			{
11834 				if (bAllUpperCase)
11835 					bAllUpperCase &= UT_UCS4_isupper(m_pText[iWordEnd]);
11836 				bHasNumeric |= UT_UCS4_isdigit(m_pText[iWordEnd]);
11837 
11838 				iWordEnd++;
11839 			}
11840 		}
11841 		UT_ASSERT(bFound || iWordEnd == m_iLength);
11842 
11843 		// This is where we want to start from at next call.
11844 		m_iPrevStartIndex = m_iStartIndex;
11845 		m_iStartIndex = iWordEnd;
11846 
11847 		// Find length of word
11848 		UT_sint32 iWordLength = static_cast<UT_sint32>(iWordEnd) - static_cast<UT_sint32>(m_iWordOffset);
11849 
11850 		// ignore some terminal characters
11851 		UT_sint32 tempIDX = static_cast<UT_sint32>(m_iWordOffset) + iWordLength - 1;
11852 		UT_ASSERT(tempIDX >= 0);
11853 		if (_ignoreLastWordCharacter(m_pText[tempIDX]))
11854 		{
11855 			iWordLength--;
11856 		}
11857 
11858 		// Ignore words where first character is a digit
11859 		if (UT_UCS4_isdigit(m_pText[m_iWordOffset]))
11860 		{
11861 			continue;
11862 		}
11863 
11864 		// Don't check all-UPPERCASE words unless so configured
11865 		if (bAllUpperCase && !m_pBL->getView()->getLayout()->getSpellCheckCaps())
11866 		{
11867 			continue;
11868 		}
11869 
11870 		// Don't check words with numbers unless so configured
11871 		if (bHasNumeric && !m_pBL->getView()->getLayout()->getSpellCheckNumbers())
11872 		{
11873 			continue;
11874 		}
11875 
11876 		// TODO i18n the CJK stuff here is a hack
11877 		if (!XAP_EncodingManager::get_instance()->noncjk_letters(m_pText+m_iWordOffset, iWordLength))
11878 		{
11879 			continue;
11880 		}
11881 
11882 
11883 		// These are the current word details
11884 		UT_uint32 iNewLength = iWordLength;
11885 		iPTLength = iWordLength;
11886 		pWord = &m_pText[m_iWordOffset];
11887 
11888 		// Now make any necessary mutations to the word before it is
11889 		// returned. Normal case is that no changes are necessary, so
11890 		// do this in two loops, only executing the second if any
11891 		// mutation is necessary. This means the normal case will not
11892 		// require the allocation+copy of the word.
11893 		FREEP(m_pMutatedString);
11894 		bool bNeedsMutation = false;
11895 		for (UT_uint32 i=0; i < static_cast<UT_uint32>(iWordLength); i++)
11896 		{
11897 			UT_UCSChar currentChar = m_pText[m_iWordOffset + i];
11898 
11899 			if (currentChar == UCS_ABI_OBJECT || currentChar == UCS_RQUOTE)
11900 			{
11901 				bNeedsMutation = true;
11902 				break;
11903 			}
11904 		}
11905 
11906 		// handle revisions and hidden text correctly
11907 		// hidden text is to be ignored (i.e., hidden from the spellcheker)
11908 		// delete revisions that are visible are also to be ignored
11909 		fp_Run * pRun2 = m_pBL->findRunAtOffset(m_iWordOffset);
11910 		if(pRun2 == NULL)
11911 		{
11912 			xxx_UT_DEBUGMSG(("No run where one is expected block %x WordOffset %d \n",this,m_iWordOffset));
11913 			return false;
11914 		}
11915 		UT_return_val_if_fail( pRun2, false );
11916 		bool bRevised = false;
11917 
11918 		while(pRun2 && (UT_sint32)pRun2->getBlockOffset() < m_iWordOffset + iWordLength)
11919 		{
11920 			if(pRun2->getVisibility() != FP_VISIBLE ||
11921 			   (pRun2->containsRevisions() && pRun2->getRevisions()->getLastRevision()->getType() == PP_REVISION_DELETION))
11922 			{
11923 				bRevised = true;
11924 				break;
11925 			}
11926 
11927 			pRun2 = pRun2->getNextRun();
11928 		}
11929 
11930 		if (bNeedsMutation || bRevised)
11931 		{
11932 			// Generate the mutated word in a new buffer pointed to by m_pMutatedString
11933 			m_pMutatedString = static_cast<UT_UCSChar*>(UT_calloc(iWordLength, sizeof(UT_UCSChar)));
11934 			UT_ASSERT(m_pMutatedString);
11935 			pWord = m_pMutatedString;
11936 			iNewLength = 0;
11937 
11938 			if(bNeedsMutation && !bRevised)
11939 			{
11940 				for (UT_uint32 i=0; i < static_cast<UT_uint32>(iWordLength); i++)
11941 				{
11942 					UT_UCSChar currentChar = m_pText[m_iWordOffset + i];
11943 
11944 					// Remove UCS_ABI_OBJECT from the word
11945 					if (currentChar == UCS_ABI_OBJECT) continue;
11946 
11947 					// Convert smart quote apostrophe to ASCII single quote to
11948 					// be compatible with ispell
11949 					if (currentChar == UCS_RQUOTE) currentChar = '\'';
11950 
11951 					m_pMutatedString[iNewLength++] = currentChar;
11952 				}
11953 			}
11954 			else if(bRevised)
11955 			{
11956 				// we need to deal with revision
11957 				// if the word is contained in multiple runs and some of these are deleted through
11958 				// revisions and visible, the revised text should be disregarded
11959 				UT_GenericVector<_spell_type *> vWordLimits;
11960 				fp_Run * pRun = m_pBL->findRunAtOffset(m_iWordOffset);
11961 
11962 				while(pRun && pRun->getBlockOffset() < (UT_uint32)(m_iWordOffset + iWordLength))
11963 				{
11964 					if(pRun->getLength() == 0)
11965 					{
11966 						pRun = pRun->getNextRun();
11967 						continue;
11968 					}
11969 
11970 					UT_uint32 iMaxLen = UT_MIN(pRun->getLength(), m_iWordOffset + iWordLength - pRun->getBlockOffset());
11971 
11972 					bool bDeletedVisible =
11973 						pRun->getVisibility() == FP_VISIBLE &&
11974 						pRun->containsRevisions() &&
11975 						pRun->getRevisions()->getLastRevision()->getType() == PP_REVISION_DELETION;
11976 
11977 					bool bNotVisible = pRun->getVisibility() != FP_VISIBLE;
11978 					bool bIgnore = bNotVisible || bDeletedVisible;
11979 
11980 					_spell_type * st2 = NULL;
11981 
11982 					if(vWordLimits.getItemCount())
11983 						st2 = vWordLimits.getLastItem();
11984 
11985 					if(st2 && st2->bIgnore == bIgnore)
11986 					{
11987 						// this run continues the last ignore section, just adjust to the end
11988 						st2->iEnd += iMaxLen;
11989 					}
11990 					else
11991 					{
11992 						_spell_type * st = new _spell_type;
11993 						UT_return_val_if_fail( st, false );
11994 
11995 						st->bIgnore = bIgnore;
11996 						st->iStart = pRun->getBlockOffset() - m_iWordOffset;
11997 						st->iEnd = st->iStart + iMaxLen;
11998 
11999 						vWordLimits.addItem(st);
12000 					}
12001 
12002 					pRun = pRun->getNextRun();
12003 				}
12004 
12005 				UT_UCS4Char * p = m_pMutatedString;
12006 
12007 				for(UT_sint32 i = 0; i < vWordLimits.getItemCount(); ++i)
12008 				{
12009 					_spell_type * st = vWordLimits.getNthItem(i);
12010 					UT_return_val_if_fail( st, false );
12011 
12012 					if(!st->bIgnore)
12013 					{
12014 						for(UT_uint32 j = st->iStart; j < st->iEnd; ++j)
12015 						{
12016 							if(m_iWordOffset + iWordLength == (UT_sint32)j)
12017 							{
12018 								// we are done (past the last char of the word)
12019 								break;
12020 							}
12021 
12022 							UT_UCS4Char c = m_pText[m_iWordOffset + j];
12023 
12024 							// Remove UCS_ABI_OBJECT from the word
12025 							if (c == UCS_ABI_OBJECT)
12026 								continue;
12027 
12028 							// Convert smart quote apostrophe to ASCII single quote to
12029 							// be compatible with ispell
12030 							if (c == UCS_RQUOTE)
12031 								c = '\'';
12032 
12033 							*p++ = c;
12034 							iNewLength++;
12035 						}
12036 					}
12037 				}
12038 
12039 				UT_VECTOR_PURGEALL(_spell_type*, vWordLimits);
12040 			}
12041 		}
12042 
12043 		// Ignore one-character words.
12044 		// Note: if this is ever changed to be 2+, the scan for word
12045 		// delimiters at the top must also be changed to check for a word
12046 		// in the last character of the block.
12047 		if (iNewLength <= 1)
12048 		{
12049 			continue;
12050 		}
12051 
12052 
12053 		// Don't blow ispell's little mind...
12054 		if (INPUTWORDLEN < iNewLength)
12055 		{
12056 			continue;
12057 		}
12058 
12059 
12060 		// OK, we found the word. Feed the length/pos details to the
12061 		// caller...
12062 		iLength = iNewLength;
12063 		iBlockPos = m_iWordOffset;
12064 
12065 		// Also remember length of m_pWord
12066 		m_iWordLength = iNewLength;
12067 
12068 		// Return success!
12069 		return true;
12070 	}
12071 }
12072 
12073 
12074 // TODO  This function finds the beginning and end of a sentence enclosing
12075 // TODO  the current misspelled word. Right now, it starts from the word
12076 // TODO  and works forward/backward until finding [.!?] or EOB
12077 // TODO  This needs to be improved badly. However, I can't think of a
12078 // TODO  algorithm to do so -- especially not one which could work with
12079 // TODO  other languages very well...
12080 // TODO  Anyone have something better?
12081 // TODO  Hipi: ICU includes an international sentence iterator
12082 // TODO  Hipi: Arabic / Hebrew reverse ? should count, Spanish upside-down
12083 // TODO  Hipi: ? should not count.  CJK scripts have their own equivalents
12084 // TODO  Hipi: to [.!?].  Indic languages can use a "danda" or "double danda".
12085 // TODO  Hipi: Unicode chartype functions may be useable
12086 
12087 /*!
12088   Update sentence baoundaries around current word
12089   Find sentence the current word is in.
12090 */
12091 void
updateSentenceBoundaries(void)12092 fl_BlockSpellIterator::updateSentenceBoundaries(void)
12093 {
12094 	UT_return_if_fail( m_pBL );
12095 	UT_sint32 iBlockLength = m_pgb->getLength();
12096 
12097 	// If the block is small, don't bother looking for
12098 	// boundaries. Just display the full block.
12099 	if (iBlockLength < 30)
12100 	{
12101 		m_iSentenceStart = 0;
12102 		m_iSentenceEnd = iBlockLength - 1;
12103 		return;
12104 	}
12105 
12106 	// Go back from the current word start until a period is found
12107 	m_iSentenceStart = m_iWordOffset;
12108 	while (m_iSentenceStart > 0) {
12109 		if (m_pBL->isSentenceSeparator(m_pText[m_iSentenceStart], m_iSentenceStart))
12110 			break;
12111 		m_iSentenceStart--;
12112 	}
12113 
12114 	// Go forward past any whitespace if sentence start is not at the
12115 	// start of the block
12116 	if (m_iSentenceStart > 0)
12117 	{
12118 		// Seeing as we're not at the start of the block, and the word
12119 		// must contain at least one character, we don't have to make
12120 		// conditional boundary checking (and UCS_UNKPUNK
12121 		// substitution).
12122 		UT_ASSERT(m_iSentenceStart > 0);
12123 		UT_ASSERT(m_iWordLength > 1);
12124 
12125 		while (++m_iSentenceStart < m_iWordOffset
12126 			   && m_pBL->isWordDelimiter(m_pText[m_iSentenceStart],
12127 											m_pText[m_iSentenceStart+1],
12128 											m_pText[m_iSentenceStart-1],
12129 											m_iSentenceStart))
12130 		{
12131 			// Nothing to do... just iterating...
12132 		};
12133 
12134 	}
12135 
12136 
12137 	// Find end of sentence. Go forward until a period is found. If
12138 	// getting to within 10 characters of the end of the block, stop
12139 	// and go with that as the end....
12140 	m_iSentenceEnd = m_iWordOffset + m_iWordLength;
12141 	while (m_iSentenceEnd < (iBlockLength - 10)) {
12142 		if (m_pBL->isSentenceSeparator(m_pText[m_iSentenceEnd], m_iSentenceEnd))
12143 		{
12144 			break;
12145 		}
12146 		m_iSentenceEnd++;
12147 	}
12148 	if (m_iSentenceEnd == (iBlockLength-10)) m_iSentenceEnd = iBlockLength-1;
12149 }
12150 
12151 /*!
12152   Get current word
12153   \result iLength Length of string.
12154   \return Pointer to word.
12155 */
12156 const UT_UCSChar*
getCurrentWord(UT_sint32 & iLength) const12157 fl_BlockSpellIterator::getCurrentWord(UT_sint32& iLength) const
12158 {
12159 	iLength = m_iWordLength;
12160 
12161 	if (NULL != m_pMutatedString)
12162 	{
12163 		return m_pMutatedString;
12164 	}
12165 	else
12166 	{
12167 		return &m_pText[m_iWordOffset];
12168 	}
12169 }
12170 
12171 /*!
12172   Get part of sentence before current word
12173   \result iLength Length of string. If 0, NULL will be returned.
12174   \return Pointer to sentence prior to current word, or NULL
12175 */
12176 const UT_UCSChar*
getPreWord(UT_sint32 & iLength) const12177 fl_BlockSpellIterator::getPreWord(UT_sint32& iLength) const
12178 {
12179 	iLength = m_iWordOffset - m_iSentenceStart;
12180 
12181 	// If it ever becomes necessary to mutate the pre-word, allocate
12182 	// space to m_pMutatedString and return it. Caller will consume
12183 	// that buffer before calling any other function.
12184 
12185 	if (0 >= iLength)
12186 		return NULL;
12187 
12188 	return reinterpret_cast<UT_UCSChar*>(m_pgb->getPointer(m_iSentenceStart));
12189 }
12190 
12191 /*!
12192   Get part of sentence after current word
12193   \result iLength Length of string. If 0, NULL will be returned.
12194   \return Pointer to sentence following current word, or NULL
12195 */
12196 const UT_UCSChar*
getPostWord(UT_sint32 & iLength) const12197 fl_BlockSpellIterator::getPostWord(UT_sint32& iLength) const
12198 {
12199 	iLength = m_iSentenceEnd - m_iStartIndex + 1;
12200 
12201 	// If it ever becomes necessary to mutate the pre-word, allocate
12202 	// space to m_pMutatedString and return it. Caller will consume
12203 	// that buffer before calling any other function.
12204 
12205 	if (0 >= iLength)
12206 		return NULL;
12207 
12208 	return reinterpret_cast<UT_UCSChar*>(m_pgb->getPointer(m_iStartIndex));
12209 }
12210 
12211 /*!
12212   Move iterator back to the previous word.
12213   This method can only be called once per iteration.
12214 */
12215 void
revertToPreviousWord()12216 fl_BlockSpellIterator::revertToPreviousWord()
12217 {
12218 	m_iStartIndex = m_iPrevStartIndex;
12219 }
12220 
12221 bool
_ignoreFirstWordCharacter(const UT_UCSChar c) const12222 fl_BlockSpellIterator::_ignoreFirstWordCharacter(const UT_UCSChar c) const
12223 {
12224     switch (c) {
12225     case '\'':
12226     case '"':
12227     case UCS_LDBLQUOTE:         // smart quoute, open double
12228     case UCS_LQUOTE:            // smart quoute, open
12229         return true;
12230     default:
12231         return false;
12232     }
12233 }
12234 
12235 bool
_ignoreLastWordCharacter(const UT_UCSChar c) const12236 fl_BlockSpellIterator::_ignoreLastWordCharacter(const UT_UCSChar c) const
12237 {
12238     switch (c) {
12239     case '\'':
12240     case '"':
12241     case UCS_RDBLQUOTE:         // smart quote, close double
12242     case UCS_RQUOTE:            // smart quote, close
12243         return true;
12244     default:
12245         return false;
12246     }
12247 }
12248 #endif /* without spell */
12249 
12250 
s_border_properties(const char * border_color,const char * border_style,const char * border_width,const char * color,const char * spacing,PP_PropertyMap::Line & line)12251 static void s_border_properties (const char * border_color,
12252 								 const char * border_style,
12253 								 const char * border_width,
12254 								 const char * color,
12255 								 const char * spacing, PP_PropertyMap::Line & line)
12256 {
12257 	/* cell-border properties:
12258 	 *
12259 	 * (1) color      - defaults to value of "color" property
12260 	 * (2) line-style - defaults to solid (in contrast to "none" in CSS)
12261 	 * (3) thickness  - defaults to 1 layout unit (??, vs "medium" in CSS)
12262 	 */
12263 	line.reset ();
12264 
12265 	PP_PropertyMap::TypeColor t_border_color = PP_PropertyMap::color_type (border_color);
12266 	if (t_border_color)
12267 	{
12268 		line.m_t_color = t_border_color;
12269 		if (t_border_color == PP_PropertyMap::color_color)
12270 			UT_parseColor (border_color, line.m_color);
12271 	}
12272 	else if (color)
12273 	{
12274 		PP_PropertyMap::TypeColor t_color = PP_PropertyMap::color_type (color);
12275 
12276 		line.m_t_color = t_color;
12277 		if (t_color == PP_PropertyMap::color_color)
12278 			UT_parseColor (color, line.m_color);
12279 	}
12280 
12281 	line.m_t_linestyle = PP_PropertyMap::linestyle_type (border_style);
12282 	if (!line.m_t_linestyle)
12283 		line.m_t_linestyle = PP_PropertyMap::linestyle_none;
12284 
12285 	line.m_t_thickness = PP_PropertyMap::thickness_type (border_width);
12286 	if (line.m_t_thickness == PP_PropertyMap::thickness_length)
12287 	{
12288 		if (UT_determineDimension (border_width, (UT_Dimension)-1) == DIM_PX)
12289 		{
12290 			double thickness = UT_LAYOUT_RESOLUTION * UT_convertDimensionless (border_width);
12291 			line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
12292 		}
12293 		else
12294 			line.m_thickness = UT_convertToLogicalUnits (border_width);
12295 
12296 		if (!line.m_thickness)
12297 		{
12298 			// default to 0.72pt
12299 			double thickness = UT_LAYOUT_RESOLUTION;
12300 			line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
12301 		}
12302 	}
12303 	else // ??
12304 	{
12305 		// default to 0.72pt
12306 		double thickness = UT_LAYOUT_RESOLUTION;
12307 		line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
12308 	}
12309 	if(spacing)
12310 	{
12311 		line.m_spacing = UT_convertToLogicalUnits(spacing);
12312 	}
12313 	else
12314 	{
12315 		line.m_spacing = UT_convertToLogicalUnits("0.02in");
12316 	}
12317 }
12318