Unfinished Find/Replace code available for grabs (or trashcan)

From: Jesper Skov (jskov_at_zoftcorp.dk)
Date: Wed Dec 03 2003 - 13:01:25 EST

  • Next message: Hubert Figuiere: "Commit (HEAD): icons loading from files"

    Hi

    I had some fun starting to rewrite the find/replace code. The core
    functionality is pretty much done (although some debugging may be
    needed). But now I lack the enthusiasm to complete it.

    Maybe someone here feels like picking up the pieces and completing the
    job?

    Looking at the new code and comparing it to the old code should provide
    you with the incitament. I find it much simpler and easier to understand
    than the old code. And I'm pretty sure it performs better as well. Oh,
    and it will allow programmed access to the find/replace functionality
    (which is why I started the rewrite).

    Some changes to the find/replace dialogs are needed to make the new
    iterator work as intended. Today, all dialogs set the search/replace
    string, even if these do not change. I started to change the GTK dialog
    to only set the strings if the user changed them, but didn't complete
    the task (as I recall). Other front ends need similar changes.

    There may be other stuff that needs to be done to make it all work. I'm
    available for answering questions if anyone should feel like making the
    last necessary changes.

    Cheers,
    Jesper


    ? text/fmt/xp/Untitled1.abw.CRASHED
    Index: text/fmt/xp/GNUmakefile.am
    ===================================================================
    RCS file: /cvsroot/abi/src/text/fmt/xp/GNUmakefile.am,v
    retrieving revision 1.22
    diff -u -p -u -5 -p -r1.22 GNUmakefile.am
    --- text/fmt/xp/GNUmakefile.am 30 Nov 2003 14:23:10 -0000 1.22
    +++ text/fmt/xp/GNUmakefile.am 3 Dec 2003 17:36:43 -0000
    @@ -23,12 +23,13 @@ INCLUDES= $(TEXT_INCLUDES) $(WP_INCLUDES
     noinst_LIBRARIES = libFmt.a
     
     libFmt_a_SOURCES= \
             fv_View.cpp \
             fv_View_cmd.cpp \
    - fv_View_protected.cpp \
    + fv_View_protected.cpp \
             fv_View_TestRoutines.cpp \
    + fv_SearchReplace.cpp \
             fb_LineBreaker.cpp \
             fb_Alignment.cpp \
             fl_BlockLayout.cpp \
             fl_Squiggles.cpp \
             fl_DocLayout.cpp \
    Index: text/fmt/xp/fv_SearchReplace.cpp
    ===================================================================
    RCS file: text/fmt/xp/fv_SearchReplace.cpp
    diff -N text/fmt/xp/fv_SearchReplace.cpp
    --- text/fmt/xp/fv_SearchReplace.cpp 1 Jan 1970 00:00:00 -0000
    +++ text/fmt/xp/fv_SearchReplace.cpp 3 Dec 2003 17:36:44 -0000
    @@ -0,0 +1,578 @@
    +/* AbiWord
    + * Copyright (C) 2003 Jesper Skov
    + *
    + * 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 "fv_View.h"
    +#include "fv_SearchReplace.h"
    +
    +#include "ut_debugmsg.h"
    +#include "ut_assert.h"
    +#include "ut_string.h"
    +
    +/*! \page searchreplace_overview Search and Replace
    +
    +The search/replace code is using a one-way iterator to work its way
    +through the document looking for matches. The (forward and backward)
    +iterators take care of skipping to the next block when required.
    +
    +*/
    +
    +/******************************
    +Problems in old code:
    +
    + o New doc, "test test|", reverse search for "test" causes assertion
    + and crash!
    + o Reverse find does not find word at start of line
    + o Reverse find does not find word at end of line
    + o (untested) Reverse replace should not stop at correct position
    +
    +Problems in new code:
    +
    + o last block, start pos > than maxOffset -> will never terminate
    + o dialog needs to set new pos -> new iterator -> old iterator starting point/wrap state is lost
    + (see ap_Dialog_Replace.cpp/h + ap_UnixDialog_Replace.cpp/h)
    +
    +
    +*******************************/
    +
    +/*!
    + Find next occurrence of string in document and select it.
    + \return true if a match was found, otherwise false.
    +*/
    +bool
    +fv_SearchReplace::findAndSelect(void)
    +{
    + if (!find()) return false;
    +
    + fv_SearchReplaceBufferIterator* ix = getIterator();
    + FV_View* pView = getView();
    + pView->_setPoint(ix->getPos());
    + pView->_setSelectionAnchor();
    + pView->_charMotion(!isReverse(), getSearchStrLen());
    +
    + return true;
    +}
    +
    +/*!
    + Find next occurrence of string in document.
    + \return true if match was found, otherwise false.
    +*/
    +bool
    +fv_SearchReplace::find(void)
    +{
    + _setFound(false);
    +
    + const UT_UCSChar* pSearch = getSearchString();
    + const UT_sint32 iSearchLen = getSearchStrLen();
    + const UT_UCSChar firstChar = pSearch[0];
    +
    + fv_SearchReplaceBufferIterator* ix = getIterator();
    + UT_UCSChar currentChar;
    + // First find match on first character of string
    +
    + if (!ix->getCurrentChar(currentChar))
    + {
    + return false;
    + }
    +
    + do
    + {
    + if (currentChar == UCS_RQUOTE) currentChar = '\'';
    + if (!isCaseSensitive()) currentChar = UT_UCS4_tolower(currentChar);
    +
    + if (currentChar != firstChar) continue;
    +
    + // Then try to match of the remainder of the string
    + UT_sint32 iMatchLen = 1;
    + while (iMatchLen < iSearchLen)
    + {
    + currentChar = ix->getAtOffset(iMatchLen);
    + if (currentChar == UCS_RQUOTE) currentChar = '\'';
    + if (!isCaseSensitive()) currentChar = UT_UCS4_tolower(currentChar);
    +
    + if (currentChar != pSearch[iMatchLen]) break;
    +
    + iMatchLen++;
    + }
    +
    + if (iMatchLen == iSearchLen)
    + {
    + if (!isWholeWord()
    + || (UT_isWordDelimiter(ix->getAtOffset(-1), UCS_UNKPUNK, UCS_UNKPUNK)
    + && UT_isWordDelimiter(ix->getAtOffset(iMatchLen), UCS_UNKPUNK, UCS_UNKPUNK)))
    + {
    + _setFound(true);
    + return true;
    + }
    + }
    + } while (ix->step(currentChar));
    +
    + return false;
    +}
    +
    +
    +/*!
    + Find and replace text string
    + \return True if string was replaced, false otherwise
    +
    + This function will replace an existing selection from a previous find
    + and make a new find.
    +*/
    +bool
    +fv_SearchReplace::findReplaceAndSelect(void)
    +{
    + UT_ASSERT(getReplaceString());
    + FV_View* pView = getView();
    +
    + bool bRes = false;
    +
    + pView->_saveAndNotifyPieceTableChange();
    + pView->getDocument()->beginUserAtomicGlob();
    +
    + // Replace selection if it's due to a find operation
    + if (isFound() && !pView->isSelectionEmpty())
    + {
    + bRes = true;
    +
    + PP_AttrProp AttrProp_Before;
    +
    + pView->_deleteSelection(&AttrProp_Before);
    +
    + // If we have a string with length, do an insert, else let it
    + // hang from the delete above
    + if (getReplaceStrLen() > 0)
    + {
    + bRes = pView->getDocument()->insertSpan(pView->getPoint(), getReplaceString(),
    + getReplaceStrLen(), &AttrProp_Before);
    + }
    +
    + pView->_generalUpdate();
    +
    + // Notify iterator that a replace was made
    + replaceAdjust();
    + }
    +
    + pView->getDocument()->endUserAtomicGlob();
    + pView->_restorePieceTableState();
    +
    + // Find next occurrence in document
    + findAndSelect();
    +
    + return bRes;
    +}
    +
    +//----------------------------------------------------------------------
    +// Buffer iterator
    +
    +/*!
    + Factory creating a forward/reverse iterator starting at the
    + specified start position.
    +
    + \param pPS Search replace properties
    + \param iStartPos Start position in document
    + \return Buffer iterator
    +*/
    +fv_SearchReplaceBufferIterator*
    +fv_SearchReplaceBufferIterator::create(
    + fv_SearchReplace* pPS, PT_DocPosition iStartPos)
    +{
    + fv_SearchReplaceBufferIterator* it;
    + if (pPS->isReverse())
    + {
    + it = new fv_SearchReplaceBufferIteratorReverse(pPS, iStartPos);
    + }
    + else
    + {
    + it = new fv_SearchReplaceBufferIteratorForward(pPS, iStartPos);
    + }
    + return it;
    +}
    +
    +/*!
    + Buffer iterator constructor.
    + \param pPS Search replace properties
    + \param iStartPos Start position in document
    +*/
    +fv_SearchReplaceBufferIterator::fv_SearchReplaceBufferIterator(
    + fv_SearchReplace* pPS, PT_DocPosition iStartPos)
    + : m_bFinished(false), m_bWrappedEnd(false), m_pBlock(NULL),
    + m_iMaxOffset(0), m_pBuffer(NULL),
    + m_iBufferLen(0), m_iBlockOffsetAdjustment(0)
    +{
    + // find first block
    + m_pPS = pPS;
    + m_iIteratorStartPos = iStartPos;
    + _setActiveBlock(pPS->getView()->_findBlockAtPosition(iStartPos));
    + m_iOffset = iStartPos - m_pBlock->getPosition(false);
    +}
    +
    +fv_SearchReplaceBufferIterator::~fv_SearchReplaceBufferIterator(void)
    +{
    + FREEP(m_pBuffer);
    +}
    +
    +/*!
    + Set block as active for buffer iterator.
    + This will get the character data from the block.
    +
    + \param pBlock New block
    + \return true if successful, false if allocation failed for some
    + reason
    +*/
    +bool
    +fv_SearchReplaceBufferIterator::_setActiveBlock(fl_BlockLayout* pBlock)
    +{
    + m_pBlock = pBlock;
    + m_iBlockOffsetAdjustment = 0;
    +
    + // Free existing buffer
    + FREEP(m_pBuffer);
    +
    + // Get content of block copied to the buffer
    + UT_GrowBuf pBuffer;
    + if (!pBlock->getBlockBuf(&pBuffer))
    + {
    + UT_DEBUGMSG(("Block %p has no buffer.\n", pBlock));
    + UT_ASSERT(0);
    + return false;
    + }
    + m_iBufferLen = pBuffer.getLength();
    + m_pBuffer = static_cast<UT_UCSChar*>(UT_calloc(m_iBufferLen + 1, sizeof(UT_UCSChar)));
    + if (NULL == m_pBuffer)
    + {
    + UT_ASSERT(0);
    + return false;
    + }
    + memmove(m_pBuffer, pBuffer.getPointer(0), (m_iBufferLen) * sizeof(UT_UCSChar));
    +
    + // Max offset where a match can happen
    + m_iMaxOffset = m_iBufferLen - m_pPS->getSearchStrLen();
    +
    + return true;
    +}
    +
    +/*!
    + Get next character in the search direction.
    +
    + When getting so close to a block boundary that there cannot be made
    + a match with the search string, it skips to the next buffer.
    +
    + \result c Found character.
    + \return true until the entire document has been stepped through,
    + then false is returned.
    +*/
    +bool
    +fv_SearchReplaceBufferIterator::step(UT_UCSChar& c)
    +{
    + // This check makes it safe to call the method after we've
    + // terminated.
    + if (isDocumentComplete()) return false;
    + _next();
    + if (isDocumentComplete()) return false;
    +
    + while (_isBlockComplete())
    + {
    + fl_BlockLayout* pNextBlock = _getNextBlock();
    + if (!_setActiveBlock(pNextBlock))
    + {
    + return false;
    + }
    +
    + m_iOffset = _getBlockStartOffset();
    +
    + // We have to check for completion here as well, or a document
    + // of all too-short blocks would cause an infinite loop
    + if (isDocumentComplete()) return false;
    + }
    + c = m_pBuffer[m_iOffset];
    + return true;
    +}
    +
    +/*!
    + Get current character
    +
    + \result c Found character.
    + \return true until the entire document has been stepped through,
    + then false is returned.
    +*/
    +bool
    +fv_SearchReplaceBufferIterator::getCurrentChar(UT_UCSChar& c)
    +{
    + if (isDocumentComplete()) return false;
    + c = m_pBuffer[m_iOffset];
    + return true;
    +}
    +
    +/*!
    + Gets char at offset from current search position.
    +
    + \param iOffset Offset to get char from
    + \return Found character, or UCS_UNKPUNK if offset is outside the
    + legal range.
    +*/
    +UT_UCSChar
    +fv_SearchReplaceBufferIterator::getAtOffset(UT_sint32 iOffset)
    +{
    + UT_sint32 i = iOffset + m_iOffset;
    + if (i < 0 || i > (UT_sint32)m_iMaxOffset)
    + {
    + return UCS_UNKPUNK;
    + }
    + return m_pBuffer[i];
    +}
    +
    +/*!
    + Reverse buffer iterator constructor.
    +
    + \param pPS Search replace properties
    + \param iStartPos Start position in document
    +*/
    +fv_SearchReplaceBufferIteratorReverse::fv_SearchReplaceBufferIteratorReverse(
    + fv_SearchReplace* pPS, PT_DocPosition iStartPos)
    + : fv_SearchReplaceBufferIterator(pPS, iStartPos)
    +{
    +}
    +
    +/*!
    + Reverse buffer iterator destructor
    +*/
    +fv_SearchReplaceBufferIteratorReverse::~fv_SearchReplaceBufferIteratorReverse()
    +{
    +}
    +
    +/*!
    + Get next block in reverse direction.
    +
    + \return Next block in reverse direction.
    +*/
    +fl_BlockLayout*
    +fv_SearchReplaceBufferIteratorReverse::_getNextBlock(void)
    +{
    + // Get previous block
    + fl_BlockLayout* pPrevBlock = m_pBlock->getPrevBlockInDocument();
    + if (NULL == pPrevBlock)
    + {
    + UT_ASSERT(!m_bWrappedEnd);
    + // Wrapped, get the last block of the document
    + PT_DocPosition endOfDoc;
    + m_pPS->getView()->getEditableBounds(true, endOfDoc);
    + pPrevBlock = m_pBlock->getDocLayout()->findBlockAtPositionReverse(endOfDoc);
    + m_bWrappedEnd = true;
    + UT_ASSERT(pPrevBlock);
    + }
    + return pPrevBlock;
    +}
    +
    +/*!
    + Called when a replacement has happened.
    +
    + \return Always returns true as there's no effect on search position
    + when doing reverse search
    +*/
    +bool
    +fv_SearchReplaceBufferIteratorReverse::replaceAdjust(void)
    +{
    + if (isDocumentComplete()) return false;
    +
    + // Make sure we stop at the right location by adjusting the start
    + // location if changes are made above it in the document
    + // (i.e. before the wrap).
    + if (!isWrapped())
    + {
    + m_iIteratorStartPos += m_pPS->getReplaceStrLen();
    + m_iIteratorStartPos -= m_pPS->getSearchStrLen();
    + }
    +
    + return true;
    +}
    +
    +/*!
    + Forward buffer iterator constructor.
    +
    + \param pPS Search replace properties
    + \param iStartPos Start position in document
    +*/
    +fv_SearchReplaceBufferIteratorForward::fv_SearchReplaceBufferIteratorForward(
    + fv_SearchReplace* pPS, PT_DocPosition iStartPos)
    + : fv_SearchReplaceBufferIterator(pPS, iStartPos)
    +{
    +}
    +
    +/*!
    + Forward buffer iterator destructor
    +*/
    +fv_SearchReplaceBufferIteratorForward::~fv_SearchReplaceBufferIteratorForward()
    +{
    +}
    +
    +/*!
    + Get next block in forward direction.
    +
    + \return Next block in forward direction.
    +*/
    +fl_BlockLayout*
    +fv_SearchReplaceBufferIteratorForward::_getNextBlock(void)
    +{
    + // Get next block
    + fl_BlockLayout* pNextBlock = m_pBlock->getNextBlockInDocument();
    + if (NULL == pNextBlock)
    + {
    + // We're going to wrap - if the starting point lies beyond the
    + // current position (i.e. is getting skipped) we're done //FIXME check with len=1 str
    + if (getPos() <= m_iIteratorStartPos)
    + {
    + m_bFinished = true;
    + }
    +
    + UT_ASSERT(!m_bWrappedEnd);
    + // Wrapped, get the first block of the document
    + PT_DocPosition startOfDoc;
    + m_pPS->getView()->getEditableBounds(false, startOfDoc);
    + pNextBlock = m_pBlock->getDocLayout()->findBlockAtPosition(startOfDoc);
    + m_bWrappedEnd = true;
    + UT_ASSERT(pNextBlock);
    + }
    + return pNextBlock;
    +}
    +
    +/*!
    + Called when a replacement has happened.
    +
    + \return false if the replace caused the search to complete, otherwise true
    +*/
    +bool
    +fv_SearchReplaceBufferIteratorForward::replaceAdjust(void)
    +{
    + if (isDocumentComplete()) return false;
    +
    + // Skip match length - the match string has been replaced with
    + // something else, so get to the point where we know our buffer
    + // matches the document.
    + for (UT_sint32 i = 0, iMax = m_pPS->getSearchStrLen(); i < iMax; i++)
    + {
    + UT_UCSChar ignored;
    + if (!step(ignored))
    + {
    + return false;
    + }
    + }
    +
    + // So some chars were skipped, but the iterator's idea of its
    + // block position is still wrong - fix it by adjusting the block
    + // offset.
    + UT_sint32 iDelta = m_pPS->getReplaceStrLen() - m_pPS->getSearchStrLen();
    + m_iBlockOffsetAdjustment += iDelta;
    +
    + if (isDocumentComplete()) return false;
    +
    + // Make sure we stop at the right location by adjusting the start
    + // location if changes are made above it in the document
    + // (i.e. after the wrap).
    + if (isWrapped())
    + {
    + m_iIteratorStartPos += iDelta;
    + }
    +
    + return true;
    +}
    +
    +//----------------------------------------------------------------------
    +// Parameter stuff
    +
    +/*!
    + Search/Replace constructor.
    +
    + \param pView View
    + \param pFind String to find.
    + \param pReplace String to replace with.
    + \param iStartPos Start position in document
    + \param bReverse True to make reverse find/replace
    + \param bWholeWord If find must match a full word
    + \param bMatchCase If find must match the case of the word
    +*/
    +
    +fv_SearchReplace::fv_SearchReplace(FV_View* pView,
    + const UT_UCSChar* pFind,
    + const UT_UCSChar* pReplace,
    + PT_DocPosition iStartPos,
    + bool bReverse, bool bWholeWord,
    + bool bMatchCase)
    + : m_pView(pView),
    + m_iStartPosition(iStartPos),
    + m_bReverseFind(bReverse), m_bWholeWord(bWholeWord),
    + m_bMatchCase(bMatchCase), m_bFound(false),
    + m_sReplace(NULL), m_iReplaceLen(0)
    +{
    + UT_ASSERT(pFind);
    +
    + m_iSearchLen = UT_UCS4_strlen(pFind);
    + m_sSearch = _cloneSearchString(pFind);
    +
    + if (pReplace)
    + {
    + m_iReplaceLen = UT_UCS4_strlen(pReplace);
    + m_sReplace = _cloneSearchString(pReplace);
    + }
    +
    + m_pIterator = fv_SearchReplaceBufferIterator::create(this,
    + iStartPos);
    +}
    +
    +/*!
    + Destructor
    +*/
    +fv_SearchReplace::~fv_SearchReplace(void)
    +{
    + FREEP(m_sSearch);
    + FREEP(m_sReplace);
    + DELETEP(m_pIterator);
    +}
    +
    +/*!
    + Clone the search string, converting it to lowercase if search
    + should ignore case.
    +
    + \param String
    + \return Cloned, possibly lower-cased, string. Caller must free this
    + string.
    +*/
    +UT_UCSChar*
    +fv_SearchReplace::_cloneSearchString(const UT_UCSChar* pFind) const
    +{
    + UT_UCSChar* pCloneStr = (UT_UCSChar*) UT_calloc(m_iSearchLen,
    + sizeof(UT_UCSChar));
    + UT_ASSERT(pCloneStr);
    + if (!pCloneStr)
    + return NULL;
    + if (m_bMatchCase)
    + {
    + for (UT_sint32 j = 0; j < m_iSearchLen; j++)
    + {
    + pCloneStr[j] = pFind[j];
    + }
    + }
    + else
    + {
    + for (UT_sint32 j = 0; j < m_iSearchLen; j++)
    + {
    + pCloneStr[j] = UT_UCS4_tolower(pFind[j]);
    + }
    + }
    +
    + return pCloneStr;
    +}
    Index: text/fmt/xp/fv_SearchReplace.h
    ===================================================================
    RCS file: text/fmt/xp/fv_SearchReplace.h
    diff -N text/fmt/xp/fv_SearchReplace.h
    --- text/fmt/xp/fv_SearchReplace.h 1 Jan 1970 00:00:00 -0000
    +++ text/fmt/xp/fv_SearchReplace.h 3 Dec 2003 17:36:44 -0000
    @@ -0,0 +1,175 @@
    +/* AbiWord
    + * Copyright (C) 2003 Jesper Skov
    + *
    + * 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.
    + */
    +
    +#ifndef FL_SEARCHREPLACE_H
    +#define FL_SEARCHREPLACE_H
    +
    +#include "fl_BlockLayout.h"
    +
    +
    +class ABI_EXPORT fv_SearchReplace;
    +
    +class ABI_EXPORT fv_SearchReplaceBufferIterator
    +{
    +public:
    + bool step(UT_UCSChar& c);
    + bool getCurrentChar(UT_UCSChar& c);
    + UT_UCSChar getAtOffset(UT_sint32 iOffset);
    + inline PT_DocPosition getPos(void) const { return m_pBlock->getPosition(false) + m_iOffset + m_iBlockOffsetAdjustment; }
    +
    + static fv_SearchReplaceBufferIterator* create(fv_SearchReplace* pPS,
    + PT_DocPosition iStartPos);
    +
    + inline bool isWrapped(void) const { return m_bWrappedEnd; }
    + virtual bool replaceAdjust(void) = 0;
    +
    + virtual inline bool isDocumentComplete(void) { return true; };
    +
    + fv_SearchReplaceBufferIterator(fv_SearchReplace* pPS,
    + PT_DocPosition iStartPos);
    + virtual ~fv_SearchReplaceBufferIterator(void);
    +
    +protected:
    + bool _setActiveBlock(fl_BlockLayout* pBlock);
    +
    + virtual void _next(void) = 0;
    + virtual bool _isBlockComplete(void) const = 0;
    + virtual UT_sint32 _getBlockStartOffset(void) const = 0;
    + virtual fl_BlockLayout* _getNextBlock(void) = 0;
    +
    + bool m_bFinished;
    + const fv_SearchReplace* m_pPS;
    + bool m_bWrappedEnd;
    + fl_BlockLayout* m_pBlock;
    + PT_DocPosition m_iOffset;
    + PT_DocPosition m_iMaxOffset;
    + PT_DocPosition m_iIteratorStartPos;
    + UT_UCSChar* m_pBuffer;
    + UT_sint32 m_iBufferLen;
    + // During replace operations, the offset in the search buffer may
    + // come out of sync with that of the document block. This
    + // adjustment makes of for that discrepancy.
    + UT_sint32 m_iBlockOffsetAdjustment;
    +};
    +
    +
    +class ABI_EXPORT fv_SearchReplaceBufferIteratorForward
    + : public fv_SearchReplaceBufferIterator
    +{
    +public:
    + fv_SearchReplaceBufferIteratorForward(fv_SearchReplace* pPS,
    + PT_DocPosition iStartPos);
    + virtual ~fv_SearchReplaceBufferIteratorForward(void);
    +
    + bool replaceAdjust(void);
    +
    + // Note that m_bFinished can also get set in the forward
    + // iterator's _getNextBlock
    + inline bool isDocumentComplete(void) {
    + return (m_bFinished = m_bFinished ||
    + (m_bWrappedEnd && getPos() >= m_iIteratorStartPos)); }
    +
    +protected:
    + inline void _next(void) { m_iOffset++; }
    + inline bool _isBlockComplete(void) const { return (m_iOffset > m_iMaxOffset); }
    + inline UT_sint32 _getBlockStartOffset(void) const { return 0; }
    + fl_BlockLayout* _getNextBlock(void);
    +};
    +
    +
    +class ABI_EXPORT fv_SearchReplaceBufferIteratorReverse
    + : public fv_SearchReplaceBufferIterator
    +{
    +public:
    + fv_SearchReplaceBufferIteratorReverse(fv_SearchReplace* pPS,
    + PT_DocPosition iStartPos);
    + virtual ~fv_SearchReplaceBufferIteratorReverse(void);
    +
    + bool replaceAdjust(void);
    +
    + inline bool isDocumentComplete(void) {
    + return (m_bFinished = m_bFinished ||
    + (m_bWrappedEnd && getPos() <= m_iIteratorStartPos)); }
    +
    +protected:
    + inline void _next(void) { m_iOffset--; }
    + inline bool _isBlockComplete(void) const {
    + return (m_iOffset < 0); }
    + inline UT_sint32 _getBlockStartOffset(void) const {
    + return m_iMaxOffset; }
    + fl_BlockLayout* _getNextBlock(void);
    +};
    +
    +class ABI_EXPORT fv_SearchReplace
    +{
    +public:
    + fv_SearchReplace(FV_View* pView,
    + const UT_UCSChar *pFind,
    + const UT_UCSChar *pReplace,
    + PT_DocPosition iStartPos,
    + bool bReverse, bool bWholeWord,
    + bool bMatchCase);
    + ~fv_SearchReplace(void);
    +
    + inline bool isForward(void) const { return !m_bReverseFind; }
    + inline bool isReverse(void) const { return m_bReverseFind; }
    + inline bool isCaseSensitive(void) const { return m_bMatchCase; }
    + inline bool isFound(void) const { return m_bFound; }
    + inline bool isDocumentComplete(void) const {
    + return getIterator()->isDocumentComplete(); }
    + inline bool isWholeWord(void) const { return m_bWholeWord; }
    +
    + inline const UT_UCSChar* getReplaceString(void) const { return m_sReplace; }
    + inline const UT_UCSChar* getSearchString(void) const { return m_sSearch; }
    +
    + inline int getReplaceStrLen(void) const { return m_iReplaceLen; }
    + inline int getSearchStrLen(void) const { return m_iSearchLen; }
    +
    + inline FV_View* getView(void) const { return m_pView; }
    +
    + inline fv_SearchReplaceBufferIterator* getIterator(void) const {
    + return m_pIterator; }
    +
    + inline bool replaceAdjust(void) const {
    + return getIterator()->replaceAdjust(); }
    +
    + bool find(void);
    + bool findAndSelect(void);
    + bool findReplaceAndSelect(void);
    +
    +private:
    + UT_UCSChar* _cloneSearchString(const UT_UCSChar* pFind) const;
    + inline void _setFound(bool b) { m_bFound = b; }
    +
    + fv_SearchReplaceBufferIterator* m_pIterator;
    +
    + FV_View* m_pView;
    +
    + PT_DocPosition m_iStartPosition;
    + bool m_bReverseFind;
    + bool m_bWholeWord;
    + bool m_bMatchCase;
    + bool m_bFound;
    + UT_UCSChar * m_sSearch;
    + UT_UCSChar * m_sReplace;
    + UT_sint32 m_iSearchLen;
    + UT_sint32 m_iReplaceLen;
    +};
    +
    +#endif /* FL_SEARCHREPLACE_H */
    Index: text/fmt/xp/fv_View.cpp
    ===================================================================
    RCS file: /cvsroot/abi/src/text/fmt/xp/fv_View.cpp,v
    retrieving revision 1.856
    diff -u -p -u -5 -p -r1.856 fv_View.cpp
    --- text/fmt/xp/fv_View.cpp 2 Dec 2003 14:02:44 -0000 1.856
    +++ text/fmt/xp/fv_View.cpp 3 Dec 2003 17:36:58 -0000
    @@ -32,10 +32,11 @@
     #include "ut_bytebuf.h"
     #include "ut_timer.h"
     
     #include "xav_View.h"
     #include "fv_View.h"
    +#include "fv_SearchReplace.h"
     #include "fl_DocLayout.h"
     #include "fl_BlockLayout.h"
     #include "fl_Squiggles.h"
     #include "fl_SectionLayout.h"
     #include "fl_FootnoteLayout.h"
    @@ -5269,20 +5270,20 @@ bool
     FV_View::findNext(bool& bDoneEntireDocument)
     {
             if ((m_startPosition >=0) && (m_startPosition <2)) {
                     m_startPosition = 2;
                     setPoint(m_startPosition);
    - }
    + }
     
             if (!isSelectionEmpty())
             {
                     _clearSelection();
             }
     
    - UT_uint32* pPrefix = _computeFindPrefix(m_sFind);
    - bool bRes = _findNext(pPrefix, bDoneEntireDocument);
    - FREEP(pPrefix);
    + bool bRes = _getFindParams()->findAndSelect();
    + bDoneEntireDocument = _getFindParams()->isDocumentComplete();
    +
     
             if (isSelectionEmpty())
             {
                     _updateInsertionPoint();
             }
    @@ -5290,47 +5291,25 @@ FV_View::findNext(bool& bDoneEntireDocum
             {
                     _ensureInsertionPointOnScreen();
                     _drawSelection();
             }
     
    - // TODO do we need to do a notifyListeners(AV_CHG_MOTION) -- yes
             notifyListeners(AV_CHG_MOTION);
             return bRes;
     }
     
     
     bool
     FV_View::findPrev(const UT_UCSChar* pFind, bool& bDoneEntireDocument)
     {
    - findSetFindString(pFind);
    - return findPrev(bDoneEntireDocument);
    + return findNext(pFind, bDoneEntireDocument);
     }
     
     bool
     FV_View::findPrev(bool& bDoneEntireDocument)
     {
    - if (!isSelectionEmpty())
    - {
    - _clearSelection();
    - }
    -
    - UT_uint32* pPrefix = _computeFindPrefix(m_sFind);
    - bool bRes = _findPrev(pPrefix, bDoneEntireDocument);
    - FREEP(pPrefix);
    -
    - if (isSelectionEmpty())
    - {
    - _updateInsertionPoint();
    - }
    - else
    - {
    - _ensureInsertionPointOnScreen();
    - _drawSelection();
    - }
    -
    - notifyListeners(AV_CHG_MOTION);
    - return bRes;
    + return findNext(bDoneEntireDocument);
     }
     
     /*!
      Find operation reset
     
    @@ -5339,21 +5318,21 @@ FV_View::findPrev(bool& bDoneEntireDocum
     */
     void
     FV_View::findSetStartAtInsPoint(void)
     {
             m_startPosition = m_iInsPoint;
    - m_wrappedEnd = false;
    - m_doneFind = false;
    + _deleteFindParams();
     }
     
     
     
     void
     FV_View::findSetFindString(const UT_UCSChar* pFind)
     {
             FREEP(m_sFind);
             UT_UCS4_cloneString(&m_sFind, pFind);
    + _deleteFindParams();
     }
     
     UT_UCSChar *
     FV_View::findGetFindString(void)
     {
    @@ -5375,10 +5354,11 @@ FV_View::findGetFindString(void)
     void
     FV_View::findSetReplaceString(const UT_UCSChar* pReplace)
     {
             FREEP(m_sReplace);
             UT_UCS4_cloneString(&m_sReplace, pReplace);
    + _deleteFindParams();
     }
     
     UT_UCSChar *
     FV_View::findGetReplaceString(void)
     {
    @@ -5399,10 +5379,11 @@ FV_View::findGetReplaceString(void)
     
     void
     FV_View::findSetReverseFind (bool newValue)
     {
             m_bReverseFind = newValue;
    + _deleteFindParams();
     }
     
     bool
     FV_View::findGetReverseFind ()
     {
    @@ -5411,10 +5392,11 @@ FV_View::findGetReverseFind ()
     
     void
     FV_View::findSetMatchCase(bool newValue)
     {
             m_bMatchCase = newValue;
    + _deleteFindParams();
     }
     
     bool
     FV_View::findGetMatchCase()
     {
    @@ -5423,10 +5405,11 @@ FV_View::findGetMatchCase()
     
     void
     FV_View::findSetWholeWord(bool newValue)
     {
             m_bWholeWord = newValue;
    + _deleteFindParams();
     }
     
     bool
     FV_View::findGetWholeWord()
     {
    @@ -5443,20 +5426,11 @@ FV_View::findAgain(void)
     {
             bool bRes = false;
     
             if (m_sFind && *m_sFind)
             {
    - bool bTmp;
    - if (m_bReverseFind)
    - {
    - bRes = findPrev(bTmp);
    - }
    - else
    - {
    - bRes = findNext(bTmp);
    - }
    -
    + bRes = _getFindParams()->findAndSelect();
                     if (bRes)
                     {
                             _drawSelection();
                     }
             }
    @@ -5467,13 +5441,12 @@ FV_View::findAgain(void)
     bool
     FV_View::findReplaceReverse(bool& bDoneEntireDocument)
     {
             UT_ASSERT(m_sFind && m_sReplace);
     
    - UT_uint32* pPrefix = _computeFindPrefix(m_sFind);
    - bool bRes = _findReplaceReverse(pPrefix, bDoneEntireDocument);
    - FREEP(pPrefix);
    + bool bRes = _getFindParams()->findReplaceAndSelect();
    + bDoneEntireDocument = _getFindParams()->isDocumentComplete();
     
             updateScreen();
     
             if (isSelectionEmpty())
             {
    @@ -5498,36 +5471,15 @@ FV_View::findReplaceReverse(bool& bDoneE
      \return True if string was replaced, false otherwise
     */
     bool
     FV_View::findReplace(bool& bDoneEntireDocument)
     {
    - UT_ASSERT(m_sFind && m_sReplace);
    -
    - UT_uint32* pPrefix = _computeFindPrefix(m_sFind);
    - bool bRes = _findReplace(pPrefix, bDoneEntireDocument);
    - FREEP(pPrefix);
    -
    - updateScreen();
    -
    - if (isSelectionEmpty())
    - {
    - _updateInsertionPoint();
    - }
    - else
    - {
    - _ensureInsertionPointOnScreen();
    - _drawSelection();
    - }
    -
    - return bRes;
    + return findReplaceReverse(bDoneEntireDocument);
     }
     
     /*!
      Find and replace all occurrences of string
    - \param pFind String to find
    - \param pReplace String to replace it with
    - \param bMatchCase True to match case, false to ignore case
      \return Number of replacements made
     */
     UT_uint32
     FV_View::findReplaceAll()
     {
    @@ -5537,25 +5489,19 @@ FV_View::findReplaceAll()
             if ((m_startPosition >=0) && (m_startPosition <2))
             {
                     m_startPosition = 2;
             }
     
    - bool bDoneEntireDocument = false;
    -
    - // Compute search prefix
    - UT_uint32* pPrefix = _computeFindPrefix(m_sFind);
    -
    - // Prime with the first match - _findReplace is really
    + // Prime with the first match - findReplaceAndSelect is really
             // replace-then-find.
    - _findNext(pPrefix, bDoneEntireDocument);
    -
    - // Keep replacing until the end of the buffer is hit
    - while (!bDoneEntireDocument)
    - {
    - _findReplace(pPrefix, bDoneEntireDocument);
    - iReplaced++;
    - }
    + if (_getFindParams()->find())
    + {
    + while (_getFindParams()->findReplaceAndSelect())
    + {
    + iReplaced++;
    + }
    + }
     
             m_pDoc->endUserAtomicGlob();
     
             _generalUpdate();
     
    @@ -5566,20 +5512,31 @@ FV_View::findReplaceAll()
             else
             {
                     _ensureInsertionPointOnScreen();
             }
     
    - FREEP(pPrefix);
             return iReplaced;
     }
     
    +fv_SearchReplace*
    +FV_View::_getFindParams(void)
    +{
    + if (!m_pPS)
    + {
    + m_pPS = new fv_SearchReplace(this, m_sFind, m_sReplace, m_startPosition,
    + m_bReverseFind, m_bWholeWord, m_bMatchCase);
    + }
    + return m_pPS;
    +}
     
    -fl_BlockLayout*
    -FV_View::getCurrentBlock(void)
    +void
    +FV_View::_deleteFindParams(void)
     {
    - return _findGetCurrentBlock();
    + DELETEP(m_pPS);
     }
    +
    +//--------------------------------------------------------------------------
     
     void FV_View::insertSymbol(UT_UCSChar c, XML_Char * symfont)
     {
     
             // First check to see if there is a selection already.
    Index: text/fmt/xp/fv_View.h
    ===================================================================
    RCS file: /cvsroot/abi/src/text/fmt/xp/fv_View.h,v
    retrieving revision 1.303
    diff -u -p -u -5 -p -r1.303 fv_View.h
    --- text/fmt/xp/fv_View.h 2 Dec 2003 14:02:44 -0000 1.303
    +++ text/fmt/xp/fv_View.h 3 Dec 2003 17:37:00 -0000
    @@ -64,10 +64,12 @@ class fl_AutoNum;
     class fp_PageSize;
     class AP_TopRuler;
     class AP_LeftRuler;
     class SpellChecker;
     class FV_Caret_Listener;
    +class fv_SearchReplace;
    +class fv_SearchReplaceBufferIterator;
     
     typedef enum _FVDocPos
     {
             FV_DOCPOS_BOB, FV_DOCPOS_EOB, // block
             FV_DOCPOS_BOD, FV_DOCPOS_EOD, // document
    @@ -176,10 +178,12 @@ class ABI_EXPORT FV_View : public AV_Vie
             friend class fl_DocListener;
             friend class fl_BlockLayout;
             friend class FL_DocLayout;
             friend class fl_Squiggles;
             friend class fl_DocSectionLayout;
    + friend class fv_SearchReplace;
    + friend class fv_SearchReplaceBufferIterator;
             friend class GR_Caret;
             friend class FV_FrameEdit;
             friend class FV_VisualDragText;
             friend class FV_Selection;
     public:
    @@ -253,11 +257,11 @@ public:
     
     // ----------------------
             FL_DocLayout* getLayout() const;
             UT_uint32 getCurrentPageNumForStatusBar(void) const;
             fp_Page* getCurrentPage(void) const;
    - fl_BlockLayout* getCurrentBlock(void);
    + fl_BlockLayout* getCurrentBlock(void) const { return _findBlockAtPosition(m_iInsPoint); }
     
             void draw(int page, dg_DrawArgs* da);
     
             // TODO some of these functions should move into protected
     
    @@ -469,30 +473,14 @@ public:
             void findSetStartAtInsPoint(void);
     
             bool findNext(bool& bDoneEntireDocument);
             bool findNext(const UT_UCSChar* pFind, bool& bDoneEntireDocument);
             
    - UT_uint32* _computeFindPrefix(const UT_UCSChar* pFind);
    -
    - bool _findNext(UT_uint32* pPrefix,
    - bool& bDoneEntireDocument);
    -
             bool findPrev(bool& bDoneEntireDocument);
             bool findPrev(const UT_UCSChar* pFind, bool& bDoneEntireDocument);
     
    - bool _findPrev(UT_uint32* pPrefix,
    - bool& bDoneEntireDocument);
    -
             bool findReplaceReverse(bool& bDoneEntireDocument);
    -
    - bool _findReplaceReverse(UT_uint32* pPrefix,
    - bool& bDoneEntireDocument);
    -
    - bool _findReplace(UT_uint32* pPrefix,
    - bool& bDoneEntireDocument);
    -
    -
             bool findReplace(bool& bDoneEntireDocument);
     
             UT_uint32 findReplaceAll();
     
     // ----------------------
    @@ -780,11 +768,14 @@ private:
             bool m_bWholeWord;
             bool m_bMatchCase;
             UT_UCSChar * m_sFind;
             UT_UCSChar * m_sReplace;
     
    - UT_sint32 _findBlockSearchRegexp(const UT_UCSChar * haystack, const UT_UCSChar * needle);
    + fv_SearchReplace* m_pPS;
    + fv_SearchReplace* _getFindParams(void);
    + void _deleteFindParams(void);
    +
     
             // prefs listener - to change cursor blink on/off (and possibly others)
             static void _prefsListener( XAP_App *, XAP_Prefs *, UT_StringPtrMap *, void *);
     
             bool m_bShowPara;
    Index: text/fmt/xp/fv_View_protected.cpp
    ===================================================================
    RCS file: /cvsroot/abi/src/text/fmt/xp/fv_View_protected.cpp,v
    retrieving revision 1.125
    diff -u -p -u -5 -p -r1.125 fv_View_protected.cpp
    --- text/fmt/xp/fv_View_protected.cpp 2 Dec 2003 01:11:27 -0000 1.125
    +++ text/fmt/xp/fv_View_protected.cpp 3 Dec 2003 17:37:07 -0000
    @@ -1989,656 +1989,10 @@ fp_Page* FV_View::_getPageForXY(UT_sint3
             }
     
             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())
    - {
    - // 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)
    - {
    - bufferLength = (m_startPosition - (newBlock)->getPosition(false)) - newOffset;
    - }
    - else
    - {
    - 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_ASSERT(m_pLayout);
    -
    - UT_ASSERT(pBlock);
    - UT_ASSERT(*pBlock);
    -
    - UT_ASSERT(pOffset);
    -
    - 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));
    - UT_ASSERT(0);
    - }
    -
    - // 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)->getPrevBlockInDocument();
    -
    - // Are we at the end of the document?
    - if (!newBlock)
    - {
    - // Then wrap (fetch the first block in the doc)
    - PT_DocPosition endOfDoc;
    - getEditableBounds(true, endOfDoc);
    -
    - newBlock = m_pLayout->findBlockAtPositionReverse(endOfDoc);
    -
    - m_wrappedEnd = true;
    -
    - UT_ASSERT(newBlock);
    - }
    -
    - // 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(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;
    - 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));
    - bufferLength = pBuffer.getLength() - blockStart;
    - }
    - else
    - {
    - 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_ASSERT(bufferSegment);
    -
    - 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::_findReplaceReverse(UT_uint32* pPrefix, bool& bDoneEntireDocument)
    -{
    - 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())
    - {
    - _deleteSelection(&AttrProp_Before);
    - }
    -
    - // 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.
    -
    - _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)
    -{
    - 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())
    - {
    - _deleteSelection(&AttrProp_Before);
    - }
    -
    - // 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.
    -
    - _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)
    Index: wp/ap/unix/ap_UnixDialog_Replace.cpp
    ===================================================================
    RCS file: /cvsroot/abi/src/wp/ap/unix/ap_UnixDialog_Replace.cpp,v
    retrieving revision 1.58
    diff -u -p -u -5 -p -r1.58 ap_UnixDialog_Replace.cpp
    --- wp/ap/unix/ap_UnixDialog_Replace.cpp 25 Sep 2003 14:18:51 -0000 1.58
    +++ wp/ap/unix/ap_UnixDialog_Replace.cpp 3 Dec 2003 17:37:13 -0000
    @@ -92,10 +92,16 @@ static void s_find_entry_change(GtkWidge
     {
             UT_ASSERT(widget && dlg);
             dlg->event_FindEntryChange();
     }
     
    +static void s_replace_entry_change(GtkWidget * widget, AP_UnixDialog_Replace * dlg)
    +{
    + UT_ASSERT(widget && dlg);
    + dlg->event_ReplaceEntryChange();
    +}
    +
     static void s_replace_entry_activate(GtkWidget * widget, AP_UnixDialog_Replace * dlg)
     {
             UT_ASSERT(widget && dlg);
             dlg->event_Replace();
     }
    @@ -179,66 +185,57 @@ void AP_UnixDialog_Replace::runModeless(
             setView(static_cast<FV_View *> (getActiveFrame()->getCurrentView()));
     }
     
     void AP_UnixDialog_Replace::event_Find(void)
     {
    - char * findEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboFind)->entry));
    + const char * findEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboFind)->entry));
             if (strlen(findEntryText) == 0) // do nothing when the find field is empty
                     return;
     
    - // utf8->ucs4
    - setFindString(UT_UCS4String(findEntryText).ucs4_str());
    -
             if (!getReverseFind())
                     findNext();
             else
                     findPrev();
     }
     
     void AP_UnixDialog_Replace::event_FindEntryChange(void)
     {
             const char *input = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboFind)->entry));
             bool enable = strlen(input) != 0;
    +
    + // utf8->ucs4
    + setFindString(UT_UCS4String(input).ucs4_str());
    +
             gtk_widget_set_sensitive(m_buttonFind, enable);
             if (m_id == AP_DIALOG_ID_REPLACE)
             {
                     gtk_widget_set_sensitive(m_buttonFindReplace, enable);
                     gtk_widget_set_sensitive(m_buttonReplaceAll, enable);
             }
     }
     
     void AP_UnixDialog_Replace::event_Replace(void)
     {
    - char * findEntryText;
    - char * replaceEntryText;
    -
    - findEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboFind)->entry));
    - replaceEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboReplace)->entry));
    -
    - setFindString(UT_UCS4String(findEntryText).ucs4_str());
    - setReplaceString(UT_UCS4String(replaceEntryText).ucs4_str());
    -
             if(!getReverseFind())
                     findReplace();
             else
                     findReplaceReverse();
     }
     
     void AP_UnixDialog_Replace::event_ReplaceAll(void)
     {
    - char * findEntryText;
    - char * replaceEntryText;
    -
    - findEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboFind)->entry));
    - replaceEntryText = (char *) gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboReplace)->entry));
    -
    - setFindString(UT_UCS4String(findEntryText).ucs4_str());
    - setReplaceString(UT_UCS4String(replaceEntryText).ucs4_str());
    -
             findReplaceAll();
     }
     
    +void AP_UnixDialog_Replace::event_ReplaceEntryChange(void)
    +{
    + const char *replace = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(m_comboReplace)->entry));
    +
    + // utf8->ucs4
    + setReplaceString(UT_UCS4String(replace).ucs4_str());
    +}
    +
     void AP_UnixDialog_Replace::event_MatchCaseToggled(void)
     {
             setMatchCase(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_checkbuttonMatchCase)));
     }
     
    @@ -385,10 +382,15 @@ GtkWidget * AP_UnixDialog_Replace::_cons
             // If the user hits "enter" in the entry field, we launch a replace
             g_signal_connect(G_OBJECT(GTK_COMBO(m_comboReplace)->entry),
                                              "activate",
                                              G_CALLBACK(s_replace_entry_activate),
                                              this);
    +
    + g_signal_connect(G_OBJECT(GTK_COMBO(m_comboReplace)->entry),
    + "changed",
    + G_CALLBACK(s_replace_entry_change),
    + (gpointer) this);
     
             // the catch-alls
             // Dont use gtk_signal_connect_after for modeless dialogs
             g_signal_connect(G_OBJECT(m_windowMain),
                                                "destroy",
    Index: wp/ap/unix/ap_UnixDialog_Replace.h
    ===================================================================
    RCS file: /cvsroot/abi/src/wp/ap/unix/ap_UnixDialog_Replace.h,v
    retrieving revision 1.13
    diff -u -p -u -5 -p -r1.13 ap_UnixDialog_Replace.h
    --- wp/ap/unix/ap_UnixDialog_Replace.h 26 May 2003 21:59:09 -0000 1.13
    +++ wp/ap/unix/ap_UnixDialog_Replace.h 3 Dec 2003 17:37:14 -0000
    @@ -45,10 +45,11 @@ public:
             // callbacks can fire these events
             void event_Find(void);
             void event_FindEntryChange(void);
             void event_Replace(void);
             void event_ReplaceAll(void);
    + void event_ReplaceEntryChange(void);
             void event_MatchCaseToggled(void);
             void event_WholeWordToggled(void);
             void event_ReverseFindToggled(void);
             void event_Cancel(void);
     
    Index: wp/ap/xp/ap_Dialog_Replace.cpp
    ===================================================================
    RCS file: /cvsroot/abi/src/wp/ap/xp/ap_Dialog_Replace.cpp,v
    retrieving revision 1.45
    diff -u -p -u -5 -p -r1.45 ap_Dialog_Replace.cpp
    --- wp/ap/xp/ap_Dialog_Replace.cpp 25 Sep 2003 14:18:55 -0000 1.45
    +++ wp/ap/xp/ap_Dialog_Replace.cpp 3 Dec 2003 17:37:14 -0000
    @@ -44,10 +44,18 @@ AP_Dialog_Replace::AP_Dialog_Replace(XAP
             m_pFrame = NULL;
             
     
             // is this used?
             m_answer = a_VOID;
    +
    + // We must ensure this gets initialized - the replace field may
    + // never be changed (user replacing string with "") which means
    + // the associated change event handler never gets to initialize
    + // the replace string.
    + UT_UCSChar* pEmpty = NULL;
    + UT_UCS4_cloneString_char(&pEmpty, "");
    + setReplaceString(pEmpty);
             
             UT_DEBUGMSG(("FODDEX: creating find/replace dialog\n"));
     }
     
     AP_Dialog_Replace::~AP_Dialog_Replace(void)



    This archive was generated by hypermail 2.1.4 : Wed Dec 03 2003 - 12:59:55 EST