1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3  * Copyright (C) 1998 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 
29 #include "ut_types.h"
30 #include "ut_sleep.h"
31 #include "fl_DocListener.h"
32 #include "fl_Layout.h"
33 #include "fl_DocLayout.h"
34 #include "fl_SectionLayout.h"
35 #include "fl_FootnoteLayout.h"
36 #include "fl_FrameLayout.h"
37 #include "fl_BlockLayout.h"
38 #include "fl_TOCLayout.h"
39 #include "fl_ContainerLayout.h"
40 #ifdef ENABLE_SPELL
41 #include "fl_Squiggles.h"
42 #endif
43 #include "fl_AutoNum.h"
44 #include "fp_Page.h"
45 #include "fp_Line.h"
46 #include "fp_TextRun.h"
47 #include "fp_Run.h"
48 #include "fp_FrameContainer.h"
49 #include "fv_View.h"
50 #include "pd_Document.h"
51 #include "pp_Property.h"
52 #include "gr_Graphics.h"
53 #include "xav_Listener.h"
54 #include "xap_App.h"
55 #include "ap_Prefs.h"
56 #include "fp_ContainerObject.h"
57 #include "fp_FootnoteContainer.h"
58 #include "ut_debugmsg.h"
59 #include "ut_assert.h"
60 #include "ut_timer.h"
61 #include "ut_string.h"
62 #include "ut_mbtowc.h"
63 #include "xap_Frame.h"
64 #include "ut_misc.h"
65 #include "pf_Frag_Strux.h"
66 #include "ie_imp_RTF.h"
67 #include "ie_exp_RTF.h"
68 #include "ap_StatusBar.h"
69 #include "ap_FrameData.h"
70 
71 #ifdef ENABLE_SPELL
72 #include "spell_manager.h"
73 #endif
74 
75 #include "gr_EmbedManager.h"
76 
77 #include "xap_EncodingManager.h"
78 
79 #include <set>
80 
81 #define REDRAW_UPDATE_MSECS	500
82 
83 const FootnoteTypeDesc s_FootnoteTypeDesc[] = {
84 	{ FOOTNOTE_TYPE_NUMERIC, "1, 2, 3 ...", "numeric" },
85 	{ FOOTNOTE_TYPE_NUMERIC_SQUARE_BRACKETS, "[1], [2], [3] ...", "numeric-square-brackets" },
86 	{ FOOTNOTE_TYPE_NUMERIC_PAREN, "(1), (2), (3) ...", "numeric-paren" },
87 	{ FOOTNOTE_TYPE_NUMERIC_OPEN_PAREN,"1), 2), 3) ...", "numeric-open-paren" },
88 	{ FOOTNOTE_TYPE_LOWER, "a, b, c ...", "lower" },
89 	{ FOOTNOTE_TYPE_LOWER_PAREN, "(a), (b), (c) ...", "lower-paren" },
90 	{ FOOTNOTE_TYPE_LOWER_OPEN_PAREN, "a), b), c) ...", "lower-paren-open" },
91 	{ FOOTNOTE_TYPE_UPPER, "A, B, C ...", "upper" },
92 	{ FOOTNOTE_TYPE_UPPER_PAREN, "(A), (B), (C) ...", "upper-paren" },
93 	{ FOOTNOTE_TYPE_UPPER_OPEN_PAREN, "A), B), C) ...", "upper-paren-open" },
94 	{ FOOTNOTE_TYPE_LOWER_ROMAN, "i, ii, iii ...", "lower-roman" },
95 	{ FOOTNOTE_TYPE_LOWER_ROMAN_PAREN, "(i), (ii), (iii) ...", "lower-roman-paren" },
96 	{ FOOTNOTE_TYPE_UPPER_ROMAN, "I, II, III ...", "upper-roman" },
97 	{ FOOTNOTE_TYPE_UPPER_ROMAN_PAREN, "(I), (II), (III) ...", "upper-roman-paren" },
98 	{ _FOOTNOTE_TYPE_INVALID, NULL, NULL }
99 };
100 
FL_DocLayout(PD_Document * doc,GR_Graphics * pG)101 FL_DocLayout::FL_DocLayout(PD_Document* doc, GR_Graphics* pG)
102   : m_docViewPageSize("A4"),
103     m_pG(pG),
104     m_pDoc(doc),
105     m_pView(NULL),
106     m_lid((PL_ListenerId)-1),
107     m_pFirstSection(NULL),
108     m_pLastSection(NULL),
109 	m_toSpellCheckHead(NULL),
110 	m_toSpellCheckTail(NULL),
111     m_pPendingBlockForSpell(NULL),
112     m_pPendingWordForSpell(NULL),
113     m_bSpellCheckCaps(true),
114     m_bSpellCheckNumbers(true),
115     m_bSpellCheckInternet(true),
116     m_bAutoSpellCheck(true),
117     m_uDocBackgroundCheckReasons(0),
118     m_bStopSpellChecking(false),
119     m_bImSpellCheckingNow(false),
120     m_pPendingBlockForSmartQuote(NULL),
121     m_uOffsetForSmartQuote(0),
122     m_pBackgroundCheckTimer(NULL),
123     m_pPrefs(NULL),
124     m_pRedrawUpdateTimer(NULL),
125     m_iSkipUpdates(0),
126     m_bDeletingLayout(false),
127     m_bisLayoutFilling(false),
128     m_iRedrawCount(0),
129     m_FootnoteType(FOOTNOTE_TYPE_NUMERIC),
130     m_iFootnoteVal(1),
131     m_bRestartFootSection(false),
132     m_bRestartFootPage(false),
133     m_iEndnoteVal(1),
134     m_EndnoteType(FOOTNOTE_TYPE_NUMERIC_SQUARE_BRACKETS),
135     m_bRestartEndSection(false),
136     m_bPlaceAtDocEnd(false),
137     m_bPlaceAtSecEnd(true),
138     m_iGraphicTick(0),
139     m_iDocSize(0),
140     m_iFilled(0),
141     m_bSpellCheckInProgress(false),
142     m_bAutoGrammarCheck(false),
143     m_PendingBlockForGrammar(NULL),
144     m_iGrammarCount(0),
145     m_bFinishedInitialCheck(false),
146     m_iPrevPos(0),
147     m_pQuickPrintGraphics(NULL),
148     m_bIsQuickPrint(false),
149     m_bDisplayAnnotations(false),
150     m_bDisplayRDFAnchors(false),
151     m_pSavedContainer(NULL),
152     m_pRebuiltBlockLayout(NULL)
153 {
154 #ifdef FMT_TEST
155         m_pDocLayout = this;
156 #endif
157         setLayoutIsFilling(false),
158 	m_pRedrawUpdateTimer = UT_Timer::static_constructor(_redrawUpdate, this);
159 	if (m_pRedrawUpdateTimer && !pG->queryProperties(GR_Graphics::DGP_PAPER))
160 	{
161 		m_pRedrawUpdateTimer->set(REDRAW_UPDATE_MSECS);
162 		m_pRedrawUpdateTimer->start();
163 	}
164 
165 	// TODO the following (both the new() and the addListener() cause
166 	// TODO g_try_malloc's to occur.  we are currently inside a constructor
167 	// TODO and are not allowed to report failure.
168 
169 	// Turn off list updating until document is formatted
170 
171 	m_pDoc->disableListUpdates();
172 
173 	strncpy(m_szCurrentTransparentColor,
174 			static_cast<const char *>(XAP_PREF_DEFAULT_ColorForTransparent), 9);
175 	m_vecFootnotes.clear();
176 	m_vecAnnotations.clear();
177 	m_vecEndnotes.clear();
178 
179 }
180 
~FL_DocLayout()181 FL_DocLayout::~FL_DocLayout()
182 {
183         UT_DEBUGMSG(("Deleting DocLayout %p DocListener %p lid %d\n",this,m_pDocListener,m_lid));
184 
185 	m_bDeletingLayout = true;
186 	if (m_pPrefs)
187 	{
188 		m_pPrefs->removeListener ( _prefsListener, this );
189 	}
190 
191 	if (m_pDoc)
192 	{
193 		m_pDoc->removeListener(m_lid);
194 	}
195 
196 	DELETEP(m_pDocListener);
197 
198 	if (m_pBackgroundCheckTimer)
199 	{
200 		m_bStopSpellChecking = true;
201 		m_pBackgroundCheckTimer->stop();
202 	}
203 
204 	DELETEP(m_pBackgroundCheckTimer);
205 
206 	if (m_pRedrawUpdateTimer)
207 	{
208 		m_pRedrawUpdateTimer->stop();
209 	}
210 
211 	DELETEP(m_pRedrawUpdateTimer);
212 
213 	UT_sint32 count = m_vecPages.getItemCount() -1;
214 	while(count >= 0)
215 	{
216 		fp_Page * pPage = static_cast<fp_Page *>(m_vecPages.getNthItem(count));
217 		if(pPage->getPrev())
218 		{
219 			pPage->getPrev()->setNext(NULL);
220 		}
221 		m_vecPages.deleteNthItem(count);
222 		delete pPage;
223 		count--;
224 	}
225 
226 	while (m_pFirstSection)
227 	{
228 		fl_DocSectionLayout* pNext = m_pFirstSection->getNextDocSection();
229 		delete m_pFirstSection;
230 		m_pFirstSection = pNext;
231 	}
232 	std::set<GR_EmbedManager *> garbage;
233 	std::map<std::string, GR_EmbedManager *>::iterator i, iend;
234 	iend = m_mapEmbedManager.end();
235 	for (i = m_mapEmbedManager.begin(); i != iend; i++)
236 		if ((*i).first == (*i).second->getObjectType())
237 			garbage.insert((*i).second);
238 	m_mapEmbedManager.clear();
239 	iend = m_mapQuickPrintEmbedManager.end();
240 	for (i = m_mapQuickPrintEmbedManager.begin(); i != iend; i++)
241 		if ((*i).first == (*i).second->getObjectType())
242 			garbage.insert((*i).second);
243 	m_mapQuickPrintEmbedManager.clear();
244 	std::set<GR_EmbedManager *>::iterator j, jend = garbage.end();
245 	for (j = garbage.begin(); j != jend; j++)
246 		delete *j;
247 	garbage.clear();
248 }
249 
250 /*!
251  * Set the variables needed for a QuickPrint
252  */
setQuickPrint(GR_Graphics * pGraphics)253 void  FL_DocLayout::setQuickPrint(GR_Graphics * pGraphics)
254 {
255 	std::set<GR_EmbedManager *> garbage;
256 	std::map<std::string, GR_EmbedManager *>::iterator i, iend;
257 	iend = m_mapQuickPrintEmbedManager.end();
258 	for (i = m_mapQuickPrintEmbedManager.begin(); i != iend; i++)
259 		if ((*i).first == (*i).second->getObjectType())
260 			garbage.insert((*i).second);
261 	m_mapQuickPrintEmbedManager.clear();
262 	std::set<GR_EmbedManager *>::iterator j, jend = garbage.end();
263 	for (j = garbage.begin(); j != jend; j++)
264 		delete *j;
265 	garbage.clear();
266 	if(pGraphics != NULL)
267 	{
268 	    m_bIsQuickPrint = true;
269 	    m_pQuickPrintGraphics = pGraphics;
270 	}
271 	else
272 	{
273 	    m_bIsQuickPrint = false;
274 	    m_pQuickPrintGraphics = NULL;
275 	    fl_BlockLayout * pBL = getFirstSection()->getFirstBlock();
276 	    //
277 	    // Clear out any hanging pointers
278 	    //
279 	    while(pBL)
280 	    {
281 		pBL->clearPrint();
282 		pBL = pBL->getNextBlockInDocument();
283 	    }
284 	    //
285 	    // Ensure all fonts are owned by the original graphics class
286 	    //
287 	    refreshRunProperties();
288 	}
289 }
290 
getQuickPrintGraphics(void) const291 GR_Graphics * FL_DocLayout::getQuickPrintGraphics(void) const
292 {
293   return  m_pQuickPrintGraphics;
294 }
295 
296 /*!
297  * Get an embedManager of the requested Type.for a quickPrint
298  */
getQuickPrintEmbedManager(const char * szEmbedType)299 GR_EmbedManager * FL_DocLayout::getQuickPrintEmbedManager(const char * szEmbedType)
300 {
301   // Look in the current collection first.
302    GR_EmbedManager * pEmbed = NULL;
303   std::map<std::string, GR_EmbedManager *>::iterator i;
304   if ((i = m_mapQuickPrintEmbedManager.find(szEmbedType)) != m_mapQuickPrintEmbedManager.end())
305     return (*i).second;
306   pEmbed = XAP_App::getApp()->getEmbeddableManager(m_pQuickPrintGraphics,szEmbedType);
307   if((strcmp(pEmbed->getObjectType(),"default") == 0) &&
308      ((i = m_mapQuickPrintEmbedManager.find("default")) != m_mapQuickPrintEmbedManager.end()))
309     {
310       delete pEmbed;
311       return (*i).second;
312     }
313   UT_DEBUGMSG(("Got manager of type %s \n",pEmbed->getObjectType()));
314   if (strcmp(pEmbed->getObjectType(), szEmbedType) != 0)
315 	{
316       if ((i = m_mapQuickPrintEmbedManager.find(pEmbed->getObjectType())) != m_mapQuickPrintEmbedManager.end())
317         {
318           m_mapQuickPrintEmbedManager[szEmbedType] = (*i).second;
319           delete pEmbed;
320           return (*i).second;
321 	    }
322       m_mapQuickPrintEmbedManager[pEmbed->getObjectType()] = pEmbed;
323     }
324   m_mapQuickPrintEmbedManager[szEmbedType] = pEmbed;
325   pEmbed->initialize();
326 
327   return pEmbed;
328 }
329 
330 /*!
331  * Get an embedManager of the requested Type.
332  */
getEmbedManager(const char * szEmbedType)333 GR_EmbedManager * FL_DocLayout::getEmbedManager(const char * szEmbedType)
334 {
335   // Look in the current collection first.
336   GR_EmbedManager * pEmbed = NULL;
337   std::map<std::string, GR_EmbedManager *>::iterator i;
338   if ((i = m_mapEmbedManager.find(szEmbedType)) != m_mapEmbedManager.end())
339     return (*i).second;
340   pEmbed = XAP_App::getApp()->getEmbeddableManager(m_pG,szEmbedType);
341   if((strcmp(pEmbed->getObjectType(),"default") == 0) &&
342      ((i = m_mapEmbedManager.find("default")) != m_mapEmbedManager.end()))
343     {
344       delete pEmbed;
345       return (*i).second;
346     }
347   UT_DEBUGMSG(("Got manager of type %s \n",pEmbed->getObjectType()));
348   if (strcmp(pEmbed->getObjectType(), szEmbedType) != 0)
349 	{
350       if ((i = m_mapEmbedManager.find(pEmbed->getObjectType())) != m_mapEmbedManager.end())
351         {
352           m_mapEmbedManager[szEmbedType] = (*i).second;
353           delete pEmbed;
354           return (*i).second;
355 	    }
356       m_mapEmbedManager[pEmbed->getObjectType()] = pEmbed;
357     }
358   m_mapEmbedManager[szEmbedType] = pEmbed;
359   pEmbed->initialize();
360 
361   return pEmbed;
362 }
363 
364 /*!
365  * little helper method for lookup properties
366  */
FootnoteTypeFromString(const gchar * pszFootnoteType)367 FootnoteType FL_DocLayout::FootnoteTypeFromString(const gchar * pszFootnoteType)
368 {
369 	FootnoteType iFootnoteType;
370 	if (pszFootnoteType == NULL)
371 	{
372 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC;
373 	}
374 	else if(pszFootnoteType[0] == 0)
375 	{
376 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC;
377 	}
378 	else if(strcmp(pszFootnoteType,"numeric") == 0)
379 	{
380 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC;
381 	}
382 	else if(strcmp(pszFootnoteType,"numeric-square-brackets") == 0)
383 	{
384 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC_SQUARE_BRACKETS;
385 	}
386 	else if(strcmp(pszFootnoteType,"numeric-paren") == 0)
387 	{
388 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC_PAREN;
389 	}
390 	else if(strcmp(pszFootnoteType,"numeric-open-paren") == 0)
391 	{
392 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC_OPEN_PAREN;
393 	}
394 	else if(strcmp(pszFootnoteType,"upper") == 0)
395 	{
396 		iFootnoteType = FOOTNOTE_TYPE_UPPER;
397 	}
398 	else if(strcmp(pszFootnoteType,"upper-paren") == 0)
399 	{
400 		iFootnoteType = FOOTNOTE_TYPE_UPPER_PAREN;
401 	}
402 	else if(strcmp(pszFootnoteType,"upper-paren-open") == 0)
403 	{
404 		iFootnoteType = FOOTNOTE_TYPE_UPPER_OPEN_PAREN;
405 	}
406 	else if(strcmp(pszFootnoteType,"lower") == 0)
407 	{
408 		iFootnoteType = FOOTNOTE_TYPE_LOWER;
409 	}
410 	else if(strcmp(pszFootnoteType,"lower-paren") == 0)
411 	{
412 		iFootnoteType = FOOTNOTE_TYPE_LOWER_PAREN;
413 	}
414 	else if(strcmp(pszFootnoteType,"lower-paren-open") == 0)
415 	{
416 		iFootnoteType = FOOTNOTE_TYPE_LOWER_OPEN_PAREN;
417 	}
418 	else if(strcmp(pszFootnoteType,"lower-roman") == 0)
419 	{
420 		iFootnoteType = FOOTNOTE_TYPE_LOWER_ROMAN;
421 	}
422 	else if(strcmp(pszFootnoteType,"lower-roman-paren") == 0)
423 	{
424 		iFootnoteType = FOOTNOTE_TYPE_LOWER_ROMAN_PAREN;
425 	}
426 	else if(strcmp(pszFootnoteType,"upper-roman") == 0)
427 	{
428 		iFootnoteType = FOOTNOTE_TYPE_UPPER_ROMAN;
429 	}
430 	else if(strcmp(pszFootnoteType,"upper-roman-paren") == 0)
431 	{
432 		iFootnoteType = FOOTNOTE_TYPE_UPPER_ROMAN_PAREN;
433 	}
434 	else
435 	{
436 		iFootnoteType = FOOTNOTE_TYPE_NUMERIC_SQUARE_BRACKETS;
437 	}
438 	return iFootnoteType;
439 }
440 
441 /*!
442  * This method reads the document properties and saves local versions of them
443  * here.
444  */
_lookupProperties(void)445 void FL_DocLayout::_lookupProperties(void)
446 {
447 	const gchar * pszFootnoteType = NULL;
448 	const PP_AttrProp* pDocAP = getDocument()->getAttrProp();
449 	UT_return_if_fail(pDocAP);
450 	pDocAP->getProperty("document-footnote-type", (const gchar *&)pszFootnoteType);
451 	m_FootnoteType = FootnoteTypeFromString(pszFootnoteType);
452 
453 	const gchar * pszEndnoteType = NULL;
454 	pDocAP->getProperty("document-endnote-type", (const gchar *&)pszEndnoteType);
455 	m_EndnoteType = FootnoteTypeFromString(pszEndnoteType);
456 
457 	const gchar * pszTmp = NULL;
458 	pDocAP->getProperty("document-footnote-initial", (const gchar *&)pszTmp);
459 	if(pszTmp && pszTmp[0])
460 	{
461 		m_iFootnoteVal =  atoi(pszTmp);
462 	}
463 	else
464 	{
465 		m_iFootnoteVal = 1;
466 	}
467 
468 	pDocAP->getProperty("document-footnote-restart-section", (const gchar *&)pszTmp);
469 	if(pszTmp && pszTmp[0])
470 	{
471 		if(strcmp(pszTmp,"1") == 0)
472 		{
473 			m_bRestartFootSection = true;
474 		}
475 		else
476 		{
477 			m_bRestartFootSection = false;
478 		}
479 	}
480 	else
481 	{
482 		m_bRestartFootSection = false;
483 	}
484 
485 	pDocAP->getProperty("document-footnote-restart-page", (const gchar *&)pszTmp);
486 	if(pszTmp && pszTmp[0])
487 	{
488 		if(strcmp(pszTmp,"1") == 0)
489 		{
490 			m_bRestartFootPage = true;
491 		}
492 		else
493 		{
494 			m_bRestartFootPage = false;
495 		}
496 	}
497 	else
498 	{
499 		m_bRestartFootPage = false;
500 	}
501 
502 	pDocAP->getProperty("document-endnote-initial", (const gchar *&)pszTmp);
503 	if(pszTmp && pszTmp[0])
504 	{
505 		m_iEndnoteVal =  atoi(pszTmp);
506 	}
507 	else
508 	{
509 		m_iEndnoteVal = 1;
510 	}
511 
512 	pDocAP->getProperty("document-endnote-restart-section", (const gchar *&)pszTmp);
513 	if(pszTmp && pszTmp[0])
514 	{
515 		if(strcmp(pszTmp,"1") == 0)
516 		{
517 			m_bRestartEndSection = true;
518 		}
519 		else
520 		{
521 			m_bRestartEndSection = false;
522 		}
523 	}
524 	else
525 	{
526 		m_bRestartEndSection = false;
527 	}
528 
529 	pDocAP->getProperty("document-endnote-place-endsection", (const gchar *&)pszTmp);
530 	if(pszTmp && pszTmp[0])
531 	{
532 		if(strcmp(pszTmp,"1") == 0)
533 		{
534 			m_bPlaceAtDocEnd = false;
535 		}
536 		else
537 		{
538 			m_bPlaceAtDocEnd = true;
539 		}
540 	}
541 	else
542 	{
543 		m_bPlaceAtDocEnd = false;
544 	}
545 
546 	pDocAP->getProperty("document-endnote-place-enddoc", (const gchar *&)pszTmp);
547 	if(pszTmp && pszTmp[0])
548 	{
549 		if(strcmp(pszTmp,"1") == 0)
550 		{
551 			m_bPlaceAtSecEnd = false;
552 		}
553 		else
554 		{
555 			m_bPlaceAtSecEnd = true;
556 		}
557 	}
558 	else
559 	{
560 		m_bPlaceAtSecEnd = true;
561 	}
562 
563 }
564 
updatePropsNoRebuild(void)565 void FL_DocLayout::updatePropsNoRebuild(void)
566 {
567 	_lookupProperties();
568 }
569 
570 /*!
571  * Change the graphics pointer for this layout.Needed for quick zoom.
572  */
setGraphics(GR_Graphics * pG)573 void FL_DocLayout::setGraphics(GR_Graphics * pG)
574 {
575 	m_pG = pG;
576 	m_iGraphicTick++;
577 
578 	// we need to at least re-acquire a new copy of the fonts
579 	// since the last ones' lives are attached to the old
580 	// graphics class
581 	updatePropsRebuild();
582 }
583 
updatePropsRebuild(void)584 void FL_DocLayout::updatePropsRebuild(void)
585 {
586 	_lookupProperties();
587 	rebuildFromHere( m_pFirstSection);
588 }
589 
getFootnoteType(void) const590 FootnoteType FL_DocLayout::getFootnoteType(void) const
591 {
592 	return m_FootnoteType;
593 }
594 
595 
clearAllCountWraps(void)596 void FL_DocLayout::clearAllCountWraps(void)
597 {
598   UT_sint32 i = 0;
599   for(i=0; i<countPages();i++)
600   {
601       getNthPage(i)->clearCountWrapNumber();
602   }
603 }
604 /*!
605  * This Method fills the layout structures from the PieceTable.
606  */
fillLayouts(void)607 void FL_DocLayout::fillLayouts(void)
608 {
609 	_lookupProperties();
610 	setLayoutIsFilling(true);
611 	AP_StatusBar * pStatusBar = NULL;
612 	m_docViewPageSize = getDocument()->m_docPageSize;
613 	if(m_pView)
614 	{
615 		m_pView->setPoint(0);
616 		m_pView->setLayoutIsFilling(true);
617 		if(m_pView->getParentData())
618 		{
619             if(AP_FrameData * pData =  static_cast<AP_FrameData *>(static_cast<XAP_Frame *>(m_pView->getParentData())->getFrameData()))
620             {
621 				pStatusBar = static_cast<AP_StatusBar *>(pData->m_pStatusBar);
622 				if(pStatusBar)
623 				{
624 					pStatusBar->setStatusProgressType(0,100,PROGRESS_STARTBAR);
625 					pStatusBar->showProgressBar();
626 				}
627             }
628 
629 		}
630 	}
631 	m_pDoc->getBounds(true,m_iDocSize);
632 //
633 // Make a document listner to get info pumped into the layouts.
634 //
635 	m_pDocListener = new fl_DocListener(m_pDoc, this);
636 	UT_return_if_fail(m_pDocListener);
637 //
638 // The act of adding the listner to the document also causes the
639 // the document to pump it's content into the layout classes.
640 //
641 	m_pDoc->setDontImmediatelyLayout(true);
642 	m_pDocListener->setHoldTableLayout(false);
643 	m_pDoc->addListener(static_cast<PL_Listener *>(m_pDocListener),&m_lid);
644 	m_pDoc->setDontImmediatelyLayout(false);
645 	UT_ASSERT(m_lid != (PL_ListenerId)-1);
646 	GR_Graphics * pG = getGraphics();
647 	formatAll(); // Do we keep this or not?
648 	m_bFinishedInitialCheck = false;
649 	m_iPrevPos = 0;
650 	m_iGrammarCount = 0;
651 	if(m_pView)
652 	{
653 		m_pView->setLayoutIsFilling(false);
654 		setLayoutIsFilling(false);
655 		m_pView->moveInsPtTo(FV_DOCPOS_BOD);
656 		m_pView->clearCursorWait();
657 		m_pView->updateLayout();
658 		if(!pG->queryProperties(GR_Graphics::DGP_PAPER))
659 		{
660 			m_pView->updateScreen(false);
661 			XAP_Frame * pFrame = static_cast<XAP_Frame *>(m_pView->getParentData());
662 			if(pFrame)
663 			{
664 				pFrame->setYScrollRange();
665 			}
666 		}
667 	}
668 	setLayoutIsFilling(false);
669 	if(!m_pView)
670 	{
671 		updateLayout();
672 	}
673 
674 	// Layout of any TOC that is built only from a restricted document range is tentative, because
675 	// the information required by the TOC might not have been available during the incremental
676 	// load.  In that case the TOCs made made certain assumptions about the presence of a given
677 	// bookmark in the doc during the fill. These assumptions now need to be verified and, if
678 	// required, fixed
679 
680 	fl_TOCLayout* pBadTOC = NULL;
681 
682 	//
683 	// Maybe one day we can fill TOC's directly from the
684 	// PT before doing the layout of the rest of the document.
685 	//
686 	for (UT_sint32 i = 0; i < getNumTOCs(); ++i)
687 	{
688 		fl_TOCLayout * pTOC = getNthTOC(i);
689 		if(!pTOC)
690 		{
691 			UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
692 			continue;
693 		}
694 		if (pTOC->isTOCEmpty())
695 		{
696 			pTOC->fillTOC();
697 			m_pView->updateLayout();
698 		}
699 
700 		// because the incremental load is sequential, the TOCs are in the order they have in the
701 		// document, so we just need to remember the first one.
702 		if(!pBadTOC && pTOC->verifyBookmarkAssumptions())
703 		{
704 			pBadTOC = pTOC;
705 		}
706 	}
707 
708 	if(pBadTOC)
709 	{
710 		// hard luck -- we need to redo the layout, since the TOC probably changed size
711 		fl_SectionLayout * pSL = pBadTOC->getSectionLayout();
712 		fl_DocSectionLayout * pDSL = NULL;
713 
714 		if(pSL->getContainerType() == FL_CONTAINER_DOCSECTION)
715 		{
716 			pDSL = static_cast<fl_DocSectionLayout*>(pSL);
717 		}
718 		else
719 		{
720 			UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
721 		}
722 
723 		if(!pDSL)
724 		{
725 			formatAll();
726 		}
727 		else
728 		{
729 			while (pSL)
730 			{
731 				pSL->format();
732 				if(pSL->getContainerType() == FL_CONTAINER_DOCSECTION)
733 				{
734 					static_cast<fl_DocSectionLayout *>(pSL)->completeBreakSection();
735 					static_cast<fl_DocSectionLayout *>(pSL)->checkAndRemovePages();
736 				}
737 				pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
738 			}
739 		}
740 
741 		if(m_pView)
742 		{
743 			m_pView->updateLayout();
744 			if(!pG->queryProperties(GR_Graphics::DGP_PAPER))
745 			{
746 			  //				m_pView->updateScreen(false);
747 				XAP_Frame * pFrame = static_cast<XAP_Frame *>(m_pView->getParentData());
748 				if(pFrame)
749 				{
750 					pFrame->setYScrollRange();
751 				}
752 			}
753 		}
754 	}
755 
756 	// Frame related tasks
757 
758 	if (m_vecFramesToBeInserted.getItemCount() > 0)
759 	{
760 		// There is a mismatch between the new layout and the saved file.
761 		// The requested page for some frames does not exists.
762 		// Insert all remaining frames on the last page.
763 		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
764 		fp_FrameContainer * pFrame = NULL;
765 		UT_sint32 k = 0;
766 		UT_sint32 kmax = m_vecFramesToBeInserted.getItemCount();
767 		fp_Page * pPage = getLastPage();
768 		for (k = 0; k < kmax; k++)
769 		{
770 			pFrame = m_vecFramesToBeInserted.getNthItem(0);
771 			m_vecFramesToBeInserted.deleteNthItem(0);
772 			pPage->insertFrameContainer(pFrame);
773 		}
774 	}
775 
776 	setFramePageNumbers(0);
777 	loadPendingObjects();
778 	//
779 	// One more time!
780 	//
781 	setFramePageNumbers(0);
782 	//
783 	// Fix all Lists
784 	//
785 	getDocument()->enableListUpdates();
786 	for(UT_uint32 j =0; j<getDocument()->getListsCount();j++)
787 	{
788 	    fl_AutoNum * pAuto = getDocument()->getNthList(j);
789 	    pAuto->markAsDirty();
790 	}
791 	getDocument()->updateDirtyLists();
792 	if(pStatusBar)
793 	{
794 	  pStatusBar->setStatusProgressType(0,100,PROGRESS_STOPBAR);
795 	  pStatusBar->hideProgressBar();
796 	}
797 }
798 
799 /*!
800  * This method loads all the pending objects that could not be loaded
801  * until the rest of the text is layed out.
802  */
loadPendingObjects(void)803 bool FL_DocLayout::loadPendingObjects(void)
804 {
805         FV_View * pView = getView();
806 	if(!pView)
807 	        return false;
808 	PD_Document * pDoc = getDocument();
809         ImagePage * pImagePage = NULL;
810 	UT_sint32 i = 0;
811 	pImagePage = pDoc->getNthImagePage(i);
812 	UT_UTF8String sVal,sProp;
813 	bool bOK = false;
814 	PT_DocPosition pos = 0;
815 	fp_Page * pPage = NULL;
816 	UT_UTF8String allProps;
817 	fl_DocSectionLayout * pDSL = NULL;
818 	for(i=0;pImagePage;pImagePage = pDoc->getNthImagePage(++i))
819         {
820 		UT_UTF8String sID = *pImagePage->getImageId();
821 		allProps = *pImagePage-> getProps();
822 		bOK = AnchoredObjectHelper(pImagePage->getXInch(),
823 					   pImagePage->getYInch(),
824 					   pImagePage->getPageNo(),
825 					   allProps,
826 					   pos,
827 					   pPage);
828 		if(!bOK)
829 		    continue;
830 
831 		// Props neeed for the image
832 
833 		sProp="frame-type";
834 		sVal="image";
835 		UT_UTF8String_setProperty(allProps,sProp,sVal);
836 
837 	  //
838 	  // Now define the Frame attributes strux
839 	  //
840 		const gchar * attributes[5] = {PT_STRUX_IMAGE_DATAID,
841 					  NULL,"props",NULL,NULL};
842 		attributes[1] = sID.utf8_str();
843 		attributes[3] = allProps.utf8_str();
844 		pf_Frag_Strux * pfFrame = NULL;
845 		pDoc->insertStrux(pos,PTX_SectionFrame,attributes,NULL,&pfFrame);
846 		PT_DocPosition posFrame = pfFrame->getPos();
847 		pDoc->insertStrux(posFrame+1,PTX_EndFrame);
848 		pView->insertParaBreakIfNeededAtPos(posFrame+2);
849 		//
850 		// Now rebreak from this page forward.
851 		pDSL = pPage->getOwningSection();
852 		pDSL->setNeedsSectionBreak(true,pPage);
853 		while(pDSL)
854 		{
855 		    pDSL->format();
856 		    pDSL = pDSL->getNextDocSection();
857 
858 		}
859 		//
860 		// Get Next ImagePage
861 		//
862 
863 	}
864 	i = 0;
865 	TextboxPage * pTBPage = pDoc->getNthTextboxPage(i);
866 	for(i=0;pTBPage;pTBPage = pDoc->getNthTextboxPage(++i))
867 	{
868 	    allProps = *pTBPage->getProps();
869 	    bOK = AnchoredObjectHelper(pTBPage->getXInch(),
870 				       pTBPage->getYInch(),
871 				       pTBPage->getPageNo(),
872 				       allProps,
873 				       pos,
874 				       pPage);
875 	    if(!bOK)
876 	        continue;
877 
878 	    // Props neeed for the Text box
879 
880 	    sProp="frame-type";
881 	    sVal="textbox";
882 	    UT_UTF8String_setProperty(allProps,sProp,sVal);
883 
884 	  //
885 	  // Now define the Frame attributes strux
886 	  //
887 	    const gchar * attributes[3] = {"props",NULL,NULL};
888 	    attributes[1] = allProps.utf8_str();
889 	    pf_Frag_Strux * pfFrame = NULL;
890 	    pDoc->insertStrux(pos,PTX_SectionFrame,attributes,NULL,&pfFrame);
891 	    PT_DocPosition posFrame = pfFrame->getPos();
892 	    pDoc->insertStrux(posFrame+1,PTX_EndFrame);
893 	    pDoc->insertStrux(posFrame+1,PTX_Block);
894 	    pView->insertParaBreakIfNeededAtPos(posFrame+3);
895 
896 	    //
897 	    // Now insert the content
898 	    //
899 
900 	    const UT_ByteBuf * pBuf = pTBPage->getContent();
901 	    PD_DocumentRange docRange(pDoc, posFrame+1,posFrame+1);
902 	    IE_Imp_RTF * pImpRTF = new IE_Imp_RTF(pDoc);
903 	    const unsigned char * pData = static_cast<const unsigned char *>(pBuf->getPointer(0));
904 	    UT_uint32 iLen = pBuf->getLength();
905 	    pImpRTF->pasteFromBuffer(&docRange,pData,iLen);
906 	    delete pImpRTF;
907 	    //
908 	    // Now rebreak from this page forward.
909 	    pDSL = pPage->getOwningSection();
910 	    pDSL->setNeedsSectionBreak(true,pPage);
911 	    while(pDSL)
912 	    {
913 		pDSL->format();
914 		pDSL = pDSL->getNextDocSection();
915 	    }
916 	}
917 	//
918 	// Remove all pending objects. They've now been loaded.
919 	//
920 	pDoc->clearAllPendingObjects();
921         return true;
922 }
923 
924 /*!
925  * Returns true if it founds a valid pos to insert the postioned object
926  */
AnchoredObjectHelper(double x,double y,UT_sint32 iPage,UT_UTF8String & allProps,PT_DocPosition & pos,fp_Page * & pPage)927 bool FL_DocLayout::AnchoredObjectHelper(double x, double y, UT_sint32 iPage, UT_UTF8String & allProps, PT_DocPosition & pos, fp_Page *& pPage)
928 {
929 	UT_UTF8String sVal,sProp;
930 	iPage = iPage -1; // Start from page 1
931 	if(iPage>=m_vecPages.getItemCount())
932 	    iPage = m_vecPages.getItemCount()-1;
933 	pPage = m_vecPages.getNthItem(iPage);
934 	UT_sint32 xPos = UT_LAYOUT_RESOLUTION*x;
935 	UT_sint32 yPos = UT_LAYOUT_RESOLUTION*y;
936 	bool bBOL,bEOL,isTOC;
937 	pPage->mapXYToPosition(xPos,yPos, pos, bBOL,bEOL,isTOC);
938 	//
939 	// Set the image position in the frame properties as well
940 	// as the properties that define this as a positioned image
941 	// positioned relative to a page.
942 	//
943 	sVal = UT_formatDimensionedValue(x,"in", NULL);
944 	sProp="frame-page-xpos";
945 	UT_UTF8String_setProperty(allProps,sProp,sVal);
946 	sVal = UT_formatDimensionedValue(y,"in", NULL);
947 	sProp="frame-page-ypos";
948 	UT_UTF8String_setProperty(allProps,sProp,sVal);
949 	sProp="position-to";
950 	sVal="page-above-text";
951 	UT_UTF8String_setProperty(allProps,sProp,sVal);
952 
953 	//
954 	// Position the object immediately after the closest block
955 	//
956 	fl_BlockLayout * pBL = findBlockAtPosition(pos);
957 	if(pBL == NULL)
958 	{
959 	    return false;
960 	}
961 	//
962 	// This should place the the frame strux immediately after the
963 	// block containing position posXY.
964 	// It returns the Frag_Strux of the new frame.
965 	//
966 
967 	fl_BlockLayout * pPrevBL = pBL;
968 	while(pBL && ((pBL->myContainingLayout()->getContainerType() == FL_CONTAINER_ENDNOTE) || (pBL->myContainingLayout()->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pBL->myContainingLayout()->getContainerType() == FL_CONTAINER_TOC)|| (pBL->myContainingLayout()->getContainerType() == FL_CONTAINER_FRAME)))
969 	{
970 	    UT_DEBUGMSG(("Skipping Block %p \n",pBL));
971 	    pPrevBL = pBL;
972 	    pBL = pBL->getPrevBlockInDocument();
973 	}
974 	if(pBL == NULL)
975 	{
976 	    pBL = pPrevBL;
977 	}
978 	UT_ASSERT((pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_HDRFTR)
979 		  && (pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_SHADOW));
980 	pos = pBL->getPosition();
981 	return true;
982 }
983 
984 /*!
985  * Code to deal with Dangling Pointers in fb_ColumnBreaker. Unfortunately there is no way to avoid the
986  * problem that some containers will get deleted during a page wrap. Even more unfortunately we need to
987  * hold a pointer to a container in fb_ColumnBreaker that coukd get deleted. This code allows us to detect
988  * and repair the damage when this occurs.
989  */
990 /*!
991  * The save the pointer that might be left dangling. This methos is called from fb_ColumnBreak.
992  * We set a boolean inside this class that tells us we need
993  * signal fl_Doclayout that it has been deleted. The pointer m_pRebuiltBlock is the block that contains the
994  * Container. The pointer to it will be set within BlockLayout when the container gets deleted
995  */
setSaveContainerPointer(fp_Container * pContainer)996 void FL_DocLayout::setSaveContainerPointer( fp_Container * pContainer)
997 {
998         m_pSavedContainer = pContainer;
999 	pContainer->setAllowDelete(false);
1000 	m_pRebuiltBlockLayout = NULL;
1001 }
1002 
setRebuiltBlock(fl_BlockLayout * pBlock)1003 void FL_DocLayout::setRebuiltBlock(fl_BlockLayout *pBlock)
1004 {
1005   m_pRebuiltBlockLayout = pBlock;
1006 }
1007 
getRebuiltBlock(void) const1008 fl_BlockLayout * FL_DocLayout::getRebuiltBlock(void) const
1009 {
1010   return m_pRebuiltBlockLayout;
1011 }
1012 
getSavedContainerPointer(void) const1013 fp_Container * FL_DocLayout::getSavedContainerPointer(void) const
1014 {
1015   return m_pSavedContainer;
1016 }
1017 
1018 #if 0
1019 // Don't think we need this code after moving this functionality to
1020 // fp_ColumnBreaker::breakSection()! FIXME remove if we're sure we dont!
1021 /*!
1022  * This method returns true of the document is not completely layed out. This
1023  * happens in documents with for example a large TOC and a bunch of footnotes
1024  * (the RTF 1.7 spec is a good example).
1025  *
1026  * This is a hack; BreakSection should automatically detect this. For now
1027  * it works though :) - MARCM
1028  */
1029 bool FL_DocLayout::needsRebreak(void)
1030 {
1031     bool bRebreak = false;
1032     fl_DocSectionLayout * pLastSec = getLastSection();
1033     if(pLastSec)
1034     {
1035         fl_ContainerLayout * pCL = pLastSec->getLastLayout();
1036 	fl_BlockLayout * pBL = NULL;
1037 	if(pCL && (pCL->getContainerType() == FL_CONTAINER_BLOCK))
1038         {
1039 	    pBL = static_cast<fl_BlockLayout *>(pCL);
1040 	}
1041 	else if(pCL)
1042 	{
1043 	    pBL = pCL->getPrevBlockInDocument();
1044 	}
1045 	else
1046 	{
1047 	    UT_ASSERT_HARMLESS(pCL);
1048 	}
1049 	if(pBL)
1050 	{
1051 	    fp_Line * pLine = static_cast<fp_Line *>(pBL->getLastContainer());
1052 	    if(pLine == NULL)
1053 	    {
1054 	        return true;
1055 	    }
1056 	    fp_Page * pPage = pLine->getPage();
1057 	    if(pPage == NULL)
1058 	    {
1059 	        return true;
1060 	    }
1061 	    else if(pLine->getY() > pPage->getHeight())
1062 	    {
1063 		fl_DocSectionLayout * pDSL= pPage->getOwningSection();
1064 		pDSL->setNeedsSectionBreak(true,pPage);
1065 		pDSL->format();
1066 		return true;
1067 	    }
1068 	    else
1069 	    {
1070 	        UT_sint32 iPage = 1;
1071 		pPage = getFirstPage();
1072 		while(pPage && pPage != pLine->getPage())
1073 		{
1074 		    pPage = pPage->getNext();
1075 		    iPage++;
1076 		}
1077 		iPage--;
1078 		if(iPage != countPages())
1079 		{
1080 		    return true;
1081 		}
1082 		if(pLine->getPage() != pPage)
1083 		{
1084 		    return true;
1085 		}
1086 	    }
1087 	}
1088 
1089     }
1090     return bRebreak;
1091 }
1092 
1093 void FL_DocLayout::Rebreak(void)
1094 {
1095     fl_DocSectionLayout * pDSL = getFirstSection();
1096     while(pDSL)
1097     {
1098 	pDSL->completeBreakSection();
1099 	pDSL = pDSL->getNextDocSection();
1100     }
1101 
1102     //
1103     // Finally set all page numbers in frames
1104     //
1105     setFramePageNumbers(0);
1106 }
1107 #endif
1108 
1109 
1110 /*!
1111  *  This method is used to reset the colorization such as what occurs
1112  * when showAuthors state is changed.
1113  */
refreshRunProperties(void)1114 void FL_DocLayout::refreshRunProperties(void)
1115 {
1116     fl_DocSectionLayout * pDSL = getFirstSection();
1117     fl_BlockLayout * pBL = pDSL->getFirstBlock();
1118     while(pBL)
1119     {
1120         pBL->refreshRunProperties();
1121 	pBL = pBL->getNextBlockInDocument();
1122     }
1123 }
1124 
1125 /*!
1126  * Starting from page iStartPage, set the page numbers of the frames in the
1127  * document.
1128  */
setFramePageNumbers(UT_sint32 iStartPage)1129 void FL_DocLayout::setFramePageNumbers(UT_sint32 iStartPage)
1130 {
1131       UT_sint32 iPage = 0;
1132       fp_Page * pPage = NULL;
1133       for(iPage=iStartPage; iPage<countPages();iPage++)
1134       {
1135 	  pPage = getNthPage(iPage);
1136 	  pPage->setPageNumberInFrames();
1137       }
1138 }
1139 
1140 /*!
1141  * relocate the frame given to a new block. This involves changing the piece table as the
1142  * frame strux is placed immediately after its parent block strux.
1143  * The function returns the pointer to the new frame layout.
1144  */
relocateFrame(fl_FrameLayout * pFL,fl_BlockLayout * newBlock,const gchar ** attributes,const gchar ** properties)1145 fl_FrameLayout * FL_DocLayout:: relocateFrame(fl_FrameLayout * pFL, fl_BlockLayout * newBlock,
1146 											  const gchar** attributes, const gchar **properties)
1147 {
1148 	if(m_pDoc->isDoingTheDo())
1149 	{
1150 		return(pFL);
1151 	}
1152 	m_pDoc->beginUserAtomicGlob();
1153 	const PP_AttrProp* pFrameAP = NULL;
1154 	PP_AttrProp * pUpdatedFrameAP = NULL;
1155 	pFL->getAP(pFrameAP);
1156 	pUpdatedFrameAP = pFrameAP->cloneWithReplacements(attributes,properties,false);
1157 
1158 	// Copy the frame content to clipboard
1159 	bool isTextBox = (pFL->getFrameType() < FL_FRAME_WRAPPER_IMAGE);
1160 	PT_DocPosition posStart = pFL->getPosition(true);
1161 	PT_DocPosition posEnd = posStart + pFL->getLength();
1162 	UT_ByteBuf * pLocalBuf = new UT_ByteBuf;
1163 	if(isTextBox)
1164 	{
1165 		PD_DocumentRange dr_oldFrame;
1166 		dr_oldFrame.set(m_pDoc,posStart+1,posEnd-1);
1167 		IE_Exp_RTF * pExpRtf = new IE_Exp_RTF(m_pDoc);
1168 		PD_DocumentRange docRange(m_pDoc, posStart+1,posEnd-1);
1169 		pExpRtf->copyToBuffer(&docRange,pLocalBuf);
1170 		delete pExpRtf;
1171 	}
1172 
1173 	// Delete Frame
1174 	pf_Frag_Strux* sdhStart =  pFL->getStruxDocHandle();
1175 	pf_Frag_Strux* sdhEnd = NULL;
1176 	posStart = m_pDoc->getStruxPosition(sdhStart);
1177 	m_pDoc->getNextStruxOfType(sdhStart, PTX_EndFrame, &sdhEnd);
1178 	if(sdhEnd == NULL)
1179 	{
1180 		posEnd = posStart+1;
1181 	}
1182 	else
1183 	{
1184 		posEnd = m_pDoc->getStruxPosition(sdhEnd)+1;
1185 	}
1186 	UT_uint32 iRealDeleteCount;
1187 	PP_AttrProp * p_AttrProp_Before = NULL;
1188 	m_pDoc->deleteSpan(posStart, posEnd, p_AttrProp_Before, iRealDeleteCount,true);
1189 	pFL = NULL;
1190 	// Insert the new frame struxes
1191 	pf_Frag_Strux * pfFrame = NULL;
1192 	m_pDoc->insertStrux(newBlock->getPosition(),PTX_SectionFrame,pUpdatedFrameAP->getAttributes(),
1193 						pUpdatedFrameAP->getProperties(),&pfFrame);
1194 	PT_DocPosition posFrame = pfFrame->getPos();
1195 	m_pDoc->insertStrux(posFrame+1,PTX_EndFrame);
1196 	m_pView->insertParaBreakIfNeededAtPos(posFrame+2);
1197 	// paste in the content of the frame.
1198 	if(isTextBox)
1199 	{
1200 		PD_DocumentRange docRange(m_pDoc,posFrame+1,posFrame+1);
1201 		IE_Imp_RTF * pImpRTF = new IE_Imp_RTF(m_pDoc);
1202 		const unsigned char * pData = static_cast<const unsigned char *>(pLocalBuf->getPointer(0));
1203 		UT_uint32 iLen = pLocalBuf->getLength();
1204 		pImpRTF->pasteFromBuffer(&docRange,pData,iLen);
1205 		delete pImpRTF;
1206 	}
1207 	delete pLocalBuf;
1208 	m_pDoc->endUserAtomicGlob();
1209 
1210 	fl_ContainerLayout * pNewFL = pfFrame->getFmtHandle(m_lid);
1211 	if (pNewFL && (pNewFL->getContainerType() == FL_CONTAINER_FRAME))
1212 	{
1213 		return (static_cast <fl_FrameLayout *>(pNewFL));
1214 	}
1215 	else
1216 	{
1217 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1218 		return NULL;
1219 	}
1220 }
1221 
1222 /*!
1223   add a frame to the list of frames that need to be inserted on a page later in the document than its
1224   parent block. A frame can be placed up to 3 pages after its parent block. This list is needed during
1225   the initial layout stage.
1226  */
1227 
addFramesToBeInserted(fp_FrameContainer * pFrame)1228 bool FL_DocLayout::addFramesToBeInserted(fp_FrameContainer * pFrame)
1229 {
1230 	m_vecFramesToBeInserted.addItem(pFrame);
1231 	return true;
1232 }
1233 
1234 /*!
1235   remove a frame from the list of frames that need to be inserted on a page later in the document.
1236  */
1237 
removeFramesToBeInserted(fp_FrameContainer * pFrame)1238 bool FL_DocLayout::removeFramesToBeInserted(fp_FrameContainer * pFrame)
1239 {
1240 	UT_sint32 i = m_vecFramesToBeInserted.findItem(pFrame);
1241 	if(i < 0)
1242 	{
1243 		return false;
1244 	}
1245 	m_vecFramesToBeInserted.deleteNthItem(i);
1246 	return true;
1247 }
1248 
1249 /*!
1250   find a frame that needs to be inserted on page pPage. Only frames that are inserted on a page later
1251   in the document than their parent block are placed in this list. This list is needed during
1252   the initial layout stage.
1253  */
1254 
findFramesToBeInserted(fp_Page * pPage)1255 fp_FrameContainer * FL_DocLayout::findFramesToBeInserted(fp_Page * pPage)
1256 {
1257 	UT_sint32 count = m_vecFramesToBeInserted.getItemCount();
1258 	if (count == 0)
1259 	{
1260 		return NULL;
1261 	}
1262 
1263 	UT_sint32 iPage = pPage->getPageNumber();
1264 	UT_sint32 k = 0;
1265 	fp_FrameContainer * pFrame = NULL;
1266 	for (k = 0;k < count;k++)
1267 	{
1268 		pFrame = m_vecFramesToBeInserted.getNthItem(k);
1269 		if (pFrame->getPreferedPageNo() == iPage)
1270 		{
1271 			return pFrame;
1272 		}
1273 	}
1274 	return NULL;
1275 }
1276 
1277 
setView(FV_View * pView)1278 void FL_DocLayout::setView(FV_View* pView)
1279 {
1280 	m_pView = pView;
1281 
1282 	fp_Page* pPage = getFirstPage();
1283 
1284 	while (pPage)
1285 	{
1286 		pPage->setView(pView);
1287 
1288 		pPage = pPage->getNext();
1289 	}
1290 
1291 	if (m_pView && !m_pPrefs )
1292 	{
1293 		XAP_Prefs *pPrefs= XAP_App::getApp()->getPrefs();
1294 		UT_ASSERT_HARMLESS(pPrefs);
1295 
1296 		if (pPrefs)
1297 		{
1298 			// remember this so we can remove the listener later
1299 			m_pPrefs = pPrefs;
1300 
1301 			// initialize the vars here
1302 			_prefsListener( pPrefs, NULL, this );
1303 
1304 			// keep updating itself
1305 			pPrefs->addListener ( _prefsListener, this );
1306 			bool b;
1307 			if (m_pPrefs->getPrefsValueBool(static_cast<const gchar *>("DebugFlash"),&b)  &&  b == true)
1308 			{
1309 				addBackgroundCheckReason(bgcrDebugFlash);
1310 			}
1311 			m_pPrefs->getPrefsValueBool(static_cast<const gchar *>("AutoGrammarCheck"),&b);
1312 			if (b)
1313 			{
1314 				addBackgroundCheckReason(bgcrGrammar);
1315 				m_bAutoGrammarCheck = true;
1316 				m_iGrammarCount = 0;
1317 				m_iPrevPos = 0;
1318 			}
1319 		}
1320 	}
1321 }
1322 
1323 /*!
1324  * This method fills the referenced string with the character representation
1325  * of the decimal footnote value based on the FootnoteType passed as a
1326  * parameter
1327  */
getStringFromFootnoteVal(UT_String & sVal,UT_sint32 iVal,FootnoteType iFootType) const1328 void FL_DocLayout::getStringFromFootnoteVal(UT_String & sVal, UT_sint32 iVal, FootnoteType iFootType) const
1329 {
1330         fl_AutoNum autoCalc(0,0,NUMBERED_LIST,0,NULL,NULL,getDocument(),getView());
1331 	switch (iFootType)
1332 	{
1333 	case FOOTNOTE_TYPE_NUMERIC:
1334 		UT_String_sprintf (sVal,"%d", iVal);
1335 		break;
1336 	case FOOTNOTE_TYPE_NUMERIC_SQUARE_BRACKETS:
1337 		UT_String_sprintf (sVal,"[%d]", iVal);
1338 		break;
1339 	case FOOTNOTE_TYPE_NUMERIC_PAREN:
1340 		UT_String_sprintf (sVal,"(%d)", iVal);
1341 		break;
1342 	case FOOTNOTE_TYPE_NUMERIC_OPEN_PAREN:
1343 		UT_String_sprintf (sVal,"%d)", iVal);
1344 		break;
1345 	case FOOTNOTE_TYPE_LOWER:
1346 	{
1347 		char * val = autoCalc.dec2ascii(iVal,96);
1348 		UT_String_sprintf (sVal,"%s",val);
1349 		FREEP(val);
1350 		break;
1351 	}
1352 	case FOOTNOTE_TYPE_LOWER_PAREN:
1353 	{
1354 		char * val = autoCalc.dec2ascii(iVal,96);
1355 		UT_String_sprintf (sVal,"(%s)",val);
1356 		FREEP(val);
1357 		break;
1358 	}
1359 	case FOOTNOTE_TYPE_LOWER_OPEN_PAREN:
1360 	{
1361 		char * val = autoCalc.dec2ascii(iVal,96);
1362 		UT_String_sprintf (sVal,"%s)",val);
1363 		FREEP(val);
1364 		break;
1365 	}
1366 	case FOOTNOTE_TYPE_UPPER:
1367 	{
1368 		char * val = autoCalc.dec2ascii(iVal,64);
1369 		UT_String_sprintf (sVal,"%s",val);
1370 		FREEP(val);
1371 		break;
1372 	}
1373 	case FOOTNOTE_TYPE_UPPER_PAREN:
1374 	{
1375 		char * val = autoCalc.dec2ascii(iVal,64);
1376 		UT_String_sprintf (sVal,"(%s)",val);
1377 		FREEP(val);
1378 		break;
1379 	}
1380 	case FOOTNOTE_TYPE_UPPER_OPEN_PAREN:
1381 	{
1382 		char * val = autoCalc.dec2ascii(iVal,64);
1383 		UT_String_sprintf (sVal,"%s)",val);
1384 		FREEP(val);
1385 		break;
1386 	}
1387 	case FOOTNOTE_TYPE_LOWER_ROMAN:
1388 	{
1389 		char * val = autoCalc.dec2roman(iVal,true);
1390 		UT_String_sprintf (sVal,"%s",val);
1391 		FREEP(val);
1392 		break;
1393 	}
1394 	case FOOTNOTE_TYPE_LOWER_ROMAN_PAREN:
1395 	{
1396 		char * val = autoCalc.dec2roman(iVal,true);
1397 		UT_String_sprintf (sVal,"(%s)",val);
1398 		FREEP(val);
1399 		break;
1400 	}
1401 	case FOOTNOTE_TYPE_UPPER_ROMAN:
1402 	{
1403 		char * val = autoCalc.dec2roman(iVal,false);
1404 		UT_String_sprintf (sVal,"%s",val);
1405 		FREEP(val);
1406 		break;
1407 	}
1408 	case FOOTNOTE_TYPE_UPPER_ROMAN_PAREN:
1409 	{
1410 		char * val = autoCalc.dec2roman(iVal,false);
1411 		UT_String_sprintf (sVal,"(%s)",val);
1412 		FREEP(val);
1413 		break;
1414 	}
1415 	default:
1416 		UT_String_sprintf (sVal,"%d", iVal);
1417 	}
1418 }
1419 
1420 
1421 /*!
1422  * This simply returns the number of footnotes in the document.
1423  */
countFootnotes(void) const1424 UT_uint32 FL_DocLayout::countFootnotes(void) const
1425 {
1426 	return m_vecFootnotes.getItemCount();
1427 }
1428 /*!
1429  * Add a footnote layout to the vector remembering them.
1430  */
addFootnote(fl_FootnoteLayout * pFL)1431 void FL_DocLayout::addFootnote(fl_FootnoteLayout * pFL)
1432 {
1433 	m_vecFootnotes.addItem(pFL);
1434 }
1435 
1436 /*!
1437  * get a pointer to the Nth footnote layout in the vector remembering them.
1438  */
getNthFootnote(UT_sint32 i) const1439 fl_FootnoteLayout * FL_DocLayout::getNthFootnote(UT_sint32 i) const
1440 {
1441 	UT_ASSERT(i>=0);
1442 	if(i >= m_vecFootnotes.getItemCount())
1443 	{
1444 		return NULL;
1445 	}
1446 	else
1447 	{
1448 		return m_vecFootnotes.getNthItem(i);
1449 	}
1450 }
1451 
1452 /*!
1453  * Remove a foonote layout from the Vector.
1454  */
removeFootnote(fl_FootnoteLayout * pFL)1455 void FL_DocLayout::removeFootnote(fl_FootnoteLayout * pFL)
1456 {
1457 	UT_sint32 i = m_vecFootnotes.findItem(pFL);
1458 	if(i< 0)
1459 	{
1460 		return;
1461 	}
1462 	m_vecFootnotes.deleteNthItem(i);
1463 }
1464 
1465 /*!
1466  * This method returns the footnote layout associated with the input PID
1467  */
findFootnoteLayout(UT_uint32 footpid) const1468 fl_FootnoteLayout * FL_DocLayout::findFootnoteLayout(UT_uint32 footpid) const
1469 {
1470 	UT_sint32 i = 0;
1471 	fl_FootnoteLayout * pTarget = NULL;
1472  	fl_FootnoteLayout * pFL = NULL;
1473 	for(i=0; i<m_vecFootnotes.getItemCount(); i++)
1474 	{
1475 		pFL = getNthFootnote(i);
1476 		if(pFL->getFootnotePID() == footpid)
1477 		{
1478 			pTarget = pFL;
1479 			break;
1480 		}
1481 	}
1482 	return pTarget;
1483 }
1484 /*!
1485  * This returns the position of the footnote in the document. This is useful
1486  * for calculating the footnote's value and positioning it in a footnote
1487  * section
1488  */
getFootnoteVal(UT_uint32 footpid) const1489 UT_sint32 FL_DocLayout::getFootnoteVal(UT_uint32 footpid) const
1490 {
1491 	UT_sint32 i =0;
1492 	UT_sint32 pos = m_iFootnoteVal;
1493 	fl_FootnoteLayout * pTarget = findFootnoteLayout(footpid);
1494  	fl_FootnoteLayout * pFL = NULL;
1495 	if(pTarget== NULL)
1496 	{
1497 		return 0;
1498 	}
1499 	PT_DocPosition posTarget = pTarget->getDocPosition();
1500 	fl_DocSectionLayout * pDocSecTarget = pTarget->getDocSectionLayout();
1501 	fp_Container * pCon = pTarget->getFirstContainer();
1502 	fp_Page * pPageTarget = NULL;
1503 	if(pCon)
1504 	{
1505 		pPageTarget = pCon->getPage();
1506 	}
1507 	for(i=0; i<m_vecFootnotes.getItemCount(); i++)
1508 	{
1509 		pFL = getNthFootnote(i);
1510 		if(!m_bRestartFootSection && !m_bRestartFootPage)
1511 		{
1512 			if(pFL->getDocPosition() < posTarget)
1513 			{
1514 				pos++;
1515 			}
1516 		}
1517 		else if(m_bRestartFootSection)
1518 		{
1519 			if((pDocSecTarget == pFL->getDocSectionLayout()) && (pFL->getDocPosition() < posTarget))
1520 			{
1521 				pos++;
1522 			}
1523 		}
1524 		else if(m_bRestartFootPage)
1525 		{
1526 			pCon = pFL->getFirstContainer();
1527 			fp_Page * pPage = NULL;
1528 			if(pCon)
1529 			{
1530 				pPage = pCon->getPage();
1531 			}
1532 			if((pPage == pPageTarget) && (pFL->getDocPosition() < posTarget))
1533 			{
1534 				pos++;
1535 			}
1536 		}
1537 	}
1538 	return pos;
1539 }
1540 
1541 // Annotation methods
1542 
1543 
compareLayouts(const void * ppCL1,const void * ppCL2)1544 static UT_sint32 compareLayouts(const void * ppCL1, const void * ppCL2)
1545 {
1546   void * v1 = const_cast<void *>(ppCL1);
1547   void * v2 = const_cast<void *>(ppCL2);
1548   fl_ContainerLayout ** pCL1 = reinterpret_cast<fl_ContainerLayout **>(v1);
1549   fl_ContainerLayout ** pCL2 = reinterpret_cast<fl_ContainerLayout **>(v2);
1550   return static_cast<UT_sint32>((*pCL1)->getPosition(true)) - static_cast<UT_sint32>((*pCL2)->getPosition(true));
1551 }
1552 
1553 /*!
1554  * This simply returns the number of annotations in the document.
1555  */
countAnnotations(void) const1556 UT_uint32 FL_DocLayout::countAnnotations(void) const
1557 {
1558 	return m_vecAnnotations.getItemCount();
1559 }
1560 
1561 /*!
1562  * Collapse all the blocks containing Annotations. This is useful for
1563  * when we toggle displaying/hiding annotations.
1564  */
collapseAnnotations(void)1565 bool  FL_DocLayout::collapseAnnotations(void)
1566 {
1567   fl_AnnotationLayout * pFL = NULL;
1568   fl_BlockLayout * pBL = NULL;
1569   UT_uint32 i = 0;
1570   for(i= 0; i<countAnnotations(); i++)
1571   {
1572       pFL = getNthAnnotation(i);
1573       if(pFL)
1574       {
1575 	  pBL = pFL->getContainingBlock();
1576 	  if(pBL)
1577 	  {
1578 	      pBL->collapse();
1579 	  }
1580 	  pBL = static_cast<fl_BlockLayout *>(pFL->getFirstLayout());
1581 	  if(pBL)
1582 	      pBL->collapse();
1583 
1584 	  pFL->collapse();
1585       }
1586   }
1587   return true;
1588 }
1589 
1590 /*!
1591  * Add a annotation layout to the vector remembering them.
1592  */
addAnnotation(fl_AnnotationLayout * pFL)1593 void FL_DocLayout::addAnnotation(fl_AnnotationLayout * pFL)
1594 {
1595 	m_vecAnnotations.addItem(pFL);
1596 	m_vecAnnotations.qsort(compareLayouts);
1597 	UT_uint32 i = 0;
1598 	for(i=0; i<countAnnotations();i++)
1599 	{
1600 	    fl_AnnotationLayout * pAL = getNthAnnotation(i);
1601 	    fp_AnnotationRun * pARun = pAL->getAnnotationRun();
1602 	    if(pARun)
1603 	    {
1604 		pARun->recalcValue();
1605 	    }
1606 	}
1607 }
1608 
1609 /*!
1610  * get a pointer to the Nth annotation layout in the vector remembering them.
1611  */
getNthAnnotation(UT_sint32 i) const1612 fl_AnnotationLayout * FL_DocLayout::getNthAnnotation(UT_sint32 i) const
1613 {
1614 	UT_ASSERT(i>=0);
1615 	if(i >= m_vecAnnotations.getItemCount())
1616 	{
1617 		return NULL;
1618 	}
1619 	else
1620 	{
1621 		return m_vecAnnotations.getNthItem(i);
1622 	}
1623 }
1624 
1625 /*!
1626  * Remove an annotation layout from the Vector.
1627  */
removeAnnotation(fl_AnnotationLayout * pFL)1628 void FL_DocLayout::removeAnnotation(fl_AnnotationLayout * pFL)
1629 {
1630 	UT_sint32 i = m_vecAnnotations.findItem(pFL);
1631 	if(i< 0)
1632 	{
1633 		return;
1634 	}
1635 	m_vecAnnotations.deleteNthItem(i);
1636 	if(isLayoutDeleting())
1637 	  return;
1638 	m_vecAnnotations.qsort(compareLayouts);
1639 	for(i=0; i<static_cast<UT_sint32>(countAnnotations());i++)
1640 	{
1641 	    fl_AnnotationLayout * pAL = getNthAnnotation(i);
1642 	    fp_AnnotationRun * pARun = pAL->getAnnotationRun();
1643 	    if(pARun)
1644 	    {
1645 		pARun->recalcValue();
1646 	    }
1647 	}
1648 }
1649 
1650 /*!
1651  * This method returns the annotation layout associated with the input PID
1652  */
findAnnotationLayout(UT_uint32 annpid) const1653 fl_AnnotationLayout * FL_DocLayout::findAnnotationLayout(UT_uint32 annpid) const
1654 {
1655 	UT_sint32 i = 0;
1656 	fl_AnnotationLayout * pTarget = NULL;
1657  	fl_AnnotationLayout * pFL = NULL;
1658 	for(i=0; i<m_vecAnnotations.getItemCount(); i++)
1659 	{
1660 		pFL = getNthAnnotation(i);
1661 		if(pFL->getAnnotationPID() == annpid)
1662 		{
1663 			pTarget = pFL;
1664 			break;
1665 		}
1666 	}
1667 	return pTarget;
1668 }
1669 /*!
1670  * This returns the position of the Annotation in the vector of annotations. This is useful
1671  * for calculating the annotionation positioning it in a annotation
1672  * section
1673  */
getAnnotationVal(UT_uint32 annpid) const1674 UT_sint32 FL_DocLayout::getAnnotationVal(UT_uint32 annpid) const
1675 {
1676 	UT_sint32 i =0;
1677 	UT_sint32 pos = 0;
1678  	fl_AnnotationLayout * pAL = NULL;
1679 	for(i=0; i<m_vecAnnotations.getItemCount(); i++)
1680 	{
1681 		pAL = getNthAnnotation(i);
1682 		if(pAL->getAnnotationPID() == annpid)
1683 		{
1684 		        pos = i;
1685 			break;
1686 		}
1687 	}
1688 	if(pos != i)
1689 	  pos = -1;
1690 	return pos;
1691 }
1692 
1693 
1694 /*!
1695  * The method returns the doc section layout before which the endnotes are
1696  * inserted.
1697  */
getDocSecForEndnote(fp_EndnoteContainer * pECon) const1698 fl_DocSectionLayout * FL_DocLayout::getDocSecForEndnote(fp_EndnoteContainer * pECon) const
1699 {
1700 	fl_DocSectionLayout *pDSL = NULL;
1701 	if(getPlaceEndAtSecEnd())
1702 	{
1703 		fl_EndnoteLayout * pEL = static_cast<fl_EndnoteLayout *>(pECon->getSectionLayout());
1704 		pDSL = pEL->getDocSectionLayout();
1705 		return pDSL;
1706 	}
1707 	pDSL = getLastSection();
1708 	return pDSL;
1709 }
1710 
1711 /*!
1712  * This method checks to too if the endnote container to be removed is the
1713  * first or last of the section. If it is the first/last pointers are updated.
1714  */
removeEndnoteContainer(fp_EndnoteContainer * pECon)1715 void FL_DocLayout::removeEndnoteContainer(fp_EndnoteContainer * pECon)
1716 {
1717 	xxx_UT_DEBUGMSG(("Remove endnote container %x \n",pECon));
1718 	fl_DocSectionLayout * pDSL = getDocSecForEndnote(pECon);
1719 	if(pDSL->getFirstEndnoteContainer() == static_cast<fp_Container *>(pECon))
1720 	{
1721 		pDSL->setFirstEndnoteContainer(static_cast<fp_EndnoteContainer *>(pECon->getNext()));
1722 	}
1723 	if(pDSL->getLastEndnoteContainer() == static_cast<fp_Container *>(pECon))
1724 	{
1725 		pDSL->setLastEndnoteContainer(static_cast<fp_EndnoteContainer *>(pECon->getPrev()));
1726 	}
1727 //
1728 // Remove from list
1729 //
1730 	if(pECon->getPrev())
1731 	{
1732 		pECon->getPrev()->setNext(pECon->getNext());
1733 	}
1734 	if(pECon->getNext())
1735 	{
1736 		pECon->getNext()->setPrev(pECon->getPrev());
1737 	}
1738 //	fl_EndnoteLayout * pEL = static_cast<fl_EndnoteLayout *>(pECon->getSectionLayout());
1739 //	pDSL = static_cast<fl_DocSectionLayout *>(pEL->myContainingLayout());
1740 //	if(!pDSL->isCollapsing())
1741 	{
1742 		fp_Column * pCol = static_cast<fp_Column *>(pECon->getContainer());
1743 
1744 		if(pCol)
1745 		{
1746 			pCol->removeContainer(pECon);
1747 		}
1748 	}
1749 }
1750 
1751 /*!
1752  * This method inserts the endnote container into the list of containers held
1753  * held by the appropriate DocSection.
1754  */
insertEndnoteContainer(fp_EndnoteContainer * pECon)1755 void FL_DocLayout::insertEndnoteContainer(fp_EndnoteContainer * pECon)
1756 {
1757 	fl_DocSectionLayout * pDSL = getDocSecForEndnote(pECon);
1758 	fp_Container * pCon = pDSL->getFirstEndnoteContainer();
1759 	if(pCon == NULL)
1760 	{
1761 		pDSL->setFirstEndnoteContainer(pECon);
1762 		pDSL->setLastEndnoteContainer(pECon);
1763 		pECon->setNext(NULL);
1764 		pECon->setPrev(NULL);
1765 		fp_Column * pCol2 =  static_cast<fp_Column *>(pDSL->getLastContainer());
1766 		if(pCol2)
1767 		{
1768 			pCol2->addContainer(pECon);
1769 //
1770 // No height defined yet. Can't layout
1771 //
1772 //			pCol->layout();
1773 		}
1774 		else
1775 		{
1776 			fp_Column * pCol = static_cast<fp_Column *>(pDSL->getNewContainer(NULL));
1777 			pCol->addContainer(pECon);
1778 //
1779 // No height defined yet. Can't layout
1780 //
1781 //			pCol->layout();
1782 		}
1783 		return;
1784 	}
1785 	fp_EndnoteContainer * pETmp = static_cast<fp_EndnoteContainer *>(pCon);
1786 	fl_EndnoteLayout * pEL = static_cast<fl_EndnoteLayout *>(pECon->getSectionLayout());
1787 	fl_EndnoteLayout * pETmpL = static_cast<fl_EndnoteLayout *>(pETmp->getSectionLayout());
1788 	bool bBefore = (pEL->getPosition() < pETmpL->getPosition());
1789 	while(!bBefore && pETmp)
1790 	{
1791 		pETmp = static_cast<fp_EndnoteContainer *>(pETmp->getNext());
1792 		if(pETmp)
1793 		{
1794 			pETmpL = static_cast<fl_EndnoteLayout *>(pETmp->getSectionLayout());
1795 			UT_return_if_fail(pETmpL);
1796 			bBefore = (pEL->getPosition() < pETmpL->getPosition());
1797 		}
1798 	}
1799 	if(bBefore)
1800 	{
1801 		fp_EndnoteContainer * pOldPrev = static_cast<fp_EndnoteContainer *>(pETmp->getPrev());
1802 		pETmp->setPrev(pECon);
1803 		if(pDSL->getFirstEndnoteContainer() == pETmp)
1804 		{
1805 			pDSL->setFirstEndnoteContainer(pECon);
1806 		}
1807 		else
1808 		{
1809 			pOldPrev->setNext(pECon);
1810 
1811 		}
1812 		fp_Column * pCol = static_cast<fp_Column *>(pETmp->getContainer());
1813 		pECon->setNext(pETmp);
1814 		pECon->setPrev(pOldPrev);
1815 		if(pOldPrev)
1816 		{
1817 			pCol->insertContainerAfter(pECon, pOldPrev);
1818 		}
1819 		else
1820 		{
1821 			pCol->insertContainer(pECon);
1822 		}
1823 		pCol->layout();
1824 	}
1825 	else
1826 	{
1827 		pETmp = static_cast<fp_EndnoteContainer *>(pDSL->getLastEndnoteContainer());
1828 		pETmp->setNext(pECon);
1829 		pECon->setPrev(pETmp);
1830 		pECon->setNext(NULL);
1831 		pDSL->setLastEndnoteContainer(pECon);
1832 		fp_Column * pCol = static_cast<fp_Column *>(pETmp->getContainer());
1833 		if(!pCol)
1834 		{
1835 			pCol = static_cast<fp_Column *>(pDSL->getLastContainer());
1836 			if(pCol == NULL)
1837 			{
1838 				pCol = static_cast<fp_Column *>(pDSL->getNewContainer());
1839 			}
1840 		}
1841 		pCol->addContainer(pECon);
1842 		pCol->layout();
1843 	}
1844 }
1845 
1846 /*!
1847  * This simply returns the number of footnotes in the document.
1848  */
countEndnotes(void) const1849 UT_uint32 FL_DocLayout::countEndnotes(void) const
1850 {
1851 	return m_vecEndnotes.getItemCount();
1852 }
1853 /*!
1854  * Add a footnote layout to the vector remembering them.
1855  */
addEndnote(fl_EndnoteLayout * pFL)1856 void FL_DocLayout::addEndnote(fl_EndnoteLayout * pFL)
1857 {
1858 	m_vecEndnotes.addItem(pFL);
1859 }
1860 
1861 /*!
1862  * get a pointer to the Nth footnote layout in the vector remembering them.
1863  */
getNthEndnote(UT_sint32 i) const1864 fl_EndnoteLayout * FL_DocLayout::getNthEndnote(UT_sint32 i) const
1865 {
1866 	UT_ASSERT(i>=0);
1867 	if(i >= m_vecEndnotes.getItemCount())
1868 	{
1869 		return NULL;
1870 	}
1871 	else
1872 	{
1873 		return m_vecEndnotes.getNthItem(i);
1874 	}
1875 }
1876 
1877 /*!
1878  * Remove a foonote layout from the Vector.
1879  */
removeEndnote(fl_EndnoteLayout * pFL)1880 void FL_DocLayout::removeEndnote(fl_EndnoteLayout * pFL)
1881 {
1882 	UT_sint32 i = m_vecEndnotes.findItem(pFL);
1883 	if(i< 0)
1884 	{
1885 		return;
1886 	}
1887 	m_vecEndnotes.deleteNthItem(i);
1888 }
1889 
1890 /*!
1891  * This method returns the footnote layout associated with the input PID
1892  */
findEndnoteLayout(UT_uint32 footpid) const1893 fl_EndnoteLayout * FL_DocLayout::findEndnoteLayout(UT_uint32 footpid) const
1894 {
1895 	UT_sint32 i = 0;
1896 	fl_EndnoteLayout * pTarget = NULL;
1897  	fl_EndnoteLayout * pFL = NULL;
1898 	for(i=0; i<m_vecEndnotes.getItemCount(); i++)
1899 	{
1900 		pFL = getNthEndnote(i);
1901 		if(pFL->getEndnotePID() == footpid)
1902 		{
1903 			pTarget = pFL;
1904 			break;
1905 		}
1906 	}
1907 	return pTarget;
1908 }
1909 /*!
1910  * This returns the position of the Endnote in the document. This is useful
1911  * for calculating the Endnote's value and positioning it in a footnote
1912  * section
1913  */
getEndnoteVal(UT_uint32 footpid) const1914 UT_sint32 FL_DocLayout::getEndnoteVal(UT_uint32 footpid) const
1915 {
1916 	UT_sint32 i =0;
1917 	UT_sint32 pos = m_iEndnoteVal;
1918 	fl_EndnoteLayout * pTarget = findEndnoteLayout(footpid);
1919  	fl_EndnoteLayout * pFL = NULL;
1920 	if(pTarget== NULL)
1921 	{
1922 		return 0;
1923 	}
1924 	PT_DocPosition posTarget = pTarget->getDocPosition();
1925 	fl_DocSectionLayout * pDocSecTarget = pTarget->getDocSectionLayout();
1926 	for(i=0; i<m_vecEndnotes.getItemCount(); i++)
1927 	{
1928 		pFL = getNthEndnote(i);
1929 		if(!m_bRestartEndSection)
1930 		{
1931 			if(pFL->getDocPosition() < posTarget)
1932 			{
1933 				pos++;
1934 			}
1935 		}
1936 		else if(m_bRestartEndSection)
1937 		{
1938 			if((pDocSecTarget == pFL->getDocSectionLayout()) && (pFL->getDocPosition() < posTarget))
1939 			{
1940 				pos++;
1941 			}
1942 		}
1943 	}
1944 	return pos;
1945 }
1946 
1947 
1948 //
1949 //--------------------------------------------------------------------
1950 // Table of content Functions.
1951 //--------------------------------------------------------------------
1952 //
1953 
getNumTOCs(void) const1954 UT_sint32 FL_DocLayout::getNumTOCs(void) const
1955 {
1956 	return m_vecTOC.getItemCount();
1957 }
1958 
getNthTOC(UT_sint32 i) const1959 fl_TOCLayout * FL_DocLayout::getNthTOC(UT_sint32 i) const
1960 {
1961 	if( i >= getNumTOCs())
1962 	{
1963 		return NULL;
1964 	}
1965 	return m_vecTOC.getNthItem(i);
1966 }
1967 
recalculateTOCFields(void)1968 void FL_DocLayout::recalculateTOCFields(void)
1969 {
1970 	UT_sint32 num = getNumTOCs();
1971 	UT_sint32 i =0;
1972 	for (i=0; i<num; i++)
1973 	{
1974 		fl_TOCLayout * pTOCL = getNthTOC(i);
1975 		pTOCL->recalculateFields(i);
1976 	}
1977 }
1978 
1979 /*!
1980  * This method scans all the TOC in the document and adds or removes the
1981  * supplied block if it needs to be either added or removed from a TOC.
1982  * This method returns true if pBlock is in at least one TOC
1983  */
addOrRemoveBlockFromTOC(fl_BlockLayout * pBlock)1984 bool FL_DocLayout::addOrRemoveBlockFromTOC(fl_BlockLayout * pBlock)
1985 {
1986 	UT_sint32 count = getNumTOCs();
1987 	if(count == 0)
1988 	{
1989 		return false;
1990 	}
1991 	UT_UTF8String sStyle;
1992 	pBlock->getStyle(sStyle);
1993 	UT_sint32 i = 0;
1994 	UT_sint32 inTOC = count;
1995 	UT_sint32 _addTOC = 0;
1996 
1997 	for(i=0; i<count; i++)
1998 	{
1999 		fl_TOCLayout * pTOC = getNthTOC(i);
2000 		if(pTOC->isBlockInTOC(pBlock))
2001 		{
2002 			if(!pTOC->isStyleInTOC(sStyle))
2003 			{
2004 				pTOC->removeBlock(pBlock);
2005 				inTOC--;
2006 			}
2007 			else
2008 			{
2009 //
2010 // Style changed so delete the old shadow of the block and make a new shadow.
2011 //
2012 				pTOC->removeBlock(pBlock);
2013 				pTOC->addBlock(pBlock);
2014 			}
2015 		}
2016 		else
2017 		{
2018 			if(pTOC->isStyleInTOC(sStyle))
2019 			{
2020 				pTOC->addBlock(pBlock);
2021 				_addTOC++;
2022 			}
2023 		}
2024 	}
2025 	if((inTOC <= 0) && (_addTOC == 0))
2026 	{
2027 		return false;
2028 	}
2029 	return true;
2030 }
2031 
2032 /*!
2033  * Remove pBlock from all the TOC's it's in.
2034  * Return false if there are no TOC's in the Doc.
2035  * return true otherwise.
2036  */
removeBlockFromTOC(fl_BlockLayout * pBlock)2037 bool FL_DocLayout::removeBlockFromTOC(fl_BlockLayout *pBlock)
2038 {
2039 	UT_sint32 count = getNumTOCs();
2040 	if(count == 0)
2041 	{
2042 		return false;
2043 	}
2044 	UT_sint32 i = 0;
2045 	for(i=0; i<count; i++)
2046 	{
2047 		fl_TOCLayout * pTOC = getNthTOC(i);
2048 		if(pTOC->isBlockInTOC(pBlock))
2049 		{
2050 			pTOC->removeBlock(pBlock);
2051 		}
2052 	}
2053 	return true;
2054 }
2055 
2056 /*!
2057  * returns true if the block is in at least one TOC.
2058  */
isBlockInTOC(fl_BlockLayout * pBlock) const2059 bool FL_DocLayout::isBlockInTOC(fl_BlockLayout * pBlock) const
2060 {
2061 	UT_sint32 count = getNumTOCs();
2062 	if(count == 0)
2063 	{
2064 		return false;
2065 	}
2066 	UT_sint32 i = 0;
2067 	for(i=0; i<count; i++)
2068 	{
2069 		fl_TOCLayout * pTOC = getNthTOC(i);
2070 		if(pTOC->isBlockInTOC(pBlock))
2071 		{
2072 			return true;
2073 		}
2074 	}
2075 	return false;
2076 }
2077 
2078 /*!
2079  * Fill the supplied vector with pointers to the blocks matching the supplied
2080  * Block.
2081  * Return false if no matching block were found.
2082  * true otherwise
2083  */
getMatchingBlocksFromTOCs(fl_BlockLayout * pBlock,UT_GenericVector<fl_BlockLayout * > * pVecBlocks) const2084 bool FL_DocLayout::getMatchingBlocksFromTOCs(fl_BlockLayout * pBlock, UT_GenericVector<fl_BlockLayout*>* pVecBlocks) const
2085 {
2086 	UT_sint32 count = getNumTOCs();
2087 	if(count == 0)
2088 	{
2089 		return false;
2090 	}
2091 	UT_sint32 i = 0;
2092 	for(i=0; i<count; i++)
2093 	{
2094 		fl_TOCLayout * pTOC = getNthTOC(i);
2095 		if(pTOC->isBlockInTOC(pBlock))
2096 		{
2097 			fl_BlockLayout * pMatch = pTOC->getMatchingBlock(pBlock);
2098 			pVecBlocks->addItem(pMatch);
2099 		}
2100 	}
2101 	return (pVecBlocks->getItemCount() > 0);
2102 }
2103 
addTOC(fl_TOCLayout * pTOC)2104 bool FL_DocLayout::addTOC(fl_TOCLayout * pTOC)
2105 {
2106 	m_vecTOC.addItem(pTOC);
2107 	return true;
2108 }
2109 
removeTOC(fl_TOCLayout * pTOC)2110 bool FL_DocLayout::removeTOC(fl_TOCLayout * pTOC)
2111 {
2112 	UT_sint32 count = getNumTOCs();
2113 	if(count == 0)
2114 	{
2115 		return false;
2116 	}
2117 	UT_sint32 i = m_vecTOC.findItem(pTOC);
2118 	if(i < 0)
2119 	{
2120 		return false;
2121 	}
2122 	m_vecTOC.deleteNthItem(i);
2123 	return true;
2124 }
2125 
2126 /*
2127    updates affected TOCs in response to bookmark operation
2128    returns true if operation resulted in change, false otherwise
2129 */
updateTOCsOnBookmarkChange(const gchar * pBookmark)2130 bool FL_DocLayout::updateTOCsOnBookmarkChange(const gchar * pBookmark)
2131 {
2132 	UT_return_val_if_fail( pBookmark && !isLayoutFilling(), false );
2133 	bool bChange = false;
2134 
2135 	for(UT_sint32 i = 0; i < getNumTOCs(); ++i)
2136 	{
2137 		fl_TOCLayout * pTOC = getNthTOC(i);
2138 		UT_return_val_if_fail( pTOC, false );
2139 
2140 		if(pTOC->getRangeBookmarkName().size() && !strcmp(pTOC->getRangeBookmarkName().utf8_str(), pBookmark))
2141 		{
2142 			// this TOC depends on the given bookmark, update ...
2143 			pTOC->fillTOC();
2144 			bChange = true;
2145 		}
2146 	}
2147 
2148 	return bChange;
2149 }
2150 
2151 
2152 /**
2153  * Calculates the total height of the layout. Includes the
2154  * vertical page margins when not printing.
2155  */
getHeight() const2156 UT_sint32 FL_DocLayout::getHeight() const
2157 {
2158 	UT_sint32 iHeight = 0;
2159 	FV_View * pView = getView(); // add page view dimensions
2160 	UT_uint32 count = m_vecPages.getItemCount();
2161 	UT_uint32 numRows = count / pView->getNumHorizPages();
2162 	if (count > (pView->getNumHorizPages() * numRows))
2163 	{
2164 		numRows++;
2165 	}
2166 
2167 	for (unsigned int i = 0; i<numRows; i++)
2168 	{
2169 		UT_uint32 iRow = i / pView->getNumHorizPages();
2170 		iHeight += pView->getMaxHeight(iRow);
2171 	}
2172 
2173 	if (m_pG->queryProperties(GR_Graphics::DGP_SCREEN))
2174 	{
2175 		if(pView)
2176 		{
2177 			iHeight += pView->getPageViewSep() * count; // Not count - 1, since we want a nice gray border at the very bottom of the document as well
2178 			iHeight += pView->getPageViewTopMargin();
2179 		}
2180 		else
2181 		{
2182 			iHeight += fl_PAGEVIEW_PAGE_SEP * count; // Not count - 1, since we want a nice gray border at the very bottom of the document as well
2183 			iHeight += fl_PAGEVIEW_MARGIN_Y;
2184 		}
2185 	}
2186 	if(iHeight < 0)
2187 	{
2188 		iHeight = 0;
2189 	}
2190 	xxx_UT_DEBUGMSG(("FL_DocLayout::getHeight() - returned height %d \n",iHeight));
2191 	return iHeight;
2192 }
2193 
2194 /**
2195  * Calculates the maximum width a page has in the layout. Includes the
2196  * left page margin when not printing.
2197  */
getWidth() const2198 UT_sint32 FL_DocLayout::getWidth() const
2199 {
2200 	UT_sint32 iWidth = 0;
2201 	int count = m_vecPages.getItemCount();
2202 
2203 	for (int i=0; i<count; i++)
2204 	{
2205 		fp_Page* p = m_vecPages.getNthItem(i);
2206 
2207 		// we layout pages vertically, so this is max, not sum
2208 		if (iWidth < p->getWidth())
2209 			iWidth = p->getWidth();
2210 	}
2211 
2212 	if (m_pG->queryProperties(GR_Graphics::DGP_SCREEN))
2213 	{
2214 		// add page view dimensions
2215 		if(getView())
2216 			iWidth += getView()->getPageViewLeftMargin() * 2;
2217 		else
2218 			iWidth += fl_PAGEVIEW_MARGIN_X * 2;
2219 	}
2220 	return iWidth;
2221 }
2222 
findFont(const PP_AttrProp * pSpanAP,const PP_AttrProp * pBlockAP,const PP_AttrProp * pSectionAP,GR_Graphics * pG,bool isField) const2223 const GR_Font* FL_DocLayout::findFont(const PP_AttrProp * pSpanAP,
2224 									  const PP_AttrProp * pBlockAP,
2225 				      const PP_AttrProp * pSectionAP,
2226 				      GR_Graphics * pG,
2227 				      bool isField) const
2228 {
2229 	const char* pszFamily	= PP_evalProperty("font-family",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2230 	const char* pszField	= PP_evalProperty("field-font",NULL,pBlockAP,NULL, m_pDoc, true);
2231 	const char* pszStyle	= PP_evalProperty("font-style",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2232 	const char* pszVariant	= PP_evalProperty("font-variant",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2233 	const char* pszWeight	= PP_evalProperty("font-weight",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2234 	const char* pszStretch	= PP_evalProperty("font-stretch",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2235 	const char* pszSize		= PP_evalProperty("font-size",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2236 	const char* pszPosition = PP_evalProperty("text-position",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2237 	const char* pszLang     = PP_evalProperty("lang",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2238 
2239 	xxx_UT_DEBUGMSG(("findFont::field-font is %s isField %d \n",pszField,isField));
2240 	if ((pszField != NULL) && isField && (strcmp(pszField, "NULL") != 0))
2241 		pszFamily = pszField;
2242 
2243 	xxx_UT_DEBUGMSG(("findFont::pszFamily is %s \n",pszFamily));
2244 	// for superscripts and subscripts, we'll automatically shrink the font size
2245 	if ((0 == strcmp(pszPosition, "superscript")) ||
2246 		(0 == strcmp(pszPosition, "subscript")))
2247 	{
2248 		double newSize = UT_convertToPoints(pszSize) * 2.0 / 3.0;
2249 		pszSize = UT_formatDimensionedValue(newSize,"pt",".0");
2250 	}
2251 	if(pG==NULL)
2252 	{
2253 	    return m_pG->findFont(pszFamily, pszStyle,
2254 						  pszVariant, pszWeight,
2255 						  pszStretch, pszSize,
2256 						  pszLang);
2257 	}
2258 	else
2259 	{
2260 
2261 	    return pG->findFont(pszFamily, pszStyle,
2262 			      pszVariant, pszWeight,
2263 			      pszStretch, pszSize,
2264 			      pszLang);
2265 	}
2266 }
2267 
2268 /*!
2269  * Set the Document View page Size to properties provided. Rebuild
2270  * the document afterwards.
2271  */
setDocViewPageSize(const PP_AttrProp * pAP)2272 bool FL_DocLayout::setDocViewPageSize(const PP_AttrProp * pAP)
2273 {
2274        const gchar ** pProps = pAP->getProperties();
2275        FV_View * pView = getView();
2276        XAP_Frame * pFrame = NULL;
2277        UT_sint32 iZoom = 100;
2278        if(pView)
2279 	    pFrame = static_cast<XAP_Frame *>(pView->getParentData());
2280        if(pFrame)
2281        {
2282 	    iZoom = pFrame->getZoomPercentage();
2283 	    XAP_Frame::tZoomType zt = pFrame->getZoomType();
2284 	    if((zt == XAP_Frame::z_PAGEWIDTH) || (zt == XAP_Frame::z_WHOLEPAGE))
2285 	    {
2286 	         if(pView->isHdrFtrEdit())
2287 		 {
2288 		       pView->clearHdrFtrEdit();
2289 		       pView->warpInsPtToXY(0,0,false);
2290 		 }
2291 		 if(zt == XAP_Frame::z_PAGEWIDTH)
2292 		 {
2293 		       iZoom = pView->calculateZoomPercentForPageWidth();
2294 		 }
2295 		 if(zt == XAP_Frame::z_WHOLEPAGE)
2296 		 {
2297 		       iZoom = pView->calculateZoomPercentForWholePage();
2298 		 }
2299 	    }
2300        }
2301        bool b = m_docViewPageSize.Set(pProps);
2302        if(pView && (pView->getViewMode() != VIEW_WEB))
2303        {
2304 	    rebuildFromHere(m_pFirstSection);
2305        }
2306        if(pFrame)
2307 	    pFrame->quickZoom(iZoom);
2308        return b;
2309 }
2310 
findFont(const PP_AttrProp * pSpanAP,const PP_AttrProp * pBlockAP,const PP_AttrProp * pSectionAP,bool isField) const2311 const GR_Font* FL_DocLayout::findFont(const PP_AttrProp * pSpanAP,
2312 									  const PP_AttrProp * pBlockAP,
2313 				      const PP_AttrProp * pSectionAP,
2314 				      bool isField) const
2315 {
2316 	const char* pszFamily	= PP_evalProperty("font-family",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2317 	const char* pszField	= PP_evalProperty("field-font",NULL,pBlockAP,NULL, m_pDoc, true);
2318 	const char* pszStyle	= PP_evalProperty("font-style",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2319 	const char* pszVariant	= PP_evalProperty("font-variant",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2320 	const char* pszWeight	= PP_evalProperty("font-weight",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2321 	const char* pszStretch	= PP_evalProperty("font-stretch",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2322 	const char* pszSize		= PP_evalProperty("font-size",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2323 	const char* pszPosition = PP_evalProperty("text-position",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2324 	const char* pszLang     = PP_evalProperty("lang",pSpanAP,pBlockAP,pSectionAP, m_pDoc, true);
2325 
2326 	if (pszField != NULL && isField && strcmp(pszField, "NULL"))
2327 		pszFamily = pszField;
2328 
2329 	// for superscripts and subscripts, we'll automatically shrink the font size
2330 	if ((0 == strcmp(pszPosition, "superscript")) ||
2331 		(0 == strcmp(pszPosition, "subscript")))
2332 	{
2333 		double newSize = UT_convertToPoints(pszSize) * 2.0 / 3.0;
2334 		pszSize = UT_formatDimensionedValue(newSize,"pt",".0");
2335 	}
2336 	return m_pG->findFont(pszFamily, pszStyle,
2337 			      pszVariant, pszWeight,
2338 			      pszStretch, pszSize,
2339 			      pszLang);
2340 }
2341 
changeDocSections(const PX_ChangeRecord_StruxChange * pcrx,fl_DocSectionLayout * pDSL)2342 void FL_DocLayout::changeDocSections(const PX_ChangeRecord_StruxChange * pcrx, fl_DocSectionLayout * pDSL)
2343 {
2344 	fl_DocSectionLayout * pCur = pDSL;
2345 	pDSL->doclistener_changeStrux(pcrx);
2346 	while(pCur != NULL)
2347 	{
2348 		if(m_pDoc->isMarginChangeOnly())
2349 		{
2350 			pCur->doMarginChangeOnly();
2351 		}
2352 		else
2353 		{
2354 			pCur->collapse();
2355 		}
2356 		pCur = pCur->getNextDocSection();
2357 	}
2358 	if(m_pDoc->isMarginChangeOnly())
2359 	{
2360 		return;
2361 	}
2362 	pCur = pDSL;
2363 	while(pCur != NULL)
2364 	{
2365 		pCur->updateDocSection();
2366 		pCur = pCur->getNextDocSection();
2367 	}
2368 }
2369 
2370 
countPages() const2371 UT_sint32 FL_DocLayout::countPages() const
2372 {
2373 	return m_vecPages.getItemCount();
2374 }
2375 
findPage(fp_Page * pPage) const2376 UT_sint32 FL_DocLayout::findPage(fp_Page * pPage) const
2377 {
2378 	UT_sint32 count = m_vecPages.getItemCount();
2379 	if(count < 1)
2380 	{
2381 		return -1;
2382 	}
2383 	return m_vecPages.findItem(pPage);
2384 }
2385 
getNthPage(int n) const2386 fp_Page* FL_DocLayout::getNthPage(int n) const
2387 {
2388 	UT_ASSERT(m_vecPages.getItemCount() > 0);
2389 	if(n >= m_vecPages.getItemCount())
2390 	  return NULL;
2391 	return m_vecPages.getNthItem(n);
2392 }
2393 
getFirstPage() const2394 fp_Page* FL_DocLayout::getFirstPage() const
2395 {
2396 	if (m_vecPages.getItemCount() == 0)
2397 	{
2398 		return NULL;
2399 	}
2400 
2401 	return m_vecPages.getNthItem(0);
2402 }
2403 
getLastPage() const2404 fp_Page* FL_DocLayout::getLastPage() const
2405 {
2406 	if (m_vecPages.getItemCount() == 0)
2407 	{
2408 		return NULL;
2409 	}
2410 
2411 	return m_vecPages.getNthItem(m_vecPages.getItemCount()-1);
2412 }
2413 
deletePage(fp_Page * pPage,bool bDontNotify)2414 void FL_DocLayout::deletePage(fp_Page* pPage, bool bDontNotify /* default false */)
2415 {
2416 	UT_sint32 ndx = m_vecPages.findItem(pPage);
2417 	UT_ASSERT(ndx >= 0);
2418 
2419 	if (pPage->getPrev())
2420 	{
2421 		pPage->getPrev()->setNext(pPage->getNext());
2422 	}
2423 
2424 	if (pPage->getNext())
2425 	{
2426 		pPage->getNext()->setPrev(pPage->getPrev());
2427 	}
2428 	pPage->setPrev(NULL);
2429 	pPage->setNext(NULL);
2430 	m_vecPages.deleteNthItem(ndx);
2431 	delete pPage;
2432 	if(ndx < countPages())
2433 	{
2434 	    setFramePageNumbers(ndx);
2435 	}
2436 	// let the view know that we deleted a page,
2437 	// so that it can update the scroll bar ranges
2438 	// and whatever else it needs to do.
2439     //
2440     // Check for point > 0 to allow multi-threaded loads
2441     //
2442 	if (m_pView && !bDontNotify && (m_pView->getPoint() > 0) && !m_pDoc->isPieceTableChanging())
2443 	{
2444 		m_pView->notifyListeners(AV_CHG_PAGECOUNT);
2445 	}
2446 }
2447 
addNewPage(fl_DocSectionLayout * pOwner,bool bNoUpdate)2448 fp_Page* FL_DocLayout::addNewPage(fl_DocSectionLayout* pOwner, bool bNoUpdate)
2449 {
2450 	fp_Page*		pLastPage;
2451 
2452 	if (countPages() > 0)
2453 	{
2454 		pLastPage = getLastPage();
2455 	}
2456 	else
2457 	{
2458 		pLastPage = NULL;
2459 	}
2460 
2461 	fp_Page* pPage = new fp_Page(	this,
2462 									m_pView,
2463 									m_docViewPageSize,
2464 									pOwner);
2465 	if (pLastPage)
2466 	{
2467 		UT_ASSERT(pLastPage->getNext() == NULL);
2468 
2469 		pLastPage->setNext(pPage);
2470 	}
2471 	pPage->setPrev(pLastPage);
2472 	m_vecPages.addItem(pPage);
2473 	pOwner->addOwnedPage(pPage);
2474 
2475 	// let the view know that we created a new page,
2476 	// so that it can update the scroll bar ranges
2477 	// and whatever else it needs to do.
2478 
2479 	if (m_pView && m_pView->shouldScreenUpdateOnGeneralUpdate() && m_pView->getPoint() > 0 && !bNoUpdate) // skip this if rebuilding or if we're loading a document
2480 	{
2481 		m_pView->notifyListeners(AV_CHG_PAGECOUNT);
2482 	}
2483 
2484 	return pPage;
2485 }
2486 
2487 /*!
2488   Find block at document position
2489   \param pos Document position
2490   \return Block at specified position.
2491   If bLookOnlyBefore = true, it returns NULL if no block can be found
2492   If bLookOnlyBefore = false, it returns the first block to the right of
2493   that position (it may still return NULL).
2494 */
findBlockAtPosition(PT_DocPosition pos,bool bLookOnlyBefore) const2495 fl_BlockLayout* FL_DocLayout::findBlockAtPosition(PT_DocPosition pos, bool bLookOnlyBefore) const
2496 {
2497 	fl_BlockLayout* pBL = NULL;
2498 	fl_ContainerLayout* sfh = 0;
2499 
2500 	PT_DocPosition posEOD;
2501 	bool bRes;
2502 	xxx_UT_DEBUGMSG(("Pos at entry %d \n",pos));
2503 	bRes = m_pDoc->getBounds(true, posEOD);
2504 	UT_ASSERT(bRes);
2505 	if(m_pDoc->isEndFootnoteAtPos(pos))
2506 	{
2507 		xxx_UT_DEBUGMSG(("End footnote found at %d \n",pos));
2508 		pos--;
2509 	}
2510 	if(m_pDoc->isFootnoteAtPos(pos))
2511 	{
2512 		xxx_UT_DEBUGMSG(("Start footnote found at %d \n",pos));
2513 		pos+=2;
2514 	}
2515 	if(m_pDoc->isFootnoteAtPos(pos-1))
2516 	{
2517 		xxx_UT_DEBUGMSG(("Start footnote found at %d \n",pos));
2518 		pos+=1;
2519 	}
2520 
2521 	bRes = m_pDoc->getStruxOfTypeFromPosition(m_lid, pos, PTX_Block, &sfh);
2522 	// If block wasn't found at position, try finding it to the right,
2523 	// limited only by the EOD.
2524 	while(!bRes && !bLookOnlyBefore && (pos < posEOD) )
2525 	{
2526 		pos++;
2527 		bRes = m_pDoc->getStruxOfTypeFromPosition(m_lid, pos, PTX_Block, &sfh);
2528 	}
2529 
2530 	if (bRes)
2531 	{
2532 		fl_Layout * pL = static_cast<fl_Layout *>(sfh);
2533 		if(!pL)
2534 			return NULL;
2535 
2536 		switch (pL->getType())
2537 		{
2538 		case PTX_Block:
2539 			pBL = static_cast<fl_BlockLayout *>(pL);
2540 			while(pBL && !pBL->canContainPoint())
2541 			{
2542 				pBL = pBL->getPrevBlockInDocument();
2543 			}
2544 
2545 			break;
2546 
2547 		case PTX_Section:
2548 		default:
2549 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2550 			// We asked for a block, and we got a section.  Bad
2551 			return NULL;
2552 		}
2553 	}
2554 	else
2555 	{
2556 		return NULL;
2557 	}
2558 
2559 	if(pBL== NULL)
2560 	{
2561 	  //
2562 	  // Give up!
2563 	  //
2564 	     return NULL;
2565 	}
2566 
2567 	fl_ContainerLayout * pMyC = pBL->myContainingLayout();
2568 	while(pMyC && (pMyC->getContainerType() != FL_CONTAINER_DOCSECTION)
2569 	      && (pMyC->getContainerType() != FL_CONTAINER_HDRFTR)
2570 	      && (pMyC->getContainerType() != FL_CONTAINER_SHADOW))
2571 	{
2572 	  pMyC = pMyC->myContainingLayout();
2573 	}
2574 	if((pMyC->getContainerType() == FL_CONTAINER_HDRFTR)
2575 	      || (pMyC->getContainerType() == FL_CONTAINER_SHADOW))
2576 	{
2577 		fl_HdrFtrShadow * pShadow = NULL;
2578 		FV_View * pView = getView();
2579 		if(pView && pView->isHdrFtrEdit())
2580 		{
2581 			pShadow = pView->getEditShadow();
2582 //
2583 // We might actually be in the other HdrFtr is the point got here from an undo!
2584 // Check for this.
2585 //
2586 			if(!pShadow->getHdrFtrSectionLayout()->isPointInHere(pos))
2587 			{
2588 			        fl_ContainerLayout * pCL = static_cast<fl_ContainerLayout *>(pBL->getSectionLayout());
2589 				while(pCL && pCL->getContainerType() != FL_CONTAINER_HDRFTR && pCL->getContainerType() != FL_CONTAINER_DOCSECTION)
2590 				{
2591 				  if(pCL == pCL->myContainingLayout())
2592 				  {
2593 				    break;
2594 				  }
2595 				  pCL = pCL->myContainingLayout();
2596 				}
2597 				fl_HdrFtrSectionLayout * pHF = NULL;
2598 				if(pCL && pCL->getContainerType() == FL_CONTAINER_HDRFTR)
2599 				{
2600 				     pHF = static_cast<fl_HdrFtrSectionLayout *>(pCL);
2601 				}
2602 				if(pHF && pHF->isPointInHere(pos))
2603 				{
2604 					pShadow = pHF->getFirstShadow();
2605 					if(pShadow)
2606 					{
2607 						pView->clearHdrFtrEdit();
2608 						pView->setHdrFtrEdit(pShadow);
2609 						pBL = static_cast<fl_BlockLayout *>(pShadow->findBlockAtPosition(pos));
2610 						return pBL;
2611 					}
2612 					else
2613 					{
2614 						return NULL;
2615 					}
2616 				}
2617 				// Ok, we're really confused now, point is nowhere to be found.
2618 				// It might be OK if pos-1 is in here, though...
2619 				if (pShadow && !pShadow->getHdrFtrSectionLayout()->isPointInHere(pos-1))
2620 				{
2621 					//			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2622 				}
2623 			}
2624 
2625 		}
2626 		else if(pMyC->getContainerType() == FL_CONTAINER_SHADOW)
2627 		{
2628 		        pShadow = static_cast<fl_HdrFtrShadow *>(pMyC);
2629 		}
2630 		else
2631 		{
2632 			pShadow = static_cast<fl_HdrFtrSectionLayout *>(pMyC)->getFirstShadow();
2633 		}
2634 		fl_BlockLayout * ppBL = NULL;
2635 		if(pShadow != NULL)
2636 		{
2637 			ppBL = static_cast<fl_BlockLayout *>(pShadow->findMatchingContainer(pBL));
2638 		}
2639 		else
2640 		{
2641 			UT_DEBUGMSG(("No Shadow! But there should be ! \n"));
2642 			//	UT_ASSERT(0);
2643 		}
2644 //
2645 // FIXME: Header/Footers
2646 // some failsafe code should not trigger. Header/footer still not perfect.
2647 //
2648 		if(!ppBL)
2649 		{
2650 			if(!isLayoutFilling())
2651 			{
2652 				//			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
2653 			}
2654 		}
2655 		else
2656 		{
2657 			pBL = ppBL;
2658 		}
2659 	}
2660 	UT_ASSERT(pBL);
2661 	return pBL;
2662 }
2663 
findBlockAtPositionReverse(PT_DocPosition pos) const2664 fl_BlockLayout* FL_DocLayout::findBlockAtPositionReverse(PT_DocPosition pos) const
2665 {
2666 	fl_BlockLayout* pBL = NULL;
2667 	fl_ContainerLayout* sfh = 0;
2668 
2669 	PT_DocPosition posBOD;
2670 	bool bRes;
2671 
2672 	bRes = m_pDoc->getBounds(false, posBOD);
2673 	UT_ASSERT(bRes);
2674 	if(m_pDoc->isEndFootnoteAtPos(pos))
2675 	{
2676 		xxx_UT_DEBUGMSG(("End footnote found at %d \n",pos));
2677 		pos--;
2678 	}
2679 	if(m_pDoc->isFootnoteAtPos(pos))
2680 	{
2681 		xxx_UT_DEBUGMSG(("Start footnote found at %d \n",pos));
2682 		pos+=2;
2683 	}
2684 	bRes = m_pDoc->getStruxOfTypeFromPosition(m_lid, pos, PTX_Block, &sfh);
2685 	// If block wasn't found at position, try finding it to the right,
2686 	// limited only by the EOD.
2687 	while(!bRes && (pos > posBOD))
2688 	{
2689 		pos--;
2690 		bRes = m_pDoc->getStruxOfTypeFromPosition(m_lid, pos, PTX_Block, &sfh);
2691 	}
2692 
2693 	if (bRes)
2694 	{
2695 		fl_Layout * pL = (fl_Layout *)sfh;
2696 		if(!pL)
2697 			return NULL;
2698 
2699 		switch (pL->getType())
2700 		{
2701 		case PTX_Block:
2702 			pBL = static_cast<fl_BlockLayout *>(pL);
2703 			break;
2704 
2705 		case PTX_Section:
2706 		default:
2707 			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2708 			// We asked for a block, and we got a section.  Bad
2709 			return NULL;
2710 		}
2711 	}
2712 	else
2713 	{
2714 		UT_ASSERT_HARMLESS(0);
2715 		return NULL;
2716 	}
2717 
2718 	if(pBL->getSectionLayout()->getType() == FL_SECTION_HDRFTR)
2719 	{
2720 		fl_HdrFtrShadow * pShadow = NULL;
2721 		FV_View * pView = getView();
2722 		if(pView && pView->isHdrFtrEdit())
2723 		{
2724 			pShadow = pView->getEditShadow();
2725 //
2726 // We might actually be in the other HdrFtr is the point got here from an undo!
2727 // Check for this.
2728 //
2729 			if(!pShadow->getHdrFtrSectionLayout()->isPointInHere(pos))
2730 			{
2731 				fl_HdrFtrSectionLayout * pHF = (fl_HdrFtrSectionLayout *) pBL->getSectionLayout();
2732 				if(pHF->isPointInHere(pos))
2733 				{
2734 					pShadow = pHF->getFirstShadow();
2735 					pView->clearHdrFtrEdit();
2736 					pView->setHdrFtrEdit(pShadow);
2737 					pBL = (fl_BlockLayout *) pShadow->findBlockAtPosition(pos);
2738 					return pBL;
2739 				}
2740 				// Ok, we're really confused now, point is nowhere to be found.
2741 				// It might be OK if pos-1 is in here, though...
2742 				if (!pShadow->getHdrFtrSectionLayout()->isPointInHere(pos-1))
2743 				{
2744 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2745 				}
2746 			}
2747 
2748 		}
2749 		else
2750 		{
2751 			pShadow = ((fl_HdrFtrSectionLayout *) pBL->getSectionLayout())->getFirstShadow();
2752 		}
2753 		fl_BlockLayout * ppBL = NULL;
2754 		if(pShadow != NULL)
2755 			ppBL = (fl_BlockLayout *) pShadow->findMatchingContainer(pBL);
2756 		else
2757 		{
2758 			if(!isLayoutFilling())
2759 			{
2760 				UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
2761 			}
2762 		}
2763 
2764 		if(ppBL) {
2765 			pBL = ppBL;
2766 			}
2767 	}
2768 	UT_ASSERT(pBL);
2769 	return pBL;
2770 }
2771 
deleteEmptyColumnsAndPages(void)2772 void FL_DocLayout::deleteEmptyColumnsAndPages(void)
2773 {
2774 	fl_DocSectionLayout* pSL = m_pFirstSection;
2775 	while (pSL)
2776 	{
2777 		pSL->deleteEmptyColumns();
2778 		pSL = pSL->getNextDocSection();
2779 	}
2780 
2781 	deleteEmptyPages();
2782 }
2783 
deleteEmptyPages(bool bDontNotify)2784 void FL_DocLayout::deleteEmptyPages( bool bDontNotify /* default false */)
2785 {
2786 	int i;
2787 
2788 	int iCountPages = m_vecPages.getItemCount();
2789 	for (i=iCountPages - 1; i>=0; i--)
2790 	{
2791 		fp_Page* p = m_vecPages.getNthItem(i);
2792 		UT_ASSERT_HARMLESS(p);
2793 		if (p && p->isEmpty())
2794 		{
2795 			deletePage(p, bDontNotify);
2796 		}
2797 	}
2798 }
2799 
updateOnViewModeChange()2800 void FL_DocLayout::updateOnViewModeChange()
2801 {
2802 	UT_DEBUGMSG(("updateOnViewModeChange \n"));
2803 	// force margin properties lookup
2804 	fl_SectionLayout* pSL = m_pFirstSection;
2805 	m_docViewPageSize = getDocument()->m_docPageSize;
2806 	UT_DebugOnly<UT_Dimension> orig_ut = DIM_IN;
2807 	orig_ut = m_docViewPageSize.getDims();
2808 	UT_DEBUGMSG(("updateOnViewModeChange - docViewPageSize width %f \n",m_docViewPageSize.Width(orig_ut)));
2809   	while (pSL)
2810   	{
2811 		pSL->lookupMarginProperties();
2812 		pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
2813   	}
2814 
2815 	// rebuild
2816 	formatAll();
2817 }
2818 
2819 
formatAll()2820 void FL_DocLayout::formatAll()
2821 {
2822 	UT_return_if_fail(m_pDoc);
2823 	m_pDoc->enableListUpdates();
2824 	fl_SectionLayout* pSL = m_pFirstSection;
2825 	clearAllCountWraps();
2826 	while (pSL)
2827 	{
2828 		if(pSL->getContainerType() == FL_CONTAINER_DOCSECTION)
2829 		{
2830 			fl_DocSectionLayout * pDSL = static_cast<fl_DocSectionLayout *>(pSL);
2831 			pDSL->recalculateFields(0);
2832 			if (!pDSL->isFirstPageValid())
2833 			{
2834 				pDSL->collapse();
2835 			}
2836 			pDSL->format();
2837 			pDSL->checkAndRemovePages();
2838 		}
2839 		else
2840 		{
2841 			pSL->recalculateFields(0);
2842 			pSL->format();
2843 		}
2844 
2845 		pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
2846 	}
2847 }
2848 
2849 
rebuildFromHere(fl_DocSectionLayout * pFirstDSL)2850 void FL_DocLayout::rebuildFromHere( fl_DocSectionLayout * pFirstDSL)
2851 {
2852   UT_DEBUGMSG(("Rebuilding DocLAyout %p doc %p \n",this,m_pDoc));
2853 	UT_ASSERT(m_pDoc);
2854 	if(isLayoutFilling())
2855 	{
2856 //		UT_ASSERT(0);
2857 		return;
2858 	}
2859 	if(m_pDoc->isMarginChangeOnly())
2860 	{
2861 		return;
2862 	}
2863 //
2864 	fl_DocSectionLayout * pStart = pFirstDSL;
2865 //	fl_DocSectionLayout * pStart = pFirstDSL->getPrevDocSection();
2866 //	if(pStart == NULL)
2867 //	{
2868 //		pStart = pFirstDSL;
2869 //	}
2870 	fl_DocSectionLayout * pDSL = pStart;
2871 	// add page view dimensions
2872 #if 1
2873 	UT_DEBUGMSG(("SEVIOR: Rebuild from section %p \n",pFirstDSL));
2874 	for(UT_sint32 k=0; k< m_vecPages.getItemCount(); k++)
2875 	{
2876 		fp_Page * pPage = m_vecPages.getNthItem(k);
2877 		if(pPage->getOwningSection() == pFirstDSL)
2878 		{
2879 			UT_DEBUGMSG(("SEVIOR: Rebuilding from page %d \n",k));
2880 			break;
2881 		}
2882 	}
2883 #endif
2884 	while (pDSL)
2885 	{
2886 		pDSL->collapse();
2887 		pDSL = pDSL->getNextDocSection();
2888 	}
2889 	deleteEmptyColumnsAndPages();
2890 	clearAllCountWraps();
2891 //
2892 // Clear out rebuild marks from this collapse
2893 //
2894 	pDSL = static_cast<fl_DocSectionLayout *>(m_pFirstSection);
2895 	while(pDSL)
2896 	{
2897 		pDSL->clearRebuild();
2898 		pDSL = pDSL->getNextDocSection();
2899 	}
2900 
2901 	deleteEmptyColumnsAndPages();
2902 	pDSL= pStart;
2903 	while (pDSL)
2904 	{
2905 		UT_DEBUGMSG(("SEVIOR: Building section %p \n",pDSL));
2906 		pDSL->updateDocSection();
2907 		pDSL->clearRebuild();
2908 		pDSL = pDSL->getNextDocSection();
2909 	}
2910 //
2911 // Clear out rebuild marks from the rebuild
2912 //
2913 	pDSL = static_cast<fl_DocSectionLayout *>(m_pFirstSection);
2914 	while(pDSL)
2915 	{
2916 		pDSL->clearRebuild();
2917 		pDSL = pDSL->getNextDocSection();
2918 	}
2919 }
2920 
updateLayout()2921 void FL_DocLayout::updateLayout()
2922 {
2923 	/*
2924 	  TODO the following routine checks every paragraph in the
2925 	  document to see if it needs a reformat.  How is this going
2926 	  to perform on a 50-page document?
2927 
2928 
2929 	  Very good point. We need a isOnScreen() method!!!
2930 	*/
2931 	UT_ASSERT(m_pDoc);
2932 
2933 	fl_SectionLayout* pSL = m_pFirstSection;
2934 	while (pSL)
2935 	{
2936 		if(!isLayoutFilling())
2937 		{
2938 		        pSL->updateLayout(false);
2939 		}
2940 		if(pSL->getType() == FL_SECTION_DOC)
2941 		{
2942 			if(static_cast<fl_DocSectionLayout *>(pSL)->needsRebuild())
2943 			{
2944 				break;
2945 			}
2946 		}
2947 		pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
2948 	}
2949 	if(pSL == NULL)
2950 	{
2951 		deleteEmptyColumnsAndPages();
2952 		return;
2953 	}
2954 	if(m_pDoc->isPieceTableChanging())
2955 	{
2956 		static_cast<fl_DocSectionLayout *>(pSL)->clearRebuild();
2957 		return;
2958 	}
2959 	rebuildFromHere(static_cast<fl_DocSectionLayout *>(pSL));
2960 }
2961 
2962 
updateColor()2963 void FL_DocLayout::updateColor()
2964 {
2965 	UT_ASSERT(m_pDoc);
2966 	FV_View * pView = getView();
2967 	if(pView)
2968 	{
2969 		XAP_App * pApp = pView->getApp();
2970 		XAP_Prefs * pPrefs = pApp->getPrefs();
2971 		const gchar * pszTransparentColor = NULL;
2972 		pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForTransparent),&pszTransparentColor);
2973 //
2974 // Save the new preference color
2975 //
2976 		strncpy(m_szCurrentTransparentColor,pszTransparentColor,9);
2977 	}
2978 //
2979 // Now loop through the document and update the Background color
2980 //
2981 	fl_DocSectionLayout* pDSL = static_cast<fl_DocSectionLayout *>(m_pFirstSection);
2982 	while (pDSL)
2983 	{
2984 		pDSL->setPaperColor();
2985 		pDSL = pDSL->getNextDocSection();
2986 	}
2987 	fp_Page * pPage = NULL;
2988 	UT_sint32 i =0;
2989 	for(i=0; i<m_vecPages.getItemCount(); i++)
2990 	{
2991 		pPage = m_vecPages.getNthItem(i);
2992 		pPage->getFillType().setTransColor(m_szCurrentTransparentColor);
2993 		pPage->getFillType().markTransparentForPrint();
2994 	}
2995 
2996 //
2997 // Redraw the view associated with this document.
2998 //
2999 	if(pView)
3000 	{
3001 		pView->updateScreen(false);
3002 	}
3003 
3004 }
3005 
3006 #define BACKGROUND_CHECK_MSECS 100
3007 
3008 #ifdef ENABLE_SPELL
3009 /*!
3010  Toggle auto spell-checking state
3011  \param bSpell True if spell-checking should be enabled, false otherwise
3012  When disabling spelling, all squiggles are deleted.
3013  When enabling spelling, force a full check of the document.
3014 */
3015 void
_toggleAutoSpell(bool bSpell)3016 FL_DocLayout::_toggleAutoSpell(bool bSpell)
3017 {
3018 	bool bOldAutoSpell = getAutoSpellCheck();
3019 	UT_DEBUGMSG(("_toggleAutoSpell %d \n",bSpell));
3020 	// Add reason to background checker
3021 	if (bSpell)
3022 	{
3023 		UT_DEBUGMSG(("Adding Auto SpellCheck  \n"));
3024 		addBackgroundCheckReason(bgcrSpelling);
3025 	}
3026 	else
3027 	{
3028 		UT_DEBUGMSG(("Removing Auto SpellCheck  \n"));
3029 		removeBackgroundCheckReason(bgcrSpelling);
3030 	}
3031 
3032 	xxx_UT_DEBUGMSG(("FL_DocLayout::_toggleAutoSpell (%s)\n",
3033 					 bSpell ? "true" : "false" ));
3034 
3035 	if (bSpell)
3036 	{
3037 		xxx_UT_DEBUGMSG(("Rechecking spelling in blocks \n"));
3038 		queueAll(bgcrSpelling);
3039 	}
3040 	else
3041 	{
3042 		// Disabling, so remove the squiggles too
3043 		fl_DocSectionLayout * pSL = getFirstSection();
3044 		if(pSL)
3045 		{
3046 			fl_ContainerLayout* b = pSL->getFirstLayout();
3047 			while (b)
3048 			{
3049 				if(b->getContainerType() == FL_CONTAINER_BLOCK)
3050 				{
3051 					static_cast<fl_BlockLayout *>(b)->removeBackgroundCheckReason(bgcrSpelling);
3052 					static_cast<fl_BlockLayout *>(b)->getSpellSquiggles()->deleteAll();
3053 					b = static_cast<fl_BlockLayout *>(b)->getNextBlockInDocument();
3054 				}
3055 				else
3056 				{
3057 					b = b->getNext();
3058 				}
3059 			}
3060 		}
3061 		if (bOldAutoSpell)
3062 		{
3063 			// If we're here, it was set to TRUE before but now it is
3064 			// being set to FALSE. This means that it is the user
3065 			// setting it. That's good.
3066 			m_pView->draw(NULL);
3067 			// A pending word would be bad. Not sure why it's not
3068 			// ignored once autospell is off, but for now it should
3069 			// definitely be annulled.
3070 			setPendingWordForSpell(NULL, NULL);
3071 		}
3072 	}
3073 }
3074 
3075 
3076 /*!
3077  Toggle auto spell-checking state
3078  \param bGrammar True if grammar-checking should be enabled, false otherwise
3079  When disabling grammar checking, all squiggles are deleted.
3080  When enabling grammar, force a full check of the document.
3081 */
3082 void
_toggleAutoGrammar(bool bGrammar)3083 FL_DocLayout::_toggleAutoGrammar(bool bGrammar)
3084 {
3085 	bool bOldAutoGrammar = getAutoGrammarCheck();
3086 	UT_DEBUGMSG(("_toggleAutoGrammar %d \n",bGrammar));
3087 	// Add reason to background checker
3088 	if (bGrammar)
3089 	{
3090 		UT_DEBUGMSG(("Adding Auto GrammarCheck  \n"));
3091 		addBackgroundCheckReason(bgcrGrammar);
3092 		m_bAutoGrammarCheck = true;
3093 	}
3094 	else
3095 	{
3096 		UT_DEBUGMSG(("Removing Auto Grammar  \n"));
3097 		removeBackgroundCheckReason(bgcrGrammar);
3098 		m_bAutoGrammarCheck = false;
3099 	}
3100 
3101 	xxx_UT_DEBUGMSG(("FL_DocLayout::_toggleAutoGrammar (%s)\n",
3102 					 bGrammar ? "true" : "false" ));
3103 
3104 	if (bGrammar)
3105 	{
3106 		xxx_UT_DEBUGMSG(("Rechecking Grammar in blocks \n"));
3107 		queueAll(bgcrGrammar);
3108 	}
3109 	else
3110 	{
3111 		// Disabling, so remove the squiggles too
3112 		fl_DocSectionLayout * pSL = getFirstSection();
3113 		if(pSL)
3114 		{
3115 			fl_ContainerLayout* b = pSL->getFirstLayout();
3116 			while (b)
3117 			{
3118 				if(b->getContainerType() == FL_CONTAINER_BLOCK)
3119 				{
3120 					static_cast<fl_BlockLayout *>(b)->removeBackgroundCheckReason(bgcrGrammar);
3121 					static_cast<fl_BlockLayout *>(b)->getGrammarSquiggles()->deleteAll();
3122 					b = static_cast<fl_BlockLayout *>(b)->getNextBlockInDocument();
3123 				}
3124 				else
3125 				{
3126 					b = b->getNext();
3127 				}
3128 			}
3129 		}
3130 		if (bOldAutoGrammar)
3131 		{
3132 			// If we're here, it was set to TRUE before but now it is
3133 			// being set to FALSE. This means that it is the user
3134 			// setting it. That's good.
3135 			m_pView->draw(NULL);
3136 		}
3137 	}
3138 }
3139 #endif
3140 
_toggleAutoSmartQuotes(bool bSQ)3141 void FL_DocLayout::_toggleAutoSmartQuotes(bool bSQ)
3142 {
3143 	setPendingSmartQuote(NULL, 0);  // avoid surprises
3144 	if (bSQ)
3145 	{
3146 		addBackgroundCheckReason(bgcrSmartQuotes);
3147 	}
3148 	else
3149 	{
3150 		removeBackgroundCheckReason(bgcrSmartQuotes);
3151 	}
3152 
3153 	UT_DEBUGMSG(("FL_DocLayout::_toggleAutoSmartQuotes(%s)\n", bSQ ? "true" : "false" ));
3154 }
3155 
3156 
setDisplayAnnotations(bool bDisplayAnnotations)3157 void FL_DocLayout::setDisplayAnnotations(bool bDisplayAnnotations)
3158 {
3159   m_bDisplayAnnotations = bDisplayAnnotations;
3160 }
3161 
displayAnnotations(void) const3162 bool FL_DocLayout::displayAnnotations(void) const
3163 {
3164   return m_bDisplayAnnotations;
3165 }
3166 
displayRDFAnchors(void) const3167 bool FL_DocLayout::displayRDFAnchors(void) const
3168 {
3169     return m_bDisplayRDFAnchors;
3170 }
3171 
setDisplayRDFAnchors(bool v)3172 void FL_DocLayout::setDisplayRDFAnchors(bool v)
3173 {
3174     m_bDisplayRDFAnchors = v;
3175 }
3176 
3177 
3178 
3179 #ifdef ENABLE_SPELL
3180 /*!
3181  Do background spell-check
3182  \param pWorker Worker object
3183  \note This is a static callback method and does not have a 'this' pointer.
3184 */
3185 void
_backgroundCheck(UT_Worker * pWorker)3186 FL_DocLayout::_backgroundCheck(UT_Worker * pWorker)
3187 {
3188 	UT_return_if_fail(pWorker);
3189 
3190 	// Get the doclayout
3191 	FL_DocLayout * pDocLayout = static_cast<FL_DocLayout *>(pWorker->getInstanceData());
3192 	UT_return_if_fail(pDocLayout);
3193 
3194 	// Win32 timers can fire prematurely on asserts (the dialog's
3195 	// message pump releases the timers)
3196 	if (!pDocLayout->m_pView)
3197 	{
3198 		return;
3199 	}
3200 
3201 //
3202 // Don't redraw on selections.
3203 //
3204 //	if (!pDocLayout->m_pView->isSelectionEmpty())
3205 //	{
3206 //		return;
3207 //	}
3208 	xxx_UT_DEBUGMSG(("BAckground check called. \n"));
3209 	// Don't spell check while printing!
3210 	if(pDocLayout->m_pG->queryProperties(GR_Graphics::DGP_PAPER))
3211 	{
3212 		return;
3213 	}
3214 
3215 	// Don't spell check if disabled, or already happening
3216 	if(pDocLayout->m_bStopSpellChecking || pDocLayout->m_bImSpellCheckingNow || pDocLayout->isLayoutFilling())
3217 	{
3218 	  xxx_UT_DEBUGMSG(("Already spellchecking!!!!!!!! \n"));
3219 		return;
3220 	}
3221 
3222 	// Code added to hold spell checks during block insertions
3223 	if(pDocLayout->m_pDoc->isPieceTableChanging())
3224 	{
3225 		return;
3226 	}
3227 
3228 	// Don't spell check while a redrawupdate is happening either...
3229 	PD_Document * pDoc = pDocLayout->getDocument();
3230 	if(pDoc->isRedrawHappenning())
3231 	{
3232 		return;
3233 	}
3234 
3235 	// Flag that spell checking is in action.
3236 	// Note: this is not a good way to do mutual exclusion!
3237 	pDocLayout->m_bImSpellCheckingNow = true;
3238 
3239 	fl_BlockLayout *pB = pDocLayout->spellQueueHead();
3240 	xxx_UT_DEBUGMSG(("Spellchecking block %x \n",pB));
3241 	if (pB != NULL)
3242 	{
3243 		// This looping seems like a lot of wasted effort when we
3244 		// don't define meaning for most of the bits, but it's
3245 		// small effort compared to all that squiggle stuff that
3246 		// goes on for the spelling stuff.
3247 		if(pB->getContainerType() == FL_CONTAINER_BLOCK)
3248 		{
3249 			for (UT_uint32 bitdex = 0;
3250 				 bitdex < 8*sizeof(pB->m_uBackgroundCheckReasons);
3251 				 bitdex++)
3252 			{
3253 				UT_uint32 mask;
3254 				mask = (1 << bitdex);
3255 				if (pB->hasBackgroundCheckReason(mask))
3256 				{
3257 					if(!pDocLayout->m_bFinishedInitialCheck
3258 					   && pDocLayout->m_iPrevPos > pB->getPosition())
3259 					{
3260 						pDocLayout->m_bFinishedInitialCheck = true;
3261 					}
3262 					pDocLayout->m_iPrevPos = pB->getPosition();
3263 
3264 					// Note that we remove this reason from queue
3265 					// before checking it (otherwise asserts could
3266 					// trigger redundant recursive calls)
3267 					switch (mask)
3268 					{
3269 					case bgcrNone:
3270 						pB->removeBackgroundCheckReason(mask);
3271 						break;
3272 					case bgcrDebugFlash:
3273 						pB->debugFlashing();
3274 						pB->removeBackgroundCheckReason(mask);
3275 						break;
3276 					case bgcrSpelling:
3277 					{
3278 						xxx_UT_DEBUGMSG(("Spelling checking block %x directly \n",pB));
3279 						bool b = pB->checkSpelling();
3280 						if(b)
3281 						{
3282 							pB->removeBackgroundCheckReason(mask);
3283 						}
3284 						break;
3285 					}
3286 					case bgcrGrammar:
3287 					{
3288 						if(!pDocLayout->m_bFinishedInitialCheck)
3289 						{
3290 							if(pDocLayout->m_iGrammarCount < 4)
3291 							{
3292 								pDocLayout->m_iGrammarCount++;
3293 								pDocLayout->m_bImSpellCheckingNow = false;
3294 								return;
3295 							}
3296 							pDocLayout->m_iGrammarCount = 0;
3297 						}
3298 
3299 						xxx_UT_DEBUGMSG(("Grammar checking block %x directly \n",pB));
3300 						XAP_App * pApp = pDocLayout->getView()->getApp();
3301 						//
3302 						// If a grammar checker plugin is loaded it will check the block now.
3303 						//
3304 						pApp->notifyListeners(pDocLayout->getView(),
3305 											  AV_CHG_BLOCKCHECK,reinterpret_cast<void *>(pB));
3306 						pB->removeBackgroundCheckReason(mask);
3307 						pB->drawGrammarSquiggles();
3308 						break;
3309 					}
3310 					case bgcrSmartQuotes:
3311 					default:
3312 						pB->removeBackgroundCheckReason(mask);
3313 						break;
3314 					}
3315 				}
3316 			}
3317 		}
3318 		// Delete block from queue if there are no more reasons
3319 		// for checking it.
3320 		if((pB->getContainerType() != FL_CONTAINER_BLOCK)
3321 		   || (!pB->m_uBackgroundCheckReasons))
3322 		{
3323 			pB->dequeueFromSpellCheck();
3324 		}
3325 	}
3326 	else
3327 	{
3328 		// No blocks to spellcheck so stop the idle/timer. Otherwise
3329 		// we consume 100% CPU.
3330 		pDocLayout->m_pBackgroundCheckTimer->stop();
3331 	}
3332 
3333 	pDocLayout->m_bImSpellCheckingNow = false;
3334 }
3335 
3336 /*!
3337  Enqueue block for background spell-checking
3338  \param iReason Reason for checking the block FIXME - enum?
3339  \param pBlock Block to enqueue
3340  \param bHead When true, insert block at head of queue
3341 
3342  This routine queues up blocks for timer-driven spell checking, etc.
3343  By default, this is a FIFO queue, but it can be explicitly
3344  reprioritized by setting bHead to true.
3345 */
3346 void
queueBlockForBackgroundCheck(UT_uint32 iReason,fl_BlockLayout * pBlock,bool bHead)3347 FL_DocLayout::queueBlockForBackgroundCheck(UT_uint32 iReason,
3348 										   fl_BlockLayout *pBlock,
3349 										   bool bHead)
3350 {
3351 	// If there's no timer running, start one
3352 	if (!m_pBackgroundCheckTimer)
3353 	{
3354 	    int inMode = UT_WorkerFactory::IDLE | UT_WorkerFactory::TIMER;
3355 	    if(getView() && getView()->isGrammarLoaded() && m_bAutoGrammarCheck)
3356 	    {
3357 	         inMode = UT_WorkerFactory::TIMER;
3358 	    }
3359 	    UT_WorkerFactory::ConstructMode outMode = UT_WorkerFactory::NONE;
3360 
3361 	    m_pBackgroundCheckTimer = UT_WorkerFactory::static_constructor (_backgroundCheck, this, inMode, outMode);
3362 
3363 	    UT_ASSERT(m_pBackgroundCheckTimer);
3364 	    UT_ASSERT(outMode != UT_WorkerFactory::NONE);
3365 
3366 		// If the worker is working on a timer instead of in the idle
3367 		// time, set the frequency of the checks.
3368 	    if ( UT_WorkerFactory::TIMER == outMode )
3369 		{
3370 			// this is really a timer, so it's safe to static_cast it
3371 			static_cast<UT_Timer*>(m_pBackgroundCheckTimer)->set(BACKGROUND_CHECK_MSECS);
3372 		}
3373 #if 1
3374 	    m_bStopSpellChecking = false;
3375 	    m_pBackgroundCheckTimer->start();
3376 #endif
3377 
3378 	}
3379 #if 1 // We need this to restart the idle handler.
3380 	else
3381 	{
3382 		//		m_pBackgroundCheckTimer->stop();
3383 		m_bStopSpellChecking = false;
3384 		m_pBackgroundCheckTimer->start();
3385 	}
3386 #endif
3387 
3388 	// Set debug flash reason on block if it is set
3389 	if (hasBackgroundCheckReason(bgcrDebugFlash))
3390 	{
3391 		pBlock->addBackgroundCheckReason(bgcrDebugFlash);
3392 	}
3393 	pBlock->addBackgroundCheckReason(iReason);
3394 
3395 	if (!pBlock->isQueued())
3396 	{
3397 		// Add block if it's not already in the queue. Add it either
3398 		// at the head, or at the tail.
3399 		if (bHead)
3400 			pBlock->enqueueToSpellCheckAfter(NULL);
3401 		else
3402 			pBlock->enqueueToSpellCheckAfter(m_toSpellCheckTail);
3403 	}
3404 	else if (bHead)
3405 	{
3406 		// Block is already in the queue, bubble it to the start
3407 		pBlock->dequeueFromSpellCheck();
3408 		pBlock->enqueueToSpellCheckAfter(NULL);
3409 	}
3410 }
3411 
dequeueAll(void)3412 void FL_DocLayout::dequeueAll(void)
3413 {
3414 	fl_BlockLayout *pB = spellQueueHead();
3415 	while (pB != NULL)
3416 	{
3417 		fl_BlockLayout *pNext = pB->nextToSpell();
3418 		pB->clearQueueing();
3419 		pB = pNext;
3420 	}
3421 	setSpellQueueHead(NULL);
3422 	setSpellQueueTail(NULL);
3423 	UT_DEBUGMSG(("Dequeue all \n"));
3424 
3425 	m_PendingBlockForGrammar = NULL;
3426 	m_bStopSpellChecking = true;
3427 	if(m_pBackgroundCheckTimer)
3428 	{
3429 		m_pBackgroundCheckTimer->stop();
3430 		// Wait for checking to complete before returning.
3431 		while(m_bImSpellCheckingNow == true)
3432 		{
3433 			// TODO shouldn't we have a little sleep here?
3434 		}
3435 	}
3436 }
3437 
queueAll(UT_uint32 iReason)3438 void FL_DocLayout::queueAll(UT_uint32 iReason)
3439 {
3440 	fl_DocSectionLayout * pSL = getFirstSection();
3441 	if(pSL)
3442 	{
3443 		// We will place the block that contains the ins point and its immediate neigbours
3444 		// at the top of the queue, this will make the check look faster to the user
3445 		FV_View * pView = getView();
3446 		UT_GenericVector<fl_BlockLayout*> vBL;
3447 		const UT_sint32 iLimit = 5;
3448 
3449 		fl_BlockLayout * pCurBL = pView->getBlockAtPosition(pView->getPoint());
3450 
3451 		if(pCurBL)
3452 		{
3453 			fl_BlockLayout * pBL = pCurBL;
3454 
3455 			UT_sint32 i = 0;
3456 			for(i = 0; i < iLimit/2 + iLimit%2 && pBL; ++i, pBL = pBL->getPrevBlockInDocument())
3457 			{
3458 				vBL.addItem(pBL);
3459 			}
3460 
3461 			pBL = pCurBL->getNextBlockInDocument();
3462 			for(i = iLimit/2 + iLimit%2; i < iLimit && pBL; ++i, pBL = pBL->getNextBlockInDocument())
3463 			{
3464 				vBL.addItem(pBL);
3465 			}
3466 		}
3467 
3468 		fl_ContainerLayout* b = pSL->getFirstLayout();
3469 		while (b)
3470 		{
3471 			// TODO: just check and remove matching squiggles
3472 			// for now, destructively recheck the whole thing
3473 			if(b->getContainerType() == FL_CONTAINER_BLOCK)
3474 			{
3475 				bool bHead = (vBL.findItem(static_cast<fl_BlockLayout *>(b)) >= 0);
3476 				queueBlockForBackgroundCheck(iReason, static_cast<fl_BlockLayout *>(b), bHead);
3477 				b = static_cast<fl_BlockLayout *>(b)->getNextBlockInDocument();
3478 			}
3479 			else
3480 			{
3481 				b = b->getNext();
3482 			}
3483 		}
3484 	}
3485 }
3486 
3487 
3488 
3489 /*!
3490  * Set the next block to be grammar checked. It won't actually get checked
3491  * until the insertPoint leaves this block.
3492  */
setPendingBlockForGrammar(fl_BlockLayout * pBL)3493 void FL_DocLayout::setPendingBlockForGrammar(fl_BlockLayout * pBL)
3494 {
3495   xxx_UT_DEBUGMSG(("Pending called with block %x pending %x \n",pBL,m_PendingBlockForGrammar));
3496   if(!m_bAutoGrammarCheck)
3497     return;
3498   if((m_PendingBlockForGrammar != NULL) && (m_PendingBlockForGrammar != pBL))
3499     {
3500       xxx_UT_DEBUGMSG(("Block %x queued \n",m_PendingBlockForGrammar));
3501       queueBlockForBackgroundCheck(bgcrGrammar,m_PendingBlockForGrammar,true);
3502     }
3503   m_PendingBlockForGrammar = pBL;
3504 }
3505 
3506 
3507 /*!
3508  * This is called from fv_View::_fixPointCoords to actually queue a grammar
3509  * check a pending block.
3510  */
triggerPendingBlock(fl_BlockLayout * pBL)3511 void FL_DocLayout::triggerPendingBlock(fl_BlockLayout * pBL)
3512 {
3513   xxx_UT_DEBUGMSG(("Trigger called with block %x pending %x \n",pBL,m_PendingBlockForGrammar));
3514   if(!m_bAutoGrammarCheck)
3515     return;
3516   if((m_PendingBlockForGrammar != NULL) && (m_PendingBlockForGrammar != pBL))
3517     {
3518       queueBlockForBackgroundCheck(bgcrGrammar,m_PendingBlockForGrammar,true);
3519       m_PendingBlockForGrammar = NULL;
3520      }
3521 }
3522 
3523 /*!
3524  Remove block from background checking queue
3525  \param pBlock Block to remove
3526 
3527  When the last block is removed from the queue, the background timer
3528  is stopped. The function does not return before the background
3529  spell-checking timer has stopped.
3530 */
3531 bool
dequeueBlockForBackgroundCheck(fl_BlockLayout * pBlock)3532 FL_DocLayout::dequeueBlockForBackgroundCheck(fl_BlockLayout *pBlock)
3533 {
3534 	bool bRes = false;
3535 
3536 	// Remove block from queue if it's found there
3537 	bRes = pBlock->isQueued();
3538 	if (bRes) {
3539 		pBlock->dequeueFromSpellCheck();
3540 	}
3541 	if(pBlock == m_PendingBlockForGrammar)
3542 	  {
3543 	    xxx_UT_DEBUGMSG(("Dequeue block %x in dequeue \n",pBlock));
3544 	    m_PendingBlockForGrammar = NULL;
3545 	  }
3546 	// When queue is empty, kill timer
3547 	if (spellQueueHead() == NULL)
3548 	{
3549 		m_bStopSpellChecking = true;
3550 		if(m_pBackgroundCheckTimer)
3551 		{
3552 			m_pBackgroundCheckTimer->stop();
3553 			// Wait for checking to complete before returning.
3554 			while(m_bImSpellCheckingNow == true)
3555 			{
3556 				// TODO shouldn't we have a little sleep here?
3557 			}
3558 		}
3559 	}
3560 
3561 	return bRes;
3562 }
3563 
3564 /*!
3565   Mark a region of a block to be spell checked
3566   \param pBlock Block
3567   \param pWord  Region
3568 
3569   If called with NULL arguments, any prior marked region will be
3570   freed. Callers must reuse pWord (by calling getPendingWordForSpell)
3571   when set.
3572 */
3573 void
setPendingWordForSpell(const fl_BlockLayout * pBlock,const fl_PartOfBlockPtr & pWord)3574 FL_DocLayout::setPendingWordForSpell(const fl_BlockLayout *pBlock,
3575 									 const fl_PartOfBlockPtr& pWord)
3576 {
3577 	// Return if matching the existing marked region
3578 	if ((pBlock == m_pPendingBlockForSpell) &&
3579 		(pWord == m_pPendingWordForSpell))
3580 		return;
3581 
3582 	// Assert an existing pWord allocation is reused
3583 	UT_ASSERT(!m_pPendingBlockForSpell || !pBlock
3584 			  || m_pPendingWordForSpell == pWord);
3585 
3586 	// Check for valid arguments
3587 	if (pBlock && m_pPendingBlockForSpell && m_pPendingWordForSpell)
3588 	{
3589 		UT_ASSERT(pWord);
3590 	}
3591 
3592 	m_pPendingBlockForSpell = pBlock;
3593 	m_pPendingWordForSpell = pWord;
3594 }
3595 
3596 /*!
3597  Spell-check pending word
3598  \result True if word checked, false otherwise
3599  If a word is pending, spell-check it.
3600 
3601  \note This function used to exit if PT was changing - but that
3602        prevents proper squiggle behavior during undo, so the check has
3603        been removed. This means that the pending word POB must be
3604        updated to reflect the PT changes before the IP is moved.
3605 */
3606 bool
checkPendingWordForSpell(void)3607 FL_DocLayout::checkPendingWordForSpell(void)
3608 {
3609 	// do not attempt to check a word if check is already in progress (see 7197)
3610 	if(m_bSpellCheckInProgress)
3611 		return false;
3612 
3613 	bool bUpdate = false;
3614 
3615 	xxx_UT_DEBUGMSG(("FL_DocLayout::checkPendingWordForSpell\n"));
3616 
3617 	if (!m_pPendingBlockForSpell)
3618 		return bUpdate;
3619 
3620 	m_bSpellCheckInProgress = true;
3621 
3622 	// Check pending word
3623 	UT_ASSERT(m_pPendingWordForSpell);
3624 	bUpdate = m_pPendingBlockForSpell->checkWord(m_pPendingWordForSpell);
3625 
3626 	m_pPendingWordForSpell = NULL;	// NB: already freed by checkWord
3627 
3628 	// Not pending any more
3629 	setPendingWordForSpell(NULL, NULL);
3630 
3631 	m_bSpellCheckInProgress = false;
3632 
3633 	return bUpdate;
3634 }
3635 
3636 /*!
3637  Is a word pending for spelling
3638  \return True if a word is pending, false otherwise
3639 */
3640 bool
isPendingWordForSpell(void) const3641 FL_DocLayout::isPendingWordForSpell(void) const
3642 {
3643 	return (m_pPendingBlockForSpell ? true : false);
3644 }
3645 
3646 /*!
3647  Determine if position touches the pending word for spelling
3648  \param pBLock Block of position
3649  \param iOffset Offset in block
3650  \param chg  FIXME
3651  \return True if position touches pending word, false otherwise
3652 
3653  FIXME: why this function/chg? Caller uses result for what?
3654 */
3655 bool
touchesPendingWordForSpell(fl_BlockLayout * pBlock,UT_sint32 iOffset,UT_sint32 chg) const3656 FL_DocLayout::touchesPendingWordForSpell(fl_BlockLayout *pBlock,
3657 										 UT_sint32 iOffset,
3658 										 UT_sint32 chg) const
3659 {
3660 	UT_uint32 len = (chg < 0) ? -chg : 0;
3661 
3662 	UT_ASSERT(pBlock);
3663 
3664 	if (!m_pPendingBlockForSpell)
3665 		return false;
3666 
3667 	// Are we in the same block?
3668 	if (m_pPendingBlockForSpell != pBlock)
3669 		return false;
3670 
3671 	UT_return_val_if_fail(m_pPendingWordForSpell,false);
3672 
3673 	return m_pPendingWordForSpell->doesTouch(iOffset, len);
3674 }
3675 #endif // ENABLE_SPELL
3676 
3677 /*!
3678  * This method appends a DocSectionLayout onto the linked list of SectionLayout's
3679  * and updates the m_pFirstSection and m_pLastSection member variables
3680  * accordingly.
3681  * The structure of this linked list is as follows.
3682  *    pDSL->pDSL->....pDSL->pEndnoteSL->pHdrFtrSL->pHdrFtrSL->NULL
3683  *     ^                ^
3684  *m_pFirstSection   m_pLastSection
3685  *ie we have all the DocSections in a linked list followed by all the Header/
3686  * Footer sections. This reflects the locations in the piece table where
3687  * the header/footer sections are located at the end of the document.
3688 \param  fl_DocSectionLayout * pSL the DocSectionLayout to be appended.
3689 \param  fl_DocSectionLayout* pAfter the DocSectionLayout after which our new
3690         DocSectionLayout is inserted.
3691 */
addSection(fl_DocSectionLayout * pSL)3692 void FL_DocLayout::addSection(fl_DocSectionLayout* pSL)
3693 {
3694 	if (m_pLastSection)
3695 	{
3696 		UT_ASSERT(m_pFirstSection);
3697 		insertSectionAfter(m_pLastSection,pSL);
3698 	}
3699 	else
3700 	{
3701 		pSL->setPrev(NULL);
3702 		pSL->setNext(NULL);
3703 		m_pFirstSection = pSL;
3704 		m_pLastSection = m_pFirstSection;
3705 	}
3706 }
3707 
3708 /*!
3709  * This method inserts a DocSectionLayout into the linked list of SectionLayout's
3710  * and updates the m_pFirstSection and m_pLastSection member variables
3711  * accordingly
3712 \param  fl_DocSectionLayout * pNewSL the DocSectionLayout to be inserted.
3713 \param  fl_DocSectionLayout* pAfter the DocSectionLayout after which our new
3714         DocSectionLayout is inserted.
3715 */
insertSectionAfter(fl_DocSectionLayout * pAfter,fl_DocSectionLayout * pNewSL)3716 void FL_DocLayout::insertSectionAfter(fl_DocSectionLayout* pAfter, fl_DocSectionLayout* pNewSL)
3717 {
3718         UT_return_if_fail(pAfter);
3719 	pNewSL->setNext(pAfter->getNext());
3720 	pNewSL->setPrev(pAfter);
3721 	if (pAfter->getNext())
3722 	{
3723 		pAfter->getNext()->setPrev(pNewSL);
3724 	}
3725 	pAfter->setNext(pNewSL);
3726 
3727 	if (m_pLastSection == pAfter)
3728 	{
3729 		m_pLastSection = pNewSL;
3730 	}
3731 }
3732 
3733 /*!
3734  * This method removes a DocSectionLayout from the linked list of SectionLayout's
3735  * and updates the m_pFirstSection and m_pLastSection member variables
3736  * accordingly
3737 \param  fl_DocSectionLayout * pSL the DocSectionLayout to be removed.
3738 */
3739 
removeSection(fl_DocSectionLayout * pSL)3740 void FL_DocLayout::removeSection(fl_DocSectionLayout * pSL)
3741 {
3742 	UT_return_if_fail(pSL);
3743 	UT_ASSERT(m_pFirstSection);
3744 
3745 	if (pSL->getPrev())
3746 	{
3747 		pSL->getPrev()->setNext(pSL->getNext());
3748 	}
3749 
3750 	if (pSL->getNext())
3751 	{
3752 		pSL->getNext()->setPrev(pSL->getPrev());
3753 	}
3754 
3755 	if (pSL == m_pFirstSection)
3756 	{
3757 		m_pFirstSection = m_pFirstSection->getNextDocSection();
3758 		if (!m_pFirstSection)
3759 		{
3760 			m_pLastSection = NULL;
3761 		}
3762 	}
3763 
3764 	if (pSL == m_pLastSection)
3765 	{
3766 		m_pLastSection = m_pLastSection->getPrevDocSection();
3767 		if (!m_pLastSection)
3768 		{
3769 			m_pFirstSection = NULL;
3770 		}
3771 	}
3772 
3773 	pSL->setNext(NULL);
3774 	pSL->setPrev(NULL);
3775 }
3776 
3777 /*!
3778  * Include the header/footer section layouts AFTER the last DocSection in the
3779  * the getNext, getPrev list. This will ensure that the headers/footers will be
3780  * formatted and updated correctly.
3781  \param fl_SectionLayout * pHdrFtrSL the header/footer layout to be inserted
3782         into the sectionlayout linked list.
3783  * The structure of this linked list is as follows.
3784  *    pDSL->pDSL->pDSL....pDSL->pEndnoteSL->pHdrFtrSL->pHdrFtrSL->NULL
3785  *     ^                   ^
3786  *m_pFirstSection   m_pLastSection
3787  *
3788  *ie we have all the DocSections in a linked list followed by all the Header/
3789  * Footer sections. This reflects the locations in the piece table where
3790  * the header/footer sections are located at the end of the document.
3791 */
addHdrFtrSection(fl_SectionLayout * pHdrFtrSL)3792 void FL_DocLayout::addHdrFtrSection(fl_SectionLayout* pHdrFtrSL)
3793 {
3794 	UT_ASSERT(m_pLastSection);
3795 
3796 	fl_SectionLayout * pLSL = static_cast<fl_SectionLayout *>(m_pLastSection);
3797 	fl_SectionLayout * pnext = static_cast<fl_SectionLayout *>(pLSL->getNext());
3798 
3799 	while (pnext && pnext->getType() == FL_SECTION_ENDNOTE)
3800 		pnext = static_cast<fl_SectionLayout *>(pnext->getNext());
3801 
3802 	if(pnext)
3803 	{
3804 		pnext->setPrev(pHdrFtrSL);
3805 		pLSL->setNext(pHdrFtrSL);
3806 		pHdrFtrSL->setPrev(pLSL);
3807 		pHdrFtrSL->setNext(pnext);
3808 	}
3809 	else
3810 	{
3811 		pLSL->setNext(pHdrFtrSL);
3812 		pHdrFtrSL->setPrev(pLSL);
3813 		pHdrFtrSL->setNext(pnext);
3814 	}
3815 }
3816 
3817 /*!
3818  *  This method removes a header/footer layout from the section linked list.
3819  \param fl_SectionLayout * pHdrFtrSL is the header/footer section to be removed
3820 */
removeHdrFtrSection(fl_SectionLayout * pHdrFtrSL)3821 void FL_DocLayout::removeHdrFtrSection(fl_SectionLayout * pHdrFtrSL)
3822 {
3823 	UT_return_if_fail(pHdrFtrSL);
3824 
3825 	if(pHdrFtrSL->getPrev())
3826 	{
3827 		pHdrFtrSL->getPrev()->setNext(pHdrFtrSL->getNext());
3828 	}
3829 	if (pHdrFtrSL->getNext())
3830 	{
3831 		pHdrFtrSL->getNext()->setPrev(pHdrFtrSL->getPrev());
3832 	}
3833 	pHdrFtrSL->setNext(NULL);
3834 	pHdrFtrSL->setPrev(NULL);
3835 }
3836 
findSectionForHdrFtr(const char * pszHdrFtrID) const3837 fl_DocSectionLayout* FL_DocLayout::findSectionForHdrFtr(const char* pszHdrFtrID) const
3838 {
3839 	if(!pszHdrFtrID)
3840 		return NULL;
3841 
3842 	const char* pszAtt = NULL;
3843 
3844 	fl_DocSectionLayout* pDocSL = m_pFirstSection;
3845 	while (pDocSL)
3846 	{
3847 		pszAtt = pDocSL->getAttribute("header");
3848 		if ( pszAtt	&& (0 == strcmp(pszAtt, pszHdrFtrID)))
3849 		{
3850 			return pDocSL;
3851 		}
3852 
3853 		pszAtt = pDocSL->getAttribute("footer");
3854 		if (pszAtt && (0 == strcmp(pszAtt, pszHdrFtrID)))
3855 		{
3856 			return pDocSL;
3857 		}
3858 		pszAtt = pDocSL->getAttribute("header-even");
3859 		if ( pszAtt	&& (0 == strcmp(pszAtt, pszHdrFtrID)))
3860 		{
3861 			return pDocSL;
3862 		}
3863 
3864 		pszAtt = pDocSL->getAttribute("footer-even");
3865 		if (pszAtt && (0 == strcmp(pszAtt, pszHdrFtrID)))
3866 		{
3867 			return pDocSL;
3868 		}
3869 		pszAtt = pDocSL->getAttribute("header-last");
3870 		if ( pszAtt	&& (0 == strcmp(pszAtt, pszHdrFtrID)))
3871 		{
3872 			return pDocSL;
3873 		}
3874 
3875 		pszAtt = pDocSL->getAttribute("footer-last");
3876 		if (pszAtt && (0 == strcmp(pszAtt, pszHdrFtrID)))
3877 		{
3878 			return pDocSL;
3879 		}
3880 		pszAtt = pDocSL->getAttribute("header-first");
3881 		if ( pszAtt	&& (0 == strcmp(pszAtt, pszHdrFtrID)))
3882 		{
3883 			return pDocSL;
3884 		}
3885 
3886 		pszAtt = pDocSL->getAttribute("footer-first");
3887 		if (pszAtt && (0 == strcmp(pszAtt, pszHdrFtrID)))
3888 		{
3889 			return pDocSL;
3890 		}
3891 
3892 		pDocSL = pDocSL->getNextDocSection();
3893 	}
3894 
3895 	return NULL;
3896 }
3897 
_prefsListener(XAP_Prefs * pPrefs,UT_StringPtrMap *,void * data)3898 /*static*/ void FL_DocLayout::_prefsListener (
3899 	XAP_Prefs			*pPrefs,
3900 	UT_StringPtrMap	* /*phChanges*/,  // not used
3901 	void				*data
3902 	)
3903 {
3904 	bool b;
3905 	FL_DocLayout *pDocLayout = static_cast<FL_DocLayout *>(data);
3906 
3907 	xxx_UT_DEBUGMSG(("spell_prefsListener %p\n", pDocLayout));
3908 	UT_ASSERT( pPrefs && data );
3909 
3910 	// caps/number/internet
3911 
3912     // Note that these options are now "ignore..." in the prefs pane,
3913     // so the opton settings are reverted for use in the doclayout
3914     // (b = !b)
3915 	bool changed = false;
3916 #ifdef ENABLE_SPELL
3917 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_SpellCheckCaps), &b );
3918     b = !b;
3919 	changed = changed || (b != pDocLayout->getSpellCheckCaps());
3920 	pDocLayout->m_bSpellCheckCaps = b;
3921 
3922 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_SpellCheckNumbers), &b );
3923     b = !b;
3924 	changed = changed || (b != pDocLayout->getSpellCheckNumbers());
3925 	pDocLayout->m_bSpellCheckNumbers = b;
3926 
3927 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_SpellCheckInternet), &b );
3928     b = !b;
3929 	changed = changed || (b != pDocLayout->getSpellCheckInternet());
3930 	pDocLayout->m_bSpellCheckInternet = b;
3931 
3932 	// auto spell
3933 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_AutoSpellCheck), &b );
3934 	changed = changed || (b != pDocLayout->m_bAutoSpellCheck);
3935 	if(b != pDocLayout->m_bAutoSpellCheck || (pDocLayout->m_iGraphicTick < 2))
3936 	{
3937 		pDocLayout->m_bAutoSpellCheck = b;
3938 		pDocLayout->_toggleAutoSpell( b );
3939 	}
3940 
3941 	// grammar check
3942 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_AutoGrammarCheck), &b );
3943 	changed = changed || (b != pDocLayout->m_bAutoSpellCheck);
3944 	if(b != pDocLayout->m_bAutoGrammarCheck || (pDocLayout->m_iGraphicTick < 2))
3945 	{
3946 		pDocLayout->m_bAutoGrammarCheck = b;
3947 		pDocLayout->_toggleAutoGrammar( b );
3948 	}
3949 #endif
3950 // autosave
3951 
3952 	UT_String stTmp;
3953 	FV_View * pView = pDocLayout->getView();
3954 	if(pView)
3955 	{
3956 		XAP_Frame * pFrame = static_cast<XAP_Frame *>(pView->getParentData());
3957 		if(pFrame)
3958 		{
3959 			pPrefs->getPrefsValueBool(static_cast<const gchar *>(XAP_PREF_KEY_AutoSaveFile), &b );
3960 			changed = (b != pFrame->isBackupRunning());
3961 			if(changed)
3962 			{
3963 				pFrame->setAutoSaveFile(b);
3964 			}
3965 
3966 // autosave period
3967 
3968 			pPrefs->getPrefsValue(XAP_PREF_KEY_AutoSaveFilePeriod, stTmp);
3969 			UT_sint32 iPeriod = atoi(stTmp.c_str());
3970 			changed = (iPeriod != pFrame->getAutoSavePeriod());
3971 			if(changed)
3972 			{
3973 				pFrame->setAutoSaveFilePeriod(iPeriod);
3974 				if(pFrame->isBackupRunning())
3975 				{
3976 					pFrame->setAutoSaveFile(false);
3977 					pFrame->setAutoSaveFile(true);
3978 				}
3979 			}
3980 		}
3981 	}
3982 
3983 
3984 	if ( changed )
3985 	{
3986 		// TODO: recheck document
3987 		;
3988 	}
3989 
3990 	pPrefs->getPrefsValueBool( static_cast<const gchar *>(XAP_PREF_KEY_SmartQuotesEnable), &b );
3991 
3992 
3993 	pDocLayout->_toggleAutoSmartQuotes( b );
3994 
3995 	const gchar * pszTransparentColor = NULL;
3996 	pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForTransparent),&pszTransparentColor);
3997 	if(strcmp(pszTransparentColor,pDocLayout->m_szCurrentTransparentColor) != 0)
3998 	{
3999 		if(pDocLayout->getView() && (pDocLayout->getView()->getPoint() > 0))
4000 		{
4001 			pDocLayout->updateColor();
4002 		}
4003 	}
4004 
4005 	// Display Annotations
4006 
4007 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_DisplayAnnotations), &b );
4008 	changed = changed || (b != pDocLayout->m_bDisplayAnnotations);
4009 	if(b != pDocLayout->m_bDisplayAnnotations || (pDocLayout->m_iGraphicTick < 2))
4010 	{
4011 		pDocLayout->m_bDisplayAnnotations = b;
4012 		pDocLayout->collapseAnnotations();
4013 		pDocLayout->formatAll();
4014 		if(pDocLayout->getView())
4015 		{
4016 		    pDocLayout->getView()->updateScreen(false);
4017 		}
4018 	}
4019 
4020 	// Display RDF Anchors
4021 
4022 	pPrefs->getPrefsValueBool(static_cast<const gchar *>(AP_PREF_KEY_DisplayRDFAnchors), &b );
4023 	changed = changed || (b != pDocLayout->m_bDisplayRDFAnchors);
4024 	if(b != pDocLayout->m_bDisplayRDFAnchors || (pDocLayout->m_iGraphicTick < 2))
4025 	{
4026 		pDocLayout->m_bDisplayRDFAnchors = b;
4027 		pDocLayout->formatAll();
4028 		if(pDocLayout->getView())
4029 		{
4030 		    pDocLayout->getView()->updateScreen(false);
4031 		}
4032 	}
4033 
4034 }
4035 
4036 #ifdef ENABLE_SPELL
recheckIgnoredWords()4037 void FL_DocLayout::recheckIgnoredWords()
4038 {
4039 	// recheck the whole doc
4040 	fl_DocSectionLayout * pSL = getFirstSection();
4041 	if(pSL)
4042 	{
4043 		fl_ContainerLayout* b = pSL->getFirstLayout();
4044 		while (b)
4045 		{
4046 			if(b->getContainerType() == FL_CONTAINER_BLOCK)
4047 			{
4048 				static_cast<fl_BlockLayout *>(b)->recheckIgnoredWords();
4049 				b = static_cast<fl_BlockLayout *>(b)->getNextBlockInDocument();
4050 			}
4051 			else
4052 			{
4053 				b = b->getNext();
4054 			}
4055 		}
4056 	}
4057 }
4058 #endif
4059 
4060 /*!
4061  * Mark the whole document for a redraw
4062  */
setNeedsRedraw(void)4063 void FL_DocLayout::setNeedsRedraw(void)
4064 {
4065      if(!m_pFirstSection)
4066          return;
4067      setSkipUpdates(0);
4068      fl_BlockLayout * pBL = m_pFirstSection->getFirstBlock();
4069      while(pBL)
4070      {
4071 	 pBL->setNeedsRedraw();
4072 	 pBL = pBL->getNextBlockInDocument();
4073      }
4074 }
4075 
_redrawUpdate(UT_Worker * pWorker)4076 void FL_DocLayout::_redrawUpdate(UT_Worker * pWorker)
4077 {
4078 	UT_return_if_fail(pWorker);
4079 
4080 	// this is a static callback method and does not have a 'this' pointer.
4081 
4082 	FL_DocLayout * pDocLayout = static_cast<FL_DocLayout *>(pWorker->getInstanceData());
4083 	UT_return_if_fail(pDocLayout);
4084 
4085 	if (!pDocLayout->m_pView || pDocLayout->isLayoutFilling())
4086 	{
4087 		// Win32 timers can fire prematurely on asserts
4088 		// (the dialog's message pump releases the timers)
4089 		return;
4090 	}
4091 	xxx_UT_DEBUGMSG(("SEVIOR: _redraw update \n"));
4092 //
4093 // Check if we're in the middle of a PieceTable change. If so don't redraw!
4094 //
4095 	PD_Document * pDoc = pDocLayout->getDocument();
4096 	if(pDoc->isPieceTableChanging())
4097 	{
4098 		UT_DEBUGMSG(("PieceTable changing don't redraw \n"));
4099 		return;
4100 	}
4101 	if(pDocLayout->isQuickPrint())
4102 	{
4103 	        UT_DEBUGMSG(("Doing a quickPrint don't redraw \n"));
4104 		return;
4105 	}
4106 //
4107 // Don't redraw while the selection is active
4108 //
4109 //	if(!pDocLayout->m_pView->isSelectionEmpty())
4110 //	{
4111 //		return;
4112 //	}
4113 //
4114 // Lock out PieceTable changes till we finished.
4115 //
4116 	pDoc->setRedrawHappenning(true);
4117 //
4118 // Check if we've been asked to wait for a while..
4119 //
4120 	UT_uint32 skip = pDocLayout->getSkipUpdates();
4121 	if(skip > 0)
4122 	{
4123 		skip--;
4124 		pDocLayout->setSkipUpdates(skip);
4125 		pDoc->setRedrawHappenning(false);
4126 		return;
4127 	}
4128 //
4129 // Check if we're printing. If so Bail out
4130 //
4131 	if(pDocLayout->m_pG->queryProperties(GR_Graphics::DGP_PAPER))
4132 	{
4133 		pDoc->setRedrawHappenning(false);
4134 		return;
4135 	}
4136 	bool bStopOnRebuild = false;
4137 	fl_SectionLayout* pSL = pDocLayout->m_pFirstSection;
4138 //
4139 // This bit is to make sure the insertionPoint is always on screen.
4140 //
4141 	FV_View * pView = pDocLayout->getView();
4142 	bool bEnd,bDir;
4143 	bEnd = false;
4144 	fl_BlockLayout * pBlock = NULL;
4145 	fp_Run *pRun = NULL;
4146 	UT_sint32 x1,x2,y1,y2;
4147 	UT_uint32 height;
4148 	UT_sint32 origY;
4149 	pView->_findPositionCoords(pView->getPoint(),bEnd,x1,y1,x2,y2,height,bDir,&pBlock,&pRun);
4150 	origY = y1;
4151 	while (pSL && !bStopOnRebuild)
4152 	{
4153 		if(pDoc->isPieceTableChanging())
4154 		{
4155 			pDoc->setRedrawHappenning(false);
4156 			return;
4157 		}
4158 		pSL->redrawUpdate();
4159 //
4160 // Might need some code here to check if we need a rebuild. In principle we should not need it.
4161 //
4162 		if(pSL->getType() == FL_SECTION_DOC)
4163 		{
4164 			if(static_cast<fl_DocSectionLayout *>(pSL)->needsRebuild())
4165 			{
4166 				bStopOnRebuild = true;
4167 			}
4168 		}
4169 		if(!bStopOnRebuild)
4170 		{
4171 			pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
4172 		}
4173 	}
4174 	pDocLayout->deleteEmptyColumnsAndPages();
4175 	if(bStopOnRebuild)
4176 	{
4177 		UT_DEBUGMSG(("SEVIOR: Rebuilding from docLayout \n"));
4178 		pDocLayout->rebuildFromHere(static_cast<fl_DocSectionLayout *>(pSL));
4179 	}
4180 	pView->_findPositionCoords(pView->getPoint(),bEnd,x1,y1,x2,y2,height,bDir,&pBlock,&pRun);
4181 //
4182 // If Y location has changed make sure it's still on screen
4183 //
4184 	if(y1 != origY)
4185 	{
4186 		UT_DEBUGMSG(("Line pos changed \n"));
4187 //		UT_ASSERT(0);
4188 		pView->_ensureInsertionPointOnScreen();
4189 	}
4190 
4191 //
4192 // we've finished
4193 //
4194 	pDoc->setRedrawHappenning(false);
4195 	pDocLayout->m_iRedrawCount++;
4196 }
4197 
setPendingSmartQuote(fl_BlockLayout * bl,UT_uint32 of)4198 void FL_DocLayout::setPendingSmartQuote(fl_BlockLayout *bl, UT_uint32 of)
4199 {
4200 	xxx_UT_DEBUGMSG(("FL_DocLayout::setPendingSmartQuote(%x, %d)\n", bl, of));
4201 	m_pPendingBlockForSmartQuote = bl;
4202 	m_uOffsetForSmartQuote = of;
4203 }
4204 
4205 /* wjc sez....
4206 
4207 This algorithm is based on my observation of how people actually use
4208 quotation marks, sometimes in contravention of generally accepted
4209 principles of punctuation.  It is certainly also true that my
4210 observations are overwhelmingly of American English text, with a
4211 smattering of various other languages observed from time to time.  I
4212 don't believe that any algorithm for this can ever be perfect.  There
4213 are too many infrequently-occurring but legitimate cases where a user
4214 might want something else.  FWIW, I haven't tested out the specifics
4215 of the smart quote algorithm in ThatOtherWordProcessor.
4216 
4217 Some terms for the purpose of this discussion (I'm open to plenty of
4218 advice on what specific items should fit in each of these classes):
4219 
4220 sqBREAK  A structural break in a document.  For example, a paragraph
4221   break, a column break, a page break, the beginning or end of a
4222   document, etc.  Does not include font, size, bold/italic/underline
4223   changes (which are completely ignored for the purposes of this
4224   algorithm).
4225 
4226 sqFOLLOWPUNCT A subset of layman's "punctuation".  I include only
4227   things that can normally occur after a quote mark with no intervening
4228   white space.  Includes period, exclamation point, question mark,
4229   semi-colon, colon, comma (but not parentheses, square and curly
4230   brackets, which are treated specially below).  There may be a few
4231   others that aren't on the kinds of keyboards I use, and there are
4232   certainly Latin1 and other locale-specific variants, but the point
4233   is that there are lots of random non-alphanumerics which aren't
4234   included in *PUNCT for this algorithm.
4235 
4236 sqOPENPUNCT  The opening half of pairwise, non-quote punctuation.  Open
4237   parenthesis, open square bracket, open curly brace.
4238 
4239 sqCLOSEPUNCT  The closing half of pairwise, non-quote punctuation.  Close
4240   parenthesis, close square bracket, close curly brace.
4241 
4242 [[The idea about open and close punctuation was found in a mid-1980s
4243 note by Dave Dunham, brought to my attention by Leonard Rosenthol
4244 <leonardr@lazerware.com>.]]
4245 
4246 sqOTHERPUNCT  Punctuation which is not sqFOLLOWPUNCT, sqOPENPUNCT, or
4247   sqCLOSEPUNCT.
4248 
4249 sqALPHA  Alphabetic characters in the C isalpha() sense, but there are
4250   certainly some non-ASCII letter characters which belong in this
4251   bucket, too.
4252 
4253 sqWHITE  White speace haracters in the C isspace() sense.
4254 
4255 QUOTE  Any of ASCII double quote, ASCII quote (which many people call
4256   the ASCII single quote or the ASCII apostrophe), or ASCII backquote.
4257   I take it as given that a significant minority of people randomly or
4258   systematically interchange their use of ASCII quote and ASCII
4259   backquote, so I treat them the same in the algorithm.  The majority
4260   of people use ASCII quote for both opening and closing single quote.
4261 
4262 PARITY  Whether a quote is single or double.  For ease of description,
4263   I'll say that the parity of single and double quotes are opposites
4264   of each other.  When QUOTEs are converted to curly form, the parity
4265   never changes.
4266 
4267 ================================================================
4268 
4269 Given a QUOTE character, these conditions/rules are logically tested in
4270 order:
4271 
4272 0.  OK, first an easy exception case: If ASCII (single) quote (but not
4273 ASCII backquote) appears between two sqALPHAs, it may be treated as an
4274 apostrophe and converted to its curly form.  Otherwise, it is treated
4275 like all other QUOTEs and follows the normal algorithm.
4276 
4277 1.  If a QUOTE is immediately preceded by a curly quote of opposite
4278 parity, it is converted to a curly quote in the same direction.
4279 
4280 2.  If a QUOTE is immediately preceded by a curly quote of the same
4281 parity, it is converted to a curly quote of opposite direction.
4282 
4283 3.  If a QUOTE is immediately followed by a curly quote of opposite
4284 parity, it is converted to a curly quote in the same direction.
4285 
4286 4.  If a QUOTE is immediately followed by a curly quote of the same
4287 parity, it is converted to a curly quote of opposite direction.
4288 
4289 [[The above cases are intended to handle normal nested quotes or cases
4290 where quotes enclose empty strings.  Different cultures use different
4291 parities as start points for nested quotes, but the algorithm doesn't
4292 care.]]
4293 
4294 5.  If a QUOTE is immediately preceded by an sqOPENPUNCT, it is
4295 converted to a curly quote in the open direction.
4296 
4297 6.  If a QUOTE is immediately followed by a sqCLOSEPUNCT, it is
4298 converted to a curly quote in the close direction.
4299 
4300 7.  If a QUOTE is in isolation, it is converted to a curly quote
4301 in the open direction.  It is in isolation if it is immediately
4302 preceded and followed by either a sqBREAK or sqWHITE.  The things
4303 before and after it don't have to be of the same type.
4304 
4305 Rule 7 was originally that it didin't convert.  This does not make
4306 sense because the most common use of smart quotes is as you're typing
4307 where there is often a space before and nothing after (since it hasn't
4308 been written yet!) -- Bobby Weinmann Feb 4, 2008
4309 
4310 8.  If a QUOTE is immediately preceded by a sqBREAK or sqWHITE and
4311 is immediately followed by anything other than a sqBREAK or sqWHITE,
4312 it is converted to the opening form of curly quote.
4313 
4314 9.  If a QUOTE is immediately followed by a sqBREAK, sqWHITE, or
4315 sqFOLLOWPUNCT and is immediately preceded by anything other than sqBREAK
4316 or sqWHITE, it is converted to the closing form of curly quote.
4317 
4318 10.  Any other QUOTE is not converted.
4319 
4320 ================================================================
4321 
4322 The algorithm doesn't make a special case of using ASCII double quote
4323 as an inches indicator (there are other uses, like lat/long minutes;
4324 ditto for the ASCII quote) because it is tough to tell if some numbers
4325 with an ASCII double quote after them are intended to be one of those
4326 "other things" or is just the end of a very long quote.  So, the
4327 algorithm will be wrong sometimes in those cases.
4328 
4329 It is otherwise sort of conservative, preferring to not convert things
4330 it doesn't feel confident about.  The reason for that is that there is
4331 a contemplated on-the-fly conversion to smart quotes, but there is no
4332 contemplated on-the-fly conversion to ASCII QUOTEs.  So, if the
4333 algorithm makes a mistake by not converting, the user can correct it
4334 by directly entering the appropriate smart quote character or by
4335 heuristically tricking AbiWord into converting it for him/her and then
4336 fixing things up.  (That heuristic step shouldn't be necessary, you
4337 know, but I think we all use software for which we have become
4338 accustomed to such things.)
4339 
4340 What about the occasions when this algorithm (or any alternative
4341 algorithm) makes a mistake and converts a QUOTE to the curly form when
4342 it really isn't wanted, in a particular case, by the user?  Although
4343 the user can change it back, some contemplated implementation details
4344 might run around behind the barn and re-convert it when the user isn't
4345 looking.  I think we need a mechanism for dealing with that, but I
4346 want to save proposals for that to be separate from the basic
4347 algorithm.
4348 */
4349 
4350 // The following are descriptions of the thing before or after a
4351 // character being considered for smart quote promotion.  The thing
4352 // is either a structural break in a document, or it is a literal
4353 // character that is part of some class (in some cases the class is
4354 // so small it has only one possible member).  The classes should
4355 // look familar from the algorithm above.  There is a special class
4356 // used only for the coding of rule:  sqDONTCARE in a rule means it
4357 // doesn't matter what occurs in that position.
4358 enum sqThingAt
4359 {
4360 	sqDONTCARE     = 1,
4361 	sqQUOTEls      = 2,
4362 	sqQUOTErs      = 3,
4363 	sqQUOTEld      = 4,
4364 	sqQUOTErd      = 5,// the smart quotes, left/right single/double
4365 	sqBREAK        = 6,
4366 	sqFOLLOWPUNCT  = 7,
4367 	sqOPENPUNCT    = 8,
4368 	sqCLOSEPUNCT   = 9,
4369 	sqOTHERPUNCT   =10,
4370 	sqALPHA        =11,
4371 	sqWHITE        =12
4372 };
4373 
4374 // TODO:  This function probably needs tuning for non-Anglo locales.
whatKindOfChar(UT_UCSChar thing)4375 static enum sqThingAt whatKindOfChar(UT_UCSChar thing)
4376 {
4377 	xxx_UT_DEBUGMSG(("what sort of character is %d 0x%x |%c|\n", thing, thing, thing));
4378 	switch (thing)
4379 	{
4380 	case UCS_LQUOTE:     return sqQUOTEls;
4381 	case UCS_RQUOTE:     return sqQUOTErs;
4382 	case UCS_LDBLQUOTE:  return sqQUOTEld;
4383 	case UCS_RDBLQUOTE:  return sqQUOTErd;
4384 
4385 	case '(': case '{': case '[':  return sqOPENPUNCT;
4386 	case ')': case '}': case ']':  return sqCLOSEPUNCT;
4387 
4388 	case '.': case ',': case ';': case ':': case '!': case '?':  return sqFOLLOWPUNCT;
4389 
4390 		// see similar control characters in fl_BlockLayout.cpp
4391 	case UCS_FF:	// form feed, forced page break
4392 	case UCS_VTAB:	// vertical tab, forced column break
4393 	case UCS_LF:	// newline
4394 	case UCS_TAB:	// tab
4395 		return sqBREAK;
4396 	}
4397 	if (UT_UCS4_isalpha(thing)) return sqALPHA;
4398 	if (UT_UCS4_ispunct(thing)) return sqOTHERPUNCT;
4399 	if (UT_UCS4_isspace(thing)) return sqWHITE;
4400 
4401 	return sqBREAK;  // supposed to be a character, but...!
4402 }
4403 
4404 struct sqTable
4405 {
4406 	enum sqThingAt before;
4407 	UT_UCSChar thing;
4408 	enum sqThingAt after;
4409 	UT_UCSChar replacement;
4410 };
4411 // The idea of the table is to drive the algorithm without lots of
4412 // cluttery code.  Something using this table pre-computes what the
4413 // things are before and after the character in question, and then
4414 // dances through this table looking for a match on all three.
4415 // The final item in each row is the character to use to replace
4416 // the candidate character.
4417 //
4418 // (Yeah, this table is big, but it is only used when a quote character
4419 // shows up in typing or in a paste, and it goes pretty fast.)
4420 //
4421 // sqDONTCARE is like a wild card for the thing before or after, and
4422 // UCS_UNKPUNK in the replacement position means don't do a replacement.
4423 static struct sqTable sqTable_en[] =
4424 {
4425 	{sqALPHA,     '\'', sqALPHA,      UCS_RQUOTE},          // rule 0
4426 	{sqALPHA,     '`',  sqALPHA,      UCS_RQUOTE},          // rule 0
4427 
4428 	{sqQUOTEld,   '\'', sqDONTCARE,   UCS_LQUOTE},          // rule 1
4429 	{sqQUOTErd,   '\'', sqDONTCARE,   UCS_RQUOTE},          // rule 1
4430 
4431 	{sqQUOTEld,   '`',  sqDONTCARE,   UCS_LQUOTE},          // rule 1
4432 	{sqQUOTErd,   '`',  sqDONTCARE,   UCS_RQUOTE},          // rule 1
4433 
4434 	{sqQUOTEls,   '"',  sqDONTCARE,   UCS_LDBLQUOTE},       // rule 1
4435 	{sqQUOTErs,   '"',  sqDONTCARE,   UCS_RDBLQUOTE},       // rule 1
4436 
4437 	{sqQUOTEls,   '\'', sqDONTCARE,   UCS_RQUOTE},          // rule 2
4438 	{sqQUOTErs,   '\'', sqDONTCARE,   UCS_LQUOTE},          // rule 2
4439 
4440 	{sqQUOTEls,   '`',  sqDONTCARE,   UCS_RQUOTE},          // rule 2
4441 	{sqQUOTErs,   '`',  sqDONTCARE,   UCS_LQUOTE},          // rule 2
4442 
4443 	{sqQUOTEld,   '"',  sqDONTCARE,   UCS_RDBLQUOTE},       // rule 2
4444 	{sqQUOTErd,   '"',  sqDONTCARE,   UCS_LDBLQUOTE},       // rule 2
4445 
4446 	{sqDONTCARE,   '\'', sqQUOTEld,   UCS_LQUOTE},          // rule 3
4447 	{sqDONTCARE,   '\'', sqQUOTErd,   UCS_RQUOTE},          // rule 3
4448 
4449 	{sqDONTCARE,   '`',  sqQUOTEld,   UCS_LQUOTE},          // rule 3
4450 	{sqDONTCARE,   '`',  sqQUOTErd,   UCS_RQUOTE},          // rule 3
4451 
4452 	{sqDONTCARE,   '"',  sqQUOTEls,   UCS_LDBLQUOTE},       // rule 3
4453 	{sqDONTCARE,   '"',  sqQUOTErs,   UCS_RDBLQUOTE},       // rule 3
4454 
4455 	{sqDONTCARE,   '\'', sqQUOTEls,   UCS_RQUOTE},          // rule 4
4456 	{sqDONTCARE,   '\'', sqQUOTErs,   UCS_LQUOTE},          // rule 4
4457 
4458 	{sqDONTCARE,   '`',  sqQUOTEls,   UCS_RQUOTE},          // rule 4
4459 	{sqDONTCARE,   '`',  sqQUOTErs,   UCS_LQUOTE},          // rule 4
4460 
4461 	{sqDONTCARE,   '"',  sqQUOTEld,   UCS_RDBLQUOTE},       // rule 4
4462 	{sqDONTCARE,   '"',  sqQUOTErd,   UCS_LDBLQUOTE},       // rule 4
4463 
4464 	{sqOPENPUNCT,  '\'', sqDONTCARE,  UCS_LQUOTE},          // rule 5
4465 	{sqOPENPUNCT,  '`',  sqDONTCARE,  UCS_LQUOTE},          // rule 5
4466 	{sqOPENPUNCT,  '"',  sqDONTCARE,  UCS_LDBLQUOTE},       // rule 5
4467 
4468 	{sqDONTCARE, '\'', sqCLOSEPUNCT,  UCS_RQUOTE},          // rule 6
4469 	{sqDONTCARE, '`',  sqCLOSEPUNCT,  UCS_RQUOTE},          // rule 6
4470 	{sqDONTCARE, '"',  sqCLOSEPUNCT,  UCS_RDBLQUOTE},       // rule 6
4471 
4472 	{sqBREAK,      '\'', sqBREAK,     UCS_LQUOTE},         // rule 7
4473 	{sqWHITE,      '\'', sqBREAK,     UCS_LQUOTE},         // rule 7
4474 	{sqBREAK,      '\'', sqWHITE,     UCS_UNKPUNK},         // rule 7
4475 	{sqWHITE,      '\'', sqWHITE,     UCS_UNKPUNK},         // rule 7
4476 
4477 	{sqBREAK,      '`',  sqBREAK,     UCS_LQUOTE},         // rule 7
4478 	{sqWHITE,      '`',  sqBREAK,     UCS_LQUOTE},         // rule 7
4479 	{sqBREAK,      '`',  sqWHITE,     UCS_UNKPUNK},         // rule 7
4480 	{sqWHITE,      '`',  sqWHITE,     UCS_UNKPUNK},         // rule 7
4481 
4482 	{sqBREAK,      '"',  sqBREAK,     UCS_LDBLQUOTE},         // rule 7
4483 	{sqWHITE,      '"',  sqBREAK,     UCS_LDBLQUOTE},         // rule 7
4484 	{sqBREAK,      '"',  sqWHITE,     UCS_LDBLQUOTE},         // rule 7
4485 	{sqWHITE,      '"',  sqWHITE,     UCS_LDBLQUOTE},         // rule 7
4486 
4487 	{sqBREAK,      '\'', sqDONTCARE,  UCS_LQUOTE},          // rule 8
4488 	{sqWHITE,      '\'', sqDONTCARE,  UCS_LQUOTE},          // rule 8
4489 
4490 	{sqBREAK,      '`',  sqDONTCARE,  UCS_LQUOTE},          // rule 8
4491 	{sqWHITE,      '`',  sqDONTCARE,  UCS_LQUOTE},          // rule 8
4492 
4493 	{sqBREAK,      '"',  sqDONTCARE,  UCS_LDBLQUOTE},       // rule 8
4494 	{sqWHITE,      '"',  sqDONTCARE,  UCS_LDBLQUOTE},       // rule 8
4495 
4496 	{sqDONTCARE,   '\'', sqBREAK,     UCS_RQUOTE},          // rule 9
4497 	{sqDONTCARE,   '\'', sqWHITE,     UCS_RQUOTE},          // rule 9
4498 	{sqDONTCARE,   '\'', sqFOLLOWPUNCT, UCS_RQUOTE},          // rule 9
4499 
4500 	{sqDONTCARE,   '`',  sqBREAK,     UCS_RQUOTE},          // rule 9
4501 	{sqDONTCARE,   '`',  sqWHITE,     UCS_RQUOTE},          // rule 9
4502 	{sqDONTCARE,   '`',  sqFOLLOWPUNCT, UCS_RQUOTE},          // rule 9
4503 
4504 	{sqDONTCARE,   '"',  sqBREAK,     UCS_RDBLQUOTE},       // rule 9
4505 	{sqDONTCARE,   '"',  sqWHITE,     UCS_RDBLQUOTE},       // rule 9
4506 	{sqDONTCARE,   '"',  sqFOLLOWPUNCT, UCS_RDBLQUOTE},       // rule 9
4507 
4508 	// following rules are the same as falling off the end of the list...
4509 
4510 	//{sqDONTCARE,   '\'', sqDONTCARE,   UCS_UNKPUNK},        // rule 10
4511 	//{sqDONTCARE,   '`',  sqDONTCARE,   UCS_UNKPUNK},        // rule 10
4512 	//{sqDONTCARE,   '"',  sqDONTCARE,   UCS_UNKPUNK},        // rule 10
4513 
4514 	{sqDONTCARE, 0, sqDONTCARE, UCS_UNKPUNK}  // signals end of table
4515 };
4516 
considerSmartQuoteCandidateAt(fl_BlockLayout * block,UT_uint32 offset)4517 void FL_DocLayout::considerSmartQuoteCandidateAt(fl_BlockLayout *block, UT_uint32 offset)
4518 {
4519 	if (!block)
4520 		return;
4521 	if (m_pView->isHdrFtrEdit())
4522 		return;
4523 	if (!getSmartQuotes())
4524 		return;
4525 	if (!m_pView->m_bAllowSmartQuoteReplacement)
4526 		return;
4527 
4528 	setPendingSmartQuote(NULL, 0);  // avoid recursion
4529 	UT_GrowBuf pgb(1024);
4530 	block->getBlockBuf(&pgb);
4531 	// this is for the benefit of the UT_DEBUGMSG and should be changed to
4532 	// something other than '?' if '?' ever shows up as UT_isSmartQuotableCharacter()
4533 	UT_UCSChar c = '?';
4534 	if (pgb.getLength() > offset) c = *pgb.getPointer(offset);
4535 	UT_DEBUGMSG(("FL_DocLayout::considerSmartQuoteCandidateAt(%p, %d)  |%c|\n", block, offset, c));
4536 
4537 	//  there are some operations that leave a dangling pending
4538 	//  smart quote, so just double check before plunging onward
4539 	if (UT_isSmartQuotableCharacter(c))
4540 	{
4541 		enum sqThingAt before = sqBREAK, after = sqBREAK;
4542 		if (offset > 0)
4543 		{
4544 			// TODO: is there a need to see if this is on a run boundary?
4545 			// TODO: Within a block, are there runs that are significant
4546 			// TODO: breaks or whatever?
4547 			before = whatKindOfChar(*pgb.getPointer(offset - 1));
4548 		}
4549 		else
4550 		{
4551 			// candidate was the first character in the block, so
4552 			// see what was at the end of the previous block, if any
4553 			fl_BlockLayout *ob = static_cast<fl_BlockLayout *>(block->getPrev());
4554 			if (ob)
4555 			{
4556 				fp_Run *last, *r = ob->getFirstRun();
4557 				do
4558 				{
4559 					last = r;
4560 				} while ((r = r->getNextRun()));  // assignment
4561 				if (last  &&  (FPRUN_TEXT == last->getType())  &&  last->getLength() > 0)
4562 				{
4563 					fp_Line *blocks_line, *lasts_line;
4564 					blocks_line = block->getFirstRun()->getLine();
4565 					lasts_line = last->getLine();
4566 					if (blocks_line == lasts_line)
4567 					{
4568 						// last run of previous block was a text run on the same line
4569 						// so find out what the final character was
4570 						UT_GrowBuf pgb_b(1024);
4571 						ob->getBlockBuf(&pgb_b);
4572 						if (pgb_b.getLength())
4573 						{
4574 							before = whatKindOfChar(*pgb_b.getPointer(pgb_b.getLength()-1));
4575 						}
4576 					}
4577 				}
4578 			}
4579 		}
4580 
4581 		if (offset+1 < pgb.getLength())
4582 		{
4583 			// TODO: is there a need to see if this is on a run boundary?
4584 			// TODO: Within a block, are there runs that are significant
4585 			// TODO: breaks or whatever?
4586 			after = whatKindOfChar(*pgb.getPointer(offset + 1));
4587 		}
4588 		else
4589 		{
4590 			// candidate was the last character in a block, so see
4591 			// what's at the beginning of the next block, if any
4592 			fl_BlockLayout *ob = static_cast<fl_BlockLayout *>(block->getNext());
4593 			if (ob)
4594 			{
4595 				fp_Run *r = ob->getFirstRun();
4596 				if (r  &&  (FPRUN_TEXT == r->getType()))
4597 				{
4598 					// first run of next block is a text run, so
4599 					// see what the first character was
4600 					UT_GrowBuf pgb_a(1024);
4601 					ob->getBlockBuf(&pgb_a);
4602 					if (pgb_a.getLength())
4603 					{
4604 						after = whatKindOfChar(*pgb_a.getPointer(0));
4605 					}
4606 				}
4607 			}
4608 		}
4609 
4610 		// we now know what the before and after things are, so
4611 		// spin through the table.
4612 		UT_UCSChar replacement = UCS_UNKPUNK;  // means don't replace
4613 		// TODO:  select a table based on default locale or on the locale
4614 		// TODO:  of the fragment of text we're working in (locale tagging
4615 		// TODO:  of text doesn't exist in Abi as of this writing)
4616 		struct sqTable *table = sqTable_en;
4617 		for (unsigned int tdex=0; table[tdex].thing; ++tdex)
4618 		{
4619 			if (c != table[tdex].thing) continue;
4620 			if (table[tdex].before == sqDONTCARE  ||  table[tdex].before == before)
4621 			{
4622 				if (table[tdex].after == sqDONTCARE  ||  table[tdex].after == after)
4623 				{
4624 					replacement = table[tdex].replacement;
4625 					break;
4626 				}
4627 			}
4628 		}
4629 		UT_DEBUGMSG(("before %d, after %d, replace %x\n", before, after, replacement));
4630 		if (replacement != UCS_UNKPUNK)
4631 		{
4632             //UT_sint32 s1,s2,s3,s4,s5;
4633             //bool b1;
4634             //fp_Run * pThisRun = block->findPointCoords(block->getPosition() + offset,false,s1,s2,s3,s4,s5,b1);
4635 
4636             xxx_UT_DEBUGMSG(("pThisRun [0x%x], vis dir. %d\n", pThisRun, pThisRun->getVisDirection()));
4637 
4638 			gint nOuterQuoteStyleIndex = 0; // Default to English
4639 			gint nInnerQuoteStyleIndex = 1; // Default to English
4640 			bool bUseCustomQuotes = false;
4641 			bool bOK2 = true;
4642 
4643 			// 1st - See if we should use custom quotes
4644 			if (m_pPrefs)
4645 			{
4646 				bOK2 = m_pPrefs->getPrefsValueBool( static_cast<const gchar *>(XAP_PREF_KEY_CustomSmartQuotes), &bUseCustomQuotes );
4647 				if (bOK2 && bUseCustomQuotes)
4648 				{
4649 					bool bOK1 = m_pPrefs->getPrefsValueInt( static_cast<const gchar *>(XAP_PREF_KEY_OuterQuoteStyle), nOuterQuoteStyleIndex );
4650 					if (!bOK1)
4651 					{
4652 						nOuterQuoteStyleIndex = 0; // English if bad
4653 					}
4654 					else
4655 					{
4656 						bool bOK = m_pPrefs->getPrefsValueInt( static_cast<const gchar *>(XAP_PREF_KEY_InnerQuoteStyle), nInnerQuoteStyleIndex );
4657 						if (!bOK)
4658 						{
4659 							nInnerQuoteStyleIndex = 1; // English if bad
4660 						}
4661 					}
4662 				}
4663 			}
4664 
4665 			// 2nd - Not using custom quotes, look up doc lang
4666 			if (!bOK2 || !bUseCustomQuotes)
4667 			{
4668 				const char * pszLang = NULL;
4669 				const gchar ** props_in = NULL;
4670 				if (m_pView->getCharFormat(&props_in))
4671 				{
4672 					pszLang = UT_getAttribute("lang", props_in);
4673 					FREEP(props_in);
4674 				}
4675 
4676 				if (pszLang && *pszLang)
4677 				{
4678 					const XAP_LangInfo* found = XAP_EncodingManager::findLangInfoByLocale(pszLang);
4679 
4680 					if (found)
4681 					{
4682 						nOuterQuoteStyleIndex = found->outerQuoteIdx;
4683 						nInnerQuoteStyleIndex = found->innerQuoteIdx;
4684 					}
4685 				}
4686 			}
4687 
4688 			// 3rd - bad thing happened, go with English
4689 			if (nOuterQuoteStyleIndex < 0 || nInnerQuoteStyleIndex < 0)
4690 			{
4691 				nOuterQuoteStyleIndex = 0;
4692 				nInnerQuoteStyleIndex = 1;
4693 			}
4694 			bool bNoChange = false;
4695 			switch (replacement)
4696 			{
4697 			case UCS_LQUOTE:
4698 				replacement = XAP_EncodingManager::smartQuoteStyles[nInnerQuoteStyleIndex].leftQuote;
4699 				bNoChange = (replacement == c);
4700 				break;
4701 			case UCS_RQUOTE:
4702 				replacement = XAP_EncodingManager::smartQuoteStyles[nInnerQuoteStyleIndex].rightQuote;
4703 				bNoChange = (replacement == c);
4704 				break;
4705 			case UCS_LDBLQUOTE:
4706 				replacement = XAP_EncodingManager::smartQuoteStyles[nOuterQuoteStyleIndex].leftQuote;
4707 				bNoChange = (replacement == c);
4708 				break;
4709 			case UCS_RDBLQUOTE:
4710 				replacement = XAP_EncodingManager::smartQuoteStyles[nOuterQuoteStyleIndex].rightQuote;
4711 				bNoChange = (replacement == c);
4712 				break;
4713 			}
4714 			if(bNoChange)
4715 			{
4716 			      UT_DEBUGMSG(("No change detected \n"));
4717 			      return ;
4718 			}
4719 			// your basic emacs (save-excursion...)  :-)
4720 			PT_DocPosition saved_pos, quotable_at;
4721 			saved_pos = m_pView->getPoint();
4722 			quotable_at = block->getPosition(false) + offset;
4723 
4724 			m_pView->moveInsPtTo(quotable_at);
4725 			// delete/insert create change records for UNDO
4726 			m_pView->cmdSelectNoNotify(quotable_at, quotable_at + 1);
4727 			m_pView->cmdCharInsert(&replacement, 1);
4728 			m_pView->moveInsPtTo(saved_pos);
4729 		}
4730 	}
4731 }
4732 
notifyBlockIsBeingDeleted(fl_BlockLayout * pBlock)4733 void FL_DocLayout::notifyBlockIsBeingDeleted(fl_BlockLayout *pBlock)
4734 {
4735 	if(pBlock == m_pPendingBlockForSpell)
4736 	{
4737 		m_pPendingBlockForSpell = NULL;
4738 	}
4739 
4740 	if(pBlock == m_pPendingBlockForSmartQuote)
4741 	{
4742 		m_pPendingBlockForSmartQuote = NULL;
4743 	}
4744 #ifdef ENABLE_SPELL
4745 	pBlock->dequeueFromSpellCheck();
4746 #endif
4747 }
4748 
getListByID(UT_uint32 id) const4749 inline fl_AutoNum * FL_DocLayout::getListByID(UT_uint32 id) const
4750 {
4751 	return m_pDoc->getListByID(id);
4752 }
4753 
getNthList(UT_uint32 i) const4754 inline fl_AutoNum * FL_DocLayout::getNthList(UT_uint32 i) const
4755 {
4756 	return m_pDoc->getNthList(i);
4757 }
4758 
getListsCount(void) const4759 inline UT_uint32 FL_DocLayout::getListsCount(void) const
4760 {
4761 	return m_pDoc->getListsCount();
4762 }
4763 
addList(fl_AutoNum * pAutoNum)4764 inline void FL_DocLayout::addList(fl_AutoNum * pAutoNum)
4765 {
4766 	m_pDoc->addList(pAutoNum);
4767 }
4768 
notifyListeners(AV_ChangeMask mask)4769 void FL_DocLayout::notifyListeners(AV_ChangeMask mask)
4770 {
4771 	if (m_pView)
4772 		m_pView->notifyListeners(mask);
4773 }
4774