diff options
Diffstat (limited to 'macosx/tkMacOSXDialog.c')
-rw-r--r-- | macosx/tkMacOSXDialog.c | 498 |
1 files changed, 465 insertions, 33 deletions
diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c index 25883ce..074dca6 100644 --- a/macosx/tkMacOSXDialog.c +++ b/macosx/tkMacOSXDialog.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.4.2.1 2004/02/14 01:08:28 wolfsuit Exp $ + * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.4.2.2 2004/02/16 00:42:34 wolfsuit Exp $ */ #include <Carbon/Carbon.h> @@ -58,6 +58,18 @@ typedef struct _OpenFileData { } OpenFileData; +/* + * The following structure is used in the tk_messageBox + * implementation. + */ +typedef struct { + WindowRef windowRef; + int buttonIndex; +} CallbackUserData; + + +static OSStatus AlertHandler _ANSI_ARGS_(( EventHandlerCallRef callRef, + EventRef eventRef, void *userData )); static Boolean MatchOneType _ANSI_ARGS_((StringPtr fileNamePtr, OSType fileType, OpenFileData *myofdPtr, FileFilter *filterPtr)); static pascal Boolean OpenFileFilterProc(AEDesc* theItem, void* info, @@ -747,11 +759,11 @@ NavServicesGetFile( diagOptions.location.v = -1; diagOptions.optionFlags = kNavDontAutoTranslate + kNavDontAddTranslateItems; - if (multiple) { diagOptions.optionFlags += kNavAllowMultipleFiles; } - + diagOptions.modality = kWindowModalityAppModal; + if (ofdPtr != NULL && ofdPtr->usePopup) { FileFilter *filterPtr; @@ -775,7 +787,7 @@ NavServicesGetFile( filterPtr->name, encoding); } diagOptions.popupExtension = CFArrayCreate(NULL, - (const void **)menuItemNames, ofdPtr->fl.numFilters, NULL);; + (const void **) menuItemNames, ofdPtr->fl.numFilters, NULL); } else { diagOptions.optionFlags += kNavNoTypePopup; diagOptions.popupExtension = NULL; @@ -815,7 +827,7 @@ NavServicesGetFile( openFileFilterUPP, ofdPtr, &dialogRef); - if (err!=noErr){ + if (err != noErr) { fprintf(stderr,"NavCreateGetFileDialog failed, %d\n", err ); dialogRef = NULL; } @@ -857,7 +869,7 @@ NavServicesGetFile( * Most commands assume that the file dialogs return a single * item, not a list. So only build a list if multiple is true... */ - if (err==noErr) { + if (err == noErr) { if (multiple) { theResult = Tcl_NewListObj(0, NULL); } else { @@ -867,7 +879,7 @@ NavServicesGetFile( err = memFullErr; } } - if (theReply.validRecord && err==noErr) { + if (theReply.validRecord && err == noErr) { AEDesc resultDesc; long count; Tcl_DString fileName; @@ -962,7 +974,7 @@ NavServicesGetFile( if (menuItemNames) { int i; - for (i=0;i < ofdPtr->fl.numFilters;i++) { + for (i = 0;i < ofdPtr->fl.numFilters; i++) { CFRelease(menuItemNames[i]); } ckfree((void *)menuItemNames); @@ -981,30 +993,59 @@ OpenFileFilterProc( NavFilterModes filterMode ) { OpenFileData *ofdPtr = (OpenFileData *) callBackUD; + if (!ofdPtr || !ofdPtr->usePopup) { return true; } else { if (ofdPtr->fl.numFilters == 0) { return true; } else { - - if ( theItem->descriptorType == typeFSS ) { - NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*)info; + if ((theItem->descriptorType == typeFSS) + || (theItem->descriptorType = typeFSRef)) { + NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo *) info; + char fileName[256]; int result; if ( !theInfo->isFolder ) { OSType fileType; StringPtr fileNamePtr; + Tcl_DString fileNameDString; int i; FileFilter *filterPtr; - fileType = theInfo->fileAndFolder.fileInfo.finderInfo.fdType; - HLock((Handle)theItem->dataHandle); - fileNamePtr = (((FSSpec *) *theItem->dataHandle)->name); + fileType = + theInfo->fileAndFolder.fileInfo.finderInfo.fdType; + Tcl_DStringInit (&fileNameDString); + if (theItem->descriptorType == typeFSS) { + int len; + fileNamePtr = (((FSSpec *) *theItem->dataHandle)->name); + len = fileNamePtr[0]; + strncpy(fileName, fileNamePtr + 1, len); + fileName[len] = '\0'; + fileNamePtr = fileName; + + } else if (theItem->descriptorType = typeFSRef) { + OSStatus err; + FSRef *theRef = (FSRef *) *theItem->dataHandle; + HFSUniStr255 uniFileName; + err = FSGetCatalogInfo (theRef, kFSCatInfoNone, NULL, + &uniFileName, NULL, NULL); + + if (err == noErr) { + Tcl_UniCharToUtfDString ( + (Tcl_UniChar *) uniFileName.unicode, + uniFileName.length, + &fileNameDString); + fileNamePtr = Tcl_DStringValue(&fileNameDString); + } else { + fileNamePtr = NULL; + } + } if (ofdPtr->usePopup) { i = ofdPtr->curType; - for (filterPtr = ofdPtr->fl.filters; filterPtr && i > 0; i--) { + for (filterPtr = ofdPtr->fl.filters; + filterPtr && i > 0; i--) { filterPtr = filterPtr->next; } if (filterPtr) { @@ -1015,9 +1056,11 @@ OpenFileFilterProc( } } else { /* - * We are not using the popup menu. In this case, the file is - * considered matched if it matches any of the file filters. + * 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) { @@ -1029,7 +1072,7 @@ OpenFileFilterProc( } } - HUnlock((Handle)theItem->dataHandle); + Tcl_DStringFree (&fileNameDString); return (result == MATCHED); } else { return true; @@ -1086,7 +1129,7 @@ OpenEventProc( static Boolean MatchOneType( StringPtr fileNamePtr, /* Name of the file */ - OSType fileType, /* Type of the file */ + OSType fileType, /* Type of the file, 0 means there was no specified type. */ OpenFileData * ofdPtr, /* Information about this file dialog */ FileFilter * filterPtr) /* Match the file described by pb against * this filter */ @@ -1110,7 +1153,8 @@ MatchOneType( * considered an error. */ - for (clausePtr=filterPtr->clauses; clausePtr; clausePtr=clausePtr->next) { + for (clausePtr = filterPtr->clauses; clausePtr; + clausePtr = clausePtr->next) { int macMatched = 0; int globMatched = 0; GlobPattern * globPtr; @@ -1123,18 +1167,13 @@ MatchOneType( macMatched = 1; } - for (globPtr=clausePtr->patterns; globPtr; globPtr=globPtr->next) { - char filename[256]; - int len; - char * p, *q, *ext; + for (globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + char *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') { @@ -1142,7 +1181,8 @@ MatchOneType( * We don't want any extensions: OK if the filename doesn't * have "." in it */ - for (q=filename; *q; q++) { + + for (q = fileNamePtr; *q; q++) { if (*q == '.') { goto glob_unmatched; } @@ -1150,7 +1190,7 @@ MatchOneType( goto glob_matched; } - if (Tcl_StringMatch(filename, ext)) { + if (Tcl_StringMatch(fileNamePtr, ext)) { goto glob_matched; } else { goto glob_unmatched; @@ -1164,14 +1204,24 @@ MatchOneType( break; } - for (mfPtr=clausePtr->macTypes; mfPtr; mfPtr=mfPtr->next) { + for (mfPtr = clausePtr->macTypes; mfPtr; mfPtr = mfPtr->next) { if (fileType == mfPtr->type) { macMatched = 1; break; } } - if (globMatched && macMatched) { + /* + * On Mac OS X, it is not uncommon for files to have NO + * file type. But folks with Tcl code on Classic MacOS pretty + * much assume that a generic file will have type TEXT. So + * if we were strict about matching types when the source file + * had NO type set, they would have to add another rule always + * with no fileType. To avoid that, we pass the macMatch side + * of the test if no fileType is set. + */ + + if (globMatched && (macMatched || (fileType == 0))) { return MATCHED; } } @@ -1209,7 +1259,7 @@ TkAboutDlg() return; } - windowRef=GetDialogWindow(aboutDlog); + windowRef = GetDialogWindow(aboutDlog); SelectWindow(windowRef); while (itemHit != 1) { @@ -1222,3 +1272,385 @@ TkAboutDlg() return; } + +/* + *---------------------------------------------------------------------- + * + * Tk_MessageBoxObjCmd -- + * + * Implements the tk_messageBox in native Mac OS X style. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * none + * + *---------------------------------------------------------------------- + */ + +int +Tk_MessageBoxObjCmd( + 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 tkwin = (Tk_Window) clientData; + AlertStdCFStringAlertParamRec paramCFStringRec; + AlertType alertType; + DialogRef dialogRef; + CFStringRef messageTextCF = NULL; + CFStringRef finemessageTextCF = NULL; + OSErr osError; + SInt16 itemHit; + Boolean haveDefaultOption = false; + Boolean haveParentOption = false; + char *str; + int index; + int defaultButtonIndex; + int defaultNativeButtonIndex; /* 1, 2, 3: right to left. */ + int typeIndex; + int i; + int indexDefaultOption; + int result = TCL_OK; + + static CONST char *movableAlertStrings[] = { + "-default", /* "-finemessage", */ "-icon", + "-message", "-parent", + "-title", "-type", + (char *)NULL + }; + static CONST char *movableTypeStrings[] = { + "abortretryignore", "ok", + "okcancel", "retrycancel", + "yesno", "yesnocancel", + (char *)NULL + }; + static CONST char *movableButtonStrings[] = { + "abort", "retry", "ignore", + "ok", "cancel", "yes", "no", + (char *)NULL + }; + static CONST char *movableIconStrings[] = { + "error", "info", "question", "warning", + (char *)NULL + }; + enum movableAlertOptions { + ALERT_DEFAULT, /* ALERT_FINEMESSAGE, */ ALERT_ICON, + ALERT_MESSAGE, ALERT_PARENT, + ALERT_TITLE, ALERT_TYPE + }; + enum movableTypeOptions { + TYPE_ABORTRETRYIGNORE, TYPE_OK, + TYPE_OKCANCEL, TYPE_RETRYCANCEL, + TYPE_YESNO, TYPE_YESNOCANCEL + }; + enum movableButtonOptions { + TEXT_ABORT, TEXT_RETRY, TEXT_IGNORE, + TEXT_OK, TEXT_CANCEL, TEXT_YES, TEXT_NO + }; + enum movableIconOptions { + ICON_ERROR, ICON_INFO, ICON_QUESTION, ICON_WARNING + }; + + /* + * Need to map from 'movableButtonStrings' and its corresponding integer index, + * to the native button index, which is 1, 2, 3, from right to left. + * This is necessary to do for each separate '-type' of button sets. + */ + + short buttonIndexAndTypeToNativeButtonIndex[][7] = { + /* abort retry ignore ok cancel yes no */ + {1, 2, 3, 0, 0, 0, 0}, /* abortretryignore */ + {0, 0, 0, 1, 0, 0, 0}, /* ok */ + {0, 0, 0, 1, 2, 0, 0}, /* okcancel */ + {0, 1, 0, 0, 2, 0, 0}, /* retrycancel */ + {0, 0, 0, 0, 0, 1, 2}, /* yesno */ + {0, 0, 0, 0, 3, 1, 2}, /* yesnocancel */ + }; + + /* + * Need also the inverse mapping, from native button (1, 2, 3) to the + * descriptive button text string index. + */ + + short nativeButtonIndexAndTypeToButtonIndex[][4] = { + {-1, 0, 1, 2}, /* abortretryignore */ + {-1, 3, 0, 0}, /* ok */ + {-1, 3, 4, 0}, /* okcancel */ + {-1, 1, 4, 0}, /* retrycancel */ + {-1, 5, 6, 0}, /* yesno */ + {-1, 5, 6, 4}, /* yesnocancel */ + }; + + alertType = kAlertPlainAlert; + typeIndex = TYPE_OK; + + GetStandardAlertDefaultParams( ¶mCFStringRec, kStdCFStringAlertVersionOne ); + paramCFStringRec.movable = true; + paramCFStringRec.helpButton = false; + paramCFStringRec.defaultButton = kAlertStdAlertOKButton; + + for (i = 1; i < objc; i += 2) { + int iconIndex; + char *string; + + if (Tcl_GetIndexFromObj( interp, objv[i], movableAlertStrings, "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 ALERT_DEFAULT: + + /* + * Need to postpone processing of this option until we are + * sure to know the '-type' as well. + */ + + haveDefaultOption = true; + indexDefaultOption = i; + break; + +/* case ALERT_FINEMESSAGE: + str = Tcl_GetStringFromObj( objv[i + 1], NULL ); + finemessageTextCF = CFStringCreateWithCString( NULL, str, kCFStringEncodingUTF8 ); + break; +*/ + case ALERT_ICON: + /* not sure about UTF translation here... */ + if (Tcl_GetIndexFromObj( interp, objv[i + 1], movableIconStrings, + "value", TCL_EXACT, &iconIndex ) != TCL_OK) { + result = TCL_ERROR; + goto end; + } + switch (iconIndex) { + case ICON_ERROR: + alertType = kAlertStopAlert; + break; + case ICON_INFO: + alertType = kAlertNoteAlert; + break; + case ICON_QUESTION: + alertType = kAlertCautionAlert; + break; + case ICON_WARNING: + alertType = kAlertCautionAlert; + break; + } + break; + + case ALERT_MESSAGE: + str = Tcl_GetStringFromObj( objv[i + 1], NULL ); + messageTextCF = CFStringCreateWithCString( NULL, str, kCFStringEncodingUTF8 ); + break; + + case ALERT_PARENT: + str = Tcl_GetStringFromObj( objv[i + 1], NULL ); + tkwin = Tk_NameToWindow( interp, str, tkwin ); + if (tkwin == NULL) { + result = TCL_ERROR; + goto end; + } + haveParentOption = true; + break; + + case ALERT_TITLE: + break; + + case ALERT_TYPE: + /* not sure about UTF translation here... */ + if (Tcl_GetIndexFromObj( interp, objv[i + 1], movableTypeStrings, + "value", TCL_EXACT, &typeIndex ) != TCL_OK) { + result = TCL_ERROR; + goto end; + } + switch (typeIndex) { + case TYPE_ABORTRETRYIGNORE: + paramCFStringRec.defaultText = CFSTR("Abort"); + paramCFStringRec.cancelText = CFSTR("Retry"); + paramCFStringRec.otherText = CFSTR("Ignore"); + break; + case TYPE_OK: + paramCFStringRec.defaultText = CFSTR("OK"); + break; + case TYPE_OKCANCEL: + paramCFStringRec.defaultText = CFSTR("OK"); + paramCFStringRec.cancelText = CFSTR("Cancel"); + break; + case TYPE_RETRYCANCEL: + paramCFStringRec.defaultText = CFSTR("Retry"); + paramCFStringRec.cancelText = CFSTR("Cancel"); + break; + case TYPE_YESNO: + paramCFStringRec.defaultText = CFSTR("Yes"); + paramCFStringRec.cancelText = CFSTR("No"); + break; + case TYPE_YESNOCANCEL: + paramCFStringRec.defaultText = CFSTR("Yes"); + paramCFStringRec.cancelText = CFSTR("No"); + paramCFStringRec.otherText = CFSTR("Cancel"); + break; + } + break; + } + } + + if (haveDefaultOption) { + + /* + * Any '-default' option needs to know the '-type' option, which is why + * we do this here. + */ + + str = Tcl_GetStringFromObj( objv[indexDefaultOption + 1], NULL ); + if (Tcl_GetIndexFromObj( interp, objv[indexDefaultOption + 1], + movableButtonStrings, "value", TCL_EXACT, + &defaultButtonIndex ) != TCL_OK) { + result = TCL_ERROR; + goto end; + } + + /* Need to map from "ok" etc. to 1, 2, 3, right to left. */ + + defaultNativeButtonIndex = + buttonIndexAndTypeToNativeButtonIndex[typeIndex][defaultButtonIndex]; + if (defaultNativeButtonIndex == 0) { + Tcl_SetObjResult( interp, + Tcl_NewStringObj( "Illegel default option", -1 )); + result = TCL_ERROR; + goto end; + } + paramCFStringRec.defaultButton = defaultNativeButtonIndex; + } + SetThemeCursor( kThemeArrowCursor ); + + if (haveParentOption) { + TkWindow *winPtr; + MacDrawable *macWin; + WindowRef windowRef; + EventTargetRef notifyTarget; + EventHandlerUPP handler; + CallbackUserData data; + const EventTypeSpec kEvents[] = { + {kEventClassCommand, kEventProcessCommand} + }; + + winPtr = (TkWindow *) tkwin; + + /* + * Create the underlying Mac window for this Tk window. + */ + + macWin = (MacDrawable *) winPtr->window; + windowRef = GetWindowFromPort( TkMacOSXGetDrawablePort((Drawable) macWin) ); + notifyTarget = GetWindowEventTarget( windowRef ); + osError = CreateStandardSheet( alertType, messageTextCF, + finemessageTextCF, ¶mCFStringRec, + notifyTarget, &dialogRef ); + if(osError != noErr) { + result = TCL_ERROR; + goto end; + } + data.windowRef = windowRef; + data.buttonIndex = 1; + handler = NewEventHandlerUPP( AlertHandler ); + InstallEventHandler(notifyTarget, handler, + GetEventTypeCount(kEvents), + kEvents, &data, NULL ); + osError = ShowSheetWindow( GetDialogWindow(dialogRef), windowRef ); + if(osError != noErr) { + result = TCL_ERROR; + goto end; + } + osError = RunAppModalLoopForWindow( windowRef ); + + itemHit = data.buttonIndex; + DisposeEventHandlerUPP( handler ); + } else { + osError = CreateStandardAlert( alertType, messageTextCF, + finemessageTextCF, ¶mCFStringRec, &dialogRef ); + if(osError != noErr) { + result = TCL_ERROR; + goto end; + } + osError = RunStandardAlert( dialogRef, NULL, &itemHit ); + } + if(osError == noErr) { + int ind; + + /* + * Map 'itemHit' (1, 2, 3) to descriptive text string. + */ + + ind = nativeButtonIndexAndTypeToButtonIndex[typeIndex][itemHit]; + Tcl_SetObjResult( interp, + Tcl_NewStringObj( movableButtonStrings[ind], -1 )); + } else { + result = TCL_ERROR; + } + + end: + if (finemessageTextCF != NULL) { + CFRelease( finemessageTextCF ); + } + if (messageTextCF != NULL) { + CFRelease( messageTextCF ); + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * AlertHandler -- + * + * Carbon event handler for the Standard Sheet dialog. + * + * Results: + * OSStatus if event handled or not. + * + * Side effects: + * May set userData. + * + *---------------------------------------------------------------------- + */ + +static OSStatus +AlertHandler( EventHandlerCallRef callRef, EventRef eventRef, void *userData ) +{ + OSStatus result = eventNotHandledErr; + HICommand cmd; + CallbackUserData *dataPtr = (CallbackUserData *) userData; + + GetEventParameter( eventRef, kEventParamDirectObject, typeHICommand, + NULL, sizeof(cmd), NULL, &cmd ); + switch (cmd.commandID) { + case kHICommandOK: + dataPtr->buttonIndex = 1; + result = noErr; + break; + case kHICommandCancel: + dataPtr->buttonIndex = 2; + result = noErr; + break; + case kHICommandOther: + dataPtr->buttonIndex = 3; + result = noErr; + break; + } + if (result == noErr) { + result = QuitAppModalLoopForWindow( dataPtr->windowRef ); + } + return result; +} |