michael@0: // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const nsIFilePicker = Components.interfaces.nsIFilePicker; michael@0: const nsIProperties = Components.interfaces.nsIProperties; michael@0: const NS_DIRECTORYSERVICE_CONTRACTID = "@mozilla.org/file/directory_service;1"; michael@0: const NS_IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; michael@0: const nsITreeBoxObject = Components.interfaces.nsITreeBoxObject; michael@0: const nsIFileView = Components.interfaces.nsIFileView; michael@0: const NS_FILEVIEW_CONTRACTID = "@mozilla.org/filepicker/fileview;1"; michael@0: const nsITreeView = Components.interfaces.nsITreeView; michael@0: const nsILocalFile = Components.interfaces.nsILocalFile; michael@0: const nsIFile = Components.interfaces.nsIFile; michael@0: const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; michael@0: const NS_PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1"; michael@0: michael@0: var sfile = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile); michael@0: var retvals; michael@0: var filePickerMode; michael@0: var homeDir; michael@0: var treeView; michael@0: var allowURLs; michael@0: michael@0: var textInput; michael@0: var okButton; michael@0: michael@0: var gFilePickerBundle; michael@0: michael@0: // name of new directory entered by the user to be remembered michael@0: // for next call of newDir() in case something goes wrong with creation michael@0: var gNewDirName = { value: "" }; michael@0: michael@0: function filepickerLoad() { michael@0: gFilePickerBundle = document.getElementById("bundle_filepicker"); michael@0: michael@0: textInput = document.getElementById("textInput"); michael@0: okButton = document.documentElement.getButton("accept"); michael@0: treeView = Components.classes[NS_FILEVIEW_CONTRACTID].createInstance(nsIFileView); michael@0: michael@0: if (window.arguments) { michael@0: var o = window.arguments[0]; michael@0: retvals = o.retvals; /* set this to a global var so we can set return values */ michael@0: const title = o.title; michael@0: filePickerMode = o.mode; michael@0: if (o.displayDirectory) { michael@0: const directory = o.displayDirectory.path; michael@0: } michael@0: michael@0: const initialText = o.defaultString; michael@0: const filterTitles = o.filters.titles; michael@0: const filterTypes = o.filters.types; michael@0: const numFilters = filterTitles.length; michael@0: michael@0: document.title = title; michael@0: allowURLs = o.allowURLs; michael@0: michael@0: if (initialText) { michael@0: textInput.value = initialText; michael@0: } michael@0: } michael@0: michael@0: if (filePickerMode != nsIFilePicker.modeOpen && filePickerMode != nsIFilePicker.modeOpenMultiple) { michael@0: var newDirButton = document.getElementById("newDirButton"); michael@0: newDirButton.removeAttribute("hidden"); michael@0: } michael@0: michael@0: if (filePickerMode == nsIFilePicker.modeGetFolder) { michael@0: var textInputLabel = document.getElementById("textInputLabel"); michael@0: textInputLabel.value = gFilePickerBundle.getString("dirTextInputLabel"); michael@0: textInputLabel.accessKey = gFilePickerBundle.getString("dirTextInputAccesskey"); michael@0: } michael@0: michael@0: if ((filePickerMode == nsIFilePicker.modeOpen) || michael@0: (filePickerMode == nsIFilePicker.modeOpenMultiple) || michael@0: (filePickerMode == nsIFilePicker.modeSave)) { michael@0: michael@0: /* build filter popup */ michael@0: var filterPopup = document.createElement("menupopup"); michael@0: michael@0: for (var i = 0; i < numFilters; i++) { michael@0: var menuItem = document.createElement("menuitem"); michael@0: if (filterTypes[i] == "..apps") michael@0: menuItem.setAttribute("label", filterTitles[i]); michael@0: else michael@0: menuItem.setAttribute("label", filterTitles[i] + " (" + filterTypes[i] + ")"); michael@0: menuItem.setAttribute("filters", filterTypes[i]); michael@0: filterPopup.appendChild(menuItem); michael@0: } michael@0: michael@0: var filterMenuList = document.getElementById("filterMenuList"); michael@0: filterMenuList.appendChild(filterPopup); michael@0: if (numFilters > 0) michael@0: filterMenuList.selectedIndex = 0; michael@0: var filterBox = document.getElementById("filterBox"); michael@0: filterBox.removeAttribute("hidden"); michael@0: michael@0: filterMenuList.selectedIndex = o.filterIndex; michael@0: michael@0: treeView.setFilter(filterTypes[o.filterIndex]); michael@0: michael@0: } else if (filePickerMode == nsIFilePicker.modeGetFolder) { michael@0: treeView.showOnlyDirectories = true; michael@0: } michael@0: michael@0: // The dialog defaults to an "open" icon, change it to "save" if applicable michael@0: if (filePickerMode == nsIFilePicker.modeSave) michael@0: okButton.setAttribute("icon", "save"); michael@0: michael@0: // start out with a filename sort michael@0: handleColumnClick("FilenameColumn"); michael@0: michael@0: try { michael@0: setOKAction(); michael@0: } catch (exception) { michael@0: // keep it set to "OK" michael@0: } michael@0: michael@0: // setup the dialogOverlay.xul button handlers michael@0: retvals.buttonStatus = nsIFilePicker.returnCancel; michael@0: michael@0: var tree = document.getElementById("directoryTree"); michael@0: if (filePickerMode == nsIFilePicker.modeOpenMultiple) michael@0: tree.removeAttribute("seltype"); michael@0: michael@0: tree.treeBoxObject.view = treeView; michael@0: michael@0: // Start out with the ok button disabled since nothing will be michael@0: // selected and nothing will be in the text field. michael@0: okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder; michael@0: michael@0: // This allows the window to show onscreen before we begin michael@0: // loading the file list michael@0: michael@0: setTimeout(setInitialDirectory, 0, directory); michael@0: } michael@0: michael@0: function setInitialDirectory(directory) michael@0: { michael@0: // Start in the user's home directory michael@0: var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID] michael@0: .getService(nsIProperties); michael@0: homeDir = dirService.get("Home", Components.interfaces.nsIFile); michael@0: michael@0: if (directory) { michael@0: sfile.initWithPath(directory); michael@0: if (!sfile.exists() || !sfile.isDirectory()) michael@0: directory = false; michael@0: } michael@0: if (!directory) { michael@0: sfile.initWithPath(homeDir.path); michael@0: } michael@0: michael@0: gotoDirectory(sfile); michael@0: } michael@0: michael@0: function onFilterChanged(target) michael@0: { michael@0: // Do this on a timeout callback so the filter list can roll up michael@0: // and we don't keep the mouse grabbed while we are refiltering. michael@0: michael@0: setTimeout(changeFilter, 0, target.getAttribute("filters")); michael@0: } michael@0: michael@0: function changeFilter(filterTypes) michael@0: { michael@0: window.setCursor("wait"); michael@0: treeView.setFilter(filterTypes); michael@0: window.setCursor("auto"); michael@0: } michael@0: michael@0: function showErrorDialog(titleStrName, messageStrName, file) michael@0: { michael@0: var errorTitle = michael@0: gFilePickerBundle.getFormattedString(titleStrName, [file.path]); michael@0: var errorMessage = michael@0: gFilePickerBundle.getFormattedString(messageStrName, [file.path]); michael@0: var promptService = michael@0: Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); michael@0: michael@0: promptService.alert(window, errorTitle, errorMessage); michael@0: } michael@0: michael@0: function openOnOK() michael@0: { michael@0: var dir = treeView.selectedFiles.queryElementAt(0, nsIFile); michael@0: if (dir) michael@0: gotoDirectory(dir); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: function selectOnOK() michael@0: { michael@0: var errorTitle, errorMessage, promptService; michael@0: var ret = nsIFilePicker.returnOK; michael@0: michael@0: var isDir = false; michael@0: var isFile = false; michael@0: michael@0: retvals.filterIndex = document.getElementById("filterMenuList").selectedIndex; michael@0: retvals.fileURL = null; michael@0: michael@0: if (allowURLs) { michael@0: try { michael@0: var ios = Components.classes[NS_IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); michael@0: retvals.fileURL = ios.newURI(textInput.value, null, null); michael@0: var fileList = []; michael@0: if (retvals.fileURL instanceof Components.interfaces.nsIFileURL) michael@0: fileList.push(retvals.fileURL.file); michael@0: gFilesEnumerator.mFiles = fileList; michael@0: retvals.files = gFilesEnumerator; michael@0: retvals.buttonStatus = ret; michael@0: michael@0: return true; michael@0: } catch (e) { michael@0: } michael@0: } michael@0: michael@0: var fileList = processPath(textInput.value); michael@0: if (!fileList) { michael@0: // generic error message, should probably never happen michael@0: showErrorDialog("errorPathProblemTitle", michael@0: "errorPathProblemMessage", michael@0: textInput.value); michael@0: return false; michael@0: } michael@0: michael@0: var curFileIndex; michael@0: for (curFileIndex = 0; curFileIndex < fileList.length && michael@0: ret != nsIFilePicker.returnCancel; ++curFileIndex) { michael@0: var file = fileList[curFileIndex].QueryInterface(nsIFile); michael@0: michael@0: // try to normalize - if this fails we will ignore the error michael@0: // because we will notice the michael@0: // error later and show a fitting error alert. michael@0: try{ michael@0: file.normalize(); michael@0: } catch(e) { michael@0: //promptService.alert(window, "Problem", "normalize failed, continuing"); michael@0: } michael@0: michael@0: var fileExists = file.exists(); michael@0: michael@0: if (!fileExists && (filePickerMode == nsIFilePicker.modeOpen || michael@0: filePickerMode == nsIFilePicker.modeOpenMultiple)) { michael@0: showErrorDialog("errorOpenFileDoesntExistTitle", michael@0: "errorOpenFileDoesntExistMessage", michael@0: file); michael@0: return false; michael@0: } michael@0: michael@0: if (!fileExists && filePickerMode == nsIFilePicker.modeGetFolder) { michael@0: showErrorDialog("errorDirDoesntExistTitle", michael@0: "errorDirDoesntExistMessage", michael@0: file); michael@0: return false; michael@0: } michael@0: michael@0: if (fileExists) { michael@0: isDir = file.isDirectory(); michael@0: isFile = file.isFile(); michael@0: } michael@0: michael@0: switch(filePickerMode) { michael@0: case nsIFilePicker.modeOpen: michael@0: case nsIFilePicker.modeOpenMultiple: michael@0: if (isFile) { michael@0: if (file.isReadable()) { michael@0: retvals.directory = file.parent.path; michael@0: } else { michael@0: showErrorDialog("errorOpeningFileTitle", michael@0: "openWithoutPermissionMessage_file", michael@0: file); michael@0: ret = nsIFilePicker.returnCancel; michael@0: } michael@0: } else if (isDir) { michael@0: if (!sfile.equals(file)) { michael@0: gotoDirectory(file); michael@0: } michael@0: textInput.value = ""; michael@0: doEnabling(); michael@0: ret = nsIFilePicker.returnCancel; michael@0: } michael@0: break; michael@0: case nsIFilePicker.modeSave: michael@0: if (isFile) { // can only be true if file.exists() michael@0: if (!file.isWritable()) { michael@0: showErrorDialog("errorSavingFileTitle", michael@0: "saveWithoutPermissionMessage_file", michael@0: file); michael@0: ret = nsIFilePicker.returnCancel; michael@0: } else { michael@0: // we need to pop up a dialog asking if you want to save michael@0: var confirmTitle = gFilePickerBundle.getString("confirmTitle"); michael@0: var message = michael@0: gFilePickerBundle.getFormattedString("confirmFileReplacing", michael@0: [file.path]); michael@0: michael@0: promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); michael@0: var rv = promptService.confirm(window, confirmTitle, message); michael@0: if (rv) { michael@0: ret = nsIFilePicker.returnReplace; michael@0: retvals.directory = file.parent.path; michael@0: } else { michael@0: ret = nsIFilePicker.returnCancel; michael@0: } michael@0: } michael@0: } else if (isDir) { michael@0: if (!sfile.equals(file)) { michael@0: gotoDirectory(file); michael@0: } michael@0: textInput.value = ""; michael@0: doEnabling(); michael@0: ret = nsIFilePicker.returnCancel; michael@0: } else { michael@0: var parent = file.parent; michael@0: if (parent.exists() && parent.isDirectory() && parent.isWritable()) { michael@0: retvals.directory = parent.path; michael@0: } else { michael@0: var oldParent = parent; michael@0: while (!parent.exists()) { michael@0: oldParent = parent; michael@0: parent = parent.parent; michael@0: } michael@0: errorTitle = michael@0: gFilePickerBundle.getFormattedString("errorSavingFileTitle", michael@0: [file.path]); michael@0: if (parent.isFile()) { michael@0: errorMessage = michael@0: gFilePickerBundle.getFormattedString("saveParentIsFileMessage", michael@0: [parent.path, file.path]); michael@0: } else { michael@0: errorMessage = michael@0: gFilePickerBundle.getFormattedString("saveParentDoesntExistMessage", michael@0: [oldParent.path, file.path]); michael@0: } michael@0: if (!parent.isWritable()) { michael@0: errorMessage = michael@0: gFilePickerBundle.getFormattedString("saveWithoutPermissionMessage_dir", [parent.path]); michael@0: } michael@0: promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); michael@0: promptService.alert(window, errorTitle, errorMessage); michael@0: ret = nsIFilePicker.returnCancel; michael@0: } michael@0: } michael@0: break; michael@0: case nsIFilePicker.modeGetFolder: michael@0: if (isDir) { michael@0: retvals.directory = file.parent.path; michael@0: } else { // if nothing selected, the current directory will be fine michael@0: retvals.directory = sfile.path; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: gFilesEnumerator.mFiles = fileList; michael@0: michael@0: retvals.files = gFilesEnumerator; michael@0: retvals.buttonStatus = ret; michael@0: michael@0: return (ret != nsIFilePicker.returnCancel); michael@0: } michael@0: michael@0: var gFilesEnumerator = { michael@0: mFiles: null, michael@0: mIndex: 0, michael@0: michael@0: hasMoreElements: function() michael@0: { michael@0: return (this.mIndex < this.mFiles.length); michael@0: }, michael@0: getNext: function() michael@0: { michael@0: if (this.mIndex >= this.mFiles.length) michael@0: throw Components.results.NS_ERROR_FAILURE; michael@0: return this.mFiles[this.mIndex++]; michael@0: } michael@0: }; michael@0: michael@0: function onCancel() michael@0: { michael@0: // Close the window. michael@0: retvals.buttonStatus = nsIFilePicker.returnCancel; michael@0: retvals.file = null; michael@0: retvals.files = null; michael@0: return true; michael@0: } michael@0: michael@0: function onDblClick(e) { michael@0: // we only care about button 0 (left click) events michael@0: if (e.button != 0) return; michael@0: michael@0: var t = e.originalTarget; michael@0: if (t.localName != "treechildren") michael@0: return; michael@0: michael@0: openSelectedFile(); michael@0: } michael@0: michael@0: function openSelectedFile() { michael@0: var fileList = treeView.selectedFiles; michael@0: if (fileList.length == 0) michael@0: return; michael@0: michael@0: var file = fileList.queryElementAt(0, nsIFile); michael@0: if (file.isDirectory()) michael@0: gotoDirectory(file); michael@0: else if (file.isFile()) michael@0: document.documentElement.acceptDialog(); michael@0: } michael@0: michael@0: function onClick(e) { michael@0: var t = e.originalTarget; michael@0: if (t.localName == "treecol") michael@0: handleColumnClick(t.id); michael@0: } michael@0: michael@0: function convertColumnIDtoSortType(columnID) { michael@0: var sortKey; michael@0: michael@0: switch (columnID) { michael@0: case "FilenameColumn": michael@0: sortKey = nsIFileView.sortName; michael@0: break; michael@0: case "FileSizeColumn": michael@0: sortKey = nsIFileView.sortSize; michael@0: break; michael@0: case "LastModifiedColumn": michael@0: sortKey = nsIFileView.sortDate; michael@0: break; michael@0: default: michael@0: dump("unsupported sort column: " + columnID + "\n"); michael@0: sortKey = 0; michael@0: break; michael@0: } michael@0: michael@0: return sortKey; michael@0: } michael@0: michael@0: function handleColumnClick(columnID) { michael@0: var sortType = convertColumnIDtoSortType(columnID); michael@0: var sortOrder = (treeView.sortType == sortType) ? !treeView.reverseSort : false; michael@0: treeView.sort(sortType, sortOrder); michael@0: michael@0: // set the sort indicator on the column we are sorted by michael@0: var sortedColumn = document.getElementById(columnID); michael@0: if (treeView.reverseSort) { michael@0: sortedColumn.setAttribute("sortDirection", "descending"); michael@0: } else { michael@0: sortedColumn.setAttribute("sortDirection", "ascending"); michael@0: } michael@0: michael@0: // remove the sort indicator from the rest of the columns michael@0: var currCol = sortedColumn.parentNode.firstChild; michael@0: while (currCol) { michael@0: if (currCol != sortedColumn && currCol.localName == "treecol") michael@0: currCol.removeAttribute("sortDirection"); michael@0: currCol = currCol.nextSibling; michael@0: } michael@0: } michael@0: michael@0: function onKeypress(e) { michael@0: if (e.keyCode == 8) /* backspace */ michael@0: goUp(); michael@0: michael@0: /* enter is handled by the ondialogaccept handler */ michael@0: } michael@0: michael@0: function doEnabling() { michael@0: if (filePickerMode != nsIFilePicker.modeGetFolder) michael@0: // Maybe add check if textInput.value would resolve to an existing michael@0: // file or directory in .modeOpen. Too costly I think. michael@0: okButton.disabled = (textInput.value == "") michael@0: } michael@0: michael@0: function onTreeFocus(event) { michael@0: // Reset the button label and enabled/disabled state. michael@0: onFileSelected(treeView.selectedFiles); michael@0: } michael@0: michael@0: function setOKAction(file) { michael@0: var buttonLabel; michael@0: var buttonIcon = "open"; // used in all but one case michael@0: michael@0: if (file && file.isDirectory()) { michael@0: document.documentElement.setAttribute("ondialogaccept", "return openOnOK();"); michael@0: buttonLabel = gFilePickerBundle.getString("openButtonLabel"); michael@0: } michael@0: else { michael@0: document.documentElement.setAttribute("ondialogaccept", "return selectOnOK();"); michael@0: switch(filePickerMode) { michael@0: case nsIFilePicker.modeGetFolder: michael@0: buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel"); michael@0: break; michael@0: case nsIFilePicker.modeOpen: michael@0: case nsIFilePicker.modeOpenMultiple: michael@0: buttonLabel = gFilePickerBundle.getString("openButtonLabel"); michael@0: break; michael@0: case nsIFilePicker.modeSave: michael@0: buttonLabel = gFilePickerBundle.getString("saveButtonLabel"); michael@0: buttonIcon = "save"; michael@0: break; michael@0: } michael@0: } michael@0: okButton.setAttribute("label", buttonLabel); michael@0: okButton.setAttribute("icon", buttonIcon); michael@0: } michael@0: michael@0: function onSelect(event) { michael@0: onFileSelected(treeView.selectedFiles); michael@0: } michael@0: michael@0: function onFileSelected(/* nsIArray */ selectedFileList) { michael@0: var validFileSelected = false; michael@0: var invalidSelection = false; michael@0: var file; michael@0: var fileCount = selectedFileList.length; michael@0: michael@0: for (var index = 0; index < fileCount; ++index) { michael@0: file = selectedFileList.queryElementAt(index, nsIFile); michael@0: if (file) { michael@0: var path = file.leafName; michael@0: michael@0: if (path) { michael@0: var isDir = file.isDirectory(); michael@0: if ((filePickerMode == nsIFilePicker.modeGetFolder) || !isDir) { michael@0: if (!validFileSelected) michael@0: textInput.value = ""; michael@0: addToTextFieldValue(path); michael@0: } michael@0: michael@0: if (isDir && fileCount > 1) { michael@0: // The user has selected multiple items, and one of them is michael@0: // a directory. This is not a valid state, so we'll disable michael@0: // the ok button. michael@0: invalidSelection = true; michael@0: } michael@0: michael@0: validFileSelected = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (validFileSelected) { michael@0: setOKAction(file); michael@0: okButton.disabled = invalidSelection; michael@0: } else if (filePickerMode != nsIFilePicker.modeGetFolder) michael@0: okButton.disabled = (textInput.value == ""); michael@0: } michael@0: michael@0: function addToTextFieldValue(path) michael@0: { michael@0: var newValue = ""; michael@0: michael@0: if (textInput.value == "") michael@0: newValue = path.replace(/\"/g, "\\\""); michael@0: else { michael@0: // Quote the existing text if needed, michael@0: // then append the new filename (quoted and escaped) michael@0: if (textInput.value[0] != '"') michael@0: newValue = '"' + textInput.value.replace(/\"/g, "\\\"") + '"'; michael@0: else michael@0: newValue = textInput.value; michael@0: michael@0: newValue = newValue + ' "' + path.replace(/\"/g, "\\\"") + '"'; michael@0: } michael@0: michael@0: textInput.value = newValue; michael@0: } michael@0: michael@0: function onTextFieldFocus() { michael@0: setOKAction(null); michael@0: doEnabling(); michael@0: } michael@0: michael@0: function onDirectoryChanged(target) michael@0: { michael@0: var path = target.getAttribute("label"); michael@0: michael@0: var file = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile); michael@0: file.initWithPath(path); michael@0: michael@0: if (!sfile.equals(file)) { michael@0: // Do this on a timeout callback so the directory list can roll up michael@0: // and we don't keep the mouse grabbed while we are loading. michael@0: michael@0: setTimeout(gotoDirectory, 0, file); michael@0: } michael@0: } michael@0: michael@0: function populateAncestorList(directory) { michael@0: var menu = document.getElementById("lookInMenu"); michael@0: michael@0: while (menu.hasChildNodes()) { michael@0: menu.removeChild(menu.firstChild); michael@0: } michael@0: michael@0: var menuItem = document.createElement("menuitem"); michael@0: menuItem.setAttribute("label", directory.path); michael@0: menuItem.setAttribute("crop", "start"); michael@0: menu.appendChild(menuItem); michael@0: michael@0: // .parent is _sometimes_ null, see bug 121489. Do a dance around that. michael@0: var parent = directory.parent; michael@0: while (parent && !parent.equals(directory)) { michael@0: menuItem = document.createElement("menuitem"); michael@0: menuItem.setAttribute("label", parent.path); michael@0: menuItem.setAttribute("crop", "start"); michael@0: menu.appendChild(menuItem); michael@0: directory = parent; michael@0: parent = directory.parent; michael@0: } michael@0: michael@0: var menuList = document.getElementById("lookInMenuList"); michael@0: menuList.selectedIndex = 0; michael@0: } michael@0: michael@0: function goUp() { michael@0: try { michael@0: var parent = sfile.parent; michael@0: } catch(ex) { dump("can't get parent directory\n"); } michael@0: michael@0: if (parent) { michael@0: gotoDirectory(parent); michael@0: } michael@0: } michael@0: michael@0: function goHome() { michael@0: gotoDirectory(homeDir); michael@0: } michael@0: michael@0: function newDir() { michael@0: var file; michael@0: var promptService = michael@0: Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); michael@0: var dialogTitle = michael@0: gFilePickerBundle.getString("promptNewDirTitle"); michael@0: var dialogMsg = michael@0: gFilePickerBundle.getString("promptNewDirMessage"); michael@0: var ret = promptService.prompt(window, dialogTitle, dialogMsg, gNewDirName, null, {value:0}); michael@0: michael@0: if (ret) { michael@0: file = processPath(gNewDirName.value); michael@0: if (!file) { michael@0: showErrorDialog("errorCreateNewDirTitle", michael@0: "errorCreateNewDirMessage", michael@0: file); michael@0: return false; michael@0: } michael@0: michael@0: file = file[0].QueryInterface(nsIFile); michael@0: if (file.exists()) { michael@0: showErrorDialog("errorNewDirDoesExistTitle", michael@0: "errorNewDirDoesExistMessage", michael@0: file); michael@0: return false; michael@0: } michael@0: michael@0: var parent = file.parent; michael@0: if (!(parent.exists() && parent.isDirectory() && parent.isWritable())) { michael@0: var oldParent = parent; michael@0: while (!parent.exists()) { michael@0: oldParent = parent; michael@0: parent = parent.parent; michael@0: } michael@0: if (parent.isFile()) { michael@0: showErrorDialog("errorCreateNewDirTitle", michael@0: "errorCreateNewDirIsFileMessage", michael@0: parent); michael@0: return false; michael@0: } michael@0: if (!parent.isWritable()) { michael@0: showErrorDialog("errorCreateNewDirTitle", michael@0: "errorCreateNewDirPermissionMessage", michael@0: parent); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: try { michael@0: file.create(nsIFile.DIRECTORY_TYPE, 0755); michael@0: } catch (e) { michael@0: showErrorDialog("errorCreateNewDirTitle", michael@0: "errorCreateNewDirMessage", michael@0: file); michael@0: return false; michael@0: } michael@0: file.normalize(); // ... in case ".." was used in the path michael@0: gotoDirectory(file); michael@0: // we remember and reshow a dirname if something goes wrong michael@0: // so that errors can be corrected more easily. If all went well, michael@0: // reset the default value to blank michael@0: gNewDirName = { value: "" }; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: function gotoDirectory(directory) { michael@0: window.setCursor("wait"); michael@0: try { michael@0: populateAncestorList(directory); michael@0: treeView.setDirectory(directory); michael@0: document.getElementById("errorShower").selectedIndex = 0; michael@0: } catch(ex) { michael@0: document.getElementById("errorShower").selectedIndex = 1; michael@0: } michael@0: michael@0: window.setCursor("auto"); michael@0: michael@0: if (filePickerMode == nsIFilePicker.modeGetFolder) { michael@0: textInput.value = ""; michael@0: } michael@0: textInput.focus(); michael@0: textInput.setAttribute("autocompletesearchparam", directory.path); michael@0: sfile = directory; michael@0: } michael@0: michael@0: function toggleShowHidden(event) { michael@0: treeView.showHiddenFiles = !treeView.showHiddenFiles; michael@0: } michael@0: michael@0: // from the current directory and whatever was entered michael@0: // in the entry field, try to make a new path. This michael@0: // uses "/" as the directory separator, "~" as a shortcut michael@0: // for the home directory (but only when seen at the start michael@0: // of a path), and ".." to denote the parent directory. michael@0: // returns an array of the files listed, michael@0: // or false if an error occurred. michael@0: function processPath(path) michael@0: { michael@0: var fileArray = new Array(); michael@0: var strLength = path.length; michael@0: michael@0: if (path[0] == '"' && filePickerMode == nsIFilePicker.modeOpenMultiple && michael@0: strLength > 1) { michael@0: // we have a quoted list of filenames, separated by spaces. michael@0: // iterate the list and process each file. michael@0: michael@0: var curFileStart = 1; michael@0: michael@0: while (1) { michael@0: var nextQuote; michael@0: michael@0: // Look for an unescaped quote michael@0: var quoteSearchStart = curFileStart + 1; michael@0: do { michael@0: nextQuote = path.indexOf('"', quoteSearchStart); michael@0: quoteSearchStart = nextQuote + 1; michael@0: } while (nextQuote != -1 && path[nextQuote - 1] == '\\'); michael@0: michael@0: if (nextQuote == -1) { michael@0: // we have a filename with no trailing quote. michael@0: // just assume that the filename ends at the end of the string. michael@0: michael@0: if (!processPathEntry(path.substring(curFileStart), fileArray)) michael@0: return false; michael@0: break; michael@0: } michael@0: michael@0: if (!processPathEntry(path.substring(curFileStart, nextQuote), fileArray)) michael@0: return false; michael@0: michael@0: curFileStart = path.indexOf('"', nextQuote + 1); michael@0: if (curFileStart == -1) { michael@0: // no more quotes, but if we're not at the end of the string, michael@0: // go ahead and process the remaining text. michael@0: michael@0: if (nextQuote < strLength - 1) michael@0: if (!processPathEntry(path.substring(nextQuote + 1), fileArray)) michael@0: return false; michael@0: break; michael@0: } michael@0: ++curFileStart; michael@0: } michael@0: } else { michael@0: // If we didn't start with a quote, assume we just have a single file. michael@0: if (!processPathEntry(path, fileArray)) michael@0: return false; michael@0: } michael@0: michael@0: return fileArray; michael@0: } michael@0: michael@0: function processPathEntry(path, fileArray) michael@0: { michael@0: var filePath; michael@0: var file; michael@0: michael@0: try { michael@0: file = sfile.clone().QueryInterface(nsILocalFile); michael@0: } catch(e) { michael@0: dump("Couldn't clone\n"+e); michael@0: return false; michael@0: } michael@0: michael@0: var tilde_file = file.clone(); michael@0: tilde_file.append("~"); michael@0: if (path[0] == '~' && // Expand ~ to $HOME, except: michael@0: !(path == "~" && tilde_file.exists()) && // If ~ was entered and such a file exists, don't expand michael@0: (path.length == 1 || path[1] == "/")) // We don't want to expand ~file to ${HOME}file michael@0: filePath = homeDir.path + path.substring(1); michael@0: else michael@0: filePath = path; michael@0: michael@0: // Unescape quotes michael@0: filePath = filePath.replace(/\\\"/g, "\""); michael@0: michael@0: if (filePath[0] == '/') /* an absolute path was entered */ michael@0: file.initWithPath(filePath); michael@0: else if ((filePath.indexOf("/../") > 0) || michael@0: (filePath.substr(-3) == "/..") || michael@0: (filePath.substr(0,3) == "../") || michael@0: (filePath == "..")) { michael@0: /* appendRelativePath doesn't allow .. */ michael@0: try{ michael@0: file.initWithPath(file.path + "/" + filePath); michael@0: } catch (e) { michael@0: dump("Couldn't init path\n"+e); michael@0: return false; michael@0: } michael@0: } michael@0: else { michael@0: try { michael@0: file.appendRelativePath(filePath); michael@0: } catch (e) { michael@0: dump("Couldn't append path\n"+e); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: fileArray[fileArray.length] = file; michael@0: return true; michael@0: }