]> git.saurik.com Git - wxWidgets.git/blob - src/mac/filedlg.cpp
Under Cygwin, in platform.h default to wxMSW unless otherwise specified.
[wxWidgets.git] / src / mac / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: AUTHOR
5 // Modified by:
6 // Created: ??/??/98
7 // RCS-ID: $Id$
8 // Copyright: (c) AUTHOR
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "filedlg.h"
14 #endif
15
16 #include "wx/defs.h"
17 #include "wx/app.h"
18 #include "wx/utils.h"
19 #include "wx/dialog.h"
20 #include "wx/filedlg.h"
21 #include "wx/intl.h"
22 #include "wx/tokenzr.h"
23
24 #ifndef __DARWIN__
25 #include "PLStringFuncs.h"
26 #endif
27
28 #if !USE_SHARED_LIBRARY
29 IMPLEMENT_CLASS(wxFileDialog, wxDialog)
30 #endif
31
32 // begin wxmac
33
34 #include "wx/mac/private.h"
35
36 #include <Navigation.h>
37
38 #ifdef __DARWIN__
39 # include "MoreFilesX.h"
40 #else
41 # include "MoreFiles.h"
42 # include "MoreFilesExtras.h"
43 #endif
44
45 extern bool gUseNavServices ;
46
47 // the data we need to pass to our standard file hook routine
48 // includes a pointer to the dialog, a pointer to the standard
49 // file reply record (so we can inspect the current selection)
50 // and a copy of the "previous" file spec of the reply record
51 // so we can see if the selection has changed
52
53 struct OpenUserDataRec {
54 int currentfilter ;
55 bool saveMode ;
56 wxArrayString name ;
57 wxArrayString extensions ;
58 wxArrayLong filtermactypes ;
59 NavMenuItemSpecArrayHandle menuitems ;
60 };
61
62 typedef struct OpenUserDataRec
63 OpenUserDataRec, *OpenUserDataRecPtr;
64
65 static pascal void NavEventProc(
66 NavEventCallbackMessage inSelector,
67 NavCBRecPtr ioParams,
68 NavCallBackUserData ioUserData);
69
70 #if TARGET_CARBON
71 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
72 #else
73 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
74 #endif
75
76 static pascal void
77 NavEventProc(
78 NavEventCallbackMessage inSelector,
79 NavCBRecPtr ioParams,
80 NavCallBackUserData ioUserData )
81 {
82 OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ;
83 if (inSelector == kNavCBEvent) {
84 wxTheApp->MacHandleOneEvent(ioParams->eventData.eventDataParms.event);
85 }
86 else if ( inSelector == kNavCBStart )
87 {
88 if ( data->menuitems )
89 NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &(*data->menuitems)[data->currentfilter]);
90 }
91 else if ( inSelector == kNavCBPopupMenuSelect )
92 {
93 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
94 if ( menu->menuCreator == 'WXNG' )
95 {
96 data->currentfilter = menu->menuType ;
97 if ( data->saveMode )
98 {
99 int i = menu->menuType ;
100 wxString extension = data->extensions[i].AfterLast('.') ;
101 extension.MakeLower() ;
102 Str255 filename ;
103 // get the current filename
104 NavCustomControl(ioParams->context, kNavCtlGetEditFileName, &filename);
105 CopyPascalStringToC( filename , (char*) filename ) ;
106 wxString sfilename( filename ) ;
107 int pos = sfilename.Find('.',TRUE) ;
108 if ( pos != wxNOT_FOUND )
109 {
110 sfilename = sfilename.Left(pos+1)+extension ;
111 CopyCStringToPascal( sfilename.c_str() , filename ) ;
112 NavCustomControl(ioParams->context, kNavCtlSetEditFileName, &filename);
113 }
114 }
115 }
116 }
117 }
118
119 const char * gfilters[] =
120 {
121 "*.TXT" ,
122 "*.TIF" ,
123 "*.JPG" ,
124
125 NULL
126 } ;
127
128 OSType gfiltersmac[] =
129 {
130 'TEXT' ,
131 'TIFF' ,
132 'JPEG' ,
133
134 '****'
135 } ;
136
137
138
139 void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
140 {
141 myData->menuitems = NULL ;
142 myData->currentfilter = 0 ;
143 myData->saveMode = FALSE ;
144
145 if ( filter && filter[0] )
146 {
147 wxString filter2(filter) ;
148 int filterIndex = 0;
149 bool isName = true ;
150 wxString current ;
151 for( unsigned int i = 0; i < filter2.Len() ; i++ )
152 {
153 if( filter2.GetChar(i) == wxT('|') )
154 {
155 if( isName ) {
156 myData->name.Add( current ) ;
157 }
158 else {
159 myData->extensions.Add( current.MakeUpper() ) ;
160 ++filterIndex ;
161 }
162 isName = !isName ;
163 current = "" ;
164 }
165 else
166 {
167 current += filter2.GetChar(i) ;
168 }
169 }
170 // we allow for compatibility reason to have a single filter expression (like *.*) without
171 // an explanatory text, in that case the first part is name and extension at the same time
172
173 wxASSERT_MSG( filterIndex == 0 || !isName , "incorrect format of format string" ) ;
174 if ( current.IsEmpty() )
175 myData->extensions.Add( myData->name[filterIndex] ) ;
176 else
177 myData->extensions.Add( current.MakeUpper() ) ;
178 if ( filterIndex == 0 || isName )
179 myData->name.Add( current.MakeUpper() ) ;
180
181 ++filterIndex ;
182
183
184 const size_t extCount = myData->extensions.GetCount();
185 for ( size_t i = 0 ; i < extCount; i++ )
186 {
187 int j ;
188 for ( j = 0 ; gfilters[j] ; j++ )
189 {
190 if ( strcmp( myData->extensions[i] , gfilters[j] ) == 0 )
191 {
192 myData->filtermactypes.Add( gfiltersmac[j] ) ;
193 break ;
194 }
195 }
196 if( gfilters[j] == NULL )
197 {
198 myData->filtermactypes.Add( '****' ) ;
199 }
200 }
201 }
202 }
203
204 static Boolean CheckFile( ConstStr255Param name , OSType type , OpenUserDataRecPtr data)
205 {
206 Str255 filename ;
207
208 #if TARGET_CARBON
209 p2cstrcpy((char *)filename, name) ;
210 #else
211 PLstrcpy( filename , name ) ;
212 p2cstr( filename ) ;
213 #endif
214 wxString file(filename) ;
215 file.MakeUpper() ;
216
217 if ( data->extensions.GetCount() > 0 )
218 {
219 //for ( int i = 0 ; i < data->numfilters ; ++i )
220 int i = data->currentfilter ;
221 if ( data->extensions[i].Right(2) == ".*" )
222 return true ;
223
224 {
225 if ( type == (OSType)data->filtermactypes[i] )
226 return true ;
227
228 wxStringTokenizer tokenizer( data->extensions[i] , ";" ) ;
229 while( tokenizer.HasMoreTokens() )
230 {
231 wxString extension = tokenizer.GetNextToken() ;
232 if ( extension.GetChar(0) == '*' )
233 extension = extension.Mid(1) ;
234
235 if ( file.Len() >= extension.Len() && extension == file.Right(extension.Len() ) )
236 return true ;
237 }
238 }
239 return false ;
240 }
241 return true ;
242 }
243
244 #ifndef __DARWIN__
245 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
246 {
247 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
248 // return true if this item is invisible or a file
249
250 Boolean visibleFlag;
251 Boolean folderFlag;
252
253 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
254 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
255
256 // because the semantics of the filter proc are "true means don't show
257 // it" we need to invert the result that we return
258
259 if ( !visibleFlag )
260 return true ;
261
262 if ( !folderFlag )
263 {
264 return !CheckFile( myCInfoPBPtr->hFileInfo.ioNamePtr , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
265 }
266
267 return false ;
268 }
269 #endif
270
271 // end wxmac
272
273 wxString wxFileSelector(const char *title,
274 const char *defaultDir, const char *defaultFileName,
275 const char *defaultExtension, const char *filter, int flags,
276 wxWindow *parent, int x, int y)
277 {
278 // If there's a default extension specified but no filter, we create a suitable
279 // filter.
280
281 wxString filter2("");
282 if ( defaultExtension && !filter )
283 filter2 = wxString("*.") + wxString(defaultExtension) ;
284 else if ( filter )
285 filter2 = filter;
286
287 wxString defaultDirString;
288 if (defaultDir)
289 defaultDirString = defaultDir;
290 else
291 defaultDirString = "";
292
293 wxString defaultFilenameString;
294 if (defaultFileName)
295 defaultFilenameString = defaultFileName;
296 else
297 defaultFilenameString = "";
298
299 wxFileDialog fileDialog(parent, title, defaultDirString, defaultFilenameString, filter2, flags, wxPoint(x, y));
300
301 if ( fileDialog.ShowModal() == wxID_OK )
302 {
303 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
304 return wxBuffer;
305 }
306 else
307 return wxGetEmptyString();
308 }
309
310 WXDLLEXPORT wxString wxFileSelectorEx(const char *title,
311 const char *defaultDir,
312 const char *defaultFileName,
313 int* defaultFilterIndex,
314 const char *filter,
315 int flags,
316 wxWindow* parent,
317 int x,
318 int y)
319
320 {
321 wxFileDialog fileDialog(parent, title ? title : "", defaultDir ? defaultDir : "",
322 defaultFileName ? defaultFileName : "", filter ? filter : "", flags, wxPoint(x, y));
323
324 if ( fileDialog.ShowModal() == wxID_OK )
325 {
326 *defaultFilterIndex = fileDialog.GetFilterIndex();
327 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
328 return wxBuffer;
329 }
330 else
331 return wxGetEmptyString();
332 }
333
334 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
335 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
336 long style, const wxPoint& pos)
337 {
338 wxASSERT_MSG( NavServicesAvailable() , "Navigation Services are not running" ) ;
339 m_message = message;
340 m_dialogStyle = style;
341 m_parent = parent;
342 m_path = "";
343 m_fileName = defaultFileName;
344 m_dir = defaultDir;
345 m_wildCard = wildCard;
346 m_filterIndex = 0;
347 }
348
349
350 pascal Boolean CrossPlatformFilterCallback (
351 AEDesc *theItem,
352 void *info,
353 void *callBackUD,
354 NavFilterModes filterMode
355 )
356 {
357 bool display = true;
358 OpenUserDataRecPtr data = (OpenUserDataRecPtr) callBackUD ;
359
360 if (filterMode == kNavFilteringBrowserList)
361 {
362 NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*) info ;
363 if (theItem->descriptorType == typeFSS && !theInfo->isFolder)
364 {
365 FSSpec spec;
366 memcpy( &spec , *theItem->dataHandle , sizeof(FSSpec) ) ;
367 display = CheckFile( spec.name , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
368 }
369 }
370
371 return display;
372 }
373
374 int wxFileDialog::ShowModal()
375 {
376 NavDialogOptions mNavOptions;
377 NavObjectFilterUPP mNavFilterUPP = NULL;
378 NavPreviewUPP mNavPreviewUPP = NULL ;
379 NavReplyRecord mNavReply;
380 AEDesc mDefaultLocation ;
381 bool mSelectDefault = false ;
382
383 ::NavGetDefaultDialogOptions(&mNavOptions);
384
385 mNavFilterUPP = nil;
386 mNavPreviewUPP = nil;
387 mSelectDefault = false;
388 mNavReply.validRecord = false;
389 mNavReply.replacing = false;
390 mNavReply.isStationery = false;
391 mNavReply.translationNeeded = false;
392 mNavReply.selection.descriptorType = typeNull;
393 mNavReply.selection.dataHandle = nil;
394 mNavReply.keyScript = smSystemScript;
395 mNavReply.fileTranslation = nil;
396
397 // Set default location, the location
398 // that's displayed when the dialog
399 // first appears
400
401 FSSpec location ;
402 wxMacFilename2FSSpec( m_dir , &location ) ;
403 OSErr err = noErr ;
404
405 mDefaultLocation.descriptorType = typeNull;
406 mDefaultLocation.dataHandle = nil;
407
408 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
409
410 if ( mDefaultLocation.dataHandle ) {
411
412 if (mSelectDefault) {
413 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
414 } else {
415 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
416 }
417 }
418
419 #if TARGET_CARBON
420 c2pstrcpy((StringPtr)mNavOptions.message, m_message) ;
421 #else
422 strcpy((char *)mNavOptions.message, m_message) ;
423 c2pstr((char *)mNavOptions.message ) ;
424 #endif
425 #if TARGET_CARBON
426 c2pstrcpy((StringPtr)mNavOptions.savedFileName, m_fileName) ;
427 #else
428 strcpy((char *)mNavOptions.savedFileName, m_fileName) ;
429 c2pstr((char *)mNavOptions.savedFileName ) ;
430 #endif
431
432 OpenUserDataRec myData;
433 MakeUserDataRec( &myData , m_wildCard ) ;
434 myData.currentfilter = m_filterIndex ;
435 if ( myData.extensions.GetCount() > 0 )
436 {
437 mNavOptions.popupExtension = (NavMenuItemSpecArrayHandle) NewHandle( sizeof( NavMenuItemSpec ) * myData.extensions.GetCount() ) ;
438 myData.menuitems = mNavOptions.popupExtension ;
439 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
440 {
441 (*mNavOptions.popupExtension)[i].version = kNavMenuItemSpecVersion ;
442 (*mNavOptions.popupExtension)[i].menuCreator = 'WXNG' ;
443 (*mNavOptions.popupExtension)[i].menuType = i ;
444 #if TARGET_CARBON
445 c2pstrcpy((StringPtr)(*mNavOptions.popupExtension)[i].menuItemName, myData.name[i]) ;
446 #else
447 strcpy((char *)(*mNavOptions.popupExtension)[i].menuItemName, myData.name[i]) ;
448 c2pstr((char *)(*mNavOptions.popupExtension)[i].menuItemName ) ;
449 #endif
450 }
451 }
452 if ( m_dialogStyle & wxSAVE )
453 {
454 myData.saveMode = true ;
455
456 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
457 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
458
459 err = ::NavPutFile(
460 &mDefaultLocation,
461 &mNavReply,
462 &mNavOptions,
463 sStandardNavEventFilter ,
464 NULL,
465 kNavGenericSignature,
466 &myData); // User Data
467 m_filterIndex = myData.currentfilter ;
468 }
469 else
470 {
471 myData.saveMode = false ;
472
473 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
474 if ( m_dialogStyle & wxMULTIPLE )
475 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
476 else
477 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
478
479 err = ::NavGetFile(
480 &mDefaultLocation,
481 &mNavReply,
482 &mNavOptions,
483 sStandardNavEventFilter ,
484 mNavPreviewUPP,
485 mNavFilterUPP,
486 NULL ,
487 &myData);
488 m_filterIndex = myData.currentfilter ;
489 }
490
491 DisposeNavObjectFilterUPP(mNavFilterUPP);
492 if ( mDefaultLocation.dataHandle != nil )
493 {
494 ::AEDisposeDesc(&mDefaultLocation);
495 }
496
497 if ( (err != noErr) && (err != userCanceledErr) ) {
498 m_path = "" ;
499 return wxID_CANCEL ;
500 }
501
502 if (mNavReply.validRecord) {
503
504 FSSpec outFileSpec ;
505 AEDesc specDesc ;
506 AEKeyword keyWord ;
507
508 long count ;
509 ::AECountItems( &mNavReply.selection , &count ) ;
510 for ( long i = 1 ; i <= count ; ++i )
511 {
512 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, &keyWord , &specDesc);
513 if ( err != noErr ) {
514 m_path = "" ;
515 return wxID_CANCEL ;
516 }
517 outFileSpec = **(FSSpec**) specDesc.dataHandle;
518 if (specDesc.dataHandle != nil) {
519 ::AEDisposeDesc(&specDesc);
520 }
521 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
522 m_paths.Add( m_path ) ;
523 m_fileName = wxFileNameFromPath(m_path);
524 m_fileNames.Add(m_fileName);
525 }
526 // set these to the first hit
527 m_path = m_paths[ 0 ] ;
528 m_fileName = wxFileNameFromPath(m_path);
529 m_dir = wxPathOnly(m_path);
530 NavDisposeReply( &mNavReply ) ;
531 return wxID_OK ;
532 }
533 return wxID_CANCEL;
534 }
535
536 // Generic file load/save dialog
537 static wxString
538 wxDefaultFileSelector(bool load, const char *what, const char *extension, const char *default_name, wxWindow *parent)
539 {
540 char *ext = (char *)extension;
541
542 char prompt[50];
543 wxString str;
544 if (load)
545 str = "Load %s file";
546 else
547 str = "Save %s file";
548 sprintf(prompt, wxGetTranslation(str), what);
549
550 if (*ext == '.') ext++;
551 char wild[60];
552 sprintf(wild, "*.%s", ext);
553
554 return wxFileSelector (prompt, NULL, default_name, ext, wild, 0, parent);
555 }
556
557 // Generic file load dialog
558 wxString
559 wxLoadFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
560 {
561 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
562 }
563
564
565 // Generic file save dialog
566 wxString
567 wxSaveFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
568 {
569 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
570 }
571
572