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 *>(<r[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