diff options
Diffstat (limited to 'macosx/tkMacOSXDialog.c')
-rw-r--r-- | macosx/tkMacOSXDialog.c | 2475 |
1 files changed, 1191 insertions, 1284 deletions
diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c index 8a51dc2..8d07996 100644 --- a/macosx/tkMacOSXDialog.c +++ b/macosx/tkMacOSXDialog.c @@ -4,107 +4,224 @@ * Contains the Mac implementation of the common dialog boxes. * * Copyright (c) 1996-1997 Sun Microsystems, Inc. - * Copyright 2001, Apple Computer, Inc. - * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * 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.36.2.6 2009/10/22 10:27:58 dkf Exp $ + * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.49 2010/06/15 12:40:07 nijtmans Exp $ */ #include "tkMacOSXPrivate.h" #include "tkFileFilter.h" -#ifndef StrLength -#define StrLength(s) (*((unsigned char *) (s))) -#endif -#ifndef StrBody -#define StrBody(s) ((char *) (s) + 1) -#endif - -#define OPEN_POPUP_ITEM 10 - -#define SAVE_FILE 0 -#define OPEN_FILE 1 -#define CHOOSE_FOLDER 2 - -#define MATCHED 0 -#define UNMATCHED 1 - -#define TK_DEFAULT_ABOUT 128 +static const char *const colorOptionStrings[] = { + "-initialcolor", "-parent", "-title", NULL +}; +enum colorOptions { + COLOR_INITIAL, COLOR_PARENT, COLOR_TITLE +}; + +static const char *const openOptionStrings[] = { + "-defaultextension", "-filetypes", "-initialdir", "-initialfile", + "-message", "-multiple", "-parent", "-title", "-typevariable", + "-command", NULL +}; +enum openOptions { + OPEN_DEFAULT, OPEN_FILETYPES, OPEN_INITDIR, OPEN_INITFILE, + OPEN_MESSAGE, OPEN_MULTIPLE, OPEN_PARENT, OPEN_TITLE, + OPEN_TYPEVARIABLE, OPEN_COMMAND, +}; +static const char *const saveOptionStrings[] = { + "-defaultextension", "-filetypes", "-initialdir", "-initialfile", + "-message", "-parent", "-title", "-typevariable", "-command", NULL +}; +enum saveOptions { + SAVE_DEFAULT, SAVE_FILETYPES, SAVE_INITDIR, SAVE_INITFILE, + SAVE_MESSAGE, SAVE_PARENT, SAVE_TITLE, SAVE_TYPEVARIABLE, SAVE_COMMAND, +}; +static const char *const chooseOptionStrings[] = { + "-initialdir", "-message", "-mustexist", "-parent", "-title", "-command", + NULL +}; +enum chooseOptions { + CHOOSE_INITDIR, CHOOSE_MESSAGE, CHOOSE_MUSTEXIST, CHOOSE_PARENT, + CHOOSE_TITLE, CHOOSE_COMMAND, +}; +typedef struct { + Tcl_Interp *interp; + Tcl_Obj *cmdObj; + int multiple; +} FilePanelCallbackInfo; + +static const char *const alertOptionStrings[] = { + "-default", "-detail", "-icon", "-message", "-parent", "-title", + "-type", "-command", NULL +}; +enum alertOptions { + ALERT_DEFAULT, ALERT_DETAIL, ALERT_ICON, ALERT_MESSAGE, ALERT_PARENT, + ALERT_TITLE, ALERT_TYPE, ALERT_COMMAND, +}; +typedef struct { + Tcl_Interp *interp; + Tcl_Obj *cmdObj; + int typeIndex; +} AlertCallbackInfo; +static const char *const alertTypeStrings[] = { + "abortretryignore", "ok", "okcancel", "retrycancel", "yesno", + "yesnocancel", NULL +}; +enum alertTypeOptions { + TYPE_ABORTRETRYIGNORE, TYPE_OK, TYPE_OKCANCEL, TYPE_RETRYCANCEL, + TYPE_YESNO, TYPE_YESNOCANCEL +}; +static const char *const alertIconStrings[] = { + "error", "info", "question", "warning", NULL +}; +enum alertIconOptions { + ICON_ERROR, ICON_INFO, ICON_QUESTION, ICON_WARNING +}; +static const char *const alertButtonStrings[] = { + "abort", "retry", "ignore", "ok", "cancel", "yes", "no", NULL +}; + +static const NSString *const alertButtonNames[][3] = { + [TYPE_ABORTRETRYIGNORE] = {@"Abort", @"Retry", @"Ignore"}, + [TYPE_OK] = {@"OK"}, + [TYPE_OKCANCEL] = {@"OK", @"Cancel"}, + [TYPE_RETRYCANCEL] = {@"Retry", @"Cancel"}, + [TYPE_YESNO] = {@"Yes", @"No"}, + [TYPE_YESNOCANCEL] = {@"Yes", @"No", @"Cancel"}, +}; +static const NSAlertStyle alertStyles[] = { + [ICON_ERROR] = NSWarningAlertStyle, + [ICON_INFO] = NSInformationalAlertStyle, + [ICON_QUESTION] = NSWarningAlertStyle, + [ICON_WARNING] = NSCriticalAlertStyle, +}; /* - * The following structures are used in the GetFileName() function. They store - * information about the file dialog and the file filters. + * Need to map from 'alertButtonStrings' 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. */ -typedef struct _OpenFileData { - FileFilterList fl; /* List of file filters. */ - SInt16 curType; /* The filetype currently being listed. */ - short initialType; /* Type to use initially */ - short popupItem; /* Item number of the popup in the dialog. */ - short usePopup; /* True if we show the popup menu (this */ - /* is an open operation and the */ - /* -filetypes option is set). */ -} OpenFileData; - -typedef struct NavHandlerUserData { - OpenFileData *ofdPtr; - NavReplyRecord reply; - OSStatus err; - CFStringRef saveNameRef; - int sheet; - WindowRef dialogWindow, origUnavailWindow; - WindowModality origModality; -} NavHandlerUserData; +static const short alertButtonIndexAndTypeToNativeButtonIndex[][7] = { + /* abort retry ignore ok cancel yes no */ + [TYPE_ABORTRETRYIGNORE] = {1, 2, 3, 0, 0, 0, 0}, + [TYPE_OK] = {0, 0, 0, 1, 0, 0, 0}, + [TYPE_OKCANCEL] = {0, 0, 0, 1, 2, 0, 0}, + [TYPE_RETRYCANCEL] = {0, 1, 0, 0, 2, 0, 0}, + [TYPE_YESNO] = {0, 0, 0, 0, 0, 1, 2}, + [TYPE_YESNOCANCEL] = {0, 0, 0, 0, 3, 1, 2}, +}; /* - * The following structure is used in the tk_messageBox implementation. + * Need also the inverse mapping, from NSAlertFirstButtonReturn etc to the + * descriptive button text string index. */ -typedef struct { - int buttonIndex; - WindowRef dialogWindow, origUnavailWindow; - WindowModality origModality; - EventHandlerRef handlerRef; -} AlertHandlerUserData; - - -static OSStatus AlertHandler(EventHandlerCallRef callRef, - EventRef eventRef, void *userData); -static Boolean MatchOneType(StringPtr fileNamePtr, OSType fileType, - OpenFileData *myofdPtr, FileFilter *filterPtr); -static pascal Boolean OpenFileFilterProc(AEDesc* theItem, void* info, - NavCallBackUserData callBackUD, - NavFilterModes filterMode); -static pascal void OpenEventProc(NavEventCallbackMessage callBackSelector, - NavCBRecPtr callBackParms, - NavCallBackUserData callBackUD); -static void InitFileDialogs(void); -static int NavServicesGetFile(Tcl_Interp *interp, - OpenFileData *ofd, AEDesc *initialDescPtr, - char *initialFile, AEDescList *selectDescPtr, - CFStringRef title, CFStringRef message, - const char *initialType, int multiple, int isOpen, - Tk_Window parent); -static int HandleInitialDirectory(Tcl_Interp *interp, - char *initialFile, char *initialDir, FSRef *dirRef, - AEDescList *selectDescPtr, AEDesc *dirDescPtr); +static const short alertNativeButtonIndexAndTypeToButtonIndex[][3] = { + [TYPE_ABORTRETRYIGNORE] = {0, 1, 2}, + [TYPE_OK] = {3, 0, 0}, + [TYPE_OKCANCEL] = {3, 4, 0}, + [TYPE_RETRYCANCEL] = {1, 4, 0}, + [TYPE_YESNO] = {5, 6, 0}, + [TYPE_YESNOCANCEL] = {5, 6, 4}, +}; + +#pragma mark TKApplication(TKDialog) -/* - * Have we initialized the file dialog subsystem - */ +@interface NSColorPanel(TKDialog) +- (void) _setUseModalAppearance: (BOOL) flag; +@end -static int fileDlgInited = 0; +@implementation TKApplication(TKDialog) -/* - * Filter and hook functions used by the tk_getOpenFile and tk_getSaveFile - * commands. - */ +- (void) tkFilePanelDidEnd: (NSSavePanel *) panel + returnCode: (NSInteger) returnCode contextInfo: (void *) contextInfo +{ + FilePanelCallbackInfo *callbackInfo = contextInfo; + + if (returnCode == NSFileHandlingPanelOKButton) { + Tcl_Obj *resultObj; + + if (callbackInfo->multiple) { + resultObj = Tcl_NewListObj(0, NULL); + for (NSString *name in [(NSOpenPanel*)panel filenames]) { + Tcl_ListObjAppendElement(callbackInfo->interp, resultObj, + Tcl_NewStringObj([name UTF8String], -1)); + } + } else { + resultObj = Tcl_NewStringObj([[panel filename] UTF8String], -1); + } + if (callbackInfo->cmdObj) { + Tcl_Obj **objv, **tmpv; + int objc, result = Tcl_ListObjGetElements(callbackInfo->interp, + callbackInfo->cmdObj, &objc, &objv); + + if (result == TCL_OK && objc) { + tmpv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (objc + 2)); + memcpy(tmpv, objv, sizeof(Tcl_Obj *) * objc); + tmpv[objc] = resultObj; + TkBackgroundEvalObjv(callbackInfo->interp, objc + 1, tmpv, + TCL_EVAL_GLOBAL); + ckfree((char *)tmpv); + } + } else { + Tcl_SetObjResult(callbackInfo->interp, resultObj); + } + } else if (returnCode == NSFileHandlingPanelCancelButton) { + Tcl_ResetResult(callbackInfo->interp); + } + if (panel == [NSApp modalWindow]) { + [NSApp stopModalWithCode:returnCode]; + } + if (callbackInfo->cmdObj) { + Tcl_DecrRefCount(callbackInfo->cmdObj); + ckfree((char*) callbackInfo); + } +} + +- (void) tkAlertDidEnd: (NSAlert *) alert returnCode: (NSInteger) returnCode + contextInfo: (void *) contextInfo +{ + AlertCallbackInfo *callbackInfo = contextInfo; + + if (returnCode != NSAlertErrorReturn) { + Tcl_Obj *resultObj = Tcl_NewStringObj(alertButtonStrings[ + alertNativeButtonIndexAndTypeToButtonIndex[callbackInfo-> + typeIndex][returnCode - NSAlertFirstButtonReturn]], -1); + + if (callbackInfo->cmdObj) { + Tcl_Obj **objv, **tmpv; + int objc, result = Tcl_ListObjGetElements(callbackInfo->interp, + callbackInfo->cmdObj, &objc, &objv); + + if (result == TCL_OK && objc) { + tmpv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (objc + 2)); + memcpy(tmpv, objv, sizeof(Tcl_Obj *) * objc); + tmpv[objc] = resultObj; + TkBackgroundEvalObjv(callbackInfo->interp, objc + 1, tmpv, + TCL_EVAL_GLOBAL); + ckfree((char *)tmpv); + } + } else { + Tcl_SetObjResult(callbackInfo->interp, resultObj); + } + } + if ([alert window] == [NSApp modalWindow]) { + [NSApp stopModalWithCode:returnCode]; + } + if (callbackInfo->cmdObj) { + Tcl_DecrRefCount(callbackInfo->cmdObj); + ckfree((char*) callbackInfo); + } +} +@end -static NavObjectFilterUPP openFileFilterUPP; -static NavEventUPP openFileEventUPP; +#pragma mark - /* *---------------------------------------------------------------------- @@ -128,35 +245,21 @@ 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. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - OSStatus err; int result = TCL_ERROR; Tk_Window parent, tkwin = clientData; - const char *title; - int i, srcRead, dstWrote; - CMError cmerr; - CMProfileRef prof; - NColorPickerInfo cpinfo; - static RGBColor color = {0xffff, 0xffff, 0xffff}; - static const char *optionStrings[] = { - "-initialcolor", "-parent", "-title", NULL - }; - enum options { - COLOR_INITIAL, COLOR_PARENT, COLOR_TITLE - }; - - title = "Choose a color:"; - bzero(&cpinfo, sizeof(cpinfo)); - cpinfo.theColor.color.rgb.red = color.red; - cpinfo.theColor.color.rgb.green = color.green; - cpinfo.theColor.color.rgb.blue = color.blue; + const char *title = NULL; + int i; + NSColor *color = nil, *initialColor = nil; + NSColorPanel *colorPanel; + NSInteger returnCode, numberOfComponents = 0; for (i = 1; i < objc; i += 2) { int index; const char *option, *value; - if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option", + if (Tcl_GetIndexFromObj(interp, objv[i], colorOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { goto end; } @@ -168,63 +271,65 @@ Tk_ChooseColorObjCmd( } value = Tcl_GetString(objv[i + 1]); - switch ((enum options) index) { - case COLOR_INITIAL: { - XColor *colorPtr; + switch (index) { + case COLOR_INITIAL: { + XColor *colorPtr; - colorPtr = Tk_GetColor(interp, tkwin, value); - if (colorPtr == NULL) { - goto end; - } - cpinfo.theColor.color.rgb.red = colorPtr->red; - cpinfo.theColor.color.rgb.green = colorPtr->green; - cpinfo.theColor.color.rgb.blue = colorPtr->blue; - Tk_FreeColor(colorPtr); - break; - } - case COLOR_PARENT: { - parent = Tk_NameToWindow(interp, value, tkwin); - if (parent == NULL) { - goto end; - } - break; + colorPtr = Tk_GetColor(interp, tkwin, value); + if (colorPtr == NULL) { + goto end; } - case COLOR_TITLE: { - title = value; - break; + initialColor = TkMacOSXGetNSColor(NULL, colorPtr->pixel); + Tk_FreeColor(colorPtr); + break; + } + case COLOR_PARENT: + parent = Tk_NameToWindow(interp, value, tkwin); + if (parent == NULL) { + goto end; } + break; + case COLOR_TITLE: + title = value; + break; } } - - cmerr = CMGetDefaultProfileBySpace(cmRGBData, &prof); - cpinfo.theColor.profile = prof; - cpinfo.dstProfile = prof; - cpinfo.flags = kColorPickerDialogIsMoveable | kColorPickerDialogIsModal; - cpinfo.placeWhere = kCenterOnMainScreen; - /* Currently, this does not actually change the colorpicker title */ - Tcl_UtfToExternal(NULL, TkMacOSXCarbonEncoding, title, -1, 0, NULL, - StrBody(cpinfo.prompt), 255, &srcRead, &dstWrote, NULL); - StrLength(cpinfo.prompt) = (unsigned char) dstWrote; - - TkMacOSXTrackingLoop(1); - err = ChkErr(NPickColor, &cpinfo); - TkMacOSXTrackingLoop(0); - cmerr = CMCloseProfile(prof); - if ((err == noErr) && (cpinfo.newColorChosen != 0)) { + colorPanel = [NSColorPanel sharedColorPanel]; + [colorPanel orderOut:NSApp]; + [colorPanel setContinuous:NO]; + [colorPanel setBecomesKeyOnlyIfNeeded:NO]; + [colorPanel setShowsAlpha: NO]; + [colorPanel _setUseModalAppearance:YES]; + if (title) { + NSString *s = [[NSString alloc] initWithUTF8String:title]; + [colorPanel setTitle:s]; + [s release]; + } + if (initialColor) { + [colorPanel setColor:initialColor]; + } + returnCode = [NSApp runModalForWindow:colorPanel]; + if (returnCode == NSOKButton) { + color = [[colorPanel color] colorUsingColorSpace: + [NSColorSpace genericRGBColorSpace]]; + numberOfComponents = [color numberOfComponents]; + } + if (color && numberOfComponents >= 3 && numberOfComponents <= 4) { + CGFloat components[4]; char colorstr[8]; - color.red = cpinfo.theColor.color.rgb.red; - color.green = cpinfo.theColor.color.rgb.green; - color.blue = cpinfo.theColor.color.rgb.blue; - snprintf(colorstr, 8, "#%02x%02x%02x", color.red >> 8, - color.green >> 8, color.blue >> 8); + [color getComponents:components]; + snprintf(colorstr, 8, "#%02x%02x%02x", + (short)(components[0] * 255), + (short)(components[1] * 255), + (short)(components[2] * 255)); Tcl_SetObjResult(interp, Tcl_NewStringObj(colorstr, 7)); } else { Tcl_ResetResult(interp); } result = TCL_OK; - end: +end: return result; } @@ -249,85 +354,61 @@ 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. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - int i, result = TCL_ERROR, multiple = 0; - OpenFileData ofd; - Tk_Window parent = NULL; - CFStringRef message = NULL, title = NULL; - AEDesc initialDesc = {typeNull, NULL}; - FSRef dirRef; - AEDesc *initialPtr = NULL; - AEDescList selectDesc = {typeNull, NULL}; - char *initialFile = NULL, *initialDir = NULL; - Tcl_Obj *typeVariablePtr = NULL; - const char *initialtype = NULL; - static const char *openOptionStrings[] = { - "-defaultextension", "-filetypes", "-initialdir", "-initialfile", - "-message", "-multiple", "-parent", "-title", "-typevariable", NULL - }; - enum openOptions { - OPEN_DEFAULT, OPEN_FILETYPES, OPEN_INITDIR, OPEN_INITFILE, - OPEN_MESSAGE, OPEN_MULTIPLE, OPEN_PARENT, OPEN_TITLE, - OPEN_TYPEVARIABLE, - }; - - if (!fileDlgInited) { - InitFileDialogs(); - } - TkInitFileFilters(&ofd.fl); - ofd.curType = 0; - ofd.initialType = -1; - ofd.popupItem = OPEN_POPUP_ITEM; - ofd.usePopup = 1; - + Tk_Window tkwin = clientData; + char *str; + int i, result = TCL_ERROR, haveParentOption = 0; + int index, len, multiple = 0; + FileFilterList fl; + Tcl_Obj *cmdObj = NULL, *typeVariablePtr = NULL; + FilePanelCallbackInfo callbackInfoStruct; + FilePanelCallbackInfo *callbackInfo = &callbackInfoStruct; + NSString *directory = nil, *filename = nil; + NSString *message, *title, *type; + NSWindow *parent; + NSMutableArray *fileTypes = nil; + NSOpenPanel *panel = [NSOpenPanel openPanel]; + NSInteger returnCode = NSAlertErrorReturn; + + TkInitFileFilters(&fl); for (i = 1; i < objc; i += 2) { - char *choice; - int index, choiceLen; - char *string; - Tcl_Obj *types; - if (Tcl_GetIndexFromObj(interp, objv[i], openOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { goto end; } if (i + 1 == objc) { - string = Tcl_GetString(objv[i]); - Tcl_AppendResult(interp, "value for \"", string, "\" missing", - NULL); + str = Tcl_GetString(objv[i]); + Tcl_AppendResult(interp, "value for \"", str, "\" missing", NULL); goto end; } - switch (index) { case OPEN_DEFAULT: break; case OPEN_FILETYPES: - types = objv[i + 1]; - if (TkGetFileFilters(interp, &ofd.fl, types, 0) != TCL_OK) { + if (TkGetFileFilters(interp, &fl, objv[i + 1], 0) != TCL_OK) { goto end; } break; case OPEN_INITDIR: - initialDir = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - /* empty strings should be like no selection given */ - if (choiceLen == 0) { - initialDir = NULL; + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + directory = [[[NSString alloc] initWithUTF8String:str] + autorelease]; } break; case OPEN_INITFILE: - initialFile = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - /* empty strings should be like no selection given */ - if (choiceLen == 0) { - initialFile = NULL; + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + filename = [[[NSString alloc] initWithUTF8String:str] + autorelease]; } break; case OPEN_MESSAGE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (message) { - CFRelease(message); - } - message = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setMessage:message]; + [message release]; break; case OPEN_MULTIPLE: if (Tcl_GetBooleanFromObj(interp, objv[i + 1], @@ -336,68 +417,97 @@ Tk_GetOpenFileObjCmd( } break; case OPEN_PARENT: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - parent = Tk_NameToWindow(interp, choice, clientData); - if (parent == NULL) { + str = Tcl_GetStringFromObj(objv[i + 1], &len); + tkwin = Tk_NameToWindow(interp, str, tkwin); + if (!tkwin) { goto end; } + haveParentOption = 1; break; case OPEN_TITLE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (title) { - CFRelease(title); - } - title = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); + title = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setTitle:title]; + [title release]; break; case OPEN_TYPEVARIABLE: typeVariablePtr = objv[i + 1]; break; + case OPEN_COMMAND: + cmdObj = objv[i+1]; + break; } } - - if (HandleInitialDirectory(interp, initialFile, initialDir, &dirRef, - &selectDesc, &initialDesc) != TCL_OK) { - goto end; + if (fl.filters) { + fileTypes = [NSMutableArray array]; + for (FileFilter *filterPtr = fl.filters; filterPtr; + filterPtr = filterPtr->next) { + for (FileFilterClause *clausePtr = filterPtr->clauses; clausePtr; + clausePtr = clausePtr->next) { + for (GlobPattern *globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + str = globPtr->pattern; + while (*str && (*str == '*' || *str == '.')) { + str++; + } + if (*str) { + type = [[NSString alloc] initWithUTF8String:str]; + if (![fileTypes containsObject:type]) { + [fileTypes addObject:type]; + } + [type release]; + } + } + for (MacFileType *mfPtr = clausePtr->macTypes; mfPtr; + mfPtr = mfPtr->next) { + if (mfPtr->type) { + type = NSFileTypeForHFSTypeCode(mfPtr->type); + if (![fileTypes containsObject:type]) { + [fileTypes addObject:type]; + } + } + } + } + } } - if (initialDesc.descriptorType == typeFSRef) { - initialPtr = &initialDesc; + [panel setAllowsMultipleSelection:multiple]; + if (cmdObj) { + callbackInfo = (FilePanelCallbackInfo *) + ckalloc(sizeof(FilePanelCallbackInfo)); + if (Tcl_IsShared(cmdObj)) { + cmdObj = Tcl_DuplicateObj(cmdObj); + } + Tcl_IncrRefCount(cmdObj); } - if (typeVariablePtr) { - initialtype = Tcl_GetVar(interp, Tcl_GetString(typeVariablePtr), - TCL_GLOBAL_ONLY); + callbackInfo->cmdObj = cmdObj; + callbackInfo->interp = interp; + callbackInfo->multiple = multiple; + parent = TkMacOSXDrawableWindow(((TkWindow *) tkwin)->window); + if (haveParentOption && parent && ![parent attachedSheet]) { + [panel beginSheetForDirectory:directory file:filename types:fileTypes + modalForWindow:parent modalDelegate:NSApp didEndSelector: + @selector(tkFilePanelDidEnd:returnCode:contextInfo:) + contextInfo:callbackInfo]; + returnCode = cmdObj ? NSAlertOtherReturn : + [NSApp runModalForWindow:panel]; + } else { + returnCode = [panel runModalForDirectory:directory file:filename + types:fileTypes]; + [NSApp tkFilePanelDidEnd:panel returnCode:returnCode + contextInfo:callbackInfo]; } - result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, &selectDesc, - title, message, initialtype, multiple, OPEN_FILE, parent); - - if (typeVariablePtr) { - FileFilter *filterPtr = ofd.fl.filters; - int i = ofd.curType; + result = (returnCode != NSAlertErrorReturn) ? TCL_OK : TCL_ERROR; + if (typeVariablePtr && result == TCL_OK) { + /* + * The -typevariable option is not really supported. + */ - while (filterPtr && i-- > 0) { - filterPtr = filterPtr->next; - } - if (Tcl_SetVar(interp, Tcl_GetString(typeVariablePtr), - filterPtr ? filterPtr->name : "", - TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { - result = TCL_ERROR; - } + Tcl_SetVar(interp, Tcl_GetString(typeVariablePtr), "", + TCL_GLOBAL_ONLY); } end: - TkFreeFileFilters(&ofd.fl); - if (initialDesc.dataHandle) { - ChkErr(AEDisposeDesc, &initialDesc); - } - if (selectDesc.dataHandle) { - ChkErr(AEDisposeDesc, &selectDesc); - } - if (title) { - CFRelease(title); - } - if (message) { - CFRelease(message); - } + TkFreeFileFilters(&fl); return result; } @@ -406,7 +516,8 @@ Tk_GetOpenFileObjCmd( * * Tk_GetSaveFileObjCmd -- * - * Same as Tk_GetOpenFileCmd but opens a "save file" dialog box instead. + * This procedure implements the "save file" dialog box for the Mac + * platform. See the user documentation for details on what it does. * * Results: * A standard Tcl result. @@ -421,114 +532,148 @@ 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. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - int i, result = TCL_ERROR; - char *initialFile = NULL; - Tk_Window parent = NULL; - AEDesc initialDesc = {typeNull, NULL}; - AEDesc *initialPtr = NULL; - FSRef dirRef; - CFStringRef title = NULL, message = NULL; - OpenFileData ofd; - static const char *saveOptionStrings[] = { - "-defaultextension", "-filetypes", "-initialdir", "-initialfile", - "-message", "-parent", "-title", "-typevariable", NULL - }; - enum saveOptions { - SAVE_DEFAULT, SAVE_FILETYPES, SAVE_INITDIR, SAVE_INITFILE, - SAVE_MESSAGE, SAVE_PARENT, SAVE_TITLE, SAVE_TYPEVARIABLE, - }; - - if (!fileDlgInited) { - InitFileDialogs(); - } - - TkInitFileFilters(&ofd.fl); - ofd.curType = 0; - ofd.usePopup = 0; - + Tk_Window tkwin = clientData; + char *str; + int i, result = TCL_ERROR, haveParentOption = 0; + int index, len; + FileFilterList fl; + Tcl_Obj *cmdObj = NULL; + FilePanelCallbackInfo callbackInfoStruct; + FilePanelCallbackInfo *callbackInfo = &callbackInfoStruct; + NSString *directory = nil, *filename = nil, *defaultType = nil; + NSString *message, *title, *type; + NSWindow *parent; + NSMutableArray *fileTypes = nil; + NSSavePanel *panel = [NSSavePanel savePanel]; + NSInteger returnCode = NSAlertErrorReturn; + + TkInitFileFilters(&fl); for (i = 1; i < objc; i += 2) { - char *choice, *string; - int index, choiceLen; - Tcl_Obj *types; - if (Tcl_GetIndexFromObj(interp, objv[i], saveOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { goto end; } if (i + 1 == objc) { - string = Tcl_GetString(objv[i]); - Tcl_AppendResult(interp, "value for \"", string, "\" missing", + str = Tcl_GetString(objv[i]); + Tcl_AppendResult(interp, "value for \"", str, "\" missing", NULL); goto end; } switch (index) { case SAVE_DEFAULT: + str = Tcl_GetStringFromObj(objv[i + 1], &len); + while (*str && (*str == '*' || *str == '.')) { + str++; + } + if (*str) { + defaultType = [[[NSString alloc] initWithUTF8String:str] + autorelease]; + } break; case SAVE_FILETYPES: - types = objv[i + 1]; - if (TkGetFileFilters(interp, &ofd.fl, types, 0) != TCL_OK) { + if (TkGetFileFilters(interp, &fl, objv[i + 1], 0) != TCL_OK) { goto end; } break; case SAVE_INITDIR: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - /* empty strings should be like no selection given */ - if (choiceLen && HandleInitialDirectory(interp, NULL, choice, - &dirRef, NULL, &initialDesc) != TCL_OK) { - goto end; + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + directory = [[[NSString alloc] initWithUTF8String:str] + autorelease]; } break; case SAVE_INITFILE: - initialFile = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - /* empty strings should be like no selection given */ - if (choiceLen == 0) { - initialFile = NULL; + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + filename = [[[NSString alloc] initWithUTF8String:str] + autorelease]; } break; case SAVE_MESSAGE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (message) { - CFRelease(message); - } - message = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setMessage:message]; + [message release]; break; case SAVE_PARENT: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - parent = Tk_NameToWindow(interp, choice, (Tk_Window) clientData); - if (parent == NULL) { + str = Tcl_GetStringFromObj(objv[i + 1], &len); + tkwin = Tk_NameToWindow(interp, str, tkwin); + if (!tkwin) { goto end; } + haveParentOption = 1; break; case SAVE_TITLE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (title) { - CFRelease(title); - } - title = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); + title = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setTitle:title]; + [title release]; + break; + case SAVE_TYPEVARIABLE: + break; + case SAVE_COMMAND: + cmdObj = objv[i+1]; break; } } - - if (initialDesc.descriptorType == typeFSRef) { - initialPtr = &initialDesc; - } - result = NavServicesGetFile(interp, &ofd, initialPtr, initialFile, NULL, - title, message, NULL, false, SAVE_FILE, parent); - TkFreeFileFilters(&ofd.fl); - end: - if (initialDesc.dataHandle) { - ChkErr(AEDisposeDesc, &initialDesc); + if (fl.filters || defaultType) { + fileTypes = [NSMutableArray array]; + [fileTypes addObject:defaultType ? defaultType : (id)kUTTypeContent]; + for (FileFilter *filterPtr = fl.filters; filterPtr; + filterPtr = filterPtr->next) { + for (FileFilterClause *clausePtr = filterPtr->clauses; clausePtr; + clausePtr = clausePtr->next) { + for (GlobPattern *globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + str = globPtr->pattern; + while (*str && (*str == '*' || *str == '.')) { + str++; + } + if (*str) { + type = [[NSString alloc] initWithUTF8String:str]; + if (![fileTypes containsObject:type]) { + [fileTypes addObject:type]; + } + [type release]; + } + } + } + } + [panel setAllowedFileTypes:fileTypes]; + [panel setAllowsOtherFileTypes:YES]; } - if (title) { - CFRelease(title); + [panel setCanSelectHiddenExtension:YES]; + [panel setExtensionHidden:NO]; + if (cmdObj) { + callbackInfo = (FilePanelCallbackInfo *) + ckalloc(sizeof(FilePanelCallbackInfo)); + if (Tcl_IsShared(cmdObj)) { + cmdObj = Tcl_DuplicateObj(cmdObj); + } + Tcl_IncrRefCount(cmdObj); } - if (message) { - CFRelease(message); + callbackInfo->cmdObj = cmdObj; + callbackInfo->interp = interp; + callbackInfo->multiple = 0; + parent = TkMacOSXDrawableWindow(((TkWindow *) tkwin)->window); + if (haveParentOption && parent && ![parent attachedSheet]) { + [panel beginSheetForDirectory:directory file:filename + modalForWindow:parent modalDelegate:NSApp didEndSelector: + @selector(tkFilePanelDidEnd:returnCode:contextInfo:) + contextInfo:callbackInfo]; + returnCode = cmdObj ? NSAlertOtherReturn : + [NSApp runModalForWindow:panel]; + } else { + returnCode = [panel runModalForDirectory:directory file:filename]; + [NSApp tkFilePanelDidEnd:panel returnCode:returnCode + contextInfo:callbackInfo]; } + result = (returnCode != NSAlertErrorReturn) ? TCL_OK : TCL_ERROR; + + end: + TkFreeFileFilters(&fl); return result; } @@ -555,104 +700,113 @@ Tk_ChooseDirectoryObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - int i, result = TCL_ERROR; - Tk_Window parent = NULL; - AEDesc initialDesc = {typeNull, NULL}, *initialPtr = NULL; - FSRef dirRef; - CFStringRef message = NULL, title = NULL; - OpenFileData ofd; - static const char *chooseOptionStrings[] = { - "-initialdir", "-message", "-mustexist", "-parent", "-title", NULL - }; - enum chooseOptions { - CHOOSE_INITDIR, CHOOSE_MESSAGE, CHOOSE_MUSTEXIST, CHOOSE_PARENT, - CHOOSE_TITLE - }; - - if (!fileDlgInited) { - InitFileDialogs(); - } + Tk_Window tkwin = clientData; + char *str; + int i, result = TCL_ERROR, haveParentOption = 0; + int index, len, mustexist = 0; + Tcl_Obj *cmdObj = NULL; + FilePanelCallbackInfo callbackInfoStruct; + FilePanelCallbackInfo *callbackInfo = &callbackInfoStruct; + NSString *directory = nil, *filename = nil; + NSString *message, *title; + NSWindow *parent; + NSOpenPanel *panel = [NSOpenPanel openPanel]; + NSInteger returnCode = NSAlertErrorReturn; for (i = 1; i < objc; i += 2) { - char *string, *choice; - int index, choiceLen; - if (Tcl_GetIndexFromObj(interp, objv[i], chooseOptionStrings, "option", TCL_EXACT, &index) != TCL_OK) { goto end; } if (i + 1 == objc) { - string = Tcl_GetString(objv[i]); - Tcl_AppendResult(interp, "value for \"", string, "\" missing", - NULL); + str = Tcl_GetString(objv[i]); + Tcl_AppendResult(interp, "value for \"", str, "\" missing", NULL); goto end; } switch (index) { case CHOOSE_INITDIR: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (choiceLen && HandleInitialDirectory(interp, NULL, choice, - &dirRef, NULL, &initialDesc) != TCL_OK) { - goto end; + str = Tcl_GetStringFromObj(objv[i + 1], &len); + if (len) { + directory = [[[NSString alloc] initWithUTF8String:str] + autorelease]; } break; case CHOOSE_MESSAGE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (message) { - CFRelease(message); + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setMessage:message]; + [message release]; + break; + case CHOOSE_MUSTEXIST: + if (Tcl_GetBooleanFromObj(interp, objv[i + 1], + &mustexist) != TCL_OK) { + goto end; } - message = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); break; case CHOOSE_PARENT: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - parent = Tk_NameToWindow(interp, choice, clientData); - if (parent == NULL) { + str = Tcl_GetStringFromObj(objv[i + 1], &len); + tkwin = Tk_NameToWindow(interp, str, tkwin); + if (!tkwin) { goto end; } + haveParentOption = 1; break; case CHOOSE_TITLE: - choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen); - if (title) { - CFRelease(title); - } - title = CFStringCreateWithBytes(NULL, (unsigned char *) choice, - choiceLen, kCFStringEncodingUTF8, false); + title = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [panel setTitle:title]; + [title release]; + break; + case CHOOSE_COMMAND: + cmdObj = objv[i+1]; break; } } - - TkInitFileFilters(&ofd.fl); - ofd.usePopup = 0; - if (initialDesc.descriptorType == typeFSRef) { - initialPtr = &initialDesc; - } - result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, NULL, title, - message, NULL, false, CHOOSE_FOLDER, parent); - TkFreeFileFilters(&ofd.fl); - end: - if (initialDesc.dataHandle) { - ChkErr(AEDisposeDesc, &initialDesc); - } - if (title) { - CFRelease(title); + [panel setPrompt:@"Choose"]; + [panel setCanChooseFiles:NO]; + [panel setCanChooseDirectories:YES]; + [panel setCanCreateDirectories:!mustexist]; + if (cmdObj) { + callbackInfo = (FilePanelCallbackInfo *) + ckalloc(sizeof(FilePanelCallbackInfo)); + if (Tcl_IsShared(cmdObj)) { + cmdObj = Tcl_DuplicateObj(cmdObj); + } + Tcl_IncrRefCount(cmdObj); } - if (message) { - CFRelease(message); + callbackInfo->cmdObj = cmdObj; + callbackInfo->interp = interp; + callbackInfo->multiple = 0; + parent = TkMacOSXDrawableWindow(((TkWindow *) tkwin)->window); + if (haveParentOption && parent && ![parent attachedSheet]) { + [panel beginSheetForDirectory:directory file:filename + modalForWindow:parent modalDelegate:NSApp didEndSelector: + @selector(tkFilePanelDidEnd:returnCode:contextInfo:) + contextInfo:callbackInfo]; + returnCode = cmdObj ? NSAlertOtherReturn : + [NSApp runModalForWindow:panel]; + } else { + returnCode = [panel runModalForDirectory:directory file:filename]; + [NSApp tkFilePanelDidEnd:panel returnCode:returnCode + contextInfo:callbackInfo]; } + result = (returnCode != NSAlertErrorReturn) ? TCL_OK : TCL_ERROR; + + end: return result; } /* *---------------------------------------------------------------------- * - * HandleInitialDirectory -- + * TkAboutDlg -- * - * Helper for -initialdir setup. + * Displays the default Tk About box. * * Results: - * Tcl result. + * None. * * Side effects: * None. @@ -660,474 +814,438 @@ Tk_ChooseDirectoryObjCmd( *---------------------------------------------------------------------- */ -int -HandleInitialDirectory( - Tcl_Interp *interp, - char *initialFile, - char *initialDir, - FSRef *dirRef, - AEDescList *selectDescPtr, - AEDesc *dirDescPtr) +void +TkAboutDlg(void) { - Tcl_DString ds; - OSStatus err; - Boolean isDirectory; - char *dirName = NULL; - int result = TCL_ERROR; + NSImage *image; + NSString *path = [NSApp tkFrameworkImagePath: @"Tk.tiff"]; - if (initialDir) { - dirName = Tcl_TranslateFileName(interp, initialDir, &ds); - if (dirName == NULL) { - goto end; - } - err = ChkErr(FSPathMakeRef, (unsigned char *) dirName, dirRef, - &isDirectory); - if (err != noErr) { - Tcl_AppendResult(interp, "bad directory \"", initialDir, "\"", - NULL); - goto end; - } - if (!isDirectory) { - Tcl_AppendResult(interp, "-intialdir \"", initialDir, "\"" - " is a file, not a directory.", NULL); - goto end; - } - ChkErr(AECreateDesc, typeFSRef, dirRef, sizeof(*dirRef), dirDescPtr); + if (path) { + image = [[[NSImage alloc] initWithContentsOfFile:path] autorelease]; + } else { + image = [NSApp applicationIconImage]; } - if (initialFile && selectDescPtr) { - FSRef fileRef; - AEDesc fileDesc; - char *namePtr; - - if (initialDir) { - Tcl_DStringAppend(&ds, "/", 1); - Tcl_DStringAppend(&ds, initialFile, -1); - namePtr = Tcl_DStringValue(&ds); - } else { - namePtr = initialFile; - } - - ChkErr(AECreateList, NULL, 0, false, selectDescPtr); - - err = ChkErr(FSPathMakeRef, (unsigned char *) namePtr, &fileRef, - &isDirectory); - if (err != noErr) { - Tcl_AppendResult(interp, "bad initialfile \"", initialFile, - "\" file does not exist.", NULL); - goto end; - } - ChkErr(AECreateDesc, typeFSRef, &fileRef, sizeof(fileRef), &fileDesc); - ChkErr(AEPutDesc, selectDescPtr, 1, &fileDesc); - ChkErr(AEDisposeDesc, &fileDesc); - } - result = TCL_OK; - end: - if (dirName) { - Tcl_DStringFree(&ds); - } - return result; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + + [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter setDateFormat:@"Y"]; + + NSString *year = [dateFormatter stringFromDate:[NSDate date]]; + + [dateFormatter release]; + + NSMutableParagraphStyle *style = + [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] + autorelease]; + + [style setAlignment:NSCenterTextAlignment]; + + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + @"Tcl & Tk", @"ApplicationName", + @"Tcl " TCL_VERSION " & Tk " TK_VERSION, @"ApplicationVersion", + @TK_PATCH_LEVEL, @"Version", + image, @"ApplicationIcon", + [NSString stringWithFormat:@"Copyright %1$C 1987-%2$@.", 0xA9, + year], @"Copyright", + [[[NSAttributedString alloc] initWithString: + [NSString stringWithFormat: + @"%1$C 1987-%2$@ Tcl Core Team." "\n\n" + "%1$C 2002-%2$@ Daniel A. Steffen." "\n\n" + "%1$C 2001-2009 Apple Inc." "\n\n" + "%1$C 2001-2002 Jim Ingham & Ian Reid" "\n\n" + "%1$C 1998-2000 Jim Ingham & Ray Johnson" "\n\n" + "%1$C 1998-2000 Scriptics Inc." "\n\n" + "%1$C 1996-1997 Sun Microsystems Inc.", 0xA9, year] attributes: + [NSDictionary dictionaryWithObject:style + forKey:NSParagraphStyleAttributeName]] autorelease], @"Credits", + nil]; + [NSApp orderFrontStandardAboutPanelWithOptions:options]; } /* *---------------------------------------------------------------------- * - * InitFileDialogs -- + * TkMacOSXStandardAboutPanelObjCmd -- * - * Initialize file dialog subsystem. + * Implements the ::tk::mac::standardAboutPanel command. * * Results: - * None. + * A standard Tcl result. * * Side effects: - * None. + * none * *---------------------------------------------------------------------- */ -void -InitFileDialogs(void) +int +TkMacOSXStandardAboutPanelObjCmd( + ClientData clientData, /* Unused. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - fileDlgInited = 1; - openFileFilterUPP = NewNavObjectFilterUPP(OpenFileFilterProc); - openFileEventUPP = NewNavEventUPP(OpenEventProc); + if (objc > 1) { + Tcl_WrongNumArgs(interp, 1, objv, NULL); + return TCL_ERROR; + } + [NSApp orderFrontStandardAboutPanelWithOptions:nil]; + return TCL_OK; } /* *---------------------------------------------------------------------- * - * NavServicesGetFile -- + * Tk_MessageBoxObjCmd -- * - * Common wrapper for NavServices API. + * Implements the tk_messageBox in native Mac OS X style. * * Results: - * Tcl result. + * A standard Tcl result. * * Side effects: - * None. + * none * *---------------------------------------------------------------------- */ int -NavServicesGetFile( - Tcl_Interp *interp, - OpenFileData *ofdPtr, - AEDesc *initialDescPtr, - char *initialFile, - AEDescList *selectDescPtr, - CFStringRef title, - CFStringRef message, - const char *initialtype, - int multiple, - int isOpen, - Tk_Window parent) +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. */ { - NavHandlerUserData data; - NavDialogCreationOptions options; - NavDialogRef dialogRef = NULL; - CFStringRef *menuItemNames = NULL; - OSStatus err; - Tcl_Obj *theResult = NULL; - int result = TCL_ERROR; - - bzero(&data, sizeof(data)); - err = NavGetDefaultDialogCreationOptions(&options); - if (err != noErr) { - return result; - } - options.optionFlags = kNavDontAutoTranslate | kNavDontAddTranslateItems - | kNavSupportPackages | kNavAllFilesInPopup; - if (multiple) { - options.optionFlags |= kNavAllowMultipleFiles; - } - options.modality = kWindowModalityAppModal; - if (parent && ((TkWindow *) parent)->window != None && - TkMacOSXHostToplevelExists(parent)) { - options.parentWindow = TkMacOSXDrawableWindow(Tk_WindowId(parent)); - TK_IF_HI_TOOLBOX (5, + Tk_Window tkwin = clientData; + char *str; + int i, result = TCL_ERROR, haveParentOption = 0; + int index, typeIndex, iconIndex, indexDefaultOption = 0; + int defaultNativeButtonIndex = 1; /* 1, 2, 3: right to left */ + Tcl_Obj *cmdObj = NULL; + AlertCallbackInfo callbackInfoStruct, *callbackInfo = &callbackInfoStruct; + NSString *message, *title; + NSWindow *parent; + NSArray *buttons; + NSAlert *alert = [NSAlert new]; + NSInteger returnCode = NSAlertErrorReturn; + + iconIndex = ICON_INFO; + typeIndex = TYPE_OK; + for (i = 1; i < objc; i += 2) { + if (Tcl_GetIndexFromObj(interp, objv[i], alertOptionStrings, "option", + TCL_EXACT, &index) != TCL_OK) { + goto end; + } + if (i + 1 == objc) { + str = Tcl_GetString(objv[i]); + Tcl_AppendResult(interp, "value for \"", str, "\" missing", NULL); + goto end; + } + switch (index) { + case ALERT_DEFAULT: /* - * Impossible to modify dialog modality with the Cocoa-based - * NavServices implementation. + * Need to postpone processing of this option until we are sure to + * know the '-type' as well. */ - ) TK_ELSE_HI_TOOLBOX (5, - if (options.parentWindow) { - options.modality = kWindowModalityWindowModal; - data.sheet = 1; - } - ) TK_ENDIF - } - /* - * Now process the selection list. We have to use the popupExtension - * to fill the menu. - */ + indexDefaultOption = i; + break; - if (ofdPtr && ofdPtr->usePopup) { - FileFilter *filterPtr = ofdPtr->fl.filters; + case ALERT_DETAIL: + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [alert setInformativeText:message]; + [message release]; + break; - if (filterPtr == NULL) { - ofdPtr->usePopup = 0; - } - } - if (ofdPtr && ofdPtr->usePopup) { - FileFilter *filterPtr; - int index = 0; - ofdPtr->curType = 0; - - menuItemNames = (CFStringRef *) - ckalloc(ofdPtr->fl.numFilters * sizeof(CFStringRef)); - - for (filterPtr = ofdPtr->fl.filters; filterPtr != NULL; - filterPtr = filterPtr->next, index++) { - menuItemNames[index] = CFStringCreateWithCString(NULL, - filterPtr->name, kCFStringEncodingUTF8); - if (initialtype && strcmp(filterPtr->name, initialtype) == 0) { - ofdPtr->initialType = index; + case ALERT_ICON: + if (Tcl_GetIndexFromObj(interp, objv[i + 1], alertIconStrings, + "value", TCL_EXACT, &iconIndex) != TCL_OK) { + goto end; } - } - options.popupExtension = CFArrayCreate(NULL, - (const void **) menuItemNames, ofdPtr->fl.numFilters, NULL); - } else { - options.optionFlags |= kNavNoTypePopup; - options.popupExtension = NULL; - } - options.clientName = CFSTR("Wish"); - options.message = message; - options.windowTitle = title; - if (initialFile) { - options.saveFileName = CFStringCreateWithCString(NULL, initialFile, - kCFStringEncodingUTF8); - } else { - options.saveFileName = NULL; - } - if (isOpen == OPEN_FILE) { - data.ofdPtr = ofdPtr; - err = ChkErr(NavCreateGetFileDialog, &options, NULL, - openFileEventUPP, NULL, openFileFilterUPP, &data, &dialogRef); - } else if (isOpen == SAVE_FILE) { - err = ChkErr(NavCreatePutFileDialog, &options, 'TEXT', 'WIsH', - openFileEventUPP, &data, &dialogRef); - } else if (isOpen == CHOOSE_FOLDER) { - err = ChkErr(NavCreateChooseFolderDialog, &options, - openFileEventUPP, openFileFilterUPP, &data, &dialogRef); - } - if (err == noErr && dialogRef) { - if (initialDescPtr) { - ChkErr(NavCustomControl, dialogRef, kNavCtlSetLocation, - initialDescPtr); - } - if (selectDescPtr && selectDescPtr->descriptorType != typeNull) { - ChkErr(NavCustomControl, dialogRef, kNavCtlSetSelection, - selectDescPtr); - } - TkMacOSXTrackingLoop(1); - err = ChkErr(NavDialogRun, dialogRef); - if (err == noErr) { - if (data.sheet) { - data.dialogWindow = NavDialogGetWindow(dialogRef); - ChkErr(GetWindowModality, data.dialogWindow, - &data.origModality, &data.origUnavailWindow); - ChkErr(SetWindowModality, data.dialogWindow, - kWindowModalityAppModal, NULL); - ChkErr(RunAppModalLoopForWindow, data.dialogWindow); + break; + + case ALERT_MESSAGE: + message = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [alert setMessageText:message]; + [message release]; + break; + + case ALERT_PARENT: + str = Tcl_GetString(objv[i + 1]); + tkwin = Tk_NameToWindow(interp, str, tkwin); + if (!tkwin) { + goto end; } - err = data.err; - } - TkMacOSXTrackingLoop(0); - } + haveParentOption = 1; + break; - /* - * Most commands assume that the file dialogs return a single item, not a - * list. So only build a list if multiple is true... - */ + case ALERT_TITLE: + title = [[NSString alloc] initWithUTF8String: + Tcl_GetString(objv[i + 1])]; + [[alert window] setTitle:title]; + [title release]; + break; - if (err == noErr) { - if (multiple) { - theResult = Tcl_NewListObj(0, NULL); - } else { - theResult = Tcl_NewObj(); - } - if (!theResult) { - err = memFullErr; + case ALERT_TYPE: + if (Tcl_GetIndexFromObj(interp, objv[i + 1], alertTypeStrings, + "value", TCL_EXACT, &typeIndex) != TCL_OK) { + goto end; + } + break; + case ALERT_COMMAND: + cmdObj = objv[i+1]; + break; } } - if (err == noErr && data.reply.validRecord) { - AEDesc resultDesc; - long count, i; - FSRef fsRef; - char pathPtr[PATH_MAX + 1]; - char saveName[PATH_MAX + 1]; - - err = ChkErr(AECountItems, &data.reply.selection, &count); - if (err != noErr) { - /* - * There was an error when counting the items? Treat as if no - * items were chosen. - */ + if (indexDefaultOption) { + /* + * Any '-default' option needs to know the '-type' option, which is + * why we do this here. + */ - goto installResult; + if (Tcl_GetIndexFromObj(interp, objv[indexDefaultOption + 1], + alertButtonStrings, "value", TCL_EXACT, &index) != TCL_OK) { + goto end; } /* - * Process the chosen files. This will be one unless -multiple was - * specified. + * Need to map from "ok" etc. to 1, 2, 3, right to left. */ - for (i = 1; i <= count; i++) { - /* - * Get the name of the selected file. - */ - - err = ChkErr(AEGetNthDesc, &data.reply.selection, i, - typeFSRef, NULL, &resultDesc); - if (err != noErr) { - continue; - } - err = ChkErr(AEGetDescData, &resultDesc, &fsRef, sizeof(fsRef)); - if (err != noErr) { - goto nextFilename; - } - err = ChkErr(FSRefMakePath, &fsRef, (unsigned char *) pathPtr, - PATH_MAX + 1); - if (err != noErr) { - goto nextFilename; - } - - /* - * If we're saving the file, we're creating a new filename and - * must therefore check whether it is a legal filename (not - * exceeding path length limits, etc.) - */ - - if (isOpen == SAVE_FILE) { - if (!data.saveNameRef) { - TkMacOSXDbgMsg("NavDialogGetSaveFileName failed"); - goto nextFilename; - } - - if (!CFStringGetCString(data.saveNameRef, saveName, - PATH_MAX + 1, kCFStringEncodingUTF8)) { - TkMacOSXDbgMsg("CFStringGetCString failed"); - goto nextFilename; - } - - if (strlen(pathPtr) + strlen(saveName) >= PATH_MAX) { - TkMacOSXDbgMsg("Path name too long"); - goto nextFilename; - } + defaultNativeButtonIndex = + alertButtonIndexAndTypeToNativeButtonIndex[typeIndex][index]; + if (!defaultNativeButtonIndex) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("Illegal default option", -1)); + goto end; + } + } + [alert setIcon:[NSApp applicationIconImage]]; + [alert setAlertStyle:alertStyles[iconIndex]]; + i = 0; + while (i < 3 && alertButtonNames[typeIndex][i]) { + [alert addButtonWithTitle:(NSString*)alertButtonNames[typeIndex][i++]]; + } + buttons = [alert buttons]; + for (NSButton *b in buttons) { + NSString *ke = [b keyEquivalent]; - strcat(pathPtr, "/"); - strcat(pathPtr, saveName); - } + if (([ke isEqualToString:@"\r"] || [ke isEqualToString:@"\033"]) && + ![b keyEquivalentModifierMask]) { + [b setKeyEquivalent:@""]; + } + } + [[buttons objectAtIndex: [buttons count]-1] setKeyEquivalent: @"\033"]; + [[buttons objectAtIndex: defaultNativeButtonIndex-1] + setKeyEquivalent: @"\r"]; + if (cmdObj) { + callbackInfo = (AlertCallbackInfo *) + ckalloc(sizeof(AlertCallbackInfo)); + if (Tcl_IsShared(cmdObj)) { + cmdObj = Tcl_DuplicateObj(cmdObj); + } + Tcl_IncrRefCount(cmdObj); + } + callbackInfo->cmdObj = cmdObj; + callbackInfo->interp = interp; + callbackInfo->typeIndex = typeIndex; + parent = TkMacOSXDrawableWindow(((TkWindow *) tkwin)->window); + if (haveParentOption && parent && ![parent attachedSheet]) { + [alert beginSheetModalForWindow:parent modalDelegate:NSApp + didEndSelector:@selector(tkAlertDidEnd:returnCode:contextInfo:) + contextInfo:callbackInfo]; + returnCode = cmdObj ? NSAlertOtherReturn : + [NSApp runModalForWindow:[alert window]]; + } else { + returnCode = [alert runModal]; + [NSApp tkAlertDidEnd:alert returnCode:returnCode + contextInfo:callbackInfo]; + } + result = (returnCode != NSAlertErrorReturn) ? TCL_OK : TCL_ERROR; + end: + [alert release]; + return result; +} + +/* + *---------------------------------------------------------------------- + */ +#pragma mark [tk fontchooser] implementation (TIP 324) +/* + *---------------------------------------------------------------------- + */ - /* - * Got a valid file name; put it in the result object. - */ +#include "tkMacOSXEvent.h" +#include "tkMacOSXFont.h" + +typedef struct FontchooserData { + Tcl_Obj *titleObj; + Tcl_Obj *cmdObj; + Tk_Window parent; +} FontchooserData; + +enum FontchooserEvent { FontchooserClosed, FontchooserSelection }; + +static void FontchooserEvent(int kind); +static Tcl_Obj * FontchooserCget(FontchooserData *fcdPtr, + int optionIndex); +static int FontchooserConfigureCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int FontchooserShowCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int FontchooserHideCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static void FontchooserParentEventHandler(ClientData clientData, + XEvent *eventPtr); +static void DeleteFontchooserData(ClientData clientData, + Tcl_Interp *interp); + +MODULE_SCOPE const TkEnsemble tkFontchooserEnsemble[]; +const TkEnsemble tkFontchooserEnsemble[] = { + { "configure", FontchooserConfigureCmd, NULL }, + { "show", FontchooserShowCmd, NULL }, + { "hide", FontchooserHideCmd, NULL }, + { NULL, NULL, NULL } +}; + +static Tcl_Interp *fontchooserInterp = NULL; +static NSFont *fontPanelFont = nil; +static NSMutableDictionary *fontPanelFontAttributes = nil; + +static const char *const fontchooserOptionStrings[] = { + "-parent", "-title", "-font", "-command", + "-visible", NULL +}; +enum FontchooserOption { + FontchooserParent, FontchooserTitle, FontchooserFont, FontchooserCmd, + FontchooserVisible +}; + +@implementation TKApplication(TKFontPanel) + +- (void) changeFont: (id) sender +{ + NSFontManager *fm = [NSFontManager sharedFontManager]; - if (multiple) { - Tcl_ListObjAppendElement(interp, theResult, - Tcl_NewStringObj(pathPtr, -1)); - } else { - Tcl_SetStringObj(theResult, pathPtr, -1); - } + if ([fm currentFontAction] == NSViaPanelFontAction) { + NSFont *font = [fm convertFont:fontPanelFont]; - nextFilename: - ChkErr(AEDisposeDesc, &resultDesc); + if (![fontPanelFont isEqual:font]) { + [fontPanelFont release]; + fontPanelFont = [font retain]; + FontchooserEvent(FontchooserSelection); } - - installResult: - Tcl_SetObjResult(interp, theResult); - result = TCL_OK; - } else if (err == userCanceledErr) { - Tcl_ResetResult(interp); - result = TCL_OK; } +} - /* - * Clean up any allocated memory. - */ +- (void) changeAttributes: (id) sender +{ + NSDictionary *attributes = [sender convertAttributes: + fontPanelFontAttributes]; - if (data.reply.validRecord) { - ChkErr(NavDisposeReply, &data.reply); + if (![fontPanelFontAttributes isEqual:attributes]) { + [fontPanelFontAttributes setDictionary:attributes]; + FontchooserEvent(FontchooserSelection); } - if (data.saveNameRef) { - CFRelease(data.saveNameRef); - } - if (options.saveFileName) { - CFRelease(options.saveFileName); - } - if (options.clientName) { - CFRelease(options.clientName); - } - if (menuItemNames) { - int i; +} - for (i = 0; i < ofdPtr->fl.numFilters; i++) { - CFRelease(menuItemNames[i]); - } - ckfree((void *) menuItemNames); - } - if (options.popupExtension) { - CFRelease(options.popupExtension); +- (NSUInteger) validModesForFontPanel: (NSFontPanel *) fontPanel +{ + return (NSFontPanelStandardModesMask & ~NSFontPanelAllEffectsModeMask) | + NSFontPanelUnderlineEffectModeMask | + NSFontPanelStrikethroughEffectModeMask; +} + +- (void) windowDidOrderOffScreen: (NSNotification *) notification +{ +#ifdef TK_MAC_DEBUG_NOTIFICATIONS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); +#endif + if ([[notification object] isEqual:[[NSFontManager sharedFontManager] + fontPanel:NO]]) { + FontchooserEvent(FontchooserClosed); } - return result; } +@end /* *---------------------------------------------------------------------- * - * OpenEventProc -- + * FontchooserEvent -- * - * NavServices event handling callback. + * This processes events generated by user interaction with the + * font panel. * * Results: * None. * * Side effects: - * None. + * Additional events may be place on the Tk event queue. * *---------------------------------------------------------------------- */ -pascal void -OpenEventProc( - NavEventCallbackMessage callBackSelector, - NavCBRecPtr callBackParams, - NavCallBackUserData callBackUD) +static void +FontchooserEvent( + int kind) { - NavHandlerUserData *data = (NavHandlerUserData*) callBackUD; - OpenFileData *ofd = data->ofdPtr; - - switch (callBackSelector) { - case kNavCBStart: - if (ofd && ofd->initialType >= 0) { - /* Select initial filter */ - FileFilter *filterPtr = ofd->fl.filters; - int i = ofd->initialType; - - while (filterPtr && i-- > 0) { - filterPtr = filterPtr->next; - } - if (filterPtr) { - NavMenuItemSpec selectItem; - - selectItem.version = kNavMenuItemSpecVersion; - selectItem.menuCreator = 0; - selectItem.menuType = ofd->initialType; - selectItem.menuItemName[0] = strlen(filterPtr->name); - strncpy((char *) &selectItem.menuItemName[1], - filterPtr->name, 255); - ChkErr(NavCustomControl, callBackParams->context, - kNavCtlSelectCustomType, &selectItem); - } - } - break; - case kNavCBPopupMenuSelect: - ofd->curType = ((NavMenuItemSpec *) - callBackParams->eventData.eventDataParms.param)->menuType; - break; - case kNavCBAccept: - case kNavCBCancel: - if (data->sheet) { - ChkErr(QuitAppModalLoopForWindow, data->dialogWindow); - ChkErr(SetWindowModality, data->dialogWindow, - data->origModality, data->origUnavailWindow); - } - break; - case kNavCBUserAction: - if (data->reply.validRecord) { - ChkErr(NavDisposeReply, &data->reply); - data->reply.validRecord = 0; - } - data->err = NavDialogGetReply(callBackParams->context, - &data->reply); - if (callBackParams->userAction == kNavUserActionSaveAs) { - data->saveNameRef = NavDialogGetSaveFileName( - callBackParams->context); - if (data->saveNameRef) { - CFRetain(data->saveNameRef); + FontchooserData *fcdPtr; + Tcl_Obj *fontObj; + + if (!fontchooserInterp) { + return; + } + fcdPtr = Tcl_GetAssocData(fontchooserInterp, "::tk::fontchooser", NULL); + switch (kind) { + case FontchooserClosed: + if (fcdPtr->parent != None) { + TkSendVirtualEvent(fcdPtr->parent, "TkFontchooserVisibility"); + fontchooserInterp = NULL; + } + break; + case FontchooserSelection: + fontObj = TkMacOSXFontDescriptionForNSFontAndNSFontAttributes( + fontPanelFont, fontPanelFontAttributes); + if (fontObj) { + if (fcdPtr->cmdObj) { + int objc, result; + Tcl_Obj **objv, **tmpv; + + result = Tcl_ListObjGetElements(fontchooserInterp, + fcdPtr->cmdObj, &objc, &objv); + if (result == TCL_OK) { + tmpv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * + (unsigned)(objc + 2)); + memcpy(tmpv, objv, sizeof(Tcl_Obj *) * objc); + tmpv[objc] = fontObj; + TkBackgroundEvalObjv(fontchooserInterp, objc + 1, tmpv, + TCL_EVAL_GLOBAL); + ckfree((char *)tmpv); } } - break; - case kNavCBTerminate: - NavDialogDispose(callBackParams->context); - break; - case kNavCBEvent: - TkMacOSXRunTclEventLoop(); - break; + TkSendVirtualEvent(fcdPtr->parent, "TkFontchooserFontChanged"); + } + break; } } /* *---------------------------------------------------------------------- * - * OpenFileFilterProc -- + * FontchooserCget -- * - * NavServices file filter callback. + * Helper for the FontchooserConfigure command to return the + * current value of any of the options (which may be NULL in + * the structure) * * Results: - * Whether to use the file in question. + * Tcl object of option value. * * Side effects: * None. @@ -1135,614 +1253,403 @@ OpenEventProc( *---------------------------------------------------------------------- */ -pascal Boolean -OpenFileFilterProc( - AEDesc *theItem, - void *info, - NavCallBackUserData callBackUD, - NavFilterModes filterMode) +static Tcl_Obj * +FontchooserCget( + FontchooserData *fcdPtr, + int optionIndex) { - OpenFileData *ofdPtr = ((NavHandlerUserData *) callBackUD)->ofdPtr; - int result = MATCHED; - - if (ofdPtr && ofdPtr->usePopup && ofdPtr->fl.numFilters > 0 && - ((theItem->descriptorType == typeFSS) - || (theItem->descriptorType == typeFSRef))) { - NavFileOrFolderInfo *theInfo = info; - char fileName[256]; - OSType fileType; - StringPtr fileNamePtr = NULL; - Tcl_DString fileNameDString; - int i; - FileFilter *filterPtr; - - if (!theInfo->isFolder) { - 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, (char *) fileNamePtr + 1, len); - fileName[len] = '\0'; - fileNamePtr = (unsigned char *) fileName; - } else if ((theItem->descriptorType == typeFSRef)) { - OSStatus err; - FSRef *theRef = (FSRef *) *theItem->dataHandle; - HFSUniStr255 uniFileName; - - err = ChkErr(FSGetCatalogInfo, theRef, kFSCatInfoNone, - NULL, &uniFileName, NULL, NULL); - - if (err == noErr) { - Tcl_UniCharToUtfDString((Tcl_UniChar *)uniFileName.unicode, - uniFileName.length, &fileNameDString); - fileNamePtr = (unsigned char *) - Tcl_DStringValue(&fileNameDString); - } - } - 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 = 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. - */ - - result = UNMATCHED; - for (filterPtr = ofdPtr->fl.filters; filterPtr; - filterPtr = filterPtr->next) { - if (MatchOneType(fileNamePtr, fileType, ofdPtr, - filterPtr) == MATCHED) { - result = MATCHED; - break; - } - } - } - Tcl_DStringFree(&fileNameDString); + Tcl_Obj *resObj = NULL; + + switch(optionIndex) { + case FontchooserParent: + if (fcdPtr->parent != None) { + resObj = Tcl_NewStringObj( + ((TkWindow *) fcdPtr->parent)->pathName, -1); + } else { + resObj = Tcl_NewStringObj(".", 1); + } + break; + case FontchooserTitle: + if (fcdPtr->titleObj) { + resObj = fcdPtr->titleObj; + } else { + resObj = Tcl_NewObj(); + } + break; + case FontchooserFont: + resObj = TkMacOSXFontDescriptionForNSFontAndNSFontAttributes( + fontPanelFont, fontPanelFontAttributes); + if (!resObj) { + resObj = Tcl_NewObj(); } + break; + case FontchooserCmd: + if (fcdPtr->cmdObj) { + resObj = fcdPtr->cmdObj; + } else { + resObj = Tcl_NewObj(); + } + break; + case FontchooserVisible: + resObj = Tcl_NewBooleanObj([[[NSFontManager sharedFontManager] + fontPanel:NO] isVisible]); + break; + default: + resObj = Tcl_NewObj(); } - return (result == MATCHED); + return resObj; } /* - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- * - * MatchOneType -- + * FontchooserConfigureCmd -- * - * Match a file with one file type in the list of file types. + * Implementation of the 'tk fontchooser configure' ensemble command. + * See the user documentation for what it does. * * Results: - * Returns MATCHED if the file matches with the file type; returns - * UNMATCHED otherwise. + * See the user documentation. * * Side effects: - * None + * Per-interp data structure may be modified * - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- */ -Boolean -MatchOneType( - StringPtr fileNamePtr, /* Name 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 */ +static int +FontchooserConfigureCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) { - FileFilterClause *clausePtr; + Tk_Window tkwin = (Tk_Window)clientData; + FontchooserData *fcdPtr = Tcl_GetAssocData(interp, "::tk::fontchooser", + NULL); + int i, r = TCL_OK; /* - * 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. + * With no arguments we return all the options in a dict */ - for (clausePtr = filterPtr->clauses; clausePtr; - clausePtr = clausePtr->next) { - int macMatched = 0; - int globMatched = 0; - GlobPattern *globPtr; - MacFileType *mfPtr; + if (objc == 1) { + Tcl_Obj *keyObj, *valueObj; + Tcl_Obj *dictObj = Tcl_NewDictObj(); - if (clausePtr->patterns == NULL) { - globMatched = 1; + for (i = 0; r == TCL_OK && fontchooserOptionStrings[i] != NULL; ++i) { + keyObj = Tcl_NewStringObj(fontchooserOptionStrings[i], -1); + valueObj = FontchooserCget(fcdPtr, i); + r = Tcl_DictObjPut(interp, dictObj, keyObj, valueObj); } - if (clausePtr->macTypes == NULL) { - macMatched = 1; + if (r == TCL_OK) { + Tcl_SetObjResult(interp, dictObj); } + return r; + } - for (globPtr = clausePtr->patterns; globPtr; - globPtr = globPtr->next) { - char *q, *ext; - - if (fileNamePtr == NULL) { - continue; - } - ext = globPtr->pattern; + for (i = 1; i < objc; i += 2) { + int optionIndex, len; - if (ext[0] == '\0') { - /* - * We don't want any extensions: OK if the filename doesn't - * have "." in it - */ + if (Tcl_GetIndexFromObj(interp, objv[i], fontchooserOptionStrings, + "option", 0, &optionIndex) != TCL_OK) { + return TCL_ERROR; + } + if (objc == 2) { + /* + * With one option and no arg, return the current value. + */ - for (q = (char*) fileNamePtr; *q; q++) { - if (*q == '.') { - goto glob_unmatched; - } - } - goto glob_matched; - } + Tcl_SetObjResult(interp, FontchooserCget(fcdPtr, optionIndex)); + return TCL_OK; + } + if (i + 1 == objc) { + Tcl_AppendResult(interp, "value for \"", + Tcl_GetString(objv[i]), "\" missing", NULL); + return TCL_ERROR; + } + switch (optionIndex) { + case FontchooserVisible: { + const char *msg = "cannot change read-only option " + "\"-visible\": use the show or hide command"; - if (Tcl_StringMatch((char*) fileNamePtr, ext)) { - goto glob_matched; + Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, sizeof(msg)-1)); + return TCL_ERROR; } - glob_unmatched: - continue; + case FontchooserParent: { + Tk_Window parent = Tk_NameToWindow(interp, + Tcl_GetString(objv[i+1]), tkwin); - glob_matched: - globMatched = 1; - break; - } - - for (mfPtr = clausePtr->macTypes; mfPtr; mfPtr = mfPtr->next) { - if (fileType == mfPtr->type) { - macMatched = 1; + if (parent == None) { + return TCL_ERROR; + } + if (fcdPtr->parent) { + Tk_DeleteEventHandler(fcdPtr->parent, StructureNotifyMask, + FontchooserParentEventHandler, fcdPtr); + } + fcdPtr->parent = parent; + Tk_CreateEventHandler(fcdPtr->parent, StructureNotifyMask, + FontchooserParentEventHandler, fcdPtr); break; } - } + case FontchooserTitle: + if (fcdPtr->titleObj) { + Tcl_DecrRefCount(fcdPtr->titleObj); + } + Tcl_GetStringFromObj(objv[i+1], &len); + if (len) { + fcdPtr->titleObj = objv[i+1]; + if (Tcl_IsShared(fcdPtr->titleObj)) { + fcdPtr->titleObj = Tcl_DuplicateObj(fcdPtr->titleObj); + } + Tcl_IncrRefCount(fcdPtr->titleObj); + } else { + fcdPtr->titleObj = NULL; + } + break; + case FontchooserFont: + Tcl_GetStringFromObj(objv[i+1], &len); + if (len) { + Tk_Font f = Tk_AllocFontFromObj(interp, tkwin, objv[i+1]); - /* - * 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 (!f) { + return TCL_ERROR; + } + [fontPanelFont autorelease]; + fontPanelFont = [TkMacOSXNSFontForFont(f) retain]; + [fontPanelFontAttributes setDictionary: + TkMacOSXNSFontAttributesForFont(f)]; + [fontPanelFontAttributes removeObjectsForKeys:[NSArray + arrayWithObjects:NSFontAttributeName, + NSLigatureAttributeName, NSKernAttributeName, + nil]]; + Tk_FreeFont(f); + } else { + [fontPanelFont release]; + fontPanelFont = nil; + [fontPanelFontAttributes removeAllObjects]; + } - if (globMatched && (macMatched || (fileType == 0))) { - return MATCHED; + NSFontManager *fm = [NSFontManager sharedFontManager]; + NSFontPanel *fp = [fm fontPanel:NO]; + + [fp setPanelFont:fontPanelFont isMultiple:NO]; + [fm setSelectedFont:fontPanelFont isMultiple:NO]; + [fm setSelectedAttributes:fontPanelFontAttributes + isMultiple:NO]; + if ([fp isVisible]) { + TkSendVirtualEvent(fcdPtr->parent, + "TkFontchooserFontChanged"); + } + break; + case FontchooserCmd: + if (fcdPtr->cmdObj) { + Tcl_DecrRefCount(fcdPtr->cmdObj); + } + Tcl_GetStringFromObj(objv[i+1], &len); + if (len) { + fcdPtr->cmdObj = objv[i+1]; + if (Tcl_IsShared(fcdPtr->cmdObj)) { + fcdPtr->cmdObj = Tcl_DuplicateObj(fcdPtr->cmdObj); + } + Tcl_IncrRefCount(fcdPtr->cmdObj); + } else { + fcdPtr->cmdObj = NULL; + } + break; } } - - return UNMATCHED; + return TCL_OK; } /* - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- * - * TkAboutDlg -- + * FontchooserShowCmd -- * - * Displays the default Tk About box. This code uses Macintosh resources - * to define the content of the About Box. + * Implements the 'tk fontchooser show' ensemble command. The + * per-interp configuration data for the dialog is held in an interp + * associated structure. * * Results: - * None. + * See the user documentation. * * Side effects: - * None. + * Font Panel may be shown. * - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- */ -void -TkAboutDlg(void) +static int +FontchooserShowCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) { - DialogPtr aboutDlog; - WindowRef windowRef; - short itemHit = -9; + FontchooserData *fcdPtr = Tcl_GetAssocData(interp, "::tk::fontchooser", + NULL); - aboutDlog = GetNewDialog(TK_DEFAULT_ABOUT, NULL, (void *) (-1)); - if (!aboutDlog) { - return; + if (fcdPtr->parent == None) { + fcdPtr->parent = (Tk_Window) clientData; + Tk_CreateEventHandler(fcdPtr->parent, StructureNotifyMask, + FontchooserParentEventHandler, fcdPtr); + } + NSFontManager *fm = [NSFontManager sharedFontManager]; + NSFontPanel *fp = [fm fontPanel:YES]; + if ([fp delegate] != NSApp) { + [fp setDelegate:NSApp]; } - windowRef = GetDialogWindow(aboutDlog); - SelectWindow(windowRef); - TkMacOSXTrackingLoop(1); - while (itemHit != 1) { - ModalDialog(NULL, &itemHit); + if (![fp isVisible]) { + [fm orderFrontFontPanel:NSApp]; + TkSendVirtualEvent(fcdPtr->parent, "TkFontchooserVisibility"); } - TkMacOSXTrackingLoop(0); - DisposeDialog(aboutDlog); - SelectWindow(ActiveNonFloatingWindow()); + fontchooserInterp = interp; + + return TCL_OK; } /* - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- * - * Tk_MessageBoxObjCmd -- + * FontchooserHideCmd -- * - * Implements the tk_messageBox in native Mac OS X style. + * Implementation of the 'tk fontchooser hide' ensemble. See the + * user documentation for details. * * Results: - * A standard Tcl result. + * See the user documentation. * * Side effects: - * none + * Font Panel may be hidden. * - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- */ -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. */ +static int +FontchooserHideCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) { - Tk_Window tkwin = clientData; - AlertStdCFStringAlertParamRec paramCFStringRec; - AlertType alertType; - DialogRef dialogRef; - CFStringRef messageTextCF = NULL, finemessageTextCF = NULL; - OSStatus err; - SInt16 itemHit; - Boolean haveDefaultOption = false, haveParentOption = false; - char *str; - int index, defaultButtonIndex; - int defaultNativeButtonIndex; /* 1, 2, 3: right to left */ - int typeIndex, i, indexDefaultOption = 0, result = TCL_ERROR; - - static const char *movableAlertStrings[] = { - "-default", "-detail", "-icon", "-message", "-parent", "-title", - "-type", NULL - }; - static const char *movableTypeStrings[] = { - "abortretryignore", "ok", "okcancel", "retrycancel", "yesno", - "yesnocancel", NULL - }; - static const char *movableButtonStrings[] = { - "abort", "retry", "ignore", "ok", "cancel", "yes", "no", NULL - }; - static const char *movableIconStrings[] = { - "error", "info", "question", "warning", NULL - }; - enum movableAlertOptions { - ALERT_DEFAULT, ALERT_DETAIL, 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; - - ChkErr(GetStandardAlertDefaultParams, ¶mCFStringRec, - kStdCFStringAlertVersionOne); - paramCFStringRec.movable = true; - paramCFStringRec.helpButton = false; - paramCFStringRec.defaultButton = kAlertStdAlertOKButton; - paramCFStringRec.cancelButton = kAlertStdAlertCancelButton; - - for (i = 1; i < objc; i += 2) { - int iconIndex; - char *string; - - if (Tcl_GetIndexFromObj(interp, objv[i], movableAlertStrings, "option", - TCL_EXACT, &index) != TCL_OK) { - goto end; - } - if (i + 1 == objc) { - string = Tcl_GetString(objv[i]); - Tcl_AppendResult(interp, "value for \"", string, "\" missing", - NULL); - 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_DETAIL: - str = Tcl_GetString(objv[i + 1]); - if (finemessageTextCF) { - CFRelease(finemessageTextCF); - } - finemessageTextCF = CFStringCreateWithCString(NULL, str, - kCFStringEncodingUTF8); - break; - - case ALERT_ICON: - if (Tcl_GetIndexFromObj(interp, objv[i + 1], movableIconStrings, - "value", TCL_EXACT, &iconIndex) != TCL_OK) { - 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_GetString(objv[i + 1]); - if (messageTextCF) { - CFRelease(messageTextCF); - } - messageTextCF = CFStringCreateWithCString(NULL, str, - kCFStringEncodingUTF8); - break; - - case ALERT_PARENT: - str = Tcl_GetString(objv[i + 1]); - tkwin = Tk_NameToWindow(interp, str, tkwin); - if (tkwin == NULL) { - goto end; - } - if (((TkWindow *) tkwin)->window != None && - TkMacOSXHostToplevelExists(tkwin)) { - haveParentOption = true; - } - break; - - case ALERT_TITLE: - break; - - case ALERT_TYPE: - if (Tcl_GetIndexFromObj(interp, objv[i + 1], movableTypeStrings, - "value", TCL_EXACT, &typeIndex) != TCL_OK) { - goto end; - } - switch (typeIndex) { - case TYPE_ABORTRETRYIGNORE: - paramCFStringRec.defaultText = CFSTR("Abort"); - paramCFStringRec.cancelText = CFSTR("Retry"); - paramCFStringRec.otherText = CFSTR("Ignore"); - paramCFStringRec.cancelButton = kAlertStdAlertOtherButton; - 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"); - paramCFStringRec.cancelButton = kAlertStdAlertOtherButton; - break; - } - break; - } + NSFontPanel *fp = [[NSFontManager sharedFontManager] fontPanel:NO]; + if ([fp isVisible]) { + [fp orderOut:NSApp]; } + return TCL_OK; +} + +/* + * ---------------------------------------------------------------------- + * + * FontchooserParentEventHandler -- + * + * Event handler for StructureNotify events on the font chooser's parent + * window. + * + * Results: + * None. + * + * Side effects: + * Font chooser parent info is cleared and font panel is hidden. + * + * ---------------------------------------------------------------------- + */ - if (haveDefaultOption) { - /* - * Any '-default' option needs to know the '-type' option, which is why - * we do this here. - */ - - str = Tcl_GetString(objv[indexDefaultOption + 1]); - if (Tcl_GetIndexFromObj(interp, objv[indexDefaultOption + 1], - movableButtonStrings, "value", TCL_EXACT, &defaultButtonIndex) - != TCL_OK) { - goto end; - } - - /* - * Need to map from "ok" etc. to 1, 2, 3, right to left. - */ +static void +FontchooserParentEventHandler( + ClientData clientData, + XEvent *eventPtr) +{ + FontchooserData *fcdPtr = clientData; - defaultNativeButtonIndex = - buttonIndexAndTypeToNativeButtonIndex[typeIndex][defaultButtonIndex]; - if (defaultNativeButtonIndex == 0) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("Illegal default option", -1)); - goto end; - } - paramCFStringRec.defaultButton = defaultNativeButtonIndex; - if (paramCFStringRec.cancelButton == defaultNativeButtonIndex) { - paramCFStringRec.cancelButton = 0; - } + if (eventPtr->type == DestroyNotify) { + Tk_DeleteEventHandler(fcdPtr->parent, StructureNotifyMask, + FontchooserParentEventHandler, fcdPtr); + fcdPtr->parent = None; + FontchooserHideCmd(NULL, NULL, 0, NULL); } - ChkErr(SetThemeCursor, kThemeArrowCursor); - - if (haveParentOption) { - AlertHandlerUserData data; - static EventHandlerUPP handler = NULL; - WindowRef windowRef; - const EventTypeSpec kEvents[] = { - {kEventClassCommand, kEventProcessCommand} - }; - - bzero(&data, sizeof(data)); - if (!handler) { - handler = NewEventHandlerUPP(AlertHandler); - } - windowRef = TkMacOSXDrawableWindow(Tk_WindowId(tkwin)); - if (!windowRef) { - goto end; - } - err = ChkErr(CreateStandardSheet, alertType, messageTextCF, - finemessageTextCF, ¶mCFStringRec, NULL, &dialogRef); - if(err != noErr) { - goto end; - } - data.dialogWindow = GetDialogWindow(dialogRef); - err = ChkErr(ShowSheetWindow, data.dialogWindow, windowRef); - if(err != noErr) { - DisposeDialog(dialogRef); - goto end; - } - ChkErr(GetWindowModality, data.dialogWindow, &data.origModality, - &data.origUnavailWindow); - ChkErr(SetWindowModality, data.dialogWindow, kWindowModalityAppModal, - NULL); - ChkErr(InstallEventHandler, GetWindowEventTarget(data.dialogWindow), - handler, GetEventTypeCount(kEvents), kEvents, &data, - &data.handlerRef); - TkMacOSXTrackingLoop(1); - ChkErr(RunAppModalLoopForWindow, data.dialogWindow); - TkMacOSXTrackingLoop(0); - itemHit = data.buttonIndex; - } else { - err = ChkErr(CreateStandardAlert, alertType, messageTextCF, - finemessageTextCF, ¶mCFStringRec, &dialogRef); - if(err != noErr) { - goto end; - } - TkMacOSXTrackingLoop(1); - err = ChkErr(RunStandardAlert, dialogRef, NULL, &itemHit); - TkMacOSXTrackingLoop(0); - if (err != noErr) { - goto end; - } - } - if (err == noErr) { - /* - * Map 'itemHit' (1, 2, 3) to descriptive text string. - */ +} + +/* + * ---------------------------------------------------------------------- + * + * DeleteFontchooserData -- + * + * Clean up the font chooser configuration data when the interp is + * destroyed. + * + * Results: + * None. + * + * Side effects: + * per-interp configuration data is destroyed. + * + * ---------------------------------------------------------------------- + */ - int ind = nativeButtonIndexAndTypeToButtonIndex[typeIndex][itemHit]; +static void +DeleteFontchooserData( + ClientData clientData, + Tcl_Interp *interp) +{ + FontchooserData *fcdPtr = clientData; - Tcl_SetObjResult(interp, Tcl_NewStringObj(movableButtonStrings[ind], - -1)); - result = TCL_OK; + if (fcdPtr->titleObj) { + Tcl_DecrRefCount(fcdPtr->titleObj); } - - end: - if (finemessageTextCF) { - CFRelease(finemessageTextCF); + if (fcdPtr->cmdObj) { + Tcl_DecrRefCount(fcdPtr->cmdObj); } - if (messageTextCF) { - CFRelease(messageTextCF); + ckfree((char *)fcdPtr); + + if (fontchooserInterp == interp) { + fontchooserInterp = NULL; } - return result; } /* - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- * - * AlertHandler -- + * TkInitFontchooser -- * - * Carbon event handler for the Standard Sheet dialog. + * Associate the font chooser configuration data with the Tcl + * interpreter. There is one font chooser per interp. * * Results: - * OSStatus if event handled or not. + * None. * * Side effects: - * May set userData. + * per-interp configuration data is destroyed. * - *---------------------------------------------------------------------- + * ---------------------------------------------------------------------- */ -OSStatus -AlertHandler( - EventHandlerCallRef callRef, - EventRef eventRef, - void *userData) +MODULE_SCOPE int +TkInitFontchooser( + Tcl_Interp *interp, + ClientData clientData) { - AlertHandlerUserData *data = userData; - HICommand cmd; - - ChkErr(GetEventParameter,eventRef, kEventParamDirectObject, typeHICommand, - NULL, sizeof(cmd), NULL, &cmd); - switch (cmd.commandID) { - case kHICommandOK: - data->buttonIndex = 1; - break; - case kHICommandCancel: - data->buttonIndex = 2; - break; - case kHICommandOther: - data->buttonIndex = 3; - break; + FontchooserData *fcdPtr = (FontchooserData*) + ckalloc(sizeof(FontchooserData)); + + bzero(fcdPtr, sizeof(FontchooserData)); + Tcl_SetAssocData(interp, "::tk::fontchooser", DeleteFontchooserData, + fcdPtr); + if (!fontPanelFontAttributes) { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + fontPanelFontAttributes = [NSMutableDictionary new]; + [pool drain]; } - if (data->buttonIndex) { - ChkErr(QuitAppModalLoopForWindow, data->dialogWindow); - ChkErr(RemoveEventHandler, data->handlerRef); - ChkErr(SetWindowModality, data->dialogWindow, - data->origModality, data->origUnavailWindow); - } - return eventNotHandledErr; + return TCL_OK; } + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |