home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 March / PCWorld_2005-03_cd.bin / komunikace / kmeleon / kmeleon09.exe / toolkit.jar / content / global / filepicker.js < prev    next >
Text File  |  2004-07-15  |  26KB  |  853 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Mozilla Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is mozilla.org code.
  14.  *
  15.  * The Initial Developer of the Original Code is Netscape Communications
  16.  * Corporation.  Portions created by Netscape are
  17.  * Copyright (C) 2000 Netscape Communications Corporation. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s): 
  21.  *  Stuart Parmenter <pavlov@netscape.com>
  22.  *  Brian Ryner <bryner@brianryner.com>
  23.  *  Jan Varga <varga@nixcorp.com>
  24.  *  Peter Annema <disttsc@bart.nl>
  25.  *  Johann Petrak <johann@ai.univie.ac.at>
  26.  *  Akkana Peck <akkana@netscape.com>
  27.  */
  28.  
  29. const nsIFilePicker       = Components.interfaces.nsIFilePicker;
  30. const nsIProperties       = Components.interfaces.nsIProperties;
  31. const NS_DIRECTORYSERVICE_CONTRACTID = "@mozilla.org/file/directory_service;1";
  32. const nsITreeBoxObject = Components.interfaces.nsITreeBoxObject;
  33. const nsIFileView = Components.interfaces.nsIFileView;
  34. const NS_FILEVIEW_CONTRACTID = "@mozilla.org/filepicker/fileview;1";
  35. const nsITreeView = Components.interfaces.nsITreeView;
  36. const nsILocalFile = Components.interfaces.nsILocalFile;
  37. const nsIFile = Components.interfaces.nsIFile;
  38. const NS_LOCALFILE_CONTRACTID = "@mozilla.org/file/local;1";
  39. const NS_PROMPTSERVICE_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
  40.  
  41. var sfile = Components.classes[NS_LOCALFILE_CONTRACTID].createInstance(nsILocalFile);
  42. var retvals;
  43. var filePickerMode;
  44. var homeDir;
  45. var treeView;
  46.  
  47. var textInput;
  48. var okButton;
  49.  
  50. var gFilePickerBundle;
  51.  
  52. // name of new directory entered by the user to be remembered
  53. // for next call of newDir() in case something goes wrong with creation
  54. var gNewDirName = { value: "" };
  55.  
  56. function filepickerLoad() {
  57.   gFilePickerBundle = document.getElementById("bundle_filepicker");
  58.  
  59.   textInput = document.getElementById("textInput");
  60.   okButton = document.documentElement.getButton("accept");
  61.   treeView = Components.classes[NS_FILEVIEW_CONTRACTID].createInstance(nsIFileView);
  62.  
  63.   if (window.arguments) {
  64.     var o = window.arguments[0];
  65.     retvals = o.retvals; /* set this to a global var so we can set return values */
  66.     const title = o.title;
  67.     filePickerMode = o.mode;
  68.     if (o.displayDirectory) {
  69.       const directory = o.displayDirectory.path;
  70.     }
  71.     const initialText = o.defaultString;
  72.     const filterTitles = o.filters.titles;
  73.     const filterTypes = o.filters.types;
  74.     const numFilters = filterTitles.length;
  75.  
  76.     window.title = title;
  77.  
  78.     if (initialText) {
  79.       textInput.value = initialText;
  80.     }
  81.   }
  82.  
  83.   if (filePickerMode != nsIFilePicker.modeOpen && filePickerMode != nsIFilePicker.modeOpenMultiple) {
  84.     var newDirButton = document.getElementById("newDirButton");
  85.     newDirButton.removeAttribute("hidden");
  86.   }
  87.  
  88.   if (filePickerMode == nsIFilePicker.modeGetFolder) {
  89.     var textInputLabel = document.getElementById("textInputLabel");
  90.     textInputLabel.value = gFilePickerBundle.getString("dirTextInputLabel");
  91.   }
  92.   
  93.   if ((filePickerMode == nsIFilePicker.modeOpen) ||
  94.       (filePickerMode == nsIFilePicker.modeOpenMultiple) ||
  95.       (filePickerMode == nsIFilePicker.modeSave)) {
  96.  
  97.     /* build filter popup */
  98.     var filterPopup = document.createElement("menupopup");
  99.  
  100.     for (var i = 0; i < numFilters; i++) {
  101.       var menuItem = document.createElement("menuitem");
  102.       if (filterTypes[i] == "..apps")
  103.         menuItem.setAttribute("label", filterTitles[i]);
  104.       else
  105.         menuItem.setAttribute("label", filterTitles[i] + " (" + filterTypes[i] + ")");
  106.       menuItem.setAttribute("filters", filterTypes[i]);
  107.       filterPopup.appendChild(menuItem);
  108.     }
  109.  
  110.     var filterMenuList = document.getElementById("filterMenuList");
  111.     filterMenuList.appendChild(filterPopup);
  112.     if (numFilters > 0)
  113.       filterMenuList.selectedIndex = 0;
  114.     var filterBox = document.getElementById("filterBox");
  115.     filterBox.removeAttribute("hidden");
  116.  
  117.     filterMenuList.selectedIndex = o.filterIndex;
  118.  
  119.     treeView.setFilter(filterTypes[o.filterIndex]);
  120.  
  121.   } else if (filePickerMode == nsIFilePicker.modeGetFolder) {
  122.     treeView.showOnlyDirectories = true;
  123.   }
  124.  
  125.   // start out with a filename sort
  126.   handleColumnClick("FilenameColumn");
  127.  
  128.   document.documentElement.setAttribute("ondialogcancel", "return onCancel();");
  129.   try {
  130.     var buttonLabel = getOKAction();
  131.     okButton.setAttribute("label", buttonLabel);
  132.   } catch (exception) {
  133.     // keep it set to "OK"
  134.   }
  135.  
  136.   // setup the dialogOverlay.xul button handlers
  137.   retvals.buttonStatus = nsIFilePicker.returnCancel;
  138.  
  139.   var tree = document.getElementById("directoryTree");
  140.   if (filePickerMode == nsIFilePicker.modeOpenMultiple)
  141.     tree.removeAttribute("seltype");
  142.  
  143.   tree.treeBoxObject.view = treeView;
  144.  
  145.   // Start out with the ok button disabled since nothing will be
  146.   // selected and nothing will be in the text field.
  147.   okButton.disabled = true;
  148.   textInput.focus();
  149.  
  150.   // This allows the window to show onscreen before we begin
  151.   // loading the file list
  152.  
  153.   setTimeout(setInitialDirectory, 0, directory);
  154. }
  155.  
  156. function setInitialDirectory(directory)
  157. {
  158.   // Start in the user's home directory
  159.   var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID]
  160.                              .getService(nsIProperties);
  161.   homeDir = dirService.get("Home", Components.interfaces.nsIFile);
  162.  
  163.   if (directory) {
  164.     sfile.initWithPath(directory);
  165.     if (!sfile.exists() || !sfile.isDirectory())
  166.       directory = false;
  167.   }
  168.   if (!directory) {
  169.     sfile.initWithPath(homeDir.path);
  170.   }
  171.  
  172.   gotoDirectory(sfile);
  173. }
  174.  
  175. function onFilterChanged(target)
  176. {
  177.   // Do this on a timeout callback so the filter list can roll up
  178.   // and we don't keep the mouse grabbed while we are refiltering.
  179.  
  180.   setTimeout(changeFilter, 0, target.getAttribute("filters"));
  181. }
  182.  
  183. function changeFilter(filterTypes)
  184. {
  185.   window.setCursor("wait");
  186.   treeView.setFilter(filterTypes);
  187.   window.setCursor("auto");
  188. }
  189.  
  190. function showErrorDialog(titleStrName, messageStrName, file)
  191. {
  192.   var errorTitle =
  193.     gFilePickerBundle.getFormattedString(titleStrName, [file.path]);
  194.   var errorMessage =
  195.     gFilePickerBundle.getFormattedString(messageStrName, [file.path]);
  196.   var promptService =
  197.     Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  198.  
  199.   promptService.alert(window, errorTitle, errorMessage);
  200. }
  201.  
  202. function openOnOK()
  203. {
  204.   var dir = treeView.selectedFiles.queryElementAt(0, nsIFile);
  205.   if (!dir.isReadable()) {
  206.     showErrorDialog("errorOpenFileDoesntExistTitle",
  207.                     "errorDirNotReadableMessage",
  208.                     dir);
  209.     return false;
  210.   }
  211.  
  212.   if (dir)
  213.     gotoDirectory(dir);
  214.  
  215.   retvals.fileList = new Array(dir);
  216.  
  217.   retvals.buttonStatus = nsIFilePicker.returnCancel;
  218.   
  219.   var filterMenuList = document.getElementById("filterMenuList");
  220.   retvals.filterIndex = filterMenuList.selectedIndex;
  221.   
  222.   return false;
  223. }
  224.  
  225. function selectOnOK()
  226. {
  227.   var errorTitle, errorMessage, promptService;
  228.   var ret = nsIFilePicker.returnOK;
  229.  
  230.   var isDir = false;
  231.   var isFile = false;
  232.  
  233.   var fileList = processPath(textInput.value);
  234.  
  235.   if (!fileList) { // generic error message, should probably never happen
  236.     showErrorDialog("errorPathProblemTitle",
  237.                     "errorPathProblemMessage",
  238.                     textInput.value);
  239.     return false;
  240.   }
  241.  
  242.   var curFileIndex;
  243.   for (curFileIndex = 0; curFileIndex < fileList.length &&
  244.          ret != nsIFilePicker.returnCancel; ++curFileIndex) {
  245.     var file = fileList[curFileIndex].QueryInterface(nsIFile);
  246.  
  247.     // try to normalize - if this fails we will ignore the error
  248.     // because we will notice the
  249.     // error later and show a fitting error alert.
  250.     try{
  251.       file.normalize();
  252.     } catch(e) {
  253.       //promptService.alert(window, "Problem", "normalize failed, continuing");
  254.     }
  255.  
  256.     var fileExists = file.exists();
  257.  
  258.     if (!fileExists && (filePickerMode == nsIFilePicker.modeOpen ||
  259.                         filePickerMode == nsIFilePicker.modeOpenMultiple)) {
  260.       showErrorDialog("errorOpenFileDoesntExistTitle",
  261.                       "errorOpenFileDoesntExistMessage",
  262.                       file);
  263.       return false;
  264.     }
  265.  
  266.     if (!fileExists && filePickerMode == nsIFilePicker.modeGetFolder) {
  267.       showErrorDialog("errorDirDoesntExistTitle",
  268.                       "errorDirDoesntExistMessage",
  269.                       file);
  270.       return false;
  271.     }
  272.  
  273.     if (fileExists) {
  274.       isDir = file.isDirectory();
  275.       isFile = file.isFile();
  276.     }
  277.  
  278.     switch(filePickerMode) {
  279.     case nsIFilePicker.modeOpen:
  280.     case nsIFilePicker.modeOpenMultiple:
  281.       if (isFile) {
  282.         if (file.isReadable()) {
  283.           retvals.directory = file.parent.path;
  284.         } else {
  285.           showErrorDialog("errorOpeningFileTitle",
  286.                           "openWithoutPermissionMessage_file",
  287.                           file);
  288.           ret = nsIFilePicker.returnCancel;
  289.         }
  290.       } else if (isDir) {
  291.         if (!sfile.equals(file)) {
  292.           gotoDirectory(file);
  293.         }
  294.         textInput.value = "";
  295.         doEnabling();
  296.         ret = nsIFilePicker.returnCancel;
  297.       }
  298.       break;
  299.     case nsIFilePicker.modeSave:
  300.       if (isFile) { // can only be true if file.exists()
  301.         if (!file.isWritable()) {
  302.           showErrorDialog("errorSavingFileTitle",
  303.                           "saveWithoutPermissionMessage_file",
  304.                           file);
  305.           ret = nsIFilePicker.returnCancel;
  306.         } else {
  307.           // we need to pop up a dialog asking if you want to save
  308.           var confirmTitle = gFilePickerBundle.getString("confirmTitle");
  309.           var message =
  310.             gFilePickerBundle.getFormattedString("confirmFileReplacing",
  311.                                                  [file.path]);
  312.           
  313.           promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  314.           var rv = promptService.confirm(window, title, message);
  315.           if (rv) {
  316.             ret = nsIFilePicker.returnReplace;
  317.             retvals.directory = file.parent.path;
  318.           } else {
  319.             ret = nsIFilePicker.returnCancel;
  320.           }
  321.         }
  322.       } else if (isDir) {
  323.         if (!sfile.equals(file)) {
  324.           gotoDirectory(file);
  325.         }
  326.         textInput.value = "";
  327.         doEnabling();
  328.         ret = nsIFilePicker.returnCancel;
  329.       } else {
  330.         var parent = file.parent;
  331.         if (parent.exists() && parent.isDirectory() && parent.isWritable()) {
  332.           retvals.directory = parent.path;
  333.         } else {
  334.           var oldParent = parent;
  335.           while (!parent.exists()) {
  336.             oldParent = parent;
  337.             parent = parent.parent;
  338.           }
  339.           errorTitle =
  340.             gFilePickerBundle.getFormattedString("errorSavingFileTitle",
  341.                                                  [file.path]);
  342.           if (parent.isFile()) {
  343.             errorMessage =
  344.               gFilePickerBundle.getFormattedString("saveParentIsFileMessage",
  345.                                                    [parent.path, file.path]);
  346.           } else {
  347.             errorMessage =
  348.               gFilePickerBundle.getFormattedString("saveParentDoesntExistMessage",
  349.                                                    [oldParent.path, file.path]);
  350.           }
  351.           if (!parent.isWritable()) {
  352.             errorMessage =
  353.               gFilePickerBundle.getFormattedString("saveWithoutPermissionMessage_dir", [parent.path]);
  354.           }
  355.           promptService = Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  356.           promptService.alert(window, errorTitle, errorMessage);
  357.           ret = nsIFilePicker.returnCancel;
  358.         }
  359.       }
  360.       break;
  361.     case nsIFilePicker.modeGetFolder:
  362.       if (isDir) {
  363.         retvals.directory = file.parent.path;
  364.       } else { // if nothing selected, the current directory will be fine
  365.         retvals.directory = sfile.path;
  366.       }
  367.       break;
  368.     }
  369.   }
  370.  
  371.   gFilesEnumerator.mFiles = fileList;
  372.  
  373.   retvals.files = gFilesEnumerator;
  374.   retvals.buttonStatus = ret;
  375.  
  376.   var filterMenuList = document.getElementById("filterMenuList");
  377.   retvals.filterIndex = filterMenuList.selectedIndex;
  378.   
  379.   return (ret != nsIFilePicker.returnCancel);
  380. }
  381.  
  382. var gFilesEnumerator = {
  383.   mFiles: null,
  384.   mIndex: 0,
  385.  
  386.   hasMoreElements: function()
  387.   {
  388.     return (this.mIndex < this.mFiles.length);
  389.   },
  390.   getNext: function()
  391.   {
  392.     if (this.mIndex >= this.mFiles.length)
  393.       throw Components.results.NS_ERROR_FAILURE;
  394.     return this.mFiles[this.mIndex++];
  395.   }
  396. };
  397.  
  398. function onCancel()
  399. {
  400.   // Close the window.
  401.   retvals.buttonStatus = nsIFilePicker.returnCancel;
  402.   retvals.file = null;
  403.   retvals.files = null;
  404.   return true;
  405. }
  406.  
  407. function onDblClick(e) {
  408.   // we only care about button 0 (left click) events
  409.   if (e.button != 0) return;
  410.  
  411.   var t = e.originalTarget;
  412.   if (t.localName != "treechildren")
  413.     return;
  414.  
  415.   openSelectedFile();
  416. }
  417.  
  418. function openSelectedFile() {
  419.   var fileList = treeView.selectedFiles;
  420.   if (fileList.length == 0)
  421.     return;
  422.  
  423.   var file = fileList.queryElementAt(0, nsIFile);
  424.   if (file.isDirectory())
  425.     gotoDirectory(file);
  426.   else if (file.isFile())
  427.     document.documentElement.acceptDialog();
  428. }
  429.  
  430. function onClick(e) {
  431.   var t = e.originalTarget;
  432.   if (t.localName == "treecol")
  433.     handleColumnClick(t.id);
  434. }
  435.  
  436. function convertColumnIDtoSortType(columnID) {
  437.   var sortKey;
  438.   
  439.   switch (columnID) {
  440.   case "FilenameColumn":
  441.     sortKey = nsIFileView.sortName;
  442.     break;
  443.   case "FileSizeColumn":
  444.     sortKey = nsIFileView.sortSize;
  445.     break;
  446.   case "LastModifiedColumn":
  447.     sortKey = nsIFileView.sortDate;
  448.     break;
  449.   default:
  450.     dump("unsupported sort column: " + columnID + "\n");
  451.     sortKey = 0;
  452.     break;
  453.   }
  454.   
  455.   return sortKey;
  456. }
  457.  
  458. function handleColumnClick(columnID) {
  459.   var sortType = convertColumnIDtoSortType(columnID);
  460.   var sortOrder = (treeView.sortType == sortType) ? !treeView.reverseSort : false;
  461.   treeView.sort(sortType, sortOrder);
  462.   
  463.   // set the sort indicator on the column we are sorted by
  464.   var sortedColumn = document.getElementById(columnID);
  465.   if (treeView.reverseSort) {
  466.     sortedColumn.setAttribute("sortDirection", "descending");
  467.   } else {
  468.     sortedColumn.setAttribute("sortDirection", "ascending");
  469.   }
  470.   
  471.   // remove the sort indicator from the rest of the columns
  472.   var currCol = sortedColumn.parentNode.firstChild;
  473.   while (currCol) {
  474.     if (currCol != sortedColumn && currCol.localName == "treecol")
  475.       currCol.removeAttribute("sortDirection");
  476.     currCol = currCol.nextSibling;
  477.   }
  478. }
  479.  
  480. function onKeypress(e) {
  481.   if (e.keyCode == 8) /* backspace */
  482.     goUp();
  483.  
  484.   /* enter is handled by the ondialogaccept handler */
  485. }
  486.  
  487. function doEnabling() {
  488.   // Maybe add check if textInput.value would resolve to an existing
  489.   // file or directory in .modeOpen. Too costly I think.
  490.   var enable = (textInput.value != "");
  491.  
  492.   okButton.disabled = !enable;
  493. }
  494.  
  495. function onTreeFocus(event) {
  496.   // Reset the button label and enabled/disabled state.
  497.   onFileSelected(treeView.selectedFiles);
  498. }
  499.  
  500. function getOKAction(file) {
  501.   var buttonLabel;
  502.  
  503.   if (file && file.isDirectory() && filePickerMode != nsIFilePicker.modeGetFolder) {
  504.     document.documentElement.setAttribute("ondialogaccept", "return openOnOK();");
  505.     buttonLabel = gFilePickerBundle.getString("openButtonLabel");
  506.   }
  507.   else {
  508.     document.documentElement.setAttribute("ondialogaccept", "return selectOnOK();");
  509.     switch(filePickerMode) {
  510.     case nsIFilePicker.modeGetFolder:
  511.       buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel");
  512.       break;
  513.     case nsIFilePicker.modeOpen:
  514.     case nsIFilePicker.modeOpenMultiple:
  515.       buttonLabel = gFilePickerBundle.getString("openButtonLabel");
  516.       break;
  517.     case nsIFilePicker.modeSave:
  518.       buttonLabel = gFilePickerBundle.getString("saveButtonLabel");
  519.       break;
  520.     }
  521.   }
  522.  
  523.   return buttonLabel;
  524. }
  525.  
  526. function onSelect(event) {
  527.   onFileSelected(treeView.selectedFiles);
  528. }
  529.  
  530. function onFileSelected(/* nsIArray */ selectedFileList) {
  531.   var validFileSelected = false;
  532.   var invalidSelection = false;
  533.   var file;
  534.   var fileCount = selectedFileList.length;
  535.  
  536.   for (var index = 0; index < fileCount; ++index) {
  537.     file = selectedFileList.queryElementAt(index, nsIFile);
  538.     if (file) {
  539.       var path = file.leafName;
  540.  
  541.       if (path) {
  542.         var isDir = file.isDirectory();
  543.         if ((filePickerMode == nsIFilePicker.modeGetFolder) || !isDir) {
  544.           if (!validFileSelected)
  545.             textInput.value = "";
  546.           addToTextFieldValue(path);
  547.         }
  548.  
  549.         if (isDir && fileCount > 1) {
  550.           // The user has selected multiple items, and one of them is
  551.           // a directory.  This is not a valid state, so we'll disable
  552.           // the ok button.
  553.           invalidSelection = true;
  554.         }
  555.  
  556.         validFileSelected = true;
  557.       }
  558.     }
  559.   }
  560.  
  561.   if (validFileSelected) {
  562.     var buttonLabel = getOKAction(file);
  563.     okButton.setAttribute("label", buttonLabel);
  564.     okButton.disabled = invalidSelection;
  565.   } else
  566.     okButton.disabled = (textInput.value == "");
  567. }
  568.  
  569. function addToTextFieldValue(path)
  570. {
  571.   var newValue = "";
  572.  
  573.   if (textInput.value == "")
  574.     newValue = path.replace(/\"/g, "\\\"");
  575.   else {
  576.     // Quote the existing text if needed,
  577.     // then append the new filename (quoted and escaped)
  578.     if (textInput.value[0] != '"')
  579.       newValue = '"' + textInput.value.replace(/\"/g, "\\\"") + '"';
  580.     else
  581.       newValue = textInput.value;
  582.  
  583.     newValue = newValue + ' "' + path.replace(/\"/g, "\\\"") + '"';
  584.   }
  585.  
  586.   textInput.value = newValue;
  587. }
  588.  
  589. function onTextFieldFocus() {
  590.   var buttonLabel = getOKAction(null);
  591.   okButton.setAttribute("label", buttonLabel);
  592.   doEnabling();
  593. }
  594.  
  595. function onDirectoryChanged(target)
  596. {
  597.   var path = target.getAttribute("label");
  598.  
  599.   var file = Components.classes[NS_LOCALFILE_CONTRACTID].createInstance(nsILocalFile);
  600.   file.initWithPath(path);
  601.  
  602.   if (!sfile.equals(file)) {
  603.     // Do this on a timeout callback so the directory list can roll up
  604.     // and we don't keep the mouse grabbed while we are loading.
  605.  
  606.     setTimeout(gotoDirectory, 0, file);
  607.   }
  608. }
  609.  
  610. function populateAncestorList(directory) {
  611.   var menu = document.getElementById("lookInMenu");
  612.  
  613.   while (menu.hasChildNodes()) {
  614.     menu.removeChild(menu.firstChild);
  615.   }
  616.   
  617.   var menuItem = document.createElement("menuitem");
  618.   menuItem.setAttribute("label", directory.path);
  619.   menuItem.setAttribute("crop", "start");
  620.   menu.appendChild(menuItem);
  621.  
  622.   // .parent is _sometimes_ null, see bug 121489.  Do a dance around that.
  623.   var parent = directory.parent;
  624.   while (parent && !parent.equals(directory)) {
  625.     menuItem = document.createElement("menuitem");
  626.     menuItem.setAttribute("label", parent.path);
  627.     menuItem.setAttribute("crop", "start");
  628.     menu.appendChild(menuItem);
  629.     directory = parent;
  630.     parent = directory.parent;
  631.   }
  632.   
  633.   var menuList = document.getElementById("lookInMenuList");
  634.   menuList.selectedIndex = 0;
  635. }
  636.  
  637. function goUp() {
  638.   try {
  639.     var parent = sfile.parent;
  640.   } catch(ex) { dump("can't get parent directory\n"); }
  641.  
  642.   if (parent) {
  643.     gotoDirectory(parent);
  644.   }
  645. }
  646.  
  647. function goHome() {
  648.   gotoDirectory(homeDir);
  649. }
  650.  
  651. function newDir() {
  652.   var file;
  653.   var promptService =
  654.     Components.classes[NS_PROMPTSERVICE_CONTRACTID].getService(Components.interfaces.nsIPromptService);
  655.   var dialogTitle =
  656.     gFilePickerBundle.getString("promptNewDirTitle");
  657.   var dialogMsg =
  658.     gFilePickerBundle.getString("promptNewDirMessage");
  659.   var ret = promptService.prompt(window, dialogTitle, dialogMsg, gNewDirName, null, {value:0});
  660.  
  661.   if (ret) {
  662.     file = processPath(gNewDirName.value);
  663.     if (!file) {
  664.       showErrorDialog("errorCreateNewDirTitle",
  665.                       "errorCreateNewDirMessage",
  666.                       file);
  667.       return false;
  668.     }
  669.     
  670.     file = file[0].QueryInterface(nsIFile);
  671.     if (file.exists()) {
  672.       showErrorDialog("errorNewDirDoesExistTitle",
  673.                       "errorNewDirDoesExistMessage",
  674.                       file);
  675.       return false;
  676.     }
  677.  
  678.     var parent = file.parent;
  679.     if (!(parent.exists() && parent.isDirectory() && parent.isWritable())) {
  680.       var oldParent = parent;
  681.       while (!parent.exists()) {
  682.         oldParent = parent;
  683.         parent = parent.parent;
  684.       }
  685.       if (parent.isFile()) {
  686.         showErrorDialog("errorCreateNewDirTitle",
  687.                         "errorCreateNewDirIsFileMessage",
  688.                         parent);
  689.         return false;
  690.       }
  691.       if (!parent.isWritable()) {
  692.         showErrorDialog("errorCreateNewDirTitle",
  693.                         "errorCreateNewDirPermissionMessage",
  694.                         parent);
  695.         return false;
  696.       }
  697.     }
  698.  
  699.     try {
  700.       file.create(nsIFile.DIRECTORY_TYPE, 0755); 
  701.     } catch (e) {
  702.       showErrorDialog("errorCreateNewDirTitle",
  703.                       "errorCreateNewDirMessage",
  704.                       file);
  705.       return false;
  706.     }
  707.     file.normalize(); // ... in case ".." was used in the path
  708.     gotoDirectory(file);
  709.     // we remember and reshow a dirname if something goes wrong
  710.     // so that errors can be corrected more easily. If all went well,
  711.     // reset the default value to blank
  712.     gNewDirName = { value: "" }; 
  713.   }
  714.   return true;
  715. }
  716.  
  717. function gotoDirectory(directory) {
  718.   window.setCursor("wait");
  719.   try {
  720.     populateAncestorList(directory);
  721.     treeView.setDirectory(directory);
  722.     document.getElementById("errorShower").selectedIndex = 0;
  723.   } catch(ex) {
  724.     document.getElementById("errorShower").selectedIndex = 1;
  725.   }
  726.  
  727.   window.setCursor("auto");
  728.  
  729.   treeView.QueryInterface(nsITreeView).selection.clearSelection();
  730.   if (filePickerMode == nsIFilePicker.modeGetFolder) {
  731.     textInput.value = "";
  732.   }
  733.   textInput.focus();
  734.   sfile = directory;
  735. }
  736.  
  737. function toggleShowHidden(event) {
  738.   treeView.showHiddenFiles = !treeView.showHiddenFiles;
  739. }
  740.  
  741. // from the current directory and whatever was entered
  742. // in the entry field, try to make a new path. This
  743. // uses "/" as the directory separator, "~" as a shortcut
  744. // for the home directory (but only when seen at the start
  745. // of a path), and ".." to denote the parent directory.
  746. // returns an array of the files listed,
  747. // or false if an error occurred.
  748. function processPath(path)
  749. {
  750.   var fileArray = new Array();
  751.   var strLength = path.length;
  752.  
  753.   if (path[0] == '"' && filePickerMode == nsIFilePicker.modeOpenMultiple &&
  754.       strLength > 1) {
  755.     // we have a quoted list of filenames, separated by spaces.
  756.     // iterate the list and process each file.
  757.  
  758.     var curFileStart = 1;
  759.  
  760.     while (1) {
  761.       var nextQuote;
  762.  
  763.       // Look for an unescaped quote
  764.       var quoteSearchStart = curFileStart + 1;
  765.       do {
  766.         nextQuote = path.indexOf('"', quoteSearchStart);
  767.         quoteSearchStart = nextQuote + 1;
  768.       } while (nextQuote != -1 && path[nextQuote - 1] == '\\');
  769.       
  770.       if (nextQuote == -1) {
  771.         // we have a filename with no trailing quote.
  772.         // just assume that the filename ends at the end of the string.
  773.  
  774.         if (!processPathEntry(path.substring(curFileStart), fileArray))
  775.           return false;
  776.         break;
  777.       }
  778.  
  779.       if (!processPathEntry(path.substring(curFileStart, nextQuote), fileArray))
  780.         return false;
  781.  
  782.       curFileStart = path.indexOf('"', nextQuote + 1);
  783.       if (curFileStart == -1) {
  784.         // no more quotes, but if we're not at the end of the string,
  785.         // go ahead and process the remaining text.
  786.  
  787.         if (nextQuote < strLength - 1)
  788.           if (!processPathEntry(path.substring(nextQuote + 1), fileArray))
  789.             return false;
  790.         break;
  791.       }
  792.       ++curFileStart;
  793.     }
  794.   } else {
  795.     // If we didn't start with a quote, assume we just have a single file.
  796.     if (!processPathEntry(path, fileArray))
  797.       return false;
  798.   }
  799.  
  800.   return fileArray;
  801. }
  802.  
  803. function processPathEntry(path, fileArray)
  804. {
  805.   var filePath;
  806.   var file;
  807.  
  808.   try {
  809.     file = sfile.clone().QueryInterface(nsILocalFile);
  810.   } catch(e) {
  811.     dump("Couldn't clone\n"+e);
  812.     return false;
  813.   }
  814.  
  815.   var tilde_file = file.clone();
  816.   tilde_file.append("~");
  817.   if (path[0] == '~' &&                        // Expand ~ to $HOME, except:
  818.       !(path == "~" && tilde_file.exists()) && // If ~ was entered and such a file exists, don't expand
  819.       (path.length == 1 || path[1] == "/"))    // We don't want to expand ~file to ${HOME}file
  820.     filePath = homeDir.path + path.substring(1);
  821.   else
  822.     filePath = path;
  823.  
  824.   // Unescape quotes
  825.   filePath = filePath.replace(/\\\"/g, "\"");
  826.   
  827.   if (filePath[0] == '/')   /* an absolute path was entered */
  828.     file.initWithPath(filePath);
  829.   else if ((filePath.indexOf("/../") > 0) ||
  830.            (filePath.substr(-3) == "/..") ||
  831.            (filePath.substr(0,3) == "../") ||
  832.            (filePath == "..")) {
  833.     /* appendRelativePath doesn't allow .. */
  834.     try{
  835.       file.initWithPath(file.path + "/" + filePath);
  836.     } catch (e) {
  837.       dump("Couldn't init path\n"+e);
  838.       return false;
  839.     }
  840.   }
  841.   else {
  842.     try {
  843.       file.appendRelativePath(filePath);
  844.     } catch (e) {
  845.       dump("Couldn't append path\n"+e);
  846.       return false;
  847.     }
  848.   }
  849.  
  850.   fileArray[fileArray.length] = file;
  851.   return true;
  852. }
  853.