/* * tkMacDialog.c -- * * Contains the Mac implementation of the common dialog boxes. * * Copyright (c) 1996-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkMacDialog.c,v 1.5 2000/04/17 02:16:50 jingham Exp $ */ #include #include #include #include #include #include #include #include #include #include #include "tkPort.h" #include "tkInt.h" #include "tclMacInt.h" #include "tkMacInt.h" #include "tkFileFilter.h" #ifndef StrLength #define StrLength(s) (*((unsigned char *) (s))) #endif #ifndef StrBody #define StrBody(s) ((char *) (s) + 1) #endif /* * The following are ID's for resources that are defined in tkMacResource.r */ #define OPEN_BOX 130 #define OPEN_POPUP 131 #define OPEN_MENU 132 #define OPEN_POPUP_ITEM 10 #define SAVE_FILE 0 #define OPEN_FILE 1 #define CHOOSE_FOLDER 2 #define MATCHED 0 #define UNMATCHED 1 /* * The following structure is used in the GetFileName() function. It stored * information about the file dialog and the file filters. */ typedef struct _OpenFileData { FileFilterList fl; /* List of file filters. */ SInt16 curType; /* The filetype currently being * listed. */ short popupItem; /* Item number of the popup in the * dialog. */ int usePopup; /* True if we show the popup menu (this * is an open operation and the * -filetypes option is set). */ } OpenFileData; static pascal Boolean FileFilterProc _ANSI_ARGS_((CInfoPBPtr pb, void *myData)); static int GetFileName _ANSI_ARGS_ ((ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int isOpen)); static int NavGetFileName _ANSI_ARGS_ ((ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int isOpen)); static Boolean MatchOneType _ANSI_ARGS_((StringPtr fileNamePtr, OSType fileType, OpenFileData *myofdPtr, FileFilter *filterPtr)); static pascal short OpenHookProc _ANSI_ARGS_((short item, DialogPtr theDialog, OpenFileData * myofdPtr)); static int ParseFileDlgArgs _ANSI_ARGS_ ((Tcl_Interp * interp, OpenFileData * myofdPtr, int argc, char ** argv, int isOpen)); static pascal Boolean OpenFileFilterProc(AEDesc* theItem, void* info, NavCallBackUserData callBackUD, NavFilterModes filterMode ); pascal void OpenEventProc(NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, NavCallBackUserData callBackUD ); static void InitFileDialogs(); static int StdGetFile(Tcl_Interp *interp, OpenFileData *ofd, unsigned char *initialFile, int isOpen); static int NavServicesGetFile(Tcl_Interp *interp, OpenFileData *ofd, AEDesc *initialDesc, unsigned char *initialFile, StringPtr title, StringPtr message, int multiple, int isOpen); static int HandleInitialDirectory (Tcl_Interp *interp, char *initialDir, FSSpec *dirSpec, AEDesc *dirDescPtr); /* * Filter and hook functions used by the tk_getOpenFile and tk_getSaveFile * commands. */ int fileDlgInited = 0; int useNavServices = 0; NavObjectFilterUPP openFileFilterUPP; NavEventUPP openFileEventUPP; static FileFilterYDUPP openFilter = NULL; static DlgHookYDUPP openHook = NULL; static DlgHookYDUPP saveHook = NULL; /* *---------------------------------------------------------------------- * * Tk_ChooseColorObjCmd -- * * This procedure implements the color dialog box for the Mac * platform. See the user documentation for details on what it * does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ int Tk_ChooseColorObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *CONST objv[]) /* Argument objects. */ { Tk_Window parent; char *title; int i, picked, srcRead, dstWrote; long response; OSErr err; static inited = 0; static RGBColor in; static char *optionStrings[] = { "-initialcolor", "-parent", "-title", NULL }; enum options { COLOR_INITIAL, COLOR_PARENT, COLOR_TITLE }; if (inited == 0) { /* * 'in' stores the last color picked. The next time the color dialog * pops up, the last color will remain in the dialog. */ in.red = 0xffff; in.green = 0xffff; in.blue = 0xffff; inited = 1; } parent = (Tk_Window) clientData; title = "Choose a color:"; picked = 0; for (i = 1; i < objc; i += 2) { int index; char *option, *value; if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", TCL_EXACT, &index) != TCL_OK) { return TCL_ERROR; } if (i + 1 == objc) { option = Tcl_GetStringFromObj(objv[i], NULL); Tcl_AppendResult(interp, "value for \"", option, "\" missing", (char *) NULL); return TCL_ERROR; } value = Tcl_GetStringFromObj(objv[i + 1], NULL); switch ((enum options) index) { case COLOR_INITIAL: { XColor *colorPtr; colorPtr = Tk_GetColor(interp, parent, value); if (colorPtr == NULL) { return TCL_ERROR; } in.red = colorPtr->red; in.green = colorPtr->green; in.blue = colorPtr->blue; Tk_FreeColor(colorPtr); break; } case COLOR_PARENT: { parent = Tk_NameToWindow(interp, value, parent); if (parent == NULL) { return TCL_ERROR; } break; } case COLOR_TITLE: { title = value; break; } } } /* * Use the gestalt manager to determine how to bring * up the color picker. If versin 2.0 isn't available * we can assume version 1.0 is available as it comes with * Color Quickdraw which Tk requires to run at all. */ err = Gestalt(gestaltColorPicker, &response); if ((err == noErr) && (response == 0x0200L)) { ColorPickerInfo cpinfo; /* * Version 2.0 of the color picker is available. Let's use it */ cpinfo.theColor.profile = 0L; cpinfo.theColor.color.rgb.red = in.red; cpinfo.theColor.color.rgb.green = in.green; cpinfo.theColor.color.rgb.blue = in.blue; cpinfo.dstProfile = 0L; cpinfo.flags = CanModifyPalette | CanAnimatePalette; cpinfo.placeWhere = kDeepestColorScreen; cpinfo.pickerType = 0L; cpinfo.eventProc = NULL; cpinfo.colorProc = NULL; cpinfo.colorProcData = NULL; Tcl_UtfToExternal(NULL, NULL, title, -1, 0, NULL, StrBody(cpinfo.prompt), 255, &srcRead, &dstWrote, NULL); StrLength(cpinfo.prompt) = (unsigned char) dstWrote; if ((PickColor(&cpinfo) == noErr) && (cpinfo.newColorChosen != 0)) { in.red = cpinfo.theColor.color.rgb.red; in.green = cpinfo.theColor.color.rgb.green; in.blue = cpinfo.theColor.color.rgb.blue; picked = 1; } } else { RGBColor out; Str255 prompt; Point point = {-1, -1}; /* * Use version 1.0 of the color picker */ Tcl_UtfToExternal(NULL, NULL, title, -1, 0, NULL, StrBody(prompt), 255, &srcRead, &dstWrote, NULL); StrLength(prompt) = (unsigned char) dstWrote; if (GetColor(point, prompt, &in, &out)) { in = out; picked = 1; } } if (picked != 0) { char result[32]; sprintf(result, "#%02x%02x%02x", in.red >> 8, in.green >> 8, in.blue >> 8); Tcl_AppendResult(interp, result, NULL); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Tk_GetOpenFileObjCmd -- * * This procedure implements the "open file" dialog box for the * Mac platform. See the user documentation for details on what * it does. * * Results: * A standard Tcl result. * * Side effects: * See user documentation. *---------------------------------------------------------------------- */ int Tk_GetOpenFileObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *CONST objv[]) /* Argument objects. */ { int i, result, multiple; OpenFileData ofd; Tk_Window parent; Str255 message, title; AEDesc initialDesc = {typeNull, NULL}; FSSpec dirSpec; static char *openOptionStrings[] = { "-filetypes", "-initialdir", "-message", "-multiple", "-parent", "-title", NULL }; enum openOptions { OPEN_TYPES, OPEN_INITDIR, OPEN_MESSAGE, OPEN_MULTIPLE, OPEN_PARENT, OPEN_TITLE }; if (!fileDlgInited) { InitFileDialogs(); } result = TCL_ERROR; parent = (Tk_Window) clientData; multiple = false; title[0] = 0; message[0] = 0; TkInitFileFilters(&ofd.fl); ofd.curType = 0; ofd.popupItem = OPEN_POPUP_ITEM; ofd.usePopup = 1; for (i = 1; i < objc; i += 2) { char *choice; int index, choiceLen; char *string; int srcRead, dstWrote; if (Tcl_GetIndexFromObj(interp, objv[i], openOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { result = TCL_ERROR; goto end; } if (i + 1 == objc) { string = Tcl_GetStringFromObj(objv[i], NULL); Tcl_AppendResult(interp, "value for \"", string, "\" missing", (char *) NULL); result = TCL_ERROR; goto end; } switch (index) { case OPEN_TYPES: choice = Tcl_GetStringFromObj(objv[i + 1], NULL); if (TkGetFileFilters(interp, &ofd.fl, choice, 0) != TCL_OK) { result = TCL_ERROR; goto end; } break; case OPEN_INITDIR: choice = Tcl_GetStringFromObj(objv[i + 1], NULL); if (HandleInitialDirectory(interp, choice, &dirSpec, &initialDesc) != TCL_OK) { result = TCL_ERROR; goto end; } break; case OPEN_MESSAGE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(message), 255, &srcRead, &dstWrote, NULL); message[0] = dstWrote; break; case OPEN_MULTIPLE: if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &multiple) != TCL_OK) { result = TCL_ERROR; goto end; } break; case OPEN_PARENT: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); parent = Tk_NameToWindow(interp, choice, parent); if (parent == NULL) { result = TCL_ERROR; goto end; } break; case OPEN_TITLE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(title), 255, &srcRead, &dstWrote, NULL); title[0] = dstWrote; break; } } if (useNavServices) { AEDesc *initialPtr = NULL; if (initialDesc.descriptorType == typeFSS) { initialPtr = &initialDesc; } result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, title, message, multiple, OPEN_FILE); } else { result = StdGetFile(interp, &ofd, NULL, OPEN_FILE); } end: TkFreeFileFilters(&ofd.fl); AEDisposeDesc(&initialDesc); return result; } /* *---------------------------------------------------------------------- * * Tk_GetSaveFileObjCmd -- * * Same as Tk_GetOpenFileCmd but opens a "save file" dialog box * instead * * Results: * A standard Tcl result. * * Side effects: * See user documentation. *---------------------------------------------------------------------- */ int Tk_GetSaveFileObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *CONST objv[]) /* Argument objects. */ { int i, result; Str255 initialFile; Tk_Window parent; AEDesc initialDesc = {typeNull, NULL}; FSSpec dirSpec; Str255 title, message; OpenFileData ofd; static char *saveOptionStrings[] = { "-defaultextension", "-initialdir", "-initialfile", "-message", "-parent", "-title", NULL }; enum saveOptions { SAVE_DEFAULT, SAVE_INITDIR, SAVE_INITFILE, SAVE_MESSAGE, SAVE_PARENT, SAVE_TITLE }; if (!fileDlgInited) { InitFileDialogs(); } result = TCL_ERROR; parent = (Tk_Window) clientData; StrLength(initialFile) = 0; title[0] = 0; message[0] = 0; for (i = 1; i < objc; i += 2) { char *choice; int index, choiceLen; char *string; Tcl_DString ds; int srcRead, dstWrote; if (Tcl_GetIndexFromObj(interp, objv[i], saveOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { return TCL_ERROR; } if (i + 1 == objc) { string = Tcl_GetStringFromObj(objv[i], NULL); Tcl_AppendResult(interp, "value for \"", string, "\" missing", (char *) NULL); return TCL_ERROR; } switch (index) { case SAVE_DEFAULT: break; case SAVE_INITDIR: choice = Tcl_GetStringFromObj(objv[i + 1], NULL); if (HandleInitialDirectory(interp, choice, &dirSpec, &initialDesc) != TCL_OK) { result = TCL_ERROR; goto end; } break; case SAVE_INITFILE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); if (Tcl_TranslateFileName(interp, choice, &ds) == NULL) { result = TCL_ERROR; goto end; } Tcl_UtfToExternal(NULL, NULL, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, StrBody(initialFile), 255, &srcRead, &dstWrote, NULL); StrLength(initialFile) = (unsigned char) dstWrote; Tcl_DStringFree(&ds); break; case SAVE_MESSAGE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(message), 255, &srcRead, &dstWrote, NULL); StrLength(message) = (unsigned char) dstWrote; break; case SAVE_PARENT: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); parent = Tk_NameToWindow(interp, choice, parent); if (parent == NULL) { result = TCL_ERROR; goto end; } break; case SAVE_TITLE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(title), 255, &srcRead, &dstWrote, NULL); StrLength(title) = (unsigned char) dstWrote; break; } } TkInitFileFilters(&ofd.fl); ofd.usePopup = 0; if (useNavServices) { AEDesc *initialPtr = NULL; if (initialDesc.descriptorType == typeFSS) { initialPtr = &initialDesc; } result = NavServicesGetFile(interp, &ofd, initialPtr, initialFile, title, message, false, SAVE_FILE); } else { result = StdGetFile(interp, NULL, initialFile, SAVE_FILE); } end: AEDisposeDesc(&initialDesc); return result; } /* *---------------------------------------------------------------------- * * Tk_ChooseDirectoryObjCmd -- * * This procedure implements the "tk_chooseDirectory" dialog box * for the Windows platform. See the user documentation for details * on what it does. * * Results: * See user documentation. * * Side effects: * A modal dialog window is created. Tcl_SetServiceMode() is * called to allow background events to be processed * *---------------------------------------------------------------------- */ int Tk_ChooseDirectoryObjCmd(clientData, interp, objc, objv) ClientData clientData; /* Main window associated with interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST objv[]; /* Argument objects. */ { int i, result; Tk_Window parent; AEDesc initialDesc = {typeNull, NULL}; FSSpec dirSpec; Str255 message, title; int srcRead, dstWrote; OpenFileData ofd; static char *chooseOptionStrings[] = { "-initialdir", "-message", "-mustexist", "-parent", "-title", NULL }; enum chooseOptions { CHOOSE_INITDIR, CHOOSE_MESSAGE, CHOOSE_MUSTEXIST, CHOOSE_PARENT, CHOOSE_TITLE }; if (!NavServicesAvailable()) { return TCL_ERROR; } if (!fileDlgInited) { InitFileDialogs(); } result = TCL_ERROR; parent = (Tk_Window) clientData; title[0] = 0; message[0] = 0; for (i = 1; i < objc; i += 2) { char *choice; int index, choiceLen; char *string; if (Tcl_GetIndexFromObj(interp, objv[i], chooseOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { return TCL_ERROR; } if (i + 1 == objc) { string = Tcl_GetStringFromObj(objv[i], NULL); Tcl_AppendResult(interp, "value for \"", string, "\" missing", (char *) NULL); return TCL_ERROR; } switch (index) { case CHOOSE_INITDIR: choice = Tcl_GetStringFromObj(objv[i + 1], NULL); if (HandleInitialDirectory(interp, choice, &dirSpec, &initialDesc) != TCL_OK) { result = TCL_ERROR; goto end; } break; case CHOOSE_MESSAGE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(message), 255, &srcRead, &dstWrote, NULL); StrLength(message) = (unsigned char) dstWrote; break; case CHOOSE_PARENT: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); parent = Tk_NameToWindow(interp, choice, parent); if (parent == NULL) { result = TCL_ERROR; goto end; } break; case CHOOSE_TITLE: choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); Tcl_UtfToExternal(NULL, NULL, choice, choiceLen, 0, NULL, StrBody(title), 255, &srcRead, &dstWrote, NULL); StrLength(title) = (unsigned char) dstWrote; break; } } TkInitFileFilters(&ofd.fl); ofd.usePopup = 0; if (useNavServices) { AEDesc *initialPtr = NULL; if (initialDesc.descriptorType == typeFSS) { initialPtr = &initialDesc; } result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, title, message, false, CHOOSE_FOLDER); } else { result = TCL_ERROR; } end: AEDisposeDesc(&initialDesc); return result; } int HandleInitialDirectory ( Tcl_Interp *interp, char *initialDir, FSSpec *dirSpec, AEDesc *dirDescPtr) { Tcl_DString ds; long dirID; OSErr err; Boolean isDirectory; Str255 dir; int srcRead, dstWrote; if (Tcl_TranslateFileName(interp, initialDir, &ds) == NULL) { return TCL_ERROR; } Tcl_UtfToExternal(NULL, NULL, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, StrBody(dir), 255, &srcRead, &dstWrote, NULL); StrLength(dir) = (unsigned char) dstWrote; Tcl_DStringFree(&ds); err = FSpLocationFromPath(StrLength(dir), StrBody(dir), dirSpec); if (err != noErr) { Tcl_AppendResult(interp, "bad directory \"", initialDir, "\"", NULL); return TCL_ERROR; } err = FSpGetDirectoryID(dirSpec, &dirID, &isDirectory); if ((err != noErr) || !isDirectory) { Tcl_AppendResult(interp, "bad directory \"", initialDir, "\"", NULL); return TCL_ERROR; } if (useNavServices) { AECreateDesc( typeFSS, dirSpec, sizeof(*dirSpec), dirDescPtr); } else { /* * Make sure you negate -dirSpec.vRefNum because the * standard file package wants it that way ! */ LMSetSFSaveDisk(-dirSpec->vRefNum); LMSetCurDirStore(dirID); } return TCL_OK; } static void InitFileDialogs() { fileDlgInited = 1; if (NavServicesAvailable()) { openFileFilterUPP = NewNavObjectFilterProc(OpenFileFilterProc); openFileEventUPP = NewNavEventProc(OpenEventProc); useNavServices = 1; } else { openFilter = NewFileFilterYDProc(FileFilterProc); openHook = NewDlgHookYDProc(OpenHookProc); saveHook = NewDlgHookYDProc(OpenHookProc); useNavServices = 0; } } /* *---------------------------------------------------------------------- * * GetFileName -- * * Calls the Mac file dialog functions for the user to choose a * file to or save. * * Results: * A standard Tcl result. * * Side effects: * If the user selects a file, the native pathname of the file * is returned in the interp's result. Otherwise an empty string * is returned in the interp's result. * *---------------------------------------------------------------------- */ static int GetFileName( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *CONST objv[], /* Argument objects. */ int isOpen) /* true if we should call GetOpenFileName(), * false if we should call GetSaveFileName() */ { return TCL_OK; } static int NavServicesGetFile( Tcl_Interp *interp, OpenFileData *ofdPtr, AEDesc *initialDesc, unsigned char *initialFile, StringPtr title, StringPtr message, int multiple, int isOpen) { NavReplyRecord theReply; NavDialogOptions diagOptions; OSErr err; Tcl_Obj *theResult; int result; diagOptions.location.h = -1; diagOptions.location.v = -1; diagOptions.dialogOptionFlags = kNavDontAutoTranslate + kNavDontAddTranslateItems; if (multiple) { diagOptions.dialogOptionFlags += kNavAllowMultipleFiles; } if (ofdPtr != NULL && ofdPtr->usePopup) { FileFilter *filterPtr; filterPtr = ofdPtr->fl.filters; if (filterPtr == NULL) { ofdPtr->usePopup = 0; } } if (ofdPtr != NULL && ofdPtr->usePopup) { NavMenuItemSpecHandle popupExtensionHandle = NULL; NavMenuItemSpec *popupItems; FileFilter *filterPtr; short index = 0; ofdPtr->curType = 0; popupExtensionHandle = (NavMenuItemSpecHandle) NewHandle(ofdPtr->fl.numFilters * sizeof(NavMenuItemSpec)); HLock((Handle) popupExtensionHandle); popupItems = *popupExtensionHandle; for (filterPtr = ofdPtr->fl.filters; filterPtr != NULL; filterPtr = filterPtr->next, popupItems++, index++) { int len; len = strlen(filterPtr->name); BlockMove(filterPtr->name, popupItems->menuItemName + 1, len); popupItems->menuItemName[0] = len; popupItems->menuCreator = 'WIsH'; popupItems->menuType = index; } HUnlock((Handle) popupExtensionHandle); diagOptions.popupExtension = popupExtensionHandle; } else { diagOptions.dialogOptionFlags += kNavNoTypePopup; diagOptions.popupExtension = NULL; } if ((initialFile != NULL) && (initialFile[0] != 0)) { char *lastColon; int len; len = initialFile[0]; p2cstr(initialFile); lastColon = strrchr((char *)initialFile, ':'); if (lastColon != NULL) { len -= lastColon - ((char *) (initialFile + 1)); BlockMove(lastColon + 1, diagOptions.savedFileName + 1, len); diagOptions.savedFileName[0] = len; } else { BlockMove(initialFile, diagOptions.savedFileName + 1, len); diagOptions.savedFileName[0] = len; } } else { diagOptions.savedFileName[0] = 0; } strcpy((char *) (diagOptions.clientName + 1),"Wish"); diagOptions.clientName[0] = strlen("Wish"); if (title == NULL) { diagOptions.windowTitle[0] = 0; } else { BlockMove(title, diagOptions.windowTitle, title[0] + 1); diagOptions.windowTitle[0] = title[0]; } if (message == NULL) { diagOptions.message[0] = 0; } else { BlockMove(message, diagOptions.message, message[0] + 1); diagOptions.message[0] = message[0]; } diagOptions.actionButtonLabel[0] = 0; diagOptions.cancelButtonLabel[0] = 0; diagOptions.preferenceKey = 0; /* Now process the selection list. We have to use the popupExtension * to fill the menu. */ if (isOpen == OPEN_FILE) { err = NavGetFile(initialDesc, &theReply, &diagOptions, openFileEventUPP, NULL, openFileFilterUPP, NULL, ofdPtr); } else if (isOpen == SAVE_FILE) { err = NavPutFile (initialDesc, &theReply, &diagOptions, openFileEventUPP, 'TEXT', 'WIsH', NULL); } else if (isOpen == CHOOSE_FOLDER) { err = NavChooseFolder (initialDesc, &theReply, &diagOptions, openFileEventUPP, NULL, NULL); } theResult = Tcl_NewListObj(0, NULL); if ( theReply.validRecord && err == noErr ) { AEDesc resultDesc; long count; Tcl_DString fileName; Handle pathHandle; int length; if ( err == noErr ) { err = AECountItems(&(theReply.selection), &count); if (err == noErr) { long i; for (i = 1; i <= count; i++ ) { err = AEGetNthDesc(&(theReply.selection), i, typeFSS, NULL, &resultDesc); if (err == noErr) { HLock(resultDesc.dataHandle); pathHandle = NULL; FSpPathFromLocation((FSSpec *) *resultDesc.dataHandle, &length, &pathHandle); HLock(pathHandle); Tcl_ExternalToUtfDString(NULL, (char *) *pathHandle, -1, &fileName); Tcl_ListObjAppendElement(interp, theResult, Tcl_NewStringObj(Tcl_DStringValue(&fileName), Tcl_DStringLength(&fileName))); Tcl_DStringFree(&fileName); HUnlock(pathHandle); DisposeHandle(pathHandle); HUnlock(resultDesc.dataHandle); AEDisposeDesc( &resultDesc ); } } } } err = NavDisposeReply( &theReply ); Tcl_SetObjResult(interp, theResult); result = TCL_OK; } else if (err == userCanceledErr) { result = TCL_OK; } else { result = TCL_ERROR; } if (diagOptions.popupExtension != NULL) { DisposeHandle((Handle) diagOptions.popupExtension); } return result; } static pascal Boolean OpenFileFilterProc( AEDesc* theItem, void* info, NavCallBackUserData callBackUD, NavFilterModes filterMode ) { OpenFileData *ofdPtr = (OpenFileData *) callBackUD; if (!ofdPtr->usePopup) { return true; } else { if (ofdPtr->fl.numFilters == 0) { return true; } else { if ( theItem->descriptorType == typeFSS ) { NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*)info; int result; if ( !theInfo->isFolder ) { OSType fileType; StringPtr fileNamePtr; int i; FileFilter *filterPtr; fileType = theInfo->fileAndFolder.fileInfo.finderInfo.fdType; HLock(theItem->dataHandle); fileNamePtr = (((FSSpec *) *theItem->dataHandle)->name); if (ofdPtr->usePopup) { i = ofdPtr->curType; for (filterPtr=ofdPtr->fl.filters; filterPtr && i>0; i--) { filterPtr = filterPtr->next; } if (filterPtr) { result = MatchOneType(fileNamePtr, fileType, ofdPtr, filterPtr); } else { result = false; } } else { /* * We are not using the popup menu. In this case, the file is * considered matched if it matches any of the file filters. */ result = UNMATCHED; for (filterPtr=ofdPtr->fl.filters; filterPtr; filterPtr=filterPtr->next) { if (MatchOneType(fileNamePtr, fileType, ofdPtr, filterPtr) == MATCHED) { result = MATCHED; break; } } } HUnlock(theItem->dataHandle); return (result == MATCHED); } else { return true; } } } return true; } } pascal void OpenEventProc( NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParams, NavCallBackUserData callBackUD ) { NavMenuItemSpec *chosenItem; OpenFileData *ofd = (OpenFileData *) callBackUD; static SInt32 otherEvent = ~(kNavCBCustomize|kNavCBStart|kNavCBTerminate |kNavCBNewLocation|kNavCBShowDesktop|kNavCBSelectEntry|kNavCBAccept |kNavCBCancel|kNavCBAdjustPreview); if (callBackSelector == kNavCBPopupMenuSelect) { chosenItem = (NavMenuItemSpec *) callBackParams->eventData.eventDataParms.param; ofd->curType = chosenItem->menuType; } else if (callBackSelector == kNavCBAdjustRect || callBackSelector & otherEvent != 0) { while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT|TCL_WINDOW_EVENTS)) { /* Empty Body */ } } } static int StdGetFile( Tcl_Interp *interp, OpenFileData *ofd, unsigned char *initialFile, int isOpen) { int i; StandardFileReply reply; Point mypoint; MenuHandle menu = NULL; /* * Set the items in the file types popup. */ /* * Delete all the entries inside the popup menu, in case there's any * left overs from previous invocation of this command */ if (ofd != NULL && ofd->usePopup) { FileFilter *filterPtr; menu = GetMenu(OPEN_MENU); for (i = CountMItems(menu); i > 0; i--) { /* * The item indices are one based. Also, if we delete from * the beginning, the items may be re-numbered. So we * delete from the end */ DeleteMenuItem(menu, i); } filterPtr = ofd->fl.filters; if (filterPtr == NULL) { ofd->usePopup = 0; } else { for ( ; filterPtr != NULL; filterPtr = filterPtr->next) { Str255 str; StrLength(str) = (unsigned char) strlen(filterPtr->name); strcpy(StrBody(str), filterPtr->name); AppendMenu(menu, str); } } } /* * Call the toolbox file dialog function. */ SetPt(&mypoint, -1, -1); TkpSetCursor(NULL); if (isOpen == OPEN_FILE) { if (ofd != NULL && ofd->usePopup) { CustomGetFile(openFilter, (short) -1, NULL, &reply, OPEN_BOX, mypoint, openHook, NULL, NULL, NULL, (void*) &ofd); } else { StandardGetFile(NULL, -1, NULL, &reply); } } else if (isOpen == SAVE_FILE) { static Str255 prompt = "\pSave as"; if (ofd != NULL && ofd->usePopup) { /* * Currently this never gets called because we don't use * popup for the save dialog. */ CustomPutFile(prompt, initialFile, &reply, OPEN_BOX, mypoint, saveHook, NULL, NULL, NULL, (void *) ofd); } else { StandardPutFile(prompt, initialFile, &reply); } } /* * Now parse the reply, and populate the Tcl result. */ if (reply.sfGood) { int length; Handle pathHandle; pathHandle = NULL; FSpPathFromLocation(&reply.sfFile, &length, &pathHandle); if (pathHandle != NULL) { Tcl_DString ds; HLock(pathHandle); Tcl_ExternalToUtfDString(NULL, (char *) *pathHandle, -1, &ds); Tcl_AppendResult(interp, Tcl_DStringValue(&ds), NULL); Tcl_DStringFree(&ds); HUnlock(pathHandle); DisposeHandle(pathHandle); } } if (menu != NULL) { DisposeMenu(menu); } return TCL_OK; } /* *---------------------------------------------------------------------- * * OpenHookProc -- * * Gets called for various events that occur in the file dialog box. * Initializes the popup menu or rebuild the file list depending on * the type of the event. * * Results: * A standard result understood by the Mac file dialog event dispatcher. * * Side effects: * The contents in the file dialog may be changed depending on * the type of the event. *---------------------------------------------------------------------- */ static pascal short OpenHookProc( short item, /* Event description. */ DialogPtr theDialog, /* The dialog where the event occurs. */ OpenFileData *ofdPtr) /* Information about the file dialog. */ { short ignore; Rect rect; Handle handle; int newType; switch (item) { case sfHookFirstCall: if (ofdPtr->usePopup) { /* * Set the popup list to display the selected type. */ GetDialogItem(theDialog, ofdPtr->popupItem, &ignore, &handle, &rect); SetControlValue((ControlRef) handle, ofdPtr->curType + 1); } return sfHookNullEvent; case OPEN_POPUP_ITEM: if (ofdPtr->usePopup) { GetDialogItem(theDialog, ofdPtr->popupItem, &ignore, &handle, &rect); newType = GetCtlValue((ControlRef) handle) - 1; if (ofdPtr->curType != newType) { if (newType<0 || newType>ofdPtr->fl.numFilters) { /* * Sanity check. Looks like the user selected an * non-existent menu item?? Don't do anything. */ } else { ofdPtr->curType = newType; } return sfHookRebuildList; } } break; } return item; } /* *---------------------------------------------------------------------- * * FileFilterProc -- * * Filters files according to file types. Get called whenever the * file list needs to be updated inside the dialog box. * * Results: * Returns MATCHED if the file should be shown in the listbox, returns * UNMATCHED otherwise. * * Side effects: * If MATCHED is returned, the file is shown in the listbox. * *---------------------------------------------------------------------- */ static pascal Boolean FileFilterProc( CInfoPBPtr pb, /* Information about the file */ void *myData) /* Client data for this file dialog */ { int i; OpenFileData * ofdPtr = (OpenFileData*)myData; FileFilter * filterPtr; if (ofdPtr->fl.numFilters == 0) { /* * No types have been specified. List all files by default */ return MATCHED; } if (pb->dirInfo.ioFlAttrib & 0x10) { /* * This is a directory: always show it */ return MATCHED; } if (ofdPtr->usePopup) { i = ofdPtr->curType; for (filterPtr=ofdPtr->fl.filters; filterPtr && i>0; i--) { filterPtr = filterPtr->next; } if (filterPtr) { return MatchOneType(pb->hFileInfo.ioNamePtr, pb->hFileInfo.ioFlFndrInfo.fdType, ofdPtr, filterPtr); } else { return UNMATCHED; } } else { /* * We are not using the popup menu. In this case, the file is * considered matched if it matches any of the file filters. */ for (filterPtr=ofdPtr->fl.filters; filterPtr; filterPtr=filterPtr->next) { if (MatchOneType(pb->hFileInfo.ioNamePtr, pb->hFileInfo.ioFlFndrInfo.fdType, ofdPtr, filterPtr) == MATCHED) { return MATCHED; } } return UNMATCHED; } } /* *---------------------------------------------------------------------- * * MatchOneType -- * * Match a file with one file type in the list of file types. * * Results: * Returns MATCHED if the file matches with the file type; returns * UNMATCHED otherwise. * * Side effects: * None * *---------------------------------------------------------------------- */ static Boolean MatchOneType( StringPtr fileNamePtr, /* Name of the file */ OSType fileType, /* Type of the file */ OpenFileData * ofdPtr, /* Information about this file dialog */ FileFilter * filterPtr) /* Match the file described by pb against * this filter */ { FileFilterClause * clausePtr; /* * A file matches with a file type if it matches with at least one * clause of the type. * * If the clause has both glob patterns and ostypes, the file must * match with at least one pattern AND at least one ostype. * * If the clause has glob patterns only, the file must match with at least * one pattern. * * If the clause has mac types only, the file must match with at least * one mac type. * * If the clause has neither glob patterns nor mac types, it's * considered an error. */ for (clausePtr=filterPtr->clauses; clausePtr; clausePtr=clausePtr->next) { int macMatched = 0; int globMatched = 0; GlobPattern * globPtr; MacFileType * mfPtr; if (clausePtr->patterns == NULL) { globMatched = 1; } if (clausePtr->macTypes == NULL) { macMatched = 1; } for (globPtr=clausePtr->patterns; globPtr; globPtr=globPtr->next) { char filename[256]; int len; char * p, *q, *ext; if (fileNamePtr == NULL) { continue; } p = (char*)(fileNamePtr); len = p[0]; strncpy(filename, p+1, len); filename[len] = '\0'; ext = globPtr->pattern; if (ext[0] == '\0') { /* * We don't want any extensions: OK if the filename doesn't * have "." in it */ for (q=filename; *q; q++) { if (*q == '.') { goto glob_unmatched; } } goto glob_matched; } if (Tcl_StringMatch(filename, ext)) { goto glob_matched; } else { goto glob_unmatched; } glob_unmatched: continue; glob_matched: globMatched = 1; break; } for (mfPtr=clausePtr->macTypes; mfPtr; mfPtr=mfPtr->next) { if (fileType == mfPtr->type) { macMatched = 1; break; } } if (globMatched && macMatched) { return MATCHED; } } return UNMATCHED; }