Back to index

lightning-sunbird  0.9+nobinonly
nsFilePicker.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *   Steve Dagley <sdagley@netscape.com>
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 #include "nsCOMPtr.h"
00043 #include "nsReadableUtils.h"
00044 #include "nsNetUtil.h"
00045 #include "nsIComponentManager.h"
00046 #include "nsILocalFile.h"
00047 #include "nsILocalFileMac.h"
00048 #include "nsIURL.h"
00049 #include "nsIFileURL.h"
00050 #include "nsToolkit.h"
00051 #include "nsIEventSink.h"
00052 #include "nsArrayEnumerator.h"
00053 
00054 #include <InternetConfig.h>
00055 
00056 #include "nsCarbonHelpers.h"
00057 
00058 #include "nsFilePicker.h"
00059 
00060 #include "nsIInternetConfigService.h"
00061 #include "nsIMIMEInfo.h"
00062 
00063 
00064 NS_IMPL_ISUPPORTS1(nsFilePicker, nsIFilePicker)
00065 
00066 OSType nsFilePicker::sCurrentProcessSignature = 0;
00067 
00068 //-------------------------------------------------------------------------
00069 //
00070 // nsFilePicker constructor
00071 //
00072 //-------------------------------------------------------------------------
00073 nsFilePicker::nsFilePicker()
00074   : mAllFilesDisplayed(PR_TRUE)
00075   , mApplicationsDisplayed(PR_FALSE)
00076   , mSelectedType(0)
00077   , mTypeOffset(0)
00078 {
00079   
00080   // Zero out the type lists
00081   for (int i = 0; i < kMaxTypeListCount; i++)
00082        mTypeLists[i] = 0L;
00083   
00084   mSelectedType = 0;
00085   // If NavServces < 2.0 we need to play games with the mSelectedType
00086   mTypeOffset = (NavLibraryVersion() < 0x02000000) ? 1 : 0;
00087 
00088   if (sCurrentProcessSignature == 0)
00089   {
00090     ProcessSerialNumber psn;
00091     ProcessInfoRec  info;
00092     
00093     psn.highLongOfPSN = 0;
00094     psn.lowLongOfPSN  = kCurrentProcess;
00095 
00096     info.processInfoLength = sizeof(ProcessInfoRec);
00097     info.processName = nil;
00098     info.processAppSpec = nil;
00099     OSErr err = ::GetProcessInformation(&psn, &info);
00100     if (err == noErr)
00101         sCurrentProcessSignature = info.processSignature;
00102     // Try again next time if error
00103   }
00104 }
00105 
00106 
00107 //-------------------------------------------------------------------------
00108 //
00109 // nsFilePicker destructor
00110 //
00111 //-------------------------------------------------------------------------
00112 nsFilePicker::~nsFilePicker()
00113 {
00114        // Destroy any filters we have built
00115        if ( mFilters.Count() ) {
00116          for (int i = 0; i < kMaxTypeListCount; i++) {
00117               if (mTypeLists[i])
00118                      DisposePtr((Ptr)mTypeLists[i]);
00119          }
00120        }      
00121   mFilters.Clear();
00122   mTitles.Clear();
00123 
00124 }
00125 
00126 
00127 void
00128 nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle,
00129                          PRInt16 aMode)
00130 {
00131   mTitle = aTitle;
00132   mMode = aMode;
00133 }
00134 
00135 
00136 //-------------------------------------------------------------------------
00137 //
00138 // Show - Display the file dialog
00139 //
00140 //-------------------------------------------------------------------------
00141 NS_IMETHODIMP nsFilePicker::Show(PRInt16 *retval)
00142 {
00143   NS_ENSURE_ARG_POINTER(retval);
00144   *retval = returnCancel;
00145       
00146   PRInt16 userClicksOK = returnCancel;
00147   
00148   mFiles.Clear();
00149   
00150   // XXX Ignore the filter list for now....
00151   
00152   mFiles.Clear();
00153   nsCOMPtr<nsILocalFile> theFile;
00154 
00155   switch (mMode)
00156   {
00157     case modeOpen:
00158       userClicksOK = GetLocalFiles(mTitle, PR_FALSE, mFiles);
00159       break;
00160     
00161     case modeOpenMultiple:
00162       userClicksOK = GetLocalFiles(mTitle, PR_TRUE, mFiles);
00163       break;
00164       
00165     case modeSave:
00166       userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile));
00167       break;
00168       
00169     case modeGetFolder:
00170       userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile));
00171       break;
00172     
00173     default:
00174       NS_ASSERTION(0, "Unknown file picker mode");
00175       break;
00176   }
00177   if (theFile)
00178     mFiles.AppendObject(theFile);
00179   
00180   *retval = userClicksOK;
00181   return NS_OK;
00182 }
00183 
00184 
00185 //
00186 // HandleShowPopupMenuSelect
00187 //
00188 // Figure out which menu item was selected and set mSelectedType accordingly
00189 // We do this by the rather easy/skanky method of using the menuType field of the
00190 // NavMenuItemSpec as the index into the menu items we've added
00191 // And oh so strangely enough Nav Services 3.0 uses the menuType field the same way - as an
00192 // index into CFArray of popupExtension strings so we don't have to special case this
00193 // based on TARGET_CARBON
00194 void nsFilePicker::HandleShowPopupMenuSelect(NavCBRecPtr callBackParms)
00195 {
00196   if (callBackParms)
00197   {
00198     NavMenuItemSpec menuItemSpec = *(NavMenuItemSpec*)callBackParms->eventData.eventDataParms.param;
00199     PRUint32        numMenuItems = mTitles.Count();
00200     if (mTypeOffset && (numMenuItems != 0))
00201     { // Special case Nav Services prior to 2.0
00202       // Make sure the menu item selected was one of ours
00203       if ((menuItemSpec.menuType != menuItemSpec.menuCreator) ||
00204           ((PRInt32)menuItemSpec.menuType < mTypeOffset) ||
00205           (menuItemSpec.menuType > numMenuItems))
00206       { // Doesn't appear to be one of our items selected so force it to be
00207         NavMenuItemSpec  menuItem;
00208         menuItem.version = kNavMenuItemSpecVersion;
00209         menuItem.menuType = mSelectedType + mTypeOffset;
00210         menuItem.menuCreator = mSelectedType + mTypeOffset;
00211         menuItem.menuItemName[0] = 0;
00212         (void)::NavCustomControl(callBackParms->context, kNavCtlSelectCustomType, &menuItem);
00213       }
00214       else
00215         mSelectedType = menuItemSpec.menuType - mTypeOffset;
00216     }
00217     else
00218       mSelectedType = menuItemSpec.menuType;
00219   }
00220 }
00221 
00222 
00223 //
00224 // FileDialogEventHandlerProc
00225 //
00226 // An event filter proc for NavServices so the dialogs will be movable-modals.
00227 //
00228 pascal void nsFilePicker::FileDialogEventHandlerProc(NavEventCallbackMessage msg, NavCBRecPtr cbRec, NavCallBackUserData callbackUD)
00229 {
00230   nsFilePicker* self = NS_REINTERPRET_CAST(nsFilePicker*, callbackUD);
00231   switch (msg)
00232   {
00233     case kNavCBEvent:
00234       switch (cbRec->eventData.eventDataParms.event->what)
00235       {
00236         case updateEvt:
00237         {
00238           WindowPtr window = reinterpret_cast<WindowPtr>(cbRec->eventData.eventDataParms.event->message);
00239           nsCOMPtr<nsIEventSink> sink;
00240           nsToolkit::GetWindowEventSink (window, getter_AddRefs(sink));
00241           if (sink)
00242           {
00243             ::BeginUpdate(window);
00244             PRBool handled = PR_FALSE;
00245             sink->DispatchEvent(cbRec->eventData.eventDataParms.event, &handled);
00246             ::EndUpdate(window);           
00247           }        
00248         }
00249         break;
00250       }
00251       break;
00252     
00253     case kNavCBStart:
00254     {
00255       NavMenuItemSpec  menuItem;
00256       menuItem.version = kNavMenuItemSpecVersion;
00257       menuItem.menuType = self->mSelectedType + self->mTypeOffset;
00258       menuItem.menuCreator = self->mSelectedType + self->mTypeOffset;
00259       menuItem.menuItemName[0] = 0;
00260       (void)::NavCustomControl(cbRec->context, kNavCtlSelectCustomType, &menuItem);
00261 
00262       // Set the directory to mDisplayDirectory if available
00263       if (self->mDisplayDirectory != nsnull) {
00264         nsCOMPtr<nsILocalFileMac> localDisplay = do_QueryInterface(self->mDisplayDirectory);
00265         if (localDisplay) {
00266           FSRef displayFSRef;
00267           if (NS_SUCCEEDED(localDisplay->GetFSRef(&displayFSRef))) {
00268             AEDesc desc;
00269             OSErr status = ::AECreateDesc(typeFSRef, &displayFSRef, sizeof(displayFSRef), &desc);
00270             if (status == noErr) {
00271               (void)::NavCustomControl(cbRec->context, kNavCtlSetLocation, &desc);
00272               (void)::AEDisposeDesc(&desc);
00273             }
00274           }
00275         }
00276       }
00277     }
00278     break;
00279     
00280     case kNavCBPopupMenuSelect:
00281       // Format menu boinked - see what's happening
00282       if (self)
00283         self->HandleShowPopupMenuSelect(cbRec);
00284       break;
00285   }
00286 }
00287 
00288 
00289 //
00290 // IsFileInFilterList
00291 //
00292 // Check our |mTypeLists| list to see if the given type is in there.
00293 //
00294 Boolean
00295 nsFilePicker::IsTypeInFilterList ( ResType inType )
00296 {
00297   for ( int i = 0; i < mFilters.Count(); ++i ) {
00298     for ( int j = 0; j < mTypeLists[i]->osTypeCount; ++j ) {
00299       if ( mTypeLists[i]->osType[j] == inType ) 
00300         return true;
00301     }  // foreach type w/in the group
00302   } // for each filter group
00303   
00304   return false;
00305   
00306 } // IsFileInFilterList
00307 
00308 
00309 Boolean
00310 nsFilePicker::IsExtensionInFilterList ( StrFileName & inFileName )
00311 {
00312   char extension[256];
00313   
00314   // determine the extension from the file name
00315   unsigned char* curr = &inFileName[inFileName[0]];
00316   while ( curr != inFileName && *curr-- != '.' ) ;
00317   if ( curr == inFileName )                         // no '.' in string, fails this check
00318     return false;
00319   ++curr;                                           // we took one too many steps back
00320   short extensionLen = (inFileName + inFileName[0]) - curr + 1;
00321   strncpy ( extension, (char*)curr, extensionLen);
00322   extension[extensionLen] = '\0';
00323   
00324   // see if it is in our list
00325   for ( int i = 0; i < mFlatFilters.Count(); ++i ) {
00326     if ( mFlatFilters[i]->Equals(extension) )
00327       return true;
00328   }
00329   return false;
00330 }
00331 
00332 
00333 //
00334 // FileDialogFilterProc
00335 //
00336 // Called from navServices with our filePicker object as |callbackUD|, check our
00337 // internal list to see if the file should be displayed.
00338 //
00339 pascal
00340 Boolean 
00341 nsFilePicker::FileDialogFilterProc(AEDesc* theItem, void* theInfo,
00342                                         NavCallBackUserData callbackUD, NavFilterModes filterMode)
00343 {
00344   Boolean shouldDisplay = true;
00345   nsFilePicker* self = NS_REINTERPRET_CAST(nsFilePicker*, callbackUD);
00346   if ( self && !self->mAllFilesDisplayed ) {
00347     if ( theItem->descriptorType == typeFSS ) {
00348       NavFileOrFolderInfo* info = NS_REINTERPRET_CAST ( NavFileOrFolderInfo*, theInfo );
00349       if ( !info->isFolder ) {
00350         // check it against our list. If that fails, check the extension directly
00351         if ( ! self->IsTypeInFilterList(info->fileAndFolder.fileInfo.finderInfo.fdType) ) {
00352           FSSpec fileSpec;
00353           if ( ::AEGetDescData(theItem, &fileSpec, sizeof(FSSpec)) == noErr )
00354             if ( ! self->IsExtensionInFilterList(fileSpec.name) )
00355               shouldDisplay = false;
00356         }
00357       } // if file isn't a folder
00358     } // if the item is an FSSpec
00359   }
00360   
00361   return shouldDisplay;
00362   
00363 } // FileDialogFilterProc                                        
00364 
00365 
00366 
00367 //-------------------------------------------------------------------------
00368 //
00369 // GetFile
00370 //
00371 // Use NavServices to do a GetFile. Returns PR_TRUE if the user presses OK in the dialog. If
00372 // they do so, ioLocalFile is initialized to that file.
00373 //
00374 //-------------------------------------------------------------------------
00375 
00376 
00377 PRInt16
00378 nsFilePicker::GetLocalFiles(const nsString& inTitle, PRBool inAllowMultiple, nsCOMArray<nsILocalFile>& outFiles)
00379 {
00380   PRInt16 retVal = returnCancel;
00381   NavEventUPP eventProc = NewNavEventUPP(FileDialogEventHandlerProc);  // doesn't really matter if this fails
00382   NavObjectFilterUPP filterProc = NewNavObjectFilterUPP(FileDialogFilterProc);  // doesn't really matter if this fails
00383   NavDialogRef dialog;
00384   NavDialogCreationOptions dialogCreateOptions;
00385   // Set defaults
00386   OSErr anErr = ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions);
00387   if (anErr != noErr)
00388     return retVal;
00389     
00390   // Set the options for how the get file dialog will appear
00391   dialogCreateOptions.optionFlags |= kNavNoTypePopup;
00392   dialogCreateOptions.optionFlags |= kNavDontAutoTranslate;
00393   dialogCreateOptions.optionFlags |= kNavDontAddTranslateItems;
00394   if (inAllowMultiple)
00395     dialogCreateOptions.optionFlags |= kNavAllowMultipleFiles;
00396   dialogCreateOptions.modality = kWindowModalityAppModal;
00397   dialogCreateOptions.parentWindow = NULL;
00398   CFStringRef titleRef = CFStringCreateWithCharacters(NULL, 
00399                                                      (const UniChar *) inTitle.get(), inTitle.Length());
00400   dialogCreateOptions.windowTitle = titleRef;
00401 
00402   // sets up the |mTypeLists| array so the filter proc can use it
00403   MapFilterToFileTypes();
00404        
00405   // allow packages to be chosen if the filter is "*" or "..apps"
00406   if (mAllFilesDisplayed || mApplicationsDisplayed)
00407     dialogCreateOptions.optionFlags |= kNavSupportPackages;           
00408 
00409   // Display the get file dialog. Only use a filter proc if there are any
00410   // filters registered.
00411   anErr = ::NavCreateGetFileDialog(
00412                                   &dialogCreateOptions,
00413                                   NULL, //  NavTypeListHandle
00414                                   eventProc,
00415                                   NULL, //  NavPreviewUPP
00416                                   mFilters.Count() ? filterProc : NULL,
00417                                   this, //  inClientData
00418                                   &dialog);
00419   if (anErr == noErr)
00420   {
00421     anErr = ::NavDialogRun(dialog);
00422 
00423     if (anErr == noErr)
00424     {
00425        NavReplyRecord reply;
00426       anErr = ::NavDialogGetReply(dialog, &reply);
00427       if (anErr == noErr && reply.validRecord)
00428       {
00429         long numItems;
00430         anErr = ::AECountItems((const AEDescList *)&reply.selection, &numItems);
00431         if (anErr == noErr)
00432         {
00433           for (long i = 1; i <= numItems; i ++)
00434           {
00435             AEKeyword   theKeyword;
00436             DescType    actualType;
00437             Size        actualSize;
00438             FSRef       theFSRef;
00439 
00440             // Get the FSRef for the file to be opened (or directory in case of a package)
00441             anErr = ::AEGetNthPtr(&(reply.selection), i, typeFSRef, &theKeyword, &actualType,
00442                                   &theFSRef, sizeof(theFSRef), &actualSize);
00443             if (anErr == noErr)
00444             {
00445               nsCOMPtr<nsILocalFile> localFile;
00446               NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00447               nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00448               if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithFSRef(&theFSRef)))
00449                 outFiles.AppendObject(localFile);
00450             }
00451           }
00452 
00453           if (outFiles.Count() > 0)
00454             retVal = returnOK;
00455         }
00456         
00457         // Some housekeeping for Nav Services 
00458         ::NavDisposeReply(&reply);
00459       }
00460     }
00461     ::NavDialogDispose(dialog);
00462   }
00463   
00464   // Free the CF objects from the dialogCreateOptions struct
00465   if (titleRef)
00466     CFRelease(titleRef);
00467 
00468   if (filterProc)
00469     ::DisposeNavObjectFilterUPP(filterProc);
00470        
00471   if ( eventProc )
00472     ::DisposeNavEventUPP(eventProc);
00473               
00474   return retVal;
00475 } // GetFile
00476 
00477 
00478 //-------------------------------------------------------------------------
00479 //
00480 // GetFolder
00481 //
00482 // Use NavServices to do a GetFolder. Returns PR_TRUE if the user presses OK in the dialog. If
00483 // they do so, ioLocalFile is initialized to be the chosen folder.
00484 //
00485 //-------------------------------------------------------------------------
00486 PRInt16
00487 nsFilePicker::GetLocalFolder(const nsString& inTitle, nsILocalFile** outFile)
00488 {
00489   NS_ENSURE_ARG_POINTER(outFile);
00490   *outFile = nsnull;
00491   
00492   PRInt16 retVal = returnCancel;
00493   NavEventUPP eventProc = NewNavEventUPP(FileDialogEventHandlerProc);  // doesn't really matter if this fails
00494   NavDialogRef dialog;
00495   NavDialogCreationOptions dialogCreateOptions;
00496 
00497   // Set defaults
00498   OSErr anErr = ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions);
00499   if (anErr != noErr)
00500     return retVal;
00501 
00502   // Set the options for how the get file dialog will appear
00503   dialogCreateOptions.optionFlags |= kNavNoTypePopup;
00504   dialogCreateOptions.optionFlags |= kNavDontAutoTranslate;
00505   dialogCreateOptions.optionFlags |= kNavDontAddTranslateItems;
00506   dialogCreateOptions.optionFlags ^= kNavAllowMultipleFiles;
00507   dialogCreateOptions.modality = kWindowModalityAppModal;
00508   dialogCreateOptions.parentWindow = NULL;
00509   CFStringRef titleRef = CFStringCreateWithCharacters(NULL, (const UniChar *) inTitle.get(), inTitle.Length());
00510   dialogCreateOptions.windowTitle = titleRef;
00511 
00512   anErr = ::NavCreateChooseFolderDialog(
00513                                         &dialogCreateOptions,
00514                                         eventProc,
00515                                         NULL, // filter proc
00516                                         this, // inClientData
00517                                         &dialog);
00518 
00519   if (anErr == noErr)
00520   {
00521     anErr = ::NavDialogRun(dialog);
00522     if (anErr == noErr)
00523     {
00524        NavReplyRecord reply;
00525       anErr = ::NavDialogGetReply(dialog, &reply);
00526       if (anErr == noErr && reply.validRecord) {
00527         AEKeyword   theKeyword;
00528         DescType    actualType;
00529         Size        actualSize;
00530         FSRef       theFSRef;
00531          
00532         // Get the FSRef for the selected folder
00533         anErr = ::AEGetNthPtr(&(reply.selection), 1, typeFSRef, &theKeyword, &actualType,
00534                               &theFSRef, sizeof(theFSRef), &actualSize);
00535         if (anErr == noErr)
00536         {
00537           nsCOMPtr<nsILocalFile> localFile;
00538           NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00539           nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00540           if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithFSRef(&theFSRef)))
00541           {
00542             *outFile = localFile;
00543             NS_ADDREF(*outFile);
00544             retVal = returnOK;
00545           }
00546         }
00547         // Some housekeeping for Nav Services 
00548         ::NavDisposeReply(&reply);
00549       }
00550     }
00551     ::NavDialogDispose(dialog);
00552   }
00553   
00554   // Free the CF objects from the dialogCreateOptions struct
00555   if (dialogCreateOptions.windowTitle)
00556     CFRelease(dialogCreateOptions.windowTitle);
00557        
00558   if (eventProc)
00559     ::DisposeNavEventUPP(eventProc);
00560               
00561   return retVal;
00562 } // GetFolder
00563 
00564 PRInt16
00565 nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsILocalFile** outFile)
00566 {
00567   NS_ENSURE_ARG_POINTER(outFile);
00568   *outFile = nsnull;
00569   
00570   PRInt16 retVal = returnCancel;
00571   NavEventUPP eventProc = NewNavEventUPP(FileDialogEventHandlerProc);  // doesn't really matter if this fails
00572   OSType typeToSave = 'TEXT';
00573   OSType creatorToSave = (sCurrentProcessSignature == 0) ? 'MOZZ' : sCurrentProcessSignature;
00574   NavDialogRef dialog;
00575   NavDialogCreationOptions dialogCreateOptions;
00576 
00577   // Set defaults
00578   OSErr anErr = ::NavGetDefaultDialogCreationOptions(&dialogCreateOptions);
00579   if (anErr != noErr)
00580     return retVal;
00581 
00582   // Set the options for how the put file dialog will appear
00583   if (mTitles.Count() == 0)
00584   { // If we have no filter titles then suppress the save type popup
00585     dialogCreateOptions.optionFlags |= kNavNoTypePopup;
00586   }
00587   else
00588   {
00589     dialogCreateOptions.optionFlags &= ~kNavAllowStationery;  // remove Stationery option
00590     creatorToSave = kNavGenericSignature;   // This supresses the default format menu items
00591     SetupFormatMenuItems(&dialogCreateOptions);
00592   }
00593   
00594   dialogCreateOptions.optionFlags |= kNavDontAutoTranslate;
00595   dialogCreateOptions.optionFlags |= kNavDontAddTranslateItems;
00596   dialogCreateOptions.optionFlags ^= kNavAllowMultipleFiles;
00597   dialogCreateOptions.modality = kWindowModalityAppModal;
00598   dialogCreateOptions.parentWindow = NULL;
00599   CFStringRef titleRef = CFStringCreateWithCharacters(NULL, 
00600                                                       (const UniChar *) inTitle.get(), inTitle.Length());
00601   dialogCreateOptions.windowTitle = titleRef;
00602   CFStringRef defaultFileNameRef = CFStringCreateWithCharacters(NULL, 
00603                                                                 (const UniChar *) inDefaultName.get(), 
00604                                                                 inDefaultName.Length());
00605   dialogCreateOptions.saveFileName = defaultFileNameRef;
00606 
00607   anErr = ::NavCreatePutFileDialog(
00608                                    &dialogCreateOptions,
00609                                    typeToSave,
00610                                    creatorToSave,
00611                                    eventProc,
00612                                    this, // inClientData
00613                                    &dialog);
00614 
00615   if (anErr == noErr)
00616   {
00617     anErr = ::NavDialogRun(dialog);
00618 
00619     if (anErr == noErr)
00620     {
00621        NavReplyRecord reply;
00622       anErr = ::NavDialogGetReply(dialog, &reply);
00623       if (anErr == noErr && reply.validRecord)
00624       {
00625         AEKeyword   theKeyword;
00626         DescType    actualType;
00627         Size        actualSize;
00628         FSRef       theFSRef;
00629 
00630         // Create a CFURL of the file to be saved. The impl of InitWithCFURL in
00631         // nsLocalFileMac.cpp handles truncating a too-long file name since only
00632         // it has that problem.
00633         anErr = ::AEGetNthPtr(&(reply.selection), 1, typeFSRef, &theKeyword, &actualType,
00634                               &theFSRef, sizeof(theFSRef), &actualSize);
00635         if (anErr == noErr)
00636         {
00637           CFURLRef fileURL;
00638           CFURLRef parentURL = ::CFURLCreateFromFSRef(NULL, &theFSRef);
00639           if (parentURL)
00640           {
00641             fileURL = ::CFURLCreateCopyAppendingPathComponent(NULL, parentURL, reply.saveFileName, PR_FALSE);
00642             if (fileURL)
00643             {
00644               nsCOMPtr<nsILocalFile> localFile;
00645               NS_NewLocalFile(EmptyString(), PR_TRUE, getter_AddRefs(localFile));
00646               nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
00647               if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL(fileURL)))
00648               {
00649                 *outFile = localFile;
00650                 NS_ADDREF(*outFile);
00651                 retVal = reply.replacing ? returnReplace : returnOK;
00652               }
00653               ::CFRelease(fileURL);
00654             }
00655             ::CFRelease(parentURL);
00656           }
00657         }                     
00658 
00659         // Some housekeeping for Nav Services 
00660         ::NavCompleteSave(&reply, kNavTranslateInPlace);
00661         ::NavDisposeReply(&reply);
00662       }
00663     }
00664     ::NavDialogDispose(dialog);
00665   }  
00666   
00667   // Free the CF objects from the dialogCreateOptions struct
00668   if (dialogCreateOptions.windowTitle)
00669     CFRelease(dialogCreateOptions.windowTitle);
00670   if (dialogCreateOptions.saveFileName)
00671     CFRelease(dialogCreateOptions.saveFileName);
00672   if (dialogCreateOptions.popupExtension)
00673     CFRelease(dialogCreateOptions.popupExtension);
00674        
00675   if (eventProc)
00676     ::DisposeNavEventUPP(eventProc);
00677        
00678   return retVal;     
00679 }
00680 
00681 //
00682 // MapFilterToFileTypes
00683 //
00684 // Take the list of file types (in a nice win32-specific format) and ask IC to give us 
00685 // the MacOS file type codes for them.
00686 //
00687 void
00688 nsFilePicker::MapFilterToFileTypes ( )
00689 {
00690   nsCOMPtr<nsIInternetConfigService> icService ( do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID) );
00691   NS_ASSERTION(icService, "Can't get InternetConfig Service, bailing out");
00692   if ( !icService ) {
00693     // We couldn't get the IC Service, bail. Since |mAllFilesDisplayed| is still
00694     // set, the dialog will allow selection of all files. 
00695     return;
00696   }
00697   
00698   if (mFilters.Count())
00699   {
00700     // First we allocate the memory for the Mac type lists
00701     for (PRInt32 loop1 = 0; loop1 < mFilters.Count() && loop1 < kMaxTypeListCount; loop1++)
00702     {
00703       mTypeLists[loop1] =
00704         (NavTypeListPtr)NewPtrClear(sizeof(NavTypeList) + kMaxTypesPerFilter * sizeof(OSType));     
00705       if ( !mTypeLists[loop1] )
00706         return;                     // don't worry, we'll clean up in the dtor
00707     }
00708     
00709     // Now loop through each of the filter strings
00710     for (PRInt32 loop1 = 0; loop1 < mFilters.Count() && loop1 < kMaxTypeListCount; loop1++)
00711     {
00712       const nsString& filterWide = *mFilters[loop1];
00713       char* filter = ToNewCString(filterWide);
00714 
00715       NS_ASSERTION ( filterWide.Length(), "Oops. filepicker.properties not correctly installed");       
00716 
00717       // look for the flag indicating applications
00718       if (filterWide.EqualsLiteral("..apps"))
00719         mApplicationsDisplayed = PR_TRUE;
00720 
00721       if ( filterWide.Length() && filter )
00722       {
00723         PRUint32 filterIndex = 0;         // Index into the filter string
00724         PRUint32 typeTempIndex = 0;       // Index into the temp string for a single filter type
00725         PRUint32 typesInThisFilter = 0;   // Count for # of types in this filter
00726         bool finishedThisFilter = false;  // Flag so we know when we're finsihed with the filter
00727         char typeTemp[256];
00728         char tempChar;           // char we're currently looking at
00729 
00730         // Loop through the characters of filter string. Every time we get to a
00731         // semicolon (or a null, meaning we've hit the end of the full string)
00732         // then we've found the filter and can pass it off to IC to get a macOS 
00733         // file type out of it.
00734         do
00735         {
00736           tempChar = filter[filterIndex];
00737           if ((tempChar == ';') || (tempChar == 0)) {   // End of filter type reached
00738             typeTemp[typeTempIndex] = '\0';             // null terminate
00739             
00740             // to make it easier to match file extensions while we're filtering, flatten
00741             // out the list. Ignore filters that are just "*" and also remove the
00742             // leading "*" from filters we do add.
00743             if ( strlen(typeTemp) > 1 )
00744               mFlatFilters.AppendCString ( nsCString((char*)&typeTemp[1]) );  // cut out the "*"
00745             
00746             // ask IC if it's not "all files" (designated by "*")
00747             if ( !(typeTemp[1] == '\0' && typeTemp[0] == '*') ) {
00748               mAllFilesDisplayed = PR_FALSE;
00749               
00750               nsCOMPtr<nsIMIMEInfo> icEntry;
00751               icService->GetMIMEInfoFromExtension(typeTemp, getter_AddRefs(icEntry));
00752               if ( icEntry )
00753               {
00754                 bool addToList = true;
00755                 OSType tempOSType;
00756                 icEntry->GetMacType(NS_REINTERPRET_CAST(PRUint32*, (&tempOSType)));
00757                 for (PRUint32 typeIndex = 0; typeIndex < typesInThisFilter; typeIndex++)
00758                 {
00759                   if (mTypeLists[loop1]->osType[typeIndex] == tempOSType)
00760                   {
00761                     addToList = false;
00762                     break;
00763                   }
00764                 }
00765                 if (addToList && typesInThisFilter < kMaxTypesPerFilter)
00766                   mTypeLists[loop1]->osType[typesInThisFilter++] = tempOSType;
00767               }
00768             } // if not "*"
00769             
00770             typeTempIndex = 0;      // Reset the temp string for the type
00771             if (tempChar == '\0')
00772               finishedThisFilter = true;
00773           }
00774           else
00775           {
00776             // strip out whitespace as we go
00777             if ( tempChar != ' ' )
00778               typeTemp[typeTempIndex++] = tempChar;
00779           }
00780           
00781           filterIndex++;
00782         } while (!finishedThisFilter);
00783         
00784         // Set how many OSTypes we actually found
00785         mTypeLists[loop1]->osTypeCount = typesInThisFilter;
00786         
00787         nsMemory::Free ( NS_REINTERPRET_CAST(void*, filter) );
00788       }
00789     }
00790   }
00791 
00792 } // MapFilterToFileTypes
00793 
00794 
00795 //-------------------------------------------------------------------------
00796 // Util func to take the array of mTitles and make it into something
00797 // Nav Services can use.
00798 // This is the TARGET_CARBON version for Nav Services 3.0
00799 //-------------------------------------------------------------------------
00800 void nsFilePicker::SetupFormatMenuItems (NavDialogCreationOptions* dialogCreateOptions)
00801 {
00802   PRInt32   numMenuItems = mTitles.Count();
00803   PRInt32   index;
00804   CFStringRef      itemStr = NULL;
00805   CFArrayCallBacks callBacks = kCFTypeArrayCallBacks;
00806   
00807   // Under NavServices 3.0 the popupExtension is actually a CFArray of CFStrings
00808   dialogCreateOptions->popupExtension = CFArrayCreateMutable(kCFAllocatorDefault, numMenuItems, &callBacks);
00809   
00810   if (dialogCreateOptions->popupExtension)
00811   {
00812     for (index = 0; index < numMenuItems; ++index)
00813     {
00814       const nsString& titleWide = *mTitles[index];
00815       itemStr = CFStringCreateWithCharacters(NULL, (const unsigned short *)titleWide.get(), titleWide.Length());
00816       CFArrayInsertValueAtIndex((CFMutableArrayRef)dialogCreateOptions->popupExtension, index, (void*)itemStr);
00817       CFRelease(itemStr);
00818     }
00819   }
00820 }
00821 
00822 
00823 //-------------------------------------------------------------------------
00824 NS_IMETHODIMP nsFilePicker::GetFile(nsILocalFile **aFile)
00825 {
00826   NS_ENSURE_ARG_POINTER(aFile);
00827   *aFile = nsnull;
00828   
00829   // just return the first file
00830   if (mFiles.Count() > 0)
00831   {
00832     *aFile = mFiles.ObjectAt(0);
00833     NS_IF_ADDREF(*aFile);
00834   }
00835 
00836   return NS_OK;
00837 }
00838 
00839 //-------------------------------------------------------------------------
00840 NS_IMETHODIMP nsFilePicker::GetFileURL(nsIFileURL **aFileURL)
00841 {
00842   NS_ENSURE_ARG_POINTER(aFileURL);
00843   *aFileURL = nsnull;
00844 
00845   if (mFiles.Count() == 0)
00846     return NS_OK;
00847 
00848   nsCOMPtr<nsIURI> uri;
00849   nsresult rv = NS_NewFileURI(getter_AddRefs(uri), mFiles.ObjectAt(0));
00850   if (NS_FAILED(rv)) return rv;
00851 
00852   nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri));
00853   NS_ENSURE_TRUE(fileURL, NS_ERROR_FAILURE);
00854   
00855   NS_ADDREF(*aFileURL = fileURL);
00856   return NS_OK;
00857 }
00858 
00859 //-------------------------------------------------------------------------
00860 NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
00861 {
00862   return NS_NewArrayEnumerator(aFiles, mFiles);
00863 }
00864 
00865 //-------------------------------------------------------------------------
00866 //
00867 // Get the file + path
00868 //
00869 //-------------------------------------------------------------------------
00870 NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString)
00871 {
00872   mDefault = aString;
00873   return NS_OK;
00874 }
00875 
00876 NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString)
00877 {
00878   return NS_ERROR_FAILURE;
00879 }
00880 
00881 //-------------------------------------------------------------------------
00882 //
00883 // The default extension to use for files
00884 //
00885 //-------------------------------------------------------------------------
00886 NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension)
00887 {
00888   aExtension.Truncate();
00889   return NS_OK;
00890 }
00891 
00892 NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension)
00893 {
00894   return NS_OK;
00895 }
00896 
00897 //-------------------------------------------------------------------------
00898 //-------------------------------------------------------------------------
00899 NS_IMETHODIMP
00900 nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
00901 {
00902   mFilters.AppendString(aFilter);
00903   mTitles.AppendString(aTitle);
00904   
00905   return NS_OK;
00906 }
00907 
00908 
00909 //-------------------------------------------------------------------------
00910 //
00911 // Get the filter index
00912 //
00913 //-------------------------------------------------------------------------
00914 NS_IMETHODIMP nsFilePicker::GetFilterIndex(PRInt32 *aFilterIndex)
00915 {
00916   *aFilterIndex = mSelectedType;
00917   return NS_OK;
00918 }
00919 
00920 //-------------------------------------------------------------------------
00921 //
00922 // Set the filter index
00923 //
00924 //-------------------------------------------------------------------------
00925 NS_IMETHODIMP nsFilePicker::SetFilterIndex(PRInt32 aFilterIndex)
00926 {
00927   mSelectedType = aFilterIndex;
00928   return NS_OK;
00929 }
00930