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