diff --git a/build/mac/build_scripts/MozillaBuildList.pm b/build/mac/build_scripts/MozillaBuildList.pm index adff9a7f5295..a7c1a8cadde6 100644 --- a/build/mac/build_scripts/MozillaBuildList.pm +++ b/build/mac/build_scripts/MozillaBuildList.pm @@ -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"); diff --git a/editor/base/nsEditorShell.cpp b/editor/base/nsEditorShell.cpp index fa72faf6555f..a66e2df3842e 100644 --- a/editor/base/nsEditorShell.cpp +++ b/editor/base/nsEditorShell.cpp @@ -2920,7 +2920,7 @@ nsEditorShell::DoFind(PRBool aFindNext) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr 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 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 diff --git a/editor/composer/src/nsEditorShell.cpp b/editor/composer/src/nsEditorShell.cpp index fa72faf6555f..a66e2df3842e 100644 --- a/editor/composer/src/nsEditorShell.cpp +++ b/editor/composer/src/nsEditorShell.cpp @@ -2920,7 +2920,7 @@ nsEditorShell::DoFind(PRBool aFindNext) return NS_ERROR_NOT_INITIALIZED; nsCOMPtr 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 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 diff --git a/editor/idl/nsIEditorShell.idl b/editor/idl/nsIEditorShell.idl index 9c8ec67520a4..65811bf61716 100644 --- a/editor/idl/nsIEditorShell.idl +++ b/editor/idl/nsIEditorShell.idl @@ -174,6 +174,7 @@ interface nsIEditorShell : nsISupports void Find(); void FindNext(); + void Replace(); /* General Utilities */ diff --git a/editor/libeditor/txtsvc/nsTextServicesFactory.cpp b/editor/libeditor/txtsvc/nsTextServicesFactory.cpp index 416245fd7195..0481b7c2f033 100644 --- a/editor/libeditor/txtsvc/nsTextServicesFactory.cpp +++ b/editor/libeditor/txtsvc/nsTextServicesFactory.cpp @@ -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 }, }; //////////////////////////////////////////////////////////////////////// diff --git a/editor/txtsvc/public/MANIFEST_IDL b/editor/txtsvc/public/MANIFEST_IDL new file mode 100644 index 000000000000..9480fa13b3b4 --- /dev/null +++ b/editor/txtsvc/public/MANIFEST_IDL @@ -0,0 +1 @@ +nsIFindAndReplace.idl diff --git a/editor/txtsvc/public/Makefile.in b/editor/txtsvc/public/Makefile.in index ae27e28dde5e..fc8b09462b53 100644 --- a/editor/txtsvc/public/Makefile.in +++ b/editor/txtsvc/public/Makefile.in @@ -35,6 +35,10 @@ EXPORTS = \ nsTextServicesCID.h \ $(NULL) +XPIDLSRCS = \ + nsIFindAndReplace.idl \ + $(NULL) + EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS)) include $(topsrcdir)/config/rules.mk diff --git a/editor/txtsvc/public/makefile.win b/editor/txtsvc/public/makefile.win index 10bb211ce00e..eb3aa895a683 100644 --- a/editor/txtsvc/public/makefile.win +++ b/editor/txtsvc/public/makefile.win @@ -31,5 +31,9 @@ EXPORTS = \ MODULE = txtsvc +XPIDLSRCS = \ + .\nsIFindAndReplace.idl \ + $(NULL) + include <$(DEPTH)\config\rules.mak> diff --git a/editor/txtsvc/public/nsIFindAndReplace.idl b/editor/txtsvc/public/nsIFindAndReplace.idl new file mode 100644 index 000000000000..74538b566e3a --- /dev/null +++ b/editor/txtsvc/public/nsIFindAndReplace.idl @@ -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); +}; diff --git a/editor/txtsvc/src/Makefile.in b/editor/txtsvc/src/Makefile.in index ea0124df6586..ee829bafac1d 100644 --- a/editor/txtsvc/src/Makefile.in +++ b/editor/txtsvc/src/Makefile.in @@ -32,6 +32,7 @@ IS_COMPONENT = 1 REQUIRES = xpcom editor layout dom js locale widget CPPSRCS = \ + nsFindAndReplace.cpp \ nsTextServicesDocument.cpp \ nsTextServicesFactory.cpp \ nsTSDNotifier.cpp \ diff --git a/editor/txtsvc/src/makefile.win b/editor/txtsvc/src/makefile.win index 48c7c955ad68..c694cc08dbe4 100644 --- a/editor/txtsvc/src/makefile.win +++ b/editor/txtsvc/src/makefile.win @@ -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 \ diff --git a/editor/txtsvc/src/nsFindAndReplace.cpp b/editor/txtsvc/src/nsFindAndReplace.cpp new file mode 100644 index 000000000000..ef38dcb13e10 --- /dev/null +++ b/editor/txtsvc/src/nsFindAndReplace.cpp @@ -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; +} diff --git a/editor/txtsvc/src/nsFindAndReplace.h b/editor/txtsvc/src/nsFindAndReplace.h new file mode 100644 index 000000000000..4a53456186d4 --- /dev/null +++ b/editor/txtsvc/src/nsFindAndReplace.h @@ -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 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__ diff --git a/editor/txtsvc/src/nsTextServicesFactory.cpp b/editor/txtsvc/src/nsTextServicesFactory.cpp index 416245fd7195..0481b7c2f033 100644 --- a/editor/txtsvc/src/nsTextServicesFactory.cpp +++ b/editor/txtsvc/src/nsTextServicesFactory.cpp @@ -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 }, }; //////////////////////////////////////////////////////////////////////// diff --git a/editor/ui/composer/content/ComposerCommands.js b/editor/ui/composer/content/ComposerCommands.js index e43171e47818..ce395bdfa68a 100644 --- a/editor/ui/composer/content/ComposerCommands.js +++ b/editor/ui/composer/content/ComposerCommands.js @@ -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 = { diff --git a/editor/ui/composer/content/editor.js b/editor/ui/composer/content/editor.js index 6032f3da16d2..d36f064461c7 100644 --- a/editor/ui/composer/content/editor.js +++ b/editor/ui/composer/content/editor.js @@ -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); diff --git a/editor/ui/composer/content/editorOverlay.xul b/editor/ui/composer/content/editorOverlay.xul index 9caca88c33ea..100c36312c41 100644 --- a/editor/ui/composer/content/editorOverlay.xul +++ b/editor/ui/composer/content/editorOverlay.xul @@ -168,6 +168,7 @@ + @@ -325,6 +326,7 @@ + diff --git a/editor/ui/composer/locale/en-US/editorOverlay.dtd b/editor/ui/composer/locale/en-US/editorOverlay.dtd index ab7c295df657..afc89adc0fbd 100644 --- a/editor/ui/composer/locale/en-US/editorOverlay.dtd +++ b/editor/ui/composer/locale/en-US/editorOverlay.dtd @@ -93,6 +93,7 @@ + diff --git a/mailnews/compose/resources/content/MsgComposeCommands.js b/mailnews/compose/resources/content/MsgComposeCommands.js index 1d7a0b896d33..aaf0151a2640 100644 --- a/mailnews/compose/resources/content/MsgComposeCommands.js +++ b/mailnews/compose/resources/content/MsgComposeCommands.js @@ -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"); diff --git a/mailnews/compose/resources/content/messengercompose.xul b/mailnews/compose/resources/content/messengercompose.xul index 328be337a28b..d842dd72dda2 100644 --- a/mailnews/compose/resources/content/messengercompose.xul +++ b/mailnews/compose/resources/content/messengercompose.xul @@ -283,6 +283,7 @@ + diff --git a/mailnews/compose/resources/locale/en-US/messengercompose.dtd b/mailnews/compose/resources/locale/en-US/messengercompose.dtd index 450ea736e358..bb371683ca2f 100644 --- a/mailnews/compose/resources/locale/en-US/messengercompose.dtd +++ b/mailnews/compose/resources/locale/en-US/messengercompose.dtd @@ -46,6 +46,7 @@ + diff --git a/xpfe/components/find/public/nsIFindComponent.idl b/xpfe/components/find/public/nsIFindComponent.idl index 7c116ed7b724..c779ac21dcef 100644 --- a/xpfe/components/find/public/nsIFindComponent.idl +++ b/xpfe/components/find/public/nsIFindComponent.idl @@ -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 | diff --git a/xpfe/components/find/public/nsISearchContext.idl b/xpfe/components/find/public/nsISearchContext.idl index dcdea3beedd3..300c323feb73 100644 --- a/xpfe/components/find/public/nsISearchContext.idl +++ b/xpfe/components/find/public/nsISearchContext.idl @@ -44,4 +44,5 @@ interface nsISearchContext : nsISupports { attribute boolean wrapSearch; readonly attribute nsIDOMWindowInternal targetWindow; attribute nsIDOMWindowInternal findDialog; + attribute nsIDOMWindowInternal replaceDialog; }; diff --git a/xpfe/components/find/resources/MANIFEST b/xpfe/components/find/resources/MANIFEST index b4049a24f62b..6b70c797a676 100644 --- a/xpfe/components/find/resources/MANIFEST +++ b/xpfe/components/find/resources/MANIFEST @@ -1,3 +1,5 @@ finddialog.xul -finddialog.js \ No newline at end of file +finddialog.js +replacedialog.xul +replacedialog.js diff --git a/xpfe/components/find/resources/locale/en-US/MANIFEST b/xpfe/components/find/resources/locale/en-US/MANIFEST index da28181c5fee..001f0da13e98 100644 --- a/xpfe/components/find/resources/locale/en-US/MANIFEST +++ b/xpfe/components/find/resources/locale/en-US/MANIFEST @@ -1 +1,2 @@ -finddialog.dtd \ No newline at end of file +finddialog.dtd +replacedialog.dtd diff --git a/xpfe/components/find/resources/locale/en-US/replacedialog.dtd b/xpfe/components/find/resources/locale/en-US/replacedialog.dtd new file mode 100644 index 000000000000..cb9a0e6bf14a --- /dev/null +++ b/xpfe/components/find/resources/locale/en-US/replacedialog.dtd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/xpfe/components/find/resources/makefile.win b/xpfe/components/find/resources/makefile.win index 1b6c29d80ca6..326823214f54 100644 --- a/xpfe/components/find/resources/makefile.win +++ b/xpfe/components/find/resources/makefile.win @@ -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> diff --git a/xpfe/components/find/resources/replacedialog.js b/xpfe/components/find/resources/replacedialog.js new file mode 100644 index 000000000000..69f05c3195c8 --- /dev/null +++ b/xpfe/components/find/resources/replacedialog.js @@ -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 + * + */ + +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"); + } +} diff --git a/xpfe/components/find/resources/replacedialog.xul b/xpfe/components/find/resources/replacedialog.xul new file mode 100644 index 000000000000..3f8b7c6dc9ae --- /dev/null +++ b/xpfe/components/find/resources/replacedialog.xul @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +