toolkit/components/filepicker/content/filepicker.js

changeset 0
6474c204b198
     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 +}

mercurial