home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 December / PCWorld_2005-12_cd.bin / komunikace / netscape / nsb-install-8-0.exe / chrome / toolkit.jar / content / global / filepicker.js < prev    next >
Text File  |  2005-09-26  |  24KB  |  822 lines

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