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