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