1 /* -*- c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- */
2 /* AbiWord
3  * Copyright (C) 1998-2000 AbiSource, Inc.
4  * Copyright (c) 2001,2002 Tomas Frydrych
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <locale.h>
31 
32 #include "ut_assert.h"
33 #include "ut_debugmsg.h"
34 #include "ut_growbuf.h"
35 #include "ut_misc.h"
36 #include "ut_string.h"
37 #include "ut_bytebuf.h"
38 #include "ut_timer.h"
39 #include "ut_types.h"
40 #include "xav_View.h"
41 #include "fl_DocLayout.h"
42 #include "fl_BlockLayout.h"
43 #include "fl_TOCLayout.h"
44 #ifdef ENABLE_SPELL
45 #include "fl_Squiggles.h"
46 #endif
47 #include "fl_SectionLayout.h"
48 #include "fl_AutoNum.h"
49 #include "fp_Page.h"
50 #include "fp_PageSize.h"
51 #include "fp_Column.h"
52 #include "fp_TableContainer.h"
53 #include "fp_Line.h"
54 #include "fp_Run.h"
55 #include "fp_TextRun.h"
56 #include "fg_Graphic.h"
57 #include "fg_GraphicRaster.h"
58 #include "pd_Document.h"
59 #include "pd_DocumentRDF.h"
60 #include "pd_Style.h"
61 #include "pp_Property.h"
62 #include "pp_AttrProp.h"
63 #include "gr_Graphics.h"
64 #include "gr_DrawArgs.h"
65 #include "ie_types.h"
66 #include "xap_App.h"
67 #include "xap_Frame.h"
68 #include "xap_Clipboard.h"
69 #include "ap_TopRuler.h"
70 #include "ap_LeftRuler.h"
71 #include "ap_Prefs.h"
72 #include "ap_Strings.h"
73 #include "fd_Field.h"
74 #include "fv_ViewDoubleBuffering.h"
75 
76 #ifdef ENABLE_SPELL
77 #include "spell_manager.h"
78 #if 1
79 // todo: work around to remove the INPUTWORDLEN restriction for pspell
80 #include "ispell_def.h"
81 #endif
82 #endif
83 
84 #include "ut_rand.h"
85 #include "fl_FootnoteLayout.h"
86 #include "pp_Revision.h"
87 #include "gr_Painter.h"
88 
89 #include "fv_View.h"
90 
91 // NB -- irrespective of this size, the piecetable will store
92 // at max BOOKMARK_NAME_LIMIT of chars as defined in pf_Frag_Bookmark.h
93 #define BOOKMARK_NAME_SIZE 30
94 #define CHECK_WINDOW_SIZE if(getWindowHeight() < 20) return;
95 // returns true iff the character BEFORE pos is a space.
96 // Special cases:
97 // -returns true if pos is at the beginning of the document
98 // -returns false if pos is not within the document
_isSpaceBefore(PT_DocPosition pos) const99 bool FV_View::_isSpaceBefore(PT_DocPosition pos) const
100 {
101 	UT_GrowBuf buffer;
102 
103 	fl_BlockLayout * block = m_pLayout->findBlockAtPosition(pos);
104 	if (block)
105 	{
106 
107 		PT_DocPosition offset = pos - block->getPosition(false);
108 		// Just look at the previous character in this block, if there is one...
109 		if (offset > 0)
110 		{
111 			block->getBlockBuf(&buffer);
112 			return (UT_UCS4_isspace(*reinterpret_cast<UT_UCSChar *>(buffer.getPointer(offset - 1))));
113 		}
114 		else
115 		{
116 			return true;
117 		}
118 	}
119 	else
120 		return false;
121 }
122 
123 
124 /*!
125   Move point to requested end of selection and clear selection
126   \param bForward True if point should be moved to the forward position
127 
128   \note Do not draw the insertion point after clearing the
129 		selection.
130   \fixme BIDI broken?
131 */
_moveToSelectionEnd(bool bForward)132 void FV_View::_moveToSelectionEnd(bool bForward)
133 {
134 	UT_ASSERT(!isSelectionEmpty());
135 
136 	PT_DocPosition curPos = getPoint();
137 	UT_ASSERT(curPos != m_Selection.getSelectionAnchor());
138 	bool bForwardSelection = (m_Selection.getSelectionAnchor() < curPos);
139 
140 	if (bForward != bForwardSelection)
141 	{
142 		swapSelectionOrientation();
143 	}
144 
145 	_clearSelection();
146 
147 	return;
148 }
149 
_eraseSelection(void)150 void FV_View::_eraseSelection(void)
151 {
152 	_fixInsertionPointCoords();
153 	if (!m_Selection.isSelected())
154 	{
155 		_resetSelection();
156 		return;
157 	}
158 
159 	UT_uint32 iPos1, iPos2;
160 
161 	if (m_Selection.getSelectionAnchor() < getPoint())
162 	{
163 		iPos1 = m_Selection.getSelectionAnchor();
164 		iPos2 = getPoint();
165 	}
166 	else
167 	{
168 		iPos1 = getPoint();
169 		iPos2 = m_Selection.getSelectionAnchor();
170 	}
171 	m_iLowDrawPoint = 0;
172 	m_iHighDrawPoint = 0;
173 
174 	_clearBetweenPositions(iPos1, iPos2, true);
175 }
176 
_clearSelection(bool bRedraw)177 void FV_View::_clearSelection(bool bRedraw)
178 {
179 	if( isSelectionEmpty() )
180 	{
181 		return;
182 	}
183 	if (m_pG)
184 		m_pG->allCarets()->enable();
185 
186 	_fixInsertionPointCoords();
187 	if (!m_Selection.isSelected())
188 	{
189 		_resetSelection();
190 		return;
191 	}
192 
193 	UT_uint32 iPos1, iPos2;
194 	if(m_Selection.getSelectionMode() < FV_SelectionMode_Multiple)
195 	{
196 		if (m_Selection.getSelectionAnchor() < getPoint())
197 		{
198 			iPos1 = m_Selection.getSelectionAnchor();
199 			iPos2 = getPoint();
200 		}
201 		else
202 		{
203 			iPos1 = getPoint();
204 			iPos2 = m_Selection.getSelectionAnchor();
205 		}
206 		bool bres = _clearBetweenPositions(iPos1, iPos2, true);
207 		if(!bres)
208 			return;
209 
210 		_resetSelection();
211 		m_iLowDrawPoint = 0;
212 		m_iHighDrawPoint = 0;
213 
214 		if (bRedraw)
215 			_drawBetweenPositions(iPos1, iPos2);
216 	}
217 	else
218 	{
219 		UT_sint32 i = 0;
220 		UT_GenericVector<PD_DocumentRange *> vecRanges;
221 
222 		for(i=0; i<m_Selection.getNumSelections();i++)
223 		{
224 			PD_DocumentRange * pTmp =m_Selection.getNthSelection(i);
225 			PD_DocumentRange * pTmp2 = new PD_DocumentRange(m_pDoc,pTmp->m_pos1,pTmp->m_pos2);
226 			vecRanges.addItem(pTmp2);
227 		}
228 		for(i=0; i< vecRanges.getItemCount();i++)
229 		{
230 			PD_DocumentRange * pDocR = vecRanges.getNthItem(i);
231 			if(pDocR)
232 			{
233 				iPos1 = pDocR->m_pos1;
234 				iPos2 = pDocR->m_pos2;
235 				if(iPos1 == iPos2)
236 				{
237 					iPos2++;
238 				}
239 
240 				if (bRedraw)
241 					/*bool bres =*/ _clearBetweenPositions(iPos1, iPos2, true);
242 			}
243 		}
244 		_resetSelection();
245 		for(i=0; i< vecRanges.getItemCount();i++)
246 		{
247 			PD_DocumentRange * pDocR = vecRanges.getNthItem(i);
248 			if(pDocR)
249 			{
250 				iPos1 = pDocR->m_pos1;
251 				iPos2 = pDocR->m_pos2;
252 				if(iPos1 == iPos2)
253 				{
254 					iPos2++;
255 				}
256 				if (bRedraw)
257 					_drawBetweenPositions(iPos1, iPos2);
258 			}
259 		}
260 		UT_VECTOR_PURGEALL(PD_DocumentRange *,vecRanges);
261 	}
262 	_resetSelection();
263 	m_iLowDrawPoint = 0;
264 	m_iHighDrawPoint = 0;
265 }
266 
_drawSelection()267 void FV_View::_drawSelection()
268 {
269 	UT_return_if_fail(!isSelectionEmpty());
270 //	CHECK_WINDOW_SIZE
271 	UT_DEBUGMSG(("_drawSelection getPoint() %d m_Selection.getSelectionAnchor() %d \n",getPoint(),m_Selection.getSelectionAnchor()));
272 	if(m_Selection.getSelectionMode() < FV_SelectionMode_Multiple)
273 	{
274 		if (m_Selection.getSelectionAnchor() < getPoint())
275 		{
276 			_drawBetweenPositions(m_Selection.getSelectionAnchor(), getPoint());
277 		}
278 		else
279 		{
280 			_drawBetweenPositions(getPoint(), m_Selection.getSelectionAnchor());
281 		}
282 		m_iLowDrawPoint = UT_MIN(m_Selection.getSelectionAnchor(),getPoint());
283 		m_iHighDrawPoint = UT_MAX(m_Selection.getSelectionAnchor(),getPoint());
284 	}
285 	else
286 	{
287 		UT_sint32 i = 0;
288 		for(i=0; i<m_Selection.getNumSelections();i++)
289 		{
290 			PD_DocumentRange * pDocR = m_Selection.getNthSelection(i);
291 			UT_DEBUGMSG(("Drawing between %d and %d \n",pDocR->m_pos1,pDocR->m_pos2));
292 			if(pDocR)
293 			{
294 				PT_DocPosition iPos1 = pDocR->m_pos1;
295 				PT_DocPosition iPos2 = pDocR->m_pos2;
296 				if(iPos1 == iPos2)
297 				{
298 					iPos2++;
299 				}
300 				_drawBetweenPositions(iPos1, iPos2);
301 			}
302 		}
303 		m_iLowDrawPoint = 0;
304 		m_iHighDrawPoint = 0;
305 	}
306 
307 }
308 
309 // Note that isClearSelection() might change its tune in one of two ways.
310 // Way #1 is by calling one of the next few methods.
311 //   BUT! this never happens because m_Selection.getSelectionAnchor() == getPoint by def.
312 // Way #2 is if the Selection is set and the point is changed so that it
313 //          no longer equals m_Selection.getSelectionAnchor().
_resetSelection(void)314 void FV_View::_resetSelection(void)
315 {
316 	m_Selection.clearSelection();
317 	m_Selection.setSelectionAnchor(getPoint());
318 	m_Selection.setSelectionLeftAnchor(getPoint());
319 	m_Selection.setSelectionRightAnchor(getPoint());
320 	m_iGrabCell = 0;
321 }
322 
_setSelectionAnchor(void)323 void FV_View::_setSelectionAnchor(void)
324 {
325 	m_Selection.setMode(FV_SelectionMode_Single);
326 	m_Selection.setSelectionAnchor(getPoint());
327 }
328 
_deleteSelection(PP_AttrProp * p_AttrProp_Before,bool bNoUpdate,bool bCaretLeft)329 void FV_View::_deleteSelection(PP_AttrProp *p_AttrProp_Before, bool bNoUpdate,
330 							   bool bCaretLeft)
331 {
332 	// delete the current selection.
333 	// NOTE: this must clear the selection.
334 
335 	UT_ASSERT(!isSelectionEmpty());
336 
337 	PT_DocPosition iPoint = getPoint();
338 
339 	UT_uint32 iRealDeleteCount;
340 
341 	UT_uint32 iSelAnchor = m_Selection.getSelectionAnchor();
342 	if(iSelAnchor < 2)
343 	{
344 		if(!m_pDoc->isTableAtPos(iSelAnchor))
345 			iSelAnchor = 2;
346 	}
347 	if(m_FrameEdit.isActive())
348 	{
349 	        deleteFrame();
350 		return;
351 	}
352 	UT_ASSERT(iPoint != iSelAnchor);
353 
354 	PT_DocPosition iLow = UT_MIN(iPoint,iSelAnchor);
355 	PT_DocPosition iHigh = UT_MAX(iPoint,iSelAnchor);
356 
357 	// deal with character clusters, such as base char + vowel + tone mark in Thai
358 	UT_uint32 iLen = iHigh - iLow;
359 	_adjustDeletePosition(iLow, iLen); // modifies both iLow and iLen
360 	iHigh = iLow + iLen;
361 //
362 // OK adjust for deletetions that cross footnote/endnote boundaries.
363 //
364 	fl_FootnoteLayout * pFHigh = NULL;
365 	fl_FootnoteLayout * pFLow = NULL;
366 	fl_EndnoteLayout * pEHigh = NULL;
367 	fl_EndnoteLayout * pELow = NULL;
368 	if(isInFootnote(iHigh))
369 	{
370 		pFHigh = getClosestFootnote(iHigh);
371 		PT_DocPosition j = pFHigh->getPosition()+1; // Leave reference
372 		if(j > iLow)
373 		{
374 			iLow = j;
375 		}
376 	}
377 	else if(isInFootnote(iLow))
378 	{
379 
380 // Here if we're not in footnote at iHigh
381 
382 		pFLow = getClosestFootnote(iLow);
383 		iHigh = pFLow->getPosition(true) + pFLow->getLength() -1;
384 	}
385 	else if(isInEndnote(iHigh))
386 	{
387 		pEHigh = getClosestEndnote(iHigh);
388 		PT_DocPosition j = pEHigh->getPosition()+1; // Leave reference
389 		if(j > iLow)
390 		{
391 			iLow = j;
392 		}
393 	}
394 	else if(isInEndnote(iLow))
395 	{
396 
397 // Here if we're not in Endnote at iHigh
398 
399 		pELow = getClosestEndnote(iLow);
400 		iHigh = pELow->getPosition(true) + pELow->getLength() -1;
401 	}
402 //
403 // Don't delete the block right before a TOC!
404 //
405 	fl_BlockLayout * pBL = _findBlockAtPosition(iLow);
406 	if(pBL && pBL->getPrev() && pBL->getPrev()->getContainerType() == FL_CONTAINER_TOC)
407 	{
408 		if(pBL->getPosition(true) == iLow)
409 		{
410 			iLow++;
411 		}
412 	}
413 	// fl_BlockLayout::getLength() *includes* the length of the block strux, hence we
414 	// should use getPosition(true) here; however, since the block is found at iLow, this
415 	// condition can never be true !!! -- Anybody knows what this is about?
416 	else if(pBL && (pBL->getPosition(true) + pBL->getLength() < iLow))
417 	{
418 		iLow++;
419 	}
420 	// Check if everything is selected from the beginning of the section to a table.
421 	// In that case, we want to delete also the block.
422 	if (pBL && !pBL->getPrev() && (pBL->myContainingLayout()->getContainerType() == FL_CONTAINER_DOCSECTION) &&
423 		isInTable(iHigh) && (pBL->getPosition(false) == iLow))
424 	{
425 		iLow--;
426 	}
427 	//
428 	// Handle end effects of table selection.
429 	//
430 	bool bDeleteTables = !isInTable(iLow) && !isInTable(iHigh);
431 	PT_DocPosition iLowTable = 0;
432 	PT_DocPosition iHighTable = 0;
433 	if(!bDeleteTables && isInTable(iLow))
434 	{
435 		if(m_pDoc->isTableAtPos(iLow))
436 		{
437 				iLowTable = iLow;
438 		}
439 		else if((iLow > 0) && m_pDoc->isTableAtPos(iLow-1))
440 		{
441 				iLowTable = iLow-1;
442 		}
443 		else if((iLow > 1) && m_pDoc->isTableAtPos(iLow-2))
444 		{
445 				iLowTable = iLow -2;
446 		}
447 		else if((iLow > 2) &&  m_pDoc->isTableAtPos(iLow-3))
448 		{
449 				iLowTable = iLow -3;
450 		}
451 		if(iLowTable > 0)
452 			iLow = iLowTable;
453 	}
454 	if(!bDeleteTables && isInTable(iHigh))
455 	{
456 			if(m_pDoc->isEndTableAtPos(iHigh))
457 			{
458 					iHighTable = iHigh+1;
459 			}
460 			if(m_pDoc->isEndTableAtPos(iHigh+1))
461 			{
462 					iHighTable = iHigh+2;
463 			}
464 			if(iHighTable > 0)
465 				iHigh = iHighTable;
466 	}
467 	if(!bDeleteTables && (iLowTable > 0) && (iHighTable > 0))
468 	{
469 			iHigh = iHighTable;
470 			iLow = iLowTable;
471 			bDeleteTables = true;
472 	}
473 	else if(!bDeleteTables && !isInTable(iLow) && (iHighTable > 0))
474 	{
475 			iHigh = iHighTable;
476 			bDeleteTables = true;
477 	}
478 	else if(!bDeleteTables && !isInTable(iHigh) && (iLowTable > 0))
479 	{
480 			iLow = iLowTable;
481 			bDeleteTables = true;
482 	}
483 	if(!isInFrame(iLow) && isInFrame(iHigh))
484 	{
485 		fl_FrameLayout * pFL = getFrameLayout(iHigh);
486 		iHigh =pFL->getPosition(true);
487 	}
488 	if(isInFrame(iLow) && !isInFrame(iHigh))
489 	{
490 		fl_FrameLayout * pFL = getFrameLayout(iLow);
491 		iHigh =pFL->getPosition(true) + pFL->getLength() -1;
492 	}
493 	if(m_pDoc->isFrameAtPos(iLow) && m_pDoc->isEndFrameAtPos(iHigh))
494 	{
495 	        iHigh++;
496 	}
497 	_resetSelection();
498 
499 	if(!bNoUpdate)
500 		_clearBetweenPositions(iLow, iHigh, true);
501 
502 	bool bOldDelete = m_pDoc->isDontImmediateLayout();
503 	if(bDeleteTables || bNoUpdate)
504 	{
505 		m_pDoc->setDontImmediatelyLayout(true);
506 	}
507 	m_pDoc->beginUserAtomicGlob();
508 	m_pDoc->deleteSpan(iLow, iHigh, p_AttrProp_Before, iRealDeleteCount, bDeleteTables);
509 	//
510 	// Handle case of no valid block because of hidden text
511 	//
512 	pBL = getCurrentBlock();
513 	if(pBL && (pBL->getNextBlockInDocument() == NULL) && (pBL->getPrevBlockInDocument() == NULL))
514 	{
515 	     if(pBL->isHidden() == FP_HIDDEN_TEXT)
516 	     {
517 	          const char * props[3] = {"display",NULL,NULL};
518 	          PT_DocPosition pos = pBL->getPosition();
519 		  PT_DocPosition posEnd = pos + pBL->getLength() -1;
520 	          m_pDoc->changeStruxFmt(PTC_RemoveFmt,pos,posEnd,NULL,props,PTX_Block);
521 		  m_pDoc->changeSpanFmt(PTC_RemoveFmt,pos,posEnd,NULL,props);
522 	     }
523 	}
524 //
525 // Stop any lists remaining if we've deleted their list fields
526 //
527 	PT_DocPosition origPos = getPoint();
528 	pBL = getCurrentBlock();
529 	if(!pBL)
530 	{
531 		// the user delete the entire document; we need to insert a new block
532 		// at origPos() (note that with revisions enabled / document history, this
533 		// position could be > 2).
534 		m_pDoc->insertStrux(origPos, PTX_Block);
535 	}
536 	else if(pBL->getPosition() == iLow)
537 	{
538 		pf_Frag_Strux* sdh = getCurrentBlock()->getStruxDocHandle();
539 		while(pBL->isListItem())
540 		{
541 			m_pDoc->StopList(sdh);
542 		}
543 	}
544 	if(bDeleteTables || bNoUpdate)
545 	{
546 		m_pDoc->setDontImmediatelyLayout(bOldDelete);
547 	}
548 //
549 // Can't leave list-tab on a line
550 //
551 	if(origPos != getPoint())
552 	{
553 		setPoint(origPos);
554 	}
555 	if(isTabListAheadPoint() == true)
556 	{
557 		UT_uint32 iRealDeleteCount2;
558 		m_pDoc->deleteSpan(getPoint(), getPoint()+2, p_AttrProp_Before, iRealDeleteCount2);
559 		iRealDeleteCount += iRealDeleteCount2;
560 	}
561 	m_pDoc->endUserAtomicGlob();
562 
563 	//special handling is required for delete in revisions mode
564 	//where we have to move the insertion point
565 	if(isMarkRevisions())
566 	{
567 		UT_ASSERT( iRealDeleteCount <= iHigh - iLow + 1 );
568 
569 		// if we are explicitely told to lef the caret on the left side of the
570 		// selection we do so, otherwise, if the point was on the left of the
571 		// original selection, we must adjust the point so that it is on the
572 		// left edge of the text to the right of what we deleted
573 		if(!bCaretLeft && iPoint == iLow)
574 			_charMotion(true,iHigh - iLow - iRealDeleteCount);
575 	}
576 //
577 // Make sure the insertion point is in a legal position
578 //
579 	PT_DocPosition posEnd = 0;
580 	getEditableBounds(true, posEnd);
581 	bool bOK = true;
582 	while(bOK && !isPointLegal() && getPoint() < posEnd)
583 	{
584 		bOK = _charMotion(true,1);
585 	}
586 	//
587 	// We could have gone too far!
588 	//
589 	if(getPoint() > posEnd)
590 	{
591 		setPoint(posEnd);
592 		PT_DocPosition posBeg = 0;
593 		getEditableBounds(false, posBeg);
594 		while(bOK && !isPointLegal() && getPoint()>=posBeg)
595 		{
596 			bOK = _charMotion(false,1);
597 		}
598 	}
599 
600 	m_pG->allCarets()->enable();
601 }
602 
603 /*!
604  * Do the merge between cells.
605  * If bBefore is true the contents of source will be prepended into destination otherwise
606  * will e appended to the end
607  */
_MergeCells(PT_DocPosition posDestination,PT_DocPosition posSource,bool)608 bool FV_View::_MergeCells( PT_DocPosition posDestination,PT_DocPosition posSource, bool /*bBefore*/)
609 {
610 //
611 // get coordinates of source and destination cells
612 //
613 	UT_sint32 sLeft,sRight,sTop,sBot;
614 	UT_sint32 dLeft,dRight,dTop,dBot;
615 	UT_sint32 fLeft,fRight,fTop,fBot;
616 	getCellParams(posSource,&sLeft,&sRight,&sTop,&sBot);
617 	getCellParams(posDestination,&dLeft,&dRight,&dTop,&dBot);
618 //
619 	fLeft = UT_MIN(sLeft,dLeft);
620 	fRight = UT_MAX(sRight,dRight);
621 	fTop = UT_MIN(sTop,dTop);
622 	fBot = UT_MAX(sBot,dBot);
623 
624 	PD_DocumentRange dr_source;
625 	pf_Frag_Strux* sourceSDH,*endSourceSDH,*destinationSDH,*endDestSDH;
626 	bool bres = m_pDoc->getStruxOfTypeFromPosition(posSource,PTX_SectionCell,&sourceSDH);
627 	if(!bres)
628 	{
629 		return false;
630 	}
631 	endSourceSDH =  m_pDoc->getEndCellStruxFromCellSDH(sourceSDH);
632 	PT_DocPosition posEndCell = m_pDoc->getStruxPosition(endSourceSDH)-1;
633 	posSource = m_pDoc->getStruxPosition(sourceSDH)+1;
634 	bres = m_pDoc->getStruxOfTypeFromPosition(posDestination,PTX_SectionCell,&destinationSDH);
635 	if(!bres)
636 	{
637 		return false;
638 	}
639 	endDestSDH = m_pDoc->getEndCellStruxFromCellSDH(destinationSDH);
640 	PT_DocPosition posEndDestCell = m_pDoc->getStruxPosition(endDestSDH);
641 //	if(!bBefore)
642 //	{
643 //		posDestination = posEndDestCell;
644 //	}
645 	m_pDoc->beginUserAtomicGlob();
646 	if(posEndCell > posSource)
647 	{
648 //
649 // OK got the doc range for the source. Set it and copy it.
650 //
651 		dr_source.set(m_pDoc,posSource,posEndCell+1);
652 //
653 // Copy to and from clipboard to populate the destination cell
654 //
655 		UT_DEBUGMSG(("SEVIOR: Copy to clipboard merging cells \n"));
656 		m_pApp->copyToClipboard(&dr_source);
657 	}
658 //
659 // Now delete the source cell. We can use the old source position since it
660 // just needs to point inside the table.
661 //
662 	_deleteCellAt(posSource,sTop,sLeft);
663 	if(posEndCell > posSource)
664 	{
665 //
666 // Now paste in the text from the source cell.
667 //
668 		PD_DocumentRange dr_dest(m_pDoc,posEndDestCell,posEndDestCell);
669 		UT_DEBUGMSG(("SEVIOR: Pasting from clipboard merging cells \n"));
670 		m_pApp->pasteFromClipboard(&dr_dest,true,true);
671 	}
672 //
673 // Expand the destination cell into the source cell
674 //
675 	_changeCellTo(posDestination,dTop,dLeft,fLeft,fRight,fTop,fBot);
676 	m_pDoc->endUserAtomicGlob();
677 
678 //
679 // We're done!
680 //
681 	return true;
682 }
683 
684 /*!
685  * This method is used to change a parameter of the table to trigger a table
686  * rebuild. It also restores all the nice needed for single step undo's
687  */
_restoreCellParams(PT_DocPosition posTable,UT_sint32 iLineType)688 bool FV_View::_restoreCellParams(PT_DocPosition posTable, UT_sint32 iLineType)
689 {
690 	const char * pszTable[3] = {NULL,NULL,NULL};
691 	pszTable[0] = "list-tag";
692 	UT_String sLineType;
693 	UT_String_sprintf(sLineType,"%d",iLineType);
694 	pszTable[1] = sLineType.c_str();
695 	UT_DEBUGMSG(("SEVIOR: Doing Table strux change of %s %s \n",pszTable[0],pszTable[1]));
696 	m_pDoc->setDontImmediatelyLayout(false);
697 	m_pDoc->changeStruxFmt(PTC_AddFmt,posTable,posTable,NULL,pszTable,PTX_SectionTable);
698 
699 //
700 // OK finish everything off with the various parameters which allow the formatter to
701 // be updated.
702 //
703 	m_pDoc->allowChangeInsPoint();
704 
705 	// restore updates and clean up dirty lists
706 	m_pDoc->enableListUpdates();
707 	m_pDoc->updateDirtyLists();
708 
709 	// Signal PieceTable Changes have finished
710 	_restorePieceTableState();
711 	_generalUpdate();
712 	m_pDoc->endUserAtomicGlob();
713 	return true;
714 }
715 
716 /*!
717  *  Change the parameters of the table.
718  * Return the line type of the table. We'll restore this later.
719  */
_changeCellParams(PT_DocPosition posTable,pf_Frag_Strux * tableSDH)720  UT_sint32 FV_View::_changeCellParams(PT_DocPosition posTable, pf_Frag_Strux* tableSDH)
721 {
722 	// Signal PieceTable Change
723 	_saveAndNotifyPieceTableChange();
724 
725 	// Turn off list updates
726 
727 	m_pDoc->disableListUpdates();
728 	m_pDoc->beginUserAtomicGlob();
729 	m_pDoc->setDontImmediatelyLayout(true);
730 	m_pDoc->setDontChangeInsPoint();
731 //
732 // Now trigger a rebuild of the whole table by sending a changeStrux to the table strux
733 // with a bogus line-type property. We'll restore it later.
734 //
735 	const char * pszTable[3] = {NULL,NULL,NULL};
736 	pszTable[0] = "list-tag";
737 	const char * szLineType = NULL;
738 	UT_String sLineType;
739 	UT_sint32 iLineType;
740 	m_pDoc->getPropertyFromSDH(tableSDH,isShowRevisions(),getRevisionLevel(),pszTable[0],&szLineType);
741 	if(szLineType == NULL || *szLineType == '\0')
742 	{
743 		iLineType = 0;
744 	}
745 	else
746 	{
747 		iLineType = atoi(szLineType);
748 		iLineType -= 1;
749 	}
750 	UT_String_sprintf(sLineType,"%d",iLineType);
751 	pszTable[1] = sLineType.c_str();
752 	UT_DEBUGMSG(("SEVIOR: Doing Table strux change of %s %s \n",pszTable[0],pszTable[1]));
753 	m_pDoc->changeStruxFmt(PTC_AddFmt,posTable,posTable,NULL,pszTable,PTX_SectionTable);
754 
755 	return iLineType;
756 }
757 
758 /*!
759  * This method deletes the cell at (row,col) in the table specified by posTable
760  */
_deleteCellAt(PT_DocPosition posTable,UT_sint32 row,UT_sint32 col)761 bool FV_View::_deleteCellAt(PT_DocPosition posTable, UT_sint32 row, UT_sint32 col)
762 {
763 	pf_Frag_Strux* cellSDH,*endCellSDH;
764 	PT_DocPosition posCell = findCellPosAt(posTable,row,col);
765 	if(posCell == 0)
766 	{
767 		return false;
768 	}
769 	bool bres = m_pDoc->getStruxOfTypeFromPosition(posCell+1,PTX_SectionCell,&cellSDH);
770 	if(!bres)
771 	{
772 		return false;
773 	}
774 	endCellSDH = m_pDoc->getEndCellStruxFromCellSDH(cellSDH);
775 	if(!endCellSDH)
776 	{
777 		return false;
778 	}
779 	PT_DocPosition posEndCell = m_pDoc->getStruxPosition(endCellSDH) +1;
780 	if(posEndCell == 0)
781 	{
782 		return false;
783 	}
784 //
785 // check that this
786 //
787 
788 //
789 // We trust that calling routine does all the things needed to make sure this goes
790 // smoothly and undo's in a single step.
791 // Here we just delete.
792 //
793 
794 //
795 // OK do the delete
796 //
797 	UT_uint32 iRealDeleteCount;
798 
799 	m_pDoc->deleteSpan( posCell, posEndCell, NULL,iRealDeleteCount,true);
800 
801 	// if in revisions mode, we might need to move the insertion point if it was within
802 	// the cell that we are deleting (since the cell stays physically in the document) but
803 	// the positions within it are now hidden from the user)
804 	if(isMarkRevisions() && m_iInsPoint > posCell && m_iInsPoint < posEndCell)
805 	{
806 		_setPoint(posEndCell);
807 	}
808 
809 	return true;
810 }
811 
812 
813 /*!
814  * This method changes the coordinates of the cell at (row,col) in the table specified by
815  * posTable to the cordinates specified.
816  */
_changeCellTo(PT_DocPosition posTable,UT_sint32 rowold,UT_sint32 colold,UT_sint32 left,UT_sint32 right,UT_sint32 top,UT_sint32 bot)817 bool FV_View::_changeCellTo(PT_DocPosition posTable, UT_sint32 rowold, UT_sint32 colold,
818 						  UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot)
819 {
820 	PT_DocPosition posCell = findCellPosAt(posTable,rowold,colold) + 1;
821 	if(posCell == 0)
822 	{
823 		return false;
824 	}
825 	const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
826 	UT_String sLeft,sRight,sTop,sBot;
827 	props[0] = "left-attach";
828 	UT_String_sprintf(sLeft,"%d",left);
829 	props[1] = sLeft.c_str();
830 	props[2] = "right-attach";
831 	UT_String_sprintf(sRight,"%d",right);
832 	props[3] = sRight.c_str();
833 	props[4] = "top-attach";
834 	UT_String_sprintf(sTop,"%d",top);
835 	props[5] = sTop.c_str();
836 	props[6] = "bot-attach";
837 	UT_String_sprintf(sBot,"%d",bot);
838 	props[7] = sBot.c_str();
839 
840 //
841 // Here we trust that the calling routine will do all the begin/end globbing and other
842 // stuff so that this will go smoothly and undo's in a single step.
843 // Here we just change
844 //
845 
846 	bool bres = m_pDoc->changeStruxFmt(PTC_AddFmt,posCell,posCell,NULL,props,PTX_SectionCell);
847 
848 	return bres;
849 }
850 
851 
852 /*!
853  * This method inserts a cell at PT_DocPosition with the given left, right, top and bottom attach.
854  */
_insertCellAt(PT_DocPosition posCell,UT_sint32 left,UT_sint32 right,UT_sint32 top,UT_sint32 bot,const gchar ** attrsBlock,const gchar ** propsBlock)855 bool FV_View::_insertCellAt(PT_DocPosition posCell, UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot,
856 							const gchar ** attrsBlock, const gchar ** propsBlock)
857 {
858 	const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
859 	UT_String sLeft,sRight,sTop,sBot;
860 	props[0] = "left-attach";
861 	UT_String_sprintf(sLeft,"%d",left);
862 	props[1] = sLeft.c_str();
863 	props[2] = "right-attach";
864 	UT_String_sprintf(sRight,"%d",right);
865 	props[3] = sRight.c_str();
866 	props[4] = "top-attach";
867 	UT_String_sprintf(sTop,"%d",top);
868 	props[5] = sTop.c_str();
869 	props[6] = "bot-attach";
870 	UT_String_sprintf(sBot,"%d",bot);
871 	props[7] = sBot.c_str();
872 
873 	bool bres = true;
874 	bres = m_pDoc->insertStrux(posCell,PTX_SectionCell,NULL,props);
875 	UT_return_val_if_fail(bres,false);
876 	bres = m_pDoc->insertStrux(posCell+1,PTX_Block,attrsBlock,propsBlock);
877 	UT_return_val_if_fail(bres,false);
878 	bres = m_pDoc->insertStrux(posCell+2,PTX_EndCell);
879 	return bres;
880 }
881 
882 
883 /*!
884   This method changes the attaches of the cell at position posCell
885  */
_changeCellAttach(PT_DocPosition posCell,UT_sint32 left,UT_sint32 right,UT_sint32 top,UT_sint32 bot)886 bool FV_View::_changeCellAttach(PT_DocPosition posCell, UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot)
887 {
888 	const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
889 	UT_String sLeft,sRight,sTop,sBot;
890 	props[0] = "left-attach";
891 	UT_String_sprintf(sLeft,"%d",left);
892 	props[1] = sLeft.c_str();
893 	props[2] = "right-attach";
894 	UT_String_sprintf(sRight,"%d",right);
895 	props[3] = sRight.c_str();
896 	props[4] = "top-attach";
897 	UT_String_sprintf(sTop,"%d",top);
898 	props[5] = sTop.c_str();
899 	props[6] = "bot-attach";
900 	UT_String_sprintf(sBot,"%d",bot);
901 	props[7] = sBot.c_str();
902 
903 	bool bres = m_pDoc->changeStruxFmt(PTC_AddFmt,posCell,posCell,NULL,props,PTX_SectionCell);
904 	return bres;
905 }
906 
907 
_getDocPos(FV_DocPos dp,bool bKeepLooking) const908 PT_DocPosition FV_View::_getDocPos(FV_DocPos dp, bool bKeepLooking) const
909 {
910 	return _getDocPosFromPoint(getPoint(),dp,bKeepLooking);
911 }
912 
_getDocPosFromPoint(PT_DocPosition iPoint,FV_DocPos dp,bool bKeepLooking) const913 PT_DocPosition FV_View::_getDocPosFromPoint(PT_DocPosition iPoint, FV_DocPos dp, bool bKeepLooking) const
914 {
915 	UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
916 	UT_uint32 iPointHeight;
917 	bool bDirection;
918 
919 	PT_DocPosition iPos;
920 
921 	// this gets called from ctor, so get out quick
922 	if (dp == FV_DOCPOS_BOD)
923 	{
924 		bool bRes = getEditableBounds(false, iPos);
925 		UT_ASSERT(bRes);
926 		if(!bRes)
927 		{
928 			UT_WARNINGMSG(("getEditableBounds() failed in %s:%d",
929 						   __FILE__,__LINE__));
930 		}
931 		fl_DocSectionLayout * pDSL = m_pLayout->getFirstSection();
932 		if(pDSL)
933 		{
934 			fl_ContainerLayout * pCL = pDSL->getFirstLayout();
935 			if(pCL->getContainerType() == FL_CONTAINER_TABLE)
936 			{
937 				iPos = pCL->getPosition(true);
938 			}
939 		}
940 		return iPos;
941 	}
942 
943 	// TODO: could cache these to save a lookup if point doesn't change
944 	fl_BlockLayout* pBlock = NULL;
945 	fp_Run* pRun = NULL;
946 	_findPositionCoords(iPoint, m_bPointEOL, xPoint,
947 						yPoint, xPoint2, yPoint2,
948 						iPointHeight, bDirection,
949 						&pBlock, &pRun);
950 
951 	UT_return_val_if_fail ( pBlock, 0 );
952 	if ( !pRun )
953 	  return pBlock->getPosition() ;
954 
955 	fp_Line* pLine = pRun->getLine();
956 	UT_return_val_if_fail ( pLine, pBlock->getPosition() );
957 
958 	// be pessimistic
959 	iPos = iPoint;
960 
961 	switch (dp)
962 	{
963 	case FV_DOCPOS_BOL:
964 	{
965 		fp_Run* pFirstRun = pLine->getFirstRun();
966 
967 		iPos = pFirstRun->getBlockOffset() + pBlock->getPosition();
968 	}
969 	break;
970 
971 	case FV_DOCPOS_EOL:
972 	{
973 		// Ignore forced breaks and EOP when finding EOL.
974 		fp_Run* pLastRun = pLine->getLastRun();
975 		while (!pLastRun->isFirstRunOnLine()
976 			   && (pLastRun->isForcedBreak()
977 				   || (FPRUN_ENDOFPARAGRAPH == pLastRun->getType())))
978 		{
979 			pLastRun = pLastRun->getPrevRun();
980 		}
981 
982 		if (pLastRun->isForcedBreak()
983 			|| (FPRUN_ENDOFPARAGRAPH == pLastRun->getType()))
984 		{
985 			iPos = pBlock->getPosition() + pLastRun->getBlockOffset();
986 		}
987 		else
988 		{
989 			iPos = pBlock->getPosition() + pLastRun->getBlockOffset() + pLastRun->getLength();
990 		}
991 	}
992 	break;
993 
994 	case FV_DOCPOS_EOD:
995 	{
996 		bool bRes = getEditableBounds(true, iPos);
997 		UT_ASSERT(bRes);
998 		if(!bRes)
999 		{
1000 			UT_WARNINGMSG(("getEditableBounds() failed in %s:%d",
1001 						   __FILE__, __LINE__));
1002 		}
1003 	}
1004 	break;
1005 
1006 	case FV_DOCPOS_BOB:
1007 	{
1008 #if 1
1009 
1010 // DOM: This used to be an #if 0. I changed it to #if 1
1011 // DOM: because after enabling this code, I can no
1012 // DOM: longer reproduce bug 403 (the bug caused by this
1013 // DOM: code being if 0'd) or bug 92 (the bug that if 0'ing
1014 // DOM: this code supposedly fixes)
1015 
1016 // TODO this piece of code attempts to go back
1017 // TODO to the previous block if we are on the
1018 // TODO edge.  this causes bug #92 (double clicking
1019 // TODO on the first line of a paragraph selects
1020 // TODO current paragraph and the previous paragraph).
1021 // TODO i'm not sure why it is here.
1022 // TODO
1023 // TODO it's here because it makes control-up-arrow
1024 // TODO when at the beginning of paragraph work. this
1025 // TODO problem is logged as bug #403.
1026 // TODO
1027 		// are we already there?
1028 		if (bKeepLooking && iPos == pBlock->getPosition())
1029 		{
1030 			// yep.  is there a prior block?
1031 			if (!pBlock->getPrevBlockInDocument())
1032 				break;
1033 
1034 			// yep.  look there instead
1035 			pBlock = pBlock->getPrevBlockInDocument();
1036 		}
1037 #endif /* 0 */
1038 
1039 		iPos = pBlock->getPosition();
1040 	}
1041 	break;
1042 
1043 	case FV_DOCPOS_EOB:
1044 	{
1045 		if (pBlock->getNextBlockInDocument())
1046 		{
1047 			fl_BlockLayout * pPrev = pBlock;
1048 			// BOB for next block
1049 			pBlock = pBlock->getNextBlockInDocument();
1050 			if((pBlock->myContainingLayout()->getContainerType() == FL_CONTAINER_FRAME) &&
1051 			   (pPrev->myContainingLayout()->getContainerType() != FL_CONTAINER_FRAME))
1052 			{
1053 				fl_FrameLayout * pFL = static_cast<fl_FrameLayout *>(pBlock->myContainingLayout());
1054 				iPos = pFL->getPosition(true) -1;
1055 			}
1056 			else
1057 			{
1058 				iPos = pBlock->getPosition();
1059 			}
1060 		}
1061 		else
1062 		{
1063 			// EOD
1064 			bool bRes = getEditableBounds(true, iPos);
1065 			UT_ASSERT(bRes);
1066 			if(!bRes)
1067 			{
1068 				UT_WARNINGMSG(("getEditableBounds() failed in %s:%d",
1069 							   __FILE__, __LINE__));
1070 			}
1071 		}
1072 	}
1073 	break;
1074 
1075 	case FV_DOCPOS_BOW:
1076 	{
1077 		UT_GrowBuf pgb(1024);
1078 
1079 		UT_DebugOnly<bool> bRes = pBlock->getBlockBuf(&pgb);
1080 		UT_ASSERT(bRes);
1081 
1082 		const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1083 
1084 		UT_ASSERT(iPos >= pBlock->getPosition());
1085 		UT_uint32 offset = iPos - pBlock->getPosition();
1086 		UT_ASSERT(offset <= pgb.getLength());
1087 
1088 		if (offset == 0)
1089 		{
1090 			if (!bKeepLooking)
1091 				break;
1092 
1093 			// is there a prior block?
1094 			pBlock = pBlock->getPrevBlockInDocument();
1095 
1096 			if (!pBlock)
1097 				break;
1098 
1099 			// yep.  look there instead
1100 			pgb.truncate(0);
1101 			bRes = pBlock->getBlockBuf(&pgb);
1102 			UT_ASSERT(bRes);
1103 
1104 			pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1105 			offset = pgb.getLength();
1106 
1107 			if (offset == 0)
1108 			{
1109 				iPos = pBlock->getPosition();
1110 				break;
1111 			}
1112 		}
1113 
1114 		if(!pSpan)
1115 		{
1116 			// empty block; this should not happen and will trigger one of the asserts
1117 			// above if(offset == 0)
1118 			return iPoint;
1119 		}
1120 
1121 		UT_uint32 iUseOffset = bKeepLooking ? offset-1 : offset;
1122 
1123 		bool bInWord = !UT_isWordDelimiter(pSpan[iUseOffset], UCS_UNKPUNK, iUseOffset > 0 ? pSpan[iUseOffset - 1] : UCS_UNKPUNK);
1124 
1125 		for (offset--; offset > 0; offset--)
1126 		{
1127 			if (UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK,pSpan[offset-1]))
1128 			{
1129 				if (bInWord)
1130 					break;
1131 			}
1132 			else
1133 				bInWord = true;
1134 		}
1135 
1136 		if ((offset > 0) && (offset < pgb.getLength()))
1137 			offset++;
1138 
1139 		iPos = offset + pBlock->getPosition();
1140 	}
1141 	break;
1142 
1143 	case FV_DOCPOS_EOW_MOVE:
1144 	{
1145 		UT_GrowBuf pgb(1024);
1146 
1147 		UT_DebugOnly<bool> bRes = pBlock->getBlockBuf(&pgb);
1148 		UT_ASSERT(bRes);
1149 
1150 		const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1151 
1152 		UT_ASSERT(iPos >= pBlock->getPosition());
1153 		UT_uint32 offset = iPos - pBlock->getPosition();
1154 		UT_ASSERT(offset <= pgb.getLength());
1155 
1156 		if (offset == pgb.getLength())
1157 		{
1158 			if (!bKeepLooking)
1159 				break;
1160 
1161 			// is there a next block?
1162 			pBlock = pBlock->getNextBlockInDocument();
1163 
1164 			if (!pBlock)
1165 				break;
1166 
1167 			// yep.  look there instead
1168 			pgb.truncate(0);
1169 			bRes = pBlock->getBlockBuf(&pgb);
1170 			UT_ASSERT(bRes);
1171 
1172 			pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1173 			offset = 0;
1174 
1175 			if (pgb.getLength() == 0)
1176 			{
1177 				iPos = pBlock->getPosition();
1178 				break;
1179 			}
1180 		}
1181 
1182 		if(!pSpan)
1183 		{
1184 			// empty block; this should not happen and will trigger one of the asserts
1185 			// above if(offset == 0)
1186 			return iPoint;
1187 		}
1188 
1189 		bool bBetween = UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK, offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK);
1190 
1191 		// Needed so ctrl-right arrow will work
1192 		// This is the code that was causing bug 10
1193 		// There is still some weird behavior that should be investigated
1194 
1195 		for (; offset < pgb.getLength(); offset++)
1196 		{
1197 			UT_UCSChar followChar, prevChar;
1198 
1199 			followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
1200 			prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;
1201 
1202 			if (!UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
1203 				break;
1204 		}
1205 
1206 		for (; offset < pgb.getLength(); offset++)
1207 		{
1208 			UT_UCSChar followChar, prevChar;
1209 
1210 			followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
1211 			prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;
1212 
1213 			if (!UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
1214 			{
1215 				if (bBetween)
1216 				{
1217 					break;
1218 				}
1219 			}
1220 			else if (pSpan[offset] != ' ')
1221 			{
1222 				break;
1223 			}
1224 			else
1225 			{
1226 				bBetween = true;
1227 			}
1228 		}
1229 
1230 		iPos = offset + pBlock->getPosition();
1231 	}
1232 	break;
1233 
1234 	case FV_DOCPOS_EOW_SELECT:
1235 	{
1236 		UT_GrowBuf pgb(1024);
1237 
1238 		UT_DebugOnly<bool> bRes = pBlock->getBlockBuf(&pgb);
1239 		UT_ASSERT(bRes);
1240 
1241 		const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1242 
1243 		UT_ASSERT(iPos >= pBlock->getPosition());
1244 		UT_uint32 offset = iPos - pBlock->getPosition();
1245 		UT_ASSERT(offset <= pgb.getLength());
1246 
1247 		if (offset == pgb.getLength())
1248 		{
1249 			if (!bKeepLooking)
1250 				break;
1251 
1252 			// is there a next block?
1253 			pBlock = pBlock->getNextBlockInDocument();
1254 
1255 			if (!pBlock)
1256 				break;
1257 
1258 			// yep.  look there instead
1259 			pgb.truncate(0);
1260 			bRes = pBlock->getBlockBuf(&pgb);
1261 			UT_ASSERT(bRes);
1262 
1263 			pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
1264 			offset = 0;
1265 
1266 			if (pgb.getLength() == 0)
1267 			{
1268 				iPos = pBlock->getPosition();
1269 				break;
1270 			}
1271 		}
1272 
1273 		if(!pSpan)
1274 		{
1275 			// empty block; this should not happen and will trigger one of the asserts
1276 			// above if(offset == 0)
1277 			return iPoint;
1278 		}
1279 
1280 		bool bBetween = UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK, offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK);
1281 
1282 		// Needed so ctrl-right arrow will work
1283 		// This is the code that was causing bug 10
1284 		// There is still some weird behavior that should be investigated
1285 		/*
1286 		  for (; offset < pgb.getLength(); offset++)
1287 		  {
1288 		  if (!UT_isWordDelimiter(pSpan[offset]))
1289 		  break;
1290 		  }
1291 		*/
1292 		for (; offset < pgb.getLength(); offset++)
1293 		{
1294 			UT_UCSChar followChar, prevChar;
1295 
1296 			followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
1297 			prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;
1298 
1299 			if (UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
1300 			{
1301 				if (bBetween)
1302 					break;
1303 			}
1304 			else if (pSpan[offset] == ' ')
1305 				break;
1306 			else
1307 				bBetween = true;
1308 		}
1309 
1310 		iPos = offset + pBlock->getPosition();
1311 	}
1312 	break;
1313 
1314 
1315 	case FV_DOCPOS_BOP:
1316 	{
1317 		fp_Container* pContainer = pLine->getColumn();
1318 		fp_Page* pPage = pContainer->getPage();
1319 
1320 		iPos = pPage->getFirstLastPos(true);
1321 	}
1322 	break;
1323 
1324 	case FV_DOCPOS_EOP:
1325 	{
1326 		fp_Container* pContainer = pLine->getColumn();
1327 		fp_Page* pPage = pContainer->getPage();
1328 
1329 		iPos = pPage->getFirstLastPos(false);
1330 	}
1331 	break;
1332 
1333 	case FV_DOCPOS_BOS:
1334 	case FV_DOCPOS_EOS:
1335 		UT_ASSERT_HARMLESS(UT_TODO);
1336 		break;
1337 
1338 	default:
1339 		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1340 		break;
1341 	}
1342 
1343 	return iPos;
1344 }
1345 
1346 
1347 /*!
1348   Find block at document position. This version is looks outside the
1349   header region if we get a null block.
1350   \param pos Document position
1351   \return Block at specified posistion, or the first block to the
1352 		  rigth of that position. May return NULL.
1353   \see m_pLayout->findBlockAtPosition
1354 */
_findBlockAtPosition(PT_DocPosition pos) const1355 fl_BlockLayout* FV_View::_findBlockAtPosition(PT_DocPosition pos) const
1356 {
1357 	fl_BlockLayout * pBL=NULL;
1358 	if(m_bEditHdrFtr && (m_pEditShadow != NULL) && (m_FrameEdit.getFrameEditMode() == FV_FrameEdit_NOT_ACTIVE))
1359 	{
1360 		pBL = static_cast<fl_BlockLayout *>(m_pEditShadow->findBlockAtPosition(pos));
1361 		if(pBL != NULL)
1362 		{
1363 			UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);
1364 			return pBL;
1365 		}
1366 	}
1367 	pBL = m_pLayout->findBlockAtPosition(pos);
1368 	// This assert makes debugging virtually impossible; this is a hack to make it assert only once ...
1369 	//UT_ASSERT(pBL);
1370 #ifdef DEBUG
1371 	static bool bAss = false;
1372 	if(!pBL && !bAss)
1373 	{
1374 		UT_ASSERT_HARMLESS( pBL );
1375 		bAss = true;
1376 	}
1377 #endif
1378 	if(!pBL)
1379 		return NULL;
1380 	UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);
1381 
1382 //
1383 // Sevior should remove this after a while..
1384 //
1385 #if(1)
1386 	if(pBL->isHdrFtr())
1387 	{
1388 //		  fl_HdrFtrSectionLayout * pSSL = static_cast<fl_HdrFtrSectionLayout *>(pBL->getSectionLayout());
1389 //		  if(pSSL && pSSL->getFirstShadow())
1390 //		  {
1391 //			  pBL = pSSL->getFirstShadow()->findMatchingBlock(pBL);
1392 			  if(!isLayoutFilling())
1393 			  {
1394 				  UT_DEBUGMSG(("SEVIOR: in view \n"));
1395 				  //UT_ASSERT(0);
1396 			  }
1397 //		  }
1398 	}
1399 #endif
1400 	return pBL;
1401 }
1402 
1403 
_insertSectionBreak(void)1404 void FV_View::_insertSectionBreak(void)
1405 {
1406 	if (!isSelectionEmpty())
1407 	{
1408 		_deleteSelection();
1409 	}
1410 //
1411 // Check and adjust insPoint if it's not valid after the selection delete
1412 //
1413 	bool bLooked = false;
1414 	fl_BlockLayout * pBL= getCurrentBlock();
1415 	while(pBL && pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_DOCSECTION)
1416 	{
1417 		bLooked = true;
1418 		pBL = pBL->getPrevBlockInDocument();
1419 	}
1420 	if(pBL == NULL)
1421 	{
1422 		pBL= getCurrentBlock();
1423 		while(pBL && pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_DOCSECTION)
1424 		{
1425 			pBL = pBL->getNextBlockInDocument();
1426 		}
1427 	}
1428 	if(bLooked && (pBL != NULL))
1429 	{
1430 		setPoint(pBL->getPosition());
1431 	}
1432 	else if(bLooked)
1433 	{
1434 		setPoint(2); // Start of document
1435 	}
1436 	//
1437 	// Get preview DocSectionLayout so we know what header/footers we have
1438 		// to insert here.
1439 	//
1440 	fl_DocSectionLayout * pPrevDSL = static_cast<fl_DocSectionLayout *>(getCurrentBlock()->getDocSectionLayout());
1441 
1442 	// insert a new paragraph with the same attributes/properties
1443 	// as the previous (or none if the first paragraph in the section).
1444 	// before inserting a section break, we insert a block break
1445 	UT_uint32 iPoint = getPoint();
1446 
1447 	m_pDoc->insertStrux(iPoint, PTX_Block);
1448 	m_pDoc->insertStrux(iPoint, PTX_Section);
1449 
1450 	_generalUpdate();
1451 	_ensureInsertionPointOnScreen();
1452 	UT_uint32 oldPoint = getPoint();
1453 	fl_DocSectionLayout * pCurDSL = static_cast<fl_DocSectionLayout *>(getCurrentBlock()->getDocSectionLayout());
1454 	//
1455 	// Duplicate previous header/footers for this section.
1456 	//
1457 	UT_GenericVector<fl_HdrFtrSectionLayout *> vecPrevHdrFtr;
1458 	pPrevDSL->getVecOfHdrFtrs( &vecPrevHdrFtr);
1459 	UT_sint32 i =0;
1460 	const gchar* block_props[] = {
1461 		"text-align", "left",
1462 		NULL, NULL
1463 	};
1464 	HdrFtrType hfType;
1465 	fl_HdrFtrSectionLayout * pHdrFtrSrc = NULL;
1466 	fl_HdrFtrSectionLayout * pHdrFtrDest = NULL;
1467 	for(i=0; i< vecPrevHdrFtr.getItemCount(); i++)
1468 	{
1469 		  pHdrFtrSrc = vecPrevHdrFtr.getNthItem(i);
1470 		  hfType = pHdrFtrSrc->getHFType();
1471 		  insertHeaderFooter(block_props, hfType, pCurDSL); // cursor is now in the header/footer
1472 		  if(hfType == FL_HDRFTR_HEADER)
1473 		  {
1474 			  pHdrFtrDest = pCurDSL->getHeader();
1475 		  }
1476 		  else if(hfType == FL_HDRFTR_FOOTER)
1477 		  {
1478 			  pHdrFtrDest = pCurDSL->getFooter();
1479 		  }
1480 		  else if(hfType == FL_HDRFTR_HEADER_FIRST)
1481 		  {
1482 			  pHdrFtrDest = pCurDSL->getHeaderFirst();
1483 		  }
1484 		  else if( hfType == FL_HDRFTR_HEADER_EVEN)
1485 		  {
1486 			  pHdrFtrDest = pCurDSL->getHeaderEven();
1487 		  }
1488 		  else if( hfType == FL_HDRFTR_HEADER_LAST)
1489 		  {
1490 			  pHdrFtrDest = pCurDSL->getHeaderLast();
1491 		  }
1492 		  else if(hfType == FL_HDRFTR_FOOTER_FIRST)
1493 		  {
1494 			  pHdrFtrDest = pCurDSL->getFooterFirst();
1495 		  }
1496 		  else if( hfType == FL_HDRFTR_FOOTER_EVEN)
1497 		  {
1498 			  pHdrFtrDest = pCurDSL->getFooterEven();
1499 		  }
1500 		  else if( hfType == FL_HDRFTR_FOOTER_LAST)
1501 		  {
1502 			  pHdrFtrDest = pCurDSL->getFooterLast();
1503 		  }
1504 		  _populateThisHdrFtr(pHdrFtrSrc,pHdrFtrDest);
1505 	}
1506 
1507 	_setPoint(oldPoint);
1508 	_generalUpdate();
1509 
1510 	_ensureInsertionPointOnScreen();
1511 }
1512 
1513 /*!
1514  * Return the next line in the document.
1515  */
_getNextLineInDoc(fp_Container * pCon) const1516 fp_Line * FV_View::_getNextLineInDoc(fp_Container * pCon) const
1517 {
1518 	fp_ContainerObject * pNext = NULL;
1519 	fl_ContainerLayout * pCL = NULL;
1520 	fl_BlockLayout * pNextB = NULL;
1521 	if(pCon->getContainerType() == FP_CONTAINER_CELL)
1522 	{
1523 		pCon = static_cast<fp_CellContainer *>(pCon)->getFirstContainer();
1524 		if(pCon->getContainerType() == FP_CONTAINER_TABLE)
1525 		{
1526 			pCon = static_cast<fp_TableContainer *>(pCon)->getFirstContainer();
1527 			return _getNextLineInDoc(pCon);
1528 		}
1529 		return static_cast<fp_Line *>(pCon);
1530 	}
1531 	if(pCon->getContainerType() != FP_CONTAINER_LINE )
1532 	{
1533 		pCL = static_cast<fl_ContainerLayout *>(pCon->getSectionLayout());
1534 		pCL = pCL->getNext();
1535 		if(pCL && pCL->getContainerType() == FL_CONTAINER_BLOCK)
1536 		{
1537 			pNextB = static_cast<fl_BlockLayout *>(pCL);
1538 		}
1539 		else if(pCL)
1540 	    {
1541 			pNextB = pCL->getNextBlockInDocument();
1542 		}
1543 		if(pNextB)
1544 			pNext = pNextB->getFirstContainer();
1545 	}
1546 	else
1547 	{
1548 		pNext = pCon->getNext();
1549 		if(pNext == NULL)
1550 		{
1551 			pNextB = static_cast<fp_Line *>(pCon)->getBlock();
1552 			pNextB = pNextB->getNextBlockInDocument();
1553 			if(pNextB)
1554 				pNext = pNextB->getFirstContainer();
1555 		}
1556 	}
1557 	while(pNext && pNext->getContainerType() !=FP_CONTAINER_LINE)
1558 	{
1559 		pCL = static_cast<fl_ContainerLayout *>(pNext->getSectionLayout());
1560 		pNextB = pCL->getNextBlockInDocument();
1561 		if(pNextB)
1562 			pNext = pNextB->getFirstContainer();
1563 	}
1564 	if(!pNext)
1565 	{
1566 		//
1567 		// end of document
1568 		//
1569 		return NULL;
1570 	}
1571 	fp_Line * pNextLine = static_cast<fp_Line *>(pNext);
1572 	return pNextLine;
1573 }
1574 
1575 /*!
1576   Move insertion point to previous or next line
1577   \param bNext True if moving to next line
1578 
1579   This function moves the IP up or down one line, attempting to get as
1580   close as possible to the prior "sticky" x position.  The notion of
1581   "next" is strictly physical, not logical.
1582 
1583   For example, instead of always moving from the last line of one
1584   block to the first line of the next, you might wind up skipping over
1585   a bunch of blocks to wind up in the first line of the second column.
1586 */
_moveInsPtNextPrevLine(bool bNext)1587 void FV_View::_moveInsPtNextPrevLine(bool bNext)
1588 {
1589 	UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
1590 	UT_uint32 iPointHeight, iLineHeight;
1591 	bool bDirection;
1592 
1593 //
1594 // No need to to do background updates for 1 seconds.
1595 //
1596  	m_pLayout->setSkipUpdates(2);
1597 	UT_sint32 xOldSticky = m_xPointSticky;
1598 
1599 	// first, find the line we are on now
1600 	PT_DocPosition iOldPoint = getPoint();
1601 
1602 	fl_BlockLayout* pOldBlock;
1603 	fp_Run* pOldRun;
1604 	_findPositionCoords(iOldPoint, m_bPointEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection, &pOldBlock, &pOldRun);
1605 	if(pOldRun == NULL)
1606 	{
1607 		PT_DocPosition posEOD;
1608 		getEditableBounds(true,posEOD);
1609 		if(posEOD <= iOldPoint)
1610 		{
1611 			setPoint(posEOD);
1612 			return;
1613 		}
1614 	}
1615 	UT_return_if_fail(pOldRun);
1616 
1617 	fl_SectionLayout* pOldSL = pOldBlock->getSectionLayout();
1618 	fp_Line* pOldLine = pOldRun->getLine();
1619 	fp_VerticalContainer* pOldContainer = static_cast<fp_VerticalContainer *>(pOldLine->getContainer());
1620 	fp_Column * pOldLeader = NULL;
1621 	fp_Page* pOldPage = pOldLine->getPage();
1622 	bool bDocSection = pOldSL->getType() == FL_SECTION_DOC;
1623 	bool bEndNoteSection =	pOldSL->getType() == FL_SECTION_ENDNOTE;
1624 	bool bFootnoteSection = pOldSL->getType() == FL_SECTION_FOOTNOTE;
1625 	bool bCellSection = (pOldSL->getContainerType() == FL_CONTAINER_CELL);
1626 	UT_sint32 iHoriz = getNumHorizPages();
1627 	bool bChangedPage = false;
1628 
1629 	if(bDocSection || bEndNoteSection || bFootnoteSection || (bCellSection && !isHdrFtrEdit()))
1630 	{
1631 		pOldLeader = (static_cast<fp_Column*>(pOldLine->getColumn()))->getLeader();
1632 	}
1633 
1634 	UT_sint32 iPageOffset;
1635 	getPageYOffset(pOldPage, iPageOffset);
1636 
1637 	UT_sint32 iLineX = 0;
1638 	UT_sint32 iLineY = 0;
1639 	UT_sint32 xOldpage = 0;
1640 	UT_sint32 xNewpage = 0;
1641 
1642 	pOldContainer->getOffsets(static_cast<fp_Container *>(pOldLine), iLineX, iLineY);
1643 	yPoint = iLineY;
1644 
1645 	iLineHeight = pOldLine->getHeight();
1646 
1647 	bool bNOOP = false;
1648 	bool bEOL = false, bBOL = false;
1649 	fp_Line * pNextLine = NULL;
1650 	fp_Page* pPage = NULL;
1651 	xxx_UT_DEBUGMSG(("fv_View::_moveInsPtNextPrevLine: old line 0x%x\n", pOldLine));
1652 
1653 	if (bNext)
1654 	{
1655 		if (pOldLine != static_cast<fp_Line *>(pOldContainer->getLastContainer()))
1656 		{
1657 			UT_sint32 iAfter = m_pG->tlu(1);
1658 			yPoint += (iLineHeight + iAfter);
1659 		}
1660 		else if (bDocSection)
1661 		{
1662 			UT_sint32 count = static_cast<UT_sint32>(pOldPage->countColumnLeaders());
1663 			UT_sint32 i = 0;
1664 			for(i =0; i < count ;i++)
1665 			{
1666 				if( static_cast<fp_Column *>(pOldPage->getNthColumnLeader(i)) == pOldLeader)
1667 				{
1668 					break;
1669 				}
1670 			}
1671 			if((i + 1) < count)
1672 			{
1673 				// Move to next container
1674 				yPoint = pOldPage->getNthColumnLeader(i+1)->getY();
1675 			}
1676 			else
1677 			{
1678 				// move to next page
1679 				pPage = pOldPage->getNext();
1680 				if (pPage)
1681 				{
1682 					getPageYOffset(pPage, iPageOffset);
1683 					yPoint = 0;
1684 					if(iHoriz > 1)
1685 					{
1686 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1687 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1688 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1689 						 bChangedPage = true;
1690 					}
1691 				}
1692 				else
1693 				{
1694 					bNOOP = true;
1695 					if (_getDocPosFromPoint(iOldPoint, FV_DOCPOS_EOL) != iOldPoint)
1696 					  bEOL = true;
1697 				}
1698 			}
1699 		}
1700 		else if(bCellSection)
1701 		{
1702 			if (iHoriz <= 1)
1703 			{
1704 				UT_sint32 iAfter = m_pG->tlu(1);
1705 				fp_CellContainer * pCell =  static_cast<fp_CellContainer*> (pOldLine->getContainer());
1706 				fp_TableContainer * pTab = static_cast<fp_TableContainer*> (pCell->getContainer());
1707 				if (pCell->getLastContainer() == pOldLine)
1708 				{
1709 					yPoint += pTab->getYOfRow(pCell->getBottomAttach()) - pTab->getYOfRow(pCell->getTopAttach());
1710 					yPoint += iAfter - pOldLine->getY();
1711 				}
1712 				else
1713 				{
1714 					yPoint += (iLineHeight + iAfter);
1715 				}
1716 			}
1717 			else
1718 			{
1719 				fp_CellContainer * pCell =  static_cast<fp_CellContainer*> (pOldLine->getContainer());
1720 			    if(pCell->getLastContainer() == pOldLine)
1721 				{
1722 					fp_TableContainer * pTab = static_cast<fp_TableContainer *>(pCell->getContainer());
1723 					UT_sint32 iBotPrev = pCell->getBottomAttach();
1724 					if(iBotPrev > pTab->getNumRows())
1725 					{
1726 						pNextLine = _getNextLineInDoc(pTab);
1727 					}
1728 					else
1729 				    {
1730 						pCell = pTab->getCellAtRowColumn(iBotPrev,pCell->getLeftAttach());
1731 						if(!pCell)
1732 						{
1733 							pNextLine = _getNextLineInDoc(pTab);
1734 						}
1735 						else
1736 						{
1737 							pNextLine = _getNextLineInDoc(pCell);
1738 						}
1739 					}
1740 				}
1741 				else
1742 				{
1743 					pNextLine =  _getNextLineInDoc(pOldLine);
1744 				}
1745 				if(!pNextLine)
1746 					return;
1747 				pPage = pNextLine->getPage();
1748 				if(pPage && (pPage != pOldPage))
1749 				{
1750 					xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1751 					xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1752 					m_xPointSticky =  m_xPointSticky - ( xOldpage -xNewpage);
1753 					if(pPage != pOldPage)
1754 					{
1755 						getPageYOffset(pPage, iPageOffset);
1756 						yPoint = 0;
1757 					}
1758 					bChangedPage = true;
1759 				}
1760 			}
1761 		}
1762 		else if(bEndNoteSection)
1763 		{
1764 			UT_sint32 iAfter = m_pG->tlu(1);
1765 			yPoint += (iLineHeight + iAfter);
1766 			if(pOldPage->getBottom() < yPoint)
1767 			{
1768 				// move to next page
1769 				pPage = pOldPage->getNext();
1770 				if (pPage)
1771 				{
1772 					getPageYOffset(pPage, iPageOffset);
1773 					yPoint = 0;
1774 					if(iHoriz > 1)
1775 					{
1776 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1777 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1778 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1779 						 bChangedPage = true;
1780 					}
1781 				}
1782 			}
1783 		}
1784 		else if(bFootnoteSection)
1785 		{
1786 			UT_sint32 iAfter = m_pG->tlu(1);
1787 			yPoint += (iLineHeight + iAfter);
1788 			if(pOldPage->getBottom() < yPoint)
1789 			{
1790 				// move to next page
1791 				pPage = pOldPage->getNext();
1792 				if (pPage)
1793 				{
1794 					xxx_UT_DEBUGMSG(("Move to next page old IpageOffset %d ypoint %d \n",iPageOffset,yPoint));
1795 					getPageYOffset(pPage, iPageOffset);
1796 					xxx_UT_DEBUGMSG(("Move to next page new IpageOffset %d \n",iPageOffset));
1797 					yPoint = 0;
1798 					if(iHoriz > 1)
1799 					{
1800 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1801 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1802 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1803 						 bChangedPage = true;
1804 					}
1805 				}
1806 			}
1807 		}
1808 		else
1809 		{
1810 			bNOOP = true;
1811 		}
1812 	}
1813 	else
1814 	{
1815 		if (pOldLine != static_cast<fp_Line *>(pOldContainer->getFirstContainer()))
1816 		{
1817 			// just move off this line
1818 			yPoint -= (pOldLine->getMarginBefore() + 1);
1819 		}
1820 		else if (bDocSection)
1821 		{
1822 			UT_sint32 count = static_cast<UT_sint32>(pOldPage->countColumnLeaders());
1823 			UT_sint32 i = 0;
1824 			for(i =0; i < count ;i++)
1825 			{
1826 				if( static_cast<fp_Column *>(pOldPage->getNthColumnLeader(i)) == pOldLeader)
1827 				{
1828 					break;
1829 				}
1830 			}
1831 			if( (i> 0) && (i < count))
1832 			{
1833 				// Move to prev container
1834 				yPoint = pOldPage->getNthColumnLeader(i-1)->getLastContainer()->getY();
1835 				yPoint +=  pOldPage->getNthColumnLeader(i-1)->getY()+2;
1836 			}
1837 			else
1838 			{
1839 				// move to prev page
1840 				pPage = pOldPage->getPrev();
1841 				if (pPage)
1842 				{
1843 					getPageYOffset(pPage, iPageOffset);
1844 					yPoint = pPage->getBottom();
1845 					if(getViewMode() != VIEW_PRINT)
1846 					{
1847 						fl_DocSectionLayout * pDSL = pPage->getOwningSection();
1848 						yPoint = yPoint - pDSL->getTopMargin() -2;
1849 					}
1850 					if(iHoriz > 1)
1851 					{
1852 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1853 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1854 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1855 						 bChangedPage = true;
1856 					}
1857 				}
1858 				else
1859 				{
1860 					bNOOP = true;
1861 					if (_getDocPosFromPoint(iOldPoint, FV_DOCPOS_BOL) != iOldPoint)
1862 					  bBOL = true;
1863 				}
1864 			}
1865 		}
1866 		else if(bCellSection)
1867 		{
1868 			UT_sint32 iAfter = m_pG->tlu(2);
1869 			yPoint -= iAfter;
1870 			if(yPoint < 0)
1871 			{
1872 				// move to prev page
1873 				pPage = pOldPage->getPrev();
1874 				if (pPage)
1875 				{
1876 					getPageYOffset(pPage, iPageOffset);
1877 					yPoint = pPage->getBottom();
1878 					if(getViewMode() != VIEW_PRINT)
1879 					{
1880 						fl_DocSectionLayout * pDSL = pPage->getOwningSection();
1881 						yPoint = yPoint - pDSL->getTopMargin() -2;
1882 					}
1883 					if(iHoriz > 1)
1884 					{
1885 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1886 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1887 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1888 						 bChangedPage = true;
1889 					}
1890 				}
1891 			}
1892 
1893 		}
1894 		else if(bEndNoteSection || bFootnoteSection)
1895 		{
1896 			UT_sint32 iAfter = m_pG->tlu(2);
1897 			yPoint -= iAfter;
1898 
1899 			// change to screen coordinates
1900 			UT_sint32 xP = m_xPointSticky - m_xScrollOffset + getPageViewLeftMargin();
1901 			UT_sint32 yP = yPoint + iPageOffset - m_yScrollOffset;
1902 
1903 			// hit-test to figure out where that puts us
1904 			UT_sint32 xC, yC;
1905 
1906 			PT_DocPosition iNewP;
1907 			fp_Page* pPage2 = _getPageForXY(xP, yP, xC, yC);
1908 			bool isTOC = false;
1909 			pPage2->mapXYToPosition(xC, yC, iNewP, bBOL, bEOL,isTOC);
1910 			UT_sint32 ii =0;
1911 			while((iNewP == iOldPoint) && (ii < 100) && (yPoint > 0))
1912 			{
1913 				yPoint -= iAfter;
1914 				yP = yPoint + iPageOffset - m_yScrollOffset;
1915 				pPage2 = _getPageForXY(xP, yP, xC, yC);
1916 				pPage2->mapXYToPosition(xC, yC, iNewP, bBOL, bEOL,isTOC);
1917 				ii++;
1918 			}
1919 			if(yPoint < 0)
1920 			{
1921 				// move to prev page
1922 				pPage = pOldPage->getPrev();
1923 				if (pPage)
1924 				{
1925 					getPageYOffset(pPage, iPageOffset);
1926 					yPoint = pPage->getBottom();
1927 					if(getViewMode() != VIEW_PRINT)
1928 					{
1929 						fl_DocSectionLayout * pDSL = pPage->getOwningSection();
1930 						yPoint = yPoint - pDSL->getTopMargin() -2;
1931 					}
1932 					if(iHoriz > 1)
1933 					{
1934 						xOldpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pOldPage)));
1935 						xNewpage = static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage)));
1936 						 m_xPointSticky =  m_xPointSticky - (xOldpage-xNewpage);
1937 						 bChangedPage = true;
1938 					}
1939 				}
1940 			}
1941 		}
1942 		else
1943 		{
1944 			bNOOP = true;
1945 		}
1946 	}
1947 
1948 	if (bNOOP)
1949 	{
1950 		if (bBOL)
1951 			moveInsPtTo(FV_DOCPOS_BOL, false);
1952 		else if (bEOL)
1953 			moveInsPtTo(FV_DOCPOS_EOL, false);
1954 		// cannot move.  should we beep?
1955 		return;
1956 	}
1957 	// hit-test to figure out where that puts us
1958 	UT_sint32 xClick, yClick;
1959 	if(!pNextLine)
1960 	{
1961 		// change to screen coordinates
1962 		xPoint = m_xPointSticky - m_xScrollOffset + getPageViewLeftMargin();
1963 		yPoint += iPageOffset - m_yScrollOffset;
1964 
1965 		pPage = _getPageForXY(xPoint, yPoint, xClick, yClick);
1966 	}
1967 	else
1968 	{
1969 		UT_Rect * pOldRec = pOldLine->getScreenRect();
1970 		UT_Rect * pNewRec = pNextLine->getScreenRect();
1971 		yPoint = pNewRec->top + pNewRec->height/2;
1972 		xPoint = xPoint + (pNewRec->left - pOldRec->left);
1973 		pPage = _getPageForXY(xPoint, yPoint, xClick, yClick);
1974 		delete pOldRec;
1975 		delete pNewRec;
1976 	}
1977 	PT_DocPosition iNewPoint = 0;
1978 	bBOL = false;
1979 	bEOL = false;
1980 	fl_HdrFtrShadow * pShadow=NULL;
1981 //
1982 // If we're not in a Header/Footer we can't get off the page with the click
1983 // version of mapXYToPosition
1984 //
1985 	bool isTOC = false;
1986 	if(isHdrFtrEdit())
1987 	{
1988 		bool bGotIt = false;
1989 		UT_sint32 iLoop = 0;
1990 		while(!bGotIt && (iLoop < 50))
1991 		{
1992 			pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC, true, &pShadow);
1993 			if(iNewPoint != iOldPoint)
1994 			{
1995 				bGotIt = true;
1996 			}
1997 			else
1998 			{
1999 				iLoop++;
2000 				if(bNext)
2001 				{
2002 					yClick += m_pG->tlu(1);
2003 				}
2004 				else
2005 				{
2006 					yClick -= m_pG->tlu(1);
2007 				}
2008 			}
2009 		}
2010 	}
2011 	else
2012 	{
2013 		pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC);
2014 		if (bNext)
2015 		{
2016 			int delta = iLineHeight;
2017 //
2018 // Whoops! foornotes/endnotes can screw this up since they placed on the page
2019 // out of order to their location in the PieceTable
2020 //
2021 			while (iNewPoint <= getPoint() && (getCurrentPage() == pPage) )
2022 			{
2023 				if (yClick+delta > pPage->getHeight())
2024 				{
2025 					pPage = pPage->getNext();
2026 					if (!pPage)
2027 					{
2028 						return;
2029 					}
2030 					delta = -yClick;
2031 				}
2032 				pPage->mapXYToPosition(xClick, yClick+delta,
2033 									   iNewPoint, bBOL, bEOL,isTOC);
2034 				delta += iLineHeight;
2035 			}
2036 		}
2037 		else
2038 		{
2039 			int delta = iLineHeight;
2040 //
2041 // Whoops! foornotes/endnotes can screw this up since they placed on the page
2042 // out of order to their location in the PieceTable
2043 //
2044 			while (iNewPoint >= getPoint() && (getCurrentPage() == pPage) )
2045 			{
2046 				if (yClick-delta < 0)
2047 				{
2048 					pPage = pPage->getPrev();
2049 					if (!pPage)
2050 					{
2051 						return;
2052 					}
2053 					delta = yClick - pPage->getBottom();
2054 				}
2055 				pPage->mapXYToPosition(xClick, yClick-delta,
2056 									   iNewPoint, bBOL, bEOL,isTOC);
2057 				delta += iLineHeight;
2058 			}
2059 		}
2060 
2061 		while(pPage && (iNewPoint == iOldPoint) && (yClick < m_pLayout->getHeight()) && (yClick > 0))
2062 		{
2063 			if (bNext)
2064 				yClick += iLineHeight/2;
2065 			else
2066 				yClick -= m_pG->tlu(2);
2067 
2068 			if(yClick > pPage->getHeight())
2069 			{
2070 				pPage = pPage->getNext();
2071 				if (!pPage) break;
2072 				yClick -= pPage->getHeight();
2073 			}
2074 
2075 			if(yClick < 0)
2076 			{
2077 				pPage = pPage->getPrev();
2078 				if (!pPage) break;
2079 				yClick += pPage->getHeight();
2080 			}
2081 
2082 			if(pPage)
2083 				pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC);
2084 		}
2085 		xxx_UT_DEBUGMSG((" pPage %x iNewPoint %d iOldPoint %d yClick %d m_pLayout->getHeight() \n",pPage,iNewPoint,yClick,m_pLayout->getHeight()));
2086 	}
2087 //
2088 // Check we're not moving out of allowed region.
2089 //
2090 	PT_DocPosition posBOD,posEOD;
2091 	getEditableBounds(false,posBOD);
2092 	getEditableBounds(true,posEOD);
2093 
2094 	xxx_UT_DEBUGMSG(("iNewPoint=%d, iOldPoint=%d, xClick=%d, yClick=%d\n",iNewPoint, iOldPoint, xClick, yClick));
2095 	UT_ASSERT(iNewPoint != iOldPoint);
2096 	if((iNewPoint >= posBOD) && (iNewPoint <= posEOD))
2097 	{
2098 		_setPoint(iNewPoint, bEOL);
2099 	}
2100 
2101 	_ensureInsertionPointOnScreen();
2102 
2103 	// this is the only place where we override changes to m_xPointSticky
2104 	if(!bChangedPage || (pPage == pOldPage))
2105 	   m_xPointSticky = xOldSticky;
2106 }
2107 
2108 /*! Scrolls the screen to make sure that the IP is on-screen.
2109  * \return true iff scrolling took place
2110  * Q: should this get called if there is a selection?  Does it do
2111  * harm?  It may, because point may be the beginning of the selection.
2112  */
_ensureInsertionPointOnScreen()2113 bool FV_View::_ensureInsertionPointOnScreen()
2114 {
2115 	xxx_UT_DEBUGMSG(("FV_View::_ensureInsertionPointOnScreen called windowHeight %d Point %d \n",getWindowHeight(),getPoint()));
2116 
2117 	// Some short circuit tests to avoid doing bad things.
2118 	if (getWindowHeight() <= 0)
2119 		return false;
2120 
2121     // If == 0 no layout information is present. Don't scroll.
2122 	if(getPoint() == 0)
2123 		return false;
2124 
2125 	xxx_UT_DEBUGMSG(("_ensure: [xp %ld][yp %ld][ph %ld] [w %ld][h %ld]\n",m_xPoint,m_yPoint,m_iPointHeight,getWindowWidth(),getWindowHeight()));
2126 
2127 	bool bRet = false;
2128 	if (m_yPoint < 0)
2129 	{
2130 		cmdScroll(AV_SCROLLCMD_LINEUP, (UT_uint32) (-(m_yPoint)));
2131 		bRet = true;
2132 	}
2133 	else if ((static_cast<UT_uint32>(m_yPoint + m_iPointHeight)) >= (static_cast<UT_uint32>(getWindowHeight())))
2134 	{
2135 		cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(m_yPoint + m_iPointHeight - getWindowHeight()));
2136 		bRet = true;
2137 	}
2138 
2139 	/*
2140 	  TODO: we really ought to try to do better than this.
2141 	*/
2142 	if (m_xPoint < 0)
2143 	{
2144 		cmdScroll(AV_SCROLLCMD_LINELEFT, (UT_uint32) (-(m_xPoint) + getPageViewLeftMargin()/2));
2145 		bRet = true;
2146 	}
2147 	else if ((static_cast<UT_uint32>(m_xPoint)) >= (static_cast<UT_uint32>(getWindowWidth())))
2148 	{
2149 		cmdScroll(AV_SCROLLCMD_LINERIGHT, static_cast<UT_uint32>(m_xPoint - getWindowWidth() + getPageViewLeftMargin()/2));
2150 		bRet = true;
2151 	}
2152 
2153 	// This used to say 'if !bRet', but I think it's cleaner to always fix,
2154 	// if possibly slower.  If we scroll, perhaps the scroll has already
2155 	// fixed, and we end up fixing twice.
2156 	_fixInsertionPointCoords();
2157 
2158 	return bRet;
2159 }
2160 
2161 /* It's unclear to me why this is used rather than ensure. -PL */
_updateInsertionPoint()2162 void FV_View::_updateInsertionPoint()
2163 {
2164 	if (isSelectionEmpty())
2165  	{
2166 		_ensureInsertionPointOnScreen();
2167 	}
2168 }
2169 
_moveInsPtNextPrevPage(bool bNext)2170 void FV_View::_moveInsPtNextPrevPage(bool bNext)
2171 {
2172 #if 0
2173 	UT_sint32 xPoint;
2174 	UT_sint32 yPoint;
2175 	UT_sint32 iPointHeight;
2176 #endif
2177 
2178 	fp_Page* pOldPage = _getCurrentPage();
2179 
2180 	// TODO when moving to the prev page, we should move to its end, not begining
2181 	// try to locate next/prev page
2182 	fp_Page* pPage = 0;
2183 
2184 	if (pOldPage)
2185 	{
2186 		pPage = (bNext ? pOldPage->getNext() : pOldPage->getPrev());
2187 	}
2188 
2189 	// if couldn't move, go to top of this page if we are looking for the previous page
2190 	// or the end of this page if we are looking for the next page
2191 	if (!pPage)
2192 	{
2193 		if(!bNext)
2194 		{
2195 			pPage = pOldPage;
2196 		}
2197 		else
2198 		{
2199 			moveInsPtTo(FV_DOCPOS_EOD,false);
2200 			return;
2201 		}
2202 	}
2203 
2204 	_moveInsPtToPage(pPage);
2205 }
2206 
_moveInsPtNextPrevScreen(bool bMovingDown,bool bClearSelection)2207 void FV_View::_moveInsPtNextPrevScreen(bool bMovingDown, bool bClearSelection)
2208 {
2209 	fl_BlockLayout * pBlock;
2210 	fp_Run * pRun;
2211 	UT_sint32 x,y,x2,y2;
2212 	UT_uint32 iHeight;
2213 	bool bDirection;
2214 	bool bBOL,bEOL,isTOC;
2215 	UT_sint32 iYnext,iYscroll;
2216 	PT_DocPosition iNewPoint;
2217 	_findPositionCoords(getPoint(),false,x,y,x2,y2,iHeight,bDirection,&pBlock,&pRun);
2218 	if(!pRun)
2219 		return;
2220 
2221 	fp_Line * pLine = pRun->getLine();
2222 	UT_return_if_fail(pLine);
2223 	fp_Page * pPage = pLine->getPage();
2224 	UT_return_if_fail(pPage);
2225 	if(isHdrFtrEdit())
2226 	{
2227 		clearHdrFtrEdit();
2228 		warpInsPtToXY(0,0,false);
2229 	}
2230 	UT_sint32 xoff,yoff;
2231 
2232     // get Screen coordinates of the top of the page and add the y location to this.
2233 	getPageScreenOffsets(pPage, xoff,yoff);
2234 	yoff = y - yoff;
2235 
2236 	UT_sint32 iDir = bMovingDown ? 1 : -1;
2237 
2238 	iYnext = yoff + getWindowHeight() * iDir;
2239 	iYscroll = m_yScrollOffset + (getWindowHeight() * iDir);
2240 	if (iYscroll < 0)
2241 	{
2242 		// We're trying to scroll past beginning/end of document
2243 		// Move insertion pointer to BOD/EOD instead
2244 		if (iDir == 1)
2245 		{
2246 			moveInsPtTo(FV_DOCPOS_EOD, bClearSelection);
2247 		}
2248 		else
2249 		{
2250 			moveInsPtTo(FV_DOCPOS_BOD, bClearSelection);
2251 		}
2252 		return;
2253 	}
2254 
2255 	xxx_UT_DEBUGMSG(("SEVIOR:!!!!!! Yoff %d iYnext %d page %x \n",yoff,iYnext,pPage));
2256 
2257 	while (pPage && (bMovingDown ? (iYnext > pPage->getHeight())
2258 					             : (iYnext < 0)))
2259 	{
2260 		iYnext -= (pPage->getHeight() + getPageViewSep()) * iDir;
2261 		pPage = bMovingDown ? pPage->getNext() : pPage->getPrev();
2262 	}
2263 
2264 	xxx_UT_DEBUGMSG(("SEVIOR:!!!!!! Set to iYnext %d page %x \n",iYnext,pPage));
2265 
2266 	if (pPage == NULL) pPage = pLine->getPage ();
2267 	if (iYnext < 0) iYnext = 0;
2268 	// convert the iYnext back into a point position, namely iNewPoint.
2269 	pPage->mapXYToPosition(x, iYnext, iNewPoint, bBOL, bEOL,isTOC);
2270 	if (bMovingDown)
2271 	{
2272 		int delta = pPage->getHeight()/4;
2273 		while (iNewPoint <= getPoint() && pPage)
2274 		{
2275 			if (iYnext+delta > pPage->getHeight())
2276 			{
2277 				delta -= pPage->getHeight();
2278 				pPage = pPage->getNext();
2279 			}
2280 			if (pPage)
2281 			{
2282 				pPage->mapXYToPosition(x, iYnext+delta,
2283 									   iNewPoint, bBOL, bEOL,isTOC);
2284 				delta += pPage->getHeight()/4;
2285 			}
2286 		}
2287 	}
2288 	else
2289 	{
2290 		int delta = pPage->getHeight()/4;
2291 		while (iNewPoint >= getPoint() && pPage)
2292 		{
2293 			if (iYnext+delta < 0)
2294 			{
2295 				delta += pPage->getHeight();
2296 				pPage = pPage->getPrev();
2297 			}
2298 			if (pPage)
2299 			{
2300 				pPage->mapXYToPosition(x, iYnext-delta,
2301 									   iNewPoint, bBOL, bEOL,isTOC);
2302 				delta += pPage->getHeight()/4;
2303 			}
2304 		}
2305 	}
2306 
2307 	UT_sint32 newX,newY;
2308 	UT_uint32 newHeight;
2309 
2310 	_findPositionCoords(iNewPoint,false,newX,newY,x2,y2,newHeight,bDirection,&pBlock,&pRun);
2311 	if(!pRun)
2312 	{
2313 		_moveInsPtNextPrevLine(bMovingDown);
2314 		return;
2315 	}
2316 
2317 	fp_Line * pNewLine = static_cast<fp_Line *>(pRun->getLine());
2318 
2319 	if(pNewLine == NULL ||
2320 	   ((pNewLine->getContainer() == pLine->getContainer()) &&
2321 	   (bMovingDown ? (pNewLine->getY() < pLine->getY())
2322 		: (pNewLine->getY() > pLine->getY()))))
2323 	{
2324 		_moveInsPtNextPrevLine(bMovingDown);
2325 		return;
2326 	}
2327 
2328     // Couldn't advance! Try scanning x across the page at this new iYnext.
2329 	if(pLine == pNewLine && pPage)
2330 	{
2331 		UT_sint32 step = pPage->getWidth()/20 + 1;
2332 
2333 		for (x=0; x < pPage->getWidth(); x += step)
2334 		{
2335 			pPage->mapXYToPosition(x, iYnext, iNewPoint, bBOL, bEOL,isTOC);
2336 			_findPositionCoords(iNewPoint,false,newX,newY,x2,y2,newHeight,bDirection,&pBlock,&pRun);
2337 			pNewLine = static_cast<fp_Line *>(pRun->getLine());
2338 			if(pLine != pNewLine)
2339 				break;
2340 		}
2341 
2342 		if (pLine == pNewLine)
2343 		{
2344 			_moveInsPtNextPrevLine(bMovingDown);
2345 			return;
2346 		}
2347 	}
2348 
2349 	_setPoint(iNewPoint);
2350 	sendVerticalScrollEvent(iYscroll);
2351 	if (!_ensureInsertionPointOnScreen())
2352 	{
2353 		_fixInsertionPointCoords();
2354 	}
2355 }
2356 
2357 
_getCurrentPage(void) const2358 fp_Page *FV_View::_getCurrentPage(void) const
2359 {
2360 	UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
2361 	UT_uint32 iPointHeight;
2362 	bool bDirection;
2363 	/*
2364 	  This function moves the IP to the beginning of the previous or
2365 	  next page (ie not this one).
2366 	*/
2367 
2368 	// first, find the page we are on now
2369 	UT_uint32 iOldPoint = getPoint();
2370 
2371 	fl_BlockLayout* pOldBlock;
2372 	fp_Run* pOldRun;
2373 	_findPositionCoords(iOldPoint, m_bPointEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection, &pOldBlock, &pOldRun);
2374 	UT_return_val_if_fail ( pOldRun, 0 );
2375 	fp_Line* pOldLine = pOldRun->getLine();
2376 	fp_Page* pOldPage = pOldLine->getPage();
2377 	return pOldPage;
2378 }
2379 
_moveInsPtNthPage(UT_sint32 n)2380 void FV_View::_moveInsPtNthPage(UT_sint32 n)
2381 {
2382 	fp_Page *page = m_pLayout->getFirstPage();
2383 
2384 	if (n > m_pLayout->countPages ())
2385 		n = m_pLayout->countPages ();
2386 
2387 	for (UT_sint32 i = 1; i < n; i++)
2388 	{
2389 		page = page->getNext ();
2390 	}
2391 
2392 	_moveInsPtToPage(page);
2393 }
2394 
_moveInsPtToPage(fp_Page * page)2395 void FV_View::_moveInsPtToPage(fp_Page *page)
2396 {
2397 	UT_return_if_fail(page);
2398 
2399 	// move to the first pos on this page
2400 	PT_DocPosition iNewPoint = page->getFirstLastPos(true);
2401 	_setPoint(iNewPoint, false);
2402 
2403 	// explicit vertical scroll to top of page
2404 	UT_sint32 iPageOffset;
2405 	getPageYOffset(page, iPageOffset);
2406 
2407 	iPageOffset -= getPageViewSep() /2;
2408 	iPageOffset -= m_yScrollOffset;
2409 
2410 	bool bVScroll = false;
2411 	if (iPageOffset < 0)
2412 	{
2413 		cmdScroll(AV_SCROLLCMD_LINEUP, static_cast<UT_uint32>(-iPageOffset));
2414 		bVScroll = true;
2415 	}
2416 	else if (iPageOffset > 0)
2417 	{
2418 		cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(iPageOffset));
2419 		bVScroll = true;
2420 	}
2421 
2422 	// also allow implicit horizontal scroll, if needed
2423 	if (!_ensureInsertionPointOnScreen() && !bVScroll)
2424 	{
2425 		_fixInsertionPointCoords();
2426 	}
2427 }
2428 
2429 static bool bScrollRunning = false;
2430 static UT_Worker * s_pScroll = NULL;
2431 
_actuallyScroll(UT_Worker * pWorker)2432 void FV_View::_actuallyScroll(UT_Worker * pWorker)
2433 {
2434 
2435 	FV_View * pView = static_cast<FV_View *>(pWorker->getInstanceData());
2436 	UT_return_if_fail(pView);
2437 	if(pView->getLayout()->getDocument()->isPieceTableChanging())
2438 	{
2439 		return;
2440 	}
2441 
2442 	PT_DocPosition iOldPoint = pView->getPoint();
2443 	UT_DEBUGMSG(("Doing autoscroll \n"));
2444 	/*
2445 	  NOTE: We update the selection here, so that the timer can keep
2446 	  triggering autoscrolls even if the mouse doesn't move.
2447 	*/
2448 	pView->extSelToXY(pView->m_xLastMouse, pView->m_yLastMouse, false);
2449 
2450 	if (pView->getPoint() != iOldPoint)
2451 	{
2452 		// do the autoscroll
2453 		pView->_ensureInsertionPointOnScreen();
2454 	}
2455 	else
2456 	{
2457 		// not far enough to change the selection ... do we still need to scroll?
2458 		UT_sint32 xPos = pView->m_xLastMouse;
2459 		UT_sint32 yPos = pView->m_yLastMouse;
2460 
2461 		// TODO: clamp xPos, yPos to viewable area??
2462 
2463 		bool bOnScreen = true;
2464 
2465 		if ((xPos < 0 || xPos > pView->getWindowWidth()) ||
2466 			(yPos < 0 || yPos > pView->getWindowHeight()))
2467 			bOnScreen = false;
2468 
2469 		if (!bOnScreen)
2470 		{
2471 			// yep, do it manually
2472 
2473 			// TODO currently we blindly send these auto scroll events without regard
2474 			// TODO to whether the window can scroll any further in that direction.
2475 			// TODO we could optimize this a bit and check the scroll range before we
2476 			// TODO fire them, but that knowledge is only stored in the frame and we
2477 			// TODO don't have a backpointer to it.
2478 			// UT_DEBUGMSG(("_auto: [xp %ld][yp %ld] [w %ld][h %ld]\n",
2479 			//			 xPos,yPos,pView->getWindowWidth(),pView->getWindowHeight()));
2480 			//
2481 			// Sevior: Is This what you wanted? Uncomment these lines when
2482 			// needed.
2483 			//
2484 			//XAP_Frame * pFrame = static_cast<XAP_Frame *>(pView->getParentData());
2485 			//UT_ASSERT((pFrame));
2486 
2487 			if (yPos < 0)
2488 			{
2489 				pView->cmdScroll(AV_SCROLLCMD_LINEUP, static_cast<UT_sint32>(-yPos));
2490 			}
2491 			else if ((static_cast<UT_uint32>(yPos)) >= (static_cast<UT_uint32>(pView->getWindowHeight())))
2492 			{
2493 				pView->cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_sint32>(yPos - pView->getWindowHeight()));
2494 			}
2495 
2496 			if (xPos < 0)
2497 			{
2498 				pView->cmdScroll(AV_SCROLLCMD_LINELEFT, (UT_uint32) (-(xPos)));
2499 			}
2500 			else if ((static_cast<UT_uint32>(xPos)) >= (static_cast<UT_uint32>(pView->getWindowWidth())))
2501 			{
2502 				pView->cmdScroll(AV_SCROLLCMD_LINERIGHT, static_cast<UT_uint32>(xPos - pView->getWindowWidth()));
2503 			}
2504 		}
2505 	}
2506 	s_pScroll->stop();
2507 	delete s_pScroll;
2508 	s_pScroll = NULL;
2509 	bScrollRunning = false;
2510 }
2511 
_autoScroll(UT_Worker * pWorker)2512 void FV_View::_autoScroll(UT_Worker * pWorker)
2513 {
2514 	UT_return_if_fail(pWorker);
2515 	if(bScrollRunning)
2516 		{
2517 			UT_DEBUGMSG(("Dropping autoscroll !!!!!!! \n"));
2518 			return;
2519 		}
2520 	// this is a static callback method and does not have a 'this' pointer.
2521 
2522 	FV_View * pView = static_cast<FV_View *>(pWorker->getInstanceData());
2523 	UT_return_if_fail(pView);
2524 	if(pView->getLayout()->getDocument()->isPieceTableChanging())
2525 	{
2526 		return;
2527 	}
2528 
2529 	int inMode = UT_WorkerFactory::IDLE | UT_WorkerFactory::TIMER;
2530 	UT_WorkerFactory::ConstructMode outMode = UT_WorkerFactory::NONE;
2531 	s_pScroll = UT_WorkerFactory::static_constructor (_actuallyScroll,pView, inMode, outMode);
2532 
2533 	// If the worker is working on a timer instead of in the idle
2534 	// time, set the frequency of the checks.
2535 	if ( UT_WorkerFactory::TIMER == outMode )
2536 	{
2537 		// this is really a timer, so it's safe to static_cast it
2538 		static_cast<UT_Timer*>(s_pScroll)->set(1);
2539 	}
2540 	bScrollRunning = true;
2541 	s_pScroll->start();
2542 }
2543 
2544 /*! Returns the page the user's mouse pointer is in.
2545  *
2546  */
_getPageForXY(UT_sint32 xPos,UT_sint32 yPos,UT_sint32 & xClick,UT_sint32 & yClick) const2547 fp_Page* FV_View::_getPageForXY(UT_sint32 xPos, UT_sint32 yPos, UT_sint32& xClick, UT_sint32& yClick) const
2548 {
2549 	xClick = xPos + m_xScrollOffset - getPageViewLeftMargin();
2550 	yClick = yPos + m_yScrollOffset - getPageViewTopMargin();
2551 	fp_Page* pPage = m_pLayout->getFirstPage();
2552 
2553 	if (xClick <= (signed)getWidthPagesInRow(pPage))  //So we can't select the next row by clicking outside
2554 	{
2555 		while (pPage) //First, find the row the page is in
2556 		{
2557 			UT_uint32 iNumHorizPages = getNumHorizPages();
2558 			UT_sint32 iPageHeight = pPage->getHeight();
2559 
2560 			if(getViewMode() != VIEW_PRINT)
2561 			{
2562 				iPageHeight = iPageHeight - pPage->getOwningSection()->getTopMargin() -
2563 					pPage->getOwningSection()->getBottomMargin();
2564 			}
2565 
2566 			if (yClick < iPageHeight)
2567 			{
2568 				//Found the first page in the row
2569 				break;
2570 			}
2571 			else
2572 			{
2573 				yClick -= iPageHeight + getPageViewSep();
2574 			}
2575 
2576 			//Loop because we're jumping entire rows
2577 			for (unsigned int i = 0; i < iNumHorizPages; i++)
2578 			{
2579 				if (pPage->getNext())
2580 				{
2581 					pPage = pPage->getNext();
2582 				}
2583 			}
2584 		}
2585 
2586 		while (pPage) //Now, find the page in the row.
2587 		{
2588 			UT_sint32 iPageWidth = pPage->getWidth();
2589 
2590 			if (xClick > iPageWidth && !rtlPages()) // Left to right
2591 			{
2592 				xClick -= iPageWidth + getHorizPageSpacing();
2593 			}
2594 			else if (xClick < static_cast<UT_sint32>(getWidthPrevPagesInRow(m_pLayout->findPage(pPage))) && rtlPages()) // Right to left
2595 			{
2596 				//Don't need to do anything.
2597 			}
2598 			else
2599 			{
2600 				if (rtlPages())
2601 				{
2602 					xClick -= getWidthPrevPagesInRow(m_pLayout->findPage(pPage));
2603 				}
2604 
2605 				//Found the page. Huzzah!
2606 				xxx_UT_DEBUGMSG(("     yClick %d \t     xClick %d\tPage %d\n", yClick, xClick, m_pLayout->findPage(pPage)));
2607 				xxx_UT_DEBUGMSG(("iPageHeight %d \t iPageWidth %d | %d\n", pPage->getHeight(), iPageWidth, getWidthPagesInRow(pPage)));
2608 				break;
2609 			}
2610 			pPage = pPage->getNext();
2611 		}
2612 	}
2613 
2614 	if (!pPage)
2615 	{
2616 		// we're below the last page
2617 		pPage = m_pLayout->getLastPage();
2618 		if(pPage == NULL)
2619 	    {
2620 			pPage = m_pLayout->getFirstPage();
2621 		}
2622 		if(pPage == NULL)
2623 		{
2624 			return pPage;
2625 		}
2626 		UT_sint32 iPageHeight = pPage->getHeight();
2627 		yClick += iPageHeight + getPageViewSep();
2628 	}
2629 	return pPage;
2630 }
2631 
2632 
2633 /*!
2634  Compute prefix function for search
2635  \param pFind String to find
2636  \param bMatchCase True to match case, false to ignore case
2637 */
2638 UT_uint32*
_computeFindPrefix(const UT_UCSChar * pFind)2639 FV_View::_computeFindPrefix(const UT_UCSChar* pFind)
2640 {
2641 	UT_uint32 m = UT_UCS4_strlen(pFind);
2642 	UT_uint32 k = 0, q = 1;
2643 	UT_uint32 *pPrefix = (UT_uint32*) UT_calloc(m + 1, sizeof(UT_uint32));
2644 	UT_return_val_if_fail(pPrefix, NULL);
2645 
2646 	pPrefix[0] = 0; // Must be this regardless of the string
2647 
2648 	if (m_bMatchCase)
2649 	{
2650 		for (q = 1; q < m; q++)
2651 		{
2652 			while (k > 0 && pFind[k] != pFind[q])
2653 				k = pPrefix[k - 1];
2654 			if(pFind[k] == pFind[q])
2655 				k++;
2656 			pPrefix[q] = k;
2657 		}
2658 	}
2659 	else
2660 	{
2661 		for (q = 1; q < m; q++)
2662 		{
2663 			while (k > 0
2664 				   && UT_UCS4_tolower(pFind[k]) != UT_UCS4_tolower(pFind[q]))
2665 				k = pPrefix[k - 1];
2666 			if(UT_UCS4_tolower(pFind[k]) == UT_UCS4_tolower(pFind[q]))
2667 				k++;
2668 			pPrefix[q] = k;
2669 		}
2670 	}
2671 
2672 	return pPrefix;
2673 }
2674 
s_smartQuoteToPlain(UT_UCS4Char currentChar)2675 static inline UT_UCS4Char s_smartQuoteToPlain(UT_UCS4Char currentChar)
2676 {
2677 	switch(currentChar)
2678 	{
2679 		case 0x201A:     //single low 9 quotation
2680 		case 0x201B:     //single reverse comma quotation
2681 		case UCS_LQUOTE:
2682 		case UCS_RQUOTE: return (UT_UCS4Char) '\'';
2683 
2684 		case 0x201E:     //double low 9 quotation
2685 		case 0x201F:     //double reverse comma quotation
2686 		case UCS_LDBLQUOTE:
2687 		case UCS_RDBLQUOTE: return (UT_UCS4Char) '\"';
2688 
2689 		default: return currentChar;
2690 	}
2691 
2692 	return currentChar;
2693 }
2694 
2695 
2696 /*!
2697  Find next occurrence of string
2698  \param pFind String to find
2699  \param True to match case, false to ignore case
2700  \result bDoneEntireDocument True if entire document searched,
2701 		 false otherwise
2702  \return True if string was found, false otherwise
2703 
2704  \fixme The conversion of UCS_RQUOTE should happen in some generic
2705 		function - it is presently done lot's of places in the code.
2706 */
2707 bool
_findNext(UT_uint32 * pPrefix,bool & bDoneEntireDocument)2708 FV_View::_findNext(UT_uint32* pPrefix,
2709 				   bool& bDoneEntireDocument)
2710 {
2711 	UT_ASSERT(m_sFind);
2712 
2713 	fl_BlockLayout* block = _findGetCurrentBlock();
2714 	PT_DocPosition offset = _findGetCurrentOffset();
2715 	UT_UCSChar* buffer = NULL;
2716 	UT_uint32 m = UT_UCS4_strlen(m_sFind);
2717 
2718 	// Clone the search string, converting it to lowercase is search
2719 	// should ignore case.
2720 	UT_UCSChar* pFindStr = (UT_UCSChar*) UT_calloc(m, sizeof(UT_UCSChar));
2721 	UT_return_val_if_fail(pFindStr,false);
2722 
2723 	UT_uint32 j;
2724 	if (m_bMatchCase)
2725 	{
2726 		for (j = 0; j < m; j++)
2727 			pFindStr[j] = m_sFind[j];
2728 	}
2729 	else
2730 	{
2731 		for (j = 0; j < m; j++)
2732 			pFindStr[j] = UT_UCS4_tolower(m_sFind[j]);
2733 	}
2734 
2735 	// Now we use the prefix function (stored as an array) to search
2736 	// through the document text.
2737 	while ((buffer = _findGetNextBlockBuffer(&block, &offset)))
2738 	{
2739 		UT_sint32 foundAt = -1;
2740 		UT_uint32 i = 0, t = 0;
2741 
2742 		UT_UCSChar currentChar;
2743 
2744 		while ((currentChar = buffer[i]) /*|| foundAt == -1*/)
2745 		{
2746 			// Convert smart quote to plain equivalent
2747 			// for smart quote matching
2748 			UT_UCS4Char cPlainQuote = s_smartQuoteToPlain(currentChar);
2749 
2750 			if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
2751 
2752 			while (t > 0 && pFindStr[t] != currentChar && pFindStr[t] != cPlainQuote)
2753 				t = pPrefix[t-1];
2754 			if (pFindStr[t] == currentChar || pFindStr[t] == cPlainQuote)
2755 				t++;
2756 			i++;
2757 			if (t == m)
2758 			{
2759 				if (m_bWholeWord)
2760 				{
2761 					bool start = true;
2762 					if((static_cast<UT_sint32>(i) - static_cast<UT_sint32>(m)) > 0)
2763 						start = UT_isWordDelimiter(buffer[i-m-1], UCS_UNKPUNK, UCS_UNKPUNK);
2764 					bool end = UT_isWordDelimiter(buffer[i], UCS_UNKPUNK, UCS_UNKPUNK);
2765 					if (start && end)
2766 					{
2767 						foundAt = i - m;
2768 						break;
2769 					}
2770 				}
2771 				else
2772 				{
2773 					foundAt = i - m;
2774 					break;
2775 				}
2776 			}
2777 		}
2778 
2779 		// Select region of matching string if found
2780 		if (foundAt != -1)
2781 		{
2782 			_setPoint(block->getPosition(false) + offset + foundAt);
2783 			_setSelectionAnchor();
2784 			_charMotion(true, m);
2785 
2786 			m_doneFind = true;
2787 
2788 			FREEP(pFindStr);
2789 			FREEP(buffer);
2790 			return true;
2791 		}
2792 		// Didn't find anything, so set the offset to the end of the
2793 		// current area
2794 		offset += UT_MAX(UT_UCS4_strlen(buffer),1);
2795 
2796 		// Must clean up buffer returned for search
2797 		FREEP(buffer);
2798 	}
2799 
2800 	bDoneEntireDocument = true;
2801 
2802 	// Reset wrap for next time
2803 	m_wrappedEnd = false;
2804 
2805 	FREEP(pFindStr);
2806 
2807 	return false;
2808 }
2809 
2810 bool
_findPrev(UT_uint32 *,bool & bDoneEntireDocument)2811 FV_View::_findPrev(UT_uint32* /*pPrefix*/,
2812 				   bool& bDoneEntireDocument)
2813 {
2814 	UT_ASSERT(m_sFind);
2815 
2816 	fl_BlockLayout* block = _findGetCurrentBlock();
2817 	PT_DocPosition offset = _findGetCurrentOffset();
2818 	UT_UCSChar* buffer = NULL;
2819 	UT_uint32 m = UT_UCS4_strlen(m_sFind);
2820 
2821 	// Clone the search string, converting it to lowercase is search
2822 	// should ignore case.
2823 	UT_UCSChar* pFindStr = (UT_UCSChar*) UT_calloc(m, sizeof(UT_UCSChar));
2824 	UT_return_val_if_fail(pFindStr,false);
2825 
2826 	UT_uint32 j;
2827 	if (m_bMatchCase)
2828 	{
2829 		for (j = 0; j < m; j++)
2830 			pFindStr[j] = m_sFind[j];
2831 	}
2832 	else
2833 	{
2834 		for (j = 0; j < m; j++)
2835 			pFindStr[j] = UT_UCS4_tolower(m_sFind[j]);
2836 	}
2837 
2838 	// Now we use the prefix function (stored as an array) to search
2839 	// through the document text.
2840 	UT_sint32 endIndex = 0;
2841 	while ((buffer = _findGetPrevBlockBuffer(&block, &offset, endIndex)))
2842 	{
2843 		UT_sint32 foundAt = -1;
2844 		UT_uint32 i = UT_MIN(offset, UT_UCS4_strlen(buffer));
2845 		if (i > m)
2846 		{
2847 			i-=m;
2848 		}
2849 		else
2850 		{
2851 			if (i==0)
2852 				i = UT_UCS4_strlen(buffer);
2853 			else
2854 				i=0;
2855 		}
2856 
2857 		UT_uint32 t = 0;
2858 		UT_UCSChar currentChar;
2859 
2860 		while (i!=UT_uint32(-1))
2861 		{
2862 			t = 0;
2863 			currentChar = buffer[i];
2864 			UT_UCS4Char cPlainQuote = s_smartQuoteToPlain(currentChar);
2865 
2866 			if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
2867 			while (((m_sFind[t] == currentChar)||(m_sFind[t] == cPlainQuote))&& (t < m))
2868 			{
2869 				t++;
2870 				currentChar = buffer[i + t];
2871 				cPlainQuote = s_smartQuoteToPlain(currentChar);
2872 				if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
2873 			}
2874 
2875 			if (t == m) {
2876 				if (m_bWholeWord)
2877 				{
2878 					bool start = UT_isWordDelimiter(buffer[i-1], UCS_UNKPUNK, UCS_UNKPUNK);
2879 					bool end = UT_isWordDelimiter(buffer[i+m], UCS_UNKPUNK, UCS_UNKPUNK);
2880 					if (start && end)
2881 					{
2882 						foundAt = i;
2883 						break;
2884 					}
2885 				}
2886 				else
2887 				{
2888 					foundAt = i;
2889 					break;
2890 				}
2891 			}
2892 
2893 			i--;
2894 		}
2895 
2896 		// Select region of matching string if found
2897 		if (foundAt >= 0)
2898 		{
2899 
2900 			UT_DEBUGMSG(("Found pos: %d", (block)->getPosition(false)+ foundAt));
2901 			UT_DEBUGMSG((" - len: %d\n", m));
2902 
2903 			_setPoint(block->getPosition(false) + foundAt + m);
2904 			_setSelectionAnchor();
2905 			_charMotion(false, m);
2906 
2907 			m_doneFind = true;
2908 
2909 			FREEP(pFindStr);
2910 			FREEP(buffer);
2911 			return true;
2912 		}
2913 
2914 		// Didn't find anything, so set the offset to the start of the
2915 		// current area (0)
2916 		offset = 0;
2917 
2918 		// Must clean up buffer returned for search
2919 		FREEP(buffer);
2920 	}
2921 
2922 	bDoneEntireDocument = true;
2923 
2924 	// Reset wrap for next time
2925 	m_wrappedEnd = false;
2926 
2927 	FREEP(pFindStr);
2928 
2929 	return false;
2930 }
2931 PT_DocPosition
_BlockOffsetToPos(fl_BlockLayout * block,PT_DocPosition offset) const2932 FV_View::_BlockOffsetToPos(fl_BlockLayout * block, PT_DocPosition offset) const
2933 {
2934 	UT_return_val_if_fail(block, 0);
2935 	return block->getPosition(false) + offset;
2936 }
2937 
2938 UT_UCSChar*
_findGetNextBlockBuffer(fl_BlockLayout ** pBlock,PT_DocPosition * pOffset)2939 FV_View::_findGetNextBlockBuffer(fl_BlockLayout** pBlock,
2940 								 PT_DocPosition* pOffset)
2941 {
2942 	UT_ASSERT(m_pLayout);
2943 
2944 	// This assert doesn't work, since the startPosition CAN
2945 	// legitimately be zero
2946 	// The beginning of the first block in any document
2947 	UT_ASSERT(m_startPosition >= 2);
2948 
2949 	UT_ASSERT(pBlock);
2950 	UT_ASSERT(*pBlock);
2951 
2952 	UT_ASSERT(pOffset);
2953 
2954 	fl_BlockLayout* newBlock = NULL;
2955 	PT_DocPosition newOffset = 0;
2956 
2957 	UT_uint32 bufferLength = 0;
2958 
2959 	UT_GrowBuf pBuffer;
2960 
2961 	// Check early for completion, from where we left off last, and
2962 	// bail if we are now at or past the start position
2963 	if (m_wrappedEnd
2964 		&& _BlockOffsetToPos(*pBlock, *pOffset) >= m_startPosition)
2965 	{
2966 		// We're done
2967 		return NULL;
2968 	}
2969 
2970 	if (!(*pBlock)->getBlockBuf(&pBuffer))
2971 	{
2972 		UT_DEBUGMSG(("Block %p has no associated buffer.\n", *pBlock));
2973 		UT_ASSERT(0);
2974 	}
2975 
2976 	// Have we already searched all the text in this buffer?
2977 	if (*pOffset >= pBuffer.getLength())
2978 	{
2979 		bool bNeedNewBlock = true;
2980 
2981 		// if pBlock was inside some kind of an embeded section, we need to make sure we
2982 		// have finished searching the enclosing block
2983 		if((*pBlock)->isEmbeddedType())
2984 		{
2985 
2986 			fl_ContainerLayout * pCL = (*pBlock)->myContainingLayout();
2987 			UT_ASSERT((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) );
2988 			fl_EmbedLayout * pFL = static_cast<fl_EmbedLayout *>(pCL);
2989 			if(pFL->isEndFootnoteIn())
2990 			{
2991 				pf_Frag_Strux* sdhStart = pCL->getStruxDocHandle();
2992 				pf_Frag_Strux* sdhEnd = NULL;
2993 				if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
2994 				{
2995 					getDocument()->getNextStruxOfType(sdhStart,PTX_EndFootnote, &sdhEnd);
2996 				}
2997 				else
2998 				{
2999 					getDocument()->getNextStruxOfType(sdhStart,PTX_EndEndnote, &sdhEnd);
3000 				}
3001 
3002 				if(sdhEnd)
3003 				{
3004 					PT_DocPosition posStart = getDocument()->getStruxPosition(sdhStart);
3005 					fl_ContainerLayout*  psfh = NULL;
3006 					getDocument()->getStruxOfTypeFromPosition((*pBlock)->getDocLayout()->getLID(),posStart,PTX_Block, &psfh);
3007 					newBlock = static_cast<fl_BlockLayout *>(psfh);
3008 
3009 					PT_DocPosition iPos = _BlockOffsetToPos(*pBlock, *pOffset);
3010 					PT_DocPosition iEncBlockPos = newBlock->getPosition(false);
3011 
3012 					newOffset = iPos - iEncBlockPos;
3013 
3014 					pBuffer.truncate(0);
3015 
3016 					if (!newBlock->getBlockBuf(&pBuffer))
3017 					{
3018 						UT_DEBUGMSG(("Block %p (a ->next block) has no buffer.\n",
3019 									 newBlock));
3020 						UT_ASSERT(0);
3021 					}
3022 
3023 					if(pBuffer.getLength() > newOffset)
3024 					{
3025 						// still stuff left in our containing block
3026 						bNeedNewBlock = false;
3027 					}
3028 
3029 				}
3030 			}
3031 		}
3032 
3033 		if(bNeedNewBlock)
3034 		{
3035 			// Then return a fresh new block's buffer
3036 			newBlock = (*pBlock)->getNextBlockInDocument();
3037 
3038 			// Are we at the end of the document?
3039 			if (!newBlock)
3040 			{
3041 				// Then wrap (fetch the first block in the doc)
3042 				PT_DocPosition startOfDoc;
3043 				getEditableBounds(false, startOfDoc);
3044 
3045 				newBlock = m_pLayout->findBlockAtPosition(startOfDoc);
3046 
3047 				m_wrappedEnd = true;
3048 
3049 				UT_ASSERT(newBlock);
3050 			}
3051 
3052 			// Re-assign the buffer contents for our new block
3053 			pBuffer.truncate(0);
3054 
3055 			// The offset starts at 0 for a fresh buffer
3056 			newOffset = 0;
3057 			if (!newBlock->getBlockBuf(&pBuffer))
3058 			{
3059 				UT_DEBUGMSG(("Block %p (a ->next block) has no buffer.\n",
3060 							 newBlock));
3061 				UT_ASSERT(0);
3062 			}
3063 
3064 			// Good to go with a full buffer for our new block
3065 		}
3066 
3067 	}
3068 	else
3069 	{
3070 		// We have some left to go in this buffer.	Buffer is still
3071 		// valid, just copy pointers
3072 		newBlock = *pBlock;
3073 		newOffset = *pOffset;
3074 	}
3075 
3076 	if(newBlock == *pBlock
3077 	   && (newBlock->getPosition(false) + pBuffer.getLength()) < m_startPosition)
3078 	{
3079 		// this happens if the document shrinks in the process of replacement
3080 		// we get the same block, but it is shorter than the stored m_startPosition
3081 		return NULL;
3082 	}
3083 
3084 	// Are we going to run into the start position in this buffer?	If
3085 	// so, we need to size our length accordingly
3086 	if (m_wrappedEnd && _BlockOffsetToPos(newBlock, newOffset) + pBuffer.getLength() >= m_startPosition)
3087 	{
3088 		// sanity check
3089 		if(m_startPosition > (newBlock)->getPosition(false) + newOffset)
3090 			bufferLength = (m_startPosition - (newBlock)->getPosition(false)) - newOffset;
3091 	}
3092 	else if(pBuffer.getLength() > newOffset)
3093 	{
3094 		bufferLength = pBuffer.getLength() - newOffset;
3095 	}
3096 
3097 	// clone a buffer (this could get really slow on large buffers!)
3098 	UT_UCSChar* bufferSegment = NULL;
3099 
3100 	// remember, the caller gets to g_free this memory
3101 	bufferSegment = static_cast<UT_UCSChar*>(UT_calloc(bufferLength + 1, sizeof(UT_UCSChar)));
3102 	UT_ASSERT(bufferSegment);
3103 
3104 	memmove(bufferSegment, pBuffer.getPointer(newOffset),
3105 			(bufferLength) * sizeof(UT_UCSChar));
3106 
3107 	// before we bail, hold up our block stuff for next round
3108 	*pBlock = newBlock;
3109 	*pOffset = newOffset;
3110 
3111 	return bufferSegment;
3112 }
3113 
3114 UT_UCSChar*
_findGetPrevBlockBuffer(fl_BlockLayout ** pBlock,PT_DocPosition * pOffset,UT_sint32 & endIndex)3115 FV_View::_findGetPrevBlockBuffer(fl_BlockLayout** pBlock,
3116 								 PT_DocPosition* pOffset,
3117 								 UT_sint32& endIndex)
3118 {
3119 	endIndex = 0;
3120 	UT_return_val_if_fail(m_pLayout && pBlock && *pBlock && pOffset,NULL);
3121 
3122 	fl_BlockLayout* newBlock = NULL;
3123 	PT_DocPosition newOffset = 0;
3124 
3125 	UT_uint32 blockStart = 0;
3126 	UT_uint32 bufferLength = 0;
3127 
3128 	UT_GrowBuf pBuffer;
3129 
3130 	// Check early for completion, from where we left off last, and
3131 	// bail if we are now at or past the start position
3132 	UT_uint32 blockOffsetToPos = _BlockOffsetToPos(*pBlock, *pOffset);
3133 	UT_DEBUGMSG(("m_wrappedEnd=%d blockOffsetToPos=%d m_startPosition=%d\n",m_wrappedEnd,blockOffsetToPos,m_startPosition));
3134 	if (m_wrappedEnd && (blockOffsetToPos <= m_startPosition))
3135 	{
3136 		// We're done
3137 		return NULL;
3138 	}
3139 
3140 	if (!(*pBlock)->getBlockBuf(&pBuffer))
3141 	{
3142 		UT_DEBUGMSG(("Block %p has no associated buffer.\n", *pBlock));
3143 		// I gather we better return ???
3144 		UT_ASSERT_HARMLESS(0);
3145 		return NULL;
3146 	}
3147 
3148 	// Have we already searched all the text in this buffer?
3149 	if (_BlockOffsetToPos(*pBlock, *pOffset) <= (*pBlock)->getPosition(false))
3150 	{
3151 		// Then return a fresh new block's buffer
3152 		newBlock = *pBlock;
3153 	    get_new_block:	newBlock = newBlock->getPrevBlockInDocument();
3154 		xxx_UT_DEBUGMSG(("Got prev block %x \n",newBlock));
3155 		// Are we at the end of the document?
3156 		if (!newBlock)
3157 		{
3158 			if(m_wrappedEnd)
3159 				return NULL;
3160 
3161 			// Then wrap (fetch the first block in the doc)
3162 			PT_DocPosition endOfDoc;
3163 			getEditableBounds(true, endOfDoc);
3164 
3165 			newBlock = m_pLayout->findBlockAtPositionReverse(endOfDoc);
3166 
3167 			m_wrappedEnd = true;
3168 			UT_DEBUGMSG(("Reached start of doc via getPrevBlockinDocument \n"));
3169 			UT_return_val_if_fail(newBlock, NULL);
3170 		}
3171 
3172 		// Re-assign the buffer contents for our new block
3173 		pBuffer.truncate(0);
3174 		// The offset starts at end of block
3175 		blockStart = 0;
3176 		if (!newBlock->getBlockBuf(&pBuffer))
3177 		{
3178 			UT_DEBUGMSG(("Block %p (a ->prev block) has no buffer.\n",
3179 						 newBlock));
3180 			UT_ASSERT_HARMLESS(0);
3181 			return NULL;
3182 		}
3183 		newOffset = pBuffer.getLength();
3184 		if(pBuffer.getLength() == 0)
3185 		{
3186 			goto get_new_block;
3187 		}
3188 		// Good to go with a full buffer for our new block
3189 	}
3190 	else
3191 	{
3192 		// We have some left to go in this buffer.	Buffer is still
3193 		// valid, just copy pointers
3194 		newBlock = *pBlock;
3195 		newOffset = *pOffset;
3196 		blockStart = 0;
3197 	}
3198 
3199 	// Are we going to run into the start position in this buffer?	If
3200 	// so, we need to size our length accordingly
3201 	if (m_wrappedEnd && (newBlock->getPosition(false) <= m_startPosition))
3202 	{
3203 		// Check for completion now, if we are now at or past the start position
3204 		blockOffsetToPos = _BlockOffsetToPos(newBlock, newOffset);
3205 		UT_DEBUGMSG(("(2) m_wrappedEnd=%d blockOffsetToPos=%d m_startPosition=%d\n",m_wrappedEnd,blockOffsetToPos,m_startPosition));
3206 		if (blockOffsetToPos <= m_startPosition)
3207 		{
3208 			// We're done
3209 			UT_DEBUGMSG(("(2) PrevSearch completed\n"));
3210 			return NULL;
3211 		}
3212 		else
3213 		{
3214 			endIndex = (m_startPosition - (newBlock->getPosition(false)));
3215 		}
3216 	}
3217 
3218 	if(blockStart >= pBuffer.getLength())
3219 	{
3220 		// we are done, as there is nothing to search .
3221 		UT_DEBUGMSG(("PrevSearch completed \n"));
3222 		return NULL;
3223 	}
3224 
3225 	bufferLength = pBuffer.getLength() - blockStart;
3226 
3227 	// clone a buffer (this could get really slow on large buffers!)
3228 	UT_UCSChar* bufferSegment = NULL;
3229 
3230 	// remember, the caller gets to g_free this memory
3231 	bufferSegment = (UT_UCSChar*)UT_calloc(bufferLength + 1, sizeof(UT_UCSChar));
3232 	UT_return_val_if_fail(bufferSegment, NULL);
3233 
3234 	memmove(bufferSegment, pBuffer.getPointer(blockStart),
3235 			(bufferLength) * sizeof(UT_UCSChar));
3236 
3237 	// before we bail, hold up our block stuff for next round
3238 	*pBlock = newBlock;
3239 	*pOffset = newOffset;
3240 
3241 	UT_DEBUGMSG(("Block pos: %d ", (newBlock)->getPosition(false)));
3242 	UT_DEBUGMSG((" - len: %d\n", pBuffer.getLength()));
3243 
3244 
3245 	return bufferSegment;
3246 }
3247 
_insertField(const char * szName,const gchar ** extra_attrs,const gchar ** extra_props)3248 bool FV_View::_insertField(const char* szName,
3249 						   const gchar ** extra_attrs,
3250 						   const gchar ** extra_props)
3251 {
3252 	bool bResult = false;
3253 	int attrCount = 0;
3254 	if(szName && ((strcmp(szName,"sum_rows") == 0) || (strcmp(szName,"sum_cols") == 0)))
3255 	{
3256 	     if(!isInTable())
3257 	     {
3258 	          return false;
3259 	     }
3260 	}
3261 	while (extra_attrs && extra_attrs[attrCount] != NULL)
3262 	{
3263 		attrCount++;
3264 	}
3265 
3266 	const gchar ** attributes = new const gchar*[attrCount+4];
3267 
3268 	int i = 0;
3269 	while (extra_attrs && extra_attrs[i] != NULL)
3270 	{
3271 		attributes[i] = extra_attrs[i];
3272 		i++;
3273 	}
3274 	attributes[i++] = "type";
3275 	attributes[i++] = szName;
3276 	attributes[i++] = NULL;
3277 	attributes[i++] = NULL;
3278 
3279 
3280 	fd_Field * pField = NULL;
3281 	if (!isSelectionEmpty() && !m_FrameEdit.isActive())
3282 	{
3283 		m_pDoc->beginUserAtomicGlob();
3284 		_deleteSelection();
3285 
3286 		insertParaBreakIfNeededAtPos(getPoint());
3287 		if(!isPointLegal(getPoint()))
3288 		{
3289 			_makePointLegal();
3290 		}
3291 		bResult = m_pDoc->insertObject(getPoint(), PTO_Field, attributes, extra_props,&pField);
3292 		if(pField != NULL)
3293 		{
3294 			pField->update();
3295 		}
3296 		m_pDoc->endUserAtomicGlob();
3297 	}
3298 	else if(m_FrameEdit.isActive())
3299 	{
3300 	       m_FrameEdit.setPointInside();
3301 	}
3302 	else
3303 	{
3304 		insertParaBreakIfNeededAtPos(getPoint());
3305 		if(!isPointLegal(getPoint()))
3306 		{
3307 			_makePointLegal();
3308 		}
3309 		bResult = m_pDoc->insertObject(getPoint(), PTO_Field, attributes, extra_props, &pField);
3310 		if(pField != NULL)
3311 		{
3312 			pField->update();
3313 		}
3314 	}
3315 
3316 	delete [] attributes;
3317 	return bResult;
3318 }
3319 
3320 bool
_findReplaceReverse(UT_uint32 * pPrefix,bool & bDoneEntireDocument,bool bNoUpdate)3321 FV_View::_findReplaceReverse(UT_uint32* pPrefix, bool& bDoneEntireDocument, bool bNoUpdate)
3322 {
3323 	UT_ASSERT(m_sFind && m_sReplace);
3324 
3325 	bool bRes = false;
3326 
3327 	_saveAndNotifyPieceTableChange();
3328 	m_pDoc->beginUserAtomicGlob();
3329 
3330 	// Replace selection if it's due to a find operation
3331 	if (m_doneFind && !isSelectionEmpty())
3332 	{
3333 		bRes = true;
3334 
3335 		PP_AttrProp AttrProp_Before;
3336 
3337 		if (!isSelectionEmpty() && !m_FrameEdit.isActive())
3338 		{
3339 			_deleteSelection(&AttrProp_Before, bNoUpdate);
3340 		}
3341 		else if(m_FrameEdit.isActive())
3342 		  {
3343 		    m_FrameEdit.setPointInside();
3344 		  }
3345 
3346 		// If we have a string with length, do an insert, else let it
3347 		// hang from the delete above
3348 		if (*m_sReplace)
3349 		{
3350 			bRes = m_pDoc->insertSpan(getPoint(), m_sReplace,
3351 									  UT_UCS4_strlen(m_sReplace),
3352 									  &AttrProp_Before);
3353 
3354 			setPoint( getPoint() - UT_UCS4_strlen(m_sReplace)) ;
3355 		}
3356 		// Do not increase the insertion point index, since the insert
3357 		// span will leave us at the correct place.
3358 
3359 		if(!bNoUpdate)
3360 			_generalUpdate();
3361 
3362 
3363 		// If we've wrapped around once, and we're doing work before
3364 		// we've hit the point at which we started, then we adjust the
3365 		// start position so that we stop at the right spot.
3366 		if (m_wrappedEnd && !bDoneEntireDocument)
3367 		{
3368 			m_startPosition += (long) UT_UCS4_strlen(m_sReplace);
3369 			m_startPosition -= (long) UT_UCS4_strlen(m_sFind);
3370 		}
3371 
3372 		UT_ASSERT(m_startPosition >= 2);
3373 	}
3374 
3375 	m_pDoc->endUserAtomicGlob();
3376 	_restorePieceTableState();
3377 
3378 	// Find next occurrence in document
3379 	_findPrev(pPrefix, bDoneEntireDocument);
3380 	return bRes;
3381 }
3382 
3383 /*!
3384  Find and replace text unit
3385  \param pFind String to find
3386  \param pReplace String to replace it with
3387  \param pPrefix Search prefix function
3388  \param bMatchCase True to match case, false to ignore case
3389  \result bDoneEntireDocument True if entire document searched,
3390 		 false otherwise
3391  \return True if string was replaced, false otherwise
3392 
3393  This function will replace an existing selection with pReplace. It
3394  will then do a search for pFind.
3395 */
3396 bool
_findReplace(UT_uint32 * pPrefix,bool & bDoneEntireDocument,bool bNoUpdate)3397 FV_View::_findReplace(UT_uint32* pPrefix, bool& bDoneEntireDocument, bool bNoUpdate)
3398 {
3399 	UT_ASSERT(m_sFind && m_sReplace);
3400 
3401 	bool bRes = false;
3402 
3403 	_saveAndNotifyPieceTableChange();
3404 	m_pDoc->beginUserAtomicGlob();
3405 
3406 	// Replace selection if it's due to a find operation
3407 	if (m_doneFind && !isSelectionEmpty())
3408 	{
3409 		bRes = true;
3410 
3411 		PP_AttrProp AttrProp_Before;
3412 
3413 		if (!isSelectionEmpty() && !m_FrameEdit.isActive())
3414 		{
3415 			_deleteSelection(&AttrProp_Before, bNoUpdate);
3416 		}
3417 		else if(m_FrameEdit.isActive())
3418 		{
3419 		  m_FrameEdit.setPointInside();
3420 		}
3421 
3422 		// If we have a string with length, do an insert, else let it
3423 		// hang from the delete above
3424 		if (*m_sReplace)
3425 			bRes = m_pDoc->insertSpan(getPoint(), m_sReplace,
3426 									  UT_UCS4_strlen(m_sReplace),
3427 									  &AttrProp_Before);
3428 
3429 		// Do not increase the insertion point index, since the insert
3430 		// span will leave us at the correct place.
3431 
3432 		if(!bNoUpdate)
3433 			_generalUpdate();
3434 
3435 		// If we've wrapped around once, and we're doing work before
3436 		// we've hit the point at which we started, then we adjust the
3437 		// start position so that we stop at the right spot.
3438 		if (m_wrappedEnd && !bDoneEntireDocument)
3439 		{
3440 			m_startPosition += (long) UT_UCS4_strlen(m_sReplace);
3441 			m_startPosition -= (long) UT_UCS4_strlen(m_sFind);
3442 		}
3443 
3444 		UT_ASSERT(m_startPosition >= 2);
3445 	}
3446 
3447 	m_pDoc->endUserAtomicGlob();
3448 	_restorePieceTableState();
3449 
3450 	// Find next occurrence in document
3451 	_findNext(pPrefix, bDoneEntireDocument);
3452 	return bRes;
3453 }
3454 
3455 fl_BlockLayout*
_findGetCurrentBlock(void) const3456 FV_View::_findGetCurrentBlock(void) const
3457 {
3458 	return _findBlockAtPosition(m_iInsPoint);
3459 }
3460 
3461 PT_DocPosition
_findGetCurrentOffset() const3462 FV_View::_findGetCurrentOffset() const
3463 {
3464 	return (m_iInsPoint - _findGetCurrentBlock()->getPosition(false));
3465 }
3466 
3467 // Any takers?
3468 UT_sint32
_findBlockSearchRegexp(const UT_UCSChar *,const UT_UCSChar *)3469 FV_View::_findBlockSearchRegexp(const UT_UCSChar* /* haystack */,
3470 								const UT_UCSChar* /* needle */)
3471 {
3472 	UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
3473 
3474 	return -1;
3475 }
3476 
3477 /*
3478   After most editing commands, it is necessary to call this method,
3479   _generalUpdate, in order to fix everything.
3480 */
_generalUpdate(void)3481 void FV_View::_generalUpdate(void)
3482 {
3483 	if(!shouldScreenUpdateOnGeneralUpdate())
3484 		return;
3485 
3486 	m_pDoc->signalListeners(PD_SIGNAL_UPDATE_LAYOUT);
3487 
3488 //
3489 // No need to update other stuff if we're doing a preview
3490 //
3491 	if(isPreview())
3492 		return;
3493 //
3494 // If we're in an illegal position move forward till we're safe.
3495 //
3496 	_makePointLegal();
3497 	/*
3498 	  TODO note that we are far too heavy handed with the mask we
3499 	  send here.  I ripped out all the individual calls to notifyListeners
3500 	  which appeared within fl_BlockLayout, and they all now go through
3501 	  here.  For that reason, I made the following mask into the union
3502 	  of all the masks I found.  I assume that this is inefficient, but
3503 	  functionally correct.
3504 
3505 	  TODO WRONG! WRONG! WRONG! notifyListener() must be called in
3506 	  TODO WRONG! WRONG! WRONG! fl_BlockLayout in response to a change
3507 	  TODO WRONG! WRONG! WRONG! notification and not here.	this call
3508 	  TODO WRONG! WRONG! WRONG! will only update the current window.
3509 	  TODO WRONG! WRONG! WRONG! having the notification in fl_BlockLayout
3510 	  TODO WRONG! WRONG! WRONG! will get each view on the document.
3511 	*/
3512 //
3513 //	notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK );
3514 	if(!m_pDoc->isDoingPaste())
3515 	{
3516 		notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK | AV_CHG_PAGECOUNT | AV_CHG_FMTSTYLE );
3517 		setCursorToContext();
3518 	}
3519 }
3520 
3521 
_extSel(UT_uint32 iOldPoint)3522 void FV_View::_extSel(UT_uint32 iOldPoint)
3523 {
3524 	/*
3525 	  We need to calculate the differences between the old
3526 	  selection and new one.
3527 
3528 	  Anything which was selected, and now is not, should
3529 	  be fixed on screen, back to normal.
3530 
3531 	  Anything which was NOT selected, and now is, should
3532 	  be fixed on screen, to show it in selected state.
3533 
3534 	  Anything which was selected, and is still selected,
3535 	  should NOT be touched.
3536 
3537 	  And, obviously, anything which was not selected, and
3538 	  is still not selected, should not be touched.
3539 	*/
3540 	UT_uint32 iNewPoint = getPoint();
3541 	xxx_UT_DEBUGMSG(("_extSel: iNewPoint %d ioldPoint %d selectionAnchor %d \n",iNewPoint,iOldPoint,m_Selection.getSelectionAnchor()));
3542 	PT_DocPosition posBOD,posEOD,dNewPoint,dOldPoint;
3543 	dNewPoint = static_cast<PT_DocPosition>(iNewPoint);
3544 	dOldPoint = static_cast<PT_DocPosition>(iOldPoint);
3545 	getEditableBounds(false,posBOD);
3546 	getEditableBounds(true,posEOD);
3547 	if(dNewPoint < posBOD || dNewPoint > posEOD || dOldPoint < posBOD
3548 	   || dOldPoint > posEOD)
3549 	{
3550 		return;
3551 	}
3552 	if (iNewPoint == iOldPoint)
3553 	{
3554 		return;
3555 	}
3556 	if(iOldPoint < iNewPoint)
3557 	{
3558 		_drawBetweenPositions(iOldPoint, iNewPoint);
3559 	}
3560 	else
3561 	{
3562 		_drawBetweenPositions(iNewPoint,iOldPoint);
3563 	}
3564 	if(getPoint() > getSelectionAnchor())
3565 	{
3566 		m_Selection.setSelectionLeftAnchor(getSelectionAnchor());
3567 		m_Selection.setSelectionRightAnchor(getPoint());
3568 	}
3569 	else
3570 	{
3571 		m_Selection.setSelectionRightAnchor(m_Selection.getSelectionAnchor());
3572 		m_Selection.setSelectionLeftAnchor(getPoint());
3573 	}
3574 
3575 }
3576 
_extSelToPos(PT_DocPosition iNewPoint)3577 void FV_View::_extSelToPos(PT_DocPosition iNewPoint)
3578 {
3579 	PT_DocPosition iOldPoint = getPoint();
3580 	if (iNewPoint == iOldPoint)
3581 		return;
3582 
3583 	PT_DocPosition posBOD,posEOD,dNewPoint,dOldPoint;
3584 	dNewPoint = static_cast<PT_DocPosition>(iNewPoint);
3585 	dOldPoint = static_cast<PT_DocPosition>(iOldPoint);
3586 	getEditableBounds(false,posBOD);
3587 	getEditableBounds(true,posEOD);
3588 	if(dNewPoint < posBOD || dNewPoint > posEOD || dOldPoint < posBOD
3589 	   || dNewPoint > posEOD)
3590 	{
3591 		return;
3592 	}
3593 
3594 	if (isSelectionEmpty())
3595 	{
3596 		_fixInsertionPointCoords();
3597 		_clearIfAtFmtMark(getPoint());
3598 		_setSelectionAnchor();
3599 	}
3600 	m_Selection.setMode(FV_SelectionMode_Single);
3601 	_setPoint(iNewPoint);
3602 //
3603 // Look if we should select the initial cell.
3604 //
3605 	_extSel(iOldPoint);
3606 	if(getSelectionAnchor() < getPoint())
3607 	{
3608 		PT_DocPosition posLow = getSelectionAnchor();
3609 		fp_CellContainer * pLowCell = NULL;
3610 		fp_CellContainer * pHighCell = NULL;
3611 		if(isInTable(posLow) )
3612 		{
3613 			pLowCell = getCellAtPos(posLow+1);
3614 			pHighCell =  getCellAtPos(getPoint());
3615 			if((pLowCell != NULL) && (pLowCell != pHighCell))
3616 			{
3617 				fl_CellLayout * pCell = static_cast<fl_CellLayout *>(pLowCell->getSectionLayout());
3618 				PT_DocPosition posCell = pCell->getPosition(true);
3619 				xxx_UT_DEBUGMSG(("posCell %d posLow %d \n",posCell,posLow));
3620 				if((posCell == posLow) && (m_iGrabCell == 0))
3621 				{
3622 					m_iGrabCell++;
3623 					m_Selection.setSelectionAnchor(posCell-1);
3624 					_drawBetweenPositions(posCell-1, getPoint());
3625 				}
3626 				else if(((posCell + 1) == posLow) && (m_iGrabCell == 0))
3627 				{
3628 					m_iGrabCell++;
3629 					m_Selection.setSelectionAnchor(posCell-1);
3630 					_drawBetweenPositions(posCell-1, getPoint());
3631 				}
3632 				else if(((posCell + 2) == posLow) && (m_iGrabCell == 0))
3633 				{
3634 					m_iGrabCell++;
3635 					m_Selection.setSelectionAnchor(posCell-1);
3636 					_drawBetweenPositions(posCell-1, getPoint());
3637 				}
3638 //
3639 // FIXME look to see if we've selected a whole row.
3640 //
3641 			}
3642 		}
3643 	}
3644 
3645 	if (isSelectionEmpty())
3646 	{
3647 		_resetSelection();
3648 	}
3649 
3650 //	notifyListeners(AV_CHG_MOTION);
3651 }
3652 
3653 
3654 /*
3655   This method simply iterates over every run between two doc positions
3656   and draws each one.
3657 */
_drawBetweenPositions(PT_DocPosition iPos1,PT_DocPosition iPos2)3658 void FV_View::_drawBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2)
3659 {
3660 	_drawOrClearBetweenPositions(iPos1, iPos2, false,false);
3661 }
3662 
3663 /*!
3664   This class was local to FV_View::_drawOrClearBetweenPositions(),
3665   but you can't use local types for a template instancation.
3666   HACK: I did move it outside and made everything private to prevent
3667   anyone else to use it.
3668  */
3669 class ABI_EXPORT CellLine
3670 {
3671 private:
CellLine(void)3672 	CellLine(void):
3673 		m_pCell(NULL),
3674 		m_pBrokenTable(NULL),
3675 		m_pLine(NULL)
3676 		{}
~CellLine(void)3677 	virtual ~CellLine(void)
3678 		{
3679 			m_pCell = NULL;
3680 			m_pBrokenTable = NULL;
3681 			m_pLine = NULL;
3682 		}
3683 	fp_CellContainer * m_pCell;
3684 	fp_TableContainer * m_pBrokenTable;
3685 	fp_Line * m_pLine;
3686 	friend class FV_View;
3687 	friend bool FV_View::_drawOrClearBetweenPositions(PT_DocPosition iPos1,
3688 													  PT_DocPosition iPos2,
3689 													  bool bClear, bool bFullLineHeight);
3690 };
3691 
3692 /*!
3693   This method simply iterates over every run between two doc positions
3694   and draws or clears and redraws each one. We clear if bClear is true
3695   otherwise we just draw selected.
3696   If bClear is true then the if bFullLineHeight is true the runs are
3697   cleared to their full height.
3698 */
_drawOrClearBetweenPositions(PT_DocPosition iPos1,PT_DocPosition iPos2,bool bClear,bool bFullLineHeight)3699 bool FV_View::_drawOrClearBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2, bool bClear, bool bFullLineHeight)
3700 {
3701 //
3702 // Handle little class for redrawing lines around cells
3703 //
3704 
3705 	// make sure iPos1 < iPos2
3706 	if (iPos1 >= iPos2)
3707 	{
3708 		PT_DocPosition tmp = iPos1;
3709 		iPos1 = iPos2;
3710 		iPos2 = tmp;
3711 	}
3712 
3713 //	CHECK_WINDOW_SIZE
3714 	xxx_UT_DEBUGMSG(("Draw between positions %d to %d \n",iPos1,iPos2));
3715 	fp_Run* pRun1;
3716 	fp_Run* pRun2;
3717 	UT_sint32 xoff2;
3718 	UT_sint32 yoff2;
3719 	UT_uint32 uheight;
3720 	UT_GenericVector<CellLine *> vecTables;
3721 	UT_GenericVector<fp_Page *>vecPages;
3722 //
3723 // This fixes a bug from insert file, when the view we copy from is selected
3724 // If don't bail out now we get all kinds of crazy dirty on the screen.
3725 //
3726 	if(m_pParentData == NULL)
3727 	{
3728 		return true;
3729 	}
3730 //	_fixInsertionPointCoords();
3731 	{
3732 		UT_sint32 x;
3733 		UT_sint32 y;
3734 		UT_sint32 x2;
3735 		UT_sint32 y2;
3736 		bool bDirection;
3737 		fl_BlockLayout* pBlock1;
3738 		fl_BlockLayout* pBlock2;
3739 
3740 		/*
3741 		  we don't really care about the coords.  We're calling these
3742 		  to get the Run pointer
3743 		*/
3744 		_findPositionCoords(iPos1, false, x, y, x2, y2, uheight, bDirection, &pBlock1, &pRun1);
3745 		_findPositionCoords(iPos2, false, x, y, x2, y2, uheight, bDirection, &pBlock2, &pRun2);
3746 	}
3747 
3748 	UT_return_val_if_fail(pRun1 && pRun2, false );
3749 
3750 	bool bDone = false;
3751 	bool bIsDirty = false;
3752 	fp_Run* pCurRun = pRun1;
3753 	//bool bShowHidden = getShowPara();
3754 	fp_CellContainer * pCell = NULL;
3755 	fl_BlockLayout* pBlockEnd = pRun2->getBlock();
3756 	PT_DocPosition posEnd = pBlockEnd->getPosition() + pRun2->getBlockOffset();
3757 
3758 	FV_ViewDoubleBuffering dblBufferingObj(this, false, false);
3759 	dblBufferingObj.beginDoubleBuffering();
3760 
3761 	while ((!bDone || bIsDirty) && pCurRun)
3762 	{
3763 
3764 		fl_BlockLayout* pBlock2 = pCurRun->getBlock();
3765 		fp_Line * pLine = pCurRun->getLine();
3766 		if(pLine == NULL || (pLine->getContainer()->getPage()== NULL))
3767 		{
3768 			UT_VECTOR_PURGEALL(CellLine *, vecTables);
3769 			return true;
3770 		}
3771 		PT_DocPosition curpos = pBlock2->getPosition() + pCurRun->getBlockOffset();
3772 		if ((pCurRun->getLength() > 0 ) && (pCurRun == pRun2 || curpos >= posEnd))
3773 		{
3774 			bDone = true;
3775 		}
3776 		if(curpos > posEnd)
3777 		{
3778 //			break;
3779 		}
3780 		xxx_UT_DEBUGMSG(("draw_between positions pos is %d width is %d \n",curpos,pCurRun->getWidth()));
3781 		UT_return_val_if_fail(pBlock2,false);
3782 //
3783 // Look to see if the Block is in a table.
3784 //
3785 		fl_ContainerLayout * pCL = pBlock2->myContainingLayout();
3786 		bool bCellSelected = false;
3787 		if(pCL->getContainerType() == FL_CONTAINER_CELL)
3788 		{
3789 			fp_Container * pCP = static_cast<fp_Container *>(pLine->getContainer());
3790 			if(pCP)
3791 			{
3792 				pCell = static_cast<fp_CellContainer *>(pCP);
3793 				fp_TableContainer * pTab = pCell->getBrokenTable(pLine);
3794 				if(pTab)
3795 				{
3796 					CellLine * pCellLine = new CellLine();
3797 					pCellLine->m_pCell = pCell;
3798 					pCellLine->m_pLine = pLine;
3799 					pCellLine->m_pBrokenTable = pTab;
3800 					xxx_UT_DEBUGMSG(("cellLine %x cell %x Table %x Line %x \n",pCellLine,pCellLine->m_pCell,pCellLine->m_pBrokenTable,pCellLine->m_pLine));
3801 					vecTables.addItem(pCellLine);
3802 					fp_Page * pPage = pTab->getPage();
3803 					if((pPage != NULL) && (vecPages.findItem(pPage) <0))
3804 					{
3805 						vecPages.addItem(pPage);
3806 					}
3807 				}
3808 			}
3809 			fl_CellLayout * pCellLayout = static_cast<fl_CellLayout *>(pCL);
3810 //
3811 // See if the whole cell is selected. If so draw it.
3812 //
3813 			bCellSelected = pCellLayout->isCellSelected();
3814 			fp_Container * pNextCon = NULL;
3815 			if(bCellSelected)
3816 			{
3817 				pNextCon = pCell->drawSelectedCell(pCurRun->getLine());
3818 				if(pNextCon == NULL)
3819 				{
3820 					fl_BlockLayout * pBlock = pCurRun->getBlock();
3821 					pBlock = pBlock->getNextBlockInDocument();
3822 					if(pBlock)
3823 					{
3824 						pCurRun = pBlock->getFirstRun();
3825 						continue;
3826 					}
3827 					pCurRun = NULL;
3828 					continue;
3829 				}
3830 				if(pNextCon->getContainerType() == FP_CONTAINER_LINE)
3831 				{
3832 					pCurRun = static_cast<fp_Line *>(pNextCon)->getFirstRun();
3833 					continue;
3834 				}
3835 				else
3836 				{
3837 					UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
3838 					pCurRun = NULL;
3839 					continue;
3840 				}
3841 			}
3842 			else
3843 			{
3844 				if(pCell && pCell->isSelected())
3845 				{
3846 					pCell->clearSelection();
3847 					pCell->clearScreen();
3848 					pCell->draw(pCurRun->getLine());
3849 					if(pCurRun->getLine() != NULL)
3850 					{
3851 						fp_Page * pPage = pCurRun->getLine()->getPage();
3852 						if((pPage != NULL) && (vecPages.findItem(pPage) <0))
3853 						{
3854 							vecPages.addItem(pPage);
3855 						}
3856 					}
3857 				}
3858 			}
3859 		}
3860 
3861 		if(!pCurRun->isHidden())
3862 		{
3863 			if(pLine == NULL || (pLine->getContainer()->getPage()== NULL))
3864 			{
3865 				UT_VECTOR_PURGEALL(CellLine *, vecTables);
3866 				return true;
3867 			}
3868 			pLine->getScreenOffsets(pCurRun, xoff2, yoff2);
3869 			dg_DrawArgs da;
3870 			da.bDirtyRunsOnly = false;
3871 			da.pG = m_pG;
3872 			da.xoff = xoff2;
3873 			da.yoff = yoff2 + pLine->getAscent();
3874 			xxx_UT_DEBUGMSG(("Draw Position CurRun %x CurLine %x Yoffset %d \n",pCurRun,pLine,yoff2));
3875 			if(!bClear)
3876 			{
3877 				xxx_UT_DEBUGMSG(("Draw Position Low %d High %d anchor %d point %d xoff %d \n",iPos1,iPos2,getSelectionAnchor(),getPoint(),xoff2));
3878 //				UT_sint32 iLow = getSelectionAnchor();
3879 //				UT_sint32 iHigh = getPoint();
3880 //				if(iHigh < iLow
3881 				pCurRun->setSelectionMode(iPos1-1,iPos2+1);
3882 				pCurRun->draw(&da);
3883 				pCurRun->clearSelectionMode();
3884 			}
3885 			else
3886 			{
3887 				xxx_UT_DEBUGMSG(("Clear Position Low %d High %d anchor %d point %d xoff %d \n",iPos1,iPos2,getSelectionAnchor(),getPoint(),xoff2));
3888 				pCurRun->setSelectionMode(iPos1-4,iPos2+4);
3889 				pCurRun->Run_ClearScreen(bFullLineHeight);
3890 				pCurRun->draw(&da);
3891 				pCurRun->clearSelectionMode();
3892 			}
3893 			fp_Page * pPage = pLine->getPage();
3894 			if((pPage != NULL) && (vecPages.findItem(pPage) <0))
3895 			{
3896 				vecPages.addItem(pPage);
3897 			}
3898 		}
3899 
3900 		pCurRun = pCurRun->getNextRun();
3901 		if (!pCurRun)
3902 		{
3903 			fl_BlockLayout* pNextBlock;
3904 
3905 			pNextBlock = pBlock2->getNextBlockInDocument();
3906 			if (pNextBlock)
3907 			{
3908 				pCurRun = pNextBlock->getFirstRun();
3909 			}
3910 		}
3911 
3912 		if (!pCurRun)
3913 		{
3914 			bIsDirty = false;
3915 		}
3916 		else
3917 		{
3918 			bIsDirty = pCurRun->isDirty();
3919 		}
3920 	}
3921 //
3922 // Now redraw the lines in any table encountered.
3923 //
3924 	xxx_UT_DEBUGMSG(("Drawing lines in tables %d \n",vecTables.getItemCount()));
3925 	UT_sint32 i =0;
3926 	for(i=0; i< vecTables.getItemCount(); i++)
3927 	{
3928  		CellLine * pCellLine = vecTables.getNthItem(i);
3929 		pCellLine->m_pCell->drawLines(pCellLine->m_pBrokenTable,getGraphics(),true);
3930 		pCellLine->m_pCell->drawLines(pCellLine->m_pBrokenTable,getGraphics(),false);
3931 		pCellLine->m_pCell->drawLinesAdjacent();
3932 
3933 	}
3934 	for(i=0; i< vecPages.getItemCount(); i++)
3935 	{
3936 		fp_Page * pPage = vecPages.getNthItem(i);
3937 		UT_sint32 xoff,yoff;
3938 		getPageScreenOffsets(pPage,xoff, yoff);
3939 		dg_DrawArgs da;
3940 		da.pG = m_pG;
3941 		da.xoff = xoff;
3942 		da.yoff = yoff;
3943 		da.bDirtyRunsOnly = true;
3944 		pPage->redrawDamagedFrames(&da);
3945 	}
3946 	UT_VECTOR_PURGEALL(CellLine *, vecTables);
3947 	xxx_UT_DEBUGMSG(("Finished Drawing lines in tables \n"));
3948 	m_pG->flush();
3949 	_generalUpdate();
3950 	return true;
3951 }
3952 
3953 /*
3954   This method simply iterates over every run between two doc positions
3955   and draws each one.
3956 */
_clearBetweenPositions(PT_DocPosition iPos1,PT_DocPosition iPos2,bool bFullLineHeightRect)3957 bool FV_View::_clearBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2, bool bFullLineHeightRect)
3958 {
3959 	return _drawOrClearBetweenPositions(iPos1, iPos2, true,bFullLineHeightRect);
3960 #if 0
3961 	xxx_UT_DEBUGMSG(("FV_View::_clearBetweenPositions called\n"));
3962 	if (iPos1 >= iPos2)
3963 	{
3964 		return true;
3965 	}
3966 	fp_Run* pRun1;
3967 	fp_Run* pRun2;
3968 	UT_uint32 uheight;
3969 
3970 	_fixInsertionPointCoords();
3971 	{
3972 		UT_sint32 x;
3973 		UT_sint32 y;
3974 		UT_sint32 x2;
3975 		UT_sint32 y2;
3976 		bool bDirection;
3977 		fl_BlockLayout* pBlock1;
3978 		fl_BlockLayout* pBlock2;
3979 
3980 		/*
3981 		  we don't really care about the coords.  We're calling these
3982 		  to get the Run pointer
3983 		*/
3984 		_findPositionCoords(iPos1, false, x, y, x2, y2, uheight, bDirection, &pBlock1, &pRun1);
3985 		_findPositionCoords(iPos2, false, x, y, x2, y2, uheight, bDirection, &pBlock2, &pRun2);
3986 	}
3987 
3988 	if (!pRun1 && !pRun2)
3989 	{
3990 		// no formatting info for either block, so just bail
3991 		// this can happen during spell, when we're trying to invalidate
3992 		// a new squiggle before the block has been formatted
3993 		return false;
3994 	}
3995 
3996 	// HACK: In certain editing cases only one of these is NULL, which
3997 	//		 makes locating runs to clear more difficult.  For now, I'm
3998 	//		 playing it safe and trying to just handle these cases here.
3999 	//		 The real solution may be to just bail if *either* is NULL,
4000 	//		 but I'm not sure.
4001 	//
4002 	//		 If you're interested in investigating this alternative
4003 	//		 approach, play with the following asserts.
4004 
4005 //	UT_ASSERT(pRun1 && pRun2);
4006 	UT_ASSERT(pRun2);
4007 
4008 	bool bDone = false;
4009 	fp_Run* pCurRun = (pRun1 ? pRun1 : pRun2);
4010 
4011 
4012 	while (!bDone)
4013 	{
4014 		if (pCurRun == pRun2)
4015 		{
4016 			bDone = true;
4017 		}
4018 
4019 		fl_BlockLayout* pBlock = pCurRun->getBlock();
4020 		UT_ASSERT(pBlock);
4021 //
4022 // Look to see if the Block is in a table.
4023 //
4024 		fl_ContainerLayout * pCL = pBlock->myContainingLayout();
4025 		if(pCL->getContainerType() == FL_CONTAINER_CELL)
4026 		{
4027 			fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pCL->getFirstContainer());
4028 			if(pCell->isSelected())
4029 			{
4030 				pCell->clearSelection();
4031 				pCell->clearScreen();
4032 
4033 				fl_BlockLayout * pBlock = NULL;
4034 				fl_ContainerLayout * pLastCL = pCL->getFirstLayout();
4035 				while(pLastCL->getNext())
4036 				{
4037 					pLastCL = pLastCL->getNext();
4038 				}
4039 				while(pLastCL->getContainerType() != FL_CONTAINER_BLOCK)
4040 				{
4041 					pLastCL = pLastCL->getFirstLayout();
4042 				}
4043 				pBlock = static_cast<fl_BlockLayout *>(pLastCL);
4044 				pBlock = pBlock->getNextBlockInDocument();
4045 				if(pBlock)
4046 				{
4047 					pCurRun = pBlock->getFirstRun();
4048 					continue;
4049 				}
4050 				pCurRun = NULL;
4051 				bDone = true;
4052 				continue;
4053 			}
4054 		}
4055 		pCurRun->clearScreen(bFullLineHeightRect);
4056 		if (pCurRun->getNextRun())
4057 		{
4058 			pCurRun = pCurRun->getNextRun();
4059 		}
4060 		else
4061 		{
4062 			fl_BlockLayout* pNextBlock;
4063 
4064 			fl_BlockLayout* pBlock = pCurRun->getBlock();
4065 			UT_ASSERT(pBlock);
4066 
4067 			pNextBlock = pBlock->getNextBlockInDocument();
4068 			if (pNextBlock)
4069 			{
4070 				pCurRun = pNextBlock->getFirstRun();
4071 			}
4072 			else
4073 				bDone = true;
4074 			// otherwise we get fun
4075 			// infinte loops
4076 		}
4077 	}
4078 	return true;
4079 #endif
4080 }
4081 
_findPositionCoords(PT_DocPosition pos,bool bEOL,UT_sint32 & x,UT_sint32 & y,UT_sint32 & x2,UT_sint32 & y2,UT_uint32 & height,bool & bDirection,fl_BlockLayout ** ppBlock,fp_Run ** ppRun) const4082 void FV_View::_findPositionCoords(PT_DocPosition pos,
4083 								  bool bEOL,
4084 								  UT_sint32& x,
4085 								  UT_sint32& y,
4086 								  UT_sint32& x2,
4087 								  UT_sint32& y2,
4088 								  UT_uint32& height,
4089 								  bool& bDirection,
4090 								  fl_BlockLayout** ppBlock,
4091 								  fp_Run** ppRun) const
4092 {
4093 	UT_sint32 xPoint = 0;
4094 	UT_sint32 yPoint = 0;
4095 	UT_sint32 xPoint2 = 0;
4096 	UT_sint32 yPoint2 = 0;
4097 	UT_sint32 iPointHeight;
4098 	if(ppRun)
4099 	{
4100 		*ppRun = NULL;
4101 	}
4102 	// Get the previous block in the document. _findBlockAtPosition
4103 	// will iterate forwards until it actually find a block if there
4104 	// isn't one previous to pos.
4105 	// (Removed code duplication. Jesper, 2001.01.25)
4106 
4107 //
4108 // Have to deal with special case of point being exactly on a footnote/endnote
4109 // boundary
4110 //
4111 	bool onFootnoteBoundary = false;
4112 	if(m_pDoc->isFootnoteAtPos(pos))
4113 	{
4114 		onFootnoteBoundary = true;
4115 		pos--;
4116 	}
4117 	fl_BlockLayout* pBlock = _findBlockAtPosition(pos);
4118 
4119 	// this assert makes debugging very hard ...
4120 #ifdef DEBUG
4121 	static bool bDoneTheAssert = false;
4122 	if(!bDoneTheAssert)
4123 	{
4124 		bDoneTheAssert = true;
4125 		UT_ASSERT_HARMLESS(pBlock && pBlock->getContainerType() == FL_CONTAINER_BLOCK);
4126 	}
4127 #endif
4128 	if(!pBlock || pBlock->getContainerType() != FL_CONTAINER_BLOCK)
4129 	{
4130 		x = x2 = 0;
4131 		y = y2 = 0;
4132 
4133 		height = 0;
4134 		if(ppBlock)
4135 			*ppBlock = 0;
4136 		return;
4137 	}
4138 
4139 
4140 	if(onFootnoteBoundary)
4141 	{
4142 		pos++;
4143 	}
4144 	// probably an empty document, return instead of
4145 	// dereferencing NULL.	Dom 11.9.00
4146 	if(!pBlock)
4147 	{
4148 		// Do the assert. Want to know from debug builds when this happens.
4149 		// This assert makes debuging virtuall impossible; the hack below makes it assert only once.
4150 		// UT_ASSERT(pBlock);
4151 #ifdef DEBUG
4152 		static bool bAss = false;
4153 		if(!pBlock && !bAss)
4154 		{
4155 			UT_ASSERT_HARMLESS( pBlock );
4156 			bAss = true;
4157 		}
4158 #endif
4159 
4160 		x = x2 = 0;
4161 		y = y2 = 0;
4162 
4163 		height = 0;
4164 		if(ppBlock)
4165 			*ppBlock = 0;
4166 		return;
4167 	}
4168 
4169 	// if the block cannot contain point, find the nearest block to
4170 	// the left that can; or to the right, if none to the left exists
4171 	fl_BlockLayout * pOrigBL = pBlock;
4172 
4173 	while(pBlock && !pBlock->canContainPoint())
4174 	{
4175 		pBlock = pBlock->getPrevBlockInDocument();
4176 	}
4177 
4178 	if(!pBlock)
4179 	{
4180 		pBlock = pOrigBL;
4181 
4182 		while(pBlock && !pBlock->canContainPoint())
4183 		{
4184 			pBlock = pBlock->getNextBlockInDocument();
4185 		}
4186 	}
4187 
4188 	if(!pBlock)
4189 	{
4190 		// no blocks that can take point in this document !!!
4191 		// one of the scenarios in which this happens is when the user did ctrl+a -> del
4192 		// in revisions mode or marked everything hidden while fmt marks are not showing
4193 		// If there is a block and it is not visible, we return that block
4194 		// Note that it is difficult to prevent this from happening on the PT level, since
4195 		// just because text is marked as hidden or deleted does not mean it is not
4196 		// visible in a given view.
4197 		fl_DocSectionLayout * pDSL = m_pLayout->getFirstSection();
4198 		pBlock = pDSL->getFirstBlock();
4199 
4200 		if(!pBlock)
4201 		{
4202 			UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
4203 			x = x2 = 0;
4204 			y = y2 = 0;
4205 
4206 			height = 0;
4207 			if(ppBlock)
4208 				*ppBlock = 0;
4209 			return;
4210 		}
4211 	}
4212 
4213 	// If block is actually to the right of the requested position
4214 	// (this happens in an empty document), update the pos with the
4215 	// start pos of the block.
4216 	PT_DocPosition iBlockPos = pBlock->getPosition(false);
4217 	if (iBlockPos > pos)
4218 	{
4219 		pos = iBlockPos;
4220 	}
4221 	xxx_UT_DEBUGMSG(("Position to find %d \n",pos));
4222 	fp_Run* pRun = pBlock->findPointCoords(pos, bEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection);
4223 
4224 
4225 	// NOTE prior call will fail if the block isn't currently formatted,
4226 	// NOTE so we won't be able to figure out more specific geometry
4227 
4228 //
4229 // Needed for piecetable fields. We don't have these in 1.0
4230 //
4231 // OK if we're at the end of document at the last run is a field we have to add
4232 // the width of the field run to xPoint, xPoint2.
4233 //
4234 	PT_DocPosition posEOD = 0;
4235 	getEditableBounds(true, posEOD);
4236 	xxx_UT_DEBUGMSG(("SEVIOR: Doing posEOD =%d getPoint %d pRun->isField %d bEOL %d \n",posEOD,getPoint(),pRun->isField(),bEOL));
4237 
4238 	if(bEOL && pRun && posEOD == getPoint())
4239 	{
4240 		bool bBack = true;
4241 		while(pRun && pRun->getPrevRun() && !pRun->isField() && pRun->getWidth() == 0)
4242 		{
4243 			bBack = false;
4244 			pRun = pRun->getPrevRun();
4245 		}
4246 		if(pRun && pRun->isField() && bBack)
4247 		{
4248 			UT_DEBUGMSG(("SEVIOR: Doing EOD work aorund \n"));
4249 			static_cast<fp_FieldRun *>(pRun)->recalcWidth();
4250 			xPoint += pRun->getWidth();
4251 			xPoint2 += pRun->getWidth();
4252 		}
4253 	}
4254 	else if( (pRun == NULL) && (posEOD == getPoint()))
4255 	{
4256 		pRun = pBlock->getFirstRun();
4257 		while(pRun && (pRun->getNextRun() != NULL))
4258 		{
4259 			pRun = pRun->getNextRun();
4260 		}
4261 	}
4262 	if (pRun)
4263 	{
4264 		// we now have coords relative to the page containing the ins pt
4265 			fp_Line * pLine =  pRun->getLine();
4266 			if(!pLine)
4267 		{
4268 			x = x2 = 0;
4269 			y = y2 = 0;
4270 
4271 			height = 0;
4272 			if(ppBlock)
4273 			  *ppBlock = 0;
4274 			return;
4275 		}
4276 
4277 		fp_Page* pPointPage = pLine->getPage();
4278 
4279 		UT_sint32 iPageOffset;
4280 		getPageYOffset(pPointPage, iPageOffset); // <- look at this later
4281 
4282 		UT_uint32 iPageNumber = m_pLayout->findPage(pPointPage);
4283 		//UT_uint32 iRow = iPageNumber / getNumHorizPages();
4284 		//UT_uint32 iCol = iPageNumber - (iRow * getNumHorizPages());
4285 
4286 		yPoint += iPageOffset; //beware wierdness discribed in getPageYOffset(...)
4287 		xPoint += getPageViewLeftMargin() + getWidthPrevPagesInRow(iPageNumber);
4288 		yPoint2 += iPageOffset; //beware wierdness discribed in getPageYOffset(...)
4289 		xPoint2 += getPageViewLeftMargin() + getWidthPrevPagesInRow(iPageNumber);
4290 
4291 
4292 		// now, we have coords absolute, as if all pages were stacked vertically
4293 		xPoint -= m_xScrollOffset;
4294 		yPoint -= m_yScrollOffset;
4295 
4296 		xPoint2 -= m_xScrollOffset;
4297 		yPoint2 -= m_yScrollOffset;
4298 
4299 		// now, return the results
4300 		x = xPoint;
4301 		y = yPoint;
4302 
4303 		x2 = xPoint2;
4304 		y2 = yPoint2;
4305 		xxx_UT_DEBUGMSG(("x,y pos in view %d,%d \n",x,y));
4306 		xxx_UT_DEBUGMSG(("x2,y2 pos in view %d,%d \n",x2,y2));
4307 
4308 		height = iPointHeight;
4309 	}
4310 	if (ppBlock)
4311 	{
4312 		*ppBlock = pBlock;
4313 	}
4314 
4315 	if (ppRun)
4316 	{
4317 		*ppRun = pRun;
4318 	}
4319 }
4320 
_fixAllInsertionPointCoords() const4321 void FV_View::_fixAllInsertionPointCoords() const
4322 {
4323 	fv_CaretProps * pCaretProps = NULL;
4324 	UT_sint32 iCount = m_vecCarets.getItemCount();
4325 	UT_sint32 i = 0;
4326 	for(i=0; i<iCount;i++)
4327 	{
4328 			pCaretProps = m_vecCarets.getNthItem(i);
4329 			_fixInsertionPointCoords(pCaretProps);
4330 	}
4331 }
4332 
_fixInsertionPointCoords(fv_CaretProps * pCP) const4333 void FV_View::_fixInsertionPointCoords(fv_CaretProps * pCP) const
4334 {
4335 	if ((pCP->m_iInsPoint > 0) && !isLayoutFilling())
4336 	{
4337 		fl_BlockLayout * pBlock = NULL;
4338 		fp_Run * pRun = NULL;
4339 		_findPositionCoords(pCP->m_iInsPoint, pCP->m_bPointEOL, pCP->m_xPoint,
4340 							pCP->m_yPoint, pCP->m_xPoint2, pCP->m_yPoint2,
4341 							pCP->m_iPointHeight, pCP->m_bPointDirection,
4342 							&pBlock, &pRun);
4343 		const fp_Page * pPage = getCurrentPage();
4344 		const UT_RGBColor * pClr = NULL;
4345 		if (pPage)
4346 			pClr = pPage->getFillType().getColor();
4347 		UT_sint32 yoff = 0;
4348 		if(pCP->m_yPoint < 0)
4349 		{
4350 			UT_sint32 negY  = -pCP->m_yPoint;
4351 			yoff = negY + 1;
4352 			if(negY > (UT_sint32)pCP->m_iPointHeight)
4353 			{
4354 				pCP->m_iPointHeight = 0;
4355 				yoff = 0;
4356 			}
4357 		}
4358 		if(pRun && (pRun->getType() == FPRUN_IMAGE))
4359 		{
4360 			UT_DEBUGMSG(("On image run with fixPointcoords \n"));
4361 		}
4362 		xxx_UT_DEBUGMSG(("Xpoint in fixpoint %d \n",m_xPoint));
4363 		pCP->m_pCaret->setCoords(pCP->m_xPoint, pCP->m_yPoint+yoff,
4364 								 pCP->m_iPointHeight-yoff,
4365 								 pCP->m_xPoint2,
4366 								 pCP->m_yPoint2+yoff,
4367 								 pCP->m_iPointHeight-yoff,
4368 								 pCP->m_bPointDirection, pClr);
4369 	}
4370 	pCP->m_pCaret->setWindowSize(getWindowWidth(), getWindowHeight());
4371 
4372 }
4373 
_fixInsertionPointCoords(bool bIgnoreAll)4374 void FV_View::_fixInsertionPointCoords(bool bIgnoreAll)
4375 {
4376 	if (m_pG->allCarets()->getBaseCaret() == NULL)
4377 		return;
4378 	if(!bIgnoreAll)
4379 		_fixAllInsertionPointCoords();
4380 
4381 	fp_Page * pPage = NULL;
4382 	fl_BlockLayout * pBlock = NULL;
4383 	fp_Run * pRun = NULL;
4384 	if(m_bInsertAtTablePending)
4385 	{
4386 		//
4387 		// Position the caret just before the table
4388 		//
4389 		fl_TableLayout * pTL = getTableAtPos(m_iPosAtTable+3);
4390 		if(pTL == NULL)
4391 		{
4392 			m_bInsertAtTablePending = false;
4393 			return;
4394 		}
4395 		pBlock = pTL->getNextBlockInDocument();
4396 		if(pBlock == NULL)
4397 		{
4398 			m_bInsertAtTablePending = false;
4399 			return;
4400 		}
4401 		UT_sint32 height= 0;
4402 		pRun = pBlock->findPointCoords(pBlock->getPosition(false), false, m_xPoint, m_yPoint, m_xPoint2, m_yPoint2, height, m_bPointDirection);
4403 		m_iPointHeight = static_cast<UT_uint32>(height);
4404 		fp_TableContainer * pTab = static_cast<fp_TableContainer *>(pTL->getFirstContainer());
4405 		fp_TableContainer * pBroke = pTab->getFirstBrokenTable();
4406 		fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pTab->getFirstContainer());
4407 		UT_sint32 iLeft,iRight,iTop,iBot,col_y =0;
4408 		bool bDoClear= true;
4409 		fp_Column * pCol = NULL;
4410 		fp_ShadowContainer * pShadow = NULL;
4411 		pCell->getScreenPositions(pBroke,getGraphics(),iLeft,iRight,iTop,iBot,col_y,pCol,pShadow,bDoClear);
4412 		m_xPoint = iLeft - getGraphics()->tlu(2);
4413 		m_xPoint2 = iLeft - getGraphics()->tlu(2);
4414 		m_yPoint = iTop;
4415 		m_yPoint2 = iTop;
4416 		pPage = getCurrentPage();
4417 		const UT_RGBColor * pClr = NULL;
4418 		if (pPage)
4419 			pClr = pPage->getFillType().getColor();
4420 		m_pG->allCarets()->getBaseCaret()->setCoords(m_xPoint, m_yPoint, m_iPointHeight,
4421 									m_xPoint2, m_yPoint2, m_iPointHeight,
4422 									m_bPointDirection, pClr);
4423 	}
4424 	else if ((getPoint() > 0) && !isLayoutFilling())
4425 	{
4426 		_findPositionCoords(getPoint(), m_bPointEOL, m_xPoint, m_yPoint, m_xPoint2, m_yPoint2, m_iPointHeight, m_bPointDirection, &pBlock, &pRun);
4427 		pPage = getCurrentPage();
4428 		const UT_RGBColor * pClr = NULL;
4429 		if (pPage)
4430 			pClr = pPage->getFillType().getColor();
4431 		UT_sint32 yoff = 0;
4432 		if(m_yPoint < 0)
4433 		{
4434 			UT_sint32 negY  = -m_yPoint;
4435 			yoff = negY + 1;
4436 			if(negY > (UT_sint32)m_iPointHeight)
4437 			{
4438 				m_iPointHeight = 0;
4439 				yoff = 0;
4440 			}
4441 		}
4442 		if(pRun && (pRun->getType() == FPRUN_IMAGE))
4443 		{
4444 			UT_DEBUGMSG(("On image run with fixPointcoords \n"));
4445 		}
4446 		xxx_UT_DEBUGMSG(("Xpoint in fixpoint %d \n",m_xPoint));
4447 		m_pG->allCarets()->getBaseCaret()->setCoords(m_xPoint, m_yPoint+yoff, m_iPointHeight-yoff,
4448 									m_xPoint2, m_yPoint2+yoff, m_iPointHeight-yoff,
4449 									m_bPointDirection, pClr);
4450 	}
4451 
4452 	m_pG->allCarets()->setWindowSize(getWindowWidth(), getWindowHeight());
4453 
4454 	xxx_UT_DEBUGMSG(("SEVIOR: m_yPoint = %d m_iPointHeight = %d \n",m_yPoint,m_iPointHeight));
4455 	// hang onto this for _moveInsPtNextPrevLine()
4456 	m_xPointSticky = m_xPoint + m_xScrollOffset - getPageViewLeftMargin();
4457 #ifdef ENABLE_SPELL
4458 	if(pBlock && pBlock->getSpellSquiggles()->get(getPoint() - pBlock->getPosition()))
4459 	{
4460 		if(m_prevMouseContext == EV_EMC_TEXT)
4461 		{
4462 			m_prevMouseContext = EV_EMC_MISSPELLEDTEXT;
4463 		}
4464 	}
4465 	if(pBlock)
4466 	{
4467 		m_pLayout->triggerPendingBlock(pBlock);
4468 	}
4469 #endif
4470 }
4471 
4472 // Finds what pages are on screen and draws them
_draw(UT_sint32 x,UT_sint32 y,UT_sint32 width,UT_sint32 height,bool bDirtyRunsOnly,bool bClip)4473 void FV_View::_draw(UT_sint32 x, UT_sint32 y,
4474 					UT_sint32 width, UT_sint32 height,
4475 					bool bDirtyRunsOnly, bool bClip)
4476 {
4477 	xxx_UT_DEBUGMSG(("FV_View::draw_3 [x %d][y %d][w %d][h %d][bClip %d]\n"
4478 					 "\t\twith [yScrollOffset %d][windowHeight %d][bDirtyRunsOnly %d]\n",
4479 					 x,y,width,height,bClip,
4480 					 m_yScrollOffset,getWindowHeight(),bDirtyRunsOnly));
4481 	if(m_pViewDoubleBufferingObject != NULL && m_pViewDoubleBufferingObject->getCallDrawOnlyAtTheEnd())
4482 	{
4483 		// record this call's arguments and return
4484 		if(bClip)
4485 		{
4486 			UT_Rect r(x, y, width, height);
4487 			m_pG->setClipRect(&r);
4488 		}
4489 		m_pViewDoubleBufferingObject->recordViewDrawCall(x, y, width, height, bDirtyRunsOnly, bClip);
4490 		m_pG->setClipRect(NULL);
4491 		return;
4492 	}
4493 
4494 	/**************************
4495 	 * STEP 0: Initialization *
4496 	 **************************/
4497 
4498 	GR_Painter painter(m_pG);
4499 
4500 	XAP_Frame * pFrame = static_cast<XAP_Frame*>(getParentData());
4501 
4502 	// CHECK_WINDOW_SIZE
4503 	// this can happen when the frame size is decreased and
4504 	// only the toolbars show...
4505 	if ((getWindowWidth() <= 0) || (getWindowHeight() <= 0))
4506 	{
4507 		UT_DEBUGMSG(("fv_View::draw() called with zero drawing area.\n"));
4508 		return;
4509 	}
4510 
4511 	if ((width <= 0) || (height <= 0))
4512 	{
4513 		UT_DEBUGMSG(("fv_View::draw() called with zero width or height expose.\n"));
4514 		return;
4515 	}
4516 
4517 	painter.beginDoubleBuffering();
4518 
4519 	// TMN: Leave this rect at function scope!
4520 	// gr_Graphics only stores a _pointer_ to it!
4521 	UT_Rect rClip;
4522 	if (bClip)
4523 	{
4524 		rClip.left = x;
4525 		rClip.top = y;
4526 		rClip.width = width;
4527 		rClip.height = height;
4528 		m_pG->setClipRect(&rClip);
4529 	}
4530 
4531 //	UT_ASSERT(m_yScrollOffset == m_pG->getPrevYOffset());
4532 
4533 	// figure out where pages go, based on current window dimensions
4534 	// TODO: don't calc for every draw
4535 	// HYP:  cache calc results at scroll/size time
4536 	calculateNumHorizPages();
4537 
4538 
4539 	/******************************************************************
4540 	 * STEP 1: Find the first page so we can start drawing from there *
4541 	 ******************************************************************/
4542 
4543 	// EYA: In case you were wondering, everything assumes that all pages have
4544 	// the same dimensions and are ordered in rows and columns as in print view
4545 	// (normal view is just print view with one column).  Abandon hope, all ye
4546 	// who would attempt to change those assumptions: they are very deeply
4547 	// embedded throughout AbiWord.
4548 
4549 	UT_sint32 iPageWidth = 0, iPageHeight = 0;
4550 	UT_sint32 iFirstVisiblePageNumber = -1;
4551 	fl_DocSectionLayout *pDSL = NULL;
4552 
4553 	// we should have at least the first page
4554 	if(getLayout() -> getFirstPage())
4555 	{
4556 		// layout ref
4557 		pDSL = getLayout() -> getFirstPage() -> getOwningSection();
4558 
4559 		// since all pages have the same width / height, set them here
4560 		iPageWidth = getLayout() -> getFirstPage() -> getWidth();
4561 		iPageHeight = getLayout() -> getFirstPage() -> getHeight();
4562 		if(getViewMode() == VIEW_NORMAL || getViewMode() == VIEW_WEB)
4563 			iPageHeight = iPageHeight - pDSL -> getTopMargin() - pDSL -> getBottomMargin();
4564 
4565 		// now guess the first visible page number
4566 		iFirstVisiblePageNumber =
4567 			((getYScrollOffset() - getPageViewTopMargin() + getPageViewSep()) /
4568 			(iPageHeight + getPageViewSep())) * getNumHorizPages();
4569 	}
4570 
4571 	/**********************
4572 	 * STEP 2: Draw pages *
4573 	 **********************/
4574 
4575 	// enter a double-buffered section
4576 
4577 	if( !bDirtyRunsOnly && (getViewMode() == VIEW_PRINT) ) {
4578 		UT_RGBColor clrMargin;
4579 		if (m_pG->getColor3D(GR_Graphics::CLR3D_BevelDown, clrMargin)) {
4580 			painter.fillRect(GR_Graphics::CLR3D_BevelDown, 0, 0,
4581 							 getWindowWidth(), getWindowHeight());
4582 		} else {
4583 			painter.fillRect(getColorMargin(), 0, 0, getWindowWidth(), getWindowHeight());
4584 		}
4585 	}
4586 
4587 	// start from the first visible page
4588 	fp_Page *pPage = NULL;
4589 	if(iFirstVisiblePageNumber >= 0)
4590 		pPage = getLayout() -> getNthPage(iFirstVisiblePageNumber);
4591 
4592 	while(pPage)
4593 	{
4594 		UT_sint32 adjustedTop = 0; // Top line of the page that defines the page's top margin,
4595 				       // relative to the top of the screen and in layout units
4596 		UT_sint32 adjustedBottom;
4597 		UT_sint32 adjustedLeft = 0;
4598 		UT_sint32 adjustedRight;
4599 
4600 		UT_sint32 iPageXOffset;
4601 		UT_sint32 iPageYOffset;
4602 
4603 		dg_DrawArgs da;
4604 
4605 		// get X / Y page offsets
4606 		getPageYOffset(pPage, iPageYOffset);
4607 		iPageXOffset = getWidthPrevPagesInRow(pPage->getPageNumber());
4608 
4609 		// if this page is not visible, then stop
4610 		if(!((iPageYOffset  <= getYScrollOffset() + getWindowHeight()) &&
4611 		    (iPageYOffset + iPageHeight >= getYScrollOffset())))
4612 			break;
4613 
4614 		// Adjust page's boundaries
4615 		switch(getViewMode())
4616 		{
4617 			case VIEW_NORMAL:
4618 			case VIEW_WEB:
4619 			adjustedTop = iPageYOffset - getYScrollOffset() + ( pPage->getPageNumber() * (m_pG->tlu(1) - getPageViewSep()) );
4620 			adjustedLeft = 0;
4621 			break;
4622 
4623 			case VIEW_PRINT:
4624 			case VIEW_PREVIEW:
4625 			adjustedTop = iPageYOffset - getYScrollOffset();
4626 			adjustedLeft = iPageXOffset - getXScrollOffset()  + getPageViewLeftMargin();
4627 			break;
4628 		}
4629 
4630 		// view independant boundaries
4631 		adjustedBottom = adjustedTop + iPageHeight;
4632 		adjustedRight = adjustedLeft + iPageWidth;
4633 
4634 		xxx_UT_DEBUGMSG(("Drawing page adjustedTop = %i, Bottom = %i, Left = %i, Right = %i\n", adjustedTop, adjustedBottom, adjustedLeft, adjustedRight));
4635 		xxx_UT_DEBUGMSG(("--Entered _draw loop:\n  iPageNumber = %i, vecitemcount = %i\n  iRow = %i, iCol = %i\n  iPageWidth = %i, iPageHeight = %i\n  getPageViewTopMargin() = %i, m_yScrollOffset = %i\n", iPageNumber, vecPagesOnScreen.getItemCount(), iRow, iCol, iPageWidth, iPageHeight, getPageViewTopMargin(), m_yScrollOffset));
4636 
4637 		xxx_UT_DEBUGMSG(("drawing page E: iPageHeight=%d curY=%d nPos=%d getWindowHeight()=%d y=%d h=%d\n", iPageHeight,curY,m_yScrollOffset,getWindowHeight(),y,height));
4638 
4639 		// set drawing args
4640 		da.pG = m_pG;
4641 		da.yoff = adjustedTop;
4642 		da.xoff = adjustedLeft;
4643 
4644 		xxx_UT_DEBUGMSG(("Drawing page with da.yoff and da.xoff %i %i\n", da.yoff, da.xoff));
4645 
4646 		// Redraw the page background, if necessary
4647 		if(!bDirtyRunsOnly || (pPage->needsRedraw() && (getViewMode() == VIEW_PRINT)))
4648 		{
4649 			const UT_RGBColor * pClr = pPage->getFillType().getColor();
4650 			if(getViewMode() == VIEW_NORMAL || getViewMode() == VIEW_WEB) // Normal/web view pages take up the whole window
4651 				painter.fillRect(*pClr, adjustedRight, adjustedTop, getWindowWidth() - adjustedRight + m_pG->tlu(1), iPageHeight);
4652 			else
4653 				painter.fillRect(*pClr, adjustedLeft + m_pG->tlu(1), adjustedTop + m_pG->tlu(1), iPageWidth - m_pG->tlu(1), iPageHeight - m_pG->tlu(1));
4654 			xxx_UT_DEBUGMSG(("   ---PAINTING PAGE %i---\n", pPage->getPageNumber()));
4655 
4656 			//
4657 			// Since we're clearing everything we have to draw every run no matter
4658 			// what.
4659 			//
4660 			da.bDirtyRunsOnly = false;
4661 		}
4662 
4663 		// Draw the page and all its subcontainers (i.e., the content)
4664 		pPage->draw(&da);
4665 
4666 		// draw page decorations
4667 		UT_RGBColor clr(0,0,0); 	// black
4668 		m_pG->setColor(clr);
4669 
4670 		// one pixel border a
4671 		if(!isPreview() && (getViewMode() == VIEW_PRINT))
4672 		{
4673 			m_pG->setLineProperties(m_pG->tluD(1.0),
4674 					GR_Graphics::JOIN_MITER,
4675 					GR_Graphics::CAP_PROJECTING,
4676 					GR_Graphics::LINE_SOLID);
4677 
4678 			painter.drawLine(adjustedLeft, adjustedTop, adjustedRight, adjustedTop);
4679 			painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom);
4680 			painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);
4681 			painter.drawLine(adjustedLeft, adjustedTop, adjustedLeft, adjustedBottom);
4682 		}
4683 
4684 		// Draw page seperator
4685 		// only in NORMAL MODE - draw a line across the screen
4686 		// at a page boundary. Not used in online/web and print
4687 		// layout modes
4688 		if(getViewMode() == VIEW_NORMAL)
4689 		{
4690 			UT_RGBColor clrPageSep(192,192,192);		// light gray
4691 			m_pG->setColor(clrPageSep);
4692 
4693 			m_pG->setLineProperties(m_pG->tluD(1.0),
4694 						GR_Graphics::JOIN_MITER,
4695 						GR_Graphics::CAP_PROJECTING,
4696 						GR_Graphics::LINE_SOLID);
4697 
4698 			painter.drawLine(adjustedLeft, adjustedBottom, getWindowWidth() + m_pG->tlu(1), adjustedBottom);
4699 			adjustedBottom += m_pG->tlu(1);
4700 			m_pG->setColor(clr);
4701 		}
4702 
4703 		// two pixel drop shadow for pages in print view
4704 		if(!isPreview() && (getViewMode() == VIEW_PRINT) && !pFrame->isMenuScrollHidden() )
4705 		{
4706 			m_pG->setLineProperties(m_pG->tluD(1.0),
4707 					GR_Graphics::JOIN_MITER,
4708 					GR_Graphics::CAP_PROJECTING,
4709 					GR_Graphics::LINE_SOLID);
4710 
4711 			adjustedLeft += m_pG->tlu(3);
4712 			adjustedBottom += m_pG->tlu(1);
4713 			painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);
4714 
4715 			adjustedBottom += m_pG->tlu(1);
4716 			painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);
4717 
4718 			adjustedTop += m_pG->tlu(3);
4719 			adjustedRight += m_pG->tlu(1);
4720 			painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom);
4721 
4722 			adjustedRight += m_pG->tlu(1);
4723 			painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom);
4724 		}
4725 
4726 		// advance to the next page
4727 		pPage = pPage -> getNext();
4728 	}
4729 	if (bClip)
4730 	{
4731 		m_pG->setClipRect(NULL);
4732 	}
4733 	//
4734 	// Look if we have to blink the caret
4735 	//
4736 	xxx_UT_DEBUGMSG(("Doing _draw bDirtyRunsOnly %d \n",bDirtyRunsOnly));
4737 
4738 	if(m_pG->allCarets()->doBlinkIfNeeded())
4739 	{
4740 		xxx_UT_DEBUGMSG(("Pending blink drawn has bDirtyRunsOnly %d \n",bDirtyRunsOnly));
4741 	}
4742 	xxx_UT_DEBUGMSG(("End _draw\n"));
4743 }
4744 
4745 
_setPoint(fv_CaretProps * pCP,PT_DocPosition pt,UT_sint32 iLen) const4746 void FV_View::_setPoint(fv_CaretProps * pCP,PT_DocPosition pt, UT_sint32 iLen) const
4747 {
4748 	getGraphics()->allCarets()->disable();
4749 	pCP->m_iInsPoint = pt + iLen;
4750 	_fixInsertionPointCoords(pCP);
4751 	getGraphics()->allCarets()->enable();
4752 }
4753 
4754 
_setPoint(PT_DocPosition pt,bool bEOL)4755 void FV_View::_setPoint(PT_DocPosition pt, bool bEOL)
4756 {
4757 	if (!m_pDoc->getAllowChangeInsPoint())
4758 		return;
4759 	if(!m_pDoc->isPieceTableChanging())
4760 	{
4761 //
4762 // Have to deal with special case of point being exactly on a footnote/endnote
4763 // boundary. Move the point past the footnote so we always have Footnote field
4764 // followed by footnotestrux in the piecetable
4765 //
4766 		fl_FootnoteLayout * pFL = NULL;
4767 		if(m_pDoc->isFootnoteAtPos(pt))
4768 		{
4769 			pFL = getClosestFootnote(pt);
4770 			if(pFL == NULL)
4771 			{
4772 				fl_EndnoteLayout * pEL = getClosestEndnote(pt);
4773 				if(pEL)
4774 				{
4775 					pt += pEL->getLength();
4776 				}
4777 			}
4778 			else
4779 			{
4780 				pt += pFL->getLength();
4781 			}
4782 		}
4783 	}
4784 	m_iInsPoint = pt;
4785 	m_Selection.checkSelectAll();
4786 	m_bInsertAtTablePending = false;
4787 	m_iPosAtTable = 0;
4788 	xxx_UT_DEBUGMSG(("Point set to %d in View %x \n",pt,this));
4789 	m_bPointEOL = bEOL;
4790 	if(!m_pDoc->isPieceTableChanging())
4791 	{
4792 		_fixInsertionPointCoords(true);
4793 		m_pLayout->considerPendingSmartQuoteCandidate();
4794 #ifdef ENABLE_SPELL
4795 		_checkPendingWordForSpell();
4796 #endif
4797 	// So, if there is a selection now, we should disable the cursor; conversely,
4798 	// if there is no longer a selection, we should enable the cursor.
4799 		if (isSelectionEmpty())
4800 		{
4801 			while(m_countDisable > 0)
4802 			{
4803 			  if(m_pG)
4804 			    m_pG->allCarets()->enable();
4805 			  m_countDisable--;
4806 			}
4807 			if(m_pG) {
4808 			  m_pG->allCarets()->disable();
4809 			  m_pG->allCarets()->enable();
4810 			}
4811 		}
4812 		else
4813 		{
4814 //
4815 // We have to remember the number of times we disabled the cursor and wind
4816 // them back to re-enable it because the cursor class keeps a count this to
4817 // handle nested disable calls.
4818 //
4819 
4820 		  if(m_pG)
4821 		    m_pG->allCarets()->disable();
4822 		  m_countDisable++;
4823 		}
4824 	}
4825 
4826 	if(m_pG)
4827 	{
4828 		xxx_UT_DEBUGMSG(("Schedule redraw in _setPoint \n"));
4829 		m_pG->allCarets()->setPendingBlink();
4830 		m_pG->flush(); // scedule a redraw for Wayland
4831 	}
4832 
4833 }
4834 
4835 
4836 #ifdef ENABLE_SPELL
4837 /*!
4838  Spell-check pending word
4839  If the IP does not touch the pending word, spell-check it.
4840 
4841  \note This function used to exit if PT was changing - but that
4842 	   prevents proper squiggle behavior during undo, so the check has
4843 	   been removed. This means that the pending word POB must be
4844 	   updated to reflect the PT changes before the IP is moved.
4845  */
4846 void
_checkPendingWordForSpell(void)4847 FV_View::_checkPendingWordForSpell(void)
4848 {
4849 	if (!m_pLayout->isPendingWordForSpell()) return;
4850 
4851 	// Find block at IP
4852 	fl_BlockLayout* pBL = _findBlockAtPosition(m_iInsPoint);
4853 	if (pBL)
4854 	{
4855 		UT_uint32 iOffset = m_iInsPoint - pBL->getPosition();
4856 
4857 		// If it doesn't touch the pending word, spell-check it
4858 		if (!m_pLayout->touchesPendingWordForSpell(pBL, iOffset, 0))
4859 		{
4860 			// no longer there, so check it
4861 			if (m_pLayout->checkPendingWordForSpell())
4862 			{
4863 				// FIXME:jskov Without this updateScreen call, the
4864 				// just squiggled word remains deleted. It's overkill
4865 				// (surely we should have a requestUpdateScreen() that
4866 				// does so after all operations have completed), but
4867 				// works. Unfortunately it causes a small screen
4868 				// artifact when pressing undo, since some runs may be
4869 				// redrawn before they have their correct location
4870 				// recalculated. In other words, make the world a
4871 				// better place by adding requestUpdateScreen or
4872 				// similar.
4873 				updateScreen();
4874 			}
4875 		}
4876 	}
4877 }
4878 #endif
4879 
_getDataCount(UT_uint32 pt1,UT_uint32 pt2) const4880 UT_uint32 FV_View::_getDataCount(UT_uint32 pt1, UT_uint32 pt2) const
4881 {
4882 	UT_ASSERT(pt2>=pt1);
4883 	return pt2 - pt1;
4884 }
4885 
4886 
_charMotion(bool bForward,UT_uint32 countChars,bool bSkipCannotContainPoint)4887 bool FV_View::_charMotion(bool bForward,UT_uint32 countChars, bool bSkipCannotContainPoint)
4888 {
4889 	// advance(backup) the current insertion point by count characters.
4890 	// return false if we ran into an end (or had an error).
4891 	bool bInsertAtTable = false;
4892 	PT_DocPosition posTable = 0;
4893 	PT_DocPosition posOld = m_iInsPoint;
4894 	fp_Run* pRun = NULL;
4895 	fl_BlockLayout* pBlock = NULL;
4896 	UT_sint32 x=0;
4897 	UT_sint32 y=0;
4898 	UT_sint32 x2=0;
4899 	UT_sint32 y2=0;
4900 	bool bDirection=false;
4901 	UT_uint32 uheight;
4902 	m_bPointEOL = false;
4903 	UT_sint32 iOldDepth = getEmbedDepth(getPoint());
4904 	xxx_UT_DEBUGMSG(("_charMotion: Old Position is %d embed depth %d \n",posOld,iOldDepth));
4905 	/*
4906 	  we don't really care about the coords.  We're calling these
4907 	  to get the Run pointer
4908 	*/
4909 	PT_DocPosition posBOD=0;
4910 	PT_DocPosition posEOD=0;
4911 	bool bRes;
4912 
4913 	bRes = getEditableBounds(false, posBOD);
4914 	bRes = getEditableBounds(true, posEOD);
4915 	UT_ASSERT(bRes);
4916 
4917 	// FIXME:jskov want to rewrite this code to use simplified
4918 	// versions of findPositionCoords. I think there's been some bugs
4919 	// due to that function being overloaded to be used from this
4920 	// code.
4921 	UT_sint32 xold,yold,x2old,y2old=0;
4922 	bool bDirectionOld=false;
4923 	xxx_UT_DEBUGMSG(("Count Chars %d \n",countChars));
4924 	_findPositionCoords(m_iInsPoint, false, xold, yold, x2old,y2old,uheight, bDirectionOld, &pBlock, &pRun);
4925 	if (bForward)
4926 	{
4927 		xxx_UT_DEBUGMSG(("Just before First forward _setPoint %d \n",m_iInsPoint));
4928 		_setPoint(m_iInsPoint + countChars);
4929 		xxx_UT_DEBUGMSG(("Just After First forward _setPoint %d \n",m_iInsPoint));
4930 //
4931 // Scan past any strux boundaries (like table controls
4932 //
4933 		while(getPoint() < posEOD && !isPointLegal())
4934 		{
4935 			xxx_UT_DEBUGMSG(("Forward scan past illegal point pos 1 %d \n",m_iInsPoint));
4936 			_setPoint(m_iInsPoint + 1);
4937 			xxx_UT_DEBUGMSG(("Forward scan past illegal point pos 2 %d \n",m_iInsPoint));
4938 		}
4939 		_findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
4940 //
4941 // If we come to a table boundary we have doc positions with no blocks.
4942 // _findPositionCoords signals this by returning pRun == NULL
4943 //
4944 		// I have added the bDirection == bDirectionOld condition
4945 		// because without it the code did not work on direction
4946 		// boundaries. However, I am not sure whether the whole of the
4947 		// x,y test is at all desirable here; any idea why it is here?
4948 		// Tomas, Jan 16, 2003.
4949 
4950 		// Testing the coords is definitely wrong; combining characters often do not advance x,y,
4951 		// but need to be treated as a valid document position. See bug 6987. Tomas, July 27, 2004
4952 
4953 		bool bExtra = false;
4954 		while(m_iInsPoint <= posEOD && (pRun == NULL /*|| ((x == xold) && (y == yold) &&
4955 														 (x2 == x2old) && (y2 == y2old) &&
4956 														 (bDirection == bDirectionOld))*/))
4957 		{
4958 			xxx_UT_DEBUGMSG(("fv_View_protected: (2) pRun = %x pos %d\n",pRun,m_iInsPoint));
4959 			_setPoint(m_iInsPoint+1);
4960 			xxx_UT_DEBUGMSG(("fv_View_protected: (3) pRun = %x pos %d \n",pRun,m_iInsPoint));
4961 			_findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
4962 			bExtra = true;
4963 		}
4964 		if(bExtra)
4965 		{
4966 			_setPoint(m_iInsPoint-1);
4967 		}
4968 
4969 
4970 #if 0
4971 		while(pRun != NULL &&  pRun->isField() && m_iInsPoint <= posEOD)
4972 		{
4973 			_setPoint(m_iInsPoint+1);
4974 			if(m_iInsPoint <= posEOD)
4975 			{
4976 				_findPositionCoords(m_iInsPoint, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
4977 			}
4978 		}
4979 #endif
4980     }
4981 	else
4982 	{
4983 		UT_sint32 iPos = static_cast<UT_sint32>(m_iInsPoint) - static_cast<UT_sint32>(countChars);
4984 		PT_DocPosition realBOD =0;
4985 		if(posBOD == 2)
4986 	    {
4987 			realBOD = 0;
4988 		}
4989 		else
4990 		{
4991 			realBOD = posBOD;
4992 			while(!m_pDoc->isHdrFtrAtPos(realBOD) && (realBOD > 0))
4993 			{
4994 				realBOD--;
4995 			}
4996 		}
4997 		if(iPos > 0 && iPos > static_cast<UT_sint32>(realBOD))
4998 		{
4999 			_setPoint(m_iInsPoint - countChars);
5000 		}
5001 		else
5002 		{
5003 			_setPoint(posBOD);
5004 		}
5005 //
5006 // Scan past any strux boundaries (like table controls
5007 //
5008 // when moving backwards, we need to also skip over the EndOfFootnote struxes
5009 //
5010 		bool bGotTableStrux = false;
5011 		bool bGotOtherStrux = false;
5012 		while(getPoint() > realBOD && !isPointLegal())
5013 		{
5014 			_setPoint(m_iInsPoint - 1);
5015 			xxx_UT_DEBUGMSG(("Backward scan past illegal point pos %d \n",m_iInsPoint));
5016 			if(!bGotOtherStrux && m_pDoc->isTableAtPos(getPoint()))
5017 			{
5018 				bGotTableStrux = true;
5019 				posTable = getPoint();
5020 			}
5021 			else if(!bGotOtherStrux && bGotTableStrux)
5022 			{
5023 				if(m_pDoc->isEndTableAtPos(getPoint()) ||
5024 				   m_pDoc->isCellAtPos(getPoint()) ||
5025 				   m_pDoc->isSectionAtPos(getPoint()) ||
5026 				   m_pDoc->isHdrFtrAtPos(getPoint()) ||
5027 				   m_pDoc->isFrameAtPos(getPoint()))
5028 				{
5029 					  bGotOtherStrux = true;
5030 				}
5031 			}
5032 		}
5033 	if(bGotOtherStrux && bGotTableStrux)
5034 		{
5035 			bInsertAtTable = true;
5036 		}
5037 		if(getPoint() < posBOD)
5038 		{
5039 			_setPoint(posBOD);
5040 		}
5041 		_findPositionCoords(m_iInsPoint, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
5042 
5043 //
5044 // If we come to a table boundary we have doc positions with no blocks.
5045 // _findPositionCoords signals this by returning pRun == NULL
5046 //
5047 		// I have added the bDirection == bDirectionOld condition
5048 		// because without it the code did not work on direction
5049 		// boundaries. However, I am not sure whether the whole of the
5050 		// x,y test is at all desirable here; any idea why it is here?
5051 		// Tomas, Jan 16, 2003.
5052 
5053 		// Testing the coords is definitely wrong; combining characters often do not advance x,y,
5054 		// but need to be treated as a valid document position. See bug 6987. Tomas, July 27, 2004
5055 
5056 		bool bExtra = false;
5057 		while( m_iInsPoint >= posBOD && (pRun == NULL /*|| ((x == xold) && (y == yold) &&
5058 														 (x2 == x2old) && (y2 == y2old) &&
5059 														 (bDirection == bDirectionOld))*/))
5060 		{
5061 			xxx_UT_DEBUGMSG(("_charMotion: Looking at point m_iInsPoint %d \n",m_iInsPoint));
5062 			_setPoint(m_iInsPoint-1);
5063 			_findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
5064 			bExtra = true;
5065 		}
5066 		if(bExtra)
5067 		{
5068 			_setPoint(m_iInsPoint-1);
5069 		}
5070 
5071 
5072 #if 0
5073 // Needed for piecetable fields - we don't have these in 1.0
5074 
5075 		while(pRun != NULL && pRun->isField() && m_iInsPoint >= posBOD)
5076 		{
5077 			_setPoint(m_iInsPoint-1);
5078 			_findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
5079 		}
5080 #endif
5081 		// if the run which declared itself for our position is end of paragraph run,
5082 		// we need to ensure that the position is just before the run, not after it
5083 		// (fixes bug 1120)
5084 		xxx_UT_DEBUGMSG(("_charMotion: pRun->getBlockOffset() %d getPosition %d insPoint %d \n",pRun->getBlockOffset(), pRun->getBlock()->getPosition(),m_iInsPoint));
5085 		if(pRun && pRun->getType() == FPRUN_ENDOFPARAGRAPH
5086 		   && (pRun->getBlockOffset() + pRun->getBlock()->getPosition()) < m_iInsPoint)
5087 		{
5088 			_setPoint(m_iInsPoint-1);
5089 		}
5090 	}
5091 
5092 	UT_ASSERT(bRes);
5093 
5094 	bRes = true;
5095 
5096 	// we might have skipped over some runs that cannot contain the
5097 	// point, but but have non-zero length, such as any hidden text;
5098 	// if this is the case, we need to adjust the document position accordingly
5099 
5100 	UT_return_val_if_fail(pBlock, false);
5101 	UT_return_val_if_fail(pRun, false);
5102 
5103 	PT_DocPosition iRunStart = pBlock->getPosition(false) + pRun->getBlockOffset();
5104 	PT_DocPosition iRunEnd = iRunStart + pRun->getLength();
5105 
5106 	// containing layout we will work with, ususally section
5107 // 	fl_ContainerLayout * pCL = pBlock->myContainingLayout();
5108 
5109 	// the layout immediately above the runs, should be block
5110 // 	fl_ContainerLayout * pBL = pBlock;
5111 
5112 	// indicates how many layout layers we had to step up to get valid pCL
5113 // 	UT_uint32 iLayoutDepth = 0;
5114 
5115 	if(iRunEnd > posEOD)
5116 		iRunEnd = posEOD;
5117 
5118 	if(bForward && ( m_iInsPoint > iRunEnd))
5119 	{
5120 		// the run we have got is the on left of the ins point, we
5121 		// need to find the right one and set the point there; we also
5122 		// need to make sure that we handle correctly any hidden
5123 		// sub layouts (blocks, sections, table cells, tables ...)
5124 
5125 		// get the next run that can contain insertion point
5126 		pRun = pRun->getNextRun();
5127 		UT_uint32 iLength = 0;
5128 		while(pRun && ((bSkipCannotContainPoint && !pRun->canContainPoint()) || pRun->getLength() == 0))
5129 		{
5130 			xxx_UT_DEBUGMSG(("_charMotion: Sweep forward through runs %d \n",pRun->getLength()));
5131 			iLength += pRun->getLength();
5132 			pRun = pRun->getNextRun();
5133 		}
5134 
5135 		if(pRun)
5136 		{
5137 			_setPoint(m_iInsPoint + iLength);
5138 		}
5139 		else
5140 		{
5141 //
5142 // FIXME: Put in some code here to handle table/cell boundaries. Right
5143 // now you have to press left arrow twice to move form outside to inside
5144 // a table.
5145 
5146 		}
5147 	}
5148 	// these two branches ensure that insertion point is not moved just after a run that is not
5149 	// suppossed to take point
5150 	else if(bSkipCannotContainPoint && bForward && m_iInsPoint == iRunStart && pRun->getLength()>0)
5151 	{
5152 		// moving forward, with insertion point ending between two runs;
5153 		// we need to check that the previous run was not one that cannot take point, in which case
5154 		// we need to advance the point by one (otherwise it will appear just after the non-point
5155 		// run
5156 		// this case happens, for example, when the user has a hyperlink at the start of line and
5157 		// presses HOME, RIGHT. The HOME key takes her before the hyperlink, the right, however,
5158 		// should skip over they hyperlink run
5159 
5160 		pRun = pRun->getPrevRun();
5161 		if(pRun && !pRun->canContainPoint())
5162 			_setPoint(m_iInsPoint + 1);
5163 	}
5164 	else if(bSkipCannotContainPoint && !bForward && m_iInsPoint == iRunStart)
5165 	{
5166 		// moving backwards, with insertion point ending between two runs; we need to scroll
5167 		// through any adjucent runs on the left that cannot contain point
5168 		pRun = pRun->getPrevRun();
5169 		UT_uint32 iLength = 0;
5170 		while(pRun && !pRun->canContainPoint())
5171 		{
5172 			iLength += pRun->getLength();
5173 			pRun = pRun->getPrevRun();
5174 		}
5175 
5176 		// do this unconditionally; if !pRun, we are at the start of the block
5177 		_setPoint(m_iInsPoint - iLength);
5178 	}
5179 
5180 	// this is much simpler, since the findPointCoords will return the
5181 	// run on the left of the requested position, so we just need to move
5182 	// to its end if the position does not fall into that run
5183 	xxx_UT_DEBUGMSG(("_charMotion: iRunEnd %d \n",iRunEnd));
5184 	if(!bForward && (iRunEnd < m_iInsPoint) && (pRun->getBlockOffset() > 0))
5185 	{
5186 		_setPoint(iRunEnd - 1);
5187 	}
5188 
5189 	// now have the get the run that actualy holds this position, and let it do any internal
5190 	// adjustments (as needed for complex scripts, e.g., Thai) not sure whether this should not come
5191 	// after the footnote sweep
5192 	if(bSkipCannotContainPoint)
5193 	{
5194 
5195 		pBlock = _findBlockAtPosition(getPoint());
5196 		UT_return_val_if_fail( pBlock, false );
5197 
5198 		pRun = pBlock->findRunAtOffset(getPoint() - pBlock->getPosition());
5199 
5200 		// at the end of document we do not have a run ...
5201 		if(pRun)
5202 		{
5203 			UT_uint32 iAdjustedPos = pRun->adjustCaretPosition(getPoint(), bForward);
5204 
5205 			if(iAdjustedPos != getPoint())
5206 			{
5207 				xxx_UT_DEBUGMSG(("FV_View::_charMotion: orig pos %d, adjusted to %d\n", getPoint(), iAdjustedPos));
5208 				_setPoint(iAdjustedPos);
5209 			}
5210 		}
5211 	}
5212 
5213 //
5214 // OK sweep through footnote sections without stopping
5215 	xxx_UT_DEBUGMSG(("Point is %d inFootnote %d bOldFootnote %d \n",m_iInsPoint,isInFootnote(),iOldDepth));
5216 	if(bForward)
5217 	{
5218 		if(iOldDepth < getEmbedDepth(m_iInsPoint))
5219 		{
5220 			bool bSweep = false;
5221 			while(m_iInsPoint <= posEOD && ( (iOldDepth < getEmbedDepth(m_iInsPoint)) || m_pDoc->isEndFootnoteAtPos(getPoint())))
5222 			{
5223 				xxx_UT_DEBUGMSG(("_charMotion: Sweep forward -1 %d \n",m_iInsPoint));
5224 				bSweep = true;
5225 				m_iInsPoint++;
5226 			}
5227 			if(bSweep && (m_iInsPoint <= posEOD))
5228 			{
5229 				_setPoint(m_iInsPoint);
5230 			}
5231 			else if(m_iInsPoint > posEOD)
5232 			{
5233 				_setPoint(posEOD);
5234 			}
5235 		}
5236 		else if((iOldDepth > getEmbedDepth(m_iInsPoint)) )
5237 		{
5238 			bool bSweep = false;
5239 			while( ((m_iInsPoint > posBOD) && (iOldDepth > getEmbedDepth(m_iInsPoint))) || m_pDoc->isFootnoteAtPos(getPoint()) )
5240 			{
5241 				xxx_UT_DEBUGMSG(("_charMotion: Sweep backward -1 %d \n",m_iInsPoint));
5242 				m_iInsPoint--;
5243 				bSweep = true;
5244 			}
5245 			if(bSweep)
5246 			{
5247 				_setPoint(m_iInsPoint);
5248 			}
5249 		}
5250 
5251 	}
5252 	else
5253 	{
5254 		if(iOldDepth < getEmbedDepth(m_iInsPoint))
5255 		{
5256 			bool bSweep = false;
5257 			while(((iOldDepth < getEmbedDepth(m_iInsPoint)) || ((m_pDoc->isFootnoteAtPos(getPoint()) ) && (m_iInsPoint >= posBOD))))
5258 			{
5259 				xxx_UT_DEBUGMSG(("_charMotion: Sweep backward -2 %d \n",m_iInsPoint));
5260 				bSweep = true;
5261 				m_iInsPoint--;
5262 			}
5263 			if(bSweep && (m_iInsPoint >= posBOD))
5264 			{
5265 				_setPoint(m_iInsPoint);
5266 			}
5267 			else if(m_iInsPoint > posEOD)
5268 			{
5269 				_setPoint(posOld);
5270 			}
5271 		}
5272 	    else
5273 		{
5274 			bool bSweep = false;
5275 			while((m_iInsPoint < posEOD) &&((iOldDepth > getEmbedDepth(m_iInsPoint)) || m_pDoc->isEndFootnoteAtPos(getPoint())))
5276 			{
5277 				xxx_UT_DEBUGMSG(("_charMotion: Sweep forward -2 %d \n",m_iInsPoint));
5278 				m_iInsPoint++;
5279 				bSweep = true;
5280 			}
5281 			if(bSweep)
5282 			{
5283 				_setPoint(m_iInsPoint);
5284 			}
5285 		}
5286 	}
5287 	PT_DocPosition legalBOD = posBOD;
5288 	if(!isHdrFtrEdit())
5289 	{
5290 		fl_DocSectionLayout * pDSL = m_pLayout->getFirstSection();
5291 		if(pDSL == NULL)
5292 		{
5293 			legalBOD =2;
5294 		}
5295 		else
5296 		{
5297 			fl_BlockLayout * pBL = pDSL->getFirstBlock();
5298 			if(pBL != NULL)
5299 			{
5300 					legalBOD = pBL->getPosition(false);
5301 			}
5302 			else
5303 			{
5304 					legalBOD = 2;
5305 			}
5306 		}
5307 	}
5308 	if (static_cast<UT_sint32>(m_iInsPoint) < static_cast<UT_sint32>(legalBOD))
5309 	{
5310 		_setPoint(legalBOD);
5311 		bRes = true;
5312 	}
5313 	else if (static_cast<UT_sint32>(m_iInsPoint) > static_cast<UT_sint32>(posEOD))
5314 	{
5315 		m_bPointEOL = true;
5316 		_setPoint(posEOD);
5317 		bRes = false;
5318 	}
5319 	if(m_iInsPoint < legalBOD)
5320 	{
5321 		_setPoint(legalBOD);
5322 		bRes = true;
5323 	}
5324 	if(bInsertAtTable)
5325 	{
5326 		m_bInsertAtTablePending = true;
5327 		m_iPosAtTable =posTable;
5328 	}
5329 	if (m_iInsPoint != posOld)
5330 	{
5331 		m_pLayout->considerPendingSmartQuoteCandidate();
5332 #ifdef ENABLE_SPELL
5333 		_checkPendingWordForSpell();
5334 #endif
5335 		_clearIfAtFmtMark(posOld);
5336 		if(!m_pDoc->isDoingPaste())
5337 		{
5338 			notifyListeners(AV_CHG_MOTION);
5339 		}
5340 	}
5341 	if(m_FrameEdit.isActive())
5342 	{
5343 		m_FrameEdit.setMode(FV_FrameEdit_NOT_ACTIVE);
5344 	}
5345 	xxx_UT_DEBUGMSG(("SEVIOR: Point = %d \n",getPoint()));
5346 	_fixInsertionPointCoords();
5347 	return (bRes && m_iInsPoint != posOld);
5348 }
5349 
5350 
_doPaste(bool bUseClipboard,bool bHonorFormatting)5351 void FV_View::_doPaste(bool bUseClipboard, bool bHonorFormatting)
5352 {
5353 	// internal portion of paste operation.
5354 
5355 	if (!isSelectionEmpty() && !m_FrameEdit.isActive())
5356 		_deleteSelection();
5357 	else if(m_FrameEdit.isActive())
5358 	{
5359 	       m_FrameEdit.setPointInside();
5360 	}
5361 
5362 	_clearIfAtFmtMark(getPoint());
5363 	PD_DocumentRange dr(m_pDoc,getPoint(),getPoint());
5364 	m_pApp->pasteFromClipboard(&dr,bUseClipboard,bHonorFormatting);
5365 	insertParaBreakIfNeededAtPos(getPoint());
5366 	fl_SectionLayout * pSL = getCurrentBlock()->getSectionLayout();
5367 	m_pDoc->setDontImmediatelyLayout(false);
5368 	pSL->checkAndAdjustCellSize();
5369 	_generalUpdate();
5370 
5371 	_updateInsertionPoint();
5372 }
5373 
5374 
_deleteBookmark(const char * szName,bool bSignal,PT_DocPosition * posStart,PT_DocPosition * posEnd)5375 UT_Error FV_View::_deleteBookmark(const char* szName, bool bSignal, PT_DocPosition *posStart, PT_DocPosition *posEnd)
5376 {
5377 	if(!m_pDoc->isBookmarkUnique(static_cast<const gchar *>(szName)))
5378 	{
5379 		// even though we will only send out a single explicit deleteSpan
5380 		// call, we need to find out where both of the markers are in the
5381 		// document, so that the caller can adjust any stored doc positions
5382 		// if necessary
5383 
5384 		PT_DocPosition pos1, pos2;
5385 
5386 		fp_BookmarkRun * pB1;
5387 		UT_uint32 bmBlockOffset[2];
5388 		fl_BlockLayout * pBlock[2];
5389 		UT_uint32 i = 0;
5390 
5391 		fl_BlockLayout *pBL;
5392 		fl_SectionLayout *pSL = m_pLayout->getFirstSection();
5393 		fp_Run * pRun = 0;
5394 		bool bFound = false;
5395 
5396 		//find the first of the two bookmarks
5397 		while(pSL)
5398 		{
5399 			pBL = pSL->getNextBlockInDocument();
5400 
5401 			while(pBL)
5402 			{
5403 				pRun = pBL->getFirstRun();
5404 
5405 				while(pRun)
5406 				{
5407 					if(pRun->getType()== FPRUN_BOOKMARK)
5408 					{
5409 						pB1 = static_cast<fp_BookmarkRun*>(pRun);
5410 						if(!strcmp(static_cast<const gchar *>(szName), pB1->getName()))
5411 						{
5412 							bmBlockOffset[i] = pRun->getBlockOffset();
5413 							pBlock[i] = pRun->getBlock();
5414 							i++;
5415 							if(i>1)
5416 							{
5417 								bFound = true;
5418 								break;
5419 							}
5420 						}
5421 					}
5422 					if(bFound)
5423 						break;
5424 					pRun = pRun->getNextRun();
5425 				}
5426 				if(bFound)
5427 					break;
5428 				pBL = static_cast<fl_BlockLayout *>(pBL->getNext());
5429 			}
5430 			if(bFound)
5431 				break;
5432 			pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
5433 		}
5434 
5435 		UT_ASSERT(pRun && pRun->getType()==FPRUN_BOOKMARK && pBlock[0] && pBlock[1]);
5436 		if(!pRun || pRun->getType()!=FPRUN_BOOKMARK || !pBlock[0] || !pBlock[1])
5437 			return false;
5438 
5439 		// Signal PieceTable Change
5440 		if(bSignal)
5441 			_saveAndNotifyPieceTableChange();
5442 
5443 		UT_DEBUGMSG(("fv_View::cmdDeleteBookmark: bl pos [%d,%d], bmOffset [%d,%d]\n",
5444 					 pBlock[0]->getPosition(false), pBlock[1]->getPosition(false),bmBlockOffset[0],bmBlockOffset[1]));
5445 
5446 		pos1 = pBlock[0]->getPosition(false) + bmBlockOffset[0];
5447 		pos2 = pBlock[1]->getPosition(false) + bmBlockOffset[1];
5448 
5449 		if (posStart && *posStart > pos1)
5450 			(*posStart)--;
5451 		if (posStart && *posStart > pos2)
5452 			(*posStart)--;
5453 
5454 		if (posEnd && *posEnd > pos1)
5455 			(*posEnd)--;
5456 		if (posEnd && *posEnd > pos1)
5457 			(*posEnd)--;
5458 
5459 		UT_uint32 iRealDeleteCount;
5460 
5461 		m_pDoc->deleteSpan(pos1,pos1 + 1,NULL,iRealDeleteCount);
5462 		// TODO -- add proper revision handling using iRealDeleteCount
5463 
5464 		// Signal PieceTable Changes have finished
5465 		if(bSignal)
5466 		{
5467 			_restorePieceTableState();
5468 			_generalUpdate();
5469 		}
5470 	}
5471 	else {
5472 		UT_DEBUGMSG(("fv_View::cmdDeleteBookmark: bookmark \"%s\" does not exist\n",szName));
5473 	}
5474 	return true;
5475 }
5476 
5477 
5478 
5479 /*! Returns the hyperlink around position pos, if any; assumes
5480  * posStart, posEnd in same block. */
_getHyperlinkInRange(PT_DocPosition & posStart,PT_DocPosition & posEnd)5481 fp_HyperlinkRun * FV_View::_getHyperlinkInRange(PT_DocPosition &posStart,
5482 												PT_DocPosition &posEnd)
5483 {
5484 	fl_BlockLayout *pBlock = _findBlockAtPosition(posStart);
5485 	PT_DocPosition curPos = posStart - pBlock->getPosition(false);
5486 	if(curPos <2)
5487 		return NULL;
5488 	fp_Run * pRun = pBlock->getFirstRun();
5489 
5490 	//find the run at pos
5491 	while(pRun && pRun->getBlockOffset() <= curPos)
5492 		pRun = pRun->getNextRun();
5493 
5494 	UT_return_val_if_fail(pRun,NULL);
5495 
5496 	// now we have the run immediately after the run in question, so
5497 	// we step back
5498 	pRun = pRun->getPrevRun();
5499 	UT_return_val_if_fail(pRun,NULL);
5500 
5501 	if (pRun->getHyperlink() != NULL)
5502 		return pRun->getHyperlink();
5503 
5504 	// Now, getHyperlink() looks NULL, so let's step forward till posEnd.
5505 
5506 	PT_DocPosition curPosEnd = posEnd - pBlock->getPosition(false);
5507 
5508 	// Continue checking for hyperlinks.
5509 	while(pRun && pRun->getBlockOffset() <= curPosEnd)
5510 	{
5511 		pRun = pRun->getNextRun();
5512 		if (pRun && pRun->getPrevRun() && pRun->getPrevRun()->getHyperlink() != NULL)
5513 			return pRun->getPrevRun()->getHyperlink();
5514 	}
5515 
5516 	// OK, we're really safe now.
5517 	return NULL;
5518 }
5519 
5520 /*
5521 	NB: this function assumes that the position it is passed is inside a
5522 	hyperlink and will assert if it is not so.
5523 */
5524 
_deleteHyperlink(PT_DocPosition & pos1,bool bSignal)5525 UT_Error FV_View::_deleteHyperlink(PT_DocPosition &pos1, bool bSignal)
5526 {
5527 	fp_HyperlinkRun * pH1 = _getHyperlinkInRange(pos1, pos1);
5528 	UT_return_val_if_fail(pH1,false);
5529 	fp_AnnotationRun  * pAR = NULL;
5530 	UT_uint32 iRunLen = 1;
5531 	if(pH1->getHyperlinkType() ==  HYPERLINK_ANNOTATION)
5532 	{
5533 			pAR = static_cast<fp_AnnotationRun *>(pH1);
5534 			fl_AnnotationLayout * pAL = getLayout()->findAnnotationLayout(pAR->getPID());
5535 			UT_return_val_if_fail(pAL,false);
5536 			iRunLen = pAL->getLength();
5537 
5538 	}
5539 
5540 	if (!isSelectionEmpty())
5541 		_clearSelection();
5542 
5543 	pos1 = pH1->getBlock()->getPosition(false) + pH1->getBlockOffset();
5544 
5545 	// Signal PieceTable Change
5546 	if(bSignal)
5547 		_saveAndNotifyPieceTableChange();
5548 
5549 	UT_DEBUGMSG(("fv_View::cmdDeleteHyperlink() position:%d len:%d\n",
5550 				 pos1, iRunLen ));
5551 
5552 	UT_uint32 iRealDeleteCount;
5553 	m_pDoc->beginUserAtomicGlob();
5554 	m_pDoc->deleteSpan(pos1,pos1 + iRunLen,NULL, iRealDeleteCount);
5555 
5556 	// TODO -- add proper revision handling using iRealDeleteCount
5557 
5558 	// Signal PieceTable Changes have finished
5559 	m_pDoc->endUserAtomicGlob();
5560 	if(bSignal)
5561 	{
5562 		_restorePieceTableState();
5563 		_generalUpdate();
5564 	}
5565 	return true;
5566 }
5567 
5568 
_deleteXMLID(const std::string & xmlid,bool bSignal,PT_DocPosition & extPosStart,PT_DocPosition & extPosEnd)5569 UT_Error FV_View::_deleteXMLID( const std::string& xmlid, bool bSignal, PT_DocPosition& extPosStart, PT_DocPosition& extPosEnd )
5570 {
5571     PD_DocumentRDFHandle rdf = m_pDoc->getDocumentRDF();
5572     std::pair< PT_DocPosition, PT_DocPosition > range = rdf->getIDRange( xmlid );
5573 
5574 	UT_DEBUGMSG(("_deleteXMLID() xmlid:%s point:%d\n", xmlid.c_str(), getPoint() ));
5575 	UT_DEBUGMSG(("_deleteXMLID() xmlid:%s start:%d end:%d\n", xmlid.c_str(), range.first, range.second ));
5576 
5577 	if( range.first == range.second )
5578  	{
5579 		return UT_ERROR;
5580 	}
5581 
5582 	fp_HyperlinkRun* r = _getHyperlinkInRange( range.first, range.first );
5583 	UT_DEBUGMSG(("_deleteXMLID() xmlid:%s r:%p\n", xmlid.c_str(), r ));
5584 	if( !r )
5585  	{
5586 		return UT_ERROR;
5587 	}
5588 
5589 	UT_DEBUGMSG(("_deleteXMLID() xmlid:%s type:%d\n", xmlid.c_str(), r->getHyperlinkType() ));
5590 	if( r->getHyperlinkType() ==  HYPERLINK_RDFANCHOR )
5591 	{
5592 		UT_DEBUGMSG(("_deleteXMLID() xmlid:%s len:%d\n", xmlid.c_str(), r->getLength() ));
5593 	}
5594 
5595 	if (!isSelectionEmpty())
5596 		_clearSelection();
5597 
5598 	PT_DocPosition pos1 = r->getBlock()->getPosition(false) + r->getBlockOffset();
5599 	int iRunLen = 1;
5600 	UT_DEBUGMSG(("_deleteXMLID() xmlid:%s pos1:%d\n", xmlid.c_str(), pos1 ));
5601 
5602 	// Signal PieceTable Change
5603 	if(bSignal)
5604 		_saveAndNotifyPieceTableChange();
5605 
5606 	UT_uint32 iRealDeleteCount;
5607 	m_pDoc->beginUserAtomicGlob();
5608 	m_pDoc->deleteSpan(pos1,pos1 + iRunLen,NULL, iRealDeleteCount);
5609 	if( extPosStart > pos1 )
5610 		extPosStart -= 2;
5611 	if( extPosEnd > pos1 )
5612 		extPosEnd   -= 2;
5613 
5614 	// TODO -- add proper revision handling using iRealDeleteCount
5615 
5616 	// Signal PieceTable Changes have finished
5617 	m_pDoc->endUserAtomicGlob();
5618 	if(bSignal)
5619 	{
5620 		_restorePieceTableState();
5621 		_generalUpdate();
5622 	}
5623 
5624 	return UT_OK;
5625 }
5626 
5627 
_deleteXMLID(const std::string & xmlid,bool bSignal)5628 UT_Error FV_View::_deleteXMLID( const std::string& xmlid, bool bSignal )
5629 {
5630 	PT_DocPosition s,e;
5631 	return _deleteXMLID( xmlid, bSignal, s, e );
5632 }
5633 
5634 
5635 
_insertGraphic(FG_Graphic * pFG,const char * szName)5636 UT_Error FV_View::_insertGraphic(FG_Graphic* pFG, const char* szName)
5637 {
5638 	UT_return_val_if_fail(pFG,UT_ERROR);
5639 	UT_ASSERT(szName);
5640 
5641 	if(!isPointLegal(getPoint()))
5642 	{
5643 		_makePointLegal();
5644 	}
5645 	return pFG->insertIntoDocument(m_pDoc, m_pG->getDeviceResolution(), getPoint(), szName);
5646 }
5647 
5648 
_insertGraphic(FG_Graphic * pFG,const char * szName,PT_DocPosition pos)5649 UT_Error FV_View::_insertGraphic(FG_Graphic* pFG, const char* szName,PT_DocPosition pos)
5650 {
5651 	UT_return_val_if_fail(pFG,UT_ERROR);
5652 	UT_ASSERT(szName);
5653 	PT_DocPosition posEOD,posBOD;
5654 	bool bRes;
5655 	bRes = getEditableBounds(true, posEOD);
5656 	bRes = getEditableBounds(false, posBOD);
5657 	bRes = false;
5658 	while(!isPointLegal(pos) && (pos <= posEOD))
5659 	{
5660 		pos++;
5661 	}
5662 	if(pos > posEOD)
5663 	{
5664 		while(!isPointLegal(pos) && pos >= posBOD)
5665 		{
5666 			pos--;
5667 		}
5668 		if(pos >= posBOD)
5669 			bRes = true;
5670 	}
5671 	else
5672 	{
5673 		bRes = true;
5674 	}
5675 	if(!bRes)
5676 		return UT_ERROR;
5677 
5678 	return pFG->insertIntoDocument(m_pDoc, m_pG->getDeviceResolution(), pos, szName);
5679 }
5680 
5681 
_clearIfAtFmtMark(PT_DocPosition dpos)5682 void FV_View::_clearIfAtFmtMark(PT_DocPosition dpos)
5683 {
5684 	// Check to see if we're at the beginning of the line. If we
5685 	// aren't, then it's safe to delete the FmtMark. Else we could
5686 	// wipe out the placeholder FmtMark for our attributes.
5687 	// Fix for Bug #863
5688 	xxx_UT_DEBUGMSG(("FV_View::_clearIfAtFmtMark called\n"));
5689 	if ( ( dpos != _getDocPosFromPoint(dpos,FV_DOCPOS_BOL) ))
5690 	{
5691 		m_pDoc->clearIfAtFmtMark(dpos);
5692 		//	_generalUpdate();//  Sevior: May be able to live with notify.. always
5693 	}
5694 	else
5695 	{
5696 		notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK);
5697 	}
5698 }
5699 
5700 
5701 #ifdef ENABLE_SPELL
5702 // NB: returns a UCS string that the caller needs to FREEP
_lookupSuggestion(fl_BlockLayout * pBL,const fl_PartOfBlockPtr & pPOB,UT_sint32 ndx)5703 UT_UCSChar * FV_View::_lookupSuggestion(fl_BlockLayout* pBL,
5704 										const fl_PartOfBlockPtr& pPOB, UT_sint32 ndx)
5705 {
5706 	// mega caching - are these assumptions valid?
5707 	UT_UCSChar * szSuggest = NULL;
5708 
5709 	// TODO these should really be static members, so we can properly
5710 	// clean up
5711 	static fl_BlockLayout * s_pLastBL = 0;
5712 	static fl_PartOfBlockPtr s_pLastPOB;
5713 	static const UT_GenericVector<UT_UCSChar*>* s_pvCachedSuggestions = 0;
5714 
5715 	// can we use the cached suggestions?
5716 	if (pBL != s_pLastBL || pPOB != s_pLastPOB)
5717 	{
5718 		// no we cannot, so we empty and invalidate the cache
5719 		if (s_pvCachedSuggestions)
5720 		{
5721 			// clean up
5722 			for (UT_sint32 i = 0; i < s_pvCachedSuggestions->getItemCount(); i++)
5723 			{
5724 				const UT_UCSChar * sug = s_pvCachedSuggestions->getNthItem(i);
5725 				FREEP(sug);
5726 			}
5727 
5728 			s_pLastBL = 0;
5729 			s_pLastPOB.reset();
5730 			DELETEP(s_pvCachedSuggestions);
5731 		}
5732 
5733 		// grab a copy of the word
5734 		UT_GrowBuf pgb(1024);
5735 		bool bRes = pBL->getBlockBuf(&pgb);
5736 		UT_ASSERT(bRes);
5737 		if(!bRes)
5738 		{
5739 			UT_WARNINGMSG(("getBlockBuf() failed in %s:%d",
5740 						   __FILE__, __LINE__));
5741 		}
5742 
5743 		UT_UCS4String stMisspelledWord;
5744 		// convert smart quote apostrophe to ASCII single quote to be
5745 		// compatible with ispell
5746 		const UT_UCSChar * pWord;
5747 		UT_sint32 iLength, iPTLength, iBlockPos;
5748 
5749 		fl_BlockSpellIterator BSI(pBL, pPOB->getOffset());
5750 		BSI.nextWordForSpellChecking(pWord, iLength, iBlockPos, iPTLength);
5751 
5752 		UT_uint32 len = iLength;
5753 		for (UT_uint32 ldex=0; ldex < len && ldex < INPUTWORDLEN; ldex++)
5754 		{
5755 			stMisspelledWord += *pWord == UCS_RQUOTE ? '\'' : *pWord;
5756 			++pWord;
5757 		}
5758 
5759 		// get language code for misspelled word
5760 		const char * szLang = NULL;
5761 
5762 		const gchar ** props_in = NULL;
5763 
5764 		if (getCharFormat(&props_in))
5765 		{
5766 			szLang = UT_getAttribute("lang", props_in);
5767 			FREEP(props_in);
5768 		}
5769 
5770 		// get spellchecker engine for language code
5771 		SpellChecker * checker = NULL;
5772 
5773 		if (szLang)
5774 		{
5775 			// we get smart and request the proper dictionary
5776 			checker = SpellManager::instance().requestDictionary(szLang);
5777 		}
5778 		else
5779 		{
5780 			// we just (dumbly) default to the last dictionary
5781 			// TODO this is known to return the wrong dictionary sometimes in multilanguge docs
5782 			checker = SpellManager::instance().lastDictionary();
5783 		}
5784 
5785 		// lookup suggestions
5786 
5787 		// create an empty vector
5788 		UT_GenericVector<UT_UCSChar*>* pvFreshSuggestions = 0;
5789 		UT_ASSERT(!pvFreshSuggestions);
5790 
5791 		pvFreshSuggestions = new UT_GenericVector<UT_UCSChar*>();
5792 		UT_ASSERT(pvFreshSuggestions);
5793 
5794 		if (checker && (checker->checkWord(stMisspelledWord.ucs4_str(), iLength) == SpellChecker::LOOKUP_FAILED))
5795 		{
5796 			// get suggestions from spelling engine
5797 			const UT_GenericVector<UT_UCSChar*>* cpvEngineSuggestions;
5798 
5799 			cpvEngineSuggestions = checker->suggestWord (stMisspelledWord.ucs4_str(), iLength);
5800 
5801 			for (UT_sint32 i = 0; i < cpvEngineSuggestions->getItemCount(); ++i)
5802 			{
5803 				UT_UCSChar *sug = cpvEngineSuggestions->getNthItem(i);
5804 				UT_ASSERT(sug);
5805 				pvFreshSuggestions->addItem(sug);
5806 			}
5807 
5808 			// add suggestions from user's AbiWord file
5809 			 m_pApp->suggestWord(pvFreshSuggestions,stMisspelledWord.ucs4_str(), iLength);
5810 		}
5811 
5812 		// update static vars for next call
5813 		s_pvCachedSuggestions = pvFreshSuggestions;
5814 		s_pLastBL = pBL;
5815 		s_pLastPOB = pPOB;
5816 	}
5817 
5818 	// return the indexed suggestion from the cache
5819 	if ((s_pvCachedSuggestions->getItemCount()) &&
5820 		( ndx <= s_pvCachedSuggestions->getItemCount()))
5821 	{
5822 		UT_UCS4_cloneString(&szSuggest, s_pvCachedSuggestions->getNthItem(ndx-1));
5823 	}
5824 
5825 	return szSuggest;
5826 }
5827 #endif
5828 
_prefsListener(XAP_Prefs * pPrefs,UT_StringPtrMap *,void * data)5829 void FV_View::_prefsListener( XAP_Prefs *pPrefs, UT_StringPtrMap * /*phChanges*/, void *data )
5830 {
5831 	FV_View *pView = static_cast<FV_View *>(data);
5832 	bool b;
5833 	UT_ASSERT(data && pPrefs);
5834 	if ( pPrefs->getPrefsValueBool(static_cast<const gchar*>(AP_PREF_KEY_CursorBlink), &b) && b != pView->m_bCursorBlink )
5835 	{
5836 		UT_DEBUGMSG(("FV_View::_prefsListener m_bCursorBlink=%s m_bCursorIsOn=%s\n",
5837 					 pView->m_bCursorBlink ? "TRUE" : "FALSE",
5838 					 pView->m_bCursorIsOn ? "TRUE" : "FALSE"));
5839 
5840 		pView->m_bCursorBlink = b;
5841 		pView->m_pG->allCarets()->setBlink(b);
5842 	}
5843 
5844 
5845 	// Update colors
5846    	const gchar * pszTmpColor = NULL;
5847 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForShowPara), &pszTmpColor))
5848 	{
5849 		UT_parseColor(pszTmpColor, pView->m_colorShowPara);
5850 	}
5851 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForSquiggle), &pszTmpColor))
5852 	{
5853 		UT_parseColor(pszTmpColor, pView->m_colorSpellSquiggle);
5854 	}
5855 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForGrammarSquiggle), &pszTmpColor))
5856 	{
5857 		UT_parseColor(pszTmpColor, pView->m_colorGrammarSquiggle);
5858 	}
5859 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForMargin), &pszTmpColor))
5860 	{
5861 		UT_parseColor(pszTmpColor, pView->m_colorMargin);
5862 	}
5863 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForFieldOffset), &pszTmpColor))
5864 	{
5865 		UT_parseColor(pszTmpColor, pView->m_colorFieldOffset);
5866 	}
5867 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForImage), &pszTmpColor))
5868 	{
5869 		UT_parseColor(pszTmpColor, pView->m_colorImage);
5870 	}
5871 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForHyperLink), &pszTmpColor))
5872 	{
5873 		UT_parseColor(pszTmpColor, pView->m_colorHyperLink);
5874 	}
5875 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForHdrFtr), &pszTmpColor))
5876 	{
5877 		UT_parseColor(pszTmpColor, pView->m_colorHdrFtr);
5878 	}
5879 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForColumnLine), &pszTmpColor))
5880 	{
5881 		UT_parseColor(pszTmpColor, pView->m_colorColumnLine);
5882 	}
5883 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision1), &pszTmpColor))
5884 	{
5885 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[0]);
5886 	}
5887 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision2), &pszTmpColor))
5888 	{
5889 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[1]);
5890 	}
5891 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision3), &pszTmpColor))
5892 	{
5893 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[2]);
5894 	}
5895 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision4), &pszTmpColor))
5896 	{
5897 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[3]);
5898 	}
5899 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision5), &pszTmpColor))
5900 	{
5901 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[4]);
5902 	}
5903 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision6), &pszTmpColor))
5904 	{
5905 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[5]);
5906 	}
5907 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision7), &pszTmpColor))
5908 	{
5909 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[6]);
5910 	}
5911 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision8), &pszTmpColor))
5912 	{
5913 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[7]);
5914 	}
5915 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision9), &pszTmpColor))
5916 	{
5917 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[8]);
5918 	}
5919 	if (pPrefs->getPrefsValue(static_cast<const gchar *>(XAP_PREF_KEY_ColorForRevision10), &pszTmpColor))
5920 	{
5921 		UT_parseColor(pszTmpColor, pView->m_colorRevisions[9]);
5922 	}
5923 	pView->m_bgColorInitted = false; // force refresh/update on next getColorSelBackground () call
5924 	//
5925 	// Set this true to redraw the entire screen during the configure event
5926 	// Other we get lefover crap at the top of the window
5927 	//
5928 	pView->setConfigure(true);
5929 
5930 	// FIXME:jskov: is it necessary to do something here to cause a full redraw?
5931 	if (!pView->m_bWarnedThatRestartNeeded &&
5932 		(( (pPrefs->getPrefsValueBool(static_cast<const gchar*>(AP_PREF_KEY_DefaultDirectionRtl), &b) && b != pView->m_bDefaultDirectionRtl))
5933 		 || ((pPrefs->getPrefsValueBool(static_cast<const gchar*>(XAP_PREF_KEY_UseHebrewContextGlyphs), &b) && b != pView->m_bUseHebrewContextGlyphs)))
5934 		)
5935 	{
5936 		/*	It is possible to change this at runtime, but it may impact the
5937 			way the document is displayed in an unexpected way (from the user's
5938 			point of view). It is therefore probably better to apply this change
5939 			only when AW will be restarted or a new document is created and
5940 			notify the user about that.
5941 		*/
5942 		XAP_Frame * pFrame = static_cast<XAP_Frame *>(pView->getParentData());
5943 		UT_return_if_fail((pFrame));
5944 
5945 		pFrame->showMessageBox(AP_STRING_ID_MSG_AfterRestartNew, XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK);
5946 		pView->m_bWarnedThatRestartNeeded = true;
5947 	}
5948 }
5949 
5950 
5951 /*!
5952  * Copy a header/footer from a pHdrFtrSrc to an empty pHdrFtrDest.
5953  * into a new type of header/footer in the same section.
5954  */
_populateThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtrSrc,fl_HdrFtrSectionLayout * pHdrFtrDest)5955 void FV_View::_populateThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtrSrc, fl_HdrFtrSectionLayout * pHdrFtrDest)
5956 {
5957 	PD_DocumentRange dr_source;
5958 	PT_DocPosition iPos1,iPos2;
5959 
5960 	UT_return_if_fail(pHdrFtrSrc->getFirstLayout());
5961 	iPos1 = m_pDoc->getStruxPosition(pHdrFtrSrc->getFirstLayout()->getStruxDocHandle());
5962 
5963 	fl_BlockLayout * pLast = static_cast<fl_BlockLayout *>(pHdrFtrSrc->getLastLayout());
5964 	iPos2 = pLast->getPosition(false);
5965 //
5966 // This code assumes there is an End of Block run at the end of the Block.
5967 // Thanks to Jesper, there always is!
5968 //
5969 	while(pLast->getNext() != NULL)
5970 	{
5971 		pLast = static_cast<fl_BlockLayout *>(pLast->getNext());
5972 	}
5973 	fp_Run * pRun = pLast->getFirstRun();
5974 	while( pRun->getNextRun() != NULL)
5975 	{
5976 		pRun = pRun->getNextRun();
5977 	}
5978 	iPos2 += pRun->getBlockOffset();
5979 //
5980 // OK got the doc range for the source. Set it and copy it.
5981 //
5982 	dr_source.set(m_pDoc,iPos1,iPos2);
5983 //
5984 // Copy to and from clipboard to populate the header/Footer
5985 //
5986 	UT_DEBUGMSG(("SEVIOR: Copy to clipboard making header/footer \n"));
5987 	m_pApp->copyToClipboard(&dr_source);
5988 	PT_DocPosition posDest = 0;
5989 	posDest = pHdrFtrDest->getFirstLayout()->getPosition(true);
5990 	PD_DocumentRange dr_dest(m_pDoc,posDest,posDest);
5991 	UT_DEBUGMSG(("SEVIOR: Pasting to clipboard making header/footer \n"));
5992 	m_pApp->pasteFromClipboard(&dr_dest,true,true);
5993 }
5994 
5995 
5996 /*!
5997  * This method removes the HdrFtr pHdrFtr
5998  */
_removeThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtr)5999 void FV_View::_removeThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtr)
6000 {
6001 	if(pHdrFtr == NULL)
6002 	{
6003 		return;
6004 	}
6005 	UT_DEBUGMSG(("view_protected: Removing HdrFtr %p \n",pHdrFtr));
6006 //
6007 // Need this to remove the HdrFtr attributes in the section strux.
6008 //
6009 	const gchar * pszHdrFtrType = NULL;
6010 	UT_ASSERT(pHdrFtr->getContainerType() == FL_CONTAINER_HDRFTR);
6011 	pf_Frag_Strux* sdhHdrFtr = pHdrFtr->getStruxDocHandle();
6012 	m_pDoc->getAttributeFromSDH(sdhHdrFtr,isShowRevisions(),getRevisionLevel(),PT_TYPE_ATTRIBUTE_NAME, &pszHdrFtrType);
6013 //
6014 // Remove the header/footer strux
6015 //
6016 	m_pDoc->deleteHdrFtrStrux(sdhHdrFtr);
6017 }
6018 
_cmdEditHdrFtr(HdrFtrType hfType)6019 void FV_View::_cmdEditHdrFtr(HdrFtrType hfType)
6020 {
6021 	fp_Page * pPage = getCurrentPage();
6022 //
6023 // If there is no header/footer, insert it and start to edit it.
6024 //
6025 	fl_HdrFtrShadow * pShadow = NULL;
6026 	fp_ShadowContainer * pHFCon = NULL;
6027 	pHFCon = pPage->getHdrFtrP(hfType);
6028 	if(pHFCon == NULL)
6029 	{
6030 		insertHeaderFooter(hfType);
6031 		return;
6032 	}
6033 
6034 	if(isHdrFtrEdit())
6035 		clearHdrFtrEdit();
6036 	pShadow = pHFCon->getShadow();
6037 	UT_return_if_fail(pShadow);
6038 //
6039 // Put the insertion point at the beginning of the header
6040 //
6041 	fl_BlockLayout * pBL = pShadow->getNextBlockInDocument();
6042 	if (!isSelectionEmpty())
6043 		_clearSelection();
6044 
6045 	_setPoint(pBL->getPosition());
6046 //
6047 // Set Header/footer mode and we're done! Easy :-)
6048 //
6049 	setHdrFtrEdit(pShadow);
6050 	_generalUpdate();
6051 	_updateInsertionPoint();
6052 }
6053 
6054 
6055 /*	the problem with using bool to store the PT state is that
6056 	when we make two successive calls to _saveAndNotifyPieceTableChange
6057 	all subsequent calls to _restorePieceTableState will end up in the
6058 	else branch, i.e, the PT will remain in state of change. Thus,
6059 	the new implementation uses int instead of bool and actually keeps
6060 	count of the calls to _save...;
6061 */
_saveAndNotifyPieceTableChange(void)6062 void FV_View::_saveAndNotifyPieceTableChange(void)
6063 {
6064 	//UT_DEBUGMSG(("notifying PieceTableChange start [%d]\n", m_iPieceTableState));
6065 	if(m_pDoc->isPieceTableChanging())
6066 		m_iPieceTableState++;
6067 	m_pDoc->notifyPieceTableChangeStart();
6068 }
6069 
_restorePieceTableState(void)6070 void FV_View::_restorePieceTableState(void)
6071 {
6072 	if(m_iPieceTableState > 0)
6073 	{
6074 		//UT_DEBUGMSG(("notifying PieceTableChange (restore/start) [%d]\n", m_iPieceTableState));
6075 		m_pDoc->notifyPieceTableChangeStart();
6076 		m_iPieceTableState--;
6077 	}
6078 	else
6079 	{
6080 		//UT_DEBUGMSG(("notifying PieceTableChange (restore/end) [%d]\n", m_iPieceTableState));
6081 		m_pDoc->notifyPieceTableChangeEnd();
6082 		m_iPieceTableState =0;
6083 	}
6084 }
6085 
6086 
_fixInsertionPointAfterRevision()6087 void FV_View::_fixInsertionPointAfterRevision()
6088 {
6089 	if(!m_pDoc->isMarkRevisions() && isSelectionEmpty())
6090 	{
6091 		UT_DebugOnly<bool> bRet;
6092 
6093 		// Signal PieceTable Change
6094 		_saveAndNotifyPieceTableChange();
6095 
6096 		PT_DocPosition posStart = getPoint();
6097 		PT_DocPosition posEnd = posStart;
6098 
6099 		const gchar rev[] = "revision";
6100 		const gchar val[] = "";
6101 		const gchar * attr[3] = {rev,val,NULL};
6102 
6103 		bRet = m_pDoc->changeSpanFmt(PTC_RemoveFmt,posStart,posEnd,attr,NULL);
6104 		UT_ASSERT(bRet);
6105 
6106 		// Signal piceTable is stable again
6107 		_restorePieceTableState();
6108 
6109 		// might need to do general update here; leave it off for now
6110 		// _generalUpdate();
6111 		_fixInsertionPointCoords();
6112 	}
6113 }
6114 
_makePointLegal(void)6115 bool FV_View::_makePointLegal(void)
6116 {
6117 		bool bOK = true;
6118 		while(!isPointLegal() && bOK)
6119 		{
6120 //
6121 // If we're in an illegal position move forward till we're safe.
6122 //
6123 			bOK = _charMotion(true,1);
6124 		}
6125 		PT_DocPosition posEnd = 0;
6126 		getEditableBounds(true, posEnd);
6127 		if(posEnd == getPoint() && !isPointLegal())
6128 		{
6129 			bOK = _charMotion(false,1);
6130 		}
6131 		if(posEnd-1 == getPoint() && !isPointLegal())
6132 		{
6133 			bOK = _charMotion(false,1);
6134 		}
6135 		if(posEnd-1 == getPoint() && m_pDoc->isEndFrameAtPos(getPoint()) && m_pDoc->isFrameAtPos(getPoint()-1))
6136 		{
6137 			bOK = _charMotion(false,1);
6138 		}
6139 		while(bOK && !isPointLegal())
6140 		{
6141 			bOK = _charMotion(false,1);
6142 		}
6143 		return bOK;
6144 }
6145 
_charInsert(const UT_UCSChar * text,UT_uint32 count,bool bForce)6146 bool FV_View::_charInsert(const UT_UCSChar * text, UT_uint32 count, bool bForce)
6147 {
6148 
6149 	// see if prefs specify we should set language based on kbd layout
6150 	UT_return_val_if_fail(m_pApp, false);
6151 	bool bSetLang = false;
6152 	m_pApp->getPrefsValueBool(static_cast<const gchar*>(XAP_PREF_KEY_ChangeLanguageWithKeyboard),
6153 							  &bSetLang);
6154 
6155 	const UT_LangRecord * pLR = NULL;
6156 
6157 	if(bSetLang)
6158 		pLR = m_pApp->getKbdLanguage();
6159 
6160 
6161 	bool bResult = true;
6162 	// So this gets rid of the annoying cursor flash at the beginning
6163 	// of the line upon character insertion, but it's the wrong thing to
6164 	// do.  The right thing to do is to either delay calculation, or to
6165 	// not make the wrong number come up; disabling the caret is wrong. -PL
6166 	GR_Painter caretDisablerPainter(m_pG); // not an elegant way to disable all carets, but it works beautifully - MARCM
6167 
6168 	STD_DOUBLE_BUFFERING_FOR_THIS_FUNCTION
6169 
6170 	// Signal PieceTable Change
6171 	_saveAndNotifyPieceTableChange();
6172 	bool doInsert = true;
6173 
6174 	// Turn off list updates
6175 	m_pDoc->disableListUpdates();
6176 	if (!isSelectionEmpty() && !m_FrameEdit.isActive())
6177 	{
6178 		m_pDoc->beginUserAtomicGlob();
6179 		PP_AttrProp AttrProp_Before;
6180 		_deleteSelection(&AttrProp_Before);
6181 		bool bOK = true;
6182 		if(!isPointLegal() && bOK)
6183 		{
6184 //
6185 // If we're in an illegal position move forward till we're safe.
6186 //
6187 			bOK = _charMotion(true,1);
6188 		}
6189 
6190 		if(pLR)
6191 			AttrProp_Before.setProperty("lang", pLR->m_szLangCode);
6192 		insertParaBreakIfNeededAtPos(getPoint());
6193 		bResult = m_pDoc->insertSpan(getPoint(), text, count, &AttrProp_Before);
6194 		m_pDoc->endUserAtomicGlob();
6195 	}
6196 	else
6197 	{
6198 		if(m_FrameEdit.isActive())
6199 		{
6200 			m_FrameEdit.setPointInside();
6201 		}
6202 		bool bOK = true;
6203 		if(!isPointLegal() && bOK)
6204 		{
6205 //
6206 // If we're in an illegal position move forward till we're safe.
6207 //
6208 			bOK = _charMotion(true,1);
6209 		}
6210 		PT_DocPosition posEnd = 0;
6211 		getEditableBounds(true, posEnd);
6212 		if(posEnd == getPoint() && !isPointLegal())
6213 		{
6214 			bOK = _charMotion(false,1);
6215 		}
6216 		if(posEnd-1 == getPoint() && !isPointLegal())
6217 		{
6218 			bOK = _charMotion(false,1);
6219 		}
6220 		if(posEnd-1 == getPoint() && m_pDoc->isEndFrameAtPos(getPoint()) && m_pDoc->isFrameAtPos(getPoint()-1))
6221 		{
6222 			bOK = _charMotion(false,1);
6223 		}
6224 		bool bOverwrite = (!m_bInsertMode && !bForce);
6225 
6226 		if (bOverwrite)
6227 		{
6228 			// we need to glob when overwriting
6229 			m_pDoc->beginUserAtomicGlob();
6230 			cmdCharDelete(true,count);
6231 		}
6232 		if(text[0] == UCS_TAB && count == 1)
6233 		{
6234 			//
6235 			// Were inserting a TAB. Handle special case of TAB
6236 			// right after a list-label combo
6237 			//
6238 			UT_sint32 iCount=0;
6239 			if((   ((isTabListBehindPoint(iCount) == true) && (iCount == 2)) || isTabListAheadPoint() == true)
6240 			    && getCurrentBlock()->isFirstInList() == false)
6241 			{
6242 				//
6243 				// OK now start a sublist of the same type as the
6244 				// current list if the list type is of numbered type
6245 				fl_BlockLayout * pBlock = getCurrentBlock();
6246 				FL_ListType curType = pBlock->getListType();
6247 //
6248 // Now increase list level for bullet lists too
6249 //
6250 				{
6251 					UT_uint32 curlevel = pBlock->getLevel();
6252 					UT_uint32 currID = pBlock->getAutoNum()->getID();
6253 					curlevel++;
6254 					fl_AutoNum * pAuto = pBlock->getAutoNum();
6255 					const gchar * pszAlign = pBlock->getProperty("margin-left",true);
6256 					const gchar * pszIndent = pBlock->getProperty("text-indent",true);
6257 					const gchar * pszFieldF = pBlock->getProperty("field-font",true);
6258 					float fAlign = static_cast<float>(atof(pszAlign));
6259 					float fIndent = static_cast<float>(atof(pszIndent));
6260 //
6261 // Convert pixels to inches.
6262 //
6263 					float maxWidthIN = static_cast<float>((static_cast<float>(pBlock->getFirstContainer()->getContainer()->getWidth()))/100. -0.6);
6264 					if(fAlign + static_cast<float>(LIST_DEFAULT_INDENT) < maxWidthIN)
6265 					{
6266 						fAlign += static_cast<float>(LIST_DEFAULT_INDENT);
6267 					}
6268 					pBlock->StartList(curType,pAuto->getStartValue32(),pAuto->getDelim(),pAuto->getDecimal(),pszFieldF,fAlign,fIndent, currID,curlevel);
6269 					doInsert = false;
6270 				}
6271 			}
6272 		}
6273 
6274 		if (doInsert)
6275 		{
6276 			if(pLR)
6277 			{
6278 				PP_AttrProp AP;
6279 				AP.setProperty("lang", pLR->m_szLangCode);
6280 				m_pDoc->insertFmtMark(PTC_AddFmt,getPoint(), &AP);
6281 			}
6282 			insertParaBreakIfNeededAtPos(getPoint());
6283 			const fl_BlockLayout * pBL = getCurrentBlock();
6284 			PP_AttrProp * pSpanAP = const_cast<PP_AttrProp *>(getAttrPropForPoint());
6285 			bResult = m_pDoc->insertSpan(getPoint(), text, count,pSpanAP);
6286 
6287 			if(!bResult)
6288 			{
6289 				const PP_AttrProp *pBlockAP = NULL;
6290 				pBL->getAP(pBlockAP);
6291 				bResult = m_pDoc->insertSpan(getPoint(), text, count,
6292 											 const_cast<PP_AttrProp *>(pBlockAP));
6293 				UT_ASSERT(bResult);
6294 			}
6295 		}
6296 
6297 		if (bOverwrite)
6298 		{
6299 			m_pDoc->endUserAtomicGlob();
6300 		}
6301 	}
6302 	if(m_FrameEdit.isActive())
6303 	{
6304 		m_FrameEdit.setMode(FV_FrameEdit_NOT_ACTIVE);
6305 	}
6306 
6307 	// Signal PieceTable Changes have finished
6308 	_restorePieceTableState();
6309 
6310 	// restore updates and clean up dirty lists
6311 	m_pDoc->enableListUpdates();
6312 	m_pDoc->updateDirtyLists();
6313 
6314 	if(!doInsert)
6315 	{
6316 	  notifyListeners(AV_CHG_ALL);
6317 	}
6318 
6319 	_generalUpdate();
6320 	_fixInsertionPointCoords();
6321 	_ensureInsertionPointOnScreen();
6322 
6323 	return bResult;
6324 }
6325 
_adjustDeletePosition(UT_uint32 & iDocPos,UT_uint32 & iCount)6326 void FV_View::_adjustDeletePosition(UT_uint32 &iDocPos, UT_uint32 &iCount)
6327 {
6328 	// This code deals with character clusters, such as the Thai base character + vowel + tone
6329 	// mark combinations. Basically, if we are asked to delete the base character, we also have
6330 	// to delete any of the other characters from its cluster. We need to find the runs that
6331 	// contain the start and end of the requested delete segment and have them to adjust the
6332 	// delete offsets.
6333 	//
6334 
6335 	//
6336 	// Also use this code to deal with attempts to delete across hdrftr
6337 	// boundaries
6338 
6339 	fl_BlockLayout * pBlock = NULL;
6340 	pBlock = _findBlockAtPosition(iDocPos);
6341 
6342 	UT_return_if_fail( pBlock );
6343 
6344 	if(static_cast<UT_uint32>(pBlock->getLength()) <  iDocPos - pBlock->getPosition())
6345 	{
6346 		return;
6347 	}
6348 
6349 	fp_Run * pRun = pBlock->findRunAtOffset(iDocPos - pBlock->getPosition());
6350 	UT_return_if_fail( pRun );
6351 
6352 	UT_uint32 pos1 = iDocPos;
6353 	UT_uint32 iRunOffset = pBlock->getPosition() + pRun->getBlockOffset();
6354 	UT_uint32 iLen = UT_MIN(iCount, pRun->getLength() - (iDocPos - iRunOffset));
6355 	bool bMoreThanOneRun = (iCount > iLen);
6356 
6357 	// this call modifies both pos1, and iLen
6358 	pRun->adjustDeletePosition(pos1, iLen);
6359 
6360 	if(bMoreThanOneRun)
6361 	{
6362 		// the deletion spans more than a single run
6363 		// locate the run that contains the last char to be deleted
6364 
6365 		UT_uint32 iOrigEndOffset = iDocPos + iCount - 1; // doc offset of the last char to be deleted
6366 
6367 		fl_BlockLayout * pEndBlock = _findBlockAtPosition(iOrigEndOffset);
6368 		UT_return_if_fail( pEndBlock );
6369 		if(static_cast<UT_uint32>(pEndBlock->getLength()) <  iOrigEndOffset - pEndBlock->getPosition())
6370 		{
6371 			return;
6372 		}
6373 
6374 		fp_Run * pEndRun = pEndBlock->findRunAtOffset(iOrigEndOffset - pEndBlock->getPosition());
6375 
6376 		if(!pEndRun)
6377 		{
6378 			// this happens when there is no real run in the block (e.g., just a fmt mark
6379 			// and eop
6380 			return;
6381 		}
6382 
6383 		UT_uint32 iEndRunOffset = pEndBlock->getPosition() + pEndRun->getBlockOffset();
6384 
6385 		// how much of the deleted sequence is in our run?
6386 		iLen = iDocPos + iCount - iEndRunOffset;
6387 		UT_ASSERT_HARMLESS( iLen <= pEndRun->getLength());
6388 
6389 		pEndRun->adjustDeletePosition(iEndRunOffset, iLen);
6390 		UT_DEBUGMSG(("iCount adjusted from %d to %d \n",iCount,iEndRunOffset + iLen - pos1));
6391 		iCount  = iEndRunOffset + iLen - pos1;
6392 	}
6393 	else
6394 	{
6395 		// adjust count
6396 		iCount  = iLen;
6397 	}
6398 
6399 	// adjust point
6400 	iDocPos = pos1;
6401 }
6402 
_updateSelectionHandles(void)6403 void FV_View::_updateSelectionHandles(void)
6404 {
6405 	if (!getVisualSelectionEnabled()){
6406 		m_SelectionHandles.hide();
6407 	} else if (isSelectionEmpty()) {
6408 		m_SelectionHandles.setCursor(getInsPoint());
6409 	} else {
6410 		m_SelectionHandles.setSelection(getSelectionLeftAnchor(),
6411 										getSelectionRightAnchor());
6412 	}
6413 }
6414