toolkit/components/filepicker/content/filepicker.js

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

mercurial