Back to index

lightning-sunbird  0.9+nobinonly
nsFilePicker.mm
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla browser.
00016  *
00017  * The Initial Developer of the Original Code is Netscape
00018  * Communications Corporation. Portions created by Netscape are
00019  * Copyright (C) 1999 Netscape Communications Corporation. All
00020  * Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Stuart Parmenter <pavlov@netscape.com>
00024  *   Steve Dagley <sdagley@netscape.com>
00025  *   David Haas <haasd@cae.wisc.edu>
00026  *   Simon Fraser <sfraser@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 // Arrgh.  Bitten by bug 154232 here.  
00043 // Need to undef DARWIN before including Cocoa & (by default) CoreFoundation
00044 // so we can use CFURLGetFSRef.
00045 
00046 #undef DARWIN
00047 #include <Cocoa/Cocoa.h>
00048 #define DARWIN  
00049 
00050 #include "nsCOMPtr.h"
00051 #include "nsReadableUtils.h"
00052 #include "nsNetUtil.h"
00053 #include "nsIComponentManager.h"
00054 #include "nsILocalFile.h"
00055 #include "nsILocalFileMac.h"
00056 #include "nsIURL.h"
00057 #include "nsIFileURL.h"
00058 #include "nsArrayEnumerator.h"
00059 
00060 #include "nsFilePicker.h"
00061 
00062 
00063 NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker)
00064 
00065 //-------------------------------------------------------------------------
00066 //
00067 // nsFilePicker constructor
00068 //
00069 //-------------------------------------------------------------------------
00070 nsFilePicker::nsFilePicker()
00071 : mAllFilesDisplayed(PR_TRUE)
00072 , mMode(0)
00073 , mSelectedType(0)
00074 {
00075 }
00076 
00077 
00078 //-------------------------------------------------------------------------
00079 //
00080 // nsFilePicker destructor
00081 //
00082 //-------------------------------------------------------------------------
00083 nsFilePicker::~nsFilePicker()
00084 {
00085   // string arrays clean themselves up
00086 }
00087 
00088 
00089 void
00090 nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle,
00091                          PRInt16 aMode)
00092 {
00093   mTitle = aTitle;
00094   mMode = aMode;
00095 }
00096 
00097 
00098 //-------------------------------------------------------------------------
00099 //
00100 // Show - Display the file dialog
00101 //
00102 //-------------------------------------------------------------------------
00103 NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
00104 {
00105   NS_ENSURE_ARG_POINTER(retval);
00106 
00107   *retval = returnCancel;
00108 
00109   PRInt16 userClicksOK = returnCancel;
00110 //
00111 // Random questions from DHH:
00112 //
00113 // Why do we pass mTitle, mDefault to the functions?  Can GetLocalFile. PutLocalFile,
00114 // and GetLocalFolder get called someplace else?  It generates a bunch of warnings
00115 // as it is right now.
00116 //
00117 // I think we could easily combine GetLocalFile and GetLocalFolder together, just
00118 // setting panel pick options based on mMode.  I didn't do it here b/c I wanted to 
00119 // make this look as much like Carbon nsFilePicker as possible.  
00120 //  
00121 //  
00122 
00123   mFiles.Clear();
00124   nsCOMPtr<nsILocalFile> theFile;
00125 
00126   switch (mMode)
00127   {
00128     case modeOpen:
00129       userClicksOK = GetLocalFiles(mTitle, PR_FALSE, mFiles);
00130       break;
00131     
00132     case modeOpenMultiple:
00133       userClicksOK = GetLocalFiles(mTitle, PR_TRUE, mFiles);
00134       break;
00135       
00136     case modeSave:
00137       userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile));
00138       break;
00139       
00140     case modeGetFolder:
00141       userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile));
00142       break;
00143     
00144     default:
00145       NS_ASSERTION(0, "Unknown file picker mode");
00146       break;
00147   }
00148 
00149   if (theFile)
00150     mFiles.AppendObject(theFile);
00151   
00152   *retval = userClicksOK;
00153   return NS_OK;
00154 }
00155 
00156 
00157 //-------------------------------------------------------------------------
00158 //
00159 // GetLocalFile
00160 //
00161 // Use OpenPanel to do a GetFile. Returns PR_TRUE if the user presses OK in the dialog. If
00162 // they do so, the selected file is in the FSSpec.
00163 //
00164 //-------------------------------------------------------------------------
00165 PRInt16
00166 nsFilePicker::GetLocalFiles(const nsString& inTitle, PRBool inAllowMultiple, nsCOMArray<nsILocalFile>& outFiles)
00167 {
00168   PRInt16 retVal = (PRInt16)returnCancel;
00169   NSOpenPanel *thePanel = [NSOpenPanel openPanel];
00170 
00171   // Get filters
00172   NSArray *filters = GenerateFilterList();
00173 
00174   // Set the options for how the get file dialog will appear
00175   SetDialogTitle(inTitle, thePanel);
00176   [thePanel setAllowsMultipleSelection:inAllowMultiple];
00177   [thePanel setCanSelectHiddenExtension:YES];
00178   [thePanel setCanChooseDirectories:NO];
00179   [thePanel setCanChooseFiles:YES];
00180   [thePanel setResolvesAliases:YES];        //this is default - probably doesn't need to be set
00181   // handle how we deal with packages. 
00182   if (filters)
00183     [thePanel setTreatsFilePackagesAsDirectories:NO];       
00184 
00185   // set up default directory
00186   NSString *theDir = PanelDefaultDirectory();
00187   int result = [thePanel runModalForDirectory:theDir file:nil types:filters];  
00188   
00189   if (result == NSFileHandlingPanelCancelButton)
00190     return retVal;
00191   
00192   // append each chosen file to our list
00193   for (unsigned int i = 0; i < [[thePanel URLs] count]; i ++)
00194   {
00195     NSURL *theURL = [[thePanel URLs] objectAtIndex:i];
00196     if (theURL)
00197     {
00198       nsCOMPtr<nsILocalFile> localFile;
00199       NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00200       nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00201       if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL)))
00202         outFiles.AppendObject(localFile);
00203     }
00204   }
00205 
00206   if (outFiles.Count() > 0)
00207     retVal = returnOK;
00208 
00209   return retVal;
00210 } // GetFile
00211 
00212 
00213 //-------------------------------------------------------------------------
00214 //
00215 // GetLocalFolder
00216 //
00217 // Use OpenPanel to do a GetFolder. Returns PR_TRUE if the user presses OK in the dialog. If
00218 // they do so, the folder location is in the FSSpec.
00219 //
00220 // Note: Apparently, not used in Chimera.  So it hasn't been tested.  At all.
00221 //       Consider yourself warned.
00222 //-------------------------------------------------------------------------
00223 PRInt16
00224 nsFilePicker::GetLocalFolder(const nsString& inTitle, nsILocalFile** outFile)
00225 {
00226   NS_ENSURE_ARG(outFile);
00227   *outFile = nsnull;
00228   
00229   PRInt16 retVal = (PRInt16)returnCancel;
00230   NSOpenPanel *thePanel = [NSOpenPanel openPanel];
00231 
00232   // Set the options for how the get file dialog will appear
00233   SetDialogTitle(inTitle, thePanel);
00234   [thePanel setAllowsMultipleSelection:NO];   //this is default -probably doesn't need to be set
00235   [thePanel setCanSelectHiddenExtension:YES];
00236   [thePanel setCanChooseDirectories:YES];
00237   [thePanel setCanChooseFiles:NO];
00238   [thePanel setResolvesAliases:YES];          //this is default - probably doesn't need to be set
00239   [thePanel setTreatsFilePackagesAsDirectories:YES]; //sure, we can pick packages.  
00240 
00241   // set up default directory
00242   NSString *theDir = PanelDefaultDirectory();
00243   int result = [thePanel runModalForDirectory:theDir file:nil types:nil];  
00244 
00245   if (result == NSFileHandlingPanelCancelButton)
00246     return retVal;
00247     
00248   // get FSRef for folder (we allow just 1, so that's all we get)
00249   NSURL *theURL = [[thePanel URLs] objectAtIndex:0];
00250   if (theURL)
00251   {
00252     nsCOMPtr<nsILocalFile> localFile;
00253     NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00254     nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00255     if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL)))
00256     {
00257       *outFile = localFile;
00258       NS_ADDREF(*outFile);
00259       retVal = returnOK;
00260     }
00261   }
00262 
00263   return retVal;
00264 } // GetFolder
00265 
00266 //-------------------------------------------------------------------------
00267 //
00268 // PutLocalFile
00269 //
00270 // Use SavePanel to do a PutFile. Returns returnOK if the user presses OK in the dialog. If
00271 // they do so, the folder location is in the FSSpec.
00272 //
00273 // Note: I don't think Chimera is using this at all.  So - it hasn't been tested.
00274 //       If you end up calling it, let me know how it turns out.
00275 //-------------------------------------------------------------------------
00276 
00277 PRInt16
00278 nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsILocalFile** outFile)
00279 {
00280   NS_ENSURE_ARG(outFile);
00281   *outFile = nsnull;
00282 
00283   PRInt16 retVal = returnCancel;
00284   NSSavePanel *thePanel = [NSSavePanel savePanel];
00285   
00286   // set up save panel options & whatnot.
00287   SetDialogTitle(inTitle, thePanel);
00288   
00289   // set up default file name
00290   NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()];
00291 
00292   // set up default directory
00293   NSString *theDir = PanelDefaultDirectory();
00294   
00295   // load the panel.
00296   int result = [thePanel runModalForDirectory:theDir file:defaultFilename];
00297   if (result == NSFileHandlingPanelCancelButton)
00298     return retVal;
00299 
00300   // Get the NSURL for the directory where the file to be saved
00301   NSURL *dirURL = [NSURL fileURLWithPath:[thePanel directory]];
00302   // append the filename
00303   NSURL *fileURL = [[NSURL alloc] initWithString:[thePanel filename] relativeToURL:dirURL];
00304   if (fileURL)
00305   { 
00306     nsCOMPtr<nsILocalFile> localFile;
00307     NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00308     nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00309     if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL)))
00310     {
00311       *outFile = localFile;
00312       NS_ADDREF(*outFile);
00313       // XXX how can we tell if we're replacing? Need to return returnReplace
00314       retVal = returnOK;
00315     }
00316 
00317     [fileURL release];
00318   }      
00319   return retVal;    
00320 }
00321 
00322 //-------------------------------------------------------------------------
00323 //
00324 // GenerateFilterList
00325 //
00326 // Take the list of file types (in a nice win32-specific format) and fills up
00327 // an NSArray of them for the Open Panel.  Cocoa's open panel doesn't recognize * 
00328 // as being equal to everything, so make sure there aren't any in mFlatFilter array.
00329 //
00330 //-------------------------------------------------------------------------
00331 
00332 NSArray *
00333 nsFilePicker::GenerateFilterList()
00334 {
00335   NSArray *filterArray = nil;
00336   if (mFilters.Count() > 0)
00337   {
00338     // Set up our filter string
00339     NSMutableString *giantFilterString = [[NSMutableString alloc] initWithString:@""];
00340     if (!giantFilterString) //dang.
00341       return filterArray;   
00342 
00343     // Loop through each of the filter strings
00344     for (PRInt32 loop = 0; loop < mFilters.Count(); loop++)
00345     {
00346       nsString* filterWide = mFilters[loop];
00347       if (filterWide && filterWide->Length() > 0)
00348         [giantFilterString appendString:[NSString stringWithCharacters:filterWide->get() length:filterWide->Length()]];
00349     }
00350     
00351     // Now we clean stuff up.  Get rid of white spaces, "*"'s, and the odd period or two.
00352     NSCharacterSet *aSet = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithString:@". *"]];
00353     NSRange aRange = [giantFilterString rangeOfCharacterFromSet:aSet];
00354     while (aRange.length) {
00355       [giantFilterString replaceCharactersInRange:aRange withString:@""];
00356       aRange = [giantFilterString rangeOfCharacterFromSet:aSet];
00357     }   
00358     // OK, if string isn't empty we'll make a new filter list
00359     if ([giantFilterString length] > 0)
00360       // every time we find a semicolon, we've found a new filter.
00361       // components SeparatedByString should do that for us.
00362       filterArray = [[[NSArray alloc] initWithArray:[giantFilterString componentsSeparatedByString:@";"]] autorelease];
00363     [giantFilterString release];
00364   }
00365   return filterArray;
00366 } // GenerateFilterList
00367 
00368 //-------------------------------------------------------------------------
00369 //
00370 // SetDialogTitle
00371 // 
00372 // Sets the dialog title to whatever it should be.  If it fails, eh,
00373 // the OS will provide a sensible default.
00374 //
00375 //-------------------------------------------------------------------------
00376 
00377 void
00378 nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel)
00379 {
00380   [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]];
00381 } 
00382 
00383 //-------------------------------------------------------------------------
00384 //
00385 // PanelDefaultDirectory
00386 //
00387 // Converts path from an nsILocalFile into a NSString path
00388 // If it fails, returns an empty string.
00389 //
00390 //-------------------------------------------------------------------------
00391 NSString *
00392 nsFilePicker::PanelDefaultDirectory()
00393 {
00394   NSString *directory = nil;
00395   if (mDisplayDirectory) {
00396     nsAutoString pathStr;
00397     mDisplayDirectory->GetPath(pathStr);
00398     directory = [[[NSString alloc] initWithCharacters:pathStr.get() length:pathStr.Length()] autorelease];
00399   }
00400   return directory;
00401 }
00402 
00403 
00404 //-------------------------------------------------------------------------
00405 NS_IMETHODIMP nsFilePicker::GetFile(nsILocalFile **aFile)
00406 {
00407   NS_ENSURE_ARG_POINTER(aFile);
00408   *aFile = nsnull;
00409   
00410   // just return the first file
00411   if (mFiles.Count() > 0)
00412   {
00413     *aFile = mFiles.ObjectAt(0);
00414     NS_IF_ADDREF(*aFile);
00415   }
00416 
00417   return NS_OK;
00418 }
00419 
00420 //-------------------------------------------------------------------------
00421 NS_IMETHODIMP nsFilePicker::GetFileURL(nsIFileURL **aFileURL)
00422 {
00423   NS_ENSURE_ARG_POINTER(aFileURL);
00424   *aFileURL = nsnull;
00425 
00426   if (mFiles.Count() == 0)
00427     return NS_OK;
00428 
00429   nsCOMPtr<nsIURI> uri;
00430   nsresult rv = NS_NewFileURI(getter_AddRefs(uri), mFiles.ObjectAt(0));
00431   if (NS_FAILED(rv)) return rv;
00432 
00433   nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri));
00434   NS_ENSURE_TRUE(fileURL, NS_ERROR_FAILURE);
00435   
00436   NS_ADDREF(*aFileURL = fileURL);
00437   return NS_OK;
00438 }
00439 
00440 //-------------------------------------------------------------------------
00441 NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
00442 {
00443   return NS_NewArrayEnumerator(aFiles, mFiles);
00444 }
00445 
00446 //-------------------------------------------------------------------------
00447 //
00448 // Get the file + path
00449 //
00450 //-------------------------------------------------------------------------
00451 NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString)
00452 {
00453   mDefault = aString;
00454   return NS_OK;
00455 }
00456 
00457 NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString)
00458 {
00459   return NS_ERROR_FAILURE;
00460 }
00461 
00462 //-------------------------------------------------------------------------
00463 //
00464 // The default extension to use for files
00465 //
00466 //-------------------------------------------------------------------------
00467 NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension)
00468 {
00469   aExtension.Truncate();
00470   return NS_OK;
00471 }
00472 
00473 NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension)
00474 {
00475   return NS_OK;
00476 }
00477 
00478 //-------------------------------------------------------------------------
00479 //
00480 // Append an entry to the filters array
00481 //
00482 //-------------------------------------------------------------------------
00483 NS_IMETHODIMP
00484 nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
00485 {
00486   mFilters.AppendString(aFilter);
00487   mTitles.AppendString(aTitle);
00488   
00489   return NS_OK;
00490 }
00491 
00492 
00493 //-------------------------------------------------------------------------
00494 //
00495 // Get the filter index - do we still need this?
00496 //
00497 //-------------------------------------------------------------------------
00498 NS_IMETHODIMP nsFilePicker::GetFilterIndex(PRInt32 *aFilterIndex)
00499 {
00500   *aFilterIndex = mSelectedType;
00501   return NS_OK;
00502 }
00503 
00504 //-------------------------------------------------------------------------
00505 //
00506 // Set the filter index - do we still need this?
00507 //
00508 //-------------------------------------------------------------------------
00509 NS_IMETHODIMP nsFilePicker::SetFilterIndex(PRInt32 aFilterIndex)
00510 {
00511   mSelectedType = aFilterIndex;
00512   return NS_OK;
00513 }
00514