80805: patch up find/replace, particularly replace all. r=cmanske sr=dveditz a=asa
git-svn-id: svn://10.0.0.236/trunk@116693 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -26,21 +26,22 @@
|
||||
var gReplaceDialog; // Quick access to document/form elements.
|
||||
var gFindInst; // nsIWebBrowserFind that we're going to use
|
||||
var gFindService; // Global service which remembers find params
|
||||
var gEditor; // the editor we're using
|
||||
|
||||
function initDialogObject()
|
||||
{
|
||||
// Create gReplaceDialog object and initialize.
|
||||
gReplaceDialog = new Object();
|
||||
gReplaceDialog.findKey = document.getElementById("dialog.findKey");
|
||||
gReplaceDialog.replaceKey = document.getElementById("dialog.replaceKey");
|
||||
gReplaceDialog.findInput = document.getElementById("dialog.findInput");
|
||||
gReplaceDialog.replaceInput = document.getElementById("dialog.replaceInput");
|
||||
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.replaceAndFind = document.getElementById("replaceAndFind");
|
||||
gReplaceDialog.replaceAll = document.getElementById("replaceAll");
|
||||
gReplaceDialog.bundle = null;
|
||||
gReplaceDialog.editor = null;
|
||||
gEditor = null;
|
||||
}
|
||||
|
||||
function loadDialog()
|
||||
@@ -48,10 +49,10 @@ function loadDialog()
|
||||
// Set initial dialog field contents.
|
||||
// Set initial dialog field contents. Use the gFindInst attributes first,
|
||||
// this is necessary for window.find()
|
||||
gReplaceDialog.findKey.value = (gFindInst.searchString
|
||||
gReplaceDialog.findInput.value = (gFindInst.searchString
|
||||
? gFindInst.searchString
|
||||
: gFindService.searchString);
|
||||
gReplaceDialog.replaceKey.value = gFindService.replaceString;
|
||||
gReplaceDialog.replaceInput.value = gFindService.replaceString;
|
||||
gReplaceDialog.caseSensitive.checked = (gFindInst.matchCase
|
||||
? gFindInst.matchCase
|
||||
: gFindService.matchCase);
|
||||
@@ -67,13 +68,10 @@ function loadDialog()
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
dump("EdReplace: onLoad()\n");
|
||||
|
||||
// Get the xul <editor> element:
|
||||
var editorXUL = window.opener.document.getElementById("content-frame");
|
||||
|
||||
// Get the nsIWebBrowserFind service:
|
||||
//gFindInst = editorXUL.docShell.QueryInterface(Components.interfaces.nsIWebBrowserFind);
|
||||
gFindInst = editorXUL.webBrowserFind;
|
||||
|
||||
try {
|
||||
@@ -86,7 +84,7 @@ function onLoad()
|
||||
initDialogObject();
|
||||
|
||||
try {
|
||||
gReplaceDialog.editor = editorXUL.editor.QueryInterface(Components.interfaces.nsIPlaintextEditor);
|
||||
gEditor = editorXUL.editorShell.editor.QueryInterface(Components.interfaces.nsIPlaintextEditor);
|
||||
} catch(e) { dump("Couldn't get an editor! " + e + "\n"); }
|
||||
// If we don't get the editor, then we won't allow replacing.
|
||||
|
||||
@@ -96,10 +94,10 @@ function onLoad()
|
||||
// Fill dialog.
|
||||
loadDialog();
|
||||
|
||||
if (gReplaceDialog.findKey.value)
|
||||
gReplaceDialog.findKey.select();
|
||||
if (gReplaceDialog.findInput.value)
|
||||
gReplaceDialog.findInput.select();
|
||||
else
|
||||
gReplaceDialog.findKey.focus();
|
||||
gReplaceDialog.findInput.focus();
|
||||
}
|
||||
|
||||
function onUnload() {
|
||||
@@ -112,7 +110,7 @@ function saveFindData()
|
||||
// Set data attributes per user input.
|
||||
if (gFindService)
|
||||
{
|
||||
gFindService.searchString = gReplaceDialog.findKey.value;
|
||||
gFindService.searchString = gReplaceDialog.findInput.value;
|
||||
gFindService.matchCase = gReplaceDialog.caseSensitive.checked;
|
||||
gFindService.wrapFind = gReplaceDialog.wrap.checked;
|
||||
gFindService.findBackwards = gReplaceDialog.searchBackwards.checked;
|
||||
@@ -121,7 +119,7 @@ function saveFindData()
|
||||
|
||||
function setUpFindInst()
|
||||
{
|
||||
gFindInst.searchString = gReplaceDialog.findKey.value;
|
||||
gFindInst.searchString = gReplaceDialog.findInput.value;
|
||||
gFindInst.matchCase = gReplaceDialog.caseSensitive.checked;
|
||||
gFindInst.wrapFind = gReplaceDialog.wrap.checked;
|
||||
gFindInst.findBackwards = gReplaceDialog.searchBackwards.checked;
|
||||
@@ -139,51 +137,165 @@ function onFindNext()
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if (!gReplaceDialog.bundle)
|
||||
gReplaceDialog.bundle = document.getElementById("findBundle");
|
||||
window.alert(gReplaceDialog.bundle.getString("notFoundWarning"));
|
||||
gReplaceDialog.findKey.select();
|
||||
gReplaceDialog.findKey.focus();
|
||||
var bundle = document.getElementById("findBundle");
|
||||
window.alert(bundle.getString("notFoundWarning"));
|
||||
SetTextboxFocus(gReplaceDialog.findKey);
|
||||
gReplaceDialog.findInput.select();
|
||||
gReplaceDialog.findInput.focus();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function onReplace()
|
||||
{
|
||||
if (!gReplaceDialog.editor)
|
||||
if (!gEditor)
|
||||
return;
|
||||
|
||||
// Does the current selection match the find string?
|
||||
var selection = gEditor.selection;
|
||||
|
||||
var selStr = selection.toString();
|
||||
var specStr = gReplaceDialog.findInput.value;
|
||||
if (!gReplaceDialog.caseSensitive.checked)
|
||||
{
|
||||
selStr = selStr.toLowerCase();
|
||||
specStr = specStr.toLowerCase();
|
||||
}
|
||||
// Unfortunately, because of whitespace we can't just check
|
||||
// whether (selStr == specStr), but have to loop ourselves.
|
||||
// N chars of whitespace in specStr can match any M >= N in selStr.
|
||||
var matches = true;
|
||||
var specLen = specStr.length;
|
||||
var selLen = selStr.length;
|
||||
if (selLen < specLen)
|
||||
matches = false;
|
||||
else
|
||||
{
|
||||
specArray = specStr.match(/\S+|\s+/g);
|
||||
selArray = selStr.match(/\S+|\s+/g);
|
||||
if ( specArray.length != selArray.length)
|
||||
matches = false;
|
||||
else
|
||||
{
|
||||
for (i=0; i<selArray.length; i++)
|
||||
{
|
||||
if (selArray[i] != specArray[i])
|
||||
{
|
||||
if ( selArray[i][0].search(/\s/) == -1 ||
|
||||
specArray[i][0].search(/\s/) == -1)
|
||||
{
|
||||
// lowercase \s, not a space chunk -- match fails
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
else if ( selArray[i].length < specArray[i].length )
|
||||
{
|
||||
// if it's a space chunk then we only care that sel be
|
||||
// at least as long as spec
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the current selection doesn't match the pattern,
|
||||
// then we want to find the next match, but not do the replace.
|
||||
// That's what most other apps seem to do.
|
||||
if (!matches)
|
||||
return onFindNext();
|
||||
|
||||
// Transfer dialog contents to the find service.
|
||||
saveFindData();
|
||||
|
||||
gReplaceDialog.editor.insertText(gReplaceDialog.replaceKey.value);
|
||||
gEditor.insertText(gReplaceDialog.replaceInput.value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function onReplaceAll()
|
||||
{
|
||||
if (!gEditor)
|
||||
return;
|
||||
|
||||
var findStr = gReplaceDialog.findInput.value;
|
||||
var repStr = gReplaceDialog.replaceInput.value;
|
||||
|
||||
// Transfer dialog contents to the find service.
|
||||
saveFindData();
|
||||
// set up the find instance
|
||||
setUpFindInst();
|
||||
|
||||
// Search.
|
||||
while (true) {
|
||||
var result = gFindInst.findNext();
|
||||
dump("result of find: " + result + "\n");
|
||||
if (!result)
|
||||
break;
|
||||
gReplaceDialog.editor.insertText(gReplaceDialog.replaceKey.value);
|
||||
var finder = Components.classes["@mozilla.org/embedcomp/rangefind;1"].createInstance().QueryInterface(Components.interfaces.nsIFind);
|
||||
|
||||
finder.caseSensitive = gReplaceDialog.caseSensitive.checked;
|
||||
finder.findBackwards = gReplaceDialog.searchBackwards.checked;
|
||||
|
||||
// Make a range containing the current selection,
|
||||
// so we don't go past it when we wrap.
|
||||
var selection = gEditor.selection;
|
||||
var selecRange;
|
||||
if (selection.rangeCount > 0)
|
||||
selecRange = selection.getRangeAt(0);
|
||||
var origRange = selecRange.cloneRange();
|
||||
|
||||
// We'll need a range for the whole document:
|
||||
var wholeDocRange = gEditor.document.createRange();
|
||||
var rootNode = gEditor.rootElement.QueryInterface(Components.interfaces.nsIDOMNode);
|
||||
wholeDocRange.selectNodeContents(rootNode);
|
||||
|
||||
// And start and end points:
|
||||
var startPt = gEditor.document.createRange();
|
||||
startPt.setStart(wholeDocRange.startContainer, wholeDocRange.startOffset);
|
||||
startPt.setEnd(wholeDocRange.startContainer, wholeDocRange.startOffset);
|
||||
|
||||
var endPt = gEditor.document.createRange();
|
||||
endPt.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset);
|
||||
endPt.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset);
|
||||
|
||||
// Find and replace from here to end of document:
|
||||
var foundRange;
|
||||
while ((foundRange = finder.Find(findStr, wholeDocRange,
|
||||
selecRange, endPt)) != null)
|
||||
{
|
||||
gEditor.selection.removeAllRanges();
|
||||
gEditor.selection.addRange(foundRange);
|
||||
gEditor.insertText(repStr);
|
||||
selection = gEditor.selection;
|
||||
if (selection.rangeCount <= 0) {
|
||||
return;
|
||||
}
|
||||
selecRange = selection.getRangeAt(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
// If wrapping, find from start of document back to start point.
|
||||
selecRange.setStart(wholeDocRange.startContainer, wholeDocRange.startOffset);
|
||||
selecRange.setEnd(wholeDocRange.startContainer, wholeDocRange.startOffset);
|
||||
// Collapse origRange to start:
|
||||
origRange.setEnd(origRange.startContainer, origRange.startOffset);
|
||||
if (gReplaceDialog.wrap.checked)
|
||||
{
|
||||
while ((foundRange = finder.Find(findStr, wholeDocRange,
|
||||
selecRange, origRange)) != null)
|
||||
{
|
||||
gEditor.selection.removeAllRanges();
|
||||
gEditor.selection.addRange(foundRange);
|
||||
gEditor.insertText(repStr);
|
||||
selection = gEditor.selection;
|
||||
if (selection.rangeCount <= 0) {
|
||||
return;
|
||||
}
|
||||
selecRange = selection.getRangeAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doEnabling()
|
||||
{
|
||||
gReplaceDialog.enabled = gReplaceDialog.findKey.value;
|
||||
gReplaceDialog.findNext.disabled = !gReplaceDialog.findKey.value;
|
||||
gReplaceDialog.replace.disabled = (!gReplaceDialog.findKey.value
|
||||
|| !gReplaceDialog.replaceKey.value);
|
||||
gReplaceDialog.replaceAll.disabled = (!gReplaceDialog.findKey.value
|
||||
|| !gReplaceDialog.replaceKey.value);
|
||||
var findStr = gReplaceDialog.findInput.value;
|
||||
var repStr = gReplaceDialog.replaceInput.value;
|
||||
gReplaceDialog.enabled = findStr;
|
||||
gReplaceDialog.findNext.disabled = !findStr;
|
||||
gReplaceDialog.replace.disabled = (!findStr || !repStr);
|
||||
gReplaceDialog.replaceAndFind.disabled = (!findStr || !repStr);
|
||||
gReplaceDialog.replaceAll.disabled = (!findStr || !repStr);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ Contributor(s): Akkana Peck <akkana@netscape.com>
|
||||
|
||||
<dialog id="replaceDlg" title="&replaceDialog.title;"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
persist="screenX screenY"
|
||||
buttons="cancel"
|
||||
onload = "onLoad()"
|
||||
ondialogaccept="return onFindNext();">
|
||||
@@ -43,29 +44,37 @@ Contributor(s): Akkana Peck <akkana@netscape.com>
|
||||
<stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
|
||||
|
||||
<hbox>
|
||||
|
||||
<!-- hack. we seem to need this separator to get it to render -->
|
||||
<separator orient="vertical" class="thin"/>
|
||||
|
||||
<vbox>
|
||||
<label value="&findField.label;"/>
|
||||
<separator class="thin"/>
|
||||
<label value="&replaceField.label;"/>
|
||||
</vbox>
|
||||
|
||||
<vbox>
|
||||
<textbox id="dialog.findKey" oninput="doEnabling();"/>
|
||||
<textbox id="dialog.replaceKey" oninput="doEnabling();"/>
|
||||
<vbox align="start">
|
||||
<checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;"/>
|
||||
<checkbox id="dialog.wrap" label="&wrapCheckbox.label;"/>
|
||||
<checkbox id="dialog.searchBackwards" label="&backwardsCheckbox.label;"/>
|
||||
</vbox>
|
||||
<spacer class="spacer"/>
|
||||
<grid align="start">
|
||||
<columns><column/><column/></columns>
|
||||
<rows>
|
||||
<row align="center">
|
||||
<label value="&findField.label;"/>
|
||||
<textbox id="dialog.findInput" oninput="doEnabling();"/>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label value="&replaceField.label;"/>
|
||||
<textbox id="dialog.replaceInput" oninput="doEnabling();"/>
|
||||
</row>
|
||||
<row align="start">
|
||||
<spacer/>
|
||||
<vbox align="start">
|
||||
<spacer class="bigspacer"/>
|
||||
<checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;"/>
|
||||
<checkbox id="dialog.wrap" label="&wrapCheckbox.label;"/>
|
||||
<checkbox id="dialog.searchBackwards" label="&backwardsCheckbox.label;"/>
|
||||
</vbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<button id="findNext" label="&findNextButton.label;" oncommand="onFindNext();" default="true"/>
|
||||
<button id="replace" label="&replaceButton.label;" oncommand="onReplace();"/>
|
||||
<button id="replaceAndFind" label="&replaceAndFindButton.label;" oncommand="onReplace(); onFindNext();"/>
|
||||
<button id="replaceAll" label="&replaceAllButton.label;" oncommand="onReplaceAll();"/>
|
||||
<button dlgtype="cancel" label="&closeButton.label;"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</dialog>
|
||||
|
||||
Reference in New Issue
Block a user