1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/filepicker/content/filepicker.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,837 @@ 1.4 +// -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +const nsIFilePicker = Components.interfaces.nsIFilePicker; 1.11 +const nsIProperties = Components.interfaces.nsIProperties; 1.12 +const NS_DIRECTORYSERVICE_CONTRACTID = "@mozilla.org/file/directory_service;1"; 1.13 +const NS_IOSERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"; 1.14 +const nsITreeBoxObject = Components.interfaces.nsITreeBoxObject; 1.15 +const nsIFileView = Components.interfaces.nsIFileView; 1.16 +const NS_FILEVIEW_CONTRACTID = "@mozilla.org/filepicker/fileview;1"; 1.17 +const nsITreeView = Components.interfaces.nsITreeView; 1.18 +const nsILocalFile = Components.interfaces.nsILocalFile; 1.19 +const nsIFile = Components.interfaces.nsIFile; 1.20 +const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1"; 1.21 +const NS_PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1"; 1.22 + 1.23 +var sfile = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile); 1.24 +var retvals; 1.25 +var filePickerMode; 1.26 +var homeDir; 1.27 +var treeView; 1.28 +var allowURLs; 1.29 + 1.30 +var textInput; 1.31 +var okButton; 1.32 + 1.33 +var gFilePickerBundle; 1.34 + 1.35 +// name of new directory entered by the user to be remembered 1.36 +// for next call of newDir() in case something goes wrong with creation 1.37 +var gNewDirName = { value: "" }; 1.38 + 1.39 +function filepickerLoad() { 1.40 + gFilePickerBundle = document.getElementById("bundle_filepicker"); 1.41 + 1.42 + textInput = document.getElementById("textInput"); 1.43 + okButton = document.documentElement.getButton("accept"); 1.44 + treeView = Components.classes[NS_FILEVIEW_CONTRACTID].createInstance(nsIFileView); 1.45 + 1.46 + if (window.arguments) { 1.47 + var o = window.arguments[0]; 1.48 + retvals = o.retvals; /* set this to a global var so we can set return values */ 1.49 + const title = o.title; 1.50 + filePickerMode = o.mode; 1.51 + if (o.displayDirectory) { 1.52 + const directory = o.displayDirectory.path; 1.53 + } 1.54 + 1.55 + const initialText = o.defaultString; 1.56 + const filterTitles = o.filters.titles; 1.57 + const filterTypes = o.filters.types; 1.58 + const numFilters = filterTitles.length; 1.59 + 1.60 + document.title = title; 1.61 + allowURLs = o.allowURLs; 1.62 + 1.63 + if (initialText) { 1.64 + textInput.value = initialText; 1.65 + } 1.66 + } 1.67 + 1.68 + if (filePickerMode != nsIFilePicker.modeOpen && filePickerMode != nsIFilePicker.modeOpenMultiple) { 1.69 + var newDirButton = document.getElementById("newDirButton"); 1.70 + newDirButton.removeAttribute("hidden"); 1.71 + } 1.72 + 1.73 + if (filePickerMode == nsIFilePicker.modeGetFolder) { 1.74 + var textInputLabel = document.getElementById("textInputLabel"); 1.75 + textInputLabel.value = gFilePickerBundle.getString("dirTextInputLabel"); 1.76 + textInputLabel.accessKey = gFilePickerBundle.getString("dirTextInputAccesskey"); 1.77 + } 1.78 + 1.79 + if ((filePickerMode == nsIFilePicker.modeOpen) || 1.80 + (filePickerMode == nsIFilePicker.modeOpenMultiple) || 1.81 + (filePickerMode == nsIFilePicker.modeSave)) { 1.82 + 1.83 + /* build filter popup */ 1.84 + var filterPopup = document.createElement("menupopup"); 1.85 + 1.86 + for (var i = 0; i < numFilters; i++) { 1.87 + var menuItem = document.createElement("menuitem"); 1.88 + if (filterTypes[i] == "..apps") 1.89 + menuItem.setAttribute("label", filterTitles[i]); 1.90 + else 1.91 + menuItem.setAttribute("label", filterTitles[i] + " (" + filterTypes[i] + ")"); 1.92 + menuItem.setAttribute("filters", filterTypes[i]); 1.93 + filterPopup.appendChild(menuItem); 1.94 + } 1.95 + 1.96 + var filterMenuList = document.getElementById("filterMenuList"); 1.97 + filterMenuList.appendChild(filterPopup); 1.98 + if (numFilters > 0) 1.99 + filterMenuList.selectedIndex = 0; 1.100 + var filterBox = document.getElementById("filterBox"); 1.101 + filterBox.removeAttribute("hidden"); 1.102 + 1.103 + filterMenuList.selectedIndex = o.filterIndex; 1.104 + 1.105 + treeView.setFilter(filterTypes[o.filterIndex]); 1.106 + 1.107 + } else if (filePickerMode == nsIFilePicker.modeGetFolder) { 1.108 + treeView.showOnlyDirectories = true; 1.109 + } 1.110 + 1.111 + // The dialog defaults to an "open" icon, change it to "save" if applicable 1.112 + if (filePickerMode == nsIFilePicker.modeSave) 1.113 + okButton.setAttribute("icon", "save"); 1.114 + 1.115 + // start out with a filename sort 1.116 + handleColumnClick("FilenameColumn"); 1.117 + 1.118 + try { 1.119 + setOKAction(); 1.120 + } catch (exception) { 1.121 + // keep it set to "OK" 1.122 + } 1.123 + 1.124 + // setup the dialogOverlay.xul button handlers 1.125 + retvals.buttonStatus = nsIFilePicker.returnCancel; 1.126 + 1.127 + var tree = document.getElementById("directoryTree"); 1.128 + if (filePickerMode == nsIFilePicker.modeOpenMultiple) 1.129 + tree.removeAttribute("seltype"); 1.130 + 1.131 + tree.treeBoxObject.view = treeView; 1.132 + 1.133 + // Start out with the ok button disabled since nothing will be 1.134 + // selected and nothing will be in the text field. 1.135 + okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder; 1.136 + 1.137 + // This allows the window to show onscreen before we begin 1.138 + // loading the file list 1.139 + 1.140 + setTimeout(setInitialDirectory, 0, directory); 1.141 +} 1.142 + 1.143 +function setInitialDirectory(directory) 1.144 +{ 1.145 + // Start in the user's home directory 1.146 + var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID] 1.147 + .getService(nsIProperties); 1.148 + homeDir = dirService.get("Home", Components.interfaces.nsIFile); 1.149 + 1.150 + if (directory) { 1.151 + sfile.initWithPath(directory); 1.152 + if (!sfile.exists() || !sfile.isDirectory()) 1.153 + directory = false; 1.154 + } 1.155 + if (!directory) { 1.156 + sfile.initWithPath(homeDir.path); 1.157 + } 1.158 + 1.159 + gotoDirectory(sfile); 1.160 +} 1.161 + 1.162 +function onFilterChanged(target) 1.163 +{ 1.164 + // Do this on a timeout callback so the filter list can roll up 1.165 + // and we don't keep the mouse grabbed while we are refiltering. 1.166 + 1.167 + setTimeout(changeFilter, 0, target.getAttribute("filters")); 1.168 +} 1.169 + 1.170 +function changeFilter(filterTypes) 1.171 +{ 1.172 + window.setCursor("wait"); 1.173 + treeView.setFilter(filterTypes); 1.174 + window.setCursor("auto"); 1.175 +} 1.176 + 1.177 +function showErrorDialog(titleStrName, messageStrName, file) 1.178 +{ 1.179 + var errorTitle = 1.180 + gFilePickerBundle.getFormattedString(titleStrName, [file.path]); 1.181 + var errorMessage = 1.182 + gFilePickerBundle.getFormattedString(messageStrName, [file.path]); 1.183 + var promptService = 1.184 + Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); 1.185 + 1.186 + promptService.alert(window, errorTitle, errorMessage); 1.187 +} 1.188 + 1.189 +function openOnOK() 1.190 +{ 1.191 + var dir = treeView.selectedFiles.queryElementAt(0, nsIFile); 1.192 + if (dir) 1.193 + gotoDirectory(dir); 1.194 + 1.195 + return false; 1.196 +} 1.197 + 1.198 +function selectOnOK() 1.199 +{ 1.200 + var errorTitle, errorMessage, promptService; 1.201 + var ret = nsIFilePicker.returnOK; 1.202 + 1.203 + var isDir = false; 1.204 + var isFile = false; 1.205 + 1.206 + retvals.filterIndex = document.getElementById("filterMenuList").selectedIndex; 1.207 + retvals.fileURL = null; 1.208 + 1.209 + if (allowURLs) { 1.210 + try { 1.211 + var ios = Components.classes[NS_IOSERVICE_CONTRACTID].getService(Components.interfaces.nsIIOService); 1.212 + retvals.fileURL = ios.newURI(textInput.value, null, null); 1.213 + var fileList = []; 1.214 + if (retvals.fileURL instanceof Components.interfaces.nsIFileURL) 1.215 + fileList.push(retvals.fileURL.file); 1.216 + gFilesEnumerator.mFiles = fileList; 1.217 + retvals.files = gFilesEnumerator; 1.218 + retvals.buttonStatus = ret; 1.219 + 1.220 + return true; 1.221 + } catch (e) { 1.222 + } 1.223 + } 1.224 + 1.225 + var fileList = processPath(textInput.value); 1.226 + if (!fileList) { 1.227 + // generic error message, should probably never happen 1.228 + showErrorDialog("errorPathProblemTitle", 1.229 + "errorPathProblemMessage", 1.230 + textInput.value); 1.231 + return false; 1.232 + } 1.233 + 1.234 + var curFileIndex; 1.235 + for (curFileIndex = 0; curFileIndex < fileList.length && 1.236 + ret != nsIFilePicker.returnCancel; ++curFileIndex) { 1.237 + var file = fileList[curFileIndex].QueryInterface(nsIFile); 1.238 + 1.239 + // try to normalize - if this fails we will ignore the error 1.240 + // because we will notice the 1.241 + // error later and show a fitting error alert. 1.242 + try{ 1.243 + file.normalize(); 1.244 + } catch(e) { 1.245 + //promptService.alert(window, "Problem", "normalize failed, continuing"); 1.246 + } 1.247 + 1.248 + var fileExists = file.exists(); 1.249 + 1.250 + if (!fileExists && (filePickerMode == nsIFilePicker.modeOpen || 1.251 + filePickerMode == nsIFilePicker.modeOpenMultiple)) { 1.252 + showErrorDialog("errorOpenFileDoesntExistTitle", 1.253 + "errorOpenFileDoesntExistMessage", 1.254 + file); 1.255 + return false; 1.256 + } 1.257 + 1.258 + if (!fileExists && filePickerMode == nsIFilePicker.modeGetFolder) { 1.259 + showErrorDialog("errorDirDoesntExistTitle", 1.260 + "errorDirDoesntExistMessage", 1.261 + file); 1.262 + return false; 1.263 + } 1.264 + 1.265 + if (fileExists) { 1.266 + isDir = file.isDirectory(); 1.267 + isFile = file.isFile(); 1.268 + } 1.269 + 1.270 + switch(filePickerMode) { 1.271 + case nsIFilePicker.modeOpen: 1.272 + case nsIFilePicker.modeOpenMultiple: 1.273 + if (isFile) { 1.274 + if (file.isReadable()) { 1.275 + retvals.directory = file.parent.path; 1.276 + } else { 1.277 + showErrorDialog("errorOpeningFileTitle", 1.278 + "openWithoutPermissionMessage_file", 1.279 + file); 1.280 + ret = nsIFilePicker.returnCancel; 1.281 + } 1.282 + } else if (isDir) { 1.283 + if (!sfile.equals(file)) { 1.284 + gotoDirectory(file); 1.285 + } 1.286 + textInput.value = ""; 1.287 + doEnabling(); 1.288 + ret = nsIFilePicker.returnCancel; 1.289 + } 1.290 + break; 1.291 + case nsIFilePicker.modeSave: 1.292 + if (isFile) { // can only be true if file.exists() 1.293 + if (!file.isWritable()) { 1.294 + showErrorDialog("errorSavingFileTitle", 1.295 + "saveWithoutPermissionMessage_file", 1.296 + file); 1.297 + ret = nsIFilePicker.returnCancel; 1.298 + } else { 1.299 + // we need to pop up a dialog asking if you want to save 1.300 + var confirmTitle = gFilePickerBundle.getString("confirmTitle"); 1.301 + var message = 1.302 + gFilePickerBundle.getFormattedString("confirmFileReplacing", 1.303 + [file.path]); 1.304 + 1.305 + promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); 1.306 + var rv = promptService.confirm(window, confirmTitle, message); 1.307 + if (rv) { 1.308 + ret = nsIFilePicker.returnReplace; 1.309 + retvals.directory = file.parent.path; 1.310 + } else { 1.311 + ret = nsIFilePicker.returnCancel; 1.312 + } 1.313 + } 1.314 + } else if (isDir) { 1.315 + if (!sfile.equals(file)) { 1.316 + gotoDirectory(file); 1.317 + } 1.318 + textInput.value = ""; 1.319 + doEnabling(); 1.320 + ret = nsIFilePicker.returnCancel; 1.321 + } else { 1.322 + var parent = file.parent; 1.323 + if (parent.exists() && parent.isDirectory() && parent.isWritable()) { 1.324 + retvals.directory = parent.path; 1.325 + } else { 1.326 + var oldParent = parent; 1.327 + while (!parent.exists()) { 1.328 + oldParent = parent; 1.329 + parent = parent.parent; 1.330 + } 1.331 + errorTitle = 1.332 + gFilePickerBundle.getFormattedString("errorSavingFileTitle", 1.333 + [file.path]); 1.334 + if (parent.isFile()) { 1.335 + errorMessage = 1.336 + gFilePickerBundle.getFormattedString("saveParentIsFileMessage", 1.337 + [parent.path, file.path]); 1.338 + } else { 1.339 + errorMessage = 1.340 + gFilePickerBundle.getFormattedString("saveParentDoesntExistMessage", 1.341 + [oldParent.path, file.path]); 1.342 + } 1.343 + if (!parent.isWritable()) { 1.344 + errorMessage = 1.345 + gFilePickerBundle.getFormattedString("saveWithoutPermissionMessage_dir", [parent.path]); 1.346 + } 1.347 + promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); 1.348 + promptService.alert(window, errorTitle, errorMessage); 1.349 + ret = nsIFilePicker.returnCancel; 1.350 + } 1.351 + } 1.352 + break; 1.353 + case nsIFilePicker.modeGetFolder: 1.354 + if (isDir) { 1.355 + retvals.directory = file.parent.path; 1.356 + } else { // if nothing selected, the current directory will be fine 1.357 + retvals.directory = sfile.path; 1.358 + } 1.359 + break; 1.360 + } 1.361 + } 1.362 + 1.363 + gFilesEnumerator.mFiles = fileList; 1.364 + 1.365 + retvals.files = gFilesEnumerator; 1.366 + retvals.buttonStatus = ret; 1.367 + 1.368 + return (ret != nsIFilePicker.returnCancel); 1.369 +} 1.370 + 1.371 +var gFilesEnumerator = { 1.372 + mFiles: null, 1.373 + mIndex: 0, 1.374 + 1.375 + hasMoreElements: function() 1.376 + { 1.377 + return (this.mIndex < this.mFiles.length); 1.378 + }, 1.379 + getNext: function() 1.380 + { 1.381 + if (this.mIndex >= this.mFiles.length) 1.382 + throw Components.results.NS_ERROR_FAILURE; 1.383 + return this.mFiles[this.mIndex++]; 1.384 + } 1.385 +}; 1.386 + 1.387 +function onCancel() 1.388 +{ 1.389 + // Close the window. 1.390 + retvals.buttonStatus = nsIFilePicker.returnCancel; 1.391 + retvals.file = null; 1.392 + retvals.files = null; 1.393 + return true; 1.394 +} 1.395 + 1.396 +function onDblClick(e) { 1.397 + // we only care about button 0 (left click) events 1.398 + if (e.button != 0) return; 1.399 + 1.400 + var t = e.originalTarget; 1.401 + if (t.localName != "treechildren") 1.402 + return; 1.403 + 1.404 + openSelectedFile(); 1.405 +} 1.406 + 1.407 +function openSelectedFile() { 1.408 + var fileList = treeView.selectedFiles; 1.409 + if (fileList.length == 0) 1.410 + return; 1.411 + 1.412 + var file = fileList.queryElementAt(0, nsIFile); 1.413 + if (file.isDirectory()) 1.414 + gotoDirectory(file); 1.415 + else if (file.isFile()) 1.416 + document.documentElement.acceptDialog(); 1.417 +} 1.418 + 1.419 +function onClick(e) { 1.420 + var t = e.originalTarget; 1.421 + if (t.localName == "treecol") 1.422 + handleColumnClick(t.id); 1.423 +} 1.424 + 1.425 +function convertColumnIDtoSortType(columnID) { 1.426 + var sortKey; 1.427 + 1.428 + switch (columnID) { 1.429 + case "FilenameColumn": 1.430 + sortKey = nsIFileView.sortName; 1.431 + break; 1.432 + case "FileSizeColumn": 1.433 + sortKey = nsIFileView.sortSize; 1.434 + break; 1.435 + case "LastModifiedColumn": 1.436 + sortKey = nsIFileView.sortDate; 1.437 + break; 1.438 + default: 1.439 + dump("unsupported sort column: " + columnID + "\n"); 1.440 + sortKey = 0; 1.441 + break; 1.442 + } 1.443 + 1.444 + return sortKey; 1.445 +} 1.446 + 1.447 +function handleColumnClick(columnID) { 1.448 + var sortType = convertColumnIDtoSortType(columnID); 1.449 + var sortOrder = (treeView.sortType == sortType) ? !treeView.reverseSort : false; 1.450 + treeView.sort(sortType, sortOrder); 1.451 + 1.452 + // set the sort indicator on the column we are sorted by 1.453 + var sortedColumn = document.getElementById(columnID); 1.454 + if (treeView.reverseSort) { 1.455 + sortedColumn.setAttribute("sortDirection", "descending"); 1.456 + } else { 1.457 + sortedColumn.setAttribute("sortDirection", "ascending"); 1.458 + } 1.459 + 1.460 + // remove the sort indicator from the rest of the columns 1.461 + var currCol = sortedColumn.parentNode.firstChild; 1.462 + while (currCol) { 1.463 + if (currCol != sortedColumn && currCol.localName == "treecol") 1.464 + currCol.removeAttribute("sortDirection"); 1.465 + currCol = currCol.nextSibling; 1.466 + } 1.467 +} 1.468 + 1.469 +function onKeypress(e) { 1.470 + if (e.keyCode == 8) /* backspace */ 1.471 + goUp(); 1.472 + 1.473 + /* enter is handled by the ondialogaccept handler */ 1.474 +} 1.475 + 1.476 +function doEnabling() { 1.477 + if (filePickerMode != nsIFilePicker.modeGetFolder) 1.478 + // Maybe add check if textInput.value would resolve to an existing 1.479 + // file or directory in .modeOpen. Too costly I think. 1.480 + okButton.disabled = (textInput.value == "") 1.481 +} 1.482 + 1.483 +function onTreeFocus(event) { 1.484 + // Reset the button label and enabled/disabled state. 1.485 + onFileSelected(treeView.selectedFiles); 1.486 +} 1.487 + 1.488 +function setOKAction(file) { 1.489 + var buttonLabel; 1.490 + var buttonIcon = "open"; // used in all but one case 1.491 + 1.492 + if (file && file.isDirectory()) { 1.493 + document.documentElement.setAttribute("ondialogaccept", "return openOnOK();"); 1.494 + buttonLabel = gFilePickerBundle.getString("openButtonLabel"); 1.495 + } 1.496 + else { 1.497 + document.documentElement.setAttribute("ondialogaccept", "return selectOnOK();"); 1.498 + switch(filePickerMode) { 1.499 + case nsIFilePicker.modeGetFolder: 1.500 + buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel"); 1.501 + break; 1.502 + case nsIFilePicker.modeOpen: 1.503 + case nsIFilePicker.modeOpenMultiple: 1.504 + buttonLabel = gFilePickerBundle.getString("openButtonLabel"); 1.505 + break; 1.506 + case nsIFilePicker.modeSave: 1.507 + buttonLabel = gFilePickerBundle.getString("saveButtonLabel"); 1.508 + buttonIcon = "save"; 1.509 + break; 1.510 + } 1.511 + } 1.512 + okButton.setAttribute("label", buttonLabel); 1.513 + okButton.setAttribute("icon", buttonIcon); 1.514 +} 1.515 + 1.516 +function onSelect(event) { 1.517 + onFileSelected(treeView.selectedFiles); 1.518 +} 1.519 + 1.520 +function onFileSelected(/* nsIArray */ selectedFileList) { 1.521 + var validFileSelected = false; 1.522 + var invalidSelection = false; 1.523 + var file; 1.524 + var fileCount = selectedFileList.length; 1.525 + 1.526 + for (var index = 0; index < fileCount; ++index) { 1.527 + file = selectedFileList.queryElementAt(index, nsIFile); 1.528 + if (file) { 1.529 + var path = file.leafName; 1.530 + 1.531 + if (path) { 1.532 + var isDir = file.isDirectory(); 1.533 + if ((filePickerMode == nsIFilePicker.modeGetFolder) || !isDir) { 1.534 + if (!validFileSelected) 1.535 + textInput.value = ""; 1.536 + addToTextFieldValue(path); 1.537 + } 1.538 + 1.539 + if (isDir && fileCount > 1) { 1.540 + // The user has selected multiple items, and one of them is 1.541 + // a directory. This is not a valid state, so we'll disable 1.542 + // the ok button. 1.543 + invalidSelection = true; 1.544 + } 1.545 + 1.546 + validFileSelected = true; 1.547 + } 1.548 + } 1.549 + } 1.550 + 1.551 + if (validFileSelected) { 1.552 + setOKAction(file); 1.553 + okButton.disabled = invalidSelection; 1.554 + } else if (filePickerMode != nsIFilePicker.modeGetFolder) 1.555 + okButton.disabled = (textInput.value == ""); 1.556 +} 1.557 + 1.558 +function addToTextFieldValue(path) 1.559 +{ 1.560 + var newValue = ""; 1.561 + 1.562 + if (textInput.value == "") 1.563 + newValue = path.replace(/\"/g, "\\\""); 1.564 + else { 1.565 + // Quote the existing text if needed, 1.566 + // then append the new filename (quoted and escaped) 1.567 + if (textInput.value[0] != '"') 1.568 + newValue = '"' + textInput.value.replace(/\"/g, "\\\"") + '"'; 1.569 + else 1.570 + newValue = textInput.value; 1.571 + 1.572 + newValue = newValue + ' "' + path.replace(/\"/g, "\\\"") + '"'; 1.573 + } 1.574 + 1.575 + textInput.value = newValue; 1.576 +} 1.577 + 1.578 +function onTextFieldFocus() { 1.579 + setOKAction(null); 1.580 + doEnabling(); 1.581 +} 1.582 + 1.583 +function onDirectoryChanged(target) 1.584 +{ 1.585 + var path = target.getAttribute("label"); 1.586 + 1.587 + var file = Components.classes[NS_LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile); 1.588 + file.initWithPath(path); 1.589 + 1.590 + if (!sfile.equals(file)) { 1.591 + // Do this on a timeout callback so the directory list can roll up 1.592 + // and we don't keep the mouse grabbed while we are loading. 1.593 + 1.594 + setTimeout(gotoDirectory, 0, file); 1.595 + } 1.596 +} 1.597 + 1.598 +function populateAncestorList(directory) { 1.599 + var menu = document.getElementById("lookInMenu"); 1.600 + 1.601 + while (menu.hasChildNodes()) { 1.602 + menu.removeChild(menu.firstChild); 1.603 + } 1.604 + 1.605 + var menuItem = document.createElement("menuitem"); 1.606 + menuItem.setAttribute("label", directory.path); 1.607 + menuItem.setAttribute("crop", "start"); 1.608 + menu.appendChild(menuItem); 1.609 + 1.610 + // .parent is _sometimes_ null, see bug 121489. Do a dance around that. 1.611 + var parent = directory.parent; 1.612 + while (parent && !parent.equals(directory)) { 1.613 + menuItem = document.createElement("menuitem"); 1.614 + menuItem.setAttribute("label", parent.path); 1.615 + menuItem.setAttribute("crop", "start"); 1.616 + menu.appendChild(menuItem); 1.617 + directory = parent; 1.618 + parent = directory.parent; 1.619 + } 1.620 + 1.621 + var menuList = document.getElementById("lookInMenuList"); 1.622 + menuList.selectedIndex = 0; 1.623 +} 1.624 + 1.625 +function goUp() { 1.626 + try { 1.627 + var parent = sfile.parent; 1.628 + } catch(ex) { dump("can't get parent directory\n"); } 1.629 + 1.630 + if (parent) { 1.631 + gotoDirectory(parent); 1.632 + } 1.633 +} 1.634 + 1.635 +function goHome() { 1.636 + gotoDirectory(homeDir); 1.637 +} 1.638 + 1.639 +function newDir() { 1.640 + var file; 1.641 + var promptService = 1.642 + Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService); 1.643 + var dialogTitle = 1.644 + gFilePickerBundle.getString("promptNewDirTitle"); 1.645 + var dialogMsg = 1.646 + gFilePickerBundle.getString("promptNewDirMessage"); 1.647 + var ret = promptService.prompt(window, dialogTitle, dialogMsg, gNewDirName, null, {value:0}); 1.648 + 1.649 + if (ret) { 1.650 + file = processPath(gNewDirName.value); 1.651 + if (!file) { 1.652 + showErrorDialog("errorCreateNewDirTitle", 1.653 + "errorCreateNewDirMessage", 1.654 + file); 1.655 + return false; 1.656 + } 1.657 + 1.658 + file = file[0].QueryInterface(nsIFile); 1.659 + if (file.exists()) { 1.660 + showErrorDialog("errorNewDirDoesExistTitle", 1.661 + "errorNewDirDoesExistMessage", 1.662 + file); 1.663 + return false; 1.664 + } 1.665 + 1.666 + var parent = file.parent; 1.667 + if (!(parent.exists() && parent.isDirectory() && parent.isWritable())) { 1.668 + var oldParent = parent; 1.669 + while (!parent.exists()) { 1.670 + oldParent = parent; 1.671 + parent = parent.parent; 1.672 + } 1.673 + if (parent.isFile()) { 1.674 + showErrorDialog("errorCreateNewDirTitle", 1.675 + "errorCreateNewDirIsFileMessage", 1.676 + parent); 1.677 + return false; 1.678 + } 1.679 + if (!parent.isWritable()) { 1.680 + showErrorDialog("errorCreateNewDirTitle", 1.681 + "errorCreateNewDirPermissionMessage", 1.682 + parent); 1.683 + return false; 1.684 + } 1.685 + } 1.686 + 1.687 + try { 1.688 + file.create(nsIFile.DIRECTORY_TYPE, 0755); 1.689 + } catch (e) { 1.690 + showErrorDialog("errorCreateNewDirTitle", 1.691 + "errorCreateNewDirMessage", 1.692 + file); 1.693 + return false; 1.694 + } 1.695 + file.normalize(); // ... in case ".." was used in the path 1.696 + gotoDirectory(file); 1.697 + // we remember and reshow a dirname if something goes wrong 1.698 + // so that errors can be corrected more easily. If all went well, 1.699 + // reset the default value to blank 1.700 + gNewDirName = { value: "" }; 1.701 + } 1.702 + return true; 1.703 +} 1.704 + 1.705 +function gotoDirectory(directory) { 1.706 + window.setCursor("wait"); 1.707 + try { 1.708 + populateAncestorList(directory); 1.709 + treeView.setDirectory(directory); 1.710 + document.getElementById("errorShower").selectedIndex = 0; 1.711 + } catch(ex) { 1.712 + document.getElementById("errorShower").selectedIndex = 1; 1.713 + } 1.714 + 1.715 + window.setCursor("auto"); 1.716 + 1.717 + if (filePickerMode == nsIFilePicker.modeGetFolder) { 1.718 + textInput.value = ""; 1.719 + } 1.720 + textInput.focus(); 1.721 + textInput.setAttribute("autocompletesearchparam", directory.path); 1.722 + sfile = directory; 1.723 +} 1.724 + 1.725 +function toggleShowHidden(event) { 1.726 + treeView.showHiddenFiles = !treeView.showHiddenFiles; 1.727 +} 1.728 + 1.729 +// from the current directory and whatever was entered 1.730 +// in the entry field, try to make a new path. This 1.731 +// uses "/" as the directory separator, "~" as a shortcut 1.732 +// for the home directory (but only when seen at the start 1.733 +// of a path), and ".." to denote the parent directory. 1.734 +// returns an array of the files listed, 1.735 +// or false if an error occurred. 1.736 +function processPath(path) 1.737 +{ 1.738 + var fileArray = new Array(); 1.739 + var strLength = path.length; 1.740 + 1.741 + if (path[0] == '"' && filePickerMode == nsIFilePicker.modeOpenMultiple && 1.742 + strLength > 1) { 1.743 + // we have a quoted list of filenames, separated by spaces. 1.744 + // iterate the list and process each file. 1.745 + 1.746 + var curFileStart = 1; 1.747 + 1.748 + while (1) { 1.749 + var nextQuote; 1.750 + 1.751 + // Look for an unescaped quote 1.752 + var quoteSearchStart = curFileStart + 1; 1.753 + do { 1.754 + nextQuote = path.indexOf('"', quoteSearchStart); 1.755 + quoteSearchStart = nextQuote + 1; 1.756 + } while (nextQuote != -1 && path[nextQuote - 1] == '\\'); 1.757 + 1.758 + if (nextQuote == -1) { 1.759 + // we have a filename with no trailing quote. 1.760 + // just assume that the filename ends at the end of the string. 1.761 + 1.762 + if (!processPathEntry(path.substring(curFileStart), fileArray)) 1.763 + return false; 1.764 + break; 1.765 + } 1.766 + 1.767 + if (!processPathEntry(path.substring(curFileStart, nextQuote), fileArray)) 1.768 + return false; 1.769 + 1.770 + curFileStart = path.indexOf('"', nextQuote + 1); 1.771 + if (curFileStart == -1) { 1.772 + // no more quotes, but if we're not at the end of the string, 1.773 + // go ahead and process the remaining text. 1.774 + 1.775 + if (nextQuote < strLength - 1) 1.776 + if (!processPathEntry(path.substring(nextQuote + 1), fileArray)) 1.777 + return false; 1.778 + break; 1.779 + } 1.780 + ++curFileStart; 1.781 + } 1.782 + } else { 1.783 + // If we didn't start with a quote, assume we just have a single file. 1.784 + if (!processPathEntry(path, fileArray)) 1.785 + return false; 1.786 + } 1.787 + 1.788 + return fileArray; 1.789 +} 1.790 + 1.791 +function processPathEntry(path, fileArray) 1.792 +{ 1.793 + var filePath; 1.794 + var file; 1.795 + 1.796 + try { 1.797 + file = sfile.clone().QueryInterface(nsILocalFile); 1.798 + } catch(e) { 1.799 + dump("Couldn't clone\n"+e); 1.800 + return false; 1.801 + } 1.802 + 1.803 + var tilde_file = file.clone(); 1.804 + tilde_file.append("~"); 1.805 + if (path[0] == '~' && // Expand ~ to $HOME, except: 1.806 + !(path == "~" && tilde_file.exists()) && // If ~ was entered and such a file exists, don't expand 1.807 + (path.length == 1 || path[1] == "/")) // We don't want to expand ~file to ${HOME}file 1.808 + filePath = homeDir.path + path.substring(1); 1.809 + else 1.810 + filePath = path; 1.811 + 1.812 + // Unescape quotes 1.813 + filePath = filePath.replace(/\\\"/g, "\""); 1.814 + 1.815 + if (filePath[0] == '/') /* an absolute path was entered */ 1.816 + file.initWithPath(filePath); 1.817 + else if ((filePath.indexOf("/../") > 0) || 1.818 + (filePath.substr(-3) == "/..") || 1.819 + (filePath.substr(0,3) == "../") || 1.820 + (filePath == "..")) { 1.821 + /* appendRelativePath doesn't allow .. */ 1.822 + try{ 1.823 + file.initWithPath(file.path + "/" + filePath); 1.824 + } catch (e) { 1.825 + dump("Couldn't init path\n"+e); 1.826 + return false; 1.827 + } 1.828 + } 1.829 + else { 1.830 + try { 1.831 + file.appendRelativePath(filePath); 1.832 + } catch (e) { 1.833 + dump("Couldn't append path\n"+e); 1.834 + return false; 1.835 + } 1.836 + } 1.837 + 1.838 + fileArray[fileArray.length] = file; 1.839 + return true; 1.840 +}