Logo Search packages:      
Sourcecode: abiword version File versions

fv_View_protected.cpp

/* -*- c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- */
/* AbiWord
 * Copyright (C) 1998-2000 AbiSource, Inc.
 * Copyright (c) 2001,2002 Tomas Frydrych
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <locale.h>

#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "ut_growbuf.h"
#include "ut_misc.h"
#include "ut_string.h"
#include "ut_bytebuf.h"
#include "ut_timer.h"
#include "ut_types.h"
#include "xav_View.h"
#include "fl_DocLayout.h"
#include "fl_BlockLayout.h"
#include "fl_TOCLayout.h"
#include "fl_Squiggles.h"
#include "fl_SectionLayout.h"
#include "fl_AutoNum.h"
#include "fp_Page.h"
#include "fp_PageSize.h"
#include "fp_Column.h"
#include "fp_TableContainer.h"
#include "fp_Line.h"
#include "fp_Run.h"
#include "fp_TextRun.h"
#include "fg_Graphic.h"
#include "fg_GraphicRaster.h"
#include "pd_Document.h"
#include "pd_Style.h"
#include "pp_Property.h"
#include "pp_AttrProp.h"
#include "gr_Graphics.h"
#include "gr_DrawArgs.h"
#include "ie_types.h"
#include "xap_App.h"
#include "xap_Frame.h"
#include "xap_Clipboard.h"
#include "ap_TopRuler.h"
#include "ap_LeftRuler.h"
#include "ap_Prefs.h"
#include "fd_Field.h"
#include "spell_manager.h"
#include "ut_rand.h"
#include "fl_FootnoteLayout.h"
#include "pp_Revision.h"
#include "gr_Painter.h"

#include "fv_View.h"

#if 1
// todo: work around to remove the INPUTWORDLEN restriction for pspell
#include "ispell_def.h"
#endif

// NB -- irrespective of this size, the piecetable will store
// at max BOOKMARK_NAME_LIMIT of chars as defined in pf_Frag_Bookmark.h
#define BOOKMARK_NAME_SIZE 30
#define CHECK_WINDOW_SIZE if(getWindowHeight() < 20) return;
// returns true iff the character BEFORE pos is a space.
// Special cases:
// -returns true if pos is at the beginning of the document
// -returns false if pos is not within the document
bool FV_View::_isSpaceBefore(PT_DocPosition pos)
{
      UT_GrowBuf buffer;

      fl_BlockLayout * block = m_pLayout->findBlockAtPosition(pos);
      if (block)
      {

            PT_DocPosition offset = pos - block->getPosition(false);
            // Just look at the previous character in this block, if there is one...
            if (offset > 0)
            {
                  block->getBlockBuf(&buffer);
                  return (UT_UCS4_isspace(*reinterpret_cast<UT_UCSChar *>(buffer.getPointer(offset - 1))));
            }
            else
            {
                  return true;
            }
      }
      else
            return false;
}

/*!
  Reverse the direction of the current selection
  Does so without changing the screen.
*/
void FV_View::_swapSelectionOrientation(void)
{
      UT_ASSERT(!isSelectionEmpty());
      _fixInsertionPointCoords();
      PT_DocPosition curPos = getPoint();
      UT_ASSERT(curPos != m_Selection.getSelectionAnchor());
      _setPoint(m_Selection.getSelectionAnchor());
      m_Selection.setSelectionAnchor(curPos);
}

/*!
  Move point to requested end of selection and clear selection
  \param bForward True if point should be moved to the forward position

  \note Do not draw the insertion point after clearing the
            selection.
  \fixme BIDI broken?
*/
void FV_View::_moveToSelectionEnd(bool bForward)
{
      UT_ASSERT(!isSelectionEmpty());

      PT_DocPosition curPos = getPoint();
      UT_ASSERT(curPos != m_Selection.getSelectionAnchor());
      bool bForwardSelection = (m_Selection.getSelectionAnchor() < curPos);

      if (bForward != bForwardSelection)
      {
            _swapSelectionOrientation();
      }

      _clearSelection();

      return;
}

void FV_View::_eraseSelection(void)
{
      _fixInsertionPointCoords();
      if (!m_Selection.isSelected())
      {
            _resetSelection();
            return;
      }

      UT_uint32 iPos1, iPos2;

      if (m_Selection.getSelectionAnchor() < getPoint())
      {
            iPos1 = m_Selection.getSelectionAnchor();
            iPos2 = getPoint();
      }
      else
      {
            iPos1 = getPoint();
            iPos2 = m_Selection.getSelectionAnchor();
      }
      m_iLowDrawPoint = 0;
      m_iHighDrawPoint = 0;;

      _clearBetweenPositions(iPos1, iPos2, true);
}

void FV_View::_clearSelection(void)
{
      if( isSelectionEmpty() )
      {
            return;
      }
      m_pG->getCaret()->enable();

      _fixInsertionPointCoords();
      if (!m_Selection.isSelected())
      {
            _resetSelection();
            return;
      }

      UT_uint32 iPos1, iPos2;
      if(m_Selection.getSelectionMode() < FV_SelectionMode_Multiple)
      {
            if (m_Selection.getSelectionAnchor() < getPoint())
            {
                  iPos1 = m_Selection.getSelectionAnchor();
                  iPos2 = getPoint();
            }
            else
            {
                  iPos1 = getPoint();
                  iPos2 = m_Selection.getSelectionAnchor();
            }
            bool bres = _clearBetweenPositions(iPos1, iPos2, true);
            if(!bres)
                  return;
      
            _resetSelection();
            m_iLowDrawPoint = 0;
            m_iHighDrawPoint = 0;

            _drawBetweenPositions(iPos1, iPos2);
      }
      else
      {
            UT_sint32 i = 0;
            UT_GenericVector<PD_DocumentRange *> vecRanges;
            vecRanges.clear();
            for(i=0; i<m_Selection.getNumSelections();i++)
            {
                  PD_DocumentRange * pTmp =m_Selection.getNthSelection(i);
                  PD_DocumentRange * pTmp2 = new PD_DocumentRange(m_pDoc,pTmp->m_pos1,pTmp->m_pos2);
                  vecRanges.addItem(pTmp2);
            }
            for(i=0; i< static_cast<UT_sint32>(vecRanges.getItemCount());i++)
            {
                  PD_DocumentRange * pDocR = vecRanges.getNthItem(i);
                  if(pDocR)
                  {
                        iPos1 = pDocR->m_pos1;
                        iPos2 = pDocR->m_pos2;
                        if(iPos1 == iPos2)
                        {
                              iPos2++;
                        }
                        /*bool bres =*/ _clearBetweenPositions(iPos1, iPos2, true);
                  }
            }
            _resetSelection();
            for(i=0; i< static_cast<UT_sint32>(vecRanges.getItemCount());i++)
            {
                  PD_DocumentRange * pDocR = vecRanges.getNthItem(i);
                  if(pDocR)
                  {
                        iPos1 = pDocR->m_pos1;
                        iPos2 = pDocR->m_pos2;
                        if(iPos1 == iPos2)
                        {
                              iPos2++;
                        }
                        _drawBetweenPositions(iPos1, iPos2);
                  }
            }
            UT_VECTOR_PURGEALL(PD_DocumentRange *,vecRanges);
      }
      _resetSelection();
      m_iLowDrawPoint = 0;
      m_iHighDrawPoint = 0;
}

void FV_View::_drawSelection()
{
      UT_return_if_fail(!isSelectionEmpty());
//    CHECK_WINDOW_SIZE
      UT_DEBUGMSG(("_drawSelection getPoint() %d m_Selection.getSelectionAnchor() %d \n",getPoint(),m_Selection.getSelectionAnchor()));
      if(m_Selection.getSelectionMode() < FV_SelectionMode_Multiple)
      {
            if (m_Selection.getSelectionAnchor() < getPoint())
            {
                  _drawBetweenPositions(m_Selection.getSelectionAnchor(), getPoint());
            }
            else
            {
                  _drawBetweenPositions(getPoint(), m_Selection.getSelectionAnchor());
            }
            m_iLowDrawPoint = UT_MIN(m_Selection.getSelectionAnchor(),getPoint());
            m_iHighDrawPoint = UT_MAX(m_Selection.getSelectionAnchor(),getPoint());
      }
      else
      {
            UT_sint32 i = 0;
            for(i=0; i<m_Selection.getNumSelections();i++)
            {
                  PD_DocumentRange * pDocR = m_Selection.getNthSelection(i);
                  UT_DEBUGMSG(("Drawing between %d and %d \n",pDocR->m_pos1,pDocR->m_pos2));
                  if(pDocR)
                  {
                        PT_DocPosition iPos1 = pDocR->m_pos1;
                        PT_DocPosition iPos2 = pDocR->m_pos2;
                        if(iPos1 == iPos2)
                        {
                              iPos2++;
                        }
                        _drawBetweenPositions(iPos1, iPos2);
                  }
            }
            m_iLowDrawPoint = 0;
            m_iHighDrawPoint = 0;
      }
}

// Note that isClearSelection() might change its tune in one of two ways.
// Way #1 is by calling one of the next few methods.
//   BUT! this never happens because m_Selection.getSelectionAnchor() == getPoint by def.
// Way #2 is if the Selection is set and the point is changed so that it
//          no longer equals m_Selection.getSelectionAnchor().
void FV_View::_resetSelection(void)
{
      m_Selection.clearSelection();
      m_Selection.setSelectionAnchor(getPoint());
      m_iGrabCell = 0;
}

void FV_View::_setSelectionAnchor(void)
{
      m_Selection.setMode(FV_SelectionMode_Single);
      m_Selection.setSelectionAnchor(getPoint());
}

void FV_View::_deleteSelection(PP_AttrProp *p_AttrProp_Before, bool bNoUpdate)
{
      // delete the current selection.
      // NOTE: this must clear the selection.

      UT_ASSERT(!isSelectionEmpty());

      PT_DocPosition iPoint = getPoint();

      UT_uint32 iRealDeleteCount;

      UT_uint32 iSelAnchor = m_Selection.getSelectionAnchor();
      if(iSelAnchor < 2)
      {
            iSelAnchor = 2;
      }
      if(m_FrameEdit.isActive())
      {
              deleteFrame();
            return;
      }
      UT_ASSERT(iPoint != iSelAnchor);

      PT_DocPosition iLow = UT_MIN(iPoint,iSelAnchor);
      PT_DocPosition iHigh = UT_MAX(iPoint,iSelAnchor);

      // deal with character clusters, such as base char + vowel + tone mark in Thai
      UT_uint32 iLen = iHigh - iLow;
      _adjustDeletePosition(iLow, iLen); // modifies both iLow and iLen
      iHigh = iLow + iLen;
//
// OK adjust for deletetions that cross footnote/endnote boundaries.
//
      fl_FootnoteLayout * pFHigh = NULL;
      fl_FootnoteLayout * pFLow = NULL;
      fl_EndnoteLayout * pEHigh = NULL;
      fl_EndnoteLayout * pELow = NULL;
      if(isInFootnote(iHigh))
      {
            pFHigh = getClosestFootnote(iHigh);
            PT_DocPosition j = pFHigh->getPosition()+1; // Leave reference
            if(j > iLow)
            {
                  iLow = j;
            }
      }
      else if(isInFootnote(iLow))
      {

// Here if we're not in footnote at iHigh

            pFLow = getClosestFootnote(iLow);
            iHigh = pFLow->getPosition(true) + pFLow->getLength() -1;
      }
      else if(isInEndnote(iHigh))
      {
            pEHigh = getClosestEndnote(iHigh);
            PT_DocPosition j = pEHigh->getPosition()+1; // Leave reference
            if(j > iLow)
            {
                  iLow = j;
            }
      }
      else if(isInEndnote(iLow))
      {

// Here if we're not in Endnote at iHigh

            pELow = getClosestEndnote(iLow);
            iHigh = pELow->getPosition(true) + pELow->getLength() -1;
      }
//
// Don't delete the block right before a TOC!
//
      fl_BlockLayout * pBL = _findBlockAtPosition(iLow);
      if(pBL->getPrev() && pBL->getPrev()->getContainerType() == FL_CONTAINER_TOC)
      {
            if(pBL->getPosition(true) == iLow)
            {
                  iLow++;
            }
      }
      else if((pBL->getPosition() + pBL->getLength()) < iLow)
      {
            iLow++;
      }
      bool bDeleteTables = !isInTable(iLow) && !isInTable(iHigh);
      if(!bDeleteTables)
      {
            if(!isInTable(iLow-1) && (isInTable(iHigh+1) && !isInTable(iHigh+2)))
            {
                  if(isInTable(iLow) && !isInTable(iLow-1))
                  {
                        iLow = iLow-1;
                  }
                  if(isInTable(iHigh+1) && !isInTable(iHigh+2))
                  {
                        iHigh = iHigh+1;
                  }
                  bDeleteTables = true;
            }
      }
      if(!isInFrame(iLow) && isInFrame(iHigh))
      {
            fl_FrameLayout * pFL = getFrameLayout(iHigh);
            iHigh =pFL->getPosition(true);
      }
      if(isInFrame(iLow) && !isInFrame(iHigh))
      {
            fl_FrameLayout * pFL = getFrameLayout(iLow);
            iHigh =pFL->getPosition(true) + pFL->getLength() -1;
      }
      if(m_pDoc->isFrameAtPos(iLow) && m_pDoc->isEndFrameAtPos(iHigh))
      {
              iHigh++;
      }
      _resetSelection();

      if(!bNoUpdate)
            _clearBetweenPositions(iLow, iHigh, true);

      bool bOldDelete = m_pDoc->isDontImmediateLayout();
      if(bDeleteTables || bNoUpdate)
      {
            m_pDoc->setDontImmediatelyLayout(true);
      }
      m_pDoc->beginUserAtomicGlob();
      m_pDoc->deleteSpan(iLow, iHigh, p_AttrProp_Before, iRealDeleteCount, bDeleteTables);
      //
      // Handle case of no valid block because of hidden text
      //
      pBL = getCurrentBlock();
      if(pBL && (pBL->getNextBlockInDocument() == NULL) && (pBL->getPrevBlockInDocument() == NULL))
      {
           if(pBL->isHidden() == FP_HIDDEN_TEXT)
           {
                const char * props[3] = {"display",NULL,NULL};
                PT_DocPosition pos = pBL->getPosition();
              PT_DocPosition posEnd = pos + pBL->getLength() -1;
                m_pDoc->changeStruxFmt(PTC_RemoveFmt,pos,posEnd,NULL,props,PTX_Block);
              m_pDoc->changeSpanFmt(PTC_RemoveFmt,pos,posEnd,NULL,props);
           }
      }
//
// Stop any lists remaining if we've deleted their list fields
//
      PT_DocPosition origPos = getPoint();
      if(getCurrentBlock()->getPosition() == iLow)
      {
            PL_StruxDocHandle sdh = getCurrentBlock()->getStruxDocHandle();
            while(getCurrentBlock()->isListItem())
            {
                  m_pDoc->StopList(sdh);
            }
      }
      if(bDeleteTables || bNoUpdate)
      {
            m_pDoc->setDontImmediatelyLayout(bOldDelete);
      }
//
// Can't leave list-tab on a line
//
      if(origPos != getPoint())
      {
            setPoint(origPos);
      }
      if(isTabListAheadPoint() == true)
      {
            UT_uint32 iRealDeleteCount2;
            m_pDoc->deleteSpan(getPoint(), getPoint()+2, p_AttrProp_Before, iRealDeleteCount2);
            iRealDeleteCount += iRealDeleteCount2;
      }
      m_pDoc->endUserAtomicGlob();

      //special handling is required for delete in revisions mode
      //where we have to move the insertion point
      if(isMarkRevisions())
      {
            UT_ASSERT( iRealDeleteCount <= iHigh - iLow + 1 );
            _charMotion(true,iHigh - iLow - iRealDeleteCount);
      }
//
// Make sure the insertion point is in a legal position
//
      PT_DocPosition posEnd = 0;
      getEditableBounds(true, posEnd);
      while(!isPointLegal() && getPoint() < posEnd)
      {
            _charMotion(true,1);
      }
      m_pG->getCaret()->enable();
}

/*!
 * Do the merge between cells.
 * If bBefore is true the contents of source will be prepended into destination otherwise
 * will e appended to the end
 */ 
bool FV_View::_MergeCells( PT_DocPosition posDestination,PT_DocPosition posSource, bool bBefore)
{
//
// get coordinates of source and destination cells
//
      UT_sint32 sLeft,sRight,sTop,sBot;
      UT_sint32 dLeft,dRight,dTop,dBot;
      UT_sint32 fLeft,fRight,fTop,fBot;
      getCellParams(posSource,&sLeft,&sRight,&sTop,&sBot);
      getCellParams(posDestination,&dLeft,&dRight,&dTop,&dBot);
//
      fLeft = UT_MIN(sLeft,dLeft);
      fRight = UT_MAX(sRight,dRight);
      fTop = UT_MIN(sTop,dTop);
      fBot = UT_MAX(sBot,dBot);

      PD_DocumentRange dr_source;
      PL_StruxDocHandle sourceSDH,endSourceSDH,destinationSDH,endDestSDH;
      bool bres = m_pDoc->getStruxOfTypeFromPosition(posSource,PTX_SectionCell,&sourceSDH);
      if(!bres)
      {
            return false;
      }
      endSourceSDH =  m_pDoc->getEndCellStruxFromCellSDH(sourceSDH);
      PT_DocPosition posEndCell = m_pDoc->getStruxPosition(endSourceSDH)-1;
      posSource = m_pDoc->getStruxPosition(sourceSDH)+1;
      bres = m_pDoc->getStruxOfTypeFromPosition(posDestination,PTX_SectionCell,&destinationSDH);
      if(!bres)
      {
            return false;
      }
      endDestSDH = m_pDoc->getEndCellStruxFromCellSDH(destinationSDH);
      PT_DocPosition posEndDestCell = m_pDoc->getStruxPosition(endDestSDH);
//    if(!bBefore)
//    {
//          posDestination = posEndDestCell;
//    }
      m_pDoc->beginUserAtomicGlob();
      if(posEndCell > posSource)
      {
//
// OK got the doc range for the source. Set it and copy it.
//
            dr_source.set(m_pDoc,posSource,posEndCell+1);
//
// Copy to and from clipboard to populate the destination cell
//
            UT_DEBUGMSG(("SEVIOR: Copy to clipboard merging cells \n"));
            m_pApp->copyToClipboard(&dr_source);
      }
//
// Now delete the source cell. We can use the old source position since it
// just needs to point inside the table. 
//
      _deleteCellAt(posSource,sTop,sLeft);
      if(posEndCell > posSource)
      {
//
// Now paste in the text from the source cell.
//
            PD_DocumentRange dr_dest(m_pDoc,posEndDestCell,posEndDestCell);
            UT_DEBUGMSG(("SEVIOR: Pasting from clipboard merging cells \n"));
            m_pApp->pasteFromClipboard(&dr_dest,true,true);
      }
//
// Expand the destination cell into the source cell
//
      _changeCellTo(posDestination,dTop,dLeft,fLeft,fRight,fTop,fBot);
      m_pDoc->endUserAtomicGlob();

//
// We're done!
//
      return true;
}

/*!
 * This method is used to change a parameter of the table to trigger a table
 * rebuild. It also restores all the nice needed for single step undo's
 */
bool FV_View::_restoreCellParams(PT_DocPosition posTable, UT_sint32 iLineType)
{
      const char * pszTable[3] = {NULL,NULL,NULL};
      pszTable[0] = "list-tag";
      UT_String sLineType;
      UT_String_sprintf(sLineType,"%d",iLineType);
      pszTable[1] = sLineType.c_str();
      UT_DEBUGMSG(("SEVIOR: Doing Table strux change of %s %s \n",pszTable[0],pszTable[1]));
      m_pDoc->setDontImmediatelyLayout(false);
      m_pDoc->changeStruxFmt(PTC_AddFmt,posTable,posTable,NULL,pszTable,PTX_SectionTable);

//
// OK finish everything off with the various parameters which allow the formatter to
// be updated.
//
      m_pDoc->allowChangeInsPoint();


      // restore updates and clean up dirty lists
      m_pDoc->enableListUpdates();
      m_pDoc->updateDirtyLists();
      _ensureInsertionPointOnScreen();


      // Signal PieceTable Changes have finished
      _restorePieceTableState();
      _generalUpdate();
      m_pDoc->endUserAtomicGlob();
      return true;
}

/*!
 *  Change the parameters of the table.
 * Return the line type of the table. We'll restore this later.
 */
 UT_sint32 FV_View::_changeCellParams(PT_DocPosition posTable, PL_StruxDocHandle tableSDH)
{
      // Signal PieceTable Change
      _saveAndNotifyPieceTableChange();

      // Turn off list updates

      m_pDoc->disableListUpdates();
      m_pDoc->beginUserAtomicGlob();
      m_pDoc->setDontImmediatelyLayout(true);
      m_pDoc->setDontChangeInsPoint();
//
// Now trigger a rebuild of the whole table by sending a changeStrux to the table strux
// with a bogus line-type property. We'll restore it later.
//
      const char * pszTable[3] = {NULL,NULL,NULL};
      pszTable[0] = "list-tag";
      const char * szLineType = NULL;
      UT_String sLineType;
      UT_sint32 iLineType;
      m_pDoc->getPropertyFromSDH(tableSDH,isShowRevisions(),getRevisionLevel(),pszTable[0],&szLineType);
      if(szLineType == NULL || *szLineType == '\0')
      {
            iLineType = 0;
      }
      else
      {
            iLineType = atoi(szLineType);
            iLineType -= 1;
      }
      UT_String_sprintf(sLineType,"%d",iLineType);
      pszTable[1] = sLineType.c_str();
      UT_DEBUGMSG(("SEVIOR: Doing Table strux change of %s %s \n",pszTable[0],pszTable[1]));
      m_pDoc->changeStruxFmt(PTC_AddFmt,posTable,posTable,NULL,pszTable,PTX_SectionTable);

      return iLineType;
}

/*!
 * This method deletes the cell at (row,col) in the table specified by posTable
 */
bool FV_View::_deleteCellAt(PT_DocPosition posTable, UT_sint32 row, UT_sint32 col)
{
      PL_StruxDocHandle cellSDH,endCellSDH;
      PT_DocPosition posCell = findCellPosAt(posTable,row,col);
      if(posCell == 0)
      {
            return false;
      }
      bool bres = m_pDoc->getStruxOfTypeFromPosition(posCell+1,PTX_SectionCell,&cellSDH);
      if(!bres)
      {
            return false;
      }
      endCellSDH = m_pDoc->getEndCellStruxFromCellSDH(cellSDH);
      if(!endCellSDH)
      {
            return false;
      }
      PT_DocPosition posEndCell = m_pDoc->getStruxPosition(endCellSDH) +1;
      if(posEndCell == 0)
      {
            return false;
      }
//
// check that this
//

//
// We trust that calling routine does all the things needed to make sure this goes
// smoothly and undo's in a single step.
// Here we just delete.
//

//
// OK do the delete
//
      UT_uint32 iRealDeleteCount;

      m_pDoc->deleteSpan( posCell, posEndCell, NULL,iRealDeleteCount,true);

      // if in revisions mode, we might need to move the insertion point if it was within
      // the cell that we are deleting (since the cell stays physically in the document) but
      // the positions within it are now hidden from the user)
      if(isMarkRevisions() && m_iInsPoint > posCell && m_iInsPoint < posEndCell)
      {
            _setPoint(posEndCell);
      }
      
      return true;
}


/*!
 * This method changes the coordinates of the cell at (row,col) in the table specified by
 * posTable to the cordinates specified.
 */
bool FV_View::_changeCellTo(PT_DocPosition posTable, UT_sint32 rowold, UT_sint32 colold,
                                      UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot)
{
      PT_DocPosition posCell = findCellPosAt(posTable,rowold,colold) + 1;
      if(posCell == 0)
      {
            return false;
      }
      const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
      UT_String sLeft,sRight,sTop,sBot;
      props[0] = "left-attach";
      UT_String_sprintf(sLeft,"%d",left);
      props[1] = sLeft.c_str();
      props[2] = "right-attach";
      UT_String_sprintf(sRight,"%d",right);
      props[3] = sRight.c_str();
      props[4] = "top-attach";
      UT_String_sprintf(sTop,"%d",top);
      props[5] = sTop.c_str();
      props[6] = "bot-attach";
      UT_String_sprintf(sBot,"%d",bot);
      props[7] = sBot.c_str();

//
// Here we trust that the calling routine will do all the begin/end globbing and other
// stuff so that this will go smoothly and undo's in a single step.
// Here we just change
//

      bool bres = m_pDoc->changeStruxFmt(PTC_AddFmt,posCell,posCell,NULL,props,PTX_SectionCell);

      return bres;
}


/*!
 * This method inserts the cell before the coordinates of the cell at (row,col) in the table
 * specified by
 * posTable at the cordinates specified.
 */
bool FV_View::_insertCellBefore(PT_DocPosition posTable, UT_sint32 rowold, UT_sint32 colold,
                                      UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot)
{
      PT_DocPosition posCell = findCellPosAt(posTable,rowold,colold);
      if(posCell == 0)
      {
            return false;
      }
      posCell--;
      const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
      UT_String sLeft,sRight,sTop,sBot;
      props[0] = "left-attach";
      UT_String_sprintf(sLeft,"%d",left);
      props[1] = sLeft.c_str();
      props[2] = "right-attach";
      UT_String_sprintf(sRight,"%d",right);
      props[3] = sRight.c_str();
      props[4] = "top-attach";
      UT_String_sprintf(sTop,"%d",top);
      props[5] = sTop.c_str();
      props[6] = "bot-attach";
      UT_String_sprintf(sBot,"%d",bot);
      props[7] = sBot.c_str();

//
// Here we trust that the calling routine will do all the begin/end globbing and other
// stuff so that this will go smoothly and undo's in a single step.
// Here we just insert
//

      bool bres = m_pDoc->insertStrux(posCell,PTX_SectionCell,NULL,props);
      if(!bres)
      {
            return false;
      }
//
// Insert a block for content
//
      bres = m_pDoc->insertStrux(posCell+1,PTX_Block);
      if(!bres)
      {
            return false;
      }
//
// Insert an endCell
//
      bres = m_pDoc->insertStrux(posCell+1,PTX_EndCell);
      if(!bres)
      {
            return false;
      }
      return bres;
}


/*!
 * This method inserts the cell after the coordinates of the cell at (row,col) in the table
 * specified by
 * posTable at the cordinates specified.
 */
bool FV_View::_insertCellAfter(PT_DocPosition posTable, UT_sint32 rowold, UT_sint32 colold,
                                      UT_sint32 left, UT_sint32 right, UT_sint32 top, UT_sint32 bot)
{
      PL_StruxDocHandle cellSDH,endCellSDH;
      PT_DocPosition posCell = findCellPosAt(posTable,rowold,colold);
      if(posCell == 0)
      {
            return false;
      }
      bool bres = m_pDoc->getStruxOfTypeFromPosition(posCell+1,PTX_SectionCell,&cellSDH);
      if(!bres)
      {
            return false;
      }
      endCellSDH = m_pDoc->getEndCellStruxFromCellSDH(cellSDH);
      if(!endCellSDH)
      {
            return false;
      }
      PT_DocPosition posEndCell = m_pDoc->getStruxPosition(endCellSDH);
      if(posEndCell == 0)
      {
            return false;
      }
      posEndCell++;
      const char * props[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
      UT_String sLeft,sRight,sTop,sBot;
      props[0] = "left-attach";
      UT_String_sprintf(sLeft,"%d",left);
      props[1] = sLeft.c_str();
      props[2] = "right-attach";
      UT_String_sprintf(sRight,"%d",right);
      props[3] = sRight.c_str();
      props[4] = "top-attach";
      UT_String_sprintf(sTop,"%d",top);
      props[5] = sTop.c_str();
      props[6] = "bot-attach";
      UT_String_sprintf(sBot,"%d",bot);
      props[7] = sBot.c_str();

//
// Here we trust that the calling routine will do all the begin/end globbing and other
// stuff so that this will go smoothly and undo's in a single step.
// Here we just insert
//

      bres = m_pDoc->insertStrux(posCell,PTX_SectionCell,NULL,props);
      if(!bres)
      {
            return false;
      }
//
// Insert a block for content
//
      bres = m_pDoc->insertStrux(posCell+1,PTX_Block);
      if(!bres)
      {
            return false;
      }
//
// Insert an endCell
//
      bres = m_pDoc->insertStrux(posCell+1,PTX_EndCell);
      if(!bres)
      {
            return false;
      }
      return bres;
}


PT_DocPosition FV_View::_getDocPos(FV_DocPos dp, bool bKeepLooking)
{
      return _getDocPosFromPoint(getPoint(),dp,bKeepLooking);
}

PT_DocPosition FV_View::_getDocPosFromPoint(PT_DocPosition iPoint, FV_DocPos dp, bool bKeepLooking)
{
      UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
      UT_uint32 iPointHeight;
      bool bDirection;

      PT_DocPosition iPos;

      // this gets called from ctor, so get out quick
      if (dp == FV_DOCPOS_BOD)
      {
            bool bRes = getEditableBounds(false, iPos);
            UT_ASSERT(bRes);

            return iPos;
      }

      // TODO: could cache these to save a lookup if point doesn't change
      fl_BlockLayout* pBlock = NULL;
      fp_Run* pRun = NULL;
      _findPositionCoords(iPoint, m_bPointEOL, xPoint,
                                    yPoint, xPoint2, yPoint2,
                                    iPointHeight, bDirection,
                                    &pBlock, &pRun);

      UT_return_val_if_fail ( pBlock, 0 );
      if ( !pRun )
        return pBlock->getPosition() ;

      fp_Line* pLine = pRun->getLine();
      UT_return_val_if_fail ( pLine, pBlock->getPosition() );     

      // be pessimistic
      iPos = iPoint;

      switch (dp)
      {
      case FV_DOCPOS_BOL:
      {
            fp_Run* pFirstRun = pLine->getFirstRun();

            iPos = pFirstRun->getBlockOffset() + pBlock->getPosition();
      }
      break;

      case FV_DOCPOS_EOL:
      {
            // Ignore forced breaks and EOP when finding EOL.
            fp_Run* pLastRun = pLine->getLastRun();
            while (!pLastRun->isFirstRunOnLine()
                     && (pLastRun->isForcedBreak()
                           || (FPRUN_ENDOFPARAGRAPH == pLastRun->getType())))
            {
                  pLastRun = pLastRun->getPrevRun();
            }

            if (pLastRun->isForcedBreak()
                  || (FPRUN_ENDOFPARAGRAPH == pLastRun->getType()))
            {
                  iPos = pBlock->getPosition() + pLastRun->getBlockOffset();
            }
            else
            {
                  iPos = pBlock->getPosition() + pLastRun->getBlockOffset() + pLastRun->getLength();
            }
      }
      break;

      case FV_DOCPOS_EOD:
      {
            bool bRes = getEditableBounds(true, iPos);
            UT_ASSERT(bRes);
      }
      break;

      case FV_DOCPOS_BOB:
      {
#if 1

// DOM: This used to be an #if 0. I changed it to #if 1
// DOM: because after enabling this code, I can no
// DOM: longer reproduce bug 403 (the bug caused by this
// DOM: code being if 0'd) or bug 92 (the bug that if 0'ing
// DOM: this code supposedly fixes)

// TODO this piece of code attempts to go back
// TODO to the previous block if we are on the
// TODO edge.  this causes bug #92 (double clicking
// TODO on the first line of a paragraph selects
// TODO current paragraph and the previous paragraph).
// TODO i'm not sure why it is here.
// TODO
// TODO it's here because it makes control-up-arrow
// TODO when at the beginning of paragraph work. this
// TODO problem is logged as bug #403.
// TODO
            // are we already there?
            if (bKeepLooking && iPos == pBlock->getPosition())
            {
                  // yep.  is there a prior block?
                  if (!pBlock->getPrevBlockInDocument())
                        break;

                  // yep.  look there instead
                  pBlock = pBlock->getPrevBlockInDocument();
            }
#endif /* 0 */

            iPos = pBlock->getPosition();
      }
      break;

      case FV_DOCPOS_EOB:
      {
            if (pBlock->getNextBlockInDocument())
            {
                  fl_BlockLayout * pPrev = pBlock;
                  // BOB for next block
                  pBlock = pBlock->getNextBlockInDocument();
                  if((pBlock->myContainingLayout()->getContainerType() == FL_CONTAINER_FRAME) && 
                     (pPrev->myContainingLayout()->getContainerType() != FL_CONTAINER_FRAME))
                  {
                        fl_FrameLayout * pFL = static_cast<fl_FrameLayout *>(pBlock->myContainingLayout());
                        iPos = pFL->getPosition(true) -1;
                  }
                  else
                  {
                        iPos = pBlock->getPosition();
                  }
            }
            else
            {
                  // EOD
                  bool bRes = getEditableBounds(true, iPos);
                  UT_ASSERT(bRes);
            }
      }
      break;

      case FV_DOCPOS_BOW:
      {
            UT_GrowBuf pgb(1024);

            bool bRes = pBlock->getBlockBuf(&pgb);
            UT_ASSERT(bRes);

            const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));

            UT_ASSERT(iPos >= pBlock->getPosition());
            UT_uint32 offset = iPos - pBlock->getPosition();
            UT_ASSERT(offset <= pgb.getLength());

            if (offset == 0)
            {
                  if (!bKeepLooking)
                        break;

                  // is there a prior block?
                  pBlock = pBlock->getPrevBlockInDocument();

                  if (!pBlock)
                        break;

                  // yep.  look there instead
                  pgb.truncate(0);
                  bRes = pBlock->getBlockBuf(&pgb);
                  UT_ASSERT(bRes);

                  pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
                  offset = pgb.getLength();

                  if (offset == 0)
                  {
                        iPos = pBlock->getPosition();
                        break;
                  }
            }

            if(!pSpan)
            {
                  // empty block; this should not happen and will trigger one of the asserts
                  // above if(offset == 0)
                  return iPoint;
            }
            
            UT_uint32 iUseOffset = bKeepLooking ? offset-1 : offset;

            bool bInWord = !UT_isWordDelimiter(pSpan[iUseOffset], UCS_UNKPUNK, iUseOffset > 0 ? pSpan[iUseOffset - 1] : UCS_UNKPUNK);

            for (offset--; offset > 0; offset--)
            {
                  if (UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK,pSpan[offset-1]))
                  {
                        if (bInWord)
                              break;
                  }
                  else
                        bInWord = true;
            }

            if ((offset > 0) && (offset < pgb.getLength()))
                  offset++;

            iPos = offset + pBlock->getPosition();
      }
      break;

      case FV_DOCPOS_EOW_MOVE:
      {
            UT_GrowBuf pgb(1024);

            bool bRes = pBlock->getBlockBuf(&pgb);
            UT_ASSERT(bRes);

            const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));

            UT_ASSERT(iPos >= pBlock->getPosition());
            UT_uint32 offset = iPos - pBlock->getPosition();
            UT_ASSERT(offset <= pgb.getLength());

            if (offset == pgb.getLength())
            {
                  if (!bKeepLooking)
                        break;

                  // is there a next block?
                  pBlock = pBlock->getNextBlockInDocument();

                  if (!pBlock)
                        break;

                  // yep.  look there instead
                  pgb.truncate(0);
                  bRes = pBlock->getBlockBuf(&pgb);
                  UT_ASSERT(bRes);

                  pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
                  offset = 0;

                  if (pgb.getLength() == 0)
                  {
                        iPos = pBlock->getPosition();
                        break;
                  }
            }

            if(!pSpan)
            {
                  // empty block; this should not happen and will trigger one of the asserts
                  // above if(offset == 0)
                  return iPoint;
            }

            bool bBetween = UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK, offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK);

            // Needed so ctrl-right arrow will work
            // This is the code that was causing bug 10
            // There is still some weird behavior that should be investigated

            for (; offset < pgb.getLength(); offset++)
            {
                  UT_UCSChar followChar, prevChar;

                  followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
                  prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;

                  if (!UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
                        break;
            }

            for (; offset < pgb.getLength(); offset++)
            {
                  UT_UCSChar followChar, prevChar;

                  followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
                  prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;

                  if (!UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
                  {
                        if (bBetween)
                        {
                              break;
                        }
                  }
                  else if (pSpan[offset] != ' ')
                  {
                        break;
                  }
                  else
                  {
                        bBetween = true;
                  }
            }

            iPos = offset + pBlock->getPosition();
      }
      break;

      case FV_DOCPOS_EOW_SELECT:
      {
            UT_GrowBuf pgb(1024);

            bool bRes = pBlock->getBlockBuf(&pgb);
            UT_ASSERT(bRes);

            const UT_UCSChar* pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));

            UT_ASSERT(iPos >= pBlock->getPosition());
            UT_uint32 offset = iPos - pBlock->getPosition();
            UT_ASSERT(offset <= pgb.getLength());

            if (offset == pgb.getLength())
            {
                  if (!bKeepLooking)
                        break;

                  // is there a next block?
                  pBlock = pBlock->getNextBlockInDocument();

                  if (!pBlock)
                        break;

                  // yep.  look there instead
                  pgb.truncate(0);
                  bRes = pBlock->getBlockBuf(&pgb);
                  UT_ASSERT(bRes);

                  pSpan = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(0));
                  offset = 0;

                  if (pgb.getLength() == 0)
                  {
                        iPos = pBlock->getPosition();
                        break;
                  }
            }

            if(!pSpan)
            {
                  // empty block; this should not happen and will trigger one of the asserts
                  // above if(offset == 0)
                  return iPoint;
            }

            bool bBetween = UT_isWordDelimiter(pSpan[offset], UCS_UNKPUNK, offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK);

            // Needed so ctrl-right arrow will work
            // This is the code that was causing bug 10
            // There is still some weird behavior that should be investigated
            /*
              for (; offset < pgb.getLength(); offset++)
              {
              if (!UT_isWordDelimiter(pSpan[offset]))
              break;
              }
            */
            for (; offset < pgb.getLength(); offset++)
            {
                  UT_UCSChar followChar, prevChar;

                  followChar = ((offset + 1) < pgb.getLength()) ? pSpan[offset+1] : UCS_UNKPUNK;
                  prevChar = offset > 0 ? pSpan[offset - 1] : UCS_UNKPUNK;

                  if (UT_isWordDelimiter(pSpan[offset], followChar, prevChar))
                  {
                        if (bBetween)
                              break;
                  }
                  else if (pSpan[offset] == ' ')
                        break;
                  else
                        bBetween = true;
            }

            iPos = offset + pBlock->getPosition();
      }
      break;


      case FV_DOCPOS_BOP:
      {
            fp_Container* pContainer = pLine->getColumn();
            fp_Page* pPage = pContainer->getPage();

            iPos = pPage->getFirstLastPos(true);
      }
      break;

      case FV_DOCPOS_EOP:
      {
            fp_Container* pContainer = pLine->getColumn();
            fp_Page* pPage = pContainer->getPage();

            iPos = pPage->getFirstLastPos(false);
      }
      break;

      case FV_DOCPOS_BOS:
      case FV_DOCPOS_EOS:
            UT_ASSERT(UT_TODO);
            break;

      default:
            UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
            break;
      }

      return iPos;
}


/*!
  Find block at document position. This version is looks outside the
  header region if we get a null block.
  \param pos Document position
  \return Block at specified posistion, or the first block to the
              rigth of that position. May return NULL.
  \see m_pLayout->findBlockAtPosition
*/
fl_BlockLayout* FV_View::_findBlockAtPosition(PT_DocPosition pos) const
{
      fl_BlockLayout * pBL=NULL;
      if(m_bEditHdrFtr && (m_pEditShadow != NULL) && (m_FrameEdit.getFrameEditMode() == FV_FrameEdit_NOT_ACTIVE))
      {
            pBL = static_cast<fl_BlockLayout *>(m_pEditShadow->findBlockAtPosition(pos));
            if(pBL != NULL)
            {           
                  UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);
                  return pBL;
            }
      }
      pBL = m_pLayout->findBlockAtPosition(pos);
      // This assert makes debugging virtually impossible; this is a hack to make it assert only once ...
      //UT_ASSERT(pBL);
#ifdef DEBUG
      bool bAss = false;
      if(!pBL && !bAss)
      {
            UT_ASSERT_HARMLESS( pBL );
            bAss = true;
      }
#endif
      if(!pBL)
            return NULL;
      UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_BLOCK);

//
// Sevior should remove this after a while..
//
#if(1)
      if(pBL->isHdrFtr())
      {
//            fl_HdrFtrSectionLayout * pSSL = static_cast<fl_HdrFtrSectionLayout *>(pBL->getSectionLayout());
//            if(pSSL && pSSL->getFirstShadow())
//            {
//                  pBL = pSSL->getFirstShadow()->findMatchingBlock(pBL);
                    if(!isLayoutFilling())
                    {
                          UT_DEBUGMSG(("SEVIOR: in view \n"));
                          //UT_ASSERT(0);
                    }
//            }
      }
#endif
      return pBL;
}


void FV_View::_insertSectionBreak(void)
{
      if (!isSelectionEmpty())
      {
            _deleteSelection();
      }
//
// Check and adjust insPoint if it's not valid after the selection delete
//
      bool bLooked = false;
      fl_BlockLayout * pBL= getCurrentBlock();
      while(pBL && pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_DOCSECTION)
      {
            bLooked = true;
            pBL = pBL->getPrevBlockInDocument();
      }
      if(pBL == NULL)
      {
            pBL= getCurrentBlock();
            while(pBL && pBL->myContainingLayout()->getContainerType() != FL_CONTAINER_DOCSECTION)
            {
                  pBL = pBL->getNextBlockInDocument();
            }
      }
      if(bLooked && (pBL != NULL))
      {
            setPoint(pBL->getPosition());
      }
      else if(bLooked)
      {
            setPoint(2); // Start of document 
      }
      //
      // Get preview DocSectionLayout so we know what header/footers we have
            // to insert here.
      //
      fl_DocSectionLayout * pPrevDSL = static_cast<fl_DocSectionLayout *>(getCurrentBlock()->getDocSectionLayout());

      // insert a new paragraph with the same attributes/properties
      // as the previous (or none if the first paragraph in the section).
      // before inserting a section break, we insert a block break
      UT_uint32 iPoint = getPoint();

      m_pDoc->insertStrux(iPoint, PTX_Block);
      m_pDoc->insertStrux(iPoint, PTX_Section);

      _generalUpdate();
      _ensureInsertionPointOnScreen();
      UT_uint32 oldPoint = getPoint();
      fl_DocSectionLayout * pCurDSL = static_cast<fl_DocSectionLayout *>(getCurrentBlock()->getDocSectionLayout());
      //
      // Duplicate previous header/footers for this section.
      //
      UT_GenericVector<fl_HdrFtrSectionLayout *> vecPrevHdrFtr;
      pPrevDSL->getVecOfHdrFtrs( &vecPrevHdrFtr);
      UT_uint32 i =0;
      const XML_Char* block_props[] = {
            "text-align", "left",
            NULL, NULL
      };
      HdrFtrType hfType;
      fl_HdrFtrSectionLayout * pHdrFtrSrc = NULL;
      fl_HdrFtrSectionLayout * pHdrFtrDest = NULL;
      for(i=0; i< vecPrevHdrFtr.getItemCount(); i++)
      {
              pHdrFtrSrc = vecPrevHdrFtr.getNthItem(i);
              hfType = pHdrFtrSrc->getHFType();
              insertHeaderFooter(block_props, hfType, pCurDSL); // cursor is now in the header/footer
              if(hfType == FL_HDRFTR_HEADER)
              {
                    pHdrFtrDest = pCurDSL->getHeader();
              }
              else if(hfType == FL_HDRFTR_FOOTER)
              {
                    pHdrFtrDest = pCurDSL->getFooter();
              }
              else if(hfType == FL_HDRFTR_HEADER_FIRST)
              {
                    pHdrFtrDest = pCurDSL->getHeaderFirst();
              }
              else if( hfType == FL_HDRFTR_HEADER_EVEN)
              {
                    pHdrFtrDest = pCurDSL->getHeaderEven();
              }
              else if( hfType == FL_HDRFTR_HEADER_LAST)
              {
                    pHdrFtrDest = pCurDSL->getHeaderLast();
              }
              else if(hfType == FL_HDRFTR_FOOTER_FIRST)
              {
                    pHdrFtrDest = pCurDSL->getFooterFirst();
              }
              else if( hfType == FL_HDRFTR_FOOTER_EVEN)
              {
                    pHdrFtrDest = pCurDSL->getFooterEven();
              }
              else if( hfType == FL_HDRFTR_FOOTER_LAST)
              {
                    pHdrFtrDest = pCurDSL->getFooterLast();
              }
              _populateThisHdrFtr(pHdrFtrSrc,pHdrFtrDest);
      }

      _setPoint(oldPoint);
      _generalUpdate();

      _ensureInsertionPointOnScreen();
}


/*!
  Move insertion point to previous or next line
  \param bNext True if moving to next line

  This function moves the IP up or down one line, attempting to get as
  close as possible to the prior "sticky" x position.  The notion of
  "next" is strictly physical, not logical.

  For example, instead of always moving from the last line of one
  block to the first line of the next, you might wind up skipping over
  a bunch of blocks to wind up in the first line of the second column.
*/
void FV_View::_moveInsPtNextPrevLine(bool bNext)
{
      UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
      UT_uint32 iPointHeight, iLineHeight;
      bool bDirection;

//
// No need to to do background updates for 1 seconds.
//
      m_pLayout->setSkipUpdates(2);
      UT_sint32 xOldSticky = m_xPointSticky;

      // first, find the line we are on now
      PT_DocPosition iOldPoint = getPoint();

      fl_BlockLayout* pOldBlock;
      fp_Run* pOldRun;
      _findPositionCoords(iOldPoint, m_bPointEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection, &pOldBlock, &pOldRun);
      if(pOldRun == NULL)
      {
            PT_DocPosition posEOD;
            getEditableBounds(true,posEOD);
            if(posEOD <= iOldPoint)
            {
                  setPoint(posEOD);
                  return;
            }
      }
      UT_return_if_fail(pOldRun);

      fl_SectionLayout* pOldSL = pOldBlock->getSectionLayout();
      fp_Line* pOldLine = pOldRun->getLine();
      fp_VerticalContainer* pOldContainer = static_cast<fp_VerticalContainer *>(pOldLine->getContainer());
      fp_Column * pOldColumn = NULL;
      fp_Column * pOldLeader = NULL;
      fp_Page* pOldPage = pOldLine->getPage();
      bool bDocSection = pOldSL->getType() == FL_SECTION_DOC;
      bool bEndNoteSection =  pOldSL->getType() == FL_SECTION_ENDNOTE;
      bool bFootnoteSection = pOldSL->getType() == FL_SECTION_FOOTNOTE;
      bool bCellSection = (pOldSL->getContainerType() == FL_CONTAINER_CELL);



      if(bDocSection || bEndNoteSection || bFootnoteSection || (bCellSection && !isHdrFtrEdit()))
      {
            pOldColumn = static_cast<fp_Column *>(pOldLine->getColumn());
            pOldLeader = (static_cast<fp_Column*>(pOldLine->getColumn()))->getLeader();
      }

      UT_sint32 iPageOffset;
      getPageYOffset(pOldPage, iPageOffset);

      UT_sint32 iLineX = 0;
      UT_sint32 iLineY = 0;

      pOldContainer->getOffsets(static_cast<fp_Container *>(pOldLine), iLineX, iLineY);
      yPoint = iLineY;

      iLineHeight = pOldLine->getHeight();

      bool bNOOP = false;
      bool bEOL = false, bBOL = false;

      xxx_UT_DEBUGMSG(("fv_View::_moveInsPtNextPrevLine: old line 0x%x\n", pOldLine));

      if (bNext)
      {
            if (pOldLine != static_cast<fp_Line *>(pOldContainer->getLastContainer()))
            {
                  UT_sint32 iAfter = m_pG->tlu(1);
                  yPoint += (iLineHeight + iAfter);
            }
            else if (bDocSection)
            {
                  UT_sint32 count = static_cast<UT_sint32>(pOldPage->countColumnLeaders());
                  UT_sint32 i = 0;
                  for(i =0; i < count ;i++)
                  {
                        if( static_cast<fp_Column *>(pOldPage->getNthColumnLeader(i)) == pOldLeader)
                        {
                              break;
                        }
                  }
                  if((i + 1) < count)
                  {
                        // Move to next container
                        yPoint = pOldPage->getNthColumnLeader(i+1)->getY();
                  }
                  else
                  {
                        // move to next page
                        fp_Page* pPage = pOldPage->getNext();
                        if (pPage)
                        {
                              getPageYOffset(pPage, iPageOffset);
                              yPoint = 0;
                        }
                        else
                        {
                              bNOOP = true;
                              if (_getDocPosFromPoint(iOldPoint, FV_DOCPOS_EOL) != iOldPoint)
                                bEOL = true;
                        }
                  }
            }
            else if(bCellSection)
            {
                  UT_sint32 iAfter = m_pG->tlu(1);
                  yPoint += (iLineHeight + iAfter);
            }
            else if(bEndNoteSection)
            {
                  UT_sint32 iAfter = m_pG->tlu(1);
                  yPoint += (iLineHeight + iAfter);
                  if(pOldPage->getBottom() < yPoint)
                  {
                        // move to next page
                        fp_Page* pPage = pOldPage->getNext();
                        if (pPage)
                        {
                              getPageYOffset(pPage, iPageOffset);
                              yPoint = 0;
                        }
                  }
            }
            else if(bFootnoteSection)
            {
                  UT_sint32 iAfter = m_pG->tlu(1);
                  yPoint += (iLineHeight + iAfter);
                  if(pOldPage->getBottom() < yPoint)
                  {
                        // move to next page
                        fp_Page* pPage = pOldPage->getNext();
                        if (pPage)
                        {
                              xxx_UT_DEBUGMSG(("Move to next page old IpageOffset %d ypoint %d \n",iPageOffset,yPoint));
                              getPageYOffset(pPage, iPageOffset);
                              xxx_UT_DEBUGMSG(("Move to next page new IpageOffset %d \n",iPageOffset));
                              yPoint = 0;
                        }
                  }
            }
            else
            {
                  bNOOP = true;
            }
      }
      else
      {
            if (pOldLine != static_cast<fp_Line *>(pOldContainer->getFirstContainer()))
            {
                  // just move off this line
                  yPoint -= (pOldLine->getMarginBefore() + 1);
            }
            else if (bDocSection)
            {
                  UT_sint32 count = static_cast<UT_sint32>(pOldPage->countColumnLeaders());
                  UT_sint32 i = 0;
                  for(i =0; i < count ;i++)
                  {
                        if( static_cast<fp_Column *>(pOldPage->getNthColumnLeader(i)) == pOldLeader)
                        {
                              break;
                        }
                  }
                  if( (i> 0) && (i < count))
                  {
                        // Move to prev container
                        yPoint = pOldPage->getNthColumnLeader(i-1)->getLastContainer()->getY();
                        yPoint +=  pOldPage->getNthColumnLeader(i-1)->getY()+2;
                  }
                  else
                  {
                        // move to prev page
                        fp_Page* pPage = pOldPage->getPrev();
                        if (pPage)
                        {
                              getPageYOffset(pPage, iPageOffset);
                              yPoint = pPage->getBottom();
                              if(getViewMode() != VIEW_PRINT)
                              {
                                    fl_DocSectionLayout * pDSL = pPage->getOwningSection();
                                    yPoint = yPoint - pDSL->getTopMargin() -2;
                              }
                        }
                        else
                        {
                              bNOOP = true;
                              if (_getDocPosFromPoint(iOldPoint, FV_DOCPOS_BOL) != iOldPoint)
                                bBOL = true;
                        }
                  }
            }
            else if(bCellSection)
            {
                  UT_sint32 iAfter = m_pG->tlu(2);
                  yPoint -= iAfter;
                  if(yPoint < 0)
                  {
                        // move to prev page
                        fp_Page* pPage = pOldPage->getPrev();
                        if (pPage)
                        {
                              getPageYOffset(pPage, iPageOffset);
                              yPoint = pPage->getBottom();
                              if(getViewMode() != VIEW_PRINT)
                              {
                                    fl_DocSectionLayout * pDSL = pPage->getOwningSection();
                                    yPoint = yPoint - pDSL->getTopMargin() -2;
                              }
                        }
                  }

            }
            else if(bEndNoteSection || bFootnoteSection)
            {
                  UT_sint32 iAfter = m_pG->tlu(2);
                  yPoint -= iAfter;

                  // change to screen coordinates
                  UT_sint32 xP = m_xPointSticky - m_xScrollOffset + getPageViewLeftMargin();
                  UT_sint32 yP = yPoint + iPageOffset - m_yScrollOffset;
                  
                  // hit-test to figure out where that puts us
                  UT_sint32 xC, yC;

                  PT_DocPosition iNewP;
                  fp_Page* pPage = _getPageForXY(xP, yP, xC, yC);
                  bool isTOC = false;
                  pPage->mapXYToPosition(xC, yC, iNewP, bBOL, bEOL,isTOC);
                  UT_sint32 ii =0;
                  while((iNewP == iOldPoint) && (ii < 100) && (yPoint > 0))
                  {
                        yPoint -= iAfter;
                        yP = yPoint + iPageOffset - m_yScrollOffset;
                        pPage = _getPageForXY(xP, yP, xC, yC);
                        pPage->mapXYToPosition(xC, yC, iNewP, bBOL, bEOL,isTOC);
                        ii++;
                  }
                  if(yPoint < 0)
                  {
                        // move to prev page
                        fp_Page* pPage = pOldPage->getPrev();
                        if (pPage)
                        {
                              getPageYOffset(pPage, iPageOffset);
                              yPoint = pPage->getBottom();
                              if(getViewMode() != VIEW_PRINT)
                              {
                                    fl_DocSectionLayout * pDSL = pPage->getOwningSection();
                                    yPoint = yPoint - pDSL->getTopMargin() -2;
                              }
                        }
                  }
            }
            else
            {
                  bNOOP = true;
            }
      }

      if (bNOOP)
      {
            if (bBOL)
                  moveInsPtTo(FV_DOCPOS_BOL, false);
            else if (bEOL)
                  moveInsPtTo(FV_DOCPOS_EOL, false);
            // cannot move.  should we beep?
            return;
      }

      // change to screen coordinates
      xPoint = m_xPointSticky - m_xScrollOffset + getPageViewLeftMargin();
      yPoint += iPageOffset - m_yScrollOffset;

      // hit-test to figure out where that puts us
      UT_sint32 xClick, yClick;
      fp_Page* pPage = _getPageForXY(xPoint, yPoint, xClick, yClick);

      PT_DocPosition iNewPoint = 0;
      bBOL = false;
      bEOL = false;
      fl_HdrFtrShadow * pShadow=NULL;
//
// If we're not in a Header/Footer we can't get off the page with the click
// version of mapXYToPosition
//
      bool isTOC = false;
      if(isHdrFtrEdit())
      {
            bool bGotIt = false;
            UT_sint32 iLoop = 0;
            while(!bGotIt && (iLoop < 50))
            {
                  pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC, true, &pShadow);
                  if(iNewPoint != iOldPoint)
                  {
                        bGotIt = true;
                  }
                  else
                  {
                        iLoop++;
                        if(bNext)
                        {
                              yClick += m_pG->tlu(1);
                        }
                        else
                        {
                              yClick -= m_pG->tlu(1);
                        }
                  }
            }
      }
      else
      {
            pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC);
            if (bNext)
            {
                  int delta = iLineHeight;
//
// Whoops! foornotes/endnotes can screw this up since they placed on the page
// out of order to their location in the PieceTable
//
                  while (iNewPoint <= getPoint() && (getCurrentPage() == pPage) )
                  {
                        if (yClick+delta > pPage->getHeight())
                        {
                              delta -= pPage->getHeight();
                              pPage = pPage->getNext();
                        }
                        if(pPage == NULL)
                        {
                              return;
                        }
                        pPage->mapXYToPosition(xClick, yClick+delta, 
                                                         iNewPoint, bBOL, bEOL,isTOC);
                        delta += iLineHeight;
                  }
            }
            else
            {
                  int delta = iLineHeight;
//
// Whoops! foornotes/endnotes can screw this up since they placed on the page
// out of order to their location in the PieceTable
//
                  while (iNewPoint >= getPoint() && (getCurrentPage() == pPage) )
                  {
                        if (yClick-delta < 0)
                        {
                              delta += pPage->getHeight();
                              pPage = pPage->getPrev();
                        }
                        if(pPage == NULL)
                        {
                              return;
                        }
                        pPage->mapXYToPosition(xClick, yClick-delta, 
                                                         iNewPoint, bBOL, bEOL,isTOC);
                        delta += iLineHeight;
                  }
            }

            while(pPage && (iNewPoint == iOldPoint) && (yClick < m_pLayout->getHeight()) && (yClick > 0))
            {
                  if (bNext)
                        yClick += iLineHeight/2;
                  else
                        yClick -= m_pG->tlu(2);

                  if(yClick > pPage->getHeight())
                  {
                        pPage = pPage->getNext();
                        if (!pPage) break;
                        yClick -= pPage->getHeight();
                  }

                  if(yClick < 0)
                  {
                        pPage = pPage->getPrev();
                        if (!pPage) break;
                        yClick += pPage->getHeight();
                  }

                  if(pPage)
                        pPage->mapXYToPosition(xClick, yClick, iNewPoint, bBOL, bEOL,isTOC);
            }
            xxx_UT_DEBUGMSG((" pPage %x iNewPoint %d iOldPoint %d yClick %d m_pLayout->getHeight() \n",pPage,iNewPoint,yClick,m_pLayout->getHeight()));
      }
//
// Check we're not moving out of allowed region.
//
      PT_DocPosition posBOD,posEOD;
      getEditableBounds(false,posBOD);
      getEditableBounds(true,posEOD);

      xxx_UT_DEBUGMSG(("iNewPoint=%d, iOldPoint=%d, xClick=%d, yClick=%d\n",iNewPoint, iOldPoint, xClick, yClick));
      UT_ASSERT(iNewPoint != iOldPoint);
      if((iNewPoint >= posBOD) && (iNewPoint <= posEOD))
      {
            _setPoint(iNewPoint, bEOL);
      }

      _ensureInsertionPointOnScreen();

      // this is the only place where we override changes to m_xPointSticky
      m_xPointSticky = xOldSticky;
}

/*! Scrolls the screen to make sure that the IP is on-screen.
 * \return true iff scrolling took place
 * Q: should this get called if there is a selection?  Does it do
 * harm?  It may, because point may be the beginning of the selection.
 */
bool FV_View::_ensureInsertionPointOnScreen()
{
      xxx_UT_DEBUGMSG(("FV_View::_ensureInsertionPointOnScreen called\n"));

      // Some short circuit tests to avoid doing bad things.
      if (getWindowHeight() <= 0)
            return false;

    // If == 0 no layout information is present. Don't scroll.
      if(getPoint() == 0)
            return false;

      xxx_UT_DEBUGMSG(("_ensure: [xp %ld][yp %ld][ph %ld] [w %ld][h %ld]\n",m_xPoint,m_yPoint,m_iPointHeight,getWindowWidth(),getWindowHeight()));

      bool bRet = false;
      if (m_yPoint < 0)
      {
            cmdScroll(AV_SCROLLCMD_LINEUP, (UT_uint32) (-(m_yPoint)));
            bRet = true;
      }
      else if ((static_cast<UT_uint32>(m_yPoint + m_iPointHeight)) >= (static_cast<UT_uint32>(getWindowHeight())))
      {
            cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(m_yPoint + m_iPointHeight - getWindowHeight()));
            bRet = true;
      }

      /*
        TODO: we really ought to try to do better than this.
      */
      if (m_xPoint < 0)
      {
            cmdScroll(AV_SCROLLCMD_LINELEFT, (UT_uint32) (-(m_xPoint) + getPageViewLeftMargin()/2));
            bRet = true;
      }
      else if ((static_cast<UT_uint32>(m_xPoint)) >= (static_cast<UT_uint32>(getWindowWidth())))
      {
            cmdScroll(AV_SCROLLCMD_LINERIGHT, static_cast<UT_uint32>(m_xPoint - getWindowWidth() + getPageViewLeftMargin()/2));
            bRet = true;
      }

      // This used to say 'if !bRet', but I think it's cleaner to always fix,
      // if possibly slower.  If we scroll, perhaps the scroll has already
      // fixed, and we end up fixing twice.
      _fixInsertionPointCoords();

      return bRet;
}

/* It's unclear to me why this is used rather than ensure. -PL */
void FV_View::_updateInsertionPoint()
{
      if (isSelectionEmpty())
      {
            _ensureInsertionPointOnScreen();
      }
}

void FV_View::_moveInsPtNextPrevPage(bool bNext)
{
#if 0
      UT_sint32 xPoint;
      UT_sint32 yPoint;
      UT_sint32 iPointHeight;
#endif

      fp_Page* pOldPage = _getCurrentPage();

      // TODO when moving to the prev page, we should move to its end, not begining
      // try to locate next/prev page
      fp_Page* pPage = (bNext ? pOldPage->getNext() : pOldPage->getPrev());

      // if couldn't move, go to top of this page if we are looking for the previous page
      // or the end of this page if we are looking for the next page
      if (!pPage)
      {
            if(!bNext)
            {
                  pPage = pOldPage;
            }
            else
            {
                  moveInsPtTo(FV_DOCPOS_EOD,false);
                  return;
            }
      }

      _moveInsPtToPage(pPage);
}

void FV_View::_moveInsPtNextPrevScreen(bool bMovingDown)
{
      fl_BlockLayout * pBlock;
      fp_Run * pRun;
      UT_sint32 x,y,x2,y2;
      UT_uint32 iHeight;
      bool bDirection;
      bool bBOL,bEOL,isTOC;
      UT_sint32 iYnext,iYscroll;
      PT_DocPosition iNewPoint;
      _findPositionCoords(getPoint(),false,x,y,x2,y2,iHeight,bDirection,&pBlock,&pRun);
      if(!pRun)
            return;

      fp_Line * pLine = pRun->getLine();
      UT_return_if_fail(pLine);
      fp_Page * pPage = pLine->getPage();
      UT_return_if_fail(pPage);
      if(isHdrFtrEdit())
      {
            clearHdrFtrEdit();
            warpInsPtToXY(0,0,false);
      }
      UT_sint32 xoff,yoff;

    // get Screen coordinates of the top of the page and add the y location to this.
      getPageScreenOffsets(pPage, xoff,yoff);
      yoff = y - yoff;

      UT_sint32 iDir = bMovingDown ? 1 : -1;

      iYnext = yoff + getWindowHeight() * iDir;
      iYscroll = m_yScrollOffset + (getWindowHeight() * iDir);
      if (iYscroll < 0)
      {
            // We're trying to scroll past beginning/end of document
            // Move insertion pointer to BOD/EOD instead
            if (iDir == 1)
            {
                  moveInsPtTo(FV_DOCPOS_EOD);
            }
            else
            {
                  moveInsPtTo(FV_DOCPOS_BOD);
            }
            return;
      }

      xxx_UT_DEBUGMSG(("SEVIOR:!!!!!! Yoff %d iYnext %d page %x \n",yoff,iYnext,pPage));

      while (pPage && (bMovingDown ? (iYnext > pPage->getHeight())
                                           : (iYnext < 0)))
      {
            iYnext -= (pPage->getHeight() + getPageViewSep()) * iDir;
            pPage = bMovingDown ? pPage->getNext() : pPage->getPrev();
      }

      xxx_UT_DEBUGMSG(("SEVIOR:!!!!!! Set to iYnext %d page %x \n",iYnext,pPage));

      if (pPage == NULL) pPage = pLine->getPage ();
      if (iYnext < 0) iYnext = 0;
      // convert the iYnext back into a point position, namely iNewPoint.
      pPage->mapXYToPosition(x, iYnext, iNewPoint, bBOL, bEOL,isTOC);
      if (bMovingDown)
      {
            int delta = pPage->getHeight()/4;
            while (iNewPoint <= getPoint() && pPage)
            {
                  if (iYnext+delta > pPage->getHeight())
                  {
                        delta -= pPage->getHeight();
                        pPage = pPage->getNext();
                  }
                  if (pPage)
                  {
                        pPage->mapXYToPosition(x, iYnext+delta, 
                                                         iNewPoint, bBOL, bEOL,isTOC);
                        delta += pPage->getHeight()/4;
                  }
            }
      }
      else
      {
            int delta = pPage->getHeight()/4;
            while (iNewPoint >= getPoint() && pPage)
            {
                  if (iYnext+delta < 0)
                  {
                        delta += pPage->getHeight();
                        pPage = pPage->getPrev();
                  }
                  if (pPage)
                  {
                        pPage->mapXYToPosition(x, iYnext-delta, 
                                                         iNewPoint, bBOL, bEOL,isTOC);
                        delta += pPage->getHeight()/4;
                  }
            }
      }

      UT_sint32 newX,newY;
      UT_uint32 newHeight;
      
      _findPositionCoords(iNewPoint,false,newX,newY,x2,y2,newHeight,bDirection,&pBlock,&pRun);
      if(!pRun)
      {
            _moveInsPtNextPrevLine(bMovingDown);
            return;
      }

      fp_Line * pNewLine = static_cast<fp_Line *>(pRun->getLine());

      if(pNewLine == NULL ||
         (pNewLine->getContainer() == pLine->getContainer()) && 
         (bMovingDown ? (pNewLine->getY() < pLine->getY())
                        : (pNewLine->getY() > pLine->getY())))
      {
            _moveInsPtNextPrevLine(bMovingDown);
            return;
      }

    // Couldn't advance! Try scanning x across the page at this new iYnext.
      if(pLine == pNewLine && pPage)
      {
            UT_sint32 step = pPage->getWidth()/20 + 1;

            for (x=0; x < pPage->getWidth(); x += step)
            {
                  pPage->mapXYToPosition(x, iYnext, iNewPoint, bBOL, bEOL,isTOC);
                  _findPositionCoords(iNewPoint,false,newX,newY,x2,y2,newHeight,bDirection,&pBlock,&pRun);
                  pNewLine = static_cast<fp_Line *>(pRun->getLine());
                  if(pLine != pNewLine)
                        break;
            }

            if (pLine == pNewLine)
            {
                  _moveInsPtNextPrevLine(bMovingDown);
                  return;
            }
      }

      _setPoint(iNewPoint);
      sendVerticalScrollEvent(iYscroll);
      if (!_ensureInsertionPointOnScreen())
      {
            _fixInsertionPointCoords();
      }
}


fp_Page *FV_View::_getCurrentPage(void)
{
      UT_sint32 xPoint, yPoint, xPoint2, yPoint2;
      UT_uint32 iPointHeight;
      bool bDirection;
      /*
        This function moves the IP to the beginning of the previous or
        next page (ie not this one).
      */

      // first, find the page we are on now
      UT_uint32 iOldPoint = getPoint();

      fl_BlockLayout* pOldBlock;
      fp_Run* pOldRun;
      _findPositionCoords(iOldPoint, m_bPointEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection, &pOldBlock, &pOldRun);
      fp_Line* pOldLine = pOldRun->getLine();
      fp_Page* pOldPage = pOldLine->getPage();
      return pOldPage;
}

void FV_View::_moveInsPtNthPage(UT_uint32 n)
{
      fp_Page *page = m_pLayout->getFirstPage();

      if (n > m_pLayout->countPages ())
            n = m_pLayout->countPages ();

      for (UT_uint32 i = 1; i < n; i++)
      {
            page = page->getNext ();
      }

      _moveInsPtToPage(page);
}

void FV_View::_moveInsPtToPage(fp_Page *page)
{
      // move to the first pos on this page
      PT_DocPosition iNewPoint = page->getFirstLastPos(true);
      _setPoint(iNewPoint, false);

      // explicit vertical scroll to top of page
      UT_sint32 iPageOffset;
      getPageYOffset(page, iPageOffset);

      iPageOffset -= getPageViewSep() /2;
      iPageOffset -= m_yScrollOffset;

      bool bVScroll = false;
      if (iPageOffset < 0)
      {
            cmdScroll(AV_SCROLLCMD_LINEUP, static_cast<UT_uint32>(-iPageOffset));
            bVScroll = true;
      }
      else if (iPageOffset > 0)
      {
            cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(iPageOffset));
            bVScroll = true;
      }

      // also allow implicit horizontal scroll, if needed
      if (!_ensureInsertionPointOnScreen() && !bVScroll)
      {
            _fixInsertionPointCoords();
      }
}

void FV_View::_autoScroll(UT_Worker * pWorker)
{
      UT_ASSERT(pWorker);

      // this is a static callback method and does not have a 'this' pointer.

      FV_View * pView = static_cast<FV_View *>(pWorker->getInstanceData());
      UT_ASSERT(pView);

      if(pView->getLayout()->getDocument()->isPieceTableChanging())
      {
            return;
      }

      PT_DocPosition iOldPoint = pView->getPoint();
      UT_DEBUGMSG(("Doing autoscroll \n"));
      /*
        NOTE: We update the selection here, so that the timer can keep
        triggering autoscrolls even if the mouse doesn't move.
      */
      pView->extSelToXY(pView->m_xLastMouse, pView->m_yLastMouse, false);

      if (pView->getPoint() != iOldPoint)
      {
            // do the autoscroll
            pView->_ensureInsertionPointOnScreen();
      }
      else
      {
            // not far enough to change the selection ... do we still need to scroll?
            UT_sint32 xPos = pView->m_xLastMouse;
            UT_sint32 yPos = pView->m_yLastMouse;

            // TODO: clamp xPos, yPos to viewable area??

            bool bOnScreen = true;

            if ((xPos < 0 || xPos > pView->getWindowWidth()) ||
                  (yPos < 0 || yPos > pView->getWindowHeight()))
                  bOnScreen = false;

            if (!bOnScreen)
            {
                  // yep, do it manually

                  // TODO currently we blindly send these auto scroll events without regard
                  // TODO to whether the window can scroll any further in that direction.
                  // TODO we could optimize this a bit and check the scroll range before we
                  // TODO fire them, but that knowledge is only stored in the frame and we
                  // TODO don't have a backpointer to it.
                  // UT_DEBUGMSG(("_auto: [xp %ld][yp %ld] [w %ld][h %ld]\n",
                  //                 xPos,yPos,pView->getWindowWidth(),pView->getWindowHeight()));
                  //
                  // Sevior: Is This what you wanted? Uncomment these lines when
                  // needed.
                  //
                  //XAP_Frame * pFrame = static_cast<XAP_Frame *>(pView->getParentData());
                  //UT_ASSERT((pFrame));

                  if (yPos < 0)
                  {
                        pView->cmdScroll(AV_SCROLLCMD_LINEUP, (UT_uint32) (-(yPos)));
                  }
                  else if ((static_cast<UT_uint32>(yPos)) >= (static_cast<UT_uint32>(pView->getWindowHeight())))
                  {
                        pView->cmdScroll(AV_SCROLLCMD_LINEDOWN, static_cast<UT_uint32>(yPos - pView->getWindowHeight()));
                  }

                  if (xPos < 0)
                  {
                        pView->cmdScroll(AV_SCROLLCMD_LINELEFT, (UT_uint32) (-(xPos)));
                  }
                  else if ((static_cast<UT_uint32>(xPos)) >= (static_cast<UT_uint32>(pView->getWindowWidth())))
                  {
                        pView->cmdScroll(AV_SCROLLCMD_LINERIGHT, static_cast<UT_uint32>(xPos - pView->getWindowWidth()));
                  }
            }
      }
}


fp_Page* FV_View::_getPageForXY(UT_sint32 xPos, UT_sint32 yPos, UT_sint32& xClick, UT_sint32& yClick) const
{
      xClick = xPos + m_xScrollOffset - getPageViewLeftMargin();
      yClick = yPos + m_yScrollOffset - getPageViewTopMargin();
      fp_Page* pPage = m_pLayout->getFirstPage();
      while (pPage)
      {
            UT_sint32 iPageHeight = pPage->getHeight();
            if(getViewMode() != VIEW_PRINT)
            {
                  iPageHeight = iPageHeight - pPage->getOwningSection()->getTopMargin() -
                        pPage->getOwningSection()->getBottomMargin();
            }
            if (yClick < iPageHeight)
            {
                  // found it
                  break;
            }
            else
            {
                  yClick -= iPageHeight + getPageViewSep();
            }
            pPage = pPage->getNext();
      }

      if (!pPage)
      {
            // we're below the last page
            pPage = m_pLayout->getLastPage();

            UT_sint32 iPageHeight = pPage->getHeight();
            yClick += iPageHeight + getPageViewSep();
      }

      return pPage;
}

/*!
 Compute prefix function for search
 \param pFind String to find
 \param bMatchCase True to match case, false to ignore case
*/
UT_uint32*
FV_View::_computeFindPrefix(const UT_UCSChar* pFind)
{
      UT_uint32 m = UT_UCS4_strlen(pFind);
      UT_uint32 k = 0, q = 1;
      UT_uint32 *pPrefix = (UT_uint32*) UT_calloc(m, sizeof(UT_uint32));
      UT_ASSERT(pPrefix);

      pPrefix[0] = 0; // Must be this regardless of the string

      if (m_bMatchCase)
      {
            for (q = 1; q < m; q++)
            {
                  while (k > 0 && pFind[k] != pFind[q])
                        k = pPrefix[k - 1];
                  if(pFind[k] == pFind[q])
                        k++;
                  pPrefix[q] = k;
            }
      }
      else
      {
            for (q = 1; q < m; q++)
            {
                  while (k > 0
                           && UT_UCS4_tolower(pFind[k]) != UT_UCS4_tolower(pFind[q]))
                        k = pPrefix[k - 1];
                  if(UT_UCS4_tolower(pFind[k]) == UT_UCS4_tolower(pFind[q]))
                        k++;
                  pPrefix[q] = k;
            }
      }

      return pPrefix;
}

/*!
 Find next occurrence of string
 \param pFind String to find
 \param True to match case, false to ignore case
 \result bDoneEntireDocument True if entire document searched,
             false otherwise
 \return True if string was found, false otherwise

 \fixme The conversion of UCS_RQUOTE should happen in some generic
            function - it is presently done lot's of places in the code.
*/
bool
FV_View::_findNext(UT_uint32* pPrefix,
                           bool& bDoneEntireDocument)
{
      UT_ASSERT(m_sFind);

      fl_BlockLayout* block = _findGetCurrentBlock();
      PT_DocPosition offset = _findGetCurrentOffset();
      UT_UCSChar* buffer = NULL;
      UT_uint32 m = UT_UCS4_strlen(m_sFind);

      // Clone the search string, converting it to lowercase is search
      // should ignore case.
      UT_UCSChar* pFindStr = (UT_UCSChar*) UT_calloc(m, sizeof(UT_UCSChar));
      UT_ASSERT(pFindStr);
      if (!pFindStr)
            return false;
      UT_uint32 j;
      if (m_bMatchCase)
      {
            for (j = 0; j < m; j++)
                  pFindStr[j] = m_sFind[j];
      }
      else
      {
            for (j = 0; j < m; j++)
                  pFindStr[j] = UT_UCS4_tolower(m_sFind[j]);
      }

      // Now we use the prefix function (stored as an array) to search
      // through the document text.
      while ((buffer = _findGetNextBlockBuffer(&block, &offset)))
      {
            UT_sint32 foundAt = -1;
            UT_uint32 i = 0, t = 0;

            UT_UCSChar currentChar;
      
            while ((currentChar = buffer[i]) /*|| foundAt == -1*/)
            {
                  // Convert smart quote apostrophe to ASCII single quote to
                  // match seach input
                  if (currentChar == UCS_RQUOTE) currentChar = '\'';
                  if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
                  
                  while (t > 0 && pFindStr[t] != currentChar)
                        t = pPrefix[t-1];
                  if (pFindStr[t] == currentChar)
                        t++;
                  i++;
                  if (t == m)
                  {
                        if (m_bWholeWord) 
                        {
                              bool start = UT_isWordDelimiter(buffer[i-m-1], UCS_UNKPUNK, UCS_UNKPUNK);
                              bool end = UT_isWordDelimiter(buffer[i], UCS_UNKPUNK, UCS_UNKPUNK);
                              if (start && end) 
                              {
                                    foundAt = i - m;
                                    break;
                              }
                        }
                        else 
                        {
                              foundAt = i - m;
                              break;
                        }
                  }
            }

            // Select region of matching string if found
            if (foundAt != -1)
            {
                  _setPoint(block->getPosition(false) + offset + foundAt);
                  _setSelectionAnchor();
                  _charMotion(true, m);

                  m_doneFind = true;

                  FREEP(pFindStr);
                  FREEP(buffer);
                  return true;
            }
            // Didn't find anything, so set the offset to the end of the
            // current area
            offset += UT_MAX(UT_UCS4_strlen(buffer),1);

            // Must clean up buffer returned for search
            FREEP(buffer);
      }

      bDoneEntireDocument = true;

      // Reset wrap for next time
      m_wrappedEnd = false;

      FREEP(pFindStr);

      return false;
}

bool
FV_View::_findPrev(UT_uint32* pPrefix,
                           bool& bDoneEntireDocument)
{
      UT_ASSERT(m_sFind);

      fl_BlockLayout* block = _findGetCurrentBlock();
      PT_DocPosition offset = _findGetCurrentOffset();
      UT_UCSChar* buffer = NULL;
      UT_uint32 m = UT_UCS4_strlen(m_sFind);

      // Clone the search string, converting it to lowercase is search
      // should ignore case.
      UT_UCSChar* pFindStr = (UT_UCSChar*) UT_calloc(m, sizeof(UT_UCSChar));
      UT_ASSERT(pFindStr);
      if (!pFindStr)
            return false;
      UT_uint32 j;
      if (m_bMatchCase)
      {
            for (j = 0; j < m; j++)
                  pFindStr[j] = m_sFind[j];
      }
      else
      {
            for (j = 0; j < m; j++)
                  pFindStr[j] = UT_UCS4_tolower(m_sFind[j]);
      }

      // Now we use the prefix function (stored as an array) to search
      // through the document text.
      while ((buffer = _findGetPrevBlockBuffer(&block, &offset)))
      {
            UT_sint32 foundAt = -1;
            UT_uint32 i = UT_MIN(offset, UT_UCS4_strlen(buffer));
            if (i > m) 
            {
                  i-=m;
            }
            else 
            {
                  if (i==0)
                        i = UT_UCS4_strlen(buffer);
                  else
                        i=0;
            }
                  
            UT_uint32 t = 0;
            UT_UCSChar currentChar;

            while ((i > 0 ))
            {
                  t = 0;
                  currentChar = buffer[i];
                  if (currentChar == UCS_RQUOTE) currentChar = '\'';
                  if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
                  while ((m_sFind[t] == currentChar)&& ( t <= m))
                  {
                        t++;
                        currentChar = buffer[i + t];
                        if (currentChar == UCS_RQUOTE) currentChar = '\'';
                        if (!m_bMatchCase) currentChar = UT_UCS4_tolower(currentChar);
                  }     
                        
                  if (t == m) {
                        if (m_bWholeWord)
                        {
                              bool start = UT_isWordDelimiter(buffer[i-1], UCS_UNKPUNK, UCS_UNKPUNK);
                              bool end = UT_isWordDelimiter(buffer[i+m], UCS_UNKPUNK, UCS_UNKPUNK);
                              if (start && end)
                              {
                                    foundAt = i;
                                    break;
                              }
                        }
                        else
                        {
                              foundAt = i;
                              break;
                        }
                  }
            
                  i--;
            }

            // Select region of matching string if found
            if (foundAt > 0)
            {

                  UT_DEBUGMSG(("Found pos: %d", (block)->getPosition(false)+ foundAt));
                  UT_DEBUGMSG((" - len: %d\n", m));
                  
                  _setPoint(block->getPosition(false) + foundAt + m);
                  _setSelectionAnchor();
                  _charMotion(false, m);

                  m_doneFind = true;

                  FREEP(pFindStr);
                  FREEP(buffer);
                  return true;
            }

            // Didn't find anything, so set the offset to the start of the
            // current area (0)
            offset = 0;

            // Must clean up buffer returned for search
            FREEP(buffer);
      }

      bDoneEntireDocument = true;

      // Reset wrap for next time
      m_wrappedEnd = false;

      FREEP(pFindStr);

      return false;
}
PT_DocPosition
FV_View::_BlockOffsetToPos(fl_BlockLayout * block, PT_DocPosition offset)
{
      UT_ASSERT(block);
      return block->getPosition(false) + offset;
}

UT_UCSChar*
FV_View::_findGetNextBlockBuffer(fl_BlockLayout** pBlock,
                                                 PT_DocPosition* pOffset)
{
      UT_ASSERT(m_pLayout);

      // This assert doesn't work, since the startPosition CAN
      // legitimately be zero
      // The beginning of the first block in any document
      UT_ASSERT(m_startPosition >= 2);

      UT_ASSERT(pBlock);
      UT_ASSERT(*pBlock);

      UT_ASSERT(pOffset);

      fl_BlockLayout* newBlock = NULL;
      PT_DocPosition newOffset = 0;

      UT_uint32 bufferLength = 0;

      UT_GrowBuf pBuffer;

      // Check early for completion, from where we left off last, and
      // bail if we are now at or past the start position
      if (m_wrappedEnd
            && _BlockOffsetToPos(*pBlock, *pOffset) >= m_startPosition)
      {
            // We're done
            return NULL;
      }

      if (!(*pBlock)->getBlockBuf(&pBuffer))
      {
            UT_DEBUGMSG(("Block %p has no associated buffer.\n", *pBlock));
            UT_ASSERT(0);
      }

      // Have we already searched all the text in this buffer?
      if (*pOffset >= pBuffer.getLength())
      {
            bool bNeedNewBlock = true;
            
            // if pBlock was inside some kind of an embeded section, we need to make sure we
            // have finished searching the enclosing block
            if((*pBlock)->isEmbeddedType())
            {
                  
                  fl_ContainerLayout * pCL = (*pBlock)->myContainingLayout();
                  UT_ASSERT((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) || (pCL->getContainerType() == FL_CONTAINER_ENDNOTE) );
                  fl_EmbedLayout * pFL = static_cast<fl_EmbedLayout *>(pCL);
                  if(pFL->isEndFootnoteIn())
                  {
                        PL_StruxDocHandle sdhStart = pCL->getStruxDocHandle();
                        PL_StruxDocHandle sdhEnd = NULL;
                        if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
                        {
                              getDocument()->getNextStruxOfType(sdhStart,PTX_EndFootnote, &sdhEnd);
                        }
                        else
                        {
                              getDocument()->getNextStruxOfType(sdhStart,PTX_EndEndnote, &sdhEnd);
                        }

                        if(sdhEnd)
                        {
                              PT_DocPosition posStart = getDocument()->getStruxPosition(sdhStart);
                              PT_DocPosition posEnd = getDocument()->getStruxPosition(sdhEnd);
                              UT_uint32 iSize = posEnd - posStart + 1;
                              PL_StruxFmtHandle  psfh = NULL;
                              getDocument()->getStruxOfTypeFromPosition((*pBlock)->getDocLayout()->getLID(),posStart,PTX_Block, &psfh);
                              newBlock = reinterpret_cast<fl_BlockLayout *>(const_cast<void *>(psfh));

                              PT_DocPosition iPos = _BlockOffsetToPos(*pBlock, *pOffset);
                              PT_DocPosition iEncBlockPos = newBlock->getPosition(false);

                              newOffset = iPos - iEncBlockPos;

                              pBuffer.truncate(0);
                              
                              if (!newBlock->getBlockBuf(&pBuffer))
                              {
                                    UT_DEBUGMSG(("Block %p (a ->next block) has no buffer.\n",
                                                       newBlock));
                                    UT_ASSERT(0);
                              }

                              if(pBuffer.getLength() > newOffset)
                              {
                                    // still stuff left in our containing block
                                    bNeedNewBlock = false;
                              }
                              
                        }
                  }
            }

            if(bNeedNewBlock)
            {
                  // Then return a fresh new block's buffer
                  newBlock = (*pBlock)->getNextBlockInDocument();

                  // Are we at the end of the document?
                  if (!newBlock)
                  {
                        // Then wrap (fetch the first block in the doc)
                        PT_DocPosition startOfDoc;
                        getEditableBounds(false, startOfDoc);

                        newBlock = m_pLayout->findBlockAtPosition(startOfDoc);

                        m_wrappedEnd = true;

                        UT_ASSERT(newBlock);
                  }

                  // Re-assign the buffer contents for our new block
                  pBuffer.truncate(0);

                  // The offset starts at 0 for a fresh buffer
                  newOffset = 0;
                  if (!newBlock->getBlockBuf(&pBuffer))
                  {
                        UT_DEBUGMSG(("Block %p (a ->next block) has no buffer.\n",
                                           newBlock));
                        UT_ASSERT(0);
                  }

                  // Good to go with a full buffer for our new block
            }
            
      }
      else
      {
            // We have some left to go in this buffer.      Buffer is still
            // valid, just copy pointers
            newBlock = *pBlock;
            newOffset = *pOffset;
      }

      // Are we going to run into the start position in this buffer?    If
      // so, we need to size our length accordingly
      if (m_wrappedEnd && _BlockOffsetToPos(newBlock, newOffset) + pBuffer.getLength() >= m_startPosition)
      {
            // sanity check
            if(m_startPosition > (newBlock)->getPosition(false) + newOffset)
                  bufferLength = (m_startPosition - (newBlock)->getPosition(false)) - newOffset;
      }
      else if(pBuffer.getLength() > newOffset)
      {
            bufferLength = pBuffer.getLength() - newOffset;
      }

      // clone a buffer (this could get really slow on large buffers!)
      UT_UCSChar* bufferSegment = NULL;

      // remember, the caller gets to free this memory
      bufferSegment = static_cast<UT_UCSChar*>(UT_calloc(bufferLength + 1, sizeof(UT_UCSChar)));
      UT_ASSERT(bufferSegment);

      memmove(bufferSegment, pBuffer.getPointer(newOffset),
                  (bufferLength) * sizeof(UT_UCSChar));

      // before we bail, hold up our block stuff for next round
      *pBlock = newBlock;
      *pOffset = newOffset;

      return bufferSegment;
}

UT_UCSChar*
FV_View::_findGetPrevBlockBuffer(fl_BlockLayout** pBlock,
                                                 PT_DocPosition* pOffset)
{
      UT_return_val_if_fail(m_pLayout && pBlock && *pBlock && pOffset,NULL);

      fl_BlockLayout* newBlock = NULL;
      PT_DocPosition newOffset = 0;
      
      UT_uint32 blockStart = 0;
      UT_uint32 bufferLength = 0;

      UT_GrowBuf pBuffer;

      // Check early for completion, from where we left off last, and
      // bail if we are now at or past the start position
      if (m_wrappedEnd
            && _BlockOffsetToPos(*pBlock, *pOffset) <= m_startPosition)
      {
            // We're done
            return NULL;
      }

      if (!(*pBlock)->getBlockBuf(&pBuffer))
      {
            UT_DEBUGMSG(("Block %p has no associated buffer.\n", *pBlock));
            // I gather we better return ???
            UT_ASSERT_HARMLESS(0);
            return NULL;
      }

      // Have we already searched all the text in this buffer?
      if (_BlockOffsetToPos(*pBlock, *pOffset) <= (*pBlock)->getPosition(false))
      {
            // Then return a fresh new block's buffer
            newBlock = *pBlock;
          get_new_block:      newBlock = newBlock->getPrevBlockInDocument();
            xxx_UT_DEBUGMSG(("Got prev block %x \n",newBlock));
            // Are we at the end of the document?
            if (!newBlock)
            {
                  if(m_wrappedEnd)
                        return NULL;
                  
                  // Then wrap (fetch the first block in the doc)
                  PT_DocPosition endOfDoc;
                  getEditableBounds(true, endOfDoc);

                  newBlock = m_pLayout->findBlockAtPositionReverse(endOfDoc);

                  m_wrappedEnd = true;
                  UT_DEBUGMSG(("Reached start of doc via getPrevBlockinDocument \n"));
                  UT_return_val_if_fail(newBlock, NULL);
            }

            // Re-assign the buffer contents for our new block
            pBuffer.truncate(0);
            // The offset starts at end of block
            newOffset = pBuffer.getLength();
            blockStart = 0;
            if (!newBlock->getBlockBuf(&pBuffer))
            {
                  UT_DEBUGMSG(("Block %p (a ->prev block) has no buffer.\n",
                                     newBlock));
                  UT_ASSERT_HARMLESS(0);
                  return NULL;
            }
            if(pBuffer.getLength() == 0)
            {
                  goto get_new_block;
            }
            // Good to go with a full buffer for our new block
      }
      else
      {
            // We have some left to go in this buffer.      Buffer is still
            // valid, just copy pointers
            newBlock = *pBlock;
            newOffset = *pOffset;
            blockStart = 0;
      }

      // Are we going to run into the start position in this buffer?    If
      // so, we need to size our length accordingly
      if (m_wrappedEnd && (newBlock->getPosition(false) <= m_startPosition))
      {
            blockStart = m_startPosition - (newBlock->getPosition(true));
      }

      if(blockStart >= pBuffer.getLength())
      {
            // we are done, as there is nothing to search .
            UT_DEBUGMSG(("PrevSearch completed \n"));
            return NULL;
      }
            
      bufferLength = pBuffer.getLength() - blockStart;

      // clone a buffer (this could get really slow on large buffers!)
      UT_UCSChar* bufferSegment = NULL;

      // remember, the caller gets to free this memory
      bufferSegment = (UT_UCSChar*)UT_calloc(bufferLength + 1, sizeof(UT_UCSChar));
      UT_return_val_if_fail(bufferSegment, NULL);

      memmove(bufferSegment, pBuffer.getPointer(blockStart),
                  (bufferLength) * sizeof(UT_UCSChar));

      // before we bail, hold up our block stuff for next round
      *pBlock = newBlock;
      *pOffset = newOffset;
      
      UT_DEBUGMSG(("Block pos: %d ", (newBlock)->getPosition(false)));
      UT_DEBUGMSG((" - len: %d\n", pBuffer.getLength()));
      

      return bufferSegment;
}

bool FV_View::_insertField(const char* szName, 
                                       const XML_Char ** extra_attrs, 
                                       const XML_Char ** extra_props)
{
      bool bResult;
      int attrCount = 0;
      if(szName && ((UT_strcmp(szName,"sum_rows") == 0) || (UT_strcmp(szName,"sum_cols") == 0)))
      {
           if(!isInTable())
           {
                return false;
           }
      }
      while (extra_attrs && extra_attrs[attrCount] != NULL)
      {
            attrCount++;
      }

      const XML_Char ** attributes = new const XML_Char*[attrCount+4];

      int i = 0;
      while (extra_attrs && extra_attrs[i] != NULL)
      {
            attributes[i] = extra_attrs[i];
            i++;
      }
      attributes[i++] = "type";
      attributes[i++] = szName;
      attributes[i++] = NULL;
      attributes[i++] = NULL;


      fd_Field * pField = NULL;
      if (!isSelectionEmpty() && !m_FrameEdit.isActive())
      {
            m_pDoc->beginUserAtomicGlob();
            _deleteSelection();

            insertParaBreakIfNeededAtPos(getPoint());
            bResult = m_pDoc->insertObject(getPoint(), PTO_Field, attributes, extra_props,&pField);
            if(pField != NULL)
            {
                  pField->update();
            }
            m_pDoc->endUserAtomicGlob();
      }
      else if(m_FrameEdit.isActive())
      {
             m_FrameEdit.setPointInside();
      }
      else
      {
            insertParaBreakIfNeededAtPos(getPoint());
            bResult = m_pDoc->insertObject(getPoint(), PTO_Field, attributes, extra_props, &pField);
            if(pField != NULL)
            {
                  pField->update();
            }
      }

      delete [] attributes;
      return bResult;
}
 
bool
FV_View::_findReplaceReverse(UT_uint32* pPrefix, bool& bDoneEntireDocument, bool bNoUpdate)
{
      UT_ASSERT(m_sFind && m_sReplace);

      bool bRes = false;

      _saveAndNotifyPieceTableChange();
      m_pDoc->beginUserAtomicGlob();

      // Replace selection if it's due to a find operation
      if (m_doneFind && !isSelectionEmpty())
      {
            bRes = true;

            PP_AttrProp AttrProp_Before;

            if (!isSelectionEmpty() && !m_FrameEdit.isActive())
            {
                  _deleteSelection(&AttrProp_Before, bNoUpdate);
            }
            else if(m_FrameEdit.isActive())
              {
                m_FrameEdit.setPointInside();
              }

            // If we have a string with length, do an insert, else let it
            // hang from the delete above
            if (*m_sReplace)
            {
                  bRes = m_pDoc->insertSpan(getPoint(), m_sReplace,
                                                        UT_UCS4_strlen(m_sReplace),
                                                        &AttrProp_Before);
                  
                  setPoint( getPoint() - UT_UCS4_strlen(m_sReplace)) ;
            }
            // Do not increase the insertion point index, since the insert
            // span will leave us at the correct place.

            if(!bNoUpdate)
                  _generalUpdate();


            // If we've wrapped around once, and we're doing work before
            // we've hit the point at which we started, then we adjust the
            // start position so that we stop at the right spot.
            if (m_wrappedEnd && !bDoneEntireDocument)
            {
                  m_startPosition += (long) UT_UCS4_strlen(m_sReplace);
                  m_startPosition -= (long) UT_UCS4_strlen(m_sFind);
            }

            UT_ASSERT(m_startPosition >= 2);
      }

      m_pDoc->endUserAtomicGlob();
      _restorePieceTableState();

      // Find next occurrence in document
      _findPrev(pPrefix, bDoneEntireDocument);
      return bRes;
}

/*!
 Find and replace text unit
 \param pFind String to find
 \param pReplace String to replace it with
 \param pPrefix Search prefix function
 \param bMatchCase True to match case, false to ignore case
 \result bDoneEntireDocument True if entire document searched,
             false otherwise
 \return True if string was replaced, false otherwise

 This function will replace an existing selection with pReplace. It
 will then do a search for pFind.
*/
bool
FV_View::_findReplace(UT_uint32* pPrefix, bool& bDoneEntireDocument, bool bNoUpdate)
{
      UT_ASSERT(m_sFind && m_sReplace);

      bool bRes = false;

      _saveAndNotifyPieceTableChange();
      m_pDoc->beginUserAtomicGlob();

      // Replace selection if it's due to a find operation
      if (m_doneFind && !isSelectionEmpty())
      {
            bRes = true;

            PP_AttrProp AttrProp_Before;

            if (!isSelectionEmpty() && !m_FrameEdit.isActive())
            {
                  _deleteSelection(&AttrProp_Before, bNoUpdate);
            }
            else if(m_FrameEdit.isActive())
            {
              m_FrameEdit.setPointInside();
            }

            // If we have a string with length, do an insert, else let it
            // hang from the delete above
            if (*m_sReplace)
                  bRes = m_pDoc->insertSpan(getPoint(), m_sReplace,
                                                        UT_UCS4_strlen(m_sReplace),
                                                        &AttrProp_Before);

            // Do not increase the insertion point index, since the insert
            // span will leave us at the correct place.

            if(!bNoUpdate)
                  _generalUpdate();

            // If we've wrapped around once, and we're doing work before
            // we've hit the point at which we started, then we adjust the
            // start position so that we stop at the right spot.
            if (m_wrappedEnd && !bDoneEntireDocument)
            {
                  m_startPosition += (long) UT_UCS4_strlen(m_sReplace);
                  m_startPosition -= (long) UT_UCS4_strlen(m_sFind);
            }

            UT_ASSERT(m_startPosition >= 2);
      }

      m_pDoc->endUserAtomicGlob();
      _restorePieceTableState();

      // Find next occurrence in document
      _findNext(pPrefix, bDoneEntireDocument);
      return bRes;
}

fl_BlockLayout*
FV_View::_findGetCurrentBlock(void)
{
      return _findBlockAtPosition(m_iInsPoint);
}

PT_DocPosition
FV_View::_findGetCurrentOffset(void)
{
      return (m_iInsPoint - _findGetCurrentBlock()->getPosition(false));
}

// Any takers?
UT_sint32
FV_View::_findBlockSearchRegexp(const UT_UCSChar* /* haystack */,
                                                const UT_UCSChar* /* needle */)
{
      UT_ASSERT(UT_NOT_IMPLEMENTED);

      return -1;
}

/*
  After most editing commands, it is necessary to call this method,
  _generalUpdate, in order to fix everything.
*/
void FV_View::_generalUpdate(void)
{
      bool bOK = true;
      if(!shouldScreenUpdateOnGeneralUpdate())
            return;
      m_pDoc->signalListeners(PD_SIGNAL_UPDATE_LAYOUT);

//
// No need to update other stuff if we're doing a preview
//
      if(isPreview())
            return;
//
// If we're in an illegal position move forward till we're safe.
//
      _makePointLegal();
      /*
        TODO note that we are far too heavy handed with the mask we
        send here.  I ripped out all the individual calls to notifyListeners
        which appeared within fl_BlockLayout, and they all now go through
        here.  For that reason, I made the following mask into the union
        of all the masks I found.  I assume that this is inefficient, but
        functionally correct.

        TODO WRONG! WRONG! WRONG! notifyListener() must be called in
        TODO WRONG! WRONG! WRONG! fl_BlockLayout in response to a change
        TODO WRONG! WRONG! WRONG! notification and not here.      this call
        TODO WRONG! WRONG! WRONG! will only update the current window.
        TODO WRONG! WRONG! WRONG! having the notification in fl_BlockLayout
        TODO WRONG! WRONG! WRONG! will get each view on the document.
      */
//
//    notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK );
      if(!m_pDoc->isDoingPaste())
      {
            notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK | AV_CHG_PAGECOUNT | AV_CHG_FMTSTYLE );
            setCursorToContext();
      }
}


void FV_View::_extSel(UT_uint32 iOldPoint)
{
      /*
        We need to calculate the differences between the old
        selection and new one.

        Anything which was selected, and now is not, should
        be fixed on screen, back to normal.

        Anything which was NOT selected, and now is, should
        be fixed on screen, to show it in selected state.

        Anything which was selected, and is still selected,
        should NOT be touched.

        And, obviously, anything which was not selected, and
        is still not selected, should not be touched.
      */
      UT_uint32 iNewPoint = getPoint();
      xxx_UT_DEBUGMSG(("_extSel: iNewPoint %d ioldPoint %d selectionAnchor %d \n",iNewPoint,iOldPoint,m_Selection.getSelectionAnchor()));
      PT_DocPosition posBOD,posEOD,dNewPoint,dOldPoint;
      dNewPoint = static_cast<PT_DocPosition>(iNewPoint);
      dOldPoint = static_cast<PT_DocPosition>(iOldPoint);
      getEditableBounds(false,posBOD);
      getEditableBounds(true,posEOD);
      if(dNewPoint < posBOD || dNewPoint > posEOD || dOldPoint < posBOD
         || dNewPoint > posEOD)
      {
            return;
      }
      if (iNewPoint == iOldPoint)
      {
            return;
      }
      if(iOldPoint < iNewPoint)
      {
            _drawBetweenPositions(iOldPoint, iNewPoint);
      }
      else
      {
            _drawBetweenPositions(iNewPoint,iOldPoint);
      }
}

void FV_View::_extSelToPos(PT_DocPosition iNewPoint)
{
      PT_DocPosition iOldPoint = getPoint();
      if (iNewPoint == iOldPoint)
            return;

      PT_DocPosition posBOD,posEOD,dNewPoint,dOldPoint;
      dNewPoint = static_cast<PT_DocPosition>(iNewPoint);
      dOldPoint = static_cast<PT_DocPosition>(iOldPoint);
      getEditableBounds(false,posBOD);
      getEditableBounds(true,posEOD);
      if(dNewPoint < posBOD || dNewPoint > posEOD || dOldPoint < posBOD
         || dNewPoint > posEOD)
      {
            return;
      }

      if (isSelectionEmpty())
      {
            _fixInsertionPointCoords();
            _clearIfAtFmtMark(getPoint());
            _setSelectionAnchor();
      }
      m_Selection.setMode(FV_SelectionMode_Single);
      _setPoint(iNewPoint);
//
// Look if we should select the initial cell.
//
      _extSel(iOldPoint);
      if(getSelectionAnchor() < getPoint())
      {
            PT_DocPosition posLow = getSelectionAnchor();
            fp_CellContainer * pLowCell = NULL;
            fp_CellContainer * pHighCell = NULL;
            if(isInTable(posLow) )
            {
                  pLowCell = getCellAtPos(posLow+1);
                  pHighCell =  getCellAtPos(getPoint());
                  if((pLowCell != NULL) && (pLowCell != pHighCell))
                  {
                        fl_CellLayout * pCell = static_cast<fl_CellLayout *>(pLowCell->getSectionLayout());
                        PT_DocPosition posCell = pCell->getPosition(true);
                        xxx_UT_DEBUGMSG(("posCell %d posLow %d \n",posCell,posLow));
                        if((posCell == posLow) && (m_iGrabCell == 0))
                        {
                              m_iGrabCell++;
                              m_Selection.setSelectionAnchor(posCell-1);
                              _drawBetweenPositions(posCell-1, getPoint());
                        }
                        else if(((posCell + 1) == posLow) && (m_iGrabCell == 0))
                        {
                              m_iGrabCell++;
                              m_Selection.setSelectionAnchor(posCell-1);
                              _drawBetweenPositions(posCell-1, getPoint());
                        }
                        else if(((posCell + 2) == posLow) && (m_iGrabCell == 0))
                        {
                              m_iGrabCell++;
                              m_Selection.setSelectionAnchor(posCell-1);
                              _drawBetweenPositions(posCell-1, getPoint());
                        }
//
// FIXME look to see if we've selected a whole row.
//
                  }
            }
      }

      if (isSelectionEmpty())
      {
            _resetSelection();
      }

//    notifyListeners(AV_CHG_MOTION);
}


/*
  This method simply iterates over every run between two doc positions
  and draws each one.
*/
void FV_View::_drawBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2)
{
      _drawOrClearBetweenPositions(iPos1, iPos2, false,false);
}

/*!
  This class was local to FV_View::_drawOrClearBetweenPositions(),
  but you can't use local types for a template instancation.
  HACK: I did move it outside and made everything private to prevent
  anyone else to use it.
 */
03310 class CellLine
{
private:
      CellLine(void):
            m_pCell(NULL),
            m_pBrokenTable(NULL),
            m_pLine(NULL)
            {}
      virtual ~CellLine(void)
            {
                  m_pCell = NULL;
                  m_pBrokenTable = NULL;
                  m_pLine = NULL;
            }
      fp_CellContainer * m_pCell;
      fp_TableContainer * m_pBrokenTable;
      fp_Line * m_pLine;
      friend class FV_View;
      friend bool FV_View::_drawOrClearBetweenPositions(PT_DocPosition iPos1,
                                                                                PT_DocPosition iPos2, 
                                                                                bool bClear, bool bFullLineHeight);
};

/*!
  This method simply iterates over every run between two doc positions
  and draws or clears and redraws each one. We clear if bClear is true 
  otherwise we just draw selected.
  If bClear is true then the if bFullLineHeight is true the runs are 
  cleared to their full height.
*/
bool FV_View::_drawOrClearBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2, bool bClear, bool bFullLineHeight)
{
//
// Handle little class for redrawing lines around cells
//
      
      // make sure iPos1 < iPos2
      if (iPos1 >= iPos2)
      {
            PT_DocPosition tmp = iPos1;
            iPos1 = iPos2;
            iPos2 = tmp;
      }
      
//    CHECK_WINDOW_SIZE
      xxx_UT_DEBUGMSG(("Draw between positions %d to %d \n",iPos1,iPos2));
      fp_Run* pRun1;
      fp_Run* pRun2;
      UT_sint32 xoff;
      UT_sint32 yoff;
      UT_uint32 uheight;
      UT_GenericVector<CellLine *> vecTables;
      UT_GenericVector<fp_Page *>vecPages;
//
// This fixes a bug from insert file, when the view we copy from is selected
// If don't bail out now we get all kinds of crazy dirty on the screen.
//
      if(m_pParentData == NULL)
      {
            return true;
      }
//    _fixInsertionPointCoords();
      {
            UT_sint32 x;
            UT_sint32 y;
            UT_sint32 x2;
            UT_sint32 y2;
            bool bDirection;
            fl_BlockLayout* pBlock1;
            fl_BlockLayout* pBlock2;

            /*
              we don't really care about the coords.  We're calling these
              to get the Run pointer
            */
            _findPositionCoords(iPos1, false, x, y, x2, y2, uheight, bDirection, &pBlock1, &pRun1);
            _findPositionCoords(iPos2, false, x, y, x2, y2, uheight, bDirection, &pBlock2, &pRun2);
      }

      UT_return_val_if_fail(pRun1 && pRun2, false );
      
      bool bDone = false;
      bool bIsDirty = false;
      fp_Run* pCurRun = pRun1;
      //bool bShowHidden = getShowPara();
      fp_CellContainer * pCell = NULL;
      fl_BlockLayout* pBlockEnd = pRun2->getBlock();
      PT_DocPosition posEnd = pBlockEnd->getPosition() + pRun2->getBlockOffset();
      while ((!bDone || bIsDirty) && pCurRun)
      {

            fl_BlockLayout* pBlock = pCurRun->getBlock();
            fp_Line * pLine = pCurRun->getLine();
            if(pLine == NULL || (pLine->getContainer()->getPage()== NULL))
            {
                  UT_VECTOR_PURGEALL(CellLine *, vecTables);
                  return true;
            }
            PT_DocPosition curpos = pBlock->getPosition() + pCurRun->getBlockOffset();
            if ((pCurRun->getLength() > 0 ) && (pCurRun == pRun2 || curpos >= posEnd))
            {
                  bDone = true;
            }
            if(curpos > posEnd)
            {
//                break;
            }
            xxx_UT_DEBUGMSG(("draw_between positions pos is %d width is %d \n",curpos,pCurRun->getWidth()));
            UT_ASSERT(pBlock);
//
// Look to see if the Block is in a table.
//
            fl_ContainerLayout * pCL = pBlock->myContainingLayout();
            bool bCellSelected = false;
            if(pCL->getContainerType() == FL_CONTAINER_CELL)
            {
                  fp_Container * pCP = static_cast<fp_Container *>(pLine->getContainer());
                  if(pCP)
                  {
                        pCell = static_cast<fp_CellContainer *>(pCP);
                        fp_TableContainer * pTab = pCell->getBrokenTable(pLine);
                        if(pTab)
                        {
                              CellLine * pCellLine = new CellLine();
                              pCellLine->m_pCell = pCell;
                              pCellLine->m_pLine = pLine;
                              pCellLine->m_pBrokenTable = pTab;
                              xxx_UT_DEBUGMSG(("cellLine %x cell %x Table %x Line %x \n",pCellLine,pCellLine->m_pCell,pCellLine->m_pBrokenTable,pCellLine->m_pLine));
                              vecTables.addItem(pCellLine);
                              fp_Page * pPage = pTab->getPage();
                              if((pPage != NULL) && (vecPages.findItem(pPage) <0))
                              {
                                    vecPages.addItem(pPage);
                              }
                        }
                  }
                  fl_CellLayout * pCellLayout = static_cast<fl_CellLayout *>(pCL);
//
// See if the whole cell is selected. If so draw it.
//
                  bCellSelected = pCellLayout->isCellSelected();
                  fp_Container * pNextCon = NULL;
                  if(bCellSelected)
                  {
                        pNextCon = pCell->drawSelectedCell(pCurRun->getLine());
                        if(pNextCon == NULL)
                        {
                              fl_BlockLayout * pBlock = pCurRun->getBlock();
                              pBlock = pBlock->getNextBlockInDocument();
                              if(pBlock)
                              {
                                    pCurRun = pBlock->getFirstRun();
                                    continue;
                              }
                              pCurRun = NULL;
                              continue;
                        }
                        if(pNextCon->getContainerType() == FP_CONTAINER_LINE)
                        {
                              pCurRun = static_cast<fp_Line *>(pNextCon)->getFirstRun();
                              continue;
                        }
                        else
                        {
                              UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
                              pCurRun = NULL;
                              continue;
                        }
                  }
                  else
                  {
                        if(pCell && pCell->isSelected())
                        {
                              pCell->clearSelection();
                              pCell->clearScreen();
                              pCell->draw(pCurRun->getLine());
                              if(pCurRun->getLine() != NULL)
                              {
                                    fp_Page * pPage = pCurRun->getLine()->getPage();
                                    if((pPage != NULL) && (vecPages.findItem(pPage) <0))
                                    {
                                          vecPages.addItem(pPage);
                                    }
                              }
                        }
                  }
            }

            if(!pCurRun->isHidden())
            {
                  if(pLine == NULL || (pLine->getContainer()->getPage()== NULL))
                  {
                        UT_VECTOR_PURGEALL(CellLine *, vecTables);
                        return true;
                  }
                  pLine->getScreenOffsets(pCurRun, xoff, yoff);

                  dg_DrawArgs da;
                  da.bDirtyRunsOnly = false;
                  da.pG = m_pG;
                  da.xoff = xoff;
                  da.yoff = yoff + pLine->getAscent();
                  if(!bClear)
                  {
                        xxx_UT_DEBUGMSG(("Draw Position Low %d High %d anchor %d point %d \n",iPos1,iPos2,getSelectionAnchor(),getPoint()));
//                      UT_sint32 iLow = getSelectionAnchor();
//                      UT_sint32 iHigh = getPoint();
//                      if(iHigh < iLow
                        pCurRun->setSelectionMode(iPos1-1,iPos2+1);
                        pCurRun->draw(&da);
                        pCurRun->clearSelectionMode();
                  }
                  else
                  {
                        xxx_UT_DEBUGMSG(("Clear Position Low %d High %d anchor %d point %d \n",iPos1,iPos2,getSelectionAnchor(),getPoint()));
                        pCurRun->setSelectionMode(iPos1-4,iPos2+4);
                        pCurRun->Run_ClearScreen(bFullLineHeight);
                        pCurRun->draw(&da);
                        pCurRun->clearSelectionMode();
                  }
                  fp_Page * pPage = pLine->getPage();
                  if((pPage != NULL) && (vecPages.findItem(pPage) <0))
                  {
                        vecPages.addItem(pPage);
                  }
            }

            pCurRun = pCurRun->getNextRun();
            if (!pCurRun)
            {
                  fl_BlockLayout* pNextBlock;

                  pNextBlock = pBlock->getNextBlockInDocument();
                  if (pNextBlock)
                  {
                        pCurRun = pNextBlock->getFirstRun();
                  }
            }

            if (!pCurRun)
            {
                  bIsDirty = false;
            }
            else
            {
                  bIsDirty = pCurRun->isDirty();
            }
      }
//
// Now redraw the lines in any table encountered.
//
      xxx_UT_DEBUGMSG(("Drawing lines in tables %d \n",vecTables.getItemCount()));
      UT_sint32 i =0;
      for(i=0; i< static_cast<UT_sint32>(vecTables.getItemCount()); i++)
      {
            CellLine * pCellLine = vecTables.getNthItem(i);
            pCellLine->m_pCell->drawLines(pCellLine->m_pBrokenTable,getGraphics());
            pCellLine->m_pCell->drawLinesAdjacent();

      }
      for(i=0; i< static_cast<UT_sint32>(vecPages.getItemCount()); i++)
      {
            fp_Page * pPage = vecPages.getNthItem(i);
            UT_sint32 xoff,yoff;
            getPageScreenOffsets(pPage,xoff, yoff);
            dg_DrawArgs da;
            da.pG = m_pG;
            da.xoff = xoff;
            da.yoff = yoff;
            da.bDirtyRunsOnly = true;
            pPage->redrawDamagedFrames(&da);
      }
      UT_VECTOR_PURGEALL(CellLine *, vecTables);
      xxx_UT_DEBUGMSG(("Finished Drawing lines in tables \n"));
      m_pG->flush();
      return true;
}

/*
  This method simply iterates over every run between two doc positions
  and draws each one.
*/
bool FV_View::_clearBetweenPositions(PT_DocPosition iPos1, PT_DocPosition iPos2, bool bFullLineHeightRect)
{
      return _drawOrClearBetweenPositions(iPos1, iPos2, true,bFullLineHeightRect);
#if 0
      xxx_UT_DEBUGMSG(("FV_View::_clearBetweenPositions called\n"));
      if (iPos1 >= iPos2)
      {
            return true;
      }
      fp_Run* pRun1;
      fp_Run* pRun2;
      UT_uint32 uheight;

      _fixInsertionPointCoords();
      {
            UT_sint32 x;
            UT_sint32 y;
            UT_sint32 x2;
            UT_sint32 y2;
            bool bDirection;
            fl_BlockLayout* pBlock1;
            fl_BlockLayout* pBlock2;

            /*
              we don't really care about the coords.  We're calling these
              to get the Run pointer
            */
            _findPositionCoords(iPos1, false, x, y, x2, y2, uheight, bDirection, &pBlock1, &pRun1);
            _findPositionCoords(iPos2, false, x, y, x2, y2, uheight, bDirection, &pBlock2, &pRun2);
      }

      if (!pRun1 && !pRun2)
      {
            // no formatting info for either block, so just bail
            // this can happen during spell, when we're trying to invalidate
            // a new squiggle before the block has been formatted
            return false;
      }

      // HACK: In certain editing cases only one of these is NULL, which
      //           makes locating runs to clear more difficult.  For now, I'm
      //           playing it safe and trying to just handle these cases here.
      //           The real solution may be to just bail if *either* is NULL,
      //           but I'm not sure.
      //
      //           If you're interested in investigating this alternative
      //           approach, play with the following asserts.

//    UT_ASSERT(pRun1 && pRun2);
      UT_ASSERT(pRun2);

      bool bDone = false;
      fp_Run* pCurRun = (pRun1 ? pRun1 : pRun2);


      while (!bDone)
      {
            if (pCurRun == pRun2)
            {
                  bDone = true;
            }

            fl_BlockLayout* pBlock = pCurRun->getBlock();
            UT_ASSERT(pBlock);
//
// Look to see if the Block is in a table.
//
            fl_ContainerLayout * pCL = pBlock->myContainingLayout();
            if(pCL->getContainerType() == FL_CONTAINER_CELL)
            {
                  fp_CellContainer * pCell = static_cast<fp_CellContainer *>(pCL->getFirstContainer());
                  if(pCell->isSelected())
                  {
                        pCell->clearSelection();
                        pCell->clearScreen();
                  
                        fl_BlockLayout * pBlock = NULL;
                        fl_ContainerLayout * pLastCL = pCL->getFirstLayout();
                        while(pLastCL->getNext())
                        {
                              pLastCL = pLastCL->getNext();
                        }
                        while(pLastCL->getContainerType() != FL_CONTAINER_BLOCK)
                        {
                              pLastCL = pLastCL->getFirstLayout();
                        }
                        pBlock = static_cast<fl_BlockLayout *>(pLastCL);
                        pBlock = pBlock->getNextBlockInDocument();
                        if(pBlock)
                        {
                              pCurRun = pBlock->getFirstRun();
                              continue;
                        }
                        pCurRun = NULL;
                        bDone = true;
                        continue;
                  }
            }
            pCurRun->clearScreen(bFullLineHeightRect);
            if (pCurRun->getNextRun())
            {
                  pCurRun = pCurRun->getNextRun();
            }
            else
            {
                  fl_BlockLayout* pNextBlock;

                  fl_BlockLayout* pBlock = pCurRun->getBlock();
                  UT_ASSERT(pBlock);

                  pNextBlock = pBlock->getNextBlockInDocument();
                  if (pNextBlock)
                  {
                        pCurRun = pNextBlock->getFirstRun();
                  }
                  else
                        bDone = true;
                  // otherwise we get fun
                  // infinte loops
            }
      }
      return true;
#endif
}

void FV_View::_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) const
{
      UT_sint32 xPoint = 0;
      UT_sint32 yPoint = 0;
      UT_sint32 xPoint2 = 0;
      UT_sint32 yPoint2 = 0;
      UT_sint32 iPointHeight;
      if(ppRun)
      {
            *ppRun = NULL;
      }
      // Get the previous block in the document. _findBlockAtPosition
      // will iterate forwards until it actually find a block if there
      // isn't one previous to pos.
      // (Removed code duplication. Jesper, 2001.01.25)

//
// Have to deal with special case of point being exactly on a footnote/endnote
// boundary
//
      bool onFootnoteBoundary = false;
      if(m_pDoc->isFootnoteAtPos(pos))
      {
            onFootnoteBoundary = true;
            pos--;
      }
      fl_BlockLayout* pBlock = _findBlockAtPosition(pos);

      // this assert makes debugging very hard ...
#ifdef DEBUG
      static bool bDoneTheAssert = false;
      if(!bDoneTheAssert)
      {
            bDoneTheAssert = true;
            UT_ASSERT_HARMLESS(pBlock && pBlock->getContainerType() == FL_CONTAINER_BLOCK);
      }
#endif
      if(!pBlock || pBlock->getContainerType() != FL_CONTAINER_BLOCK)
      {
            x = x2 = 0;
            y = y2 = 0;

            height = 0;
            if(ppBlock)
                  *ppBlock = 0;
            return;
      }


      if(onFootnoteBoundary)
      {
            pos++;
      }
      // probably an empty document, return instead of
      // dereferencing NULL.  Dom 11.9.00
      if(!pBlock)
      {
            // Do the assert. Want to know from debug builds when this happens.
            // This assert makes debuging virtuall impossible; the hack below makes it assert only once.
            // UT_ASSERT(pBlock);
#ifdef DEBUG
            static bool bAss = false;
            if(!pBlock && !bAss)
            {
                  UT_ASSERT_HARMLESS( pBlock );
                  bAss = true;
            }
#endif

            x = x2 = 0;
            y = y2 = 0;

            height = 0;
            if(ppBlock)
                  *ppBlock = 0;
            return;
      }

      // if the block cannot contain point, find the nearest block to
      // the left that can; or to the right, if none to the left exists
      fl_BlockLayout * pOrigBL = pBlock;
      
      while(pBlock && !pBlock->canContainPoint())
      {
            pBlock = pBlock->getPrevBlockInDocument();
      }

      if(!pBlock)
      {
            pBlock = pOrigBL;
            
            while(pBlock && !pBlock->canContainPoint())
            {
                  pBlock = pBlock->getNextBlockInDocument();
            }
      }

      if(!pBlock)
      {
            // no blocks that can take point in this document !!!
            UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
            x = x2 = 0;
            y = y2 = 0;

            height = 0;
            if(ppBlock)
                  *ppBlock = 0;
            return;
      }
      
      // If block is actually to the right of the requested position
      // (this happens in an empty document), update the pos with the
      // start pos of the block.
      PT_DocPosition iBlockPos = pBlock->getPosition(false);
      if (iBlockPos > pos)
      {
            pos = iBlockPos;
      }
      xxx_UT_DEBUGMSG(("Position to find %d \n",pos));
      fp_Run* pRun = pBlock->findPointCoords(pos, bEOL, xPoint, yPoint, xPoint2, yPoint2, iPointHeight, bDirection);


      // NOTE prior call will fail if the block isn't currently formatted,
      // NOTE so we won't be able to figure out more specific geometry

//
// Needed for piecetable fields. We don't have these in 1.0
//
// OK if we're at the end of document at the last run is a field we have to add
// the width of the field run to xPoint, xPoint2.
//
      PT_DocPosition posEOD = 0;
      getEditableBounds(true, posEOD);
      xxx_UT_DEBUGMSG(("SEVIOR: Doing posEOD =%d getPoint %d pRun->isField %d bEOL %d \n",posEOD,getPoint(),pRun->isField(),bEOL));

      if(bEOL && pRun && posEOD == getPoint())
      {
            bool bBack = true;
            while(pRun && pRun->getPrevRun() && !pRun->isField() && pRun->getWidth() == 0)
            {
                  bBack = false;
                  pRun = pRun->getPrevRun();
            }
            if(pRun && pRun->isField() && bBack)
            {
                  UT_DEBUGMSG(("SEVIOR: Doing EOD work aorund \n"));
                  static_cast<fp_FieldRun *>(pRun)->recalcWidth();
                  xPoint += pRun->getWidth();
                  xPoint2 += pRun->getWidth();
            }
      }
      else if( (pRun == NULL) && (posEOD == getPoint()))
      {
            pRun = pBlock->getFirstRun();
            while(pRun && (pRun->getNextRun() != NULL))
            {
                  pRun = pRun->getNextRun();
            }
      }
      if (pRun)
      {
            // we now have coords relative to the page containing the ins pt
                  fp_Line * pLine =  pRun->getLine();
                  if(!pLine)
            {
                  x = x2 = 0;
                  y = y2 = 0;

                  height = 0;
                  if(ppBlock)
                    *ppBlock = 0;
                  return;
            }

            fp_Page* pPointPage = pLine->getPage();

            UT_sint32 iPageOffset;
            getPageYOffset(pPointPage, iPageOffset);

            yPoint += iPageOffset;
            xPoint += getPageViewLeftMargin();
            yPoint2 += iPageOffset;
            xPoint2 += getPageViewLeftMargin();

            // now, we have coords absolute, as if all pages were stacked vertically
            xPoint -= m_xScrollOffset;
            yPoint -= m_yScrollOffset;

            xPoint2 -= m_xScrollOffset;
            yPoint2 -= m_yScrollOffset;

            // now, return the results
            x = xPoint;
            y = yPoint;

            x2 = xPoint2;
            y2 = yPoint2;
            xxx_UT_DEBUGMSG(("x pos in view %d \n",x));

            height = iPointHeight;
      }
      if (ppBlock)
      {
            *ppBlock = pBlock;
      }

      if (ppRun)
      {
            *ppRun = pRun;
      }
}

void FV_View::_fixInsertionPointCoords()
{
      if (m_pG->getCaret() == NULL)
            return;

      fp_Page * pPage = NULL;
      fl_BlockLayout * pBlock = NULL;
      fp_Run * pRun = NULL;
      if ((getPoint() > 0) && !isLayoutFilling())
      {
            _findPositionCoords(getPoint(), m_bPointEOL, m_xPoint, m_yPoint, m_xPoint2, m_yPoint2, m_iPointHeight, m_bPointDirection, &pBlock, &pRun);
            pPage = getCurrentPage();
            UT_RGBColor * pClr = NULL;
            if (pPage)
                  pClr = pPage->getFillType()->getColor();
            UT_sint32 yoff = 0;
            if(m_yPoint < 0)
            {
                  UT_sint32 negY  = -m_yPoint;
                  yoff = negY + 1;
                  if(negY > (UT_sint32)m_iPointHeight)
                  {
                        m_iPointHeight = 0;
                        yoff = 0;
                  }
            }
            xxx_UT_DEBUGMSG(("Xpoint in fixpoint %d \n",m_xPoint));
            m_pG->getCaret()->setCoords(m_xPoint, m_yPoint+yoff, m_iPointHeight-yoff,
                                                      m_xPoint2, m_yPoint2+yoff, m_iPointHeight-yoff, 
                                                      m_bPointDirection, pClr);
      }

      m_pG->getCaret()->setWindowSize(getWindowWidth(), getWindowHeight());

      xxx_UT_DEBUGMSG(("SEVIOR: m_yPoint = %d m_iPointHeight = %d \n",m_yPoint,m_iPointHeight));
      // hang onto this for _moveInsPtNextPrevLine()
      m_xPointSticky = m_xPoint + m_xScrollOffset - getPageViewLeftMargin();
      if(pBlock && pBlock->getSquiggles()->get(getPoint() - pBlock->getPosition()))
      {
            if(m_prevMouseContext == EV_EMC_TEXT)
            {
                  m_prevMouseContext = EV_EMC_MISSPELLEDTEXT;
            }
      }
}

void FV_View::_draw(UT_sint32 x, UT_sint32 y,
                              UT_sint32 width, UT_sint32 height,
                              bool bDirtyRunsOnly, bool bClip)
{
      xxx_UT_DEBUGMSG(("FV_View::draw_3 [x %ld][y %ld][w %ld][h %ld][bClip %ld]\n"
                               "\t\twith [yScrollOffset %ld][windowHeight %ld]\n",
                               x,y,width,height,bClip,
                               m_yScrollOffset,getWindowHeight()));
      
      GR_Painter painter(m_pG);

      // CHECK_WINDOW_SIZE
      // this can happen when the frame size is decreased and
      // only the toolbars show...

      if ((getWindowWidth() <= 0) || (getWindowHeight() <= 0))
      {
            UT_DEBUGMSG(("fv_View::draw() called with zero drawing area.\n"));
            return;
      }

      if ((width <= 0) || (height <= 0))
      {
            UT_DEBUGMSG(("fv_View::draw() called with zero width or height expose.\n"));
            return;
      }

      // TMN: Leave this rect at function scope!
      // gr_Graphics only stores a _pointer_ to it!
      UT_Rect rClip;
      if (bClip)
      {
            rClip.left = x;
            rClip.top = y;
            rClip.width = width;
            rClip.height = height;
            m_pG->setClipRect(&rClip);
      }
//    UT_ASSERT(m_yScrollOffset == m_pG->getPrevYOffset());
      
      // figure out where pages go, based on current window dimensions
      // TODO: don't calc for every draw
      // HYP:  cache calc results at scroll/size time
      UT_sint32 iDocHeight = m_pLayout->getHeight();

      // TODO: handle positioning within oversized viewport
      // TODO: handle variable-size pages (envelope, landscape, etc.)

      /*
        In page view mode, so draw outside decorations first, then each
        page with its decorations.
      */

      UT_RGBColor clrMargin = getColorMargin();

      if (!bDirtyRunsOnly)
      {
            if ((m_xScrollOffset < getPageViewLeftMargin()) && (getViewMode() == VIEW_PRINT))
            {
                  // fill left margin
                  painter.fillRect(clrMargin, 0, 0, getPageViewLeftMargin() - m_xScrollOffset, getWindowHeight() + m_pG->tlu(1));
            }

            if (m_yScrollOffset < getPageViewTopMargin() && (getViewMode() == VIEW_PRINT))
            {
                  // fill top margin
                  painter.fillRect(clrMargin, 0, 0, getWindowWidth() + m_pG->tlu(1), getPageViewTopMargin() - m_yScrollOffset);
            }
      }

      UT_sint32 curY = getPageViewTopMargin();
      fp_Page* pPage = m_pLayout->getFirstPage();
      fl_DocSectionLayout * pDSL = NULL;
      
      if (pPage)  // pPage can be NULL at this point
            pDSL = pPage->getOwningSection();

#if 0
//
// fixme for multiple sections in normal and web view
// 
      UT_sint32 totPageHeight = pPage->getHeight();
      if(getViewMode() == VIEW_PRINT)
      {
            totPageHeight += getPageViewSep();
      }
      else
      {
            curY = 0;
            totPageHeight = totPageHeight  - pDSL->getTopMargin() - pDSL->getBottomMargin();
      }
      UT_sint32 nPage = (m_yScrollOffset-curY)/totPageHeight -1;
      if(nPage >= static_cast<UT_sint32>(m_pLayout->countPages()))
      {
            nPage = m_pLayout->countPages() - 1;
      }
      if(nPage < 0) 
      {
            nPage = 0;
      }
      pPage = m_pLayout->getNthPage(nPage);
      curY = curY + nPage*totPageHeight;
#endif
      bool bNotEnd = false;
      xxx_UT_DEBUGMSG(("Starting at page %x \n",pPage));
      while (pPage)
      {
            UT_sint32 iPageWidth          = pPage->getWidth();
            UT_sint32 iPageHeight         = pPage->getHeight();
            UT_sint32 adjustedTop         = curY - m_yScrollOffset;
            pDSL = pPage->getOwningSection();
            if(getViewMode() != VIEW_PRINT)
            {
                  iPageHeight = iPageHeight - pDSL->getTopMargin() - pDSL->getBottomMargin();
            }

            UT_sint32 adjustedBottom = adjustedTop + iPageHeight + getPageViewSep();

            if (adjustedTop > getWindowHeight())
            {
                  // the start of this page is past the bottom
                  // of the window, so we don't need to draw it.

                  xxx_UT_DEBUGMSG(("not drawing page A: iPageHeight=%d curY=%d nPos=%d getWindowHeight()=%d\n",
                                           iPageHeight,
                                           curY,
                                           m_yScrollOffset,
                                           getWindowHeight()));

                  // since all other pages are below this one, we
                  // don't need to draw them either.  exit loop now.
                  bNotEnd = true;
                  break;
            }
            else if (adjustedBottom < 0)
            {
                  // the end of this page is above the top of
                  // the window, so we don't need to draw it.

                  xxx_UT_DEBUGMSG(("not drawing page B: iPageHeight=%d curY=%d nPos=%d getWindowHeight()=%d\n",
                                           iPageHeight,
                                           curY,
                                           m_yScrollOffset,
                                           getWindowHeight()));
            }
            else if (adjustedTop > y + height)
            {
                  // the top of this page is beyond the end
                  // of the clipping region, so we don't need
                  // to draw it.

                  xxx_UT_DEBUGMSG(("not drawing page C: iPageHeight=%d curY=%d nPos=%d getWindowHeight()=%d y=%d h=%d\n",
                                           iPageHeight,
                                           curY,
                                           m_yScrollOffset,
                                           getWindowHeight(),
                                           y,height));
                  bNotEnd = true;
                  break;
            }
            else if (adjustedBottom < y)
            {
                  // the bottom of this page is above the top
                  // of the clipping region, so we don't need
                  // to draw it.

                  xxx_UT_DEBUGMSG(("not drawing page D: iPageHeight=%d curY=%d nPos=%d getWindowHeight()=%d y=%d h=%d\n",
                                           iPageHeight,
                                           curY,
                                           m_yScrollOffset,
                                           getWindowHeight(),
                                           y,height));
                  //TF NOTE: Can we break out here?
            }
            else
            {
                  // this page is on screen and intersects the clipping region,
                  // so we *DO* draw it.

                  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));

                  dg_DrawArgs da;

                  da.bDirtyRunsOnly = bDirtyRunsOnly;
                  da.pG = m_pG;
                  da.xoff = getPageViewLeftMargin() - m_xScrollOffset;
                  da.yoff = adjustedTop;
                  UT_sint32 adjustedLeft  = getPageViewLeftMargin() - m_xScrollOffset;
                  UT_sint32 adjustedRight = adjustedLeft + iPageWidth;

                  adjustedBottom -= getPageViewSep();

                  if (!bDirtyRunsOnly || pPage->needsRedraw() && (getViewMode() == VIEW_PRINT))
                  {
                    UT_RGBColor * pClr = pPage->getFillType()->getColor();
                    painter.fillRect(*pClr,adjustedLeft+m_pG->tlu(1),adjustedTop+m_pG->tlu(1),iPageWidth + m_pG->tlu(1),iPageHeight + m_pG->tlu(1));
//
// Since we're clearing everything we have to draw every run no matter
// what.
//
                    da.bDirtyRunsOnly = false;
                  }
                  pPage->draw(&da);

                  // draw page decorations
                  UT_RGBColor clr(0,0,0);       // black
                  m_pG->setColor(clr);

                  // one pixel border a
                  if(!isPreview() && (getViewMode() == VIEW_PRINT))
                  {
                        m_pG->setLineProperties(m_pG->tluD(1.0),
                                                            GR_Graphics::JOIN_MITER,
                                                            GR_Graphics::CAP_PROJECTING,
                                                            GR_Graphics::LINE_SOLID);

                        painter.drawLine(adjustedLeft, adjustedTop, adjustedRight, adjustedTop);
                        painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom);
                        painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);
                        painter.drawLine(adjustedLeft, adjustedTop, adjustedLeft, adjustedBottom);
                  }

//
// Draw page seperator
//
                  UT_RGBColor paperColor = *(pPage->getFillType()->getColor());

                  // only in NORMAL MODE - draw a line across the screen
                  // at a page boundary. Not used in online/web and print
                  // layout modes
                  if(getViewMode() == VIEW_NORMAL)
                  {
                        UT_RGBColor clrPageSep(192,192,192);            // light gray
                        m_pG->setColor(clrPageSep);

                        m_pG->setLineProperties(m_pG->tluD(1.0),
                                                            GR_Graphics::JOIN_MITER,
                                                            GR_Graphics::CAP_PROJECTING,
                                                            GR_Graphics::LINE_SOLID);

                        painter.drawLine(adjustedLeft, adjustedBottom, getWindowWidth() + m_pG->tlu(1), adjustedBottom);
                        adjustedBottom += m_pG->tlu(1);
                        m_pG->setColor(clr);
                  }
                  // fill to right of page
                  if (getWindowWidth() - (adjustedRight + m_pG->tlu(1)) > 0)
                  {
                        // In normal mode, the right margin is
                        // white (since the whole screen is white).
                        if(getViewMode() != VIEW_PRINT)
                        {
                              painter.fillRect(paperColor, adjustedRight, adjustedTop, getWindowWidth() - adjustedRight + m_pG->tlu(1), iPageHeight);
                        }
                        // Otherwise, the right margin is the
                        // margin color (gray).
                        else
                        {
                              painter.fillRect(clrMargin, adjustedRight + m_pG->tlu(1), adjustedTop, getWindowWidth() - adjustedRight, iPageHeight + m_pG->tlu(3));
                        }
                  }

                  // fill separator below page
                  if ((getWindowHeight() - (adjustedBottom + m_pG->tlu(1)) > 0) && (VIEW_PRINT == getViewMode()))
                  {
                        if(pPage->getNext() != NULL)
                        {
                              painter.fillRect(clrMargin, adjustedLeft, adjustedBottom + m_pG->tlu(1), getWindowWidth() - adjustedLeft + m_pG->tlu(1), getPageViewSep());
                        }
                        else // found last page
                        {
                              UT_sint32 botfill = getWindowHeight() - adjustedBottom - m_pG->tlu(1) ;
                              painter.fillRect(clrMargin, adjustedLeft, adjustedBottom + m_pG->tlu(1), getWindowWidth() - adjustedLeft + m_pG->tlu(1), botfill + m_pG->tlu(1));
                        }
                  }

                  // two pixel drop shadow

                  if(!isPreview() && (getViewMode() == VIEW_PRINT))
                  {
                        m_pG->setLineProperties(m_pG->tluD(1.0),
                                                            GR_Graphics::JOIN_MITER,
                                                            GR_Graphics::CAP_PROJECTING,
                                                            GR_Graphics::LINE_SOLID);

                        adjustedLeft += m_pG->tlu(3);
                        adjustedBottom += m_pG->tlu(1);
                        painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);

                        adjustedBottom += m_pG->tlu(1);
                        painter.drawLine(adjustedLeft, adjustedBottom, adjustedRight + m_pG->tlu(1), adjustedBottom);

                        adjustedTop += m_pG->tlu(3);
                        adjustedRight += m_pG->tlu(1);
                        painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom + m_pG->tlu(1));

                        adjustedRight += m_pG->tlu(1);
                        painter.drawLine(adjustedRight, adjustedTop, adjustedRight, adjustedBottom + m_pG->tlu(1));
                  }
            }
            xxx_UT_DEBUGMSG(("PageHeight %d Page %x \n",iPageHeight,pPage));
            curY += iPageHeight + getPageViewSep();

            pPage = pPage->getNext();
      }

      if ((curY <= iDocHeight) && !bNotEnd)
      {
            // fill below bottom of document
            UT_sint32 y = curY - m_yScrollOffset;
            UT_sint32 h = getWindowHeight() - y;
            if (h > 0)
            {
                  xxx_UT_DEBUGMSG(("Height of grey fill %d window height %d y %d curY %d \n",h,getWindowHeight(),y,curY));
      
                  UT_RGBColor clrFillColor;
                  if (getViewMode() != VIEW_PRINT)
                  {
                        const XML_Char * pszTransparentColor = NULL;
                        this->getApp()->getPrefs()->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForTransparent),&pszTransparentColor);
                        clrFillColor.setColor(pszTransparentColor);
                  }
                  else
                  {
                        clrFillColor = clrMargin;
                  }
                  painter.fillRect(clrFillColor, 0, y, getWindowWidth() + m_pG->tlu(1), h + m_pG->tlu(1));
            }
      }

      if (bClip)
      {
            m_pG->setClipRect(NULL);
      }

}

void FV_View::_setPoint(PT_DocPosition pt, bool bEOL)
{
      if (!m_pDoc->getAllowChangeInsPoint())
            return;
      if(!m_pDoc->isPieceTableChanging())
      {
//
// Have to deal with special case of point being exactly on a footnote/endnote
// boundary. Move the point past the footnote so we always have Footnote field
// followed by footnotestrux in the piecetable
//
            fl_FootnoteLayout * pFL = NULL;
            if(m_pDoc->isFootnoteAtPos(pt))
            {
                  pFL = getClosestFootnote(pt);
                  if(pFL == NULL)
                  {
                        fl_EndnoteLayout * pEL = getClosestEndnote(pt);
                        if(pEL)
                        {
                              pt += pEL->getLength();
                        }
                  }
                  else
                  {
                        pt += pFL->getLength();
                  }
            }           
      }
      m_iInsPoint = pt;
      xxx_UT_DEBUGMSG(("Point set to %d in View %x \n",pt,this));
      m_bPointEOL = bEOL;
      if(!m_pDoc->isPieceTableChanging())
      {
            _fixInsertionPointCoords();
            m_pLayout->considerPendingSmartQuoteCandidate();
            _checkPendingWordForSpell();
      // So, if there is a selection now, we should disable the cursor; conversely,
      // if there is no longer a selection, we should enable the cursor.
            if (isSelectionEmpty())
            {     
                  while(m_countDisable > 0)
                  {
                    if(m_pG->getCaret())
                      m_pG->getCaret()->enable();
                    m_countDisable--;
                  }
                  if(m_pG->getCaret()) {
                    m_pG->getCaret()->disable();
                    m_pG->getCaret()->enable();
                  }
            }
            else
            {     
//
// We have to remember the number of times we disabled the cursor and wind
// them back to re-enable it because the cursor class keeps a count this to
// handle nested disable calls.
//

              if(m_pG->getCaret())
                m_pG->getCaret()->disable();
              m_countDisable++;
            }
      }
}


/*!
 Spell-check pending word
 If the IP does not touch the pending word, spell-check it.

 \note This function used to exit if PT was changing - but that
         prevents proper squiggle behavior during undo, so the check has
         been removed. This means that the pending word POB must be
         updated to reflect the PT changes before the IP is moved.
 */
void
FV_View::_checkPendingWordForSpell(void)
{
      if (!m_pLayout->isPendingWordForSpell()) return;

      // Find block at IP
      fl_BlockLayout* pBL = _findBlockAtPosition(m_iInsPoint);
      if (pBL)
      {
            UT_uint32 iOffset = m_iInsPoint - pBL->getPosition();

            // If it doesn't touch the pending word, spell-check it
            if (!m_pLayout->touchesPendingWordForSpell(pBL, iOffset, 0))
            {
                  // no longer there, so check it
                  if (m_pLayout->checkPendingWordForSpell())
                  {
                        // FIXME:jskov Without this updateScreen call, the
                        // just squiggled word remains deleted. It's overkill
                        // (surely we should have a requestUpdateScreen() that
                        // does so after all operations have completed), but
                        // works. Unfortunately it causes a small screen
                        // artifact when pressing undo, since some runs may be
                        // redrawn before they have their correct location
                        // recalculated. In other words, make the world a
                        // better place by adding requestUpdateScreen or
                        // similar.
                        updateScreen();
                  }
            }
      }
}

UT_uint32 FV_View::_getDataCount(UT_uint32 pt1, UT_uint32 pt2)
{
      UT_ASSERT(pt2>=pt1);
      return pt2 - pt1;
}


bool FV_View::_charMotion(bool bForward,UT_uint32 countChars, bool bSkipCannotContainPoint)
{
      // advance(backup) the current insertion point by count characters.
      // return false if we ran into an end (or had an error).

      PT_DocPosition posOld = m_iInsPoint;
      fp_Run* pRun = NULL;
      fl_BlockLayout* pBlock = NULL;
      UT_sint32 x=0;
      UT_sint32 y=0;
      UT_sint32 x2=0;
      UT_sint32 y2=0;
      bool bDirection=false;
      UT_uint32 uheight;
      m_bPointEOL = false;
      UT_sint32 iOldDepth = getEmbedDepth(getPoint());
      UT_DEBUGMSG(("_charMotion: Old Position is %d embed depth %d \n",posOld,iOldDepth));
      /*
        we don't really care about the coords.  We're calling these
        to get the Run pointer
      */
      PT_DocPosition posBOD=0;
      PT_DocPosition posEOD=0;
      bool bRes;

      bRes = getEditableBounds(false, posBOD);
      bRes = getEditableBounds(true, posEOD);
      UT_ASSERT(bRes);

      // FIXME:jskov want to rewrite this code to use simplified
      // versions of findPositionCoords. I think there's been some bugs
      // due to that function being overloaded to be used from this
      // code.
      UT_sint32 xold,yold,x2old,y2old=0;
      bool bDirectionOld=false;
      UT_DEBUGMSG(("Count Chars %d \n",countChars));
      _findPositionCoords(m_iInsPoint, false, xold, yold, x2old,y2old,uheight, bDirectionOld, &pBlock, &pRun);
      if (bForward)
      {
            UT_DEBUGMSG(("Just before First forward _setPoint %d \n",m_iInsPoint));
            _setPoint(m_iInsPoint + countChars);
            UT_DEBUGMSG(("Just After First forward _setPoint %d \n",m_iInsPoint));
//
// Scan past any strux boundaries (like table controls
//
            while(getPoint() < posEOD && !isPointLegal())
            {
                  UT_DEBUGMSG(("Forward scan past illegal point pos 1 %d \n",m_iInsPoint));
                  _setPoint(m_iInsPoint + 1);
                  UT_DEBUGMSG(("Forward scan past illegal point pos 2 %d \n",m_iInsPoint));
            }
            _findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
//
// If we come to a table boundary we have doc positions with no blocks.
// _findPositionCoords signals this by returning pRun == NULL
//
            // I have added the bDirection == bDirectionOld condition
            // because without it the code did not work on direction
            // boundaries. However, I am not sure whether the whole of the
            // x,y test is at all desirable here; any idea why it is here?
            // Tomas, Jan 16, 2003.

            // Testing the coords is definitely wrong; combining characters often do not advance x,y,
            // but need to be treated as a valid document position. See bug 6987. Tomas, July 27, 2004

            bool bExtra = false;
            while(m_iInsPoint <= posEOD && (pRun == NULL /*|| ((x == xold) && (y == yold) &&
                                                                                     (x2 == x2old) && (y2 == y2old) &&
                                                                                     (bDirection == bDirectionOld))*/))
            {
                  UT_DEBUGMSG(("fv_View_protected: (2) pRun = %x pos %d\n",pRun,m_iInsPoint));
                  _setPoint(m_iInsPoint+1);
                  UT_DEBUGMSG(("fv_View_protected: (3) pRun = %x pos %d \n",pRun,m_iInsPoint));
                  _findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
                  bExtra = true;
            }
            if(bExtra)
            {
                  _setPoint(m_iInsPoint-1);
            }

            
#if 0
            while(pRun != NULL &&  pRun->isField() && m_iInsPoint <= posEOD)
            {
                  _setPoint(m_iInsPoint+1);
                  if(m_iInsPoint <= posEOD)
                  {
                        _findPositionCoords(m_iInsPoint, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
                  }
            }
#endif
    }
      else
      {
            _setPoint(m_iInsPoint - countChars);
//
// Scan past any strux boundaries (like table controls
//
// when moving backwards, we need to also skip over the EndOfFootnote struxes
            while(getPoint() > posBOD && !isPointLegal())
            {
                  _setPoint(m_iInsPoint - 1);
                  UT_DEBUGMSG(("Backward scan past illegal point pos %d \n",m_iInsPoint));
            }
            _findPositionCoords(m_iInsPoint, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);

//
// If we come to a table boundary we have doc positions with no blocks.
// _findPositionCoords signals this by returning pRun == NULL
//
            // I have added the bDirection == bDirectionOld condition
            // because without it the code did not work on direction
            // boundaries. However, I am not sure whether the whole of the
            // x,y test is at all desirable here; any idea why it is here?
            // Tomas, Jan 16, 2003.

            // Testing the coords is definitely wrong; combining characters often do not advance x,y,
            // but need to be treated as a valid document position. See bug 6987. Tomas, July 27, 2004

            bool bExtra = false;
            while( m_iInsPoint >= posBOD && (pRun == NULL /*|| ((x == xold) && (y == yold) &&
                                                                                     (x2 == x2old) && (y2 == y2old) &&
                                                                                     (bDirection == bDirectionOld))*/))
            {
                  xxx_UT_DEBUGMSG(("_charMotion: Looking at point m_iInsPoint %d \n",m_iInsPoint));
                  _setPoint(m_iInsPoint-1);
                  _findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
                  bExtra = true;
            }
            if(bExtra)
            {
                  _setPoint(m_iInsPoint-1);
            }

            
#if 0
// Needed for piecetable fields - we don't have these in 1.0

            while(pRun != NULL && pRun->isField() && m_iInsPoint >= posBOD)
            {
                  _setPoint(m_iInsPoint-1);
                  _findPositionCoords(m_iInsPoint-1, false, x, y, x2,y2,uheight, bDirection, &pBlock, &pRun);
            }
#endif
            // if the run which declared itself for our position is end of paragraph run,
            // we need to ensure that the position is just before the run, not after it
            // (fixes bug 1120)
            xxx_UT_DEBUGMSG(("_charMotion: pRun->getBlockOffset() %d getPosition %d insPoint %d \n",pRun->getBlockOffset(), pRun->getBlock()->getPosition(),m_iInsPoint));
            if(pRun && pRun->getType() == FPRUN_ENDOFPARAGRAPH
               && (pRun->getBlockOffset() + pRun->getBlock()->getPosition()) < m_iInsPoint)
            {
                  _setPoint(m_iInsPoint-1);
            }
      }

      UT_ASSERT(bRes);

      bRes = true;

      // we might have skipped over some runs that cannot contain the
      // point, but but have non-zero length, such as any hidden text;
      // if this is the case, we need to adjust the document position accordingly

      UT_return_val_if_fail(pBlock, false);
      UT_return_val_if_fail(pRun, false);
      
      PT_DocPosition iRunStart = pBlock->getPosition(false) + pRun->getBlockOffset();
      PT_DocPosition iRunEnd = iRunStart + pRun->getLength();

      // containing layout we will work with, ususally section
//    fl_ContainerLayout * pCL = pBlock->myContainingLayout();

      // the layout immediately above the runs, should be block
//    fl_ContainerLayout * pBL = pBlock;

      // indicates how many layout layers we had to step up to get valid pCL
//    UT_uint32 iLayoutDepth = 0;

      if(bForward && ( m_iInsPoint > iRunEnd))
      {
            // the run we have got is the on left of the ins point, we
            // need to find the right one and set the point there; we also
            // need to make sure that we handle correctly any hidden
            // sub layouts (blocks, sections, table cells, tables ...)

            // get the next run that can contain insertion point
            pRun = pRun->getNextRun();
            UT_uint32 iLength = 0;
            while(pRun && ((bSkipCannotContainPoint && !pRun->canContainPoint()) || pRun->getLength() == 0))
            {
                  UT_DEBUGMSG(("_charMotion: Sweep forward through runs %d \n",pRun->getLength()));
                  iLength += pRun->getLength();
                  pRun = pRun->getNextRun();
            }

            if(pRun)
            {
                  _setPoint(m_iInsPoint + iLength);
            }
            else
            {
//
// FIXME: Put in some code here to handle table/cell boundaries. Right
// now you have to press left arrow twice to move form outside to inside
// a table.

            }
      }
      // these two branches ensure that insertion point is not moved just after a run that is not
      // suppossed to take point
      else if(bSkipCannotContainPoint && bForward && m_iInsPoint == iRunStart && pRun->getLength()>0)
      {
            // moving forward, with insertion point ending between two runs;
            // we need to check that the previous run was not one that cannot take point, in which case
            // we need to advance the point by one (otherwise it will appear just after the non-point
            // run
            // this case happens, for example, when the user has a hyperlink at the start of line and
            // presses HOME, RIGHT. The HOME key takes her before the hyperlink, the right, however,
            // should skip over they hyperlink run
            
            pRun = pRun->getPrevRun();
            if(pRun && !pRun->canContainPoint())
                  _setPoint(m_iInsPoint + 1);
      }
      else if(bSkipCannotContainPoint && !bForward && m_iInsPoint == iRunStart)
      {
            // moving backwards, with insertion point ending between two runs; we need to scroll
            // through any adjucent runs on the left that cannot contain point
            pRun = pRun->getPrevRun();
            UT_uint32 iLength = 0;
            while(pRun && !pRun->canContainPoint())
            {
                  iLength += pRun->getLength();
                  pRun = pRun->getPrevRun();
            }

            // do this unconditionally; if !pRun, we are at the start of the block
            _setPoint(m_iInsPoint - iLength);
      }

      // this is much simpler, since the findPointCoords will return the
      // run on the left of the requested position, so we just need to move
      // to its end if the position does not fall into that run
      UT_DEBUGMSG(("_charMotion: iRunEnd %d \n",iRunEnd));
      if(!bForward && (iRunEnd < m_iInsPoint) && (pRun->getBlockOffset() > 0))
      {
            _setPoint(iRunEnd - 1);
      }

      // now have the get the run that actualy holds this position, and let it do any internal
      // adjustments (as needed for complex scripts, e.g., Thai) not sure whether this should not come
      // after the footnote sweep
      if(bSkipCannotContainPoint)
      {
            
            pBlock = _findBlockAtPosition(getPoint());
            UT_return_val_if_fail( pBlock, false );

            pRun = pBlock->findRunAtOffset(getPoint() - pBlock->getPosition());

            // at the end of document we do not have a run ...
            if(pRun)
            {
                  UT_uint32 iAdjustedPos = pRun->adjustCaretPosition(getPoint(), bForward);

                  if(iAdjustedPos != getPoint())
                  {
                        UT_DEBUGMSG(("FV_View::_charMotion: orig pos %d, adjusted to %d\n", getPoint(), iAdjustedPos));
                        _setPoint(iAdjustedPos);
                  }
            }
      }
      
//
// OK sweep through footnote sections without stopping
      xxx_UT_DEBUGMSG(("Point is %d inFootnote %d bOldFootnote %d \n",m_iInsPoint,isInFootnote(),iOldDepth));
      if(bForward)
      {
            if(iOldDepth < getEmbedDepth(m_iInsPoint))
            {
                  bool bSweep = false;
                  while(m_iInsPoint <= posEOD && ( (iOldDepth < getEmbedDepth(m_iInsPoint)) || m_pDoc->isEndFootnoteAtPos(getPoint())))
                  { 
                        UT_DEBUGMSG(("_charMotion: Sweep forward -1 %d \n",m_iInsPoint));
                        bSweep = true;
                        m_iInsPoint++;
                  }
                  if(bSweep && (m_iInsPoint <= posEOD))
                  {
                        _setPoint(m_iInsPoint);
                  }
                  else if(m_iInsPoint > posEOD)
                  {
                        _setPoint(posEOD);
                  }
            }
            else if((iOldDepth > getEmbedDepth(m_iInsPoint)) )
            {
                  bool bSweep = false;
                  while((m_iInsPoint > posBOD) && (iOldDepth > getEmbedDepth(m_iInsPoint)) || m_pDoc->isFootnoteAtPos(getPoint()) )
                  {
                        UT_DEBUGMSG(("_charMotion: Sweep backward -1 %d \n",m_iInsPoint));
                        m_iInsPoint--;
                        bSweep = true;
                  }
                  if(bSweep)
                  {
                        _setPoint(m_iInsPoint);
                  }
            }

      }
      else
      {
            if(iOldDepth < getEmbedDepth(m_iInsPoint))
            {
                  bool bSweep = false;
                  while(((iOldDepth < getEmbedDepth(m_iInsPoint)) || m_pDoc->isFootnoteAtPos(getPoint()) ) && (m_iInsPoint >= posBOD))
                  { 
                        UT_DEBUGMSG(("_charMotion: Sweep backward -2 %d \n",m_iInsPoint));
                        bSweep = true;
                        m_iInsPoint--;
                  }
                  if(bSweep && (m_iInsPoint >= posBOD))
                  {
                        _setPoint(m_iInsPoint);
                  }
                  else if(m_iInsPoint > posEOD)
                  {
                        _setPoint(posOld);
                  }
            }
          else
            {
                  bool bSweep = false;
                  while((m_iInsPoint < posEOD) &&((iOldDepth > getEmbedDepth(m_iInsPoint)) || m_pDoc->isEndFootnoteAtPos(getPoint())))
                  {
                        UT_DEBUGMSG(("_charMotion: Sweep forward -2 %d \n",m_iInsPoint));
                        m_iInsPoint++;
                        bSweep = true;
                  }
                  if(bSweep)
                  {
                        _setPoint(m_iInsPoint);
                  }
            }
      }
      if (static_cast<UT_sint32>(m_iInsPoint) < static_cast<UT_sint32>(posBOD))
      {
            _setPoint(posBOD);
            bRes = false;
      }
      else if (static_cast<UT_sint32>(m_iInsPoint) > static_cast<UT_sint32>(posEOD))
      {
            m_bPointEOL = true;
            _setPoint(posEOD);
            bRes = false;
      }

      if (m_iInsPoint != posOld)
      {
            m_pLayout->considerPendingSmartQuoteCandidate();
            _checkPendingWordForSpell();
            _clearIfAtFmtMark(posOld);
            if(!m_pDoc->isDoingPaste())
            {
                  notifyListeners(AV_CHG_MOTION);
            }
      }
      if(m_FrameEdit.isActive())
      {
            m_FrameEdit.setMode(FV_FrameEdit_NOT_ACTIVE);
      }
      UT_DEBUGMSG(("SEVIOR: Point = %d \n",getPoint()));
      _fixInsertionPointCoords();
      return bRes;
}


void FV_View::_doPaste(bool bUseClipboard, bool bHonorFormatting)
{
      // internal portion of paste operation.

      if (!isSelectionEmpty() && !m_FrameEdit.isActive())
            _deleteSelection();
      else if(m_FrameEdit.isActive())
      {
             m_FrameEdit.setPointInside();
      }

      _clearIfAtFmtMark(getPoint());
      PD_DocumentRange dr(m_pDoc,getPoint(),getPoint());
      m_pApp->pasteFromClipboard(&dr,bUseClipboard,bHonorFormatting);
      fl_SectionLayout * pSL = getCurrentBlock()->getSectionLayout();
      m_pDoc->setDontImmediatelyLayout(false);
      pSL->checkAndAdjustCellSize();
      _generalUpdate();

      _updateInsertionPoint();
}


UT_Error FV_View::_deleteBookmark(const char* szName, bool bSignal, PT_DocPosition *posStart, PT_DocPosition *posEnd)
{
      if(!m_pDoc->isBookmarkUnique(static_cast<const XML_Char *>(szName)))
      {
            // even though we will only send out a single explicit deleteSpan
            // call, we need to find out where both of the markers are in the
            // document, so that the caller can adjust any stored doc positions
            // if necessary

            PT_DocPosition pos1, pos2;

            fp_BookmarkRun * pB1;
            UT_uint32 bmBlockOffset[2];
            fl_BlockLayout * pBlock[2];
            UT_uint32 i = 0;

            fl_BlockLayout *pBL;
            fl_SectionLayout *pSL = m_pLayout->getFirstSection();
            fp_Run * pRun = 0;
            bool bFound = false;

            //find the first of the two bookmarks
            while(pSL)
            {
                  pBL = static_cast<fl_BlockLayout *>(pSL->getFirstLayout());

                  while(pBL)
                  {
                        pRun = pBL->getFirstRun();

                        while(pRun)
                        {
                              if(pRun->getType()== FPRUN_BOOKMARK)
                              {
                                    pB1 = static_cast<fp_BookmarkRun*>(pRun);
                                    if(!UT_XML_strcmp(static_cast<const XML_Char *>(szName), pB1->getName()))
                                    {
                                          bmBlockOffset[i] = pRun->getBlockOffset();
                                          pBlock[i] = pRun->getBlock();
                                          i++;
                                          if(i>1)
                                          {
                                                bFound = true;
                                                break;
                                          }
                                    }
                              }
                              if(bFound)
                                    break;
                              pRun = pRun->getNextRun();
                        }
                        if(bFound)
                              break;
                        pBL = static_cast<fl_BlockLayout *>(pBL->getNext());
                  }
                  if(bFound)
                        break;
                  pSL = static_cast<fl_SectionLayout *>(pSL->getNext());
            }

            UT_ASSERT(pRun && pRun->getType()==FPRUN_BOOKMARK && pBlock || pBlock);
            if(!pRun || pRun->getType()!=FPRUN_BOOKMARK || !pBlock || !pBlock)
                  return false;

            // Signal PieceTable Change
            if(bSignal)
                  _saveAndNotifyPieceTableChange();

            UT_DEBUGMSG(("fv_View::cmdDeleteBookmark: bl pos [%d,%d], bmOffset [%d,%d]\n",
                               pBlock[0]->getPosition(false), pBlock[1]->getPosition(false),bmBlockOffset[0],bmBlockOffset[1]));

            pos1 = pBlock[0]->getPosition(false) + bmBlockOffset[0];
            pos2 = pBlock[1]->getPosition(false) + bmBlockOffset[1];

            if (posStart && *posStart > pos1)
                  (*posStart)--;
            if (posStart && *posStart > pos2)
                  (*posStart)--;

            if (posEnd && *posEnd > pos1)
                  (*posEnd)--;
            if (posEnd && *posEnd > pos1)
                  (*posEnd)--;

            UT_uint32 iRealDeleteCount;

            m_pDoc->deleteSpan(pos1,pos1 + 1,NULL,iRealDeleteCount);
            // TODO -- add proper revision handling using iRealDeleteCount

            // Signal PieceTable Changes have finished
            if(bSignal)
            {
                  _restorePieceTableState();
                  _generalUpdate();
            }
      }
      else
            UT_DEBUGMSG(("fv_View::cmdDeleteBookmark: bookmark \"%s\" does not exist\n",szName));
      return true;
}


/*! Returns the hyperlink around position pos, if any; assumes
 * posStart, posEnd in same block. */
fp_HyperlinkRun * FV_View::_getHyperlinkInRange(PT_DocPosition &posStart,
                                                                        PT_DocPosition &posEnd)
{
      fl_BlockLayout *pBlock = _findBlockAtPosition(posStart);
      PT_DocPosition curPos = posStart - pBlock->getPosition(false);

      fp_Run * pRun = pBlock->getFirstRun();

      //find the run at pos
      while(pRun && pRun->getBlockOffset() <= curPos)
            pRun = pRun->getNextRun();

      UT_ASSERT(pRun);
      if(!pRun)
            return NULL;

      // now we have the run immediately after the run in question, so
      // we step back
      pRun = pRun->getPrevRun();
      UT_ASSERT(pRun);
      if(!pRun)
            return NULL;

      if (pRun->getHyperlink() != NULL)
            return pRun->getHyperlink();

      // Now, getHyperlink() looks NULL, so let's step forward till posEnd.

      PT_DocPosition curPosEnd = posEnd - pBlock->getPosition(false);

      // Continue checking for hyperlinks.
      while(pRun && pRun->getBlockOffset() <= curPosEnd)
      {
            pRun = pRun->getNextRun();
            if (pRun && pRun->getPrevRun() && pRun->getPrevRun()->getHyperlink() != NULL)
                  return pRun->getPrevRun()->getHyperlink();
      }

      // OK, we're really safe now.
      return NULL;
}

/*
      NB: this function assumes that the position it is passed is inside a
      hyperlink and will assert if it is not so.
*/

UT_Error FV_View::_deleteHyperlink(PT_DocPosition &pos1, bool bSignal)
{
      fp_HyperlinkRun * pH1 = _getHyperlinkInRange(pos1, pos1);

      UT_ASSERT(pH1);
      if(!pH1)
            return false;

      if (!isSelectionEmpty())
            _clearSelection();

      pos1 = pH1->getBlock()->getPosition(false) + pH1->getBlockOffset();

      // now reset the hyperlink member for the runs that belonged to this
      // hyperlink

      fp_Run * pRun = pRun = pH1->getNextRun();
      UT_ASSERT(pRun);
      while(pRun && pRun->getHyperlink() != NULL)
      {
            UT_DEBUGMSG(("fv_View::_deleteHyperlink: reseting run 0x%x\n", pRun));
            pRun->setHyperlink(NULL);
            pRun = pRun->getNextRun();
      }

      UT_ASSERT(pRun);

      // Signal PieceTable Change
      if(bSignal)
            _saveAndNotifyPieceTableChange();

      UT_DEBUGMSG(("fv_View::cmdDeleteHyperlink: position [%d]\n",
                        pos1));

      UT_uint32 iRealDeleteCount;

      m_pDoc->deleteSpan(pos1,pos1 + 1,NULL, iRealDeleteCount);
      // TODO -- add proper revision handling using iRealDeleteCount

      // Signal PieceTable Changes have finished
      if(bSignal)
      {
            _restorePieceTableState();
            _generalUpdate();
      }
      return true;
}


UT_Error FV_View::_insertGraphic(FG_Graphic* pFG, const char* szName)
{
      UT_ASSERT(pFG);
      UT_ASSERT(szName);

      if (!pFG)
        return UT_ERROR;

      return pFG->insertIntoDocument(m_pDoc, m_pG->getDeviceResolution(), getPoint(), szName);
}


void FV_View::_clearIfAtFmtMark(PT_DocPosition dpos)
{
      // Check to see if we're at the beginning of the line. If we
      // aren't, then it's safe to delete the FmtMark. Else we could
      // wipe out the placeholder FmtMark for our attributes.
      // Fix for Bug #863
      xxx_UT_DEBUGMSG(("FV_View::_clearIfAtFmtMark called\n"));
      if ( ( dpos != _getDocPosFromPoint(dpos,FV_DOCPOS_BOL) ))
      {
            m_pDoc->clearIfAtFmtMark(dpos);
            //    _generalUpdate();//  Sevior: May be able to live with notify.. always
      }
      else
      {
            notifyListeners(AV_CHG_TYPING | AV_CHG_FMTCHAR | AV_CHG_FMTBLOCK);
      }
}


// NB: returns a UCS string that the caller needs to FREEP
UT_UCSChar * FV_View::_lookupSuggestion(fl_BlockLayout* pBL,
                                                            fl_PartOfBlock* pPOB, UT_uint32 ndx)
{
      // mega caching - are these assumptions valid?
      UT_UCSChar * szSuggest = NULL;

      // TODO these should really be static members, so we can properly
      // clean up
      static fl_BlockLayout * s_pLastBL = 0;
      static fl_PartOfBlock * s_pLastPOB = 0;
      static const UT_GenericVector<UT_UCSChar*>* s_pvCachedSuggestions = 0;

      // can we use the cached suggestions?
      if (pBL != s_pLastBL || pPOB != s_pLastPOB)
      {
            // no we cannot, so we empty and invalidate the cache
            if (s_pvCachedSuggestions)
            {
                  // clean up
                  for (UT_uint32 i = 0; i < s_pvCachedSuggestions->getItemCount(); i++)
                  {
                        const UT_UCSChar * sug = s_pvCachedSuggestions->getNthItem(i);
                        FREEP(sug);
                  }

                  s_pLastBL = 0;
                  s_pLastPOB = 0;
                  DELETEP(s_pvCachedSuggestions);
            }

            // grab a copy of the word
            UT_GrowBuf pgb(1024);
            bool bRes = pBL->getBlockBuf(&pgb);
            UT_ASSERT(bRes);

            UT_UCS4String stMisspelledWord;
            // convert smart quote apostrophe to ASCII single quote to be
            // compatible with ispell
            const UT_UCSChar * pWord = reinterpret_cast<UT_UCSChar*>(pgb.getPointer(pPOB->getOffset()));
            UT_uint32 len = pPOB->getLength();
            for (UT_uint32 ldex=0; ldex < len && ldex < INPUTWORDLEN; ldex++)
            {
                  stMisspelledWord += *pWord == UCS_RQUOTE ? '\'' : *pWord;
                  ++pWord;
            }

            // get language code for misspelled word
            const char * szLang = NULL;

            const XML_Char ** props_in = NULL;

            if (getCharFormat(&props_in))
            {
                  szLang = UT_getAttribute("lang", props_in);
                  FREEP(props_in);
            }

            // get spellchecker engine for language code
            SpellChecker * checker = NULL;

            if (szLang)
            {
                  // we get smart and request the proper dictionary
                  checker = SpellManager::instance().requestDictionary(szLang);
            }
            else
            {
                  // we just (dumbly) default to the last dictionary
                  // TODO this is known to return the wrong dictionary sometimes in multilanguge docs
                  checker = SpellManager::instance().lastDictionary();
            }

            // lookup suggestions

            // create an empty vector
            UT_GenericVector<UT_UCSChar*>* pvFreshSuggestions = 0;
            UT_ASSERT(!pvFreshSuggestions);

            pvFreshSuggestions = new UT_GenericVector<UT_UCSChar*>();
            UT_ASSERT(pvFreshSuggestions);

            if (checker->checkWord(stMisspelledWord.ucs4_str(), pPOB->getLength()) == SpellChecker::LOOKUP_FAILED)
            {
                  // get suggestions from spelling engine
                  const UT_GenericVector<UT_UCSChar*>* cpvEngineSuggestions;

                  cpvEngineSuggestions = checker->suggestWord (stMisspelledWord.ucs4_str(), pPOB->getLength());

                  for (UT_uint32 i = 0; i < cpvEngineSuggestions->getItemCount(); ++i)
                  {
                        UT_UCSChar *sug = cpvEngineSuggestions->getNthItem(i);
                        UT_ASSERT(sug);
                        pvFreshSuggestions->addItem(sug);
                  }

                  // add suggestions from user's AbiWord file
                   m_pApp->suggestWord(pvFreshSuggestions,stMisspelledWord.ucs4_str(), pPOB->getLength());
            }

            // update static vars for next call
            s_pvCachedSuggestions = pvFreshSuggestions;
            s_pLastBL = pBL;
            s_pLastPOB = pPOB;
      }

      // return the indexed suggestion from the cache
      if ((s_pvCachedSuggestions->getItemCount()) &&
            ( ndx <= s_pvCachedSuggestions->getItemCount()))
      {
            UT_UCS4_cloneString(&szSuggest, s_pvCachedSuggestions->getNthItem(ndx-1));
      }

      return szSuggest;
}


void FV_View::_prefsListener( XAP_App * /*pApp*/, XAP_Prefs *pPrefs, UT_StringPtrMap * /*phChanges*/, void *data )
{
      FV_View *pView = static_cast<FV_View *>(data);
      bool b;
      UT_ASSERT(data && pPrefs);
      if ( pPrefs->getPrefsValueBool(static_cast<const XML_Char*>(AP_PREF_KEY_CursorBlink), &b) && b != pView->m_bCursorBlink )
      {
            UT_DEBUGMSG(("FV_View::_prefsListener m_bCursorBlink=%s m_bCursorIsOn=%s\n",
                               pView->m_bCursorBlink ? "TRUE" : "FALSE",
                               pView->m_bCursorIsOn ? "TRUE" : "FALSE"));

            pView->m_bCursorBlink = b;
            pView->m_pG->getCaret()->setBlink(b);
      }


      // Update colors
      const XML_Char * pszTmpColor = NULL;
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForShowPara), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorShowPara);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForSquiggle), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorSquiggle);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForMargin), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorMargin);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForFieldOffset), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorFieldOffset);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForImage), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorImage);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForHyperLink), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorHyperLink);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForHdrFtr), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorHdrFtr);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForColumnLine), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorColumnLine);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision1), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[0]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision2), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[1]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision3), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[2]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision4), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[3]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision5), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[4]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision6), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[5]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision7), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[6]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision8), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[7]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision9), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[8]);
      }
      if (pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForRevision10), &pszTmpColor))
      {
            UT_parseColor(pszTmpColor, pView->m_colorRevisions[9]);
      }
      pView->m_bgColorInitted = false; // force refresh/update on next getColorSelBackground () call

      // FIXME:jskov: is it necessary to do something here to cause a full redraw?

      if (!pView->m_bWarnedThatRestartNeeded &&
            ( pPrefs->getPrefsValueBool(static_cast<const XML_Char*>(AP_PREF_KEY_DefaultDirectionRtl), &b) && b != pView->m_bDefaultDirectionRtl)
             || (pPrefs->getPrefsValueBool(static_cast<const XML_Char*>(XAP_PREF_KEY_UseHebrewContextGlyphs), &b) && b != pView->m_bUseHebrewContextGlyphs)
            )
      {
            /*    It is possible to change this at runtime, but it may impact the
                  way the document is displayed in an unexpected way (from the user's
                  point of view). It is therefore probably better to apply this change
                  only when AW will be restarted or a new document is created and
                  notify the user about that.
            */
            XAP_Frame * pFrame = static_cast<XAP_Frame *>(pView->getParentData());
            UT_ASSERT((pFrame));

            pFrame->showMessageBox(AP_STRING_ID_MSG_AfterRestartNew, XAP_Dialog_MessageBox::b_O, XAP_Dialog_MessageBox::a_OK);
            pView->m_bWarnedThatRestartNeeded = true;
      }
}


/*!
 * Copy a header/footer from a pHdrFtrSrc to an empty pHdrFtrDest.
 * into a new type of header/footer in the same section.
 */
void FV_View::_populateThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtrSrc, fl_HdrFtrSectionLayout * pHdrFtrDest)
{
      PD_DocumentRange dr_source;
      PT_DocPosition iPos1,iPos2;
      iPos1 = m_pDoc->getStruxPosition(pHdrFtrSrc->getFirstLayout()->getStruxDocHandle());
      fl_BlockLayout * pLast = static_cast<fl_BlockLayout *>(pHdrFtrSrc->getLastLayout());
      iPos2 = pLast->getPosition(false);
//
// This code assumes there is an End of Block run at the end of the Block.
// Thanks to Jesper, there always is!
//
      while(pLast->getNext() != NULL)
      {
            pLast = static_cast<fl_BlockLayout *>(pLast->getNext());
      }
      fp_Run * pRun = pLast->getFirstRun();
      while( pRun->getNextRun() != NULL)
      {
            pRun = pRun->getNextRun();
      }
      iPos2 += pRun->getBlockOffset();
//
// OK got the doc range for the source. Set it and copy it.
//
      dr_source.set(m_pDoc,iPos1,iPos2);
//
// Copy to and from clipboard to populate the header/Footer
//
      UT_DEBUGMSG(("SEVIOR: Copy to clipboard making header/footer \n"));
      m_pApp->copyToClipboard(&dr_source);
      PT_DocPosition posDest = 0;
      posDest = pHdrFtrDest->getFirstLayout()->getPosition(true);
      PD_DocumentRange dr_dest(m_pDoc,posDest,posDest);
      UT_DEBUGMSG(("SEVIOR: Pasting to clipboard making header/footer \n"));
      m_pApp->pasteFromClipboard(&dr_dest,true,true);
}


/*!
 * This method removes the HdrFtr pHdrFtr
 */
void FV_View::_removeThisHdrFtr(fl_HdrFtrSectionLayout * pHdrFtr)
{
      if(pHdrFtr == NULL)
      {
            return;
      }
      UT_DEBUGMSG(("view_protected: Removing HdrFtr %x \n",pHdrFtr));
//
// Need this to remove the HdrFtr attributes in the section strux.
//
      fl_DocSectionLayout * pDSL = pHdrFtr->getDocSectionLayout();
      const XML_Char * pszHdrFtrType = NULL;
      UT_ASSERT(pHdrFtr->getContainerType() == FL_CONTAINER_HDRFTR);
      PL_StruxDocHandle sdhHdrFtr = pHdrFtr->getStruxDocHandle();
      m_pDoc->getAttributeFromSDH(sdhHdrFtr,isShowRevisions(),getRevisionLevel(),PT_TYPE_ATTRIBUTE_NAME, &pszHdrFtrType);
      PT_DocPosition    posDSL = m_pDoc->getStruxPosition(pDSL->getStruxDocHandle());
//
// Remove the header/footer strux
//
      m_pDoc->deleteHdrFtrStrux(sdhHdrFtr);

}

void FV_View::_cmdEditHdrFtr(HdrFtrType hfType)
{
      fp_Page * pPage = getCurrentPage();
//
// If there is no header/footer, insert it and start to edit it.
//
      fl_HdrFtrShadow * pShadow = NULL;
      fp_ShadowContainer * pHFCon = NULL;
      pHFCon = pPage->getHdrFtrP(hfType);
      if(pHFCon == NULL)
      {
            insertHeaderFooter(hfType);
            return;
      }
      if(isHdrFtrEdit())
            clearHdrFtrEdit();
      pShadow = pHFCon->getShadow();
      UT_ASSERT(pShadow);
//
// Put the insertion point at the beginning of the header
//
      fl_BlockLayout * pBL = static_cast<fl_BlockLayout *>(pShadow->getFirstLayout());
      if (!isSelectionEmpty())
            _clearSelection();

      _setPoint(pBL->getPosition());
//
// Set Header/footer mode and we're done! Easy :-)
//
      setHdrFtrEdit(pShadow);
      _generalUpdate();
      _updateInsertionPoint();
}


/*    the problem with using bool to store the PT state is that
      when we make two successive calls to _saveAndNotifyPieceTableChange
      all subsequent calls to _restorePieceTableState will end up in the
      else branch, i.e, the PT will remain in state of change. Thus,
      the new implementation uses int instead of bool and actually keeps
      count of the calls to _save...;
*/
void FV_View::_saveAndNotifyPieceTableChange(void)
{
      //UT_DEBUGMSG(("notifying PieceTableChange start [%d]\n", m_iPieceTableState));
      if(m_pDoc->isPieceTableChanging())
            m_iPieceTableState++;
      m_pDoc->notifyPieceTableChangeStart();
}

void FV_View::_restorePieceTableState(void)
{
      if(m_iPieceTableState > 0)
      {
            //UT_DEBUGMSG(("notifying PieceTableChange (restore/start) [%d]\n", m_iPieceTableState));
            m_pDoc->notifyPieceTableChangeStart();
            m_iPieceTableState--;
      }
      else
      {
            //UT_DEBUGMSG(("notifying PieceTableChange (restore/end) [%d]\n", m_iPieceTableState));
            m_pDoc->notifyPieceTableChangeEnd();
            m_iPieceTableState =0;
      }
}


void FV_View::_fixInsertionPointAfterRevision()
{
      if(!m_pDoc->isMarkRevisions() && isSelectionEmpty())
      {
            bool bRet;

            // Signal PieceTable Change
            _saveAndNotifyPieceTableChange();

            PT_DocPosition posStart = getPoint();
            PT_DocPosition posEnd = posStart;

            const XML_Char rev[] = "revision";
            const XML_Char val[] = "";
            const XML_Char * attr[3] = {rev,val,NULL};

            bRet = m_pDoc->changeSpanFmt(PTC_RemoveFmt,posStart,posEnd,attr,NULL);

            // Signal piceTable is stable again
            _restorePieceTableState();

            // might need to do general update here; leave it off for now
            // _generalUpdate();
            _fixInsertionPointCoords();
      }
}

bool FV_View::_makePointLegal(void)
{
            bool bOK = true;
            while(!isPointLegal() && bOK)
            {
//
// If we're in an illegal position move forward till we're safe.
//
                  bOK = _charMotion(true,1);
            }
            PT_DocPosition posEnd = 0;
            getEditableBounds(true, posEnd);
            if(posEnd == getPoint() && !isPointLegal())
            {
                  bOK = _charMotion(false,1);
            }
            if(posEnd-1 == getPoint() && !isPointLegal())
            {
                  bOK = _charMotion(false,1);
            }
            if(posEnd-1 == getPoint() && m_pDoc->isEndFrameAtPos(getPoint()) && m_pDoc->isFrameAtPos(getPoint()-1))
            {
                  bOK = _charMotion(false,1);
            }
            while(bOK && !isPointLegal())
            {
                  bOK = _charMotion(false,1);
            }
            return bOK;
}

bool FV_View::_charInsert(const UT_UCSChar * text, UT_uint32 count, bool bForce)
{
      // see if prefs specify we should set language based on kbd layout
      UT_return_val_if_fail(m_pApp, false);
      bool bSetLang = false;
      m_pApp->getPrefsValueBool(static_cast<const XML_Char*>(XAP_PREF_KEY_ChangeLanguageWithKeyboard),
                                            &bSetLang);

      const UT_LangRecord * pLR = NULL;

      if(bSetLang)
            pLR = m_pApp->getKbdLanguage();

      
      bool bResult = true;
      // So this gets rid of the annoying cursor flash at the beginning
      // of the line upon character insertion, but it's the wrong thing to
      // do.  The right thing to do is to either delay calculation, or to
      // not make the wrong number come up; disabling the caret is wrong. -PL
      GR_CaretDisabler caretDisabler(m_pG->getCaret());

      // Signal PieceTable Change
      _saveAndNotifyPieceTableChange();
      bool doInsert = true;

      // Turn off list updates
      m_pDoc->disableListUpdates();
      if (!isSelectionEmpty() && !m_FrameEdit.isActive())
      {
            m_pDoc->beginUserAtomicGlob();
            PP_AttrProp AttrProp_Before;
            _deleteSelection(&AttrProp_Before);
            bool bOK = true;
            if(!isPointLegal() && bOK)
            {
//
// If we're in an illegal position move forward till we're safe.
//
                  bOK = _charMotion(true,1);
            }

            if(pLR)
                  AttrProp_Before.setProperty("lang", pLR->m_szLangCode);
            insertParaBreakIfNeededAtPos(getPoint());
            bResult = m_pDoc->insertSpan(getPoint(), text, count, &AttrProp_Before);
            m_pDoc->endUserAtomicGlob();
      }
      else
      {
            if(m_FrameEdit.isActive())
            {
                  m_FrameEdit.setPointInside();
            }
            bool bOK = true;
            if(!isPointLegal() && bOK)
            {
//
// If we're in an illegal position move forward till we're safe.
//
                  bOK = _charMotion(true,1);
            }
            PT_DocPosition posEnd = 0;
            getEditableBounds(true, posEnd);
            if(posEnd == getPoint() && !isPointLegal())
            {
                  bOK = _charMotion(false,1);
            }
            if(posEnd-1 == getPoint() && !isPointLegal())
            {
                  bOK = _charMotion(false,1);
            }
            if(posEnd-1 == getPoint() && m_pDoc->isEndFrameAtPos(getPoint()) && m_pDoc->isFrameAtPos(getPoint()-1))
            {
                  bOK = _charMotion(false,1);
            }
            bool bOverwrite = (!m_bInsertMode && !bForce);

            if (bOverwrite)
            {
                  // we need to glob when overwriting
                  m_pDoc->beginUserAtomicGlob();
                  cmdCharDelete(true,count);
            }
            if(text[0] == UCS_TAB && count == 1)
            {
                  //
                  // Were inserting a TAB. Handle special case of TAB
                  // right after a list-label combo
                  //
                  if((   isTabListBehindPoint() == true || isTabListAheadPoint() == true)
                      && getCurrentBlock()->isFirstInList() == false)
                  {
                        //
                        // OK now start a sublist of the same type as the
                        // current list if the list type is of numbered type
                        fl_BlockLayout * pBlock = getCurrentBlock();
                        FL_ListType curType = pBlock->getListType();
//
// Now increase list level for bullet lists too
//
                        {
                              UT_uint32 curlevel = pBlock->getLevel();
                              UT_uint32 currID = pBlock->getAutoNum()->getID();
                              curlevel++;
                              fl_AutoNum * pAuto = pBlock->getAutoNum();
                              const XML_Char * pszAlign = pBlock->getProperty("margin-left",true);
                              const XML_Char * pszIndent = pBlock->getProperty("text-indent",true);
                              const XML_Char * pszFieldF = pBlock->getProperty("field-font",true);
                              float fAlign = static_cast<float>(atof(pszAlign));
                              float fIndent = static_cast<float>(atof(pszIndent));
//
// Convert pixels to inches.
//
                              float maxWidthIN = static_cast<float>((static_cast<float>(pBlock->getFirstContainer()->getContainer()->getWidth()))/100. -0.6);
                              if(fAlign + static_cast<float>(LIST_DEFAULT_INDENT) < maxWidthIN)
                              {
                                    fAlign += static_cast<float>(LIST_DEFAULT_INDENT);
                              }
                              pBlock->StartList(curType,pAuto->getStartValue32(),pAuto->getDelim(),pAuto->getDecimal(),pszFieldF,fAlign,fIndent, currID,curlevel);
                              doInsert = false;
                        }
                  }
            }
            
            if (doInsert)
            {
                  if(pLR)
                  {
                        PP_AttrProp AP;
                        AP.setProperty("lang", pLR->m_szLangCode);
                        m_pDoc->insertFmtMark(PTC_AddFmt,getPoint(), &AP);
                  }
                  insertParaBreakIfNeededAtPos(getPoint());
                  bResult = m_pDoc->insertSpan(getPoint(), text, count, NULL);

                  if(!bResult)
                  {
                        const fl_BlockLayout * pBL = getCurrentBlock();
                        const PP_AttrProp *pBlockAP = NULL;
                        pBL->getAP(pBlockAP);

                        bResult = m_pDoc->insertSpan(getPoint(), text, count,
                                                                   const_cast<PP_AttrProp *>(pBlockAP));
                        UT_ASSERT(bResult);
                  }
            }

            if (bOverwrite)
            {
                  m_pDoc->endUserAtomicGlob();
            }
      }
      if(m_FrameEdit.isActive())
      {
            m_FrameEdit.setMode(FV_FrameEdit_NOT_ACTIVE);
      }

      // Signal PieceTable Changes have finished
      _restorePieceTableState();

      _generalUpdate();


      // restore updates and clean up dirty lists
      m_pDoc->enableListUpdates();
      m_pDoc->updateDirtyLists();

      _setPoint(getPoint());
      _fixInsertionPointCoords();
      _ensureInsertionPointOnScreen();
      if(!doInsert)
      {
        notifyListeners(AV_CHG_ALL);
      }
      return bResult;
}

void FV_View::_adjustDeletePosition(UT_uint32 &iDocPos, UT_uint32 &iCount)
{
      // This code deals with character clusters, such as the Thai base character + vowel + tone
      // mark combinations. Basically, if we are asked to delete the base character, we also have
      // to delete any of the other characters from its cluster. We need to find the runs that
      // contain the start and end of the requested delete segment and have them to adjust the
      // delete offsets.
      //

      //
      // Also use this code to deal with attempts to delete across hdrftr 
      // boundaries
      
      fl_BlockLayout * pBlock = NULL;
      pBlock = _findBlockAtPosition(iDocPos);

      UT_return_if_fail( pBlock );

      if(static_cast<UT_uint32>(pBlock->getLength()) <  iDocPos - pBlock->getPosition())
      {
            return;
      }
      
      fp_Run * pRun = pBlock->findRunAtOffset(iDocPos - pBlock->getPosition());
      UT_return_if_fail( pRun );

      UT_uint32 pos1 = iDocPos;
      UT_uint32 iRunOffset = pBlock->getPosition() + pRun->getBlockOffset();
      UT_uint32 iLen = UT_MIN(iCount, pRun->getLength() - (iDocPos - iRunOffset));
      bool bMoreThanOneRun = (iCount > iLen);
            
      // this call modifies both pos1, and iLen
      pRun->adjustDeletePosition(pos1, iLen);
            
      if(bMoreThanOneRun)
      {
            // the deletion spans more than a single run
            // locate the run that contains the last char to be deleted
                  
            UT_uint32 iOrigEndOffset = iDocPos + iCount - 1; // doc offset of the last char to be deleted

            fl_BlockLayout * pEndBlock = _findBlockAtPosition(iOrigEndOffset);
            UT_return_if_fail( pEndBlock );
            if(static_cast<UT_uint32>(pEndBlock->getLength()) <  iOrigEndOffset - pEndBlock->getPosition())
            {
                  return;
            }
            
            fp_Run * pEndRun = pEndBlock->findRunAtOffset(iOrigEndOffset - pEndBlock->getPosition());

            if(!pEndRun)
            {
                  // this happens when there is no real run in the block (e.g., just a fmt mark
                  // and eop
                  return;
            }

            UT_uint32 iEndRunOffset = pEndBlock->getPosition() + pEndRun->getBlockOffset();

            // how much of the deleted sequence is in our run?
            iLen = iDocPos + iCount - iEndRunOffset;
            UT_ASSERT_HARMLESS( iLen <= pEndRun->getLength());
                  
            pEndRun->adjustDeletePosition(iEndRunOffset, iLen);
            UT_DEBUGMSG(("iCount adjusted from %d to %d \n",iCount,iEndRunOffset + iLen - pos1));
            iCount  = iEndRunOffset + iLen - pos1;
      }
      else
      {
            // adjust count
            iCount  = iLen;
      }

      // adjust point
      iDocPos = pos1;
}

Generated by  Doxygen 1.6.0   Back to index