Bug 313331 autocomplete file names in xul filepicker r=bz sr=jag

This commit is contained in:
neil%parkwaycc.co.uk 2006-04-12 23:00:52 +00:00
parent b9419bb330
commit 100b4af49b
7 changed files with 273 additions and 28 deletions

View File

@ -753,6 +753,7 @@ function gotoDirectory(directory) {
textInput.value = "";
}
textInput.focus();
textInput.setAttribute("autocompletesearchparam", directory.path);
sfile = directory;
}

View File

@ -101,6 +101,7 @@
<row align="center">
<label value="&textInput.label;" id="textInputLabel" control="textInput" accesskey="&textInput.accesskey;"/>
<textbox id="textInput" flex="1" oninput="doEnabling()"
type="autocomplete" autocompletesearch="file"
onfocus="onTextFieldFocus();"/>
</row>
<row id="filterBox" hidden="true" align="center">

View File

@ -52,7 +52,8 @@
this.ifSetAttribute("disableKeyNavigation", true);
// initialize the search sessions
this.searchSessions = this.getAttribute("searchSessions");
this.initSearchSessions();
this.initAutoCompleteSearch();
// hack to work around lack of bottom-up constructor calling
if ("initialize" in this.resultsPopup)
@ -82,35 +83,46 @@
onget="return this.getAttribute('focused') == 'true';"/>
<!-- space-delimited string of search session types to use -->
<property name="searchSessions" onget="return this.getAttribute('searchSessions')">
<setter><![CDATA[
val = val ? val : "";
var list = val.split(" ");
this.mSessions = {};
this.mListeners = {};
this.mLastResults = {};
this.mLastStatus = {};
<property name="searchSessions" readonly="true" onget="return this.getAttribute('searchSessions')"/>
for (var i in list) {
<method name="initSearchSessions">
<body><![CDATA[
var list = this.getAttribute("searchSessions").split(" ");
for (var i = 0; i < list.length; i++) {
var name = list[i];
if (name != "") {
var contractid = "@mozilla.org/autocompleteSession;1?type=" + name;
try {
var session =
Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSession);
this.addSession(session, name);
} catch (e) {
dump("### ERROR - unable to create search session \"" + session + "\".\n");
break;
dump("### ERROR - unable to create search session \"" + name + "\".\n");
}
this.mSessions[name] = session;
this.mListeners[name] = new (this.mAutoCompleteListener)(name);
this.mLastResults[name] = null;
this.mLastStatus[name] = null;
++this.sessionCount;
}
}
]]></setter>
</property>
]]></body>
</method>
<method name="initAutoCompleteSearch">
<body><![CDATA[
var list = this.getAttribute("autocompletesearch").split(" ");
for (var i = 0; i < list.length; i++) {
var name = list[i];
if (name != "") {
var contractid = "@mozilla.org/autocomplete/search;1?name=" + name;
try {
var search =
Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSearch);
var session = new (this.mAutoCompleteSession)(search);
this.addSession(session, name);
} catch (e) {
dump("### ERROR - unable to create search \"" + name + "\".\n");
}
}
}
]]></body>
</method>
<!-- the number of sessions currently in use -->
<field name="sessionCount">0</field>
@ -282,10 +294,10 @@
<!-- =================== PRIVATE PROPERTIES =================== -->
<field name="mSessions">null</field>
<field name="mListeners">null</field>
<field name="mLastResults">null</field>
<field name="mLastStatus">null</field>
<field name="mSessions">({})</field>
<field name="mListeners">({})</field>
<field name="mLastResults">({})</field>
<field name="mLastStatus">({})</field>
<field name="mLastKeyCode">null</field>
<field name="mAutoCompleteTimer">0</field>
<field name="mMenuOpen">false</field>
@ -300,14 +312,81 @@
<field name="oninit">null</field>
<field name="mDefaultMatchFilled">false</field>
<field name="mAutoCompleteSession"><![CDATA[
var session = function(aSession) { this.session = aSession };
session.prototype = {
session: null,
param: this,
onStartLookup: function(aSearchString, aPreviousSearchResult, aListener) {
this.session.startSearch(aSearchString,
this.param.getAttribute("autocompletesearchparam"),
aPreviousSearchResult && aPreviousSearchResult.lastResult,
aListener);
},
onStopLookup: function() {
this.session.stopSearch();
},
onAutoComplete: function() {
}
};
session;
]]></field>
<field name="mAutoCompleteListener"><![CDATA[
var listener = function(aSession) { this.sessionName = aSession };
var listener = function(aName) { this.sessionName = aName };
listener.prototype = {
param: this,
sessionName: null,
lastResult: null,
lastItems: [],
onAutoComplete: function(aResults, aStatus)
{
this.param.processResults(this.sessionName, aResults, aStatus);
},
onSearchResult: function(aSearch, aResult)
{
this.lastResult = aResult;
this.lastItems = new Array(aResult.matchCount);
const nsIAutoCompleteStatus = Components.interfaces.nsIAutoCompleteStatus;
var status = nsIAutoCompleteStatus.failed;
if (aResult.errorDescription) {
status = nsIAutoCompleteStatus.failureItems;
this.lastItems = [{
value: aResult.errorDescription,
comment: null,
className: null,
param: null
}];
}
else if (aResult.searchResult == aResult.RESULT_IGNORED)
status = nsIAutoCompleteStatus.ignored;
else if (aResult.searchResult == aResult.RESULT_NOMATCH)
status = nsIAutoCompleteStatus.nomatch;
else if (aResult.searchResult == aResult.RESULT_SUCCESS)
status = nsIAutoCompleteStatus.matchFound;
this.param.processResults(this.sessionName, {
searchString: aResult.searchString,
items: this,
defaultItemIndex: aResult.defaultIndex,
param: null,
lastResult: aResult
}, status);
},
Count: function() {
return this.lastItems.length;
},
QueryElementAt: function(aIndex, aIID) {
if (aIndex < 0 || aIndex >= this.lastItems.length)
return null;
if (!this.lastItems[aIndex]) {
this.lastItems[aIndex] = {
value: this.lastResult.getValueAt(aIndex),
comment: this.lastResult.getCommentAt(aIndex),
className: this.lastResult.getStyleAt(aIndex),
param: null
}
}
return this.lastItems[aIndex];
}
};
listener;
@ -414,13 +493,15 @@
<!-- add a session by reference -->
<method name="addSession">
<parameter name="aSession"/>
<parameter name="aName"/>
<body><![CDATA[
++this.sessionCount;
var name = "anon_"+this.sessionCount;
var name = aName || "anon_"+this.sessionCount;
this.mSessions[name] = aSession;
this.mListeners[name] = new (this.mAutoCompleteListener)(name);
this.mLastResults[name] = null;
this.mLastStatus[name] = null;
return this.mSessions[name];
]]></body>
</method>

View File

@ -311,7 +311,7 @@ function selectOnOK()
[file.path]);
promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
var rv = promptService.confirm(window, title, message);
var rv = promptService.confirm(window, confirmTitle, message);
if (rv) {
ret = nsIFilePicker.returnReplace;
retvals.directory = file.parent.path;
@ -725,11 +725,11 @@ function gotoDirectory(directory) {
window.setCursor("auto");
treeView.QueryInterface(nsITreeView).selection.clearSelection();
if (filePickerMode == nsIFilePicker.modeGetFolder) {
textInput.value = "";
}
textInput.focus();
textInput.setAttribute("autocompletesearchparam", directory.path);
sfile = directory;
}

View File

@ -102,6 +102,7 @@
<row align="center">
<label value="&textInput.label;" id="textInputLabel" control="textInput" accesskey="&textInput.accesskey;"/>
<textbox id="textInput" flex="1" oninput="doEnabling()"
type="autocomplete" autocompletesearch="file"
onfocus="onTextFieldFocus();"/>
</row>
<row id="filterBox" hidden="true" align="center">

View File

@ -60,6 +60,7 @@ REQUIRES = \
dom \
string \
locale \
autocomplete \
$(NULL)
CPPSRCS = \

View File

@ -42,7 +42,7 @@
#include "nsITreeSelection.h"
#include "nsITreeColumns.h"
#include "nsITreeBoxObject.h"
#include "nsIFile.h"
#include "nsILocalFile.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsCRT.h"
@ -53,11 +53,163 @@
#include "nsDateTimeFormatCID.h"
#include "nsQuickSort.h"
#include "nsIAtom.h"
#include "nsIAutoCompleteResult.h"
#include "nsIAutoCompleteSearch.h"
#include "nsISimpleEnumerator.h"
#include "nsAutoPtr.h"
#include "nsIMutableArray.h"
#include "nsWildCard.h"
#define NS_FILECOMPLETE_CID { 0xcb60980e, 0x18a5, 0x4a77, \
{ 0x91, 0x10, 0x81, 0x46, 0x61, 0x4c, 0xa7, 0xf0 } }
#define NS_FILECOMPLETE_CONTRACTID "@mozilla.org/autocomplete/search;1?name=file"
class nsFileResult : public nsIAutoCompleteResult
{
public:
// aSearchString is the text typed into the autocomplete widget
// aSearchParam is the picker's currently displayed directory
nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam);
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTOCOMPLETERESULT
nsStringArray mValues;
nsAutoString mSearchString;
PRInt32 mSlashPos;
PRUint16 mSearchResult;
};
NS_IMPL_ISUPPORTS1(nsFileResult, nsIAutoCompleteResult)
nsFileResult::nsFileResult(const nsAString& aSearchString,
const nsAString& aSearchParam):
mSearchString(aSearchString),
mSlashPos(mSearchString.RFindChar('/'))
{
if (aSearchString.IsEmpty())
mSearchResult = RESULT_IGNORED;
else {
mSearchResult = RESULT_FAILURE;
nsCOMPtr<nsILocalFile> directory;
nsDependentSubstring parent(Substring(mSearchString, 0, mSlashPos + 1));
if (mSlashPos != kNotFound)
NS_NewLocalFile(parent, PR_TRUE, getter_AddRefs(directory));
if (!directory) {
if (NS_FAILED(NS_NewLocalFile(aSearchParam, PR_TRUE, getter_AddRefs(directory))))
return;
if (mSlashPos > 0)
directory->AppendRelativePath(Substring(mSearchString, 0, mSlashPos));
}
nsCOMPtr<nsISimpleEnumerator> dirEntries;
if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(dirEntries))))
return;
mSearchResult = RESULT_NOMATCH;
PRBool hasMore = PR_FALSE;
nsDependentSubstring prefix(Substring(mSearchString, mSlashPos + 1));
while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> nextItem;
dirEntries->GetNext(getter_AddRefs(nextItem));
nsCOMPtr<nsILocalFile> nextFile(do_QueryInterface(nextItem));
nsAutoString fileName;
nextFile->GetLeafName(fileName);
if (StringBeginsWith(fileName, prefix)) {
fileName.Insert(parent, 0);
mValues.AppendString(fileName);
if (mSearchResult == RESULT_NOMATCH && fileName.Equals(mSearchString))
mSearchResult = RESULT_IGNORED;
else
mSearchResult = RESULT_SUCCESS;
}
}
mValues.Sort();
}
}
NS_IMETHODIMP nsFileResult::GetSearchString(nsAString & aSearchString)
{
aSearchString.Assign(mSearchString);
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetSearchResult(PRUint16 *aSearchResult)
{
NS_ENSURE_ARG_POINTER(aSearchResult);
*aSearchResult = mSearchResult;
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetDefaultIndex(PRInt32 *aDefaultIndex)
{
NS_ENSURE_ARG_POINTER(aDefaultIndex);
*aDefaultIndex = -1;
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetErrorDescription(nsAString & aErrorDescription)
{
aErrorDescription.Truncate();
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetMatchCount(PRUint32 *aMatchCount)
{
NS_ENSURE_ARG_POINTER(aMatchCount);
*aMatchCount = mValues.Count();
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetValueAt(PRInt32 index, nsAString & aValue)
{
mValues.StringAt(index, aValue);
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetCommentAt(PRInt32 index, nsAString & aComment)
{
aComment.Truncate();
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetStyleAt(PRInt32 index, nsAString & aStyle)
{
aStyle.Truncate();
return NS_OK;
}
NS_IMETHODIMP nsFileResult::RemoveValueAt(PRInt32 rowIndex, PRBool removeFromDb)
{
return NS_OK;
}
class nsFileComplete : public nsIAutoCompleteSearch
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTOCOMPLETESEARCH
};
NS_IMPL_ISUPPORTS1(nsFileComplete, nsIAutoCompleteSearch)
NS_IMETHODIMP
nsFileComplete::StartSearch(const nsAString& aSearchString,
const nsAString& aSearchParam,
nsIAutoCompleteResult *aPreviousResult,
nsIAutoCompleteObserver *aListener)
{
NS_ENSURE_ARG_POINTER(aListener);
nsRefPtr<nsFileResult> result = new nsFileResult(aSearchString, aSearchParam);
NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
return aListener->OnSearchResult(this, result);
}
NS_IMETHODIMP
nsFileComplete::StopSearch()
{
return NS_OK;
}
#define NS_FILEVIEW_CID { 0xa5570462, 0x1dd1, 0x11b2, \
{ 0x9d, 0x19, 0xdf, 0x30, 0xa2, 0x7f, 0xbd, 0xc4 } }
@ -104,10 +256,13 @@ protected:
};
// Factory constructor
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileComplete)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileView, Init)
static const nsModuleComponentInfo components[] =
{
{ "nsFileComplete", NS_FILECOMPLETE_CID,
NS_FILECOMPLETE_CONTRACTID, nsFileCompleteConstructor },
{ "nsFileView", NS_FILEVIEW_CID,
NS_FILEVIEW_CONTRACTID, nsFileViewConstructor }
};
@ -190,6 +345,11 @@ nsFileView::SetShowOnlyDirectories(PRBool aOnlyDirs)
} else {
// Run the filter again to get the file list back
FilterFiles();
SortArray(mFilteredFiles);
if (mReverseSort)
ReverseArray(mFilteredFiles);
if (mTree)
mTree->RowCountChanged(dirCount, mTotalRows - dirCount);
}