Use sheets for native file dialogs if the dialog has its parent set.
[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: filedlg.mm 40007 2006-07-05 13:10:46Z SC $
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 #endif
31
32 #include "wx/filename.h"
33
34 #include "wx/osx/private.h"
35
36 // ============================================================================
37 // implementation
38 // ============================================================================
39
40 // Open Items:
41 // - support for old style MacOS creator / type combos
42 // - parameter support for descending into packages as directories (setTreatsFilePackagesAsDirectories)
43
44 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
45
46 wxFileDialog::wxFileDialog(
47     wxWindow *parent, const wxString& message,
48     const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
49     long style, const wxPoint& pos, const wxSize& sz, const wxString& name)
50     : wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos, sz, name)
51 {
52 }
53
54 NSArray* GetTypesFromFilter( const wxString filter )
55 {
56     NSMutableArray* types = nil;
57     if ( !filter.empty() )
58     {
59         wxArrayString names ;
60         wxArrayString extensions;
61
62         wxString filter2(filter) ;
63         int filterIndex = 0;
64         bool isName = true ;
65         wxString current ;
66
67         for ( unsigned int i = 0; i < filter2.length() ; i++ )
68         {
69             if ( filter2.GetChar(i) == wxT('|') )
70             {
71                 if ( isName )
72                 {
73                     names.Add( current ) ;
74                 }
75                 else
76                 {
77                     extensions.Add( current ) ;
78                     ++filterIndex ;
79                 }
80
81                 isName = !isName ;
82                 current = wxEmptyString ;
83             }
84             else
85             {
86                 current += filter2.GetChar(i) ;
87             }
88         }
89         // we allow for compatibility reason to have a single filter expression (like *.*) without
90         // an explanatory text, in that case the first part is name and extension at the same time
91
92         wxASSERT_MSG( filterIndex == 0 || !isName , wxT("incorrect format of format string") ) ;
93         if ( current.empty() )
94             extensions.Add( names[filterIndex] ) ;
95         else
96             extensions.Add( current ) ;
97         if ( filterIndex == 0 || isName )
98             names.Add( current ) ;
99
100         ++filterIndex ;
101
102         const size_t extCount = extensions.GetCount();
103         for ( size_t i = 0 ; i < extCount; i++ )
104         {
105             wxString extension = extensions[i];
106
107             // Remove leading '*'
108             if (extension.length() && (extension.GetChar(0) == '*'))
109                 extension = extension.Mid( 1 );
110
111             // Remove leading '.'
112             if (extension.length() && (extension.GetChar(0) == '.'))
113                 extension = extension.Mid( 1 );
114
115             if ( extension.IsEmpty() )
116             {
117                 if ( types != nil )
118                     [types release];
119                 return nil;
120             }
121
122
123             if ( types == nil )
124                 types = [[NSMutableArray alloc] init];
125
126             wxCFStringRef cfext(extension);
127             [types addObject: (NSString*)cfext.AsNSString()  ];
128 #if 0
129             // add support for classic fileType / creator here
130             wxUint32 fileType, creator;
131             // extension -> mactypes
132 #endif
133         }
134     }
135     return types;
136 }
137
138 int wxFileDialog::ShowModal()
139 {
140     int result = wxID_CANCEL;
141
142     NSSavePanel *panel = nil;
143
144     wxCFStringRef cf( m_message );
145
146     wxCFStringRef dir( m_dir );
147     wxCFStringRef file( m_fileName );
148
149     m_path = wxEmptyString;
150     m_fileNames.Clear();
151     
152     wxNonOwnedWindow* parentWindow = NULL;
153     int returnCode = -1;
154     
155     if (GetParent()) 
156     {
157         parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
158     }
159
160     if (HasFlag(wxFD_SAVE))
161     {
162         NSSavePanel* sPanel = [NSSavePanel savePanel];
163         // makes things more convenient:
164         [sPanel setCanCreateDirectories:YES];
165         [sPanel setMessage:cf.AsNSString()];
166         // if we should be able to descend into pacakges we must somehow
167         // be able to pass this in
168         [sPanel setTreatsFilePackagesAsDirectories:NO];
169         [sPanel setCanSelectHiddenExtension:YES];
170
171         if ( HasFlag(wxFD_OVERWRITE_PROMPT) )
172         {
173         }
174     
175         if (parentWindow)
176         {
177             NSWindow* nativeParent = parentWindow->GetWXWindow();
178             ModalDialogDelegate* sheetDelegate = [[ModalDialogDelegate alloc] init]; 
179             [sPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString() 
180                 modalForWindow: nativeParent modalDelegate: sheetDelegate 
181                 didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:) 
182                 contextInfo: nil];
183             [sheetDelegate waitForSheetToFinish];
184             result = [sheetDelegate code];
185             [sheetDelegate release];
186         }
187         else
188         {
189             result = [sPanel runModalForDirectory:dir.AsNSString() file:file.AsNSString() ];
190         }
191         
192         if (result == NSOKButton )
193         {
194             panel = sPanel;
195             result = wxID_OK;
196
197             wxCFStringRef filename( [[sPanel filename] retain] );
198
199             m_path = filename.AsString();
200             m_fileName = wxFileNameFromPath(m_path);
201             m_dir = wxPathOnly( m_path );
202         }
203     }
204     else
205     {
206         NSArray* types = GetTypesFromFilter( m_wildCard ) ;
207         NSOpenPanel* oPanel = [NSOpenPanel openPanel];
208         [oPanel setTreatsFilePackagesAsDirectories:NO];
209         [oPanel setCanChooseDirectories:NO];
210         [oPanel setResolvesAliases:YES];
211         [oPanel setCanChooseFiles:YES];
212         [oPanel setMessage:cf.AsNSString()];
213
214         if (parentWindow)
215         {
216             NSWindow* nativeParent = parentWindow->GetWXWindow();
217             ModalDialogDelegate* sheetDelegate = [[ModalDialogDelegate alloc] init]; 
218             [oPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString() 
219                 types: types modalForWindow: nativeParent 
220                 modalDelegate: sheetDelegate 
221                 didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:) 
222                 contextInfo: nil];
223             [sheetDelegate waitForSheetToFinish];
224             result = [sheetDelegate code];
225             [sheetDelegate release];
226         }
227         else
228         {
229             result = [oPanel runModalForDirectory:dir.AsNSString() 
230                         file:file.AsNSString() types:types];
231         }
232         if (result == NSOKButton )
233         {
234             panel = oPanel;
235             result = wxID_OK;
236             NSArray* filenames = [oPanel filenames];
237             for ( size_t i = 0 ; i < [filenames count] ; ++ i )
238             {
239                 wxCFStringRef filename( [(NSString*) [filenames objectAtIndex:i] retain] );
240                 wxString fnstr = filename.AsString();
241                 m_paths.Add( fnstr );
242                 m_fileNames.Add( wxFileNameFromPath(fnstr) );
243                 if ( i == 0 )
244                 {
245                     m_path = fnstr;
246                     m_fileName = wxFileNameFromPath(fnstr);
247                     m_dir = wxPathOnly( fnstr );
248                 }
249             }
250         }
251         if ( types != nil )
252             [types release];
253     }
254
255     return result;
256 }
257
258 #endif // wxUSE_FILEDLG