]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/filedlg.cpp
wxMac Unicode support
[wxWidgets.git] / src / mac / carbon / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Stefan Csomor
5 // Modified by:
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Stefan Csomor
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 #if !TARGET_CARBON
85 wxTheApp->MacHandleOneEvent(ioParams->eventData.eventDataParms.event);
86 #endif
87 }
88 else if ( inSelector == kNavCBStart )
89 {
90 if ( data->menuitems )
91 NavCustomControl(ioParams->context, kNavCtlSelectCustomType, &(*data->menuitems)[data->currentfilter]);
92 }
93 else if ( inSelector == kNavCBPopupMenuSelect )
94 {
95 NavMenuItemSpec * menu = (NavMenuItemSpec *) ioParams->eventData.eventDataParms.param ;
96 if ( menu->menuCreator == 'WXNG' )
97 {
98 data->currentfilter = menu->menuType ;
99 if ( data->saveMode )
100 {
101 int i = menu->menuType ;
102 wxString extension = data->extensions[i].AfterLast('.') ;
103 extension.MakeLower() ;
104 Str255 filename ;
105 // get the current filename
106 NavCustomControl(ioParams->context, kNavCtlGetEditFileName, &filename);
107 CopyPascalStringToC( filename , (char*) filename ) ;
108 wxString sfilename( filename ) ;
109 int pos = sfilename.Find('.',TRUE) ;
110 if ( pos != wxNOT_FOUND )
111 {
112 sfilename = sfilename.Left(pos+1)+extension ;
113 CopyCStringToPascal( sfilename.c_str() , filename ) ;
114 NavCustomControl(ioParams->context, kNavCtlSetEditFileName, &filename);
115 }
116 }
117 }
118 }
119 }
120
121 const char * gfilters[] =
122 {
123 "*.TXT" ,
124 "*.TIF" ,
125 "*.JPG" ,
126
127 NULL
128 } ;
129
130 OSType gfiltersmac[] =
131 {
132 'TEXT' ,
133 'TIFF' ,
134 'JPEG' ,
135
136 '****'
137 } ;
138
139
140
141 void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
142 {
143 myData->menuitems = NULL ;
144 myData->currentfilter = 0 ;
145 myData->saveMode = FALSE ;
146
147 if ( filter && filter[0] )
148 {
149 wxString filter2(filter) ;
150 int filterIndex = 0;
151 bool isName = true ;
152 wxString current ;
153 for( unsigned int i = 0; i < filter2.Len() ; i++ )
154 {
155 if( filter2.GetChar(i) == wxT('|') )
156 {
157 if( isName ) {
158 myData->name.Add( current ) ;
159 }
160 else {
161 myData->extensions.Add( current.MakeUpper() ) ;
162 ++filterIndex ;
163 }
164 isName = !isName ;
165 current = "" ;
166 }
167 else
168 {
169 current += filter2.GetChar(i) ;
170 }
171 }
172 // we allow for compatibility reason to have a single filter expression (like *.*) without
173 // an explanatory text, in that case the first part is name and extension at the same time
174
175 wxASSERT_MSG( filterIndex == 0 || !isName , "incorrect format of format string" ) ;
176 if ( current.IsEmpty() )
177 myData->extensions.Add( myData->name[filterIndex] ) ;
178 else
179 myData->extensions.Add( current.MakeUpper() ) ;
180 if ( filterIndex == 0 || isName )
181 myData->name.Add( current.MakeUpper() ) ;
182
183 ++filterIndex ;
184
185
186 const size_t extCount = myData->extensions.GetCount();
187 for ( size_t i = 0 ; i < extCount; i++ )
188 {
189 int j ;
190 for ( j = 0 ; gfilters[j] ; j++ )
191 {
192 if ( strcmp( myData->extensions[i] , gfilters[j] ) == 0 )
193 {
194 myData->filtermactypes.Add( gfiltersmac[j] ) ;
195 break ;
196 }
197 }
198 if( gfilters[j] == NULL )
199 {
200 myData->filtermactypes.Add( '****' ) ;
201 }
202 }
203 }
204 }
205
206 static Boolean CheckFile( ConstStr255Param name , OSType type , OpenUserDataRecPtr data)
207 {
208 Str255 filename ;
209
210 #if TARGET_CARBON
211 p2cstrcpy((char *)filename, name) ;
212 #else
213 PLstrcpy( filename , name ) ;
214 p2cstr( filename ) ;
215 #endif
216 wxString file(filename) ;
217 file.MakeUpper() ;
218
219 if ( data->extensions.GetCount() > 0 )
220 {
221 //for ( int i = 0 ; i < data->numfilters ; ++i )
222 int i = data->currentfilter ;
223 if ( data->extensions[i].Right(2) == ".*" )
224 return true ;
225
226 {
227 if ( type == (OSType)data->filtermactypes[i] )
228 return true ;
229
230 wxStringTokenizer tokenizer( data->extensions[i] , ";" ) ;
231 while( tokenizer.HasMoreTokens() )
232 {
233 wxString extension = tokenizer.GetNextToken() ;
234 if ( extension.GetChar(0) == '*' )
235 extension = extension.Mid(1) ;
236
237 if ( file.Len() >= extension.Len() && extension == file.Right(extension.Len() ) )
238 return true ;
239 }
240 }
241 return false ;
242 }
243 return true ;
244 }
245
246 #ifndef __DARWIN__
247 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
248 {
249 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
250 // return true if this item is invisible or a file
251
252 Boolean visibleFlag;
253 Boolean folderFlag;
254
255 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
256 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
257
258 // because the semantics of the filter proc are "true means don't show
259 // it" we need to invert the result that we return
260
261 if ( !visibleFlag )
262 return true ;
263
264 if ( !folderFlag )
265 {
266 return !CheckFile( myCInfoPBPtr->hFileInfo.ioNamePtr , myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType , data ) ;
267 }
268
269 return false ;
270 }
271 #endif
272
273 // end wxmac
274
275 wxString wxFileSelector(const char *title,
276 const char *defaultDir, const char *defaultFileName,
277 const char *defaultExtension, const char *filter, int flags,
278 wxWindow *parent, int x, int y)
279 {
280 // If there's a default extension specified but no filter, we create a suitable
281 // filter.
282
283 wxString filter2("");
284 if ( defaultExtension && !filter )
285 filter2 = wxString("*.") + wxString(defaultExtension) ;
286 else if ( filter )
287 filter2 = filter;
288
289 wxString defaultDirString;
290 if (defaultDir)
291 defaultDirString = defaultDir;
292 else
293 defaultDirString = "";
294
295 wxString defaultFilenameString;
296 if (defaultFileName)
297 defaultFilenameString = defaultFileName;
298 else
299 defaultFilenameString = "";
300
301 wxFileDialog fileDialog(parent, title, defaultDirString, defaultFilenameString, filter2, flags, wxPoint(x, y));
302
303 if ( fileDialog.ShowModal() == wxID_OK )
304 {
305 return fileDialog.GetPath();
306 }
307 else
308 return wxGetEmptyString();
309 }
310
311 WXDLLEXPORT wxString wxFileSelectorEx(const char *title,
312 const char *defaultDir,
313 const char *defaultFileName,
314 int* defaultFilterIndex,
315 const char *filter,
316 int flags,
317 wxWindow* parent,
318 int x,
319 int y)
320
321 {
322 wxFileDialog fileDialog(parent, title ? title : "", defaultDir ? defaultDir : "",
323 defaultFileName ? defaultFileName : "", filter ? filter : "", flags, wxPoint(x, y));
324
325 if ( fileDialog.ShowModal() == wxID_OK )
326 {
327 *defaultFilterIndex = fileDialog.GetFilterIndex();
328 return fileDialog.GetPath();
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 // setup dialog
384
385 ::NavGetDefaultDialogOptions(&mNavOptions);
386
387 mNavFilterUPP = nil;
388 mNavPreviewUPP = nil;
389 mSelectDefault = false;
390 mNavReply.validRecord = false;
391 mNavReply.replacing = false;
392 mNavReply.isStationery = false;
393 mNavReply.translationNeeded = false;
394 mNavReply.selection.descriptorType = typeNull;
395 mNavReply.selection.dataHandle = nil;
396 mNavReply.keyScript = smSystemScript;
397 mNavReply.fileTranslation = nil;
398
399 // Set default location, the location
400 // that's displayed when the dialog
401 // first appears
402
403 FSSpec location ;
404 wxMacFilename2FSSpec( m_dir , &location ) ;
405 OSErr err = noErr ;
406
407 mDefaultLocation.descriptorType = typeNull;
408 mDefaultLocation.dataHandle = nil;
409
410 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
411
412 if ( mDefaultLocation.dataHandle ) {
413
414 if (mSelectDefault) {
415 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
416 } else {
417 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
418 }
419 }
420
421 #if TARGET_CARBON
422 c2pstrcpy((StringPtr)mNavOptions.message, m_message) ;
423 #else
424 strcpy((char *)mNavOptions.message, m_message) ;
425 c2pstr((char *)mNavOptions.message ) ;
426 #endif
427 #if TARGET_CARBON
428 c2pstrcpy((StringPtr)mNavOptions.savedFileName, m_fileName) ;
429 #else
430 strcpy((char *)mNavOptions.savedFileName, m_fileName) ;
431 c2pstr((char *)mNavOptions.savedFileName ) ;
432 #endif
433
434 // zero all data
435
436 m_path = wxEmptyString ;
437 m_fileName = wxEmptyString ;
438 m_paths.Empty();
439 m_fileNames.Empty();
440
441 OpenUserDataRec myData;
442 MakeUserDataRec( &myData , m_wildCard ) ;
443 myData.currentfilter = m_filterIndex ;
444 if ( myData.extensions.GetCount() > 0 )
445 {
446 mNavOptions.popupExtension = (NavMenuItemSpecArrayHandle) NewHandle( sizeof( NavMenuItemSpec ) * myData.extensions.GetCount() ) ;
447 myData.menuitems = mNavOptions.popupExtension ;
448 for ( size_t i = 0 ; i < myData.extensions.GetCount() ; ++i )
449 {
450 (*mNavOptions.popupExtension)[i].version = kNavMenuItemSpecVersion ;
451 (*mNavOptions.popupExtension)[i].menuCreator = 'WXNG' ;
452 (*mNavOptions.popupExtension)[i].menuType = i ;
453 #if TARGET_CARBON
454 c2pstrcpy((StringPtr)(*mNavOptions.popupExtension)[i].menuItemName, myData.name[i]) ;
455 #else
456 strcpy((char *)(*mNavOptions.popupExtension)[i].menuItemName, myData.name[i]) ;
457 c2pstr((char *)(*mNavOptions.popupExtension)[i].menuItemName ) ;
458 #endif
459 }
460 }
461 if ( m_dialogStyle & wxSAVE )
462 {
463 myData.saveMode = true ;
464
465 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
466 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
467
468 err = ::NavPutFile(
469 &mDefaultLocation,
470 &mNavReply,
471 &mNavOptions,
472 sStandardNavEventFilter ,
473 NULL,
474 kNavGenericSignature,
475 &myData); // User Data
476 m_filterIndex = myData.currentfilter ;
477 }
478 else
479 {
480 myData.saveMode = false ;
481
482 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
483 if ( m_dialogStyle & wxMULTIPLE )
484 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
485 else
486 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
487
488 err = ::NavGetFile(
489 &mDefaultLocation,
490 &mNavReply,
491 &mNavOptions,
492 sStandardNavEventFilter ,
493 mNavPreviewUPP,
494 mNavFilterUPP,
495 NULL ,
496 &myData);
497 m_filterIndex = myData.currentfilter ;
498 }
499
500 DisposeNavObjectFilterUPP(mNavFilterUPP);
501 if ( mDefaultLocation.dataHandle != nil )
502 {
503 ::AEDisposeDesc(&mDefaultLocation);
504 }
505
506 if ( (err != noErr) && (err != userCanceledErr) ) {
507 return wxID_CANCEL ;
508 }
509
510 if (mNavReply.validRecord) {
511
512 FSSpec outFileSpec ;
513 AEDesc specDesc ;
514 AEKeyword keyWord ;
515
516 long count ;
517 ::AECountItems( &mNavReply.selection , &count ) ;
518 for ( long i = 1 ; i <= count ; ++i )
519 {
520 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, &keyWord , &specDesc);
521 if ( err != noErr ) {
522 m_path = "" ;
523 return wxID_CANCEL ;
524 }
525 outFileSpec = **(FSSpec**) specDesc.dataHandle;
526 if (specDesc.dataHandle != nil) {
527 ::AEDisposeDesc(&specDesc);
528 }
529 m_path = wxMacFSSpec2MacFilename( &outFileSpec ) ;
530 m_paths.Add( m_path ) ;
531 m_fileName = wxFileNameFromPath(m_path);
532 m_fileNames.Add(m_fileName);
533 }
534 // set these to the first hit
535 m_path = m_paths[ 0 ] ;
536 m_fileName = wxFileNameFromPath(m_path);
537 m_dir = wxPathOnly(m_path);
538 NavDisposeReply( &mNavReply ) ;
539 return wxID_OK ;
540 }
541 return wxID_CANCEL;
542 }
543
544 // Generic file load/save dialog
545 static wxString
546 wxDefaultFileSelector(bool load, const char *what, const char *extension, const char *default_name, wxWindow *parent)
547 {
548 char *ext = (char *)extension;
549
550 char prompt[50];
551 wxString str;
552 if (load)
553 str = "Load %s file";
554 else
555 str = "Save %s file";
556 sprintf(prompt, wxGetTranslation(str), what);
557
558 if (*ext == '.') ext++;
559 char wild[60];
560 sprintf(wild, "*.%s", ext);
561
562 return wxFileSelector (prompt, NULL, default_name, ext, wild, 0, parent);
563 }
564
565 // Generic file load dialog
566 wxString
567 wxLoadFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
568 {
569 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
570 }
571
572
573 // Generic file save dialog
574 wxString
575 wxSaveFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
576 {
577 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
578 }
579
580