]> git.saurik.com Git - wxWidgets.git/blame - src/mac/filedlg.cpp
more informative error messages
[wxWidgets.git] / src / mac / filedlg.cpp
CommitLineData
e9576ca5
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: filedlg.cpp
2f1ae414 3// Purpose: wxFileDialog
a31a5f85 4// Author: Stefan Csomor
e9576ca5 5// Modified by:
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
a31a5f85 8// Copyright: (c) Stefan Csomor
e9576ca5
SC
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13#pragma implementation "filedlg.h"
14#endif
15
16#include "wx/defs.h"
5fde6fcc 17#include "wx/app.h"
e9576ca5
SC
18#include "wx/utils.h"
19#include "wx/dialog.h"
20#include "wx/filedlg.h"
21#include "wx/intl.h"
fe35d097 22#include "wx/tokenzr.h"
e9576ca5 23
f11bdd03 24#ifndef __DARWIN__
03e11df5
GD
25 #include "PLStringFuncs.h"
26#endif
5b781a67 27
2f1ae414 28#if !USE_SHARED_LIBRARY
e9576ca5 29IMPLEMENT_CLASS(wxFileDialog, wxDialog)
2f1ae414 30#endif
e9576ca5 31
519cb848
SC
32// begin wxmac
33
76a5e5d2
SC
34#include "wx/mac/private.h"
35
bb378910 36#include <Navigation.h>
5b781a67 37
2d4e4f80
GD
38#ifdef __DARWIN__
39# include "MoreFilesX.h"
40#else
41# include "MoreFiles.h"
42# include "MoreFilesExtras.h"
43#endif
44
5b781a67
SC
45extern bool gUseNavServices ;
46
4d4d8bbf
SC
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
4d4d8bbf
SC
53struct OpenUserDataRec {
54 int currentfilter ;
e40298d5 55 bool saveMode ;
2b5f62a0
VZ
56 wxArrayString name ;
57 wxArrayString extensions ;
e40298d5 58 wxArrayLong filtermactypes ;
2b5f62a0 59 NavMenuItemSpecArrayHandle menuitems ;
4d4d8bbf 60};
2b5f62a0 61
4d4d8bbf 62typedef struct OpenUserDataRec
e40298d5 63 OpenUserDataRec, *OpenUserDataRecPtr;
4d4d8bbf 64
e40298d5
JS
65static pascal void NavEventProc(
66 NavEventCallbackMessage inSelector,
67 NavCBRecPtr ioParams,
68 NavCallBackUserData ioUserData);
5b781a67
SC
69
70#if TARGET_CARBON
e40298d5 71 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
5b781a67 72#else
e40298d5 73 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
5b781a67
SC
74#endif
75
76static pascal void
77NavEventProc(
e40298d5
JS
78 NavEventCallbackMessage inSelector,
79 NavCBRecPtr ioParams,
80 NavCallBackUserData ioUserData )
5b781a67 81{
e40298d5
JS
82 OpenUserDataRec * data = ( OpenUserDataRec *) ioUserData ;
83 if (inSelector == kNavCBEvent) {
ac9b5f98 84#if !TARGET_CARBON
5fde6fcc 85 wxTheApp->MacHandleOneEvent(ioParams->eventData.eventDataParms.event);
ac9b5f98 86#endif
e40298d5
JS
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 }
5b781a67
SC
119}
120
72055702 121const char * gfilters[] =
519cb848 122{
e40298d5
JS
123 "*.TXT" ,
124 "*.TIF" ,
125 "*.JPG" ,
126
127 NULL
519cb848
SC
128} ;
129
130OSType gfiltersmac[] =
131{
e40298d5
JS
132 'TEXT' ,
133 'TIFF' ,
134 'JPEG' ,
135
136 '****'
519cb848
SC
137} ;
138
2f1ae414 139
519cb848 140
e40298d5 141void MakeUserDataRec(OpenUserDataRec *myData , const wxString& filter )
4d4d8bbf 142{
e40298d5
JS
143 myData->menuitems = NULL ;
144 myData->currentfilter = 0 ;
145 myData->saveMode = FALSE ;
146
147 if ( filter && filter[0] )
4d4d8bbf 148 {
e40298d5
JS
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 }
4d4d8bbf 171 }
e40298d5
JS
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
2b5f62a0 185
2b5f62a0 186 const size_t extCount = myData->extensions.GetCount();
e40298d5
JS
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 }
4d4d8bbf 204}
bb378910 205
4d4d8bbf
SC
206static Boolean CheckFile( ConstStr255Param name , OSType type , OpenUserDataRecPtr data)
207{
e40298d5 208 Str255 filename ;
da2b4b7a
GD
209
210#if TARGET_CARBON
211 p2cstrcpy((char *)filename, name) ;
212#else
213 PLstrcpy( filename , name ) ;
9f92f6fb 214 p2cstr( filename ) ;
da2b4b7a 215#endif
9f92f6fb
SC
216 wxString file(filename) ;
217 file.MakeUpper() ;
218
2b5f62a0 219 if ( data->extensions.GetCount() > 0 )
da2b4b7a 220 {
e40298d5
JS
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 ;
da2b4b7a
GD
242 }
243 return true ;
4d4d8bbf
SC
244}
245
bb378910 246#ifndef __DARWIN__
5fde6fcc 247static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
e40298d5
JS
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 ;
519cb848 270}
bb378910 271#endif
519cb848
SC
272
273// end wxmac
274
275wxString wxFileSelector(const char *title,
e9576ca5
SC
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 {
ac9b5f98 305 return fileDialog.GetPath();
e9576ca5
SC
306 }
307 else
8be97d65 308 return wxGetEmptyString();
e9576ca5
SC
309}
310
169935ad 311WXDLLEXPORT wxString wxFileSelectorEx(const char *title,
e9576ca5
SC
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();
ac9b5f98 328 return fileDialog.GetPath();
e9576ca5
SC
329 }
330 else
8be97d65 331 return wxGetEmptyString();
e9576ca5
SC
332}
333
334wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
335 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
336 long style, const wxPoint& pos)
337{
e40298d5 338 wxASSERT_MSG( NavServicesAvailable() , "Navigation Services are not running" ) ;
e9576ca5
SC
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;
2b5f62a0 346 m_filterIndex = 0;
e9576ca5
SC
347}
348
4d4d8bbf 349
f11bdd03 350pascal Boolean CrossPlatformFilterCallback (
4d4d8bbf
SC
351 AEDesc *theItem,
352 void *info,
353 void *callBackUD,
354 NavFilterModes filterMode
355)
356{
2d4e4f80
GD
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 {
e40298d5 365 FSSpec spec;
2d4e4f80
GD
366 memcpy( &spec , *theItem->dataHandle , sizeof(FSSpec) ) ;
367 display = CheckFile( spec.name , theInfo->fileAndFolder.fileInfo.finderInfo.fdType , data ) ;
368 }
369 }
370
371 return display;
4d4d8bbf
SC
372}
373
e9576ca5
SC
374int wxFileDialog::ShowModal()
375{
e40298d5
JS
376 NavDialogOptions mNavOptions;
377 NavObjectFilterUPP mNavFilterUPP = NULL;
378 NavPreviewUPP mNavPreviewUPP = NULL ;
379 NavReplyRecord mNavReply;
380 AEDesc mDefaultLocation ;
381 bool mSelectDefault = false ;
382
383 // zero all data
384
385 m_path = wxEmptyString ;
386 m_fileName = wxEmptyString ;
387 m_paths.Empty();
388 m_fileNames.Empty();
389
390 // setup dialog
391
2d4e4f80 392 ::NavGetDefaultDialogOptions(&mNavOptions);
e40298d5
JS
393
394 mNavFilterUPP = nil;
395 mNavPreviewUPP = nil;
396 mSelectDefault = false;
2d4e4f80 397 mNavReply.validRecord = false;
e40298d5 398 mNavReply.replacing = false;
2d4e4f80 399 mNavReply.isStationery = false;
e40298d5 400 mNavReply.translationNeeded = false;
2d4e4f80
GD
401 mNavReply.selection.descriptorType = typeNull;
402 mNavReply.selection.dataHandle = nil;
e40298d5 403 mNavReply.keyScript = smSystemScript;
2d4e4f80 404 mNavReply.fileTranslation = nil;
e40298d5 405
2d4e4f80
GD
406 // Set default location, the location
407 // that's displayed when the dialog
408 // first appears
e40298d5 409
2d4e4f80
GD
410 FSSpec location ;
411 wxMacFilename2FSSpec( m_dir , &location ) ;
412 OSErr err = noErr ;
e40298d5 413
2d4e4f80
GD
414 mDefaultLocation.descriptorType = typeNull;
415 mDefaultLocation.dataHandle = nil;
5b781a67 416
2d4e4f80 417 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
5b781a67 418
2d4e4f80 419 if ( mDefaultLocation.dataHandle ) {
e40298d5 420
2d4e4f80
GD
421 if (mSelectDefault) {
422 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
423 } else {
424 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
425 }
426 }
e40298d5 427
03e11df5 428#if TARGET_CARBON
2d4e4f80 429 c2pstrcpy((StringPtr)mNavOptions.message, m_message) ;
03e11df5 430#else
2d4e4f80
GD
431 strcpy((char *)mNavOptions.message, m_message) ;
432 c2pstr((char *)mNavOptions.message ) ;
03e11df5
GD
433#endif
434#if TARGET_CARBON
2d4e4f80 435 c2pstrcpy((StringPtr)mNavOptions.savedFileName, m_fileName) ;
03e11df5 436#else
2d4e4f80
GD
437 strcpy((char *)mNavOptions.savedFileName, m_fileName) ;
438 c2pstr((char *)mNavOptions.savedFileName ) ;
03e11df5 439#endif
5b781a67 440
e40298d5 441 OpenUserDataRec myData;
2d4e4f80
GD
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 ;
2b5f62a0 464
2d4e4f80
GD
465 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
466 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
e40298d5 467
2d4e4f80
GD
468 err = ::NavPutFile(
469 &mDefaultLocation,
470 &mNavReply,
471 &mNavOptions,
472 sStandardNavEventFilter ,
473 NULL,
474 kNavGenericSignature,
e40298d5 475 &myData); // User Data
2d4e4f80
GD
476 m_filterIndex = myData.currentfilter ;
477 }
478 else
479 {
480 myData.saveMode = false ;
4d4d8bbf 481
2d4e4f80
GD
482 mNavFilterUPP = NewNavObjectFilterUPP( CrossPlatformFilterCallback ) ;
483 if ( m_dialogStyle & wxMULTIPLE )
484 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
485 else
486 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
e40298d5 487
2d4e4f80
GD
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 }
e40298d5 499
2d4e4f80
GD
500 DisposeNavObjectFilterUPP(mNavFilterUPP);
501 if ( mDefaultLocation.dataHandle != nil )
502 {
503 ::AEDisposeDesc(&mDefaultLocation);
504 }
e40298d5 505
2d4e4f80 506 if ( (err != noErr) && (err != userCanceledErr) ) {
2d4e4f80
GD
507 return wxID_CANCEL ;
508 }
5b781a67 509
2d4e4f80 510 if (mNavReply.validRecord) {
e40298d5 511
2d4e4f80
GD
512 FSSpec outFileSpec ;
513 AEDesc specDesc ;
514 AEKeyword keyWord ;
e40298d5 515
2d4e4f80
GD
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 ;
e40298d5 524 }
2d4e4f80
GD
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 ) ;
24fe8dc7 531 m_fileName = wxFileNameFromPath(m_path);
2d4e4f80
GD
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;
5b781a67 542}
e9576ca5
SC
543
544// Generic file load/save dialog
169935ad 545static wxString
e9576ca5
SC
546wxDefaultFileSelector(bool load, const char *what, const char *extension, const char *default_name, wxWindow *parent)
547{
2d4e4f80
GD
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);
e9576ca5
SC
563}
564
565// Generic file load dialog
169935ad 566wxString
e9576ca5
SC
567wxLoadFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
568{
e40298d5 569 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
e9576ca5
SC
570}
571
572
573// Generic file save dialog
169935ad 574wxString
e9576ca5
SC
575wxSaveFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
576{
e40298d5 577 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
e9576ca5
SC
578}
579
580