]> git.saurik.com Git - wxWidgets.git/blob - src/mac/filedlg.cpp
using wxURL now enforces linking http.o, ftp.o and sckfile.o
[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/utils.h"
18 #include "wx/dialog.h"
19 #include "wx/filedlg.h"
20 #include "wx/intl.h"
21
22 #if !defined(__UNIX__)
23 #include "PLStringFuncs.h"
24 #endif
25
26 #if !USE_SHARED_LIBRARY
27 IMPLEMENT_CLASS(wxFileDialog, wxDialog)
28 #endif
29
30 // begin wxmac
31
32 #if defined(__UNIX__)
33 #include <NavigationServices/Navigation.h>
34 #else
35 #include <Navigation.h>
36 #endif
37
38 #ifndef __UNIX__
39 #include "morefile.h"
40 #include "moreextr.h"
41 #include "fullpath.h"
42 #include "fspcompa.h"
43 #include "PLStringFuncs.h"
44 #endif
45
46 extern bool gUseNavServices ;
47
48 static pascal void NavEventProc(
49 NavEventCallbackMessage inSelector,
50 NavCBRecPtr ioParams,
51 NavCallBackUserData ioUserData);
52
53 #if TARGET_CARBON
54 static NavEventUPP sStandardNavEventFilter = NewNavEventUPP(NavEventProc);
55 #else
56 static NavEventUPP sStandardNavEventFilter = NewNavEventProc(NavEventProc);
57 #endif
58
59 static pascal void
60 NavEventProc(
61 NavEventCallbackMessage inSelector,
62 NavCBRecPtr ioParams,
63 NavCallBackUserData /* ioUserData */)
64 {
65 if (inSelector == kNavCBEvent) {
66 // In Universal Headers 3.2, Apple changed the definition of
67 /*
68 #if UNIVERSAL_INTERFACES_VERSION >= 0x0320 // Universal Headers 3.2
69 UModalAlerts::ProcessModalEvent(*(ioParams->eventData.eventDataParms.event));
70
71 #else
72 UModalAlerts::ProcessModalEvent(*(ioParams->eventData.event));
73 #endif
74 */
75
76 }
77 }
78
79 char * gfilters[] =
80 {
81 "*.TXT" ,
82
83 NULL
84 } ;
85
86 OSType gfiltersmac[] =
87 {
88 'TEXT' ,
89
90 '****'
91 } ;
92
93 // the data we need to pass to our standard file hook routine
94 // includes a pointer to the dialog, a pointer to the standard
95 // file reply record (so we can inspect the current selection)
96 // and a copy of the "previous" file spec of the reply record
97 // so we can see if the selection has changed
98
99 const int kwxMacFileTypes = 10 ;
100
101 struct OpenUserDataRec {
102 StandardFileReply *sfrPtr;
103 FSSpec oldSelectionFSSpec;
104 char filter[kwxMacFileTypes][10] ;
105 OSType filtermactypes[kwxMacFileTypes] ;
106 int numfilters ;
107 DialogPtr theDlgPtr;
108 };
109 typedef struct OpenUserDataRec
110 OpenUserDataRec, *OpenUserDataRecPtr;
111
112 #if !TARGET_CARBON
113
114 static void wxMacSetupStandardFile(short newVRefNum, long newDirID)
115 {
116 enum
117 { SFSaveDisk = 0x214, CurDirStore = 0x398 };
118 *(short *) SFSaveDisk = -1 * newVRefNum;
119 *(long *) CurDirStore = newDirID;
120 }
121
122 static void wxMacSetupStandardFileFromPath( const char* s )
123 {
124 Str255 volume ;
125 Str255 path ;
126 short vRefNum ;
127 long dirRef ;
128 short i,j ;
129 Boolean isDirectory ;
130
131 for (i=0 ; (s[i]!=0) && (s[i]!=':') ;i++)
132 {
133 volume[i]=s[i] ;
134 }
135 volume[i]=':' ;
136 volume[i+1]=0 ;
137
138 // then copy the rest of the filename
139
140 for (j=0;(s[i]!=0);i++,j++)
141 {
142 path[j]=s[i] ;
143 }
144 path[j]=0 ;
145
146 c2pstr((Ptr) volume) ;
147 c2pstr((Ptr) path) ;
148
149 SetVol(volume, 0) ;
150 GetVol( NULL, &vRefNum ) ;
151
152 GetDirectoryID( vRefNum , fsRtDirID , path , &dirRef , &isDirectory ) ;
153 wxMacSetupStandardFile(vRefNum, dirRef) ;
154 }
155
156 enum {
157 kSelectItem = 10, // select button item number
158 kSFGetFileDlgID = 251, // dialog resource number
159 kStrListID = 251, // our strings
160 kSelectStrNum = 1, // word 'Select: ' for button
161 kDesktopStrNum = 2, // word 'Desktop' for button
162 kSelectNoQuoteStrNum = 3, // word 'Select: ' for button
163
164 kUseQuotes = true, // parameter for SetButtonName
165 kDontUseQuotes = false
166 };
167
168 static void GetLabelString(StringPtr theStr, short stringNum)
169 {
170 GetIndString(theStr, kStrListID, stringNum);
171 }
172
173 static void CopyPStr(StringPtr src, StringPtr dest)
174 {
175 BlockMoveData(src, dest, 1 + src[0]);
176 }
177
178 static char GetSelectKey(void)
179 {
180 // this is the key used to trigger the select button
181
182 // NOT INTERNATIONAL SAVVY; should at least grab it from resources
183
184 return 's';
185 }
186
187 // FlashButton briefly highlights the dialog button
188 // as feedback for key equivalents
189
190 static void FlashButton(DialogPtr theDlgPtr, short buttonID)
191 {
192 short buttonType;
193 Handle buttonHandle;
194 Rect buttonRect;
195 unsigned long finalTicks;
196
197 GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
198 HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
199 Delay(10, &finalTicks);
200 HiliteControl((ControlHandle) buttonHandle, 0);
201 }
202
203 static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
204 {
205 return (spec1->vRefNum == spec2->vRefNum
206 && spec1->parID == spec2->parID
207 && EqualString(spec1->name, spec2->name, false, false));
208 }
209 // MyModalDialogFilter maps a key to the Select button, and handles
210 // flashing of the button when the key is hit
211
212 static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
213 short *item, Ptr dataPtr)
214 {
215 #pragma unused (dataPtr)
216
217 // make certain the proper dialog is showing, 'cause standard file
218 // can nest dialogs but calls the same filter for each
219
220 if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
221 {
222 // check if the select button was hit
223 /*
224 if ((eventRec->what == keyDown)
225 && (eventRec->modifiers & cmdKey)
226 && ((eventRec->message & charCodeMask) == GetSelectKey()))
227 {
228 *item = kSelectItem;
229 FlashButton(theDlgPtr, kSelectItem);
230 return true;
231 }
232 */
233 }
234
235 return false;
236 }
237 #endif !TARGET_CARBON
238
239 void ExtendedOpenFile( ConstStr255Param message , ConstStr255Param path , const char *filter , FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
240 {
241 Point thePt;
242 OpenUserDataRec myData;
243 FSSpec tempSpec;
244 Boolean folderFlag;
245 Boolean wasAliasedFlag;
246 DlgHookYDUPP dlgHookUPP;
247 ModalFilterYDUPP myModalFilterUPP;
248 OSErr err;
249 SFTypeList types ;
250
251
252 // presumably we're running System 7 or later so CustomGetFile is
253 // available
254
255 // set initial contents of Select button to a space
256
257 memcpy( theSFR->sfFile.name , "\p " , 2 ) ;
258
259 // point the user data parameter at the reply record so we can get to it later
260
261 myData.sfrPtr = theSFR;
262 if ( filter && filter[0] )
263 {
264 myData.numfilters = 1 ;
265 for ( int i = 0 ; i < myData.numfilters ; i++ )
266 {
267 int j ;
268
269 strcpy( myData.filter[i] , filter ) ;
270 for( j = 0 ; myData.filter[i][j] ; j++ )
271 {
272 myData.filter[i][j] = toupper( myData.filter[i][j] ) ;
273 }
274 for ( j = 0 ; gfilters[j] ; j++ )
275 {
276 if ( strcmp( myData.filter[i] , gfilters[j] ) == 0 )
277 {
278 myData.filtermactypes[i] = gfiltersmac[j] ;
279 break ;
280 }
281 }
282 if( gfilters[j] == NULL )
283 {
284 myData.filtermactypes[i] = '****' ;
285 }
286 }
287 }
288 else
289 {
290 myData.numfilters = 0 ;
291 }
292 // display the dialog
293
294 #if !TARGET_CARBON
295
296 dlgHookUPP = NULL ;
297 // dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
298 myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
299
300 thePt.h = thePt.v = -1; // center dialog
301
302 ParamText( message , NULL , NULL , NULL ) ;
303
304 CustomGetFile( fileFilter,
305 -1, // show all types
306 NULL,
307 theSFR,
308 kSFGetFileDlgID,
309 thePt, // top left point
310 dlgHookUPP,
311 myModalFilterUPP,
312 nil, // activate list
313 nil, // activate proc
314 &myData);
315
316 DisposeRoutineDescriptor(dlgHookUPP);
317 DisposeRoutineDescriptor(myModalFilterUPP);
318 #else
319 #endif
320 // if cancel wasn't pressed and no fatal error occurred...
321
322 if (theSFR->sfGood)
323 {
324 // if no name is in the reply record file spec,
325 // use the file spec of the parent folder
326
327 if (theSFR->sfFile.name[0] == '\0')
328 {
329 err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
330 "\p", &tempSpec);
331 if (err == noErr)
332 {
333 theSFR->sfFile = tempSpec;
334 }
335 else
336 {
337 // no name to return, forget it
338
339 theSFR->sfGood = false;
340 }
341 }
342
343 // if there is now a name in the file spec, check if it's
344 // for a folder or a volume
345
346 if (theSFR->sfFile.name[0] != '\0')
347 {
348 // the parID of the root of a disk is always fsRtParID == 1
349
350 if (theSFR->sfFile.parID == fsRtParID)
351 {
352 theSFR->sfIsVolume = true;
353 theSFR->sfIsFolder = false; // it would be reasonable for this to be true, too
354 }
355
356 // we have a valid FSSpec, now let's make sure it's not for an alias file
357
358 err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
359 if (err != noErr)
360 {
361 theSFR->sfGood = false;
362 }
363
364 // did the alias resolve to a folder?
365
366 if (folderFlag && ! theSFR->sfIsVolume)
367 {
368 theSFR->sfIsFolder = true;
369 }
370 }
371 }
372 }
373
374 static pascal Boolean CrossPlatformFileFilter(CInfoPBPtr myCInfoPBPtr, Ptr dataPtr)
375 {
376 Str255 filename ;
377 OpenUserDataRecPtr data = (OpenUserDataRecPtr) dataPtr ;
378 // return true if this item is invisible or a file
379
380 Boolean visibleFlag;
381 Boolean folderFlag;
382
383 visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
384 folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
385
386 // because the semantics of the filter proc are "true means don't show
387 // it" we need to invert the result that we return
388
389 if ( !visibleFlag )
390 return true ;
391
392 if ( !folderFlag )
393 {
394 if ( data->numfilters > 0 )
395 {
396 PLstrcpy( filename ,myCInfoPBPtr->hFileInfo.ioNamePtr ) ;
397 if ( filename[0] >= 4 )
398 {
399 for( int j = 1 ; j <= filename[0] ; j++ )
400 {
401 filename[j] = toupper( filename[j] ) ;
402 }
403 for ( int i = 0 ; i < data->numfilters ; ++i )
404 {
405 if ( myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdType == data->filtermactypes[i] )
406 return false ;
407
408 if ( strncmp( (char*) filename + 1 + filename[0] - 4 ,
409 & data->filter[i][ strlen(data->filter[i]) - 4 ] , 4 ) == 0 )
410 return false ;
411 }
412 }
413 return true ;
414 }
415 }
416
417 return false ;
418 }
419
420 // end wxmac
421
422 wxString wxFileSelector(const char *title,
423 const char *defaultDir, const char *defaultFileName,
424 const char *defaultExtension, const char *filter, int flags,
425 wxWindow *parent, int x, int y)
426 {
427 // If there's a default extension specified but no filter, we create a suitable
428 // filter.
429
430 wxString filter2("");
431 if ( defaultExtension && !filter )
432 filter2 = wxString("*.") + wxString(defaultExtension) ;
433 else if ( filter )
434 filter2 = filter;
435
436 wxString defaultDirString;
437 if (defaultDir)
438 defaultDirString = defaultDir;
439 else
440 defaultDirString = "";
441
442 wxString defaultFilenameString;
443 if (defaultFileName)
444 defaultFilenameString = defaultFileName;
445 else
446 defaultFilenameString = "";
447
448 wxFileDialog fileDialog(parent, title, defaultDirString, defaultFilenameString, filter2, flags, wxPoint(x, y));
449
450 if ( fileDialog.ShowModal() == wxID_OK )
451 {
452 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
453 return wxBuffer;
454 }
455 else
456 return wxGetEmptyString();
457 }
458
459 WXDLLEXPORT wxString wxFileSelectorEx(const char *title,
460 const char *defaultDir,
461 const char *defaultFileName,
462 int* defaultFilterIndex,
463 const char *filter,
464 int flags,
465 wxWindow* parent,
466 int x,
467 int y)
468
469 {
470 wxFileDialog fileDialog(parent, title ? title : "", defaultDir ? defaultDir : "",
471 defaultFileName ? defaultFileName : "", filter ? filter : "", flags, wxPoint(x, y));
472
473 if ( fileDialog.ShowModal() == wxID_OK )
474 {
475 *defaultFilterIndex = fileDialog.GetFilterIndex();
476 strcpy(wxBuffer, (const char *)fileDialog.GetPath());
477 return wxBuffer;
478 }
479 else
480 return wxGetEmptyString();
481 }
482
483 wxFileDialog::wxFileDialog(wxWindow *parent, const wxString& message,
484 const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
485 long style, const wxPoint& pos)
486 {
487 m_message = message;
488 m_dialogStyle = style;
489 m_parent = parent;
490 m_path = "";
491 m_fileName = defaultFileName;
492 m_dir = defaultDir;
493 m_wildCard = wildCard;
494 m_filterIndex = 1;
495 }
496
497 int wxFileDialog::ShowModal()
498 {
499 #if !TARGET_CARBON
500 if ( !gUseNavServices )
501 {
502 if ( m_dialogStyle & wxSAVE )
503 {
504 StandardFileReply reply ;
505 Str255 prompt ;
506 Str255 filename ;
507
508 #if TARGET_CARBON
509 c2pstrcpy((StringPtr)prompt, m_message) ;
510 #else
511 strcpy((char *)prompt, m_message) ;
512 c2pstr((char *)prompt ) ;
513 #endif
514 #if TARGET_CARBON
515 c2pstrcpy((StringPtr)filename, m_fileName) ;
516 #else
517 strcpy((char *)filename, m_fileName) ;
518 c2pstr((char *)filename ) ;
519 #endif
520
521 #if !TARGET_CARBON
522
523 StandardPutFile( prompt , filename , &reply ) ;
524
525 #else
526 #endif
527 if ( reply.sfGood == false )
528 {
529 m_path = "" ;
530 return wxID_CANCEL ;
531 }
532 else
533 {
534 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
535 return wxID_OK ;
536 }
537 }
538 else
539 {
540 OSType types = '????' ;
541 Str255 prompt ;
542 Str255 path ;
543
544 #if TARGET_CARBON
545 c2pstrcpy((StringPtr)prompt, m_message) ;
546 #else
547 strcpy((char *)prompt, m_message) ;
548 c2pstr((char *)prompt ) ;
549 #endif
550 #if TARGET_CARBON
551 c2pstrcpy((StringPtr)path, m_dir ) ;
552 #else
553 strcpy((char *)path, m_dir ) ;
554 c2pstr((char *)path ) ;
555 #endif
556
557 StandardFileReply reply ;
558 FileFilterYDUPP crossPlatformFileFilterUPP = 0 ;
559 #if !TARGET_CARBON
560 crossPlatformFileFilterUPP =
561 NewFileFilterYDProc(CrossPlatformFileFilter);
562 #endif
563
564 ExtendedOpenFile( prompt , path , m_wildCard , crossPlatformFileFilterUPP, &reply);
565 #if !TARGET_CARBON
566 DisposeFileFilterYDUPP(crossPlatformFileFilterUPP);
567 #endif
568 if ( reply.sfGood == false )
569 {
570 m_path = "" ;
571 return wxID_CANCEL ;
572 }
573 else
574 {
575 m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
576 return wxID_OK ;
577 }
578 }
579 return wxID_CANCEL;
580 }
581 else
582 #endif
583 {
584 NavDialogOptions mNavOptions;
585 NavObjectFilterUPP mNavFilterUPP = NULL;
586 NavPreviewUPP mNavPreviewUPP = NULL ;
587 NavReplyRecord mNavReply;
588 AEDesc mDefaultLocation ;
589 bool mSelectDefault = false ;
590
591 ::NavGetDefaultDialogOptions(&mNavOptions);
592
593 mNavFilterUPP = nil;
594 mNavPreviewUPP = nil;
595 mSelectDefault = false;
596 mNavReply.validRecord = false;
597 mNavReply.replacing = false;
598 mNavReply.isStationery = false;
599 mNavReply.translationNeeded = false;
600 mNavReply.selection.descriptorType = typeNull;
601 mNavReply.selection.dataHandle = nil;
602 mNavReply.keyScript = smSystemScript;
603 mNavReply.fileTranslation = nil;
604
605 // Set default location, the location
606 // that's displayed when the dialog
607 // first appears
608
609 FSSpec location ;
610 wxUnixFilename2FSSpec( m_dir , &location ) ;
611 OSErr err = noErr ;
612
613 mDefaultLocation.descriptorType = typeNull;
614 mDefaultLocation.dataHandle = nil;
615
616 err = ::AECreateDesc(typeFSS, &location, sizeof(FSSpec), &mDefaultLocation );
617
618 if ( mDefaultLocation.dataHandle ) {
619
620 if (mSelectDefault) {
621 mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
622 } else {
623 mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
624 }
625 }
626
627 #if TARGET_CARBON
628 c2pstrcpy((StringPtr)mNavOptions.message, m_message) ;
629 #else
630 strcpy((char *)mNavOptions.message, m_message) ;
631 c2pstr((char *)mNavOptions.message ) ;
632 #endif
633 #if TARGET_CARBON
634 c2pstrcpy((StringPtr)mNavOptions.savedFileName, m_fileName) ;
635 #else
636 strcpy((char *)mNavOptions.savedFileName, m_fileName) ;
637 c2pstr((char *)mNavOptions.savedFileName ) ;
638 #endif
639
640 if ( m_dialogStyle & wxSAVE )
641 {
642
643 mNavOptions.dialogOptionFlags |= kNavNoTypePopup ;
644 mNavOptions.dialogOptionFlags |= kNavDontAutoTranslate ;
645 mNavOptions.dialogOptionFlags |= kNavDontAddTranslateItems ;
646
647 err = ::NavPutFile(
648 &mDefaultLocation,
649 &mNavReply,
650 &mNavOptions,
651 sStandardNavEventFilter ,
652 'TEXT',
653 'TEXT',
654 0L); // User Data
655 }
656 else
657 {
658 if ( m_dialogStyle & wxMULTIPLE )
659 mNavOptions.dialogOptionFlags |= kNavAllowMultipleFiles ;
660 else
661 mNavOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles ;
662
663 err = ::NavGetFile(
664 &mDefaultLocation,
665 &mNavReply,
666 &mNavOptions,
667 sStandardNavEventFilter ,
668 mNavPreviewUPP,
669 mNavFilterUPP,
670 0L /*inFileTypes.TypeListHandle() */,
671 0L); // User Data
672 }
673
674 if ( mDefaultLocation.dataHandle != nil )
675 {
676 ::AEDisposeDesc(&mDefaultLocation);
677 }
678
679 if ( (err != noErr) && (err != userCanceledErr) ) {
680 m_path = "" ;
681 return wxID_CANCEL ;
682 }
683
684 if (mNavReply.validRecord) {
685
686 FSSpec outFileSpec ;
687 AEDesc specDesc ;
688
689 long count ;
690 ::AECountItems( &mNavReply.selection , &count ) ;
691 for ( long i = 1 ; i <= count ; ++i )
692 {
693 OSErr err = ::AEGetNthDesc( &mNavReply.selection , i , typeFSS, NULL , &specDesc);
694 if ( err != noErr ) {
695 m_path = "" ;
696 return wxID_CANCEL ;
697 }
698 outFileSpec = **(FSSpec**) specDesc.dataHandle;
699 if (specDesc.dataHandle != nil) {
700 ::AEDisposeDesc(&specDesc);
701 }
702
703
704 // outFolderDirID = thePB.dirInfo.ioDrDirID;
705 m_path = wxMacFSSpec2UnixFilename( &outFileSpec ) ;
706 m_paths.Add( m_path ) ;
707 m_fileNames.Add(m_fileName);
708 }
709 // set these to the first hit
710 m_path = m_paths[ 0 ] ;
711 m_fileName = wxFileNameFromPath(m_path);
712 m_dir = wxPathOnly(m_path);
713
714 return wxID_OK ;
715 }
716 return wxID_CANCEL;
717 }
718 }
719
720 // Generic file load/save dialog
721 static wxString
722 wxDefaultFileSelector(bool load, const char *what, const char *extension, const char *default_name, wxWindow *parent)
723 {
724 char *ext = (char *)extension;
725
726 char prompt[50];
727 wxString str;
728 if (load)
729 str = "Load %s file";
730 else
731 str = "Save %s file";
732 sprintf(prompt, wxGetTranslation(str), what);
733
734 if (*ext == '.') ext++;
735 char wild[60];
736 sprintf(wild, "*.%s", ext);
737
738 return wxFileSelector (prompt, NULL, default_name, ext, wild, 0, parent);
739 }
740
741 // Generic file load dialog
742 wxString
743 wxLoadFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
744 {
745 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
746 }
747
748
749 // Generic file save dialog
750 wxString
751 wxSaveFileSelector(const char *what, const char *extension, const char *default_name, wxWindow *parent)
752 {
753 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
754 }
755
756