fixes potential crash under gatekeeper
[wxWidgets.git] / src / osx / cocoa / filedlg.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/filedlg.mm
3 // Purpose:     wxFileDialog for wxCocoa
4 // Author:      Ryan Norton
5 // Modified by:
6 // Created:     2004-10-02
7 // RCS-ID:      $Id$
8 // Copyright:   (c) Ryan Norton
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #if wxUSE_FILEDLG
24
25 #include "wx/filedlg.h"
26
27 #ifndef WX_PRECOMP
28     #include "wx/msgdlg.h"
29     #include "wx/app.h"
30     #include "wx/sizer.h"
31     #include "wx/stattext.h"
32     #include "wx/choice.h"
33 #endif
34
35 #include "wx/filename.h"
36 #include "wx/tokenzr.h"
37 #include "wx/evtloop.h"
38
39 #include "wx/osx/private.h"
40 #include "wx/sysopt.h"
41 #include "wx/modalhook.h"
42
43 #include <mach-o/dyld.h>
44
45 // ============================================================================
46 // implementation
47 // ============================================================================
48
49 // Open Items:
50 // - parameter support for descending into packages as directories (setTreatsFilePackagesAsDirectories)
51 // - as setAllowedFileTypes is only functional for NSOpenPanel on 10.6+, on earlier systems, the file
52 // type choice will not be shown, but all possible file items will be shown, if a popup must be working
53 // then the delegate method - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename will have to
54 // be implemented
55
56 namespace
57 {
58
59 bool HasAppKit_10_6()
60 {
61     // Even if we require 10.6, we might be loaded by an application that
62     // was linked against 10.5.  setAllowedFileTypes will still be ignored
63     // in this case.  From NSSavePanel.h:
64     // NSOpenPanel: On versions less than 10.6, this property is ignored.
65     // For applications that link against 10.6 and higher, this property will
66     // determine which files should be enabled in the open panel.
67     int32_t version = NSVersionOfLinkTimeLibrary("AppKit");
68     if (version == -1)
69     {
70         // If we're loaded by an application that doesn't link against AppKit,
71         // use the runtime version instead.  This check will not work for the
72         // case above.
73         version = NSVersionOfRunTimeLibrary("AppKit");
74     }
75
76     // Notice that this still works correctly even if version is -1.
77     return version >= 0x40e2400 /* version of 10.6 AppKit */;
78 }
79
80 } // anonymous namespace
81
82 @interface wxOpenPanelDelegate : NSObject wxOSX_10_6_AND_LATER(<NSOpenSavePanelDelegate>)
83 {
84     wxFileDialog* _dialog;
85 }
86
87 - (wxFileDialog*) fileDialog;
88 - (void) setFileDialog:(wxFileDialog*) dialog;
89
90 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
91
92 @end
93
94 @implementation wxOpenPanelDelegate
95
96 - (id) init
97 {
98     self = [super init];
99     _dialog = NULL;
100     return self;
101 }
102
103 - (wxFileDialog*) fileDialog
104 {
105     return _dialog;
106 }
107
108 - (void) setFileDialog:(wxFileDialog*) dialog
109 {
110     _dialog = dialog;
111 }
112
113 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
114 {
115     BOOL showObject = YES;
116     
117     NSString* resolvedLink = [[NSFileManager defaultManager] pathContentOfSymbolicLinkAtPath:filename];
118     if ( resolvedLink != nil )
119         filename = resolvedLink;
120     
121     NSDictionary* fileAttribs = [[NSFileManager defaultManager]
122                                  fileAttributesAtPath:filename traverseLink:YES];
123     if (fileAttribs)
124     {
125         // check for packages
126         if ([NSFileTypeDirectory isEqualTo:[fileAttribs objectForKey:NSFileType]])
127         {
128             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO)
129                 showObject = YES;    // it's a folder, OK to show
130             else
131             {
132                 // it's a packaged directory, apply check
133                 wxCFStringRef filecf([filename retain]);
134                 showObject = _dialog->CheckFile(filecf.AsString());  
135             }
136         }
137         else
138         {
139             // the code above only solves links, not aliases, do this here:
140             
141             NSString* resolvedAlias = nil;
142             
143             CFURLRef url = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, 
144                                                           (CFStringRef)filename, 
145                                                           kCFURLPOSIXPathStyle,
146                                                           NO); 
147             if (url != NULL) 
148             {
149                 FSRef fsRef; 
150                 if (CFURLGetFSRef(url, &fsRef)) 
151                 {
152                     Boolean targetIsFolder, wasAliased;
153                     OSErr err = FSResolveAliasFile (&fsRef, true, &targetIsFolder, &wasAliased);
154                     
155                     if ((err == noErr) && wasAliased) 
156                     {
157                         CFURLRef resolvedUrl = CFURLCreateFromFSRef(kCFAllocatorDefault,  &fsRef);
158                         if (resolvedUrl != NULL) 
159                         {
160                             resolvedAlias = (NSString*) CFURLCopyFileSystemPath(resolvedUrl,
161                                                                                kCFURLPOSIXPathStyle); 
162                             CFRelease(resolvedUrl);
163                         }
164                     } 
165                 }
166                 CFRelease(url);
167             }
168
169             if (resolvedAlias != nil) 
170             {
171                 // recursive call
172                 [resolvedAlias autorelease];
173                 showObject = [self panel:sender shouldShowFilename:resolvedAlias];
174             }
175             else
176             {
177                 wxCFStringRef filecf([filename retain]);
178                 showObject = _dialog->CheckFile(filecf.AsString());  
179             }
180         }
181     }
182
183     return showObject;    
184 }
185
186 @end
187
188 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
189
190 wxFileDialog::wxFileDialog(
191     wxWindow *parent, const wxString& message,
192     const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
193     long style, const wxPoint& pos, const wxSize& sz, const wxString& name)
194     : wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos, sz, name)
195 {
196     m_filterIndex = -1;
197     m_sheetDelegate = [[ModalDialogDelegate alloc] init];
198     [(ModalDialogDelegate*)m_sheetDelegate setImplementation: this];
199     m_delegate = nil;
200 }
201
202 wxFileDialog::~wxFileDialog()
203 {
204     [m_sheetDelegate release];
205 }
206
207 bool wxFileDialog::SupportsExtraControl() const
208 {
209     return true;
210 }
211
212 NSArray* GetTypesFromExtension( const wxString extensiongroup, wxArrayString& extensions )
213 {
214     NSMutableArray* types = nil;
215     extensions.Clear();
216
217     wxStringTokenizer tokenizer( extensiongroup, wxT(";") ) ;
218     while ( tokenizer.HasMoreTokens() )
219     {
220         wxString extension = tokenizer.GetNextToken() ;
221         // Remove leading '*'
222         if ( extension.length() && (extension.GetChar(0) == '*') )
223             extension = extension.Mid( 1 );
224
225         // Remove leading '.'
226         if ( extension.length() && (extension.GetChar(0) == '.') )
227             extension = extension.Mid( 1 );
228
229         // Remove leading '*', this is for handling *.*
230         if ( extension.length() && (extension.GetChar(0) == '*') )
231             extension = extension.Mid( 1 );
232
233         if ( extension.IsEmpty() )
234         {
235             extensions.Clear();
236             [types release];
237             types = nil;
238             return nil;
239         }
240
241         if ( types == nil )
242             types = [[NSMutableArray alloc] init];
243
244         extensions.Add(extension.Lower());
245         wxCFStringRef cfext(extension);
246         [types addObject: (NSString*)cfext.AsNSString()  ];
247 #if 0
248         // add support for classic fileType / creator here
249         wxUint32 fileType, creator;
250         // extension -> mactypes
251 #endif
252     }
253     [types autorelease];
254     return types;
255 }
256
257 NSArray* GetTypesFromFilter( const wxString& filter, wxArrayString& names, wxArrayString& extensiongroups )
258 {
259     NSMutableArray* types = nil;
260     bool allowAll = false;
261
262     names.Clear();
263     extensiongroups.Clear();
264
265     if ( !filter.empty() )
266     {
267         wxStringTokenizer tokenizer( filter, wxT("|") );
268         int numtokens = (int)tokenizer.CountTokens();
269         if(numtokens == 1)
270         {
271             // we allow for compatibility reason to have a single filter expression (like *.*) without
272             // an explanatory text, in that case the first part is name and extension at the same time
273             wxString extension = tokenizer.GetNextToken();
274             names.Add( extension );
275             extensiongroups.Add( extension );
276         }
277         else
278         {
279             int numextensions = numtokens / 2;
280             for(int i = 0; i < numextensions; i++)
281             {
282                 wxString name = tokenizer.GetNextToken();
283                 wxString extension = tokenizer.GetNextToken();
284                 names.Add( name );
285                 extensiongroups.Add( extension );
286             }
287         }
288
289         const size_t extCount = extensiongroups.GetCount();
290         wxArrayString extensions;
291         for ( size_t i = 0 ; i < extCount; i++ )
292         {
293             NSArray* exttypes = GetTypesFromExtension(extensiongroups[i], extensions);
294             if ( exttypes != nil )
295             {
296                 if ( allowAll == false )
297                 {
298                     if ( types == nil )
299                         types = [[NSMutableArray alloc] init];
300
301                     [types addObjectsFromArray:exttypes];
302                 }
303             }
304             else
305             {
306                 allowAll = true;
307                 [types release];
308                 types = nil;
309             }
310         }
311     }
312     [types autorelease];
313     return types;
314 }
315
316 void wxFileDialog::ShowWindowModal()
317 {
318     wxCFStringRef cf( m_message );
319     wxCFStringRef dir( m_dir );
320     wxCFStringRef file( m_fileName );
321
322     wxNonOwnedWindow* parentWindow = NULL;
323     
324     m_modality = wxDIALOG_MODALITY_WINDOW_MODAL;
325
326     if (GetParent())
327         parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
328
329     wxASSERT_MSG(parentWindow, "Window modal display requires parent.");
330
331     NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ;
332     if ( HasFlag(wxFD_SAVE) )
333     {
334         NSSavePanel* sPanel = [NSSavePanel savePanel];
335
336         SetupExtraControls(sPanel);
337
338         // makes things more convenient:
339         [sPanel setCanCreateDirectories:YES];
340         [sPanel setMessage:cf.AsNSString()];
341         // if we should be able to descend into pacakges we must somehow
342         // be able to pass this in
343         [sPanel setTreatsFilePackagesAsDirectories:NO];
344         [sPanel setCanSelectHiddenExtension:YES];
345         [sPanel setAllowedFileTypes:types];
346         [sPanel setAllowsOtherFileTypes:NO];
347         
348         NSWindow* nativeParent = parentWindow->GetWXWindow();
349         [sPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString()
350             modalForWindow: nativeParent modalDelegate: m_sheetDelegate
351             didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
352             contextInfo: nil];
353     }
354     else 
355     {
356         NSOpenPanel* oPanel = [NSOpenPanel openPanel];
357         
358         SetupExtraControls(oPanel);
359
360         [oPanel setTreatsFilePackagesAsDirectories:NO];
361         [oPanel setCanChooseDirectories:NO];
362         [oPanel setResolvesAliases:YES];
363         [oPanel setCanChooseFiles:YES];
364         [oPanel setMessage:cf.AsNSString()];
365         [oPanel setAllowsMultipleSelection: (HasFlag(wxFD_MULTIPLE) ? YES : NO )];
366         
367         NSWindow* nativeParent = parentWindow->GetWXWindow();
368         [oPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString()
369             types: types modalForWindow: nativeParent
370             modalDelegate: m_sheetDelegate
371             didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
372             contextInfo: nil];
373     }
374 }
375
376 // Create a panel with the file type drop down list
377 // If extra controls need to be added (see wxFileDialog::SetExtraControlCreator), add
378 // them to the panel as well
379 // Returns the newly created wxPanel
380
381 wxWindow* wxFileDialog::CreateFilterPanel(wxWindow *extracontrol)
382 {
383     wxPanel *extrapanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
384     wxBoxSizer *verticalSizer = new wxBoxSizer(wxVERTICAL);
385     extrapanel->SetSizer(verticalSizer);
386     
387     // the file type control
388     {
389         wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL);
390         verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0);
391         wxStaticText *stattext = new wxStaticText( extrapanel, wxID_ANY, _("File type:") );
392         horizontalSizer->Add(stattext, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
393         m_filterChoice = new wxChoice(extrapanel, wxID_ANY);
394         horizontalSizer->Add(m_filterChoice, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);
395         m_filterChoice->Append(m_filterNames);
396         if( m_filterNames.GetCount() > 0)
397         {
398             if ( m_firstFileTypeFilter >= 0 )
399                 m_filterChoice->SetSelection(m_firstFileTypeFilter);
400         }
401         m_filterChoice->Connect(wxEVT_CHOICE, wxCommandEventHandler(wxFileDialog::OnFilterSelected), NULL, this);
402     }
403         
404     if(extracontrol)
405     {
406         wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL);
407         verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0);
408
409         extracontrol->Reparent(extrapanel);
410         horizontalSizer->Add(extracontrol);
411     }
412
413     verticalSizer->Layout();
414     verticalSizer->SetSizeHints(extrapanel);
415     return extrapanel;
416 }
417
418 void wxFileDialog::DoOnFilterSelected(int index)
419 {
420     NSArray* types = GetTypesFromExtension(m_filterExtensions[index],m_currentExtensions);
421     NSSavePanel* panel = (NSSavePanel*) GetWXWindow();
422     if ( m_delegate )
423         [panel validateVisibleColumns];
424     else
425         [panel setAllowedFileTypes:types];
426 }
427
428 // An item has been selected in the file filter wxChoice:
429 void wxFileDialog::OnFilterSelected( wxCommandEvent &WXUNUSED(event) )
430 {
431     DoOnFilterSelected( m_filterChoice->GetSelection() );
432 }
433
434 bool wxFileDialog::CheckFile( const wxString& filename )
435 {
436     if ( m_currentExtensions.GetCount() == 0 )
437         return true;
438     
439     wxString ext = filename.AfterLast('.').Lower();
440     
441     for ( size_t i = 0; i < m_currentExtensions.GetCount(); ++i )
442     {
443         if ( ext == m_currentExtensions[i] )
444             return true;
445     }
446     return false;
447 }
448
449 void wxFileDialog::SetupExtraControls(WXWindow nativeWindow)
450 {
451     NSSavePanel* panel = (NSSavePanel*) nativeWindow;
452     // for sandboxed app we cannot access the outer structures
453     // this leads to problems with extra controls, so as a temporary
454     // workaround for crashes we don't support those yet
455     if ( [panel contentView] == nil )
456         return;
457     
458     wxNonOwnedWindow::Create( GetParent(), nativeWindow );
459     wxWindow* extracontrol = NULL;
460     if ( HasExtraControlCreator() )
461     {
462         CreateExtraControl();
463         extracontrol = GetExtraControl();
464     }
465
466     NSView* accView = nil;
467
468     if ( m_useFileTypeFilter )
469     {
470         m_filterPanel = CreateFilterPanel(extracontrol);
471         accView = m_filterPanel->GetHandle();
472         if( HasFlag(wxFD_OPEN) )
473         {
474             if ( UMAGetSystemVersion() < 0x1060 || !HasAppKit_10_6() )
475             {
476                 wxOpenPanelDelegate* del = [[wxOpenPanelDelegate alloc]init];
477                 [del setFileDialog:this];
478                 [panel setDelegate:del];
479                 m_delegate = del;
480             }
481         }
482     }
483     else
484     {
485         m_filterPanel = NULL;
486         m_filterChoice = NULL;
487         if ( extracontrol != nil )
488             accView = extracontrol->GetHandle();
489     }
490
491     if ( accView != nil )
492     {
493         [accView removeFromSuperview];
494         [panel setAccessoryView:accView];
495     }
496     else
497     {
498         [panel setAccessoryView:nil];
499     }
500 }
501
502 int wxFileDialog::ShowModal()
503 {
504     WX_HOOK_MODAL_DIALOG();
505
506     wxCFEventLoopPauseIdleEvents pause;
507
508     wxMacAutoreleasePool autoreleasepool;
509     
510     wxCFStringRef cf( m_message );
511
512     wxCFStringRef dir( m_dir );
513     wxCFStringRef file( m_fileName );
514
515     m_path = wxEmptyString;
516     m_fileNames.Clear();
517     m_paths.Clear();
518
519     wxNonOwnedWindow* parentWindow = NULL;
520     int returnCode = -1;
521
522     if (GetParent())
523     {
524         parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
525     }
526
527
528     NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ;
529
530     m_useFileTypeFilter = m_filterExtensions.GetCount() > 1;
531
532     if( HasFlag(wxFD_OPEN) )
533     {
534         if ( !(wxSystemOptions::HasOption( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) && (wxSystemOptions::GetOptionInt( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) == 1)) )
535             m_useFileTypeFilter = false;            
536     }
537
538     m_firstFileTypeFilter = -1;
539
540     if ( m_useFileTypeFilter
541         && m_filterIndex >= 0 && m_filterIndex < m_filterExtensions.GetCount() )
542     {
543         m_firstFileTypeFilter = m_filterIndex;
544     }
545     else if ( m_useFileTypeFilter )
546     {
547         types = nil;
548         bool useDefault = true;
549         for ( size_t i = 0; i < m_filterExtensions.GetCount(); ++i )
550         {
551             types = GetTypesFromExtension(m_filterExtensions[i], m_currentExtensions);
552             if ( m_currentExtensions.GetCount() == 0 )
553             {
554                 useDefault = false;
555                 m_firstFileTypeFilter = i;
556                 break;
557             }
558             
559             for ( size_t j = 0; j < m_currentExtensions.GetCount(); ++j )
560             {
561                 if ( m_fileName.EndsWith(m_currentExtensions[j]) )
562                 {
563                     m_firstFileTypeFilter = i;
564                     useDefault = false;
565                     break;
566                 }
567             }
568             if ( !useDefault )
569                 break;
570         }
571         if ( useDefault )
572         {
573             types = GetTypesFromExtension(m_filterExtensions[0], m_currentExtensions);
574             m_firstFileTypeFilter = 0;
575         }
576     }
577
578     if ( HasFlag(wxFD_SAVE) )
579     {
580         NSSavePanel* sPanel = [NSSavePanel savePanel];
581
582         SetupExtraControls(sPanel);
583
584         // makes things more convenient:
585         [sPanel setCanCreateDirectories:YES];
586         [sPanel setMessage:cf.AsNSString()];
587         // if we should be able to descend into pacakges we must somehow
588         // be able to pass this in
589         [sPanel setTreatsFilePackagesAsDirectories:NO];
590         [sPanel setCanSelectHiddenExtension:YES];
591         [sPanel setAllowedFileTypes:types];
592         [sPanel setAllowsOtherFileTypes:NO];
593
594         if ( HasFlag(wxFD_OVERWRITE_PROMPT) )
595         {
596         }
597
598         /*
599         Let the file dialog know what file type should be used initially.
600         If this is not done then when setting the filter index
601         programmatically to 1 the file will still have the extension
602         of the first file type instead of the second one. E.g. when file
603         types are foo and bar, a filename "myletter" with SetDialogIndex(1)
604         would result in saving as myletter.foo, while we want myletter.bar.
605         */
606         if(m_firstFileTypeFilter > 0)
607         {
608             DoOnFilterSelected(m_firstFileTypeFilter);
609         }
610
611         returnCode = [sPanel runModalForDirectory: m_dir.IsEmpty() ? nil : dir.AsNSString() file:file.AsNSString() ];
612         ModalFinishedCallback(sPanel, returnCode);
613     }
614     else
615     {
616         NSOpenPanel* oPanel = [NSOpenPanel openPanel];
617         
618         SetupExtraControls(oPanel);
619                 
620         [oPanel setTreatsFilePackagesAsDirectories:NO];
621         [oPanel setCanChooseDirectories:NO];
622         [oPanel setResolvesAliases:YES];
623         [oPanel setCanChooseFiles:YES];
624         [oPanel setMessage:cf.AsNSString()];
625         [oPanel setAllowsMultipleSelection: (HasFlag(wxFD_MULTIPLE) ? YES : NO )];
626
627 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
628         if ( UMAGetSystemVersion() >= 0x1060 && HasAppKit_10_6() )
629         {
630             [oPanel setAllowedFileTypes: (m_delegate == nil ? types : nil)];
631             if ( !m_dir.IsEmpty() )
632                 [oPanel setDirectoryURL:[NSURL fileURLWithPath:dir.AsNSString() 
633                                                    isDirectory:YES]];
634             returnCode = [oPanel runModal];
635         }
636         else 
637 #endif
638         {
639             returnCode = [oPanel runModalForDirectory:m_dir.IsEmpty() ? nil : dir.AsNSString()
640                                                  file:file.AsNSString() types:(m_delegate == nil ? types : nil)];
641         }
642             
643         ModalFinishedCallback(oPanel, returnCode);
644     }
645
646     return GetReturnCode();
647 }
648
649 void wxFileDialog::ModalFinishedCallback(void* panel, int returnCode)
650 {
651     int result = wxID_CANCEL;
652     if (HasFlag(wxFD_SAVE))
653     {
654         if (returnCode == NSOKButton )
655         {
656             NSSavePanel* sPanel = (NSSavePanel*)panel;
657             result = wxID_OK;
658
659             m_path = wxCFStringRef::AsStringWithNormalizationFormC([sPanel filename]);
660             m_fileName = wxFileNameFromPath(m_path);
661             m_dir = wxPathOnly( m_path );
662             if (m_filterChoice)
663             {
664                 m_filterIndex = m_filterChoice->GetSelection();
665             }
666         }
667     }
668     else
669     {
670         NSOpenPanel* oPanel = (NSOpenPanel*)panel;
671         if (returnCode == NSOKButton )
672         {
673             panel = oPanel;
674             result = wxID_OK;
675             NSArray* filenames = [oPanel filenames];
676             for ( size_t i = 0 ; i < [filenames count] ; ++ i )
677             {
678                 wxString fnstr = wxCFStringRef::AsStringWithNormalizationFormC([filenames objectAtIndex:i]);
679                 m_paths.Add( fnstr );
680                 m_fileNames.Add( wxFileNameFromPath(fnstr) );
681                 if ( i == 0 )
682                 {
683                     m_path = fnstr;
684                     m_fileName = wxFileNameFromPath(fnstr);
685                     m_dir = wxPathOnly( fnstr );
686                 }
687             }
688         }
689         if ( m_delegate )
690         {
691             [oPanel setDelegate:nil];
692             [m_delegate release];
693             m_delegate = nil;
694         }
695     }
696     SetReturnCode(result);
697     
698     if (GetModality() == wxDIALOG_MODALITY_WINDOW_MODAL)
699         SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED  );
700     
701     // workaround for sandboxed app, see above
702     if ( m_isNativeWindowWrapper )
703         UnsubclassWin();
704     [(NSSavePanel*) panel setAccessoryView:nil];
705 }
706
707 #endif // wxUSE_FILEDLG