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