widget/cocoa/nsFilePicker.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #import <Cocoa/Cocoa.h>
michael@0 7
michael@0 8 #include "nsFilePicker.h"
michael@0 9 #include "nsCOMPtr.h"
michael@0 10 #include "nsReadableUtils.h"
michael@0 11 #include "nsNetUtil.h"
michael@0 12 #include "nsIComponentManager.h"
michael@0 13 #include "nsIFile.h"
michael@0 14 #include "nsILocalFileMac.h"
michael@0 15 #include "nsIURL.h"
michael@0 16 #include "nsArrayEnumerator.h"
michael@0 17 #include "nsIStringBundle.h"
michael@0 18 #include "nsCocoaFeatures.h"
michael@0 19 #include "nsCocoaUtils.h"
michael@0 20 #include "mozilla/Preferences.h"
michael@0 21
michael@0 22 // This must be included last:
michael@0 23 #include "nsObjCExceptions.h"
michael@0 24
michael@0 25 using namespace mozilla;
michael@0 26
michael@0 27 const float kAccessoryViewPadding = 5;
michael@0 28 const int kSaveTypeControlTag = 1;
michael@0 29
michael@0 30 static bool gCallSecretHiddenFileAPI = false;
michael@0 31 const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles";
michael@0 32
michael@0 33 /**
michael@0 34 * This class is an observer of NSPopUpButton selection change.
michael@0 35 */
michael@0 36 @interface NSPopUpButtonObserver : NSObject
michael@0 37 {
michael@0 38 NSPopUpButton* mPopUpButton;
michael@0 39 NSOpenPanel* mOpenPanel;
michael@0 40 nsFilePicker* mFilePicker;
michael@0 41 }
michael@0 42 - (void) setPopUpButton:(NSPopUpButton*)aPopUpButton;
michael@0 43 - (void) setOpenPanel:(NSOpenPanel*)aOpenPanel;
michael@0 44 - (void) setFilePicker:(nsFilePicker*)aFilePicker;
michael@0 45 - (void) menuChangedItem:(NSNotification*)aSender;
michael@0 46 @end
michael@0 47
michael@0 48 NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
michael@0 49
michael@0 50 // We never want to call the secret show hidden files API unless the pref
michael@0 51 // has been set. Once the pref has been set we always need to call it even
michael@0 52 // if it disappears so that we stop showing hidden files if a user deletes
michael@0 53 // the pref. If the secret API was used once and things worked out it should
michael@0 54 // continue working for subsequent calls so the user is at no more risk.
michael@0 55 static void SetShowHiddenFileState(NSSavePanel* panel)
michael@0 56 {
michael@0 57 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 58
michael@0 59 bool show = false;
michael@0 60 if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) {
michael@0 61 gCallSecretHiddenFileAPI = true;
michael@0 62 }
michael@0 63
michael@0 64 if (gCallSecretHiddenFileAPI) {
michael@0 65 // invoke a method to get a Cocoa-internal nav view
michael@0 66 SEL navViewSelector = @selector(_navView);
michael@0 67 NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector];
michael@0 68 if (!navViewSignature)
michael@0 69 return;
michael@0 70 NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature];
michael@0 71 [navViewInvocation setSelector:navViewSelector];
michael@0 72 [navViewInvocation setTarget:panel];
michael@0 73 [navViewInvocation invoke];
michael@0 74
michael@0 75 // get the returned nav view
michael@0 76 id navView = nil;
michael@0 77 [navViewInvocation getReturnValue:&navView];
michael@0 78
michael@0 79 // invoke the secret show hidden file state method on the nav view
michael@0 80 SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:);
michael@0 81 NSMethodSignature* showHiddenFilesSignature = [navView methodSignatureForSelector:showHiddenFilesSelector];
michael@0 82 if (!showHiddenFilesSignature)
michael@0 83 return;
michael@0 84 NSInvocation* showHiddenFilesInvocation = [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature];
michael@0 85 [showHiddenFilesInvocation setSelector:showHiddenFilesSelector];
michael@0 86 [showHiddenFilesInvocation setTarget:navView];
michael@0 87 [showHiddenFilesInvocation setArgument:&show atIndex:2];
michael@0 88 [showHiddenFilesInvocation invoke];
michael@0 89 }
michael@0 90
michael@0 91 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 92 }
michael@0 93
michael@0 94 nsFilePicker::nsFilePicker()
michael@0 95 : mSelectedTypeIndex(0)
michael@0 96 {
michael@0 97 }
michael@0 98
michael@0 99 nsFilePicker::~nsFilePicker()
michael@0 100 {
michael@0 101 }
michael@0 102
michael@0 103 void
michael@0 104 nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle)
michael@0 105 {
michael@0 106 mTitle = aTitle;
michael@0 107 }
michael@0 108
michael@0 109 NSView* nsFilePicker::GetAccessoryView()
michael@0 110 {
michael@0 111 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 112
michael@0 113 NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease];
michael@0 114
michael@0 115 // Set a label's default value.
michael@0 116 NSString* label = @"Format:";
michael@0 117
michael@0 118 // Try to get the localized string.
michael@0 119 nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
michael@0 120 nsCOMPtr<nsIStringBundle> bundle;
michael@0 121 nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle));
michael@0 122 if (NS_SUCCEEDED(rv)) {
michael@0 123 nsXPIDLString locaLabel;
michael@0 124 bundle->GetStringFromName(NS_LITERAL_STRING("formatLabel").get(),
michael@0 125 getter_Copies(locaLabel));
michael@0 126 if (locaLabel) {
michael@0 127 label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get())
michael@0 128 length:locaLabel.Length()];
michael@0 129 }
michael@0 130 }
michael@0 131
michael@0 132 // set up label text field
michael@0 133 NSTextField* textField = [[[NSTextField alloc] init] autorelease];
michael@0 134 [textField setEditable:NO];
michael@0 135 [textField setSelectable:NO];
michael@0 136 [textField setDrawsBackground:NO];
michael@0 137 [textField setBezeled:NO];
michael@0 138 [textField setBordered:NO];
michael@0 139 [textField setFont:[NSFont labelFontOfSize:13.0]];
michael@0 140 [textField setStringValue:label];
michael@0 141 [textField setTag:0];
michael@0 142 [textField sizeToFit];
michael@0 143
michael@0 144 // set up popup button
michael@0 145 NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) pullsDown:NO] autorelease];
michael@0 146 uint32_t numMenuItems = mTitles.Length();
michael@0 147 for (uint32_t i = 0; i < numMenuItems; i++) {
michael@0 148 const nsString& currentTitle = mTitles[i];
michael@0 149 NSString *titleString;
michael@0 150 if (currentTitle.IsEmpty()) {
michael@0 151 const nsString& currentFilter = mFilters[i];
michael@0 152 titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentFilter.get())
michael@0 153 length:currentFilter.Length()];
michael@0 154 }
michael@0 155 else {
michael@0 156 titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentTitle.get())
michael@0 157 length:currentTitle.Length()];
michael@0 158 }
michael@0 159 [popupButton addItemWithTitle:titleString];
michael@0 160 [titleString release];
michael@0 161 }
michael@0 162 if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems)
michael@0 163 [popupButton selectItemAtIndex:mSelectedTypeIndex];
michael@0 164 [popupButton setTag:kSaveTypeControlTag];
michael@0 165 [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us
michael@0 166 // This is just a default width that works well, doesn't truncate the vast majority of
michael@0 167 // things that might end up in the menu.
michael@0 168 [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)];
michael@0 169
michael@0 170 // position everything based on control sizes with kAccessoryViewPadding pix padding
michael@0 171 // on each side kAccessoryViewPadding pix horizontal padding between controls
michael@0 172 float greatestHeight = [textField frame].size.height;
michael@0 173 if ([popupButton frame].size.height > greatestHeight)
michael@0 174 greatestHeight = [popupButton frame].size.height;
michael@0 175 float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2;
michael@0 176 float totalViewWidth = [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3;
michael@0 177 [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)];
michael@0 178
michael@0 179 float textFieldOriginY = ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding;
michael@0 180 [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)];
michael@0 181
michael@0 182 float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2;
michael@0 183 float popupOriginY = ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding;
michael@0 184 [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)];
michael@0 185
michael@0 186 [accessoryView addSubview:textField];
michael@0 187 [accessoryView addSubview:popupButton];
michael@0 188 return accessoryView;
michael@0 189
michael@0 190 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 191 }
michael@0 192
michael@0 193 // Display the file dialog
michael@0 194 NS_IMETHODIMP nsFilePicker::Show(int16_t *retval)
michael@0 195 {
michael@0 196 NS_ENSURE_ARG_POINTER(retval);
michael@0 197
michael@0 198 *retval = returnCancel;
michael@0 199
michael@0 200 int16_t userClicksOK = returnCancel;
michael@0 201
michael@0 202 // Random questions from DHH:
michael@0 203 //
michael@0 204 // Why do we pass mTitle, mDefault to the functions? Can GetLocalFile. PutLocalFile,
michael@0 205 // and GetLocalFolder get called someplace else? It generates a bunch of warnings
michael@0 206 // as it is right now.
michael@0 207 //
michael@0 208 // I think we could easily combine GetLocalFile and GetLocalFolder together, just
michael@0 209 // setting panel pick options based on mMode. I didn't do it here b/c I wanted to
michael@0 210 // make this look as much like Carbon nsFilePicker as possible.
michael@0 211
michael@0 212 mFiles.Clear();
michael@0 213 nsCOMPtr<nsIFile> theFile;
michael@0 214
michael@0 215 switch (mMode)
michael@0 216 {
michael@0 217 case modeOpen:
michael@0 218 userClicksOK = GetLocalFiles(mTitle, false, mFiles);
michael@0 219 break;
michael@0 220
michael@0 221 case modeOpenMultiple:
michael@0 222 userClicksOK = GetLocalFiles(mTitle, true, mFiles);
michael@0 223 break;
michael@0 224
michael@0 225 case modeSave:
michael@0 226 userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile));
michael@0 227 break;
michael@0 228
michael@0 229 case modeGetFolder:
michael@0 230 userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile));
michael@0 231 break;
michael@0 232
michael@0 233 default:
michael@0 234 NS_ERROR("Unknown file picker mode");
michael@0 235 break;
michael@0 236 }
michael@0 237
michael@0 238 if (theFile)
michael@0 239 mFiles.AppendObject(theFile);
michael@0 240
michael@0 241 *retval = userClicksOK;
michael@0 242 return NS_OK;
michael@0 243 }
michael@0 244
michael@0 245 static
michael@0 246 void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters)
michael@0 247 {
michael@0 248 // If we show all file types, also "expose" bundles' contents.
michael@0 249 [aPanel setTreatsFilePackagesAsDirectories:!aFilters];
michael@0 250
michael@0 251 [aPanel setAllowedFileTypes:aFilters];
michael@0 252 }
michael@0 253
michael@0 254 @implementation NSPopUpButtonObserver
michael@0 255 - (void) setPopUpButton:(NSPopUpButton*)aPopUpButton
michael@0 256 {
michael@0 257 mPopUpButton = aPopUpButton;
michael@0 258 }
michael@0 259
michael@0 260 - (void) setOpenPanel:(NSOpenPanel*)aOpenPanel
michael@0 261 {
michael@0 262 mOpenPanel = aOpenPanel;
michael@0 263 }
michael@0 264
michael@0 265 - (void) setFilePicker:(nsFilePicker*)aFilePicker
michael@0 266 {
michael@0 267 mFilePicker = aFilePicker;
michael@0 268 }
michael@0 269
michael@0 270 - (void) menuChangedItem:(NSNotification *)aSender
michael@0 271 {
michael@0 272 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 273 int32_t selectedItem = [mPopUpButton indexOfSelectedItem];
michael@0 274 if (selectedItem < 0) {
michael@0 275 return;
michael@0 276 }
michael@0 277
michael@0 278 mFilePicker->SetFilterIndex(selectedItem);
michael@0 279 UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList());
michael@0 280
michael@0 281 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN();
michael@0 282 }
michael@0 283 @end
michael@0 284
michael@0 285 // Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog.
michael@0 286 int16_t
michael@0 287 nsFilePicker::GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles)
michael@0 288 {
michael@0 289 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 290
michael@0 291 int16_t retVal = (int16_t)returnCancel;
michael@0 292 NSOpenPanel *thePanel = [NSOpenPanel openPanel];
michael@0 293
michael@0 294 SetShowHiddenFileState(thePanel);
michael@0 295
michael@0 296 // Set the options for how the get file dialog will appear
michael@0 297 SetDialogTitle(inTitle, thePanel);
michael@0 298 [thePanel setAllowsMultipleSelection:inAllowMultiple];
michael@0 299 [thePanel setCanSelectHiddenExtension:YES];
michael@0 300 [thePanel setCanChooseDirectories:NO];
michael@0 301 [thePanel setCanChooseFiles:YES];
michael@0 302 [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set
michael@0 303
michael@0 304 // Get filters
michael@0 305 // filters may be null, if we should allow all file types.
michael@0 306 NSArray *filters = GetFilterList();
michael@0 307
michael@0 308 // set up default directory
michael@0 309 NSString *theDir = PanelDefaultDirectory();
michael@0 310
michael@0 311 // if this is the "Choose application..." dialog, and no other start
michael@0 312 // dir has been set, then use the Applications folder.
michael@0 313 if (!theDir) {
michael@0 314 if (filters && [filters count] == 1 &&
michael@0 315 [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"])
michael@0 316 theDir = @"/Applications/";
michael@0 317 else
michael@0 318 theDir = @"";
michael@0 319 }
michael@0 320
michael@0 321 if (theDir) {
michael@0 322 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]];
michael@0 323 }
michael@0 324
michael@0 325 int result;
michael@0 326 nsCocoaUtils::PrepareForNativeAppModalDialog();
michael@0 327 if (mFilters.Length() > 1) {
michael@0 328 // [NSURL initWithString:] (below) throws an exception if URLString is nil.
michael@0 329
michael@0 330 NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init];
michael@0 331
michael@0 332 NSView* accessoryView = GetAccessoryView();
michael@0 333 [thePanel setAccessoryView:accessoryView];
michael@0 334
michael@0 335 [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]];
michael@0 336 [observer setOpenPanel:thePanel];
michael@0 337 [observer setFilePicker:this];
michael@0 338
michael@0 339 [[NSNotificationCenter defaultCenter]
michael@0 340 addObserver:observer
michael@0 341 selector:@selector(menuChangedItem:)
michael@0 342 name:NSMenuWillSendActionNotification object:nil];
michael@0 343
michael@0 344 UpdatePanelFileTypes(thePanel, filters);
michael@0 345 result = [thePanel runModal];
michael@0 346
michael@0 347 [[NSNotificationCenter defaultCenter] removeObserver:observer];
michael@0 348 [observer release];
michael@0 349 } else {
michael@0 350 // If we show all file types, also "expose" bundles' contents.
michael@0 351 if (!filters) {
michael@0 352 [thePanel setTreatsFilePackagesAsDirectories:YES];
michael@0 353 }
michael@0 354 [thePanel setAllowedFileTypes:filters];
michael@0 355 result = [thePanel runModal];
michael@0 356 }
michael@0 357 nsCocoaUtils::CleanUpAfterNativeAppModalDialog();
michael@0 358
michael@0 359 if (result == NSFileHandlingPanelCancelButton)
michael@0 360 return retVal;
michael@0 361
michael@0 362 // Converts data from a NSArray of NSURL to the returned format.
michael@0 363 // We should be careful to not call [thePanel URLs] more than once given that
michael@0 364 // it creates a new array each time.
michael@0 365 // We are using Fast Enumeration, thus the NSURL array is created once then
michael@0 366 // iterated.
michael@0 367 for (NSURL* url in [thePanel URLs]) {
michael@0 368 if (!url) {
michael@0 369 continue;
michael@0 370 }
michael@0 371
michael@0 372 nsCOMPtr<nsIFile> localFile;
michael@0 373 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile));
michael@0 374 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
michael@0 375 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) {
michael@0 376 outFiles.AppendObject(localFile);
michael@0 377 }
michael@0 378 }
michael@0 379
michael@0 380 if (outFiles.Count() > 0)
michael@0 381 retVal = returnOK;
michael@0 382
michael@0 383 return retVal;
michael@0 384
michael@0 385 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
michael@0 386 }
michael@0 387
michael@0 388 // Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog.
michael@0 389 int16_t
michael@0 390 nsFilePicker::GetLocalFolder(const nsString& inTitle, nsIFile** outFile)
michael@0 391 {
michael@0 392 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 393 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer");
michael@0 394
michael@0 395 int16_t retVal = (int16_t)returnCancel;
michael@0 396 NSOpenPanel *thePanel = [NSOpenPanel openPanel];
michael@0 397
michael@0 398 SetShowHiddenFileState(thePanel);
michael@0 399
michael@0 400 // Set the options for how the get file dialog will appear
michael@0 401 SetDialogTitle(inTitle, thePanel);
michael@0 402 [thePanel setAllowsMultipleSelection:NO]; //this is default -probably doesn't need to be set
michael@0 403 [thePanel setCanSelectHiddenExtension:YES];
michael@0 404 [thePanel setCanChooseDirectories:YES];
michael@0 405 [thePanel setCanChooseFiles:NO];
michael@0 406 [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set
michael@0 407 [thePanel setCanCreateDirectories:YES];
michael@0 408
michael@0 409 // packages != folders
michael@0 410 [thePanel setTreatsFilePackagesAsDirectories:NO];
michael@0 411
michael@0 412 // set up default directory
michael@0 413 NSString *theDir = PanelDefaultDirectory();
michael@0 414 if (theDir) {
michael@0 415 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]];
michael@0 416 }
michael@0 417 nsCocoaUtils::PrepareForNativeAppModalDialog();
michael@0 418 int result = [thePanel runModal];
michael@0 419 nsCocoaUtils::CleanUpAfterNativeAppModalDialog();
michael@0 420
michael@0 421 if (result == NSFileHandlingPanelCancelButton)
michael@0 422 return retVal;
michael@0 423
michael@0 424 // get the path for the folder (we allow just 1, so that's all we get)
michael@0 425 NSURL *theURL = [[thePanel URLs] objectAtIndex:0];
michael@0 426 if (theURL) {
michael@0 427 nsCOMPtr<nsIFile> localFile;
michael@0 428 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile));
michael@0 429 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
michael@0 430 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) {
michael@0 431 *outFile = localFile;
michael@0 432 NS_ADDREF(*outFile);
michael@0 433 retVal = returnOK;
michael@0 434 }
michael@0 435 }
michael@0 436
michael@0 437 return retVal;
michael@0 438
michael@0 439 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
michael@0 440 }
michael@0 441
michael@0 442 // Returns |returnOK| if the user presses OK in the dialog.
michael@0 443 int16_t
michael@0 444 nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile)
michael@0 445 {
michael@0 446 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
michael@0 447 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer");
michael@0 448
michael@0 449 int16_t retVal = returnCancel;
michael@0 450 NSSavePanel *thePanel = [NSSavePanel savePanel];
michael@0 451
michael@0 452 SetShowHiddenFileState(thePanel);
michael@0 453
michael@0 454 SetDialogTitle(inTitle, thePanel);
michael@0 455
michael@0 456 // set up accessory view for file format options
michael@0 457 NSView* accessoryView = GetAccessoryView();
michael@0 458 [thePanel setAccessoryView:accessoryView];
michael@0 459
michael@0 460 // set up default file name
michael@0 461 NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()];
michael@0 462
michael@0 463 // set up default directory
michael@0 464 NSString *theDir = PanelDefaultDirectory();
michael@0 465 if (theDir) {
michael@0 466 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]];
michael@0 467 }
michael@0 468
michael@0 469 // load the panel
michael@0 470 nsCocoaUtils::PrepareForNativeAppModalDialog();
michael@0 471 [thePanel setNameFieldStringValue:defaultFilename];
michael@0 472 int result = [thePanel runModal];
michael@0 473 nsCocoaUtils::CleanUpAfterNativeAppModalDialog();
michael@0 474 if (result == NSFileHandlingPanelCancelButton)
michael@0 475 return retVal;
michael@0 476
michael@0 477 // get the save type
michael@0 478 NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag];
michael@0 479 if (popupButton) {
michael@0 480 mSelectedTypeIndex = [popupButton indexOfSelectedItem];
michael@0 481 }
michael@0 482
michael@0 483 NSURL* fileURL = [thePanel URL];
michael@0 484 if (fileURL) {
michael@0 485 nsCOMPtr<nsIFile> localFile;
michael@0 486 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile));
michael@0 487 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile);
michael@0 488 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) {
michael@0 489 *outFile = localFile;
michael@0 490 NS_ADDREF(*outFile);
michael@0 491 // We tell if we are replacing or not by just looking to see if the file exists.
michael@0 492 // The user could not have hit OK and not meant to replace the file.
michael@0 493 if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
michael@0 494 retVal = returnReplace;
michael@0 495 else
michael@0 496 retVal = returnOK;
michael@0 497 }
michael@0 498 }
michael@0 499
michael@0 500 return retVal;
michael@0 501
michael@0 502 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
michael@0 503 }
michael@0 504
michael@0 505 NSArray *
michael@0 506 nsFilePicker::GetFilterList()
michael@0 507 {
michael@0 508 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 509
michael@0 510 if (!mFilters.Length()) {
michael@0 511 return nil;
michael@0 512 }
michael@0 513
michael@0 514 if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) {
michael@0 515 NS_WARNING("An out of range index has been selected. Using the first index instead.");
michael@0 516 mSelectedTypeIndex = 0;
michael@0 517 }
michael@0 518
michael@0 519 const nsString& filterWide = mFilters[mSelectedTypeIndex];
michael@0 520 if (!filterWide.Length()) {
michael@0 521 return nil;
michael@0 522 }
michael@0 523
michael@0 524 if (filterWide.Equals(NS_LITERAL_STRING("*"))) {
michael@0 525 return nil;
michael@0 526 }
michael@0 527
michael@0 528 // The extensions in filterWide are in the format "*.ext" but are expected
michael@0 529 // in the format "ext" by NSOpenPanel. So we need to filter some characters.
michael@0 530 NSMutableString* filterString = [[[NSMutableString alloc] initWithString:
michael@0 531 [NSString stringWithCharacters:reinterpret_cast<const unichar*>(filterWide.get())
michael@0 532 length:filterWide.Length()]] autorelease];
michael@0 533 NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@". *"];
michael@0 534 NSRange range = [filterString rangeOfCharacterFromSet:set];
michael@0 535 while (range.length) {
michael@0 536 [filterString replaceCharactersInRange:range withString:@""];
michael@0 537 range = [filterString rangeOfCharacterFromSet:set];
michael@0 538 }
michael@0 539
michael@0 540 return [[[NSArray alloc] initWithArray:
michael@0 541 [filterString componentsSeparatedByString:@";"]] autorelease];
michael@0 542
michael@0 543 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 544 }
michael@0 545
michael@0 546 // Sets the dialog title to whatever it should be. If it fails, eh,
michael@0 547 // the OS will provide a sensible default.
michael@0 548 void
michael@0 549 nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel)
michael@0 550 {
michael@0 551 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
michael@0 552
michael@0 553 [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]];
michael@0 554
michael@0 555 NS_OBJC_END_TRY_ABORT_BLOCK;
michael@0 556 }
michael@0 557
michael@0 558 // Converts path from an nsIFile into a NSString path
michael@0 559 // If it fails, returns an empty string.
michael@0 560 NSString *
michael@0 561 nsFilePicker::PanelDefaultDirectory()
michael@0 562 {
michael@0 563 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
michael@0 564
michael@0 565 NSString *directory = nil;
michael@0 566 if (mDisplayDirectory) {
michael@0 567 nsAutoString pathStr;
michael@0 568 mDisplayDirectory->GetPath(pathStr);
michael@0 569 directory = [[[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(pathStr.get())
michael@0 570 length:pathStr.Length()] autorelease];
michael@0 571 }
michael@0 572 return directory;
michael@0 573
michael@0 574 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
michael@0 575 }
michael@0 576
michael@0 577 NS_IMETHODIMP nsFilePicker::GetFile(nsIFile **aFile)
michael@0 578 {
michael@0 579 NS_ENSURE_ARG_POINTER(aFile);
michael@0 580 *aFile = nullptr;
michael@0 581
michael@0 582 // just return the first file
michael@0 583 if (mFiles.Count() > 0) {
michael@0 584 *aFile = mFiles.ObjectAt(0);
michael@0 585 NS_IF_ADDREF(*aFile);
michael@0 586 }
michael@0 587
michael@0 588 return NS_OK;
michael@0 589 }
michael@0 590
michael@0 591 NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI **aFileURL)
michael@0 592 {
michael@0 593 NS_ENSURE_ARG_POINTER(aFileURL);
michael@0 594 *aFileURL = nullptr;
michael@0 595
michael@0 596 if (mFiles.Count() == 0)
michael@0 597 return NS_OK;
michael@0 598
michael@0 599 return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0));
michael@0 600 }
michael@0 601
michael@0 602 NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
michael@0 603 {
michael@0 604 return NS_NewArrayEnumerator(aFiles, mFiles);
michael@0 605 }
michael@0 606
michael@0 607 NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString)
michael@0 608 {
michael@0 609 mDefault = aString;
michael@0 610 return NS_OK;
michael@0 611 }
michael@0 612
michael@0 613 NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString)
michael@0 614 {
michael@0 615 return NS_ERROR_FAILURE;
michael@0 616 }
michael@0 617
michael@0 618 // The default extension to use for files
michael@0 619 NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension)
michael@0 620 {
michael@0 621 aExtension.Truncate();
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension)
michael@0 626 {
michael@0 627 return NS_OK;
michael@0 628 }
michael@0 629
michael@0 630 // Append an entry to the filters array
michael@0 631 NS_IMETHODIMP
michael@0 632 nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
michael@0 633 {
michael@0 634 // "..apps" has to be translated with native executable extensions.
michael@0 635 if (aFilter.EqualsLiteral("..apps")) {
michael@0 636 mFilters.AppendElement(NS_LITERAL_STRING("*.app"));
michael@0 637 } else {
michael@0 638 mFilters.AppendElement(aFilter);
michael@0 639 }
michael@0 640 mTitles.AppendElement(aTitle);
michael@0 641
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 // Get the filter index - do we still need this?
michael@0 646 NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t *aFilterIndex)
michael@0 647 {
michael@0 648 *aFilterIndex = mSelectedTypeIndex;
michael@0 649 return NS_OK;
michael@0 650 }
michael@0 651
michael@0 652 // Set the filter index - do we still need this?
michael@0 653 NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex)
michael@0 654 {
michael@0 655 mSelectedTypeIndex = aFilterIndex;
michael@0 656 return NS_OK;
michael@0 657 }

mercurial