Fix for bug #6254 (Find/Replace needs to be implemented.)

First pass implementation of Find and Replace.
sr=sfraser@netscape.com  r=ducarroz@netscape.com

New Files:

    editor/txtsvc/macbuild/txtsvcIDL.mcp
    editor/txtsvc/public/MANIFEST_IDL
    editor/txtsvc/public/nsIFindAndReplace.idl
    editor/txtsvc/src/nsFindAndReplace.cpp
    editor/txtsvc/src/nsFindAndReplace.h
    xpfe/components/find/resources/replacedialog.js
    xpfe/components/find/resources/replacedialog.xul
    xpfe/components/find/resources/locale/en-US/replacedialog.dtd


Modified Files:

    build/mac/build_scripts/MozillaBuildList.pm
    editor/base/nsEditorShell.cpp
    editor/idl/nsIEditorShell.idl
    editor/txtsvc/macbuild/txtsvc.mcp
    editor/txtsvc/public/Makefile.in
    editor/txtsvc/public/makefile.win
    editor/txtsvc/public/MANIFEST
    editor/txtsvc/src/Makefile.in
    editor/txtsvc/src/makefile.win
    editor/txtsvc/src/nsTextServicesFactory.cpp
    editor/ui/composer/content/ComposerCommands.js
    editor/ui/composer/content/editor.js
    editor/ui/composer/content/editorOverlay.xul
    editor/ui/composer/locale/en-US/editorOverlay.dtd
    mailnews/compose/resources/content/messengercompose.xul
    mailnews/compose/resources/content/MsgComposeCommands.js
    mailnews/compose/resources/locale/en-US/messengercompose.dtd
    xpfe/components/jar.mn
    xpfe/components/find/public/nsIFindComponent.idl
    xpfe/components/find/public/nsISearchContext.idl
    xpfe/components/find/resources/makefile.win
    xpfe/components/find/resources/MANIFEST
    xpfe/components/find/resources/locale/en-US/MANIFEST
    xpfe/components/find/src/nsFindComponent.cpp
    xpfe/components/find/src/nsFindComponent.h
This commit is contained in:
kin%netscape.com 2001-01-23 21:55:04 +00:00
Родитель 3bd146e8d8
Коммит 4275b833c4
32 изменённых файлов: 1549 добавлений и 476 удалений

Просмотреть файл

@ -670,6 +670,7 @@ sub BuildClientDist()
InstallFromManifest(":mozilla:editor:txmgr:idl:MANIFEST", "$distdirectory:idl:");
InstallFromManifest(":mozilla:editor:public:MANIFEST", "$distdirectory:editor:");
InstallFromManifest(":mozilla:editor:txmgr:public:MANIFEST", "$distdirectory:editor:txmgr");
InstallFromManifest(":mozilla:editor:txtsvc:public:MANIFEST_IDL", "$distdirectory:idl:");
InstallFromManifest(":mozilla:editor:txtsvc:public:MANIFEST", "$distdirectory:editor:txtsvc");
#SILENTDL
@ -918,6 +919,7 @@ sub BuildIDLProjects()
BuildIDLProject(":mozilla:widget:macbuild:widgetIDL.mcp", "widget");
BuildIDLProject(":mozilla:editor:macbuild:EditorIDL.mcp", "editor");
BuildIDLProject(":mozilla:editor:txmgr:macbuild:txmgrIDL.mcp", "txmgr");
BuildIDLProject(":mozilla:editor:txtsvc:macbuild:txtsvcIDL.mcp", "txtsvc");
BuildIDLProject(":mozilla:profile:macbuild:ProfileServicesIDL.mcp", "profileservices");
BuildIDLProject(":mozilla:profile:pref-migrator:macbuild:prefmigratorIDL.mcp", "prefm");

Просмотреть файл

@ -2920,7 +2920,7 @@ nsEditorShell::DoFind(PRBool aFindNext)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMWindowInternal> cwP = do_QueryReferent(mContentWindow);
if (!cwP) return NS_ERROR_NOT_INITIALIZED;
rv = findComponent->CreateContext(cwP, nsnull, getter_AddRefs(mSearchContext));
rv = findComponent->CreateContext(cwP, this, getter_AddRefs(mSearchContext));
}
if (NS_SUCCEEDED(rv))
@ -2946,6 +2946,34 @@ nsEditorShell::FindNext()
return DoFind(PR_TRUE);
}
NS_IMETHODIMP
nsEditorShell::Replace()
{
if (!mContentAreaDocShell)
return NS_ERROR_NOT_INITIALIZED;
// Get find component.
nsresult rv;
NS_WITH_SERVICE(nsIFindComponent, findComponent, NS_IFINDCOMPONENT_CONTRACTID, &rv);
NS_ASSERTION(((NS_SUCCEEDED(rv)) && findComponent), "GetService failed for find component.");
if (NS_FAILED(rv)) { return rv; }
// make the search context if we need to
if (!mSearchContext)
{
if(!mContentWindow)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMWindowInternal> cwP = do_QueryReferent(mContentWindow);
if (!cwP) return NS_ERROR_NOT_INITIALIZED;
rv = findComponent->CreateContext(cwP, this, getter_AddRefs(mSearchContext));
}
if (NS_SUCCEEDED(rv))
rv = findComponent->Replace(mSearchContext);
return rv;
}
/* Get localized strings for UI from the Editor's string bundle */
// Use this version from JavaScript:
NS_IMETHODIMP

Просмотреть файл

@ -2920,7 +2920,7 @@ nsEditorShell::DoFind(PRBool aFindNext)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMWindowInternal> cwP = do_QueryReferent(mContentWindow);
if (!cwP) return NS_ERROR_NOT_INITIALIZED;
rv = findComponent->CreateContext(cwP, nsnull, getter_AddRefs(mSearchContext));
rv = findComponent->CreateContext(cwP, this, getter_AddRefs(mSearchContext));
}
if (NS_SUCCEEDED(rv))
@ -2946,6 +2946,34 @@ nsEditorShell::FindNext()
return DoFind(PR_TRUE);
}
NS_IMETHODIMP
nsEditorShell::Replace()
{
if (!mContentAreaDocShell)
return NS_ERROR_NOT_INITIALIZED;
// Get find component.
nsresult rv;
NS_WITH_SERVICE(nsIFindComponent, findComponent, NS_IFINDCOMPONENT_CONTRACTID, &rv);
NS_ASSERTION(((NS_SUCCEEDED(rv)) && findComponent), "GetService failed for find component.");
if (NS_FAILED(rv)) { return rv; }
// make the search context if we need to
if (!mSearchContext)
{
if(!mContentWindow)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMWindowInternal> cwP = do_QueryReferent(mContentWindow);
if (!cwP) return NS_ERROR_NOT_INITIALIZED;
rv = findComponent->CreateContext(cwP, this, getter_AddRefs(mSearchContext));
}
if (NS_SUCCEEDED(rv))
rv = findComponent->Replace(mSearchContext);
return rv;
}
/* Get localized strings for UI from the Editor's string bundle */
// Use this version from JavaScript:
NS_IMETHODIMP

Просмотреть файл

@ -174,6 +174,7 @@ interface nsIEditorShell : nsISupports
void Find();
void FindNext();
void Replace();
/* General Utilities */

Просмотреть файл

@ -23,14 +23,22 @@
#include "nsIGenericFactory.h"
#include "nsTextServicesDocument.h"
#include "nsFindAndReplace.h"
#include "nsTextServicesCID.h"
#define NS_TEXTSERVICESFINDANDREPLACE_CID \
{ /* 8B0EEFE1-C4AE-11d4-A401-000064657374 */ \
0x8b0eefe1, 0xc4ae, 0x11d4, \
{ 0xa4, 0x1, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
//
// NOTE: This creates an instance of objects by using the default constructor
//
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextServicesDocument)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindAndReplace)
////////////////////////////////////////////////////////////////////////
// Define a table of CIDs implemented by this module along with other
@ -38,7 +46,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextServicesDocument)
// class name.
//
static nsModuleComponentInfo components[] = {
{ NULL, NS_TEXTSERVICESDOCUMENT_CID, NULL, nsTextServicesDocumentConstructor },
{ NULL, NS_TEXTSERVICESDOCUMENT_CID, "@mozilla.org/textservices/textservicesdocument;1", nsTextServicesDocumentConstructor },
{ NULL, NS_TEXTSERVICESFINDANDREPLACE_CID, NS_FINDANDREPLACE_CONTRACTID, nsFindAndReplaceConstructor },
};
////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -0,0 +1 @@
nsIFindAndReplace.idl

Просмотреть файл

@ -35,6 +35,10 @@ EXPORTS = \
nsTextServicesCID.h \
$(NULL)
XPIDLSRCS = \
nsIFindAndReplace.idl \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -31,5 +31,9 @@ EXPORTS = \
MODULE = txtsvc
XPIDLSRCS = \
.\nsIFindAndReplace.idl \
$(NULL)
include <$(DEPTH)\config\rules.mak>

Просмотреть файл

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
interface nsITextServicesDocument;
%{ C++
#define NS_FINDANDREPLACE_CONTRACTID "@mozilla.org/textservices/findandreplace;1"
%} C++
[scriptable, uuid(E0178A21-B9A2-11d4-A3FF-000064657374)]
interface nsIFindAndReplace : nsISupports
{
[noscript] attribute nsITextServicesDocument tsDoc;
attribute boolean findBackwards;
attribute boolean caseSensitive;
attribute boolean wrapFind;
attribute boolean entireWord;
readonly attribute boolean replaceEnabled;
/**
* Find some text in the current context. The implementation is
* responsible for performing the find and highliting the text.
* @param aFindText The text to search for.
* @retval Returns true if an instance of aFindText was found.
*/
boolean Find(in wstring aFindText);
/**
* Finds the text specified by aFindText and replaces it with
* the text specified by aReplaceText. The implementation is responsible
* for performing the find, replacing the text, and finding the
* next instance.
* @param aFindText The text to search for.
* @param aReplaceText The text to replace aFindText with.
* @param aAllOccurrences If true, the Replace() call will replace all
* instances of aFindText in the document with a ReplaceText.
* @retval Returns true if another instance of aFindText was found.
*/
boolean Replace(in wstring aFindText, in wstring aReplaceText, in boolean aAllOccurrences);
};

Просмотреть файл

@ -32,6 +32,7 @@ IS_COMPONENT = 1
REQUIRES = xpcom editor layout dom js locale widget
CPPSRCS = \
nsFindAndReplace.cpp \
nsTextServicesDocument.cpp \
nsTextServicesFactory.cpp \
nsTSDNotifier.cpp \

Просмотреть файл

@ -25,12 +25,14 @@ DEPTH=..\..\..
LIBRARY_NAME=txtsvc
CPPSRCS = \
nsFindAndReplace.cpp \
nsTextServicesDocument.cpp \
nsTextServicesFactory.cpp \
nsTSDNotifier.cpp \
$(NULL)
CPP_OBJS = \
.\$(OBJDIR)\nsFindAndReplace.obj \
.\$(OBJDIR)\nsTextServicesDocument.obj \
.\$(OBJDIR)\nsTextServicesFactory.obj \
.\$(OBJDIR)\nsTSDNotifier.obj \

Просмотреть файл

@ -0,0 +1,741 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsFindAndReplace.h"
#include "nsString.h"
NS_IMPL_ISUPPORTS1(nsFindAndReplace, nsIFindAndReplace)
nsFindAndReplace::nsFindAndReplace()
: mFindBackwards(PR_FALSE)
, mCaseSensitive(PR_FALSE)
, mWrapFind(PR_FALSE)
, mEntireWord(PR_FALSE)
, mStartBlockIndex(0)
, mStartSelOffset(0)
, mCurrentBlockIndex(0)
, mCurrentSelOffset(0)
, mWrappedOnce(PR_FALSE)
{
NS_INIT_ISUPPORTS();
}
nsFindAndReplace::~nsFindAndReplace()
{
/* destructor code */
}
/* [noscript] attribute nsITextServicesDocument tsDoc; */
NS_IMETHODIMP
nsFindAndReplace::GetTsDoc(nsITextServicesDocument * *aTsDoc)
{
if (!aTsDoc)
return NS_ERROR_NULL_POINTER;
*aTsDoc = mTsDoc;
NS_IF_ADDREF((*aTsDoc));
return NS_OK;
}
NS_IMETHODIMP
nsFindAndReplace::SetTsDoc(nsITextServicesDocument * aTsDoc)
{
mTsDoc = aTsDoc;
return NS_OK;
}
/* attribute boolean findBackwards; */
NS_IMETHODIMP
nsFindAndReplace::GetFindBackwards(PRBool *aFindBackwards)
{
if (!aFindBackwards)
return NS_ERROR_NULL_POINTER;
*aFindBackwards = mFindBackwards;
return NS_OK;
}
NS_IMETHODIMP
nsFindAndReplace::SetFindBackwards(PRBool aFindBackwards)
{
mFindBackwards = aFindBackwards;
return NS_OK;
}
/* attribute boolean caseSensitive; */
NS_IMETHODIMP
nsFindAndReplace::GetCaseSensitive(PRBool *aCaseSensitive)
{
if (!aCaseSensitive)
return NS_ERROR_NULL_POINTER;
*aCaseSensitive = mCaseSensitive;
return NS_OK;
}
NS_IMETHODIMP
nsFindAndReplace::SetCaseSensitive(PRBool aCaseSensitive)
{
mCaseSensitive = aCaseSensitive;
return NS_OK;
}
/* attribute boolean wrapFind; */
NS_IMETHODIMP
nsFindAndReplace::GetWrapFind(PRBool *aWrapFind)
{
if (!aWrapFind)
return NS_ERROR_NULL_POINTER;
*aWrapFind = mWrapFind;
return NS_OK;
}
NS_IMETHODIMP
nsFindAndReplace::SetWrapFind(PRBool aWrapFind)
{
mWrapFind = aWrapFind;
return NS_OK;
}
/* attribute boolean entireWord; */
NS_IMETHODIMP
nsFindAndReplace::GetEntireWord(PRBool *aEntireWord)
{
if (!aEntireWord)
return NS_ERROR_NULL_POINTER;
*aEntireWord = mEntireWord;
return NS_OK;
}
NS_IMETHODIMP
nsFindAndReplace::SetEntireWord(PRBool aEntireWord)
{
mEntireWord = aEntireWord;
return NS_OK;
}
/* readonly attribute boolean replaceEnabled; */
NS_IMETHODIMP
nsFindAndReplace::GetReplaceEnabled(PRBool *aReplaceEnabled)
{
if (!aReplaceEnabled)
return NS_ERROR_NULL_POINTER;
*aReplaceEnabled = PR_FALSE;
nsresult result = NS_OK;
if (mTsDoc)
result = mTsDoc->CanEdit(aReplaceEnabled);
return result;
}
/* boolean Find (in wstring aFindText); */
NS_IMETHODIMP nsFindAndReplace::Find(const PRUnichar *aFindText, PRBool *aDidFind)
{
if (!aFindText || !aDidFind)
return NS_ERROR_NULL_POINTER;
if (!mTsDoc)
return NS_ERROR_NOT_INITIALIZED;
nsAutoString findStr(aFindText);
if (!mCaseSensitive)
findStr.ToLowerCase();
nsresult result = SetupDocForFind(mTsDoc, &mStartSelOffset);
if (NS_FAILED(result))
return result;
// find out where we started
result = GetCurrentBlockIndex(mTsDoc, &mStartBlockIndex);
if (NS_FAILED(result))
return result;
// and set the starting position again (hopefully, in future we won't have to do this)
result = SetupDocForFind(mTsDoc, &mStartSelOffset);
if (NS_FAILED(result))
return result;
mCurrentBlockIndex = mStartBlockIndex;
mCurrentSelOffset = mStartSelOffset;
mWrappedOnce = PR_FALSE;
return DoFind(mTsDoc, findStr, aDidFind);
}
/* boolean Replace (in wstring aFindText, in wstring aReplaceText, in boolean aAllOccurrences); */
NS_IMETHODIMP
nsFindAndReplace::Replace(const PRUnichar *aFindText, const PRUnichar *aReplaceText, PRBool aAllOccurrences, PRBool *aDidFind)
{
if (!aFindText || !aReplaceText || !aDidFind)
return NS_ERROR_NULL_POINTER;
*aDidFind = PR_FALSE;
if (!mTsDoc)
return NS_ERROR_NOT_INITIALIZED;
PRBool enabled;
GetReplaceEnabled(&enabled);
if (!enabled)
return NS_OK;
nsAutoString findStr(aFindText);
if (!mCaseSensitive)
findStr.ToLowerCase();
nsresult result = SetupDocForReplace(mTsDoc, findStr, &mStartSelOffset);
if (NS_FAILED(result))
return result;
// find out where we started
result = GetCurrentBlockIndex(mTsDoc, &mStartBlockIndex);
if (NS_FAILED(result))
return result;
// and set the starting position again (hopefully, in future we won't have to do this)
result = SetupDocForReplace(mTsDoc, findStr, &mStartSelOffset);
if (NS_FAILED(result))
return result;
mCurrentBlockIndex = mStartBlockIndex;
mCurrentSelOffset = mStartSelOffset;
mWrappedOnce = PR_FALSE;
nsAutoString replaceStr(aReplaceText);
PRBool didReplace = PR_FALSE;
// Keep looping until DoFind() fails to find another instance
// of the find string or has looped through the entire document.
do
{
result = DoFind(mTsDoc, findStr, aDidFind);
if (NS_FAILED(result))
break;
if (!*aDidFind || (didReplace && !aAllOccurrences))
break;
if (replaceStr.Length() == 0)
result = mTsDoc->DeleteSelection();
else
{
result = mTsDoc->InsertText(&replaceStr);
// DoFind() sets mCurrentSelOffset to the beginning
// of the selection. If we are searching in the forward
// direction, set the offset after the string we just
// inserted so that we don't search through it when we
// call DoFind() again.
if (!mFindBackwards)
mCurrentSelOffset += replaceStr.Length();
}
if (NS_FAILED(result))
break;
didReplace = PR_TRUE;
}
while (*aDidFind);
return result;
}
// ----------------------------------------------------------------
// CharsMatch
//
// Compare chars. Match if both are whitespace, or both are
// non whitespace and same char.
// ----------------------------------------------------------------
inline static PRBool
CharsMatch(PRUnichar c1, PRUnichar c2)
{
return (nsCRT::IsAsciiSpace(c1) && nsCRT::IsAsciiSpace(c2)) || (c1 == c2);
}
// ----------------------------------------------------------------
// FindInString
//
// Routine to search in an nsString which is smart about extra
// whitespace, can search backwards, and do case insensitive search.
//
// This uses a brute-force algorithm, which should be sufficient
// for our purposes (text searching)
//
// searchStr contains the text from a content node, which can contain
// extra white space between words, which we have to deal with.
// The offsets passed in and back are offsets into searchStr,
// and thus include extra white space.
//
// If we are ignoring case, the strings have already been lowercased
// at this point.
//
// startOffset is the offset in the search string to start searching
// at. If -1, it means search from the start (forwards) or end (backwards).
//
// Returns -1 if the string is not found, or if the pattern is an
// empty string, or if startOffset is off the end of the string.
// ----------------------------------------------------------------
static PRInt32 FindInString(const nsString &searchStr, const nsString &patternStr,
PRInt32 startOffset, PRBool searchBackwards)
{
PRInt32 foundOffset = -1;
PRInt32 patternLen = patternStr.Length();
PRInt32 searchStrLen = searchStr.Length();
if (patternLen == 0) // pattern is empty
return -1;
if (startOffset < 0)
startOffset = (searchBackwards) ? searchStrLen : 0;
if (startOffset > searchStrLen) // bad start offset
return -1;
if (patternLen > searchStrLen) // pattern is longer than string to search
return -1;
const PRUnichar *searchBuf = searchStr.GetUnicode();
const PRUnichar *patternBuf = patternStr.GetUnicode();
const PRUnichar *searchEnd = searchBuf + searchStrLen;
const PRUnichar *patEnd = patternBuf + patternLen;
if (searchBackwards)
{
// searching backwards
const PRUnichar *s = searchBuf + startOffset - patternLen;
while (s >= searchBuf)
{
if (CharsMatch(*patternBuf, *s)) // start potential match
{
const PRUnichar *t = s;
const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p);
while (p < patEnd && CharsMatch(*p, *t))
{
if (inWhitespace && !nsCRT::IsAsciiSpace(*p))
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*(t + 1)))
t ++;
inWhitespace = PR_FALSE;
}
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++;
p ++;
}
if (p == patEnd)
{
foundOffset = curMatchOffset;
goto done;
}
// could be smart about decrementing s here
}
s --;
}
}
else
{
// searching forwards
const PRUnichar *s = &searchBuf[startOffset];
while (s < searchEnd)
{
if (CharsMatch(*patternBuf, *s)) // start potential match
{
const PRUnichar *t = s;
const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p);
while (p < patEnd && CharsMatch(*p, *t))
{
if (inWhitespace && !nsCRT::IsAsciiSpace(*p))
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*(t + 1)))
t ++;
inWhitespace = PR_FALSE;
}
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++;
p ++;
}
if (p == patEnd)
{
foundOffset = curMatchOffset;
goto done;
}
// could be smart about incrementing s here
}
s ++;
}
}
done:
return foundOffset;
}
// utility method to discover which block we're in. The TSDoc interface doesn't give
// us this, because it can't assume a read-only document.
nsresult
nsFindAndReplace::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex)
{
PRInt32 blockIndex = 0;
PRBool isDone = PR_FALSE;
while (NS_SUCCEEDED(aDoc->IsDone(&isDone)) && !isDone)
{
aDoc->PrevBlock();
blockIndex ++;
}
*outBlockIndex = blockIndex;
return NS_OK;
}
nsresult
nsFindAndReplace::SetupDocForFind(nsITextServicesDocument *aDoc, PRInt32 *outBlockOffset)
{
nsresult rv;
nsITextServicesDocument::TSDBlockSelectionStatus blockStatus;
PRInt32 selOffset;
PRInt32 selLength;
// Set up the TSDoc. We are going to start searching thus:
//
// Searching forwards:
// Look forward from the end of the selection
// Searching backwards:
// Look backwards from the start of the selection
//
if (!mFindBackwards) // searching forwards
{
rv = aDoc->LastSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
// the TS doc points to the block we want.
*outBlockOffset = selOffset + selLength;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block after this one.
rv = aDoc->NextBlock();
*outBlockOffset = 0;
break;
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
*outBlockOffset = selOffset + selLength;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else //failed to get last sel block. Just start at beginning
{
rv = aDoc->FirstBlock();
}
}
else // searching backwards
{
rv = aDoc->FirstSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
// must be a text block before the selection, so
// offset should be at end of block.
*outBlockOffset = -1;
break;
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
// the TS doc points to the block we want.
*outBlockOffset = selOffset;
// if (selOffset > 0 && selLength > 0)
// *outBlockOffset -= 1;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block before this one.
rv = aDoc->PrevBlock();
*outBlockOffset = -1;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else
{
rv = aDoc->LastBlock();
}
}
return rv;
}
nsresult
nsFindAndReplace::SetupDocForReplace(nsITextServicesDocument *aDoc, const nsString &aFindStr, PRInt32 *outBlockOffset)
{
nsresult rv;
nsITextServicesDocument::TSDBlockSelectionStatus blockStatus;
PRInt32 selOffset;
PRInt32 selLength;
// Set up the TSDoc. We are going to start replacing thus:
//
// Searching forwards:
// Look forward from the end of the selection
// Searching backwards:
// Look backwards from the start of the selection
//
if (!mFindBackwards) // searching forwards
{
rv = aDoc->LastSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
// the TS doc points to the block we want.
*outBlockOffset = selOffset + selLength;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block after this one.
rv = aDoc->NextBlock();
*outBlockOffset = 0;
break;
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
*outBlockOffset = selOffset;
if (selLength != (PRInt32)aFindStr.Length())
*outBlockOffset += selLength;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else //failed to get last sel block. Just start at beginning
{
rv = aDoc->FirstBlock();
}
}
else // searching backwards
{
rv = aDoc->FirstSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
// the TS doc points to the block we want.
*outBlockOffset = selOffset;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block before this one.
rv = aDoc->PrevBlock();
*outBlockOffset = -1;
break;
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
*outBlockOffset = selOffset;
if (selLength == (PRInt32)aFindStr.Length())
*outBlockOffset += selLength;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else
{
rv = aDoc->LastBlock();
}
}
return rv;
}
nsresult
nsFindAndReplace::DoFind(nsITextServicesDocument *aTxtDoc, const nsString &aFindString, PRBool *aDidFind)
{
if (!aTxtDoc || !aDidFind)
return NS_ERROR_NULL_POINTER;
*aDidFind = PR_FALSE;
nsresult rv = NS_OK;
// Set up the TSDoc. We are going to start searching thus:
//
// Searching forwards:
// Look forward from the end of the selection
// Searching backwards:
// Look backwards from the start of the selection
//
PRBool done = PR_FALSE;
// Loop till we find a match or fail.
while ( !done )
{
PRBool atExtremum = PR_FALSE; // are we at the end (or start)
while ( NS_SUCCEEDED(aTxtDoc->IsDone(&atExtremum)) && !atExtremum )
{
nsString str;
rv = aTxtDoc->GetCurrentTextBlock(&str);
if (NS_FAILED(rv))
return rv;
if (!mCaseSensitive)
str.ToLowerCase();
PRInt32 foundOffset = FindInString(str, aFindString, mCurrentSelOffset, mFindBackwards);
if (foundOffset != -1)
{
mCurrentSelOffset = foundOffset; // reset for next call to DoFind().
// Match found. Select it, remember where it was, and quit.
aTxtDoc->SetSelection(foundOffset, aFindString.Length());
aTxtDoc->ScrollSelectionIntoView();
done = PR_TRUE;
*aDidFind = PR_TRUE;
break;
}
else
{
mCurrentSelOffset = -1; // reset for next block
// have we already been around once?
if (mWrappedOnce && (mCurrentBlockIndex == mStartBlockIndex))
{
done = PR_TRUE;
break;
}
// No match found in this block, try the next (or previous) one.
if (mFindBackwards) {
aTxtDoc->PrevBlock();
mCurrentBlockIndex--;
} else {
aTxtDoc->NextBlock();
mCurrentBlockIndex++;
}
}
} // while !atExtremum
// At end (or matched). Decide which it was...
if (!done)
{
// Hit end without a match. If we haven't passed this way already,
// then reset to the first/last block (depending on search direction).
if (!mWrappedOnce)
{
// Reset now.
mWrappedOnce = PR_TRUE;
// If not wrapping, give up.
if ( !mWrapFind ) {
done = PR_TRUE;
}
else
{
if ( mFindBackwards ) {
// Reset to last block.
rv = aTxtDoc->LastBlock();
// ugh
rv = GetCurrentBlockIndex(aTxtDoc, &mCurrentBlockIndex);
rv = aTxtDoc->LastBlock();
} else {
// Reset to first block.
rv = aTxtDoc->FirstBlock();
mCurrentBlockIndex = 0;
}
}
}
else
{
// already wrapped. This means no matches were found.
done = PR_TRUE;
}
}
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsFindAndReplace_h__
#define nsFindAndReplace_h__
#include "nsCOMPtr.h"
#include "nsIFindAndReplace.h"
#include "nsITextServicesDocument.h"
class nsFindAndReplace : public nsIFindAndReplace
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFINDANDREPLACE
nsFindAndReplace();
virtual ~nsFindAndReplace();
protected:
nsCOMPtr<nsITextServicesDocument> mTsDoc;
PRPackedBool mFindBackwards;
PRPackedBool mCaseSensitive;
PRPackedBool mWrapFind;
PRPackedBool mEntireWord;
PRInt32 mStartBlockIndex;
PRInt32 mStartSelOffset;
PRInt32 mCurrentBlockIndex;
PRInt32 mCurrentSelOffset;
PRPackedBool mWrappedOnce;
nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex);
nsresult SetupDocForFind(nsITextServicesDocument *aDoc, PRInt32 *outBlockOffset);
nsresult SetupDocForReplace(nsITextServicesDocument *aDoc, const nsString &aFindStr, PRInt32 *outBlockOffset);
nsresult DoFind(nsITextServicesDocument *aTxtDoc, const nsString &aFindString, PRBool *aDidFind);
};
#endif // nsFindAndReplace_h__

Просмотреть файл

@ -23,14 +23,22 @@
#include "nsIGenericFactory.h"
#include "nsTextServicesDocument.h"
#include "nsFindAndReplace.h"
#include "nsTextServicesCID.h"
#define NS_TEXTSERVICESFINDANDREPLACE_CID \
{ /* 8B0EEFE1-C4AE-11d4-A401-000064657374 */ \
0x8b0eefe1, 0xc4ae, 0x11d4, \
{ 0xa4, 0x1, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
//
// NOTE: This creates an instance of objects by using the default constructor
//
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextServicesDocument)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindAndReplace)
////////////////////////////////////////////////////////////////////////
// Define a table of CIDs implemented by this module along with other
@ -38,7 +46,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextServicesDocument)
// class name.
//
static nsModuleComponentInfo components[] = {
{ NULL, NS_TEXTSERVICESDOCUMENT_CID, NULL, nsTextServicesDocumentConstructor },
{ NULL, NS_TEXTSERVICESDOCUMENT_CID, "@mozilla.org/textservices/textservicesdocument;1", nsTextServicesDocumentConstructor },
{ NULL, NS_TEXTSERVICESFINDANDREPLACE_CID, NS_FINDANDREPLACE_CONTRACTID, nsFindAndReplaceConstructor },
};
////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -41,6 +41,7 @@ function SetupHTMLEditorCommands()
gHTMLEditorCommandManager.registerCommand("cmd_find", nsFindCommand);
gHTMLEditorCommandManager.registerCommand("cmd_findNext", nsFindNextCommand);
gHTMLEditorCommandManager.registerCommand("cmd_replace", nsReplaceCommand);
gHTMLEditorCommandManager.registerCommand("cmd_spelling", nsSpellingCommand);
gHTMLEditorCommandManager.registerCommand("cmd_insertChars", nsInsertCharsCommand);
@ -542,6 +543,20 @@ var nsFindNextCommand =
}
};
//-----------------------------------------------------------------------------------
var nsReplaceCommand =
{
isCommandEnabled: function(aCommand, dummy)
{
return (window.editorShell != null);
},
doCommand: function(aCommand)
{
window.editorShell.Replace();
}
};
//-----------------------------------------------------------------------------------
var nsSpellingCommand =
{

Просмотреть файл

@ -1225,6 +1225,7 @@ function DisableMenusForHTMLSource(disable)
// Edit menu items
DisableItem("menu_find", disable);
DisableItem("menu_findnext", disable);
DisableItem("menu_replace", disable);
DisableItem("menu_checkspelling", disable);
// Disable all items in the view menu except mode switch items
@ -1983,6 +1984,10 @@ function RemoveInapplicableUIElements()
if (findMenuItem)
findMenuItem.setAttribute("hidden", "true");
var replaceMenuItem = document.getElementById("menu_replace");
if (replaceMenuItem)
replaceMenuItem.setAttribute("hidden", "true");
var findSepItem = document.getElementById("sep_find");
if (findSepItem)
findSepItem.parentNode.removeChild(findSepItem);

Просмотреть файл

@ -168,6 +168,7 @@
<command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
<command id="cmd_findNext" oncommand="goDoCommand('cmd_findNext')" value="&findAgainCmd.label;"/>
<command id="cmd_replace" oncommand="goDoCommand('cmd_replace')" />
<command id="cmd_spelling" oncommand="goDoCommand('cmd_spelling')"/>
<command id="cmd_pasteQuote" oncommand="goDoCommand('cmd_pasteQuote')" value="&pasteAsQuotationCmd.label;"/>
@ -325,6 +326,7 @@
<menuseparator id="sep_find" class="hide-in-IM"/>
<menuitem id="menu_find" accesskey="&editfind.accesskey;" key="findkb" observes="cmd_find" value="&findCmd.label;"/>
<menuitem id="menu_findnext" accesskey="&editfindnext.accesskey;" key="findnextkb" observes="cmd_findNext"/>
<menuitem id="menu_replace" observes="cmd_replace" value="&replaceCmd.label;"/>
<menuseparator id="sep_checkspelling" class="hide-in-IM" />
<menuitem id="menu_checkspelling" accesskey="&editcheckspelling.accesskey;" key="checkspellingkb" observes="cmd_spelling" value="&checkSpellingCmd.label;"/>
<menuseparator/>

Просмотреть файл

@ -93,6 +93,7 @@
<!ENTITY findAgainCmd.label "Find Again">
<!ENTITY editfindnext.accesskey "n">
<!ENTITY editfindnext.keybinding "g">
<!ENTITY replaceCmd.label "Replace...">
<!ENTITY checkSpellingCmd.label "Check Spelling">
<!ENTITY editcheckspelling.accesskey "s">
<!ENTITY editcheckspelling.keybinding "k">

Просмотреть файл

@ -117,6 +117,7 @@ var defaultController =
case "cmd_pasteQuote":
case "cmd_find":
case "cmd_findNext":
case "cmd_replace":
case "cmd_account":
case "cmd_preferences":
@ -230,6 +231,7 @@ var defaultController =
case "cmd_pasteQuote":
case "cmd_find":
case "cmd_findNext":
case "cmd_replace":
//Disable the editor specific edit commands if the focus is not into the body
return /*!focusedElement*/false;
case "cmd_account":
@ -405,6 +407,7 @@ function CommandUpdate_MsgCompose()
goUpdateCommand("cmd_pasteQuote");
goUpdateCommand("cmd_find");
goUpdateCommand("cmd_findNext");
goUpdateCommand("cmd_replace");
goUpdateCommand("cmd_account");
goUpdateCommand("cmd_preferences");

Просмотреть файл

@ -283,6 +283,7 @@
<menuseparator/>
<menuitem value="&findCmd.label;" key="key_find" accesskey="&findCmd.accesskey;" observes="cmd_find"/>
<menuitem value="&findAgainCmd.label;" key="key_findNext" accesskey="&findAgainCmd.accesskey;" observes="cmd_findNext"/>
<menuitem value="&replaceCmd.label;" observes="cmd_replace"/>
<menuseparator/>
<menuitem value="&accountManagerCmd.label;" accesskey="&accountManagerCmd.accesskey;" observes="cmd_account"/>
<menuitem id="menu_preferences" observes="cmd_preferences"/>

Просмотреть файл

@ -46,6 +46,7 @@
<!ENTITY findAgainCmd.label "Find Again">
<!ENTITY findAgainCmd.key "G">
<!ENTITY findAgainCmd.accesskey "g">
<!ENTITY replaceCmd.label "Replace...">
<!ENTITY accountManagerCmd.label "Mail/News Account Settings...">
<!ENTITY accountManagerCmd.accesskey "m">

Просмотреть файл

@ -89,15 +89,6 @@ interface nsIFindComponent : nsIAppShellComponent
--------------------------------------------------------------------------*/
boolean find( in nsISupports aContext );
/*--------------------------------- Replace -----------------------------------
| Replace the currently selected text. It is intended that the string used |
| for replacement is sourced internally to the component (e.g. from a |
| search/replace dialog). |
| |
| Returns an error if the current context does not allow replacement. |
--------------------------------------------------------------------------*/
void replace( in nsISupports aContext );
/*------------------------------- FindNext ---------------------------------
| Finds the next occurrence (of the previously searched for string) in |
| the given search context (document). |
@ -107,6 +98,23 @@ interface nsIFindComponent : nsIAppShellComponent
--------------------------------------------------------------------------*/
boolean findNext( in nsISupports aContext );
/*--------------------------------- Replace --------------------------------
| Replace the currently selected text. It is intended that the string used |
| for replacement is sourced internally to the component (e.g. from a |
| search/replace dialog). |
| |
| Returns an error if the current context does not allow replacement. |
--------------------------------------------------------------------------*/
void replace( in nsISupports aContext );
/*------------------------------- ReplaceNext ------------------------------
| Replaces the currently selected text, then searches for next occurrence |
| (of previously searched for string) in the given search context |
| (document). If allOccurrences is TRUE, all occurrences of previously |
| searched for string will be replaced during this method call.
--------------------------------------------------------------------------*/
boolean replaceNext( in nsISupports aContext, in boolean allOccurrences );
/*----------------------------- ResetContext -------------------------------
| Reset the given search context to search a new web shell. Generally, |
| this will be the equivalent of calling Release() on the old context and |

Просмотреть файл

@ -44,4 +44,5 @@ interface nsISearchContext : nsISupports {
attribute boolean wrapSearch;
readonly attribute nsIDOMWindowInternal targetWindow;
attribute nsIDOMWindowInternal findDialog;
attribute nsIDOMWindowInternal replaceDialog;
};

Просмотреть файл

@ -1,3 +1,5 @@
finddialog.xul
finddialog.js
finddialog.js
replacedialog.xul
replacedialog.js

Просмотреть файл

@ -1 +1,2 @@
finddialog.dtd
finddialog.dtd
replacedialog.dtd

Просмотреть файл

@ -0,0 +1,15 @@
<!-- extracted from replacedialog.xul -->
<!ENTITY replaceDialog.title "Replace in This Page">
<!ENTITY replaceDialogFrame.title "Replace in This Frame">
<!ENTITY findField.label "Find what:">
<!ENTITY replaceField.label "Replace with:">
<!ENTITY caseSensitiveCheckbox.label "Match upper/lower case">
<!ENTITY wrapCheckbox.label "Wrap around">
<!ENTITY backwardsCheckbox.label "Search backwards">
<!ENTITY findField.tooltip "Type one or more words to search for">
<!ENTITY replaceField.tooltip "Type one or more words to replace with">
<!ENTITY findNextButton.label "Find Next">
<!ENTITY replaceButton.label "Replace">
<!ENTITY replaceAllButton.label "Replace All">
<!ENTITY closeButton.label "Close">

Просмотреть файл

@ -32,10 +32,13 @@ CHROME_L10N_DIR = locales\en-US\global\locale
CHROME_CONTENT = \
.\finddialog.xul \
.\finddialog.js \
.\replacedialog.xul \
.\replacedialog.js \
$(NULL)
CHROME_L10N = \
.\locale\en-US\finddialog.dtd \
.\locale\en-US\replacedialog.dtd \
$(NULL)
include <$(DEPTH)\config\rules.mak>

Просмотреть файл

@ -0,0 +1,200 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released March
* 31, 1998.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Kin Blas <kin@netscape.com>
*
*/
var gFindComponent; // Find component.
var gFindReplaceData; // Search context (passed as argument).
var gReplaceDialog; // Quick access to document/form elements.
function string2Bool( value )
{
return value != "false";
}
function bool2String( value )
{
if ( value )
return "true";
else
return "false";
}
function initDialog()
{
// Create gReplaceDialog object and initialize.
gReplaceDialog = new Object;
gReplaceDialog.findKey = document.getElementById("dialog.findKey");
gReplaceDialog.replaceKey = document.getElementById("dialog.replaceKey");
gReplaceDialog.caseSensitive = document.getElementById("dialog.caseSensitive");
gReplaceDialog.wrap = document.getElementById("dialog.wrap");
gReplaceDialog.searchBackwards = document.getElementById("dialog.searchBackwards");
gReplaceDialog.findNext = document.getElementById("findNext");
gReplaceDialog.replace = document.getElementById("replace");
gReplaceDialog.replaceAll = document.getElementById("replaceAll");
gReplaceDialog.close = document.getElementById("close");
gReplaceDialog.enabled = false;
}
function loadDialog()
{
// Set initial dialog field contents.
gReplaceDialog.findKey.setAttribute( "value", gFindReplaceData.searchString );
gReplaceDialog.replaceKey.setAttribute( "value", gFindReplaceData.replaceString );
if ( gFindReplaceData.caseSensitive ) {
gReplaceDialog.caseSensitive.setAttribute( "checked", "" );
} else {
gReplaceDialog.caseSensitive.removeAttribute( "checked" );
}
if ( gFindReplaceData.wrapSearch ) {
gReplaceDialog.wrap.setAttribute( "checked", "" );
} else {
gReplaceDialog.wrap.removeAttribute( "checked" );
}
if ( gFindReplaceData.searchBackwards ) {
gReplaceDialog.searchBackwards.setAttribute( "checked", "" );
} else {
gReplaceDialog.searchBackwards.removeAttribute( "checked" );
}
// disable the findNext button if no text
if (gReplaceDialog.findKey.getAttribute("value") == "") {
gReplaceDialog.findNext.setAttribute("disabled", "true");
}
// disable the replace and replaceAll buttons if no
// find or replace text
if (gReplaceDialog.findKey.getAttribute("value") == "") {
gReplaceDialog.replace.setAttribute("disabled", "true");
gReplaceDialog.replaceAll.setAttribute("disabled", "true");
}
gReplaceDialog.findKey.focus();
}
function loadData()
{
// Set gFindReplaceData attributes per user input.
gFindReplaceData.searchString = gReplaceDialog.findKey.value;
gFindReplaceData.replaceString = gReplaceDialog.replaceKey.value;
gFindReplaceData.caseSensitive = gReplaceDialog.caseSensitive.checked;
gFindReplaceData.wrapSearch = gReplaceDialog.wrap.checked;
gFindReplaceData.searchBackwards = gReplaceDialog.searchBackwards.checked;
}
function onLoad()
{
// Init gReplaceDialog.
initDialog();
// Get find component.
gFindComponent = Components.classes[ "@mozilla.org/appshell/component/find;1" ].getService();
gFindComponent = gFindComponent.QueryInterface( Components.interfaces.nsIFindComponent );
if ( !gFindComponent ) {
alert( "Error accessing find component\n" );
window.close();
return;
}
// Save search context.
gFindReplaceData = window.arguments[0];
// Tell search context about this dialog.
gFindReplaceData.replaceDialog = window;
// Fill dialog.
loadDialog();
// Give focus to search text field.
gReplaceDialog.findKey.focus();
}
function onUnload() {
// Disconnect context from this dialog.
gFindReplaceData.replaceDialog = null;
}
function onFindNext()
{
// Transfer dialog contents to data elements.
loadData();
// Search.
gFindComponent.findNext( gFindReplaceData );
return true;
}
function onReplace()
{
// Transfer dialog contents to data elements.
loadData();
// Replace.
gFindComponent.replaceNext( gFindReplaceData, false );
return true;
}
function onReplaceAll()
{
// Transfer dialog contents to data elements.
loadData();
// Replace.
gFindComponent.replaceNext( gFindReplaceData, true );
return true;
}
function onClose()
{
window.close();
return true;
}
function onTyping()
{
if ( gReplaceDialog.enabled ) {
// Disable findNext, replace, and replaceAll if they delete all the text.
if ( gReplaceDialog.findKey.value == "" ) {
gReplaceDialog.enabled = false;
gReplaceDialog.findNext.setAttribute("disabled", "true");
}
} else {
// Enable OK once the user types something.
if ( gReplaceDialog.findKey.value != "" ) {
gReplaceDialog.enabled = true;
gReplaceDialog.findNext.removeAttribute("disabled");
}
}
if ( gReplaceDialog.enabled ) {
gReplaceDialog.replace.removeAttribute("disabled");
gReplaceDialog.replaceAll.removeAttribute("disabled");
} else {
gReplaceDialog.replace.setAttribute("disabled", "true");
gReplaceDialog.replaceAll.setAttribute("disabled", "true");
}
}

Просмотреть файл

@ -0,0 +1,77 @@
<?xml version="1.0"?> <!-- -*- Mode: HTML -*- -->
<!--
The contents of this file are subject to the Netscape Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/NPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The Original Code is Mozilla Communicator client code, released
March 31, 1998.
The Initial Developer of the Original Code is Netscape
Communications Corporation. Portions created by Netscape are
Copyright (C) 1998-1999 Netscape Communications Corporation. All
Rights Reserved.
Contributor(s): Kin Blas <kin@netscape.com>
-->
<?xml-stylesheet href="chrome://navigator/skin/" type="text/css"?>
<?xul-overlay href="chrome://global/content/dialogOverlay.xul"?>
<!DOCTYPE window SYSTEM "chrome://global/locale/replacedialog.dtd">
<window xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="replaceDialog"
onload="onLoad();"
onunload="onUnload();"
title="&replaceDialog.title;"
debug="false"
class="dialog"
align="vertical"
persist="screenX screenY"
screenX="24" screenY="24">
<html:script language="JavaScript" src="chrome://global/content/replacedialog.js"/>
<box>
<!-- hack. we seem to need this separator to get it to render -->
<separator orient="vertical" class="thin"/>
<box orient="vertical">
<text class="label" value="&findField.label;"/>
<text class="label" value="&replaceField.label;"/>
</box>
<box orient="vertical">
<textfield id="dialog.findKey" oninput="return onTyping();"/>
<textfield id="dialog.replaceKey" oninput="return onTyping();"/>
<box orient="vertical" autostretch="never">
<checkbox id="dialog.caseSensitive" value="&caseSensitiveCheckbox.label;"/>
<checkbox id="dialog.wrap" value="&wrapCheckbox.label;"/>
<checkbox id="dialog.searchBackwards" value="&backwardsCheckbox.label;"/>
</box>
</box>
<box orient="vertical">
<button class="dialog" id="findNext" value="&findNextButton.label;" oncommand="onFindNext();"/>
<button class="dialog" id="replace" value="&replaceButton.label;" oncommand="onReplace();"/>
<button class="dialog" id="replaceAll" value="&replaceAllButton.label;" oncommand="onReplaceAll();"/>
<button class="dialog" id="close" value="&closeButton.label;" oncommand="onClose();"/>
</box>
</box>
<separator/>
<!-- Places to overlay common dialog buttons and keyset -->
<keyset id="keyset"/>
</window>

Просмотреть файл

@ -46,6 +46,8 @@
#include "nsWidgetsCID.h" // ugh! contractID, please
#include "nsFindComponent.h"
#include "nsIFindAndReplace.h"
#include "nsIEditor.h"
#ifdef DEBUG
@ -56,7 +58,7 @@
nsFindComponent::Context::Context()
{
NS_INIT_REFCNT();
// all our other members are self-initiating
// all our other members are self-initiating
}
@ -65,11 +67,18 @@ nsFindComponent::Context::~Context()
#ifdef DEBUG_law
printf( "\nnsFindComponent::Context destructor called\n\n" );
#endif
// Close the dialog (if there is one).
if ( mFindDialog ) {
mFindDialog->Close();
mFindDialog = 0;
}
// Close the dialog (if there is one).
if ( mReplaceDialog ) {
mReplaceDialog->Close();
mReplaceDialog = 0;
}
}
NS_IMETHODIMP
@ -81,19 +90,23 @@ nsFindComponent::Context::Init( nsIDOMWindowInternal *aWindow,
PRBool lastSearchBackward,
PRBool lastWrapSearch)
{
if (!aWindow)
return NS_ERROR_INVALID_ARG;
if (!aWindow)
return NS_ERROR_INVALID_ARG;
mEditorShell = aEditorShell; // don't AddRef
mTargetWindow = aWindow; // don't AddRef
mSearchString = lastSearchString;
mReplaceString = lastReplaceString;
mCaseSensitive = lastCaseSensitive;
mSearchBackwards = lastSearchBackward;
mWrapSearch = lastWrapSearch;
mFindDialog = 0;
return NS_OK;
mEditorShell = aEditorShell; // don't AddRef
mTargetWindow = aWindow; // don't AddRef
mSearchString = lastSearchString;
mReplaceString = lastReplaceString;
mCaseSensitive = lastCaseSensitive;
mSearchBackwards = lastSearchBackward;
mWrapSearch = lastWrapSearch;
mFindDialog = 0;
mReplaceDialog = 0;
nsresult rv = NS_OK;
mTSFind = do_CreateInstance(NS_FINDANDREPLACE_CONTRACTID, &rv);
return rv;
}
@ -120,30 +133,45 @@ nsFindComponent::Context::MakeTSDocument(nsIDOMWindowInternal* aWindow, nsITextS
if (NS_FAILED(rv) || !tempDoc)
return rv;
nsCOMPtr<nsIScriptGlobalObject> globalObj = do_QueryInterface(aWindow, &rv);
if (NS_FAILED(rv) || !globalObj)
return NS_ERROR_FAILURE;
if (mEditorShell)
{
nsCOMPtr<nsIEditor> editor;
rv = mEditorShell->GetEditor(getter_AddRefs(editor));
if (NS_FAILED(rv))
return rv;
if (!editor)
return NS_ERROR_FAILURE;
rv = tempDoc->InitWithEditor(editor);
if (NS_FAILED(rv))
return rv;
}
else
{
nsCOMPtr<nsIScriptGlobalObject> globalObj = do_QueryInterface(aWindow, &rv);
if (NS_FAILED(rv) || !globalObj)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocShell> docShell;
globalObj->GetDocShell(getter_AddRefs(docShell));
if (!docShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocShell> docShell;
globalObj->GetDocShell(getter_AddRefs(docShell));
if (!docShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresShell> presShell;
docShell->GetPresShell(getter_AddRefs(presShell));
if (!presShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresShell> presShell;
docShell->GetPresShell(getter_AddRefs(presShell));
if (!presShell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> document;
presShell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(document);
if (!domDoc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> document;
presShell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(document);
if (!domDoc)
return NS_ERROR_FAILURE;
// Initialize the text services document.
rv = tempDoc->InitWithDocument(domDoc, presShell);
if (NS_FAILED(rv))
return rv;
// Initialize the text services document.
rv = tempDoc->InitWithDocument(domDoc, presShell);
if (NS_FAILED(rv))
return rv;
}
// Return the resulting text services document.
*aDoc = tempDoc;
@ -152,278 +180,16 @@ nsFindComponent::Context::MakeTSDocument(nsIDOMWindowInternal* aWindow, nsITextS
return rv;
}
// ----------------------------------------------------------------
// CharsMatch
//
// Compare chars. Match if both are whitespace, or both are
// non whitespace and same char.
// ----------------------------------------------------------------
inline static PRBool CharsMatch(PRUnichar c1, PRUnichar c2)
{
return (nsCRT::IsAsciiSpace(c1) && nsCRT::IsAsciiSpace(c2)) ||
(c1 == c2);
}
// ----------------------------------------------------------------
// FindInString
//
// Routine to search in an nsString which is smart about extra
// whitespace, can search backwards, and do case insensitive search.
//
// This uses a brute-force algorithm, which should be sufficient
// for our purposes (text searching)
//
// searchStr contains the text from a content node, which can contain
// extra white space between words, which we have to deal with.
// The offsets passed in and back are offsets into searchStr,
// and thus include extra white space.
//
// If we are ignoring case, the strings have already been lowercased
// at this point.
//
// startOffset is the offset in the search string to start seraching
// at. If -1, it means search from the start (forwards) or end (backwards).
//
// Returns -1 if the string is not found, or if the pattern is an
// empty string, or if startOffset is off the end of the string.
// ----------------------------------------------------------------
static PRInt32 FindInString(const nsString &searchStr, const nsString &patternStr,
PRInt32 startOffset, PRBool searchBackwards)
{
PRInt32 foundOffset = -1;
PRInt32 patternLen = patternStr.Length();
PRInt32 searchStrLen = searchStr.Length();
if (patternLen == 0) // pattern is empty
return -1;
if (startOffset < 0)
startOffset = (searchBackwards) ? searchStrLen : 0;
if (startOffset > searchStrLen) // bad start offset
return -1;
if (patternLen > searchStrLen) // pattern is longer than string to search
return -1;
const PRUnichar *searchBuf = searchStr.GetUnicode();
const PRUnichar *patternBuf = patternStr.GetUnicode();
const PRUnichar *searchEnd = searchBuf + searchStrLen;
const PRUnichar *patEnd = patternBuf + patternLen;
if (searchBackwards)
{
// searching backwards
const PRUnichar *s = searchBuf + startOffset - patternLen - 1;
while (s >= searchBuf)
{
if (CharsMatch(*patternBuf, *s)) // start potential match
{
const PRUnichar *t = s;
const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p);
while (p < patEnd && CharsMatch(*p, *t))
{
if (inWhitespace && !nsCRT::IsAsciiSpace(*p))
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*(t + 1)))
t ++;
inWhitespace = PR_FALSE;
}
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++;
p ++;
}
if (p == patEnd)
{
foundOffset = curMatchOffset;
goto done;
}
// could be smart about decrementing s here
}
s --;
}
}
else
{
// searching forwards
const PRUnichar *s = &searchBuf[startOffset];
while (s < searchEnd)
{
if (CharsMatch(*patternBuf, *s)) // start potential match
{
const PRUnichar *t = s;
const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p);
while (p < patEnd && CharsMatch(*p, *t))
{
if (inWhitespace && !nsCRT::IsAsciiSpace(*p))
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*(t + 1)))
t ++;
inWhitespace = PR_FALSE;
}
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++;
p ++;
}
if (p == patEnd)
{
foundOffset = curMatchOffset;
goto done;
}
// could be smart about incrementing s here
}
s ++;
}
}
done:
return foundOffset;
}
// utility method to discover which block we're in. The TSDoc interface doesn't give
// us this, because it can't assume a read-only document.
NS_IMETHODIMP
nsFindComponent::Context::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex)
{
PRInt32 blockIndex = 0;
PRBool isDone = PR_FALSE;
while (NS_SUCCEEDED(aDoc->IsDone(&isDone)) && !isDone)
{
aDoc->PrevBlock();
blockIndex ++;
}
*outBlockIndex = blockIndex;
return NS_OK;
}
NS_IMETHODIMP
nsFindComponent::Context::SetupDocForSearch(nsITextServicesDocument *aDoc, PRInt32 *outBlockOffset)
{
nsresult rv;
nsITextServicesDocument::TSDBlockSelectionStatus blockStatus;
PRInt32 selOffset;
PRInt32 selLength;
if (!mSearchBackwards) // searching forwards
{
rv = aDoc->LastSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
// the TS doc points to the block we want.
*outBlockOffset = selOffset + selLength;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block after this one.
rv = aDoc->NextBlock();
*outBlockOffset = 0;
break;
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
*outBlockOffset = selOffset + selLength;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else //failed to get last sel block. Just start at beginning
{
rv = aDoc->FirstBlock();
}
}
else // searching backwards
{
rv = aDoc->FirstSelectedBlock(&blockStatus, &selOffset, &selLength);
if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
{
switch (blockStatus)
{
case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
// the TS doc points to the block we want.
*outBlockOffset = selOffset;
break;
case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
// we want the block before this one.
rv = aDoc->PrevBlock();
*outBlockOffset = -1;
break;
case nsITextServicesDocument::eBlockContains: // TB contains entire S.
*outBlockOffset = selOffset;
break;
case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
default:
NS_NOTREACHED("Shouldn't ever get this status");
}
}
else
{
rv = aDoc->LastBlock();
}
}
return rv;
}
NS_IMETHODIMP
nsFindComponent::Context::DoFind(PRBool *aDidFind)
{
if (!aDidFind)
return NS_ERROR_NULL_POINTER;
if (!mTargetWindow)
return NS_ERROR_NOT_INITIALIZED;
*aDidFind = PR_FALSE;
nsAutoString matchString(mSearchString);
if (!mCaseSensitive)
matchString.ToLowerCase();
*aDidFind = PR_FALSE;
if (!mTargetWindow)
return NS_ERROR_NOT_INITIALIZED;
nsresult rv = NS_OK;
@ -434,128 +200,63 @@ nsFindComponent::Context::DoFind(PRBool *aDidFind)
if (NS_FAILED(rv) || !txtDoc)
return rv;
// Set up the TSDoc. We are going to start searching thus:
//
// Searching forwards:
// Look forward from the end of the selection
// Searching backwards:
// Look backwards from the start of the selection
//
PRInt32 selOffset = 0;
rv = SetupDocForSearch(txtDoc, &selOffset);
if (NS_FAILED(rv))
return rv;
// find out where we started
PRInt32 blockIndex;
rv = GetCurrentBlockIndex(txtDoc, &blockIndex);
if (!mTSFind)
return NS_ERROR_NOT_INITIALIZED;
rv = mTSFind->SetCaseSensitive(mCaseSensitive);
rv = mTSFind->SetFindBackwards(mSearchBackwards);
rv = mTSFind->SetWrapFind(mWrapSearch);
rv = mTSFind->SetTsDoc(txtDoc);
if (NS_FAILED(rv))
return rv;
// remember where we started
PRInt32 startingBlockIndex = blockIndex;
// and set the starting position again (hopefully, in future we won't have to do this)
rv = SetupDocForSearch(txtDoc, &selOffset);
if (NS_FAILED(rv))
return rv;
PRBool wrappedOnce = PR_FALSE; // Remember whether we've already wrapped
PRBool done = PR_FALSE;
// Loop till we find a match or fail.
while ( !done )
{
PRBool atExtremum = PR_FALSE; // are we at the end (or start)
rv = mTSFind->Find(mSearchString.GetUnicode(), aDidFind);
while ( NS_SUCCEEDED(txtDoc->IsDone(&atExtremum)) && !atExtremum )
{
nsString str;
rv = txtDoc->GetCurrentTextBlock(&str);
if (NS_FAILED(rv))
return rv;
if (!mCaseSensitive)
str.ToLowerCase();
PRInt32 foundOffset = FindInString(str, matchString, selOffset, mSearchBackwards);
selOffset = -1; // reset for next block
if (foundOffset != -1)
{
// Match found. Select it, remember where it was, and quit.
txtDoc->SetSelection(foundOffset, mSearchString.Length());
txtDoc->ScrollSelectionIntoView();
done = PR_TRUE;
*aDidFind = PR_TRUE;
break;
}
else
{
// have we already been around once?
if (wrappedOnce && (blockIndex == startingBlockIndex))
{
done = PR_TRUE;
break;
}
mTSFind->SetTsDoc(nsnull);
// No match found in this block, try the next (or previous) one.
if (mSearchBackwards) {
txtDoc->PrevBlock();
blockIndex--;
} else {
txtDoc->NextBlock();
blockIndex++;
}
}
} // while !atExtremum
// At end (or matched). Decide which it was...
if (!done)
{
// Hit end without a match. If we haven't passed this way already,
// then reset to the first/last block (depending on search direction).
if (!wrappedOnce)
{
// Reset now.
wrappedOnce = PR_TRUE;
// If not wrapping, give up.
if ( !mWrapSearch ) {
done = PR_TRUE;
}
else
{
if ( mSearchBackwards ) {
// Reset to last block.
rv = txtDoc->LastBlock();
// ugh
rv = GetCurrentBlockIndex(txtDoc, &blockIndex);
rv = txtDoc->LastBlock();
} else {
// Reset to first block.
rv = txtDoc->FirstBlock();
blockIndex = 0;
}
}
}
else
{
// already wrapped. This means no matches were found.
done = PR_TRUE;
}
}
}
return NS_OK;
return rv;
}
NS_IMETHODIMP
nsFindComponent::Context::DoReplace()
nsFindComponent::Context::DoReplace(PRBool aAllOccurrences, PRBool *aDidFind)
{
return NS_ERROR_NOT_IMPLEMENTED;
if (!mTargetWindow)
return NS_ERROR_NOT_INITIALIZED;
if (!aDidFind)
return NS_ERROR_NULL_POINTER;
*aDidFind = PR_FALSE;
nsresult rv = NS_OK;
// Construct a text services document to use. This is freed when we
// return from this function.
nsCOMPtr<nsITextServicesDocument> txtDoc;
rv = MakeTSDocument(mTargetWindow, getter_AddRefs(txtDoc));
if (NS_FAILED(rv) || !txtDoc)
return rv;
if (!mTSFind)
return NS_ERROR_NOT_INITIALIZED;
rv = mTSFind->SetCaseSensitive(mCaseSensitive);
rv = mTSFind->SetFindBackwards(mSearchBackwards);
rv = mTSFind->SetWrapFind(mWrapSearch);
rv = mTSFind->SetTsDoc(txtDoc);
if (NS_FAILED(rv))
return rv;
rv = mTSFind->Replace(mSearchString.GetUnicode(),
mReplaceString.GetUnicode(),
aAllOccurrences, aDidFind);
mTSFind->SetTsDoc(nsnull);
return rv;
}
NS_IMETHODIMP
@ -688,6 +389,21 @@ nsFindComponent::Context::SetFindDialog( nsIDOMWindowInternal *aDialog )
return NS_OK;
}
NS_IMETHODIMP
nsFindComponent::Context::GetReplaceDialog( nsIDOMWindowInternal * *aDialog)
{
NS_ENSURE_ARG_POINTER(aDialog);
NS_IF_ADDREF(*aDialog = mReplaceDialog);
return NS_OK;
}
NS_IMETHODIMP
nsFindComponent::Context::SetReplaceDialog( nsIDOMWindowInternal *aDialog )
{
mReplaceDialog = aDialog;
return NS_OK;
}
#ifdef XP_MAC
#pragma mark -
#endif
@ -855,61 +571,129 @@ nsFindComponent::Find(nsISupports *aContext, PRBool *aDidFind)
}
NS_IMETHODIMP
nsFindComponent::Replace( nsISupports *aContext )
nsFindComponent::FindNext(nsISupports *aContext, PRBool *aDidFind)
{
if (!aContext)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
return NS_ERROR_NOT_IMPLEMENTED;
if (!aContext)
return NS_ERROR_NULL_POINTER;
Context *context = (Context*)aContext;
// If we haven't searched yet, put up dialog (via Find).
if ( context->mSearchString.IsEmpty() ) {
return this->Find( aContext, aDidFind );
}
context->DoFind(aDidFind);
// Record this for out-of-the-blue FindNext calls.
mLastSearchString = context->mSearchString;
mLastCaseSensitive = context->mCaseSensitive;
mLastSearchBackwards = context->mSearchBackwards;
mLastWrapSearch = context->mWrapSearch;
if (!*aDidFind)
{
static NS_DEFINE_IID(kSoundCID, NS_SOUND_CID);
nsCOMPtr<nsISound> sound = do_CreateInstance(kSoundCID);
if (sound)
sound->Beep();
}
return rv;
}
NS_IMETHODIMP
nsFindComponent::FindNext(nsISupports *aContext, PRBool *aDidFind)
nsFindComponent::Replace( nsISupports *aContext )
{
nsresult rv = NS_OK;
if (!aContext)
return NS_ERROR_NULL_POINTER;
Context *context = (Context*)aContext;
// If we haven't searched yet, put up dialog (via Find).
if ( context->mSearchString.IsEmpty() ) {
return this->Find( aContext, aDidFind );
// See if replace dialog is already up.
if ( aContext ) {
nsCOMPtr<nsISearchContext> context = do_QueryInterface( aContext, &rv );
if ( NS_SUCCEEDED( rv ) && context ) {
nsCOMPtr<nsIDOMWindowInternal> dialog;
rv = context->GetReplaceDialog( getter_AddRefs( dialog ) );
if ( NS_SUCCEEDED( rv ) && dialog ) {
// Just give focus back to the dialog.
dialog->Focus();
return NS_OK;
}
}
// Test for error (GetReplaceDialog succeeds if there's no dialog).
if ( NS_FAILED( rv ) ) {
DEBUG_PRINTF( PR_STDOUT, "%s %d: Error getting replace dialog, rv=0x%08X\n",
__FILE__, (int)__LINE__, (int)rv );
return rv;
}
}
context->DoFind(aDidFind);
// Record this for out-of-the-blue FindNext calls.
mLastSearchString = context->mSearchString;
mLastCaseSensitive = context->mCaseSensitive;
mLastSearchBackwards = context->mSearchBackwards;
mLastWrapSearch = context->mWrapSearch;
if (!*aDidFind)
if (aContext && GetAppShell())
{
static NS_DEFINE_IID(kSoundCID, NS_SOUND_CID);
nsCOMPtr<nsISound> sound = do_CreateInstance(kSoundCID);
if (sound)
sound->Beep();
nsCOMPtr<nsISearchContext> context = do_QueryInterface( aContext, &rv );
if (NS_FAILED(rv))
return rv;
// Open Replace dialog and prompt for search parameters.
char * urlStr = "chrome://global/content/replacedialog.xul";
// We need the parent's nsIDOMWindowInternal...
// 1. Get topLevelWindow nsIWebShellContainer (chrome included).
nsCOMPtr<nsIDOMWindowInternal> window;
rv = context->GetTargetWindow( getter_AddRefs( window ) );
if ( NS_SUCCEEDED( rv ) && window )
{
nsCOMPtr<nsIDOMWindow> topWindow;
window->GetTop(getter_AddRefs(topWindow));
if (topWindow) {
nsCOMPtr<nsIDOMWindowInternal> topInternal = do_QueryInterface(topWindow);
rv = OpenDialogWithArg(topInternal, context, urlStr);
}
}
} else {
rv = NS_ERROR_NULL_POINTER;
}
return rv;
}
NS_IMETHODIMP
nsFindComponent::ReplaceNext( nsISupports *aContext, PRBool aAllOccurrences, PRBool *aDidFind )
{
if (!aContext)
return NS_ERROR_NULL_POINTER;
Context *context = (Context*)aContext;
if (!context)
return NS_ERROR_FAILURE;
nsresult rv = context->DoReplace(aAllOccurrences, aDidFind);
// Record this for out-of-the-blue FindNext calls.
mLastSearchString = context->mSearchString;
mLastCaseSensitive = context->mCaseSensitive;
mLastSearchBackwards = context->mSearchBackwards;
mLastWrapSearch = context->mWrapSearch;
return rv;
}
NS_IMETHODIMP
nsFindComponent::ResetContext( nsISupports *aContext,
nsIDOMWindowInternal *aNewWindow,
nsIEditorShell* aEditorShell )
{
NS_ENSURE_ARG(aContext);
NS_ENSURE_ARG(aNewWindow);
NS_ENSURE_ARG(aContext);
NS_ENSURE_ARG(aNewWindow);
// Pass on the new document to the context.
Context *context = (Context*)aContext;
context->Reset(aNewWindow);
// Pass on the new document to the context.
Context *context = (Context*)aContext;
context->Reset(aNewWindow);
return NS_OK;
return NS_OK;
}
// nsFindComponent::Context implementation...

Просмотреть файл

@ -22,6 +22,8 @@
#include "nsIAppShellComponentImpl.h"
#include "nsIFindComponent.h"
#include "nsISearchContext.h"
#include "nsCOMPtr.h"
#include "nsIFindAndReplace.h"
class nsITextServicesDocument;
@ -50,37 +52,37 @@ public:
// "Context" for this implementation.
class Context : public nsISearchContext
{
public:
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISEARCHCONTEXT
Context();
virtual ~Context();
NS_IMETHOD Init( nsIDOMWindowInternal *aWindow,
nsIEditorShell* aEditorShell,
const nsString& lastSearchString,
const nsString& lastReplaceString,
PRBool lastCaseSensitive,
PRBool lastSearchBackwards,
PRBool lastWrapSearch);
Context();
virtual ~Context();
NS_IMETHOD Init( nsIDOMWindowInternal *aWindow,
nsIEditorShell* aEditorShell,
const nsString& lastSearchString,
const nsString& lastReplaceString,
PRBool lastCaseSensitive,
PRBool lastSearchBackwards,
PRBool lastWrapSearch);
NS_IMETHOD Reset(nsIDOMWindowInternal *aNewWindow);
NS_IMETHOD DoFind(PRBool *aDidFind);
NS_IMETHOD DoReplace();
NS_IMETHOD Reset(nsIDOMWindowInternal *aNewWindow);
NS_IMETHOD DoFind(PRBool *aDidFind);
NS_IMETHOD DoReplace(PRBool aAllOccurrences, PRBool *aDidFind);
// Utility to construct new TS document from our webshell.
NS_IMETHOD MakeTSDocument(nsIDOMWindowInternal* aWindow, nsITextServicesDocument** aDoc);
NS_IMETHOD GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex);
NS_IMETHOD SetupDocForSearch(nsITextServicesDocument *aDoc, PRInt32 *outBlockOffset);
nsIDOMWindowInternal* mTargetWindow; // weak link. Don't hold a reference
nsIEditorShell* mEditorShell; // weak link. Don't hold a reference
nsIDOMWindowInternal* mTargetWindow; // weak link. Don't hold a reference
nsIEditorShell* mEditorShell; // weak link. Don't hold a reference
nsCOMPtr<nsIFindAndReplace> mTSFind;
nsString mSearchString;
nsString mReplaceString;
PRBool mCaseSensitive;
PRBool mSearchBackwards;
PRBool mWrapSearch;
nsIDOMWindowInternal* mFindDialog;
nsIDOMWindowInternal* mReplaceDialog;
}; // nsFindComponent::Context

Просмотреть файл

@ -167,6 +167,7 @@ en-US.jar:
locale/en-US/global/console.properties (console/resources/locale/en-US/console.properties)
locale/en-US/global/finddialog.dtd (find/resources/locale/en-US/finddialog.dtd)
locale/en-US/global/finddialog.properties (find/resources/locale/en-US/finddialog.properties)
locale/en-US/global/replacedialog.dtd (find/resources/locale/en-US/replacedialog.dtd)
toolkit.jar:
content/global/console.js (console/resources/content/console.js)
@ -175,6 +176,8 @@ toolkit.jar:
content/global/consoleBindings.xml (console/resources/content/consoleBindings.xml)
content/global/finddialog.js (find/resources/finddialog.js)
content/global/finddialog.xul (find/resources/finddialog.xul)
content/global/replacedialog.js (find/resources/replacedialog.js)
content/global/replacedialog.xul (find/resources/replacedialog.xul)
content/global/unknownContent.xul (ucth/resources/unknownContent.xul)
content/global/unknownContent.js (ucth/resources/unknownContent.js)
content/global/helperAppLauncher.xul (ucth/resources/helperAppLauncher.xul)