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