protected bool ProcessParagraphs(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs)
        {
            foreach (Word.Paragraph aParagraph in aParagraphs)
            {
                // get the Range object for this paragraph
                Word.Range aParagraphRange = aParagraph.Range;

                // if we're picking up where we left off and we're not there yet...
                int nCharIndex = 0;
                if (aWordProcessor.AreLeftOvers)
                {
                    if (aWordProcessor.LeftOvers.Start > aParagraphRange.End)
                    {
                        continue;   // skip to the next paragraph
                    }
                    nCharIndex = aWordProcessor.LeftOvers.StartIndex;
                    aWordProcessor.LeftOvers = null; // turn off "left overs"
                }

                WordRange aThisParagraph = new WordRange(aParagraphRange);
                if (!ProcessRangeAsWords(aWordProcessor, aThisParagraph, nCharIndex))
                {
                    return(false);
                }
            }

            return(true);
        }
        public bool ProcessWholeParagraphs(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs)
        {
            foreach (Word.Paragraph aParagraph in aParagraphs)
            {
                // get the Range object for this paragraph
                Word.Range aParagraphRange = aParagraph.Range;

                // if we're picking up where we left off and we're not there yet...
                int nCharIndex = 0;
                if (aWordProcessor.AreLeftOvers)
                {
                    if (aWordProcessor.LeftOvers.Start > aParagraphRange.End)
                    {
                        continue;   // skip to the next paragraph
                    }
                    nCharIndex = aWordProcessor.LeftOvers.StartIndex;
                    aWordProcessor.LeftOvers = null; // turn off "left overs"
                }

                WordRange aRunRange  = new WordRange(aParagraphRange);
                int       nEndIndex  = --aRunRange.EndIndex;
                int       nLastIndex = nCharIndex;

                // exclude the paragraph end and process it as a whole unit (we may have to do this multiple times
                bool bStop = false;
                while (!bStop && (nCharIndex < nEndIndex))
                {
                    aRunRange.StartIndex = nCharIndex;
                    if (aRunRange.EndIndex != nEndIndex)
                    {
                        aRunRange.EndIndex = nEndIndex;
                    }

                    nLastIndex = nCharIndex;

                    System.Diagnostics.Trace.WriteLine(String.Format("Start: {0}, End: {1}, text: {2}, length: {3}",
                                                                     aRunRange.Start, aRunRange.End, aRunRange.Text, aRunRange.Text.Length));

                    if (!aWordProcessor.Process(aRunRange, ref nCharIndex))
                    {
                        aWordProcessor.LeftOvers = aRunRange;
                        return(false);
                    }

                    if (nLastIndex == nCharIndex)
                    {
                        break;
                    }
                }
            }

            return(true);
        }
        public bool ProcessParagraphsIsoFormat(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs)
        {
            foreach (Word.Paragraph aParagraph in aParagraphs)
            {
                // get the Range object for this paragraph
                Word.Range aParagraphRange = aParagraph.Range;

                // if we're picking up where we left off and we're not there yet...
                int nCharIndex = 0;
                if (aWordProcessor.AreLeftOvers)
                {
                    if (aWordProcessor.LeftOvers.Start > aParagraphRange.End)
                    {
                        continue;                           // skip to the next paragraph
                    }
                    nCharIndex = aWordProcessor.LeftOvers.StartIndex;
                    aWordProcessor.LeftOvers = null;                     // turn off "left overs"
                }

                WordRange aRunRange   = new WordRange(aParagraphRange);
                int       nStartIndex = aRunRange.StartIndex;
                System.Diagnostics.Debug.Assert(nStartIndex == 0);

                // if we have mixed character formatting, then use a binary search algorithm to find
                //	the maximum run.
                if (MixedCharacterFormatting(aRunRange))
                {
                    int nWidth = aRunRange.EndIndex / 2;

                    do
                    {
                        aRunRange.EndIndex++;
                    } while (!MixedCharacterFormatting(aRunRange));

                    aRunRange.EndIndex--;                       // back up one
                }
                else
                {
                    // the whole paragraph seems to be iso formatted, so exclude the paragraph end and
                    // process it as a whole unit
                    aRunRange.EndIndex--;
                    if (!aWordProcessor.Process(aRunRange, ref nCharIndex))
                    {
                        aWordProcessor.LeftOvers = aRunRange;
                        return(false);
                    }
                }
            }

            return(true);
        }
        public override bool ProcessWordByWord(OfficeDocumentProcessor aWordProcessor)
        {
            // if multiple paragraphs...
            int            nCharIndex       = 0;
            WordParagraphs aParagraphRanges = new WordParagraphs(Document.Application.Selection);

            foreach (Word.Range aRange in aParagraphRanges)
            {
                WordRange aThisParagraph = new WordRange(aRange);
                if (!ProcessRangeAsWords(aWordProcessor, aThisParagraph, nCharIndex))
                {
                    return(false);
                }
            }
            return(true);
        }
 public void ReplaceText(WordRange aRunRange, string strNewText)
 {
     try
     {
         aRunRange.Text = strNewText;
     }
     catch (Exception ex)
     {
         if (ex.Message == "The range cannot be deleted.")
         {
             aRunRange.End++;
             string strText = aRunRange.Text;
             if (strText[strText.Length - 1] == '\n')
             {
                 throw new ApplicationException(String.Format("The paragraphs in this document end with both a carriage return (i.e. \\r) and a line feed character (i.e. \\n).{0}You need to change your 'Find what' string to include both (i.e. '\r\n'), or the found text can't be replaced.", Environment.NewLine));
             }
         }
     }
 }
 public WordRange(WordRange basedOnRange)
 {
     m_theRangeBasedOn = basedOnRange.m_theRangeBasedOn.Duplicate;
     // for some unknown reason, the Range sometimes doesn't operate the way one would expect
     //  an array to work. Sometimes (I can't tell why) the first "character" (which is always
     //  available from Text[0]), comes from a Start value of 0 and sometimes from 1.
     //  In the latter case, I think something hidden must be at 0 and the first "character"
     //  is really coming from offset 1.
     // Anyway, the good news is that I think I can detect this case as follows:
     //  The offset to add to the Start and End properties is one less than the End property
     //  value of the first "Character". For the weird case, the Start is 0 and the End 2, but
     //  the length and Text is only 1!?
     // But only if this isn't a zero-length range
     if (m_theRangeBasedOn.Start != m_theRangeBasedOn.End)
     {
         Word.Range aRange = m_theRangeBasedOn.Characters.First;
         m_nOffset = aRange.End - aRange.Text.Length;
     }
 }
        public void ProcessParagraphsFindAndReplace(FindWordProcessor aWordProcessor, Word.Range theRangeToSearch,
                                                    BackgroundWorker worker)
        {
            // get Range object we can manipulate for searching (i.e. don't muck with the original
            WordRange aRunRange = GetStartingRange(theRangeToSearch);

            if (aRunRange == null)
            {
                return;
            }

            // how many paragraphs ahead we have to look for a match
            int    nEndRangeToSearch = -1; // might change from loop to loop if we do replacements (so update inside loop)
            object nOffsetParagraph  = aWordProcessor.NumOfParagraphsToSearch - 1;

            do
            {
                SearchAreaStart = aRunRange.Start;

                // this updates the progress bar
                worker.ReportProgress(SearchAreaStart);

                // if the search string contains multiple '\r's, then we have to look ahead that many paragraphs
                int SearchAreaEnd;
                nEndRangeToSearch = theRangeToSearch.End;   // might change from loop to loop if we do replacements
                if (0 < (int)nOffsetParagraph)
                {
                    Word.Paragraph aEndParagraph = m_paraSearch.Next(ref nOffsetParagraph);
                    if (aEndParagraph == null)
                    {
                        return; // means we can't possibly find it now
                    }
                    SearchAreaEnd = Math.Min(aEndParagraph.Range.End, nEndRangeToSearch);
                    aRunRange.End = SearchAreaEnd;
                }
                else
                {
                    int nEndOfRun = aRunRange.End;
                    if (nEndOfRun > nEndRangeToSearch)
                    {
                        aRunRange.End = SearchAreaEnd = nEndRangeToSearch;
                    }
                    else
                    {
                        SearchAreaEnd = nEndOfRun;
                    }
                }

                // loop until the end of the paragraph and process it as a whole unit (we may have to do this multiple times)
                bool bGoToNext = true;
                while (SearchAreaStart < SearchAreaEnd)
                {
                    // keep track of the last index so we can tell whether anything was found or not
                    FindWordProcessor.FindResult res = aWordProcessor.FindReplaceCompare(aRunRange, ref SearchAreaStart, ref FoundAreaLength);

                    // if we found it and need to stop (or if the user had cancelled the search)...
                    bGoToNext = true;   // assume we will
                    if ((res == FindWordProcessor.FindResult.eFindFound) || worker.CancellationPending)
                    {
                        return;
                    }

                    // otherwise, it might have been a replacement and we have to update the end of the paragraph
                    //  value and the end of the range and do it again (e.g. find the next occurrence to replace)
                    else if (res == FindWordProcessor.FindResult.eReplaceFound)
                    {
                        // we might have actually removed the paragraph mark, in which case the next paragraph
                        //  is now part of the current paragraph. Unfortunately, the aParagraphRange isn't
                        //  updated.
                        // if the search string contained '\r's, then we have to look ahead all over again
                        if (aWordProcessor.IsAnyCRs)
                        {
                            bGoToNext = false;
                            break;
                        }
                        else
                        {
                            Word.Range aParagraphRange = m_paraSearch.Range;

                            SearchAreaEnd   = aParagraphRange.End;
                            aRunRange.End   = SearchAreaEnd;
                            aRunRange.Start = SearchAreaStart;
                        }
                    }

                    // otherwise, it means we didn't find it here and we're done with this paragraph
                    else
                    {
                        System.Diagnostics.Debug.Assert(!aWordProcessor.IsFound);
                        break;
                    }
                }

                // advance to the next paragraph
                if (bGoToNext)
                {
                    m_paraSearch = m_paraSearch.Next(ref offset1);
                }

                if (m_paraSearch != null)
                {
                    aRunRange = new WordRange(m_paraSearch.Range);

                    // in the case we replace some CRs, we re-start where we left off (after the replaced text)
                    if (!bGoToNext)
                    {
                        aRunRange.Start = SearchAreaStart;
                    }
                }
            } while ((m_paraSearch != null) && (aRunRange.Start < nEndRangeToSearch));
        }
        // determine if this section of text (some portion of one or more paragraphs) has the find what text in it
        public FindResult FindReplaceCompare(WordRange aRunRange, ref int SearchAreaStart, ref int FoundAreaLength)
        {
            FindResult res      = FindResult.eNothingFound;
            string     strInput = aRunRange.Text;

            if (String.IsNullOrEmpty(strInput) || (m_aECRegex == null))
            {
                return(res);
            }

            // otherwise 'convert' it to see if the 'Find what' string is in it
            string strOutput = m_aECRegex.Convert(strInput);

            // if the input string is different from the output string, then the FindWhat string was found.
            if (strInput != strOutput)
            {
#if !DefineToNotUseSplitAndConvert
                // The way the convert works is that it will replace each instance of the input string that matches
                //  the FindWhat syntax (i.e. there may be more than one replacement we have to deal with).
                // here's the problem: if there was more than one replacment, then I really can't tell what portion
                //  of the FindWhat text each replacement goes with. Consider the string "ababbbbbabb". If the 'Find
                //  what' string is "ab+", then it breaks up to: "ab", "abbbbb", and "abb", but if these three are
                //  right in a row and the replacement is "", then the resulting strOutput will be <delim><delim>...
                //  repeated 3 times. So there's no way to tell which portion of the input text corresponds to which
                //  portion of the output text. My original stab at it just treated them evenly and divide by the
                //  number of consecutive replacements... this works if the 'FindWhat' is something like "ab". But all
                //  bets are off if the user uses the "+" (eat-em-up) expression code.
                // I think the only solution is to always only deal with a single replacement... So... if we have more
                //  than one match in a particular output (which we can tell by the Length of astrSegments), do a binary
                //  search until we have only one.
                string[] astrSegments = GetFirstReplacementSplitArray(ref strInput, ref strOutput);

                // get the index to the first character of the replacement (which is the same as the first character
                //  of the 'Find what' string as well).
                int nIndex = astrSegments[0].Length;

                // remember this so we pick up here later
                SearchAreaStart += nIndex;
                if (nIndex > 0)
                {
                    aRunRange.Start = SearchAreaStart;
                }

                // the replacement string is easy. It's just whatever's in the one'th element of the Split array.
                string strReplacementString = astrSegments[1];

                // There might be some stuff between the end of the replacement and the end of the input string.
                string strStuffFollowingMatch = astrSegments[2];   // may be null

                // get the index to the end of the 'Find what' string
                int nEndOfFindWhatSelection;
                if (String.IsNullOrEmpty(strStuffFollowingMatch))
                {
                    nEndOfFindWhatSelection = strInput.Length;
                }
                else
                {
                    nEndOfFindWhatSelection = strInput.Length - strStuffFollowingMatch.Length;
                }
#else
                // this could probably be done more elegantly with Split rather than what I do in the #else case
                string[] astrSegments = strOutput.Split(m_achDelimiter);

                // must be odd (before, ||: replace, following :||), where any or all can be null
                int nTotalSegments = astrSegments.Length;
                System.Diagnostics.Debug.Assert((nTotalSegments % 2) == 1);

                // get the index to the first character of the replacement (which is the same as the first character
                //  of the 'Find what' as well).
                int nIndex = astrSegments[0].Length;

                // remember this so we pick up here later
                SearchAreaStart += nIndex;
                if (nIndex > 0)
                {
                    aRunRange.Start = SearchAreaStart;
                }

                // the replacement string is easy. It's just whatever's in the 1st element of the Split array.
                //  but we have to figure out what the 'Find what' text is so that we can select it (so we can replace
                //  it). This is not so easy, because it could be anything and not just a string of text like in a normal
                //  find.
                string strReplacementString = astrSegments[1];

                // Between the end of the first replacement and the beginning of the next (if multiple replacments)
                //  is a string which should match something in the original, which we can search for
                string strStuffFollowingMatch = astrSegments[2];   // may be null

                int nEndOfFindWhatSelection;
                if (String.IsNullOrEmpty(strStuffFollowingMatch))
                {
                    // If the 'Find what' is repeated twice in a row, then the stuff in-between the two instances of
                    //  replacement text will be null.
                    // Detect this by looking at the length of the even number string array elements (2, 4, etc),
                    //  which are the segments following the replacements. This tells us what we have to divide by
                    //  to get the proportion for only one find.
                    int nNumReplacmentsInARow = 1;
                    int nNextReplacementIndex = 2;
                    nTotalSegments--;
                    while ((nNextReplacementIndex < nTotalSegments) && String.IsNullOrEmpty(astrSegments[nNextReplacementIndex]))
                    {
                        nNumReplacmentsInARow++;
                        nNextReplacementIndex += 2;
                    }

                    if (nNextReplacementIndex < astrSegments.Length)
                    {
                        strStuffFollowingMatch = astrSegments[nNextReplacementIndex];
                    }

                    int numerator;
                    if (String.IsNullOrEmpty(strStuffFollowingMatch))
                    {
                        numerator = strInput.Length;
                    }
                    else
                    {
                        numerator = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1);
                    }
                    nEndOfFindWhatSelection = ((numerator - nIndex) / nNumReplacmentsInARow) + nIndex;
                }
                else
                {
                    nEndOfFindWhatSelection = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1);
                }

                /*
                 * int nIndex = strOutput.IndexOf(m_achDelimiter[0]);
                 * System.Diagnostics.Debug.Assert(nIndex != -1);
                 * SearchAreaStart += nIndex;
                 * if (nIndex > 0)
                 *  aRunRange.Start = SearchAreaStart;
                 *
                 * int nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nIndex + 1);
                 * System.Diagnostics.Debug.Assert(nEndOfReplacement != -1);
                 *
                 * // the replacement string is what's between these two
                 * string strReplacementString = strOutput.Substring(nIndex + 1, (nEndOfReplacement - nIndex - 1));
                 *
                 * // now the complicated part. Between the end of the first replacement and the next
                 * //  one is a string which should match something in the original. But if the replacement
                 * //  were null, then it could be the very next character...
                 * // This also handles the situation where there may be several "found whats"
                 * int nNumReplacmentsInARow = 1;
                 * int nNextIndex = nEndOfReplacement + 1;
                 * while ((nNextIndex < strOutput.Length) && (strOutput[nNextIndex] == m_achDelimiter[0]))
                 * {
                 *  nNumReplacmentsInARow++;
                 *  nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nNextIndex + 1);
                 *  nNextIndex = nEndOfReplacement + 1;
                 * }
                 *
                 * if (nNextIndex < strOutput.Length)
                 * {
                 *  nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nNextIndex + 1);
                 *  if (nEndOfReplacement == -1)
                 *      nEndOfReplacement = strOutput.Length;
                 * }
                 * else if (nNextIndex == strOutput.Length)
                 *  nNextIndex--;
                 *
                 * string strStuffFollowingMatch = strOutput.Substring(nNextIndex, nEndOfReplacement - nNextIndex);
                 *
                 * nEndOfFindWhatSelection = ((strInput.Length - nIndex) / nNumReplacmentsInARow) + nIndex;
                 * if (!String.IsNullOrEmpty(strStuffFollowingMatch))
                 * {
                 *  nEndOfFindWhatSelection = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1) / nNumReplacmentsInARow;
                 * }
                 */
#endif // !UseSplitToFindReplacements

                FoundAreaLength = nEndOfFindWhatSelection - nIndex;
                aRunRange.End   = SearchAreaStart + FoundAreaLength;

                // if we're doing ReplaceAll or Replace when the FindWhat string has been found...
                System.Diagnostics.Debug.Assert(FormButton != FormButtons.Cancel);  // means it wasn't initialized
                if (!DontReplaceOnNextFind &&
                    ((ReplaceAll) || ((FormButton == FormButtons.ReplaceOnce) && (nIndex == 0))))
                {
                    if (FormButton == FormButtons.ReplaceOnce)
                    {
                        DontReplaceOnNextFind = true;  // so we do a virtual find next after this
                    }
                    // this means replace the word in situ, with what was converted
                    ReplaceText(aRunRange, strReplacementString);
                    NumOfReplacements++;

                    // start the next search after this replaced value
                    string strReplacedText = aRunRange.Text;   // this may not be exactly the same as strReplace (e.g. after replacing final '\r')
                    if (!String.IsNullOrEmpty(strReplacedText))
                    {
                        FoundAreaLength  = strReplacedText.Length;
                        SearchAreaStart += FoundAreaLength;
                    }

                    res = FindResult.eReplaceFound;
                }
                else
                {
                    // select just the search string and return as if cancelled so the outer loop can prompt for it.
                    IsFound = true;

                    // select the FindWhat text found
                    aRunRange.Select();

                    res = FindResult.eFindFound;
                }
            }
            else if (FormButton == FormButtons.ReplaceOnce)
            {
                // otherwise, if the user clicked ReplaceOnce and we didn't find it right away,
                //  then change it to function like Find instead.
                DontReplaceOnNextFind = true;  // so we do a virtual find next after this
            }

            return(res);
        }