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