fix for #121122 (nsIFilePicker should allow picking multiple files) and #43015 (Cannot add multiple attachment in Attach File dialog).

right now, only supported on windows.  bugs logged for mac, beos, cocoa, os/2, qnx
and the xpfilepicker (linux).

r=bz, sr=bienvenu, a=asa
This commit is contained in:
sspitzer%netscape.com 2002-09-07 05:23:45 +00:00
Родитель ccb57e7a19
Коммит d73ec61a67
12 изменённых файлов: 167 добавлений и 27 удалений

Просмотреть файл

@ -178,8 +178,8 @@ function enableEditableFields()
var gComposeRecyclingListener = { var gComposeRecyclingListener = {
onClose: function() { onClose: function() {
//Reset recipients and attachments //Reset recipients and attachments
awResetAllRows(); awResetAllRows();
RemoveAllAttachments(); RemoveAllAttachments();
//We need to clear the identity popup menu in case the user will change them. It will be rebuilded later in ComposeStartup //We need to clear the identity popup menu in case the user will change them. It will be rebuilded later in ComposeStartup
ClearIdentityListPopup(document.getElementById("msgIdentityPopup")); ClearIdentityListPopup(document.getElementById("msgIdentityPopup"));
@ -2191,12 +2191,12 @@ function SetLastAttachDirectory(attachedLocalFile)
function AttachFile() function AttachFile()
{ {
var currentAttachment = ""; var attachments;
//Get file using nsIFilePicker and convert to URL //Get file using nsIFilePicker and convert to URL
try { try {
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, sComposeMsgsBundle.getString("chooseFileToAttach"), nsIFilePicker.modeOpen); fp.init(window, sComposeMsgsBundle.getString("chooseFileToAttach"), nsIFilePicker.modeOpenMultiple);
var lastDirectory = GetLastAttachDirectory(); var lastDirectory = GetLastAttachDirectory();
if (lastDirectory) if (lastDirectory)
@ -2204,28 +2204,36 @@ function AttachFile()
fp.appendFilters(nsIFilePicker.filterAll); fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() == nsIFilePicker.returnOK) { if (fp.show() == nsIFilePicker.returnOK) {
currentAttachment = fp.fileURL.spec; attachments = fp.files;
SetLastAttachDirectory(fp.file)
} }
} }
catch (ex) { catch (ex) {
dump("failed to get the local file to attach\n"); dump("failed to get attachments: " + ex + "\n");
} }
if (currentAttachment == "") if (!attachments || !attachments.hasMoreElements())
return; return;
if (DuplicateFileCheck(currentAttachment)) var haveSetAttachDirectory = false;
{
dump("Error, attaching the same item twice\n"); while (attachments.hasMoreElements()) {
} var currentFile = attachments.getNext().QueryInterface(Components.interfaces.nsILocalFile);
else
{ if (!haveSetAttachDirectory) {
var attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"] SetLastAttachDirectory(currentFile);
.createInstance(Components.interfaces.nsIMsgAttachment); haveSetAttachDirectory = true;
attachment.url = currentAttachment; }
AddAttachment(attachment);
gContentChanged = true; var ioService = Components.classes["@mozilla.org/network/io-service;1"]
ioService = ioService.getService(Components.interfaces.nsIIOService);
var currentAttachment = ioService.getURLSpecFromFile(currentFile);
if (!DuplicateFileCheck(currentAttachment)) {
var attachment = Components.classes["@mozilla.org/messengercompose/attachment;1"].createInstance(Components.interfaces.nsIMsgAttachment);
attachment.url = currentAttachment;
AddAttachment(attachment);
gContentChanged = true;
}
} }
} }

Просмотреть файл

@ -26,6 +26,7 @@
interface nsILocalFile; interface nsILocalFile;
interface nsIFileURL; interface nsIFileURL;
interface nsIDOMWindowInternal; interface nsIDOMWindowInternal;
interface nsISimpleEnumerator;
[scriptable, uuid(c47de916-1dd1-11b2-8141-82507fa02b21)] [scriptable, uuid(c47de916-1dd1-11b2-8141-82507fa02b21)]
interface nsIFilePicker : nsISupports interface nsIFilePicker : nsISupports
@ -33,6 +34,7 @@ interface nsIFilePicker : nsISupports
const short modeOpen = 0; // Load a file or directory const short modeOpen = 0; // Load a file or directory
const short modeSave = 1; // Save a file or directory const short modeSave = 1; // Save a file or directory
const short modeGetFolder = 2; // Select a folder/directory const short modeGetFolder = 2; // Select a folder/directory
const short modeOpenMultiple= 3; // Load multiple files
const short returnOK = 0; // User hit Ok, process selection const short returnOK = 0; // User hit Ok, process selection
const short returnCancel = 1; // User hit cancel, ignore selection const short returnCancel = 1; // User hit cancel, ignore selection
@ -118,6 +120,14 @@ interface nsIFilePicker : nsISupports
*/ */
readonly attribute nsIFileURL fileURL; readonly attribute nsIFileURL fileURL;
/**
* Get the enumerator for the selected files
* only works in the modeOpenMultiple mode
*
* @return Returns the files currently selected
*/
readonly attribute nsISimpleEnumerator files;
/** /**
* Show File Dialog. The dialog is displayed modally. * Show File Dialog. The dialog is displayed modally.
* *

Просмотреть файл

@ -80,7 +80,7 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
node_flavors = B_DIRECTORY_NODE; node_flavors = B_DIRECTORY_NODE;
panel_mode = B_OPEN_PANEL; panel_mode = B_OPEN_PANEL;
} }
else if (mMode == modeOpen) { else if (mMode == modeOpen || mMode == modeOpenMultiple) {
node_flavors = B_FILE_NODE; node_flavors = B_FILE_NODE;
panel_mode = B_OPEN_PANEL; panel_mode = B_OPEN_PANEL;
} }
@ -136,7 +136,7 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
result = PR_FALSE; result = PR_FALSE;
} }
if (mMode == modeOpen && ppanel->IsOpenSelected()) { if ((mMode == modeOpen || mMode == modeOpenMultiple) && ppanel->IsOpenSelected()) {
BList *list = ppanel->OpenRefs(); BList *list = ppanel->OpenRefs();
if ((list) && list->CountItems() >= 1) { if ((list) && list->CountItems() >= 1) {
entry_ref *ref = (entry_ref *)list->ItemAt(0); entry_ref *ref = (entry_ref *)list->ItemAt(0);

Просмотреть файл

@ -208,7 +208,7 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
// XXX Ignore the filter list for now.... // XXX Ignore the filter list for now....
if (mMode == modeOpen) if (mMode == modeOpen || mMode == modeOpenMultiple)
userClicksOK = GetLocalFile(title, &theFile); userClicksOK = GetLocalFile(title, &theFile);
else if (mMode == modeSave) else if (mMode == modeSave)
userClicksOK = PutLocalFile(title, defaultName, &theFile); userClicksOK = PutLocalFile(title, defaultName, &theFile);

Просмотреть файл

@ -160,7 +160,7 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
// XXX Ignore the filter list for now.... // XXX Ignore the filter list for now....
if (mMode == modeOpen) if (mMode == modeOpen || mMode == modeOpenMultiple)
userClicksOK = GetLocalFile(mTitle, &theFile); userClicksOK = GetLocalFile(mTitle, &theFile);
else if (mMode == modeSave) else if (mMode == modeSave)
userClicksOK = PutLocalFile(mTitle, mDefault, &theFile); userClicksOK = PutLocalFile(mTitle, mDefault, &theFile);

Просмотреть файл

@ -19,6 +19,7 @@
* *
* Contributor(s): * Contributor(s):
* Stuart Parmenter <pavlov@netscape.com> * Stuart Parmenter <pavlov@netscape.com>
* Seth Spitzer <sspitzer@netscape.com>
*/ */
// Define so header files for openfilename are included // Define so header files for openfilename are included
@ -36,6 +37,7 @@
#include "nsIURL.h" #include "nsIURL.h"
#include "nsIFileURL.h" #include "nsIFileURL.h"
#include "nsIStringBundle.h" #include "nsIStringBundle.h"
#include "nsEnumeratorUtils.h"
#include "nsCRT.h" #include "nsCRT.h"
#include <windows.h> #include <windows.h>
#include <SHLOBJ.H> #include <SHLOBJ.H>
@ -404,6 +406,10 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *aReturnVal)
ofn.Flags |= OFN_FILEMUSTEXIST; ofn.Flags |= OFN_FILEMUSTEXIST;
result = ::GetOpenFileName(&ofn); result = ::GetOpenFileName(&ofn);
} }
else if (mMode == modeOpenMultiple) {
ofn.Flags |= OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
result = ::GetOpenFileName(&ofn);
}
else if (mMode == modeSave) { else if (mMode == modeSave) {
ofn.Flags |= OFN_NOREADONLYRETURN; ofn.Flags |= OFN_NOREADONLYRETURN;
result = ::GetSaveFileName(&ofn); result = ::GetSaveFileName(&ofn);
@ -433,9 +439,53 @@ NS_IMETHODIMP nsFilePicker::Show(PRInt16 *aReturnVal)
if (result == PR_TRUE) { if (result == PR_TRUE) {
// I think it also needs a conversion here (to unicode since appending to nsString) // I think it also needs a conversion here (to unicode since appending to nsString)
// but doing that generates garbage file name, weird. // but doing that generates garbage file name, weird.
if (mMode == modeOpenMultiple) {
nsresult rv = NS_NewISupportsArray(getter_AddRefs(mFiles));
NS_ENSURE_SUCCESS(rv,rv);
// from msdn.microsoft.com, "Open and Save As Dialog Boxes" section:
// If you specify OFN_EXPLORER,
// The directory and file name strings are NULL separated,
// with an extra NULL character after the last file name.
// This format enables the Explorer-style dialog boxes
// to return long file names that include spaces.
char *current = fileBuffer;
const char *dirName = current;
while (current && *current && *(current + strlen(current) + 1)) {
current = current + strlen(current) + 1;
nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1", &rv);
NS_ENSURE_SUCCESS(rv,rv);
// dirName contains a trailing slash
rv = file->InitWithNativePath(nsDependentCString(dirName) + nsDependentCString(current));
NS_ENSURE_SUCCESS(rv,rv);
rv = mFiles->AppendElement(file);
NS_ENSURE_SUCCESS(rv,rv);
}
// handle the case where the user selected just one
// file. according to msdn.microsoft.com:
// If you specify OFN_ALLOWMULTISELECT and the user selects
// only one file, the lpstrFile string does not have
// a separator between the path and file name.
if (current && *current && (current == fileBuffer)) {
nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1", &rv);
NS_ENSURE_SUCCESS(rv,rv);
rv = file->InitWithNativePath(nsDependentCString(current));
NS_ENSURE_SUCCESS(rv,rv);
rv = mFiles->AppendElement(file);
NS_ENSURE_SUCCESS(rv,rv);
}
}
else {
mFile.Append(fileBuffer); mFile.Append(fileBuffer);
} }
}
} }
if (title) if (title)
@ -528,6 +578,12 @@ NS_IMETHODIMP nsFilePicker::GetFileURL(nsIFileURL **aFileURL)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
{
NS_ENSURE_ARG_POINTER(aFiles);
return NS_NewArrayEnumerator(aFiles, mFiles);
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// //
// Get the file + path // Get the file + path

Просмотреть файл

@ -19,12 +19,15 @@
* *
* Contributor(s): * Contributor(s):
* Stuart Parmenter <pavlov@netscape.com> * Stuart Parmenter <pavlov@netscape.com>
* Seth Spitzer <sspitzer@netscape.com>
*/ */
#ifndef nsFilePicker_h__ #ifndef nsFilePicker_h__
#define nsFilePicker_h__ #define nsFilePicker_h__
#include "nsILocalFile.h" #include "nsILocalFile.h"
#include "nsISimpleEnumerator.h"
#include "nsISupportsArray.h"
#include "nsICharsetConverterManager.h" #include "nsICharsetConverterManager.h"
#include "nsBaseFilePicker.h" #include "nsBaseFilePicker.h"
@ -54,6 +57,7 @@ public:
NS_IMETHOD SetFilterIndex(PRInt32 aFilterIndex); NS_IMETHOD SetFilterIndex(PRInt32 aFilterIndex);
NS_IMETHOD GetFile(nsILocalFile * *aFile); NS_IMETHOD GetFile(nsILocalFile * *aFile);
NS_IMETHOD GetFileURL(nsIFileURL * *aFileURL); NS_IMETHOD GetFileURL(nsIFileURL * *aFileURL);
NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles);
NS_IMETHOD Show(PRInt16 *aReturnVal); NS_IMETHOD Show(PRInt16 *aReturnVal);
#ifdef MOZ_UNICODE #ifdef MOZ_UNICODE
NS_IMETHOD ShowW(PRInt16 *aReturnVal); NS_IMETHOD ShowW(PRInt16 *aReturnVal);
@ -83,7 +87,7 @@ protected:
nsIUnicodeDecoder* mUnicodeDecoder; nsIUnicodeDecoder* mUnicodeDecoder;
nsCOMPtr<nsILocalFile> mDisplayDirectory; nsCOMPtr<nsILocalFile> mDisplayDirectory;
PRInt16 mSelectedType; PRInt16 mSelectedType;
nsCOMPtr <nsISupportsArray> mFiles;
static char mLastUsedDirectory[]; static char mLastUsedDirectory[];
#ifdef MOZ_UNICODE #ifdef MOZ_UNICODE

Просмотреть файл

@ -36,6 +36,9 @@
#include "nsIStringBundle.h" #include "nsIStringBundle.h"
#include "nsXPIDLString.h" #include "nsXPIDLString.h"
#include "nsIServiceManager.h" #include "nsIServiceManager.h"
#include "nsISupportsArray.h"
#include "nsILocalFile.h"
#include "nsEnumeratorUtils.h"
#include "nsBaseFilePicker.h" #include "nsBaseFilePicker.h"
@ -183,3 +186,22 @@ NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(PRInt32 aFilterIndex)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsBaseFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
{
NS_ENSURE_ARG_POINTER(aFiles);
nsCOMPtr <nsISupportsArray> files;
nsresult rv = NS_NewISupportsArray(getter_AddRefs(files));
NS_ENSURE_SUCCESS(rv,rv);
// if we get into the base class, the platform
// doesn't implement GetFiles() yet.
// so we fake it.
nsCOMPtr <nsILocalFile> file;
rv = GetFile(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv,rv);
rv = files->AppendElement(file);
NS_ENSURE_SUCCESS(rv,rv);
return NS_NewArrayEnumerator(aFiles, files);
}

Просмотреть файл

@ -27,6 +27,7 @@
#include "nsIFilePicker.h" #include "nsIFilePicker.h"
#include "nsIWidget.h" #include "nsIWidget.h"
#include "nsISimpleEnumerator.h"
class nsBaseFilePicker : public nsIFilePicker class nsBaseFilePicker : public nsIFilePicker
{ {
@ -41,6 +42,7 @@ public:
NS_IMETHOD AppendFilters(PRInt32 filterMask); NS_IMETHOD AppendFilters(PRInt32 filterMask);
NS_IMETHOD GetFilterIndex(PRInt32 *aFilterIndex); NS_IMETHOD GetFilterIndex(PRInt32 *aFilterIndex);
NS_IMETHOD SetFilterIndex(PRInt32 aFilterIndex); NS_IMETHOD SetFilterIndex(PRInt32 aFilterIndex);
NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles);
protected: protected:

Просмотреть файл

@ -78,12 +78,13 @@ function filepickerLoad() {
} }
} }
if (filePickerMode != nsIFilePicker.modeOpen) { if (filePickerMode != nsIFilePicker.modeOpen && filePickerMode != nsIFilePicker.modeOpenMultiple) {
var newDirButton = document.getElementById("newDirButton"); var newDirButton = document.getElementById("newDirButton");
newDirButton.removeAttribute("hidden"); newDirButton.removeAttribute("hidden");
} }
if ((filePickerMode == nsIFilePicker.modeOpen) || if ((filePickerMode == nsIFilePicker.modeOpen) ||
(filePickerMode == nsIFilePicker.modeOpenMultiple) ||
(filePickerMode == nsIFilePicker.modeSave)) { (filePickerMode == nsIFilePicker.modeSave)) {
treeView.setFilter(filterTypes[0]); treeView.setFilter(filterTypes[0]);
@ -199,7 +200,9 @@ function openOnOK()
if (dir) if (dir)
gotoDirectory(dir); gotoDirectory(dir);
retvals.file = dir; retvals.file = dir;
retvals.buttonStatus = nsIFilePicker.returnCancel; retvals.buttonStatus = nsIFilePicker.returnCancel;
var filterMenuList = document.getElementById("filterMenuList"); var filterMenuList = document.getElementById("filterMenuList");
@ -248,6 +251,7 @@ function selectOnOK()
switch(filePickerMode) { switch(filePickerMode) {
case nsIFilePicker.modeOpen: case nsIFilePicker.modeOpen:
case nsIFilePicker.modeOpenMultiple:
if (isFile) { if (isFile) {
if (file.isReadable()) { if (file.isReadable()) {
retvals.directory = file.parent.path; retvals.directory = file.parent.path;
@ -339,6 +343,11 @@ function selectOnOK()
} }
retvals.file = file; retvals.file = file;
gFilesEnumerator.mFile = file;
gFilesEnumerator.mHasMore = true;
retvals.files = gFilesEnumerator;
retvals.buttonStatus = ret; retvals.buttonStatus = ret;
var filterMenuList = document.getElementById("filterMenuList"); var filterMenuList = document.getElementById("filterMenuList");
@ -347,11 +356,27 @@ function selectOnOK()
return (ret != nsIFilePicker.returnCancel); return (ret != nsIFilePicker.returnCancel);
} }
var gFilesEnumerator = {
mHasMore: false,
mFile: null,
hasMoreElements: function()
{
return this.mHasMore;
},
getNext: function()
{
this.mHasMore = false;
return this.mFile;
}
};
function onCancel() function onCancel()
{ {
// Close the window. // Close the window.
retvals.buttonStatus = nsIFilePicker.returnCancel; retvals.buttonStatus = nsIFilePicker.returnCancel;
retvals.file = null; retvals.file = null;
retvals.files = null;
return true; return true;
} }
@ -465,6 +490,7 @@ function getOKAction(file) {
buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel"); buttonLabel = gFilePickerBundle.getString("selectFolderButtonLabel");
break; break;
case nsIFilePicker.modeOpen: case nsIFilePicker.modeOpen:
case nsIFilePicker.modeOpenMultiple:
buttonLabel = gFilePickerBundle.getString("openButtonLabel"); buttonLabel = gFilePickerBundle.getString("openButtonLabel");
break; break;
case nsIFilePicker.modeSave: case nsIFilePicker.modeSave:

Просмотреть файл

@ -84,6 +84,10 @@ nsFilePicker.prototype = {
set file(a) { throw "readonly property"; }, set file(a) { throw "readonly property"; },
get file() { return this.mFile; }, get file() { return this.mFile; },
/* readonly attribute nsISimpleEnumerator files; */
set files(a) { throw "readonly property"; },
get files() { return this.mFilesEnumerator; },
/* readonly attribute nsIFileURL fileURL; */ /* readonly attribute nsIFileURL fileURL; */
set fileURL(a) { throw "readonly property"; }, set fileURL(a) { throw "readonly property"; },
get fileURL() { get fileURL() {
@ -110,6 +114,7 @@ nsFilePicker.prototype = {
/* members */ /* members */
mFile: undefined, mFile: undefined,
mFilesEnumerator: undefined,
mParentWindow: null, mParentWindow: null,
/* methods */ /* methods */
@ -197,6 +202,7 @@ nsFilePicker.prototype = {
o); o);
this.mFile = o.retvals.file; this.mFile = o.retvals.file;
this.mFilterIndex = o.retvals.filterIndex; this.mFilterIndex = o.retvals.filterIndex;
this.mFilesEnumerator = o.retvals.files;
lastDirectory = o.retvals.directory; lastDirectory = o.retvals.directory;
return o.retvals.buttonStatus; return o.retvals.buttonStatus;
} catch(ex) { dump("unable to open file picker\n" + ex + "\n"); } } catch(ex) { dump("unable to open file picker\n" + ex + "\n"); }

Просмотреть файл

@ -84,6 +84,10 @@ nsFilePicker.prototype = {
set file(a) { throw "readonly property"; }, set file(a) { throw "readonly property"; },
get file() { return this.mFile; }, get file() { return this.mFile; },
/* readonly attribute nsISimpleEnumerator files; */
set files(a) { throw "readonly property"; },
get files() { return this.mFilesEnumerator; },
/* readonly attribute nsIFileURL fileURL; */ /* readonly attribute nsIFileURL fileURL; */
set fileURL(a) { throw "readonly property"; }, set fileURL(a) { throw "readonly property"; },
get fileURL() { get fileURL() {
@ -110,6 +114,7 @@ nsFilePicker.prototype = {
/* members */ /* members */
mFile: undefined, mFile: undefined,
mFilesEnumerator: undefined,
mParentWindow: null, mParentWindow: null,
/* methods */ /* methods */
@ -197,6 +202,7 @@ nsFilePicker.prototype = {
o); o);
this.mFile = o.retvals.file; this.mFile = o.retvals.file;
this.mFilterIndex = o.retvals.filterIndex; this.mFilterIndex = o.retvals.filterIndex;
this.mFilesEnumerator = o.retvals.files;
lastDirectory = o.retvals.directory; lastDirectory = o.retvals.directory;
return o.retvals.buttonStatus; return o.retvals.buttonStatus;
} catch(ex) { dump("unable to open file picker\n" + ex + "\n"); } } catch(ex) { dump("unable to open file picker\n" + ex + "\n"); }