tkMacOSXDialog.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:45k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tkMacOSXDialog.c --
  3.  *
  4.  * Contains the Mac implementation of the common dialog boxes.
  5.  *
  6.  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
  7.  * Copyright 2001, Apple Computer, Inc.
  8.  * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net>
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.4.2.21 2007/11/09 06:26:55 das Exp $
  14.  */
  15. #include "tkMacOSXPrivate.h"
  16. #include "tkFileFilter.h"
  17. #ifndef StrLength
  18. #define StrLength(s) (*((unsigned char *) (s)))
  19. #endif
  20. #ifndef StrBody
  21. #define StrBody(s) ((char *) (s) + 1)
  22. #endif
  23. #define OPEN_POPUP_ITEM 10
  24. #define SAVE_FILE 0
  25. #define OPEN_FILE 1
  26. #define CHOOSE_FOLDER 2
  27. #define MATCHED 0
  28. #define UNMATCHED 1
  29. #define TK_DEFAULT_ABOUT 128
  30. /*
  31.  * The following structures are used in the GetFileName() function. They store
  32.  * information about the file dialog and the file filters.
  33.  */
  34. typedef struct _OpenFileData {
  35.     FileFilterList fl;          /* List of file filters.                   */
  36.     SInt16 curType;             /* The filetype currently being listed.    */
  37.     short initialType;          /* Type to use initially                   */
  38.     short popupItem;            /* Item number of the popup in the dialog. */
  39.     short usePopup;             /* True if we show the popup menu (this    */
  40.                                 /* is an open operation and the            */
  41.                                 /* -filetypes option is set).              */
  42. } OpenFileData;
  43. typedef struct NavHandlerUserData {
  44.     OpenFileData *ofdPtr;
  45.     NavReplyRecord reply;
  46.     OSStatus err;
  47.     CFStringRef saveNameRef;
  48.     int sheet;
  49.     WindowRef dialogWindow, origUnavailWindow;
  50.     WindowModality origModality;
  51. } NavHandlerUserData;
  52. /*
  53.  * The following structure is used in the tk_messageBox implementation.
  54.  */
  55. typedef struct {
  56.     int buttonIndex;
  57.     WindowRef dialogWindow, origUnavailWindow;
  58.     WindowModality origModality;
  59.     EventHandlerRef handlerRef;
  60. } AlertHandlerUserData;
  61. static OSStatus AlertHandler(EventHandlerCallRef callRef,
  62.     EventRef eventRef, void *userData);
  63. static Boolean MatchOneType(StringPtr fileNamePtr, OSType fileType,
  64.     OpenFileData *myofdPtr, FileFilter *filterPtr);
  65. static pascal Boolean OpenFileFilterProc(AEDesc* theItem, void* info,
  66.     NavCallBackUserData callBackUD,
  67.     NavFilterModes filterMode);
  68. static pascal void OpenEventProc(NavEventCallbackMessage callBackSelector,
  69.     NavCBRecPtr callBackParms,
  70.     NavCallBackUserData callBackUD);
  71. static void InitFileDialogs(void);
  72. static int NavServicesGetFile(Tcl_Interp *interp,
  73.     OpenFileData *ofd, AEDesc *initialDescPtr,
  74.     char *initialFile, AEDescList *selectDescPtr,
  75.     CFStringRef title, CFStringRef message,
  76.     const char *initialType, int multiple, int isOpen,
  77.     Tk_Window parent);
  78. static int HandleInitialDirectory(Tcl_Interp *interp,
  79.     char *initialFile, char *initialDir, FSRef *dirRef,
  80.     AEDescList *selectDescPtr, AEDesc *dirDescPtr);
  81. /*
  82.  * Have we initialized the file dialog subsystem
  83.  */
  84. static int fileDlgInited = 0;
  85. /*
  86.  * Filter and hook functions used by the tk_getOpenFile and tk_getSaveFile
  87.  * commands.
  88.  */
  89. static NavObjectFilterUPP openFileFilterUPP;
  90. static NavEventUPP openFileEventUPP;
  91. /*
  92.  *----------------------------------------------------------------------
  93.  *
  94.  * Tk_ChooseColorObjCmd --
  95.  *
  96.  * This procedure implements the color dialog box for the Mac
  97.  * platform. See the user documentation for details on what it
  98.  * does.
  99.  *
  100.  * Results:
  101.  * A standard Tcl result.
  102.  *
  103.  * Side effects:
  104.  * See the user documentation.
  105.  *
  106.  *----------------------------------------------------------------------
  107.  */
  108. int
  109. Tk_ChooseColorObjCmd(
  110.     ClientData clientData, /* Main window associated with interpreter. */
  111.     Tcl_Interp *interp, /* Current interpreter. */
  112.     int objc, /* Number of arguments. */
  113.     Tcl_Obj *CONST objv[]) /* Argument objects. */
  114. {
  115.     OSStatus err;
  116.     int result = TCL_ERROR;
  117.     Tk_Window parent, tkwin = (Tk_Window) clientData;
  118.     const char *title;
  119.     int i, srcRead, dstWrote;
  120.     CMError cmerr;
  121.     CMProfileRef prof;
  122.     NColorPickerInfo cpinfo;
  123.     static RGBColor color = {0xffff, 0xffff, 0xffff};
  124.     static const char *optionStrings[] = {
  125. "-initialcolor", "-parent", "-title", NULL
  126.     };
  127.     enum options {
  128. COLOR_INITIAL, COLOR_PARENT, COLOR_TITLE
  129.     };
  130.     title = "Choose a color:";
  131.     bzero(&cpinfo, sizeof(cpinfo));
  132.     cpinfo.theColor.color.rgb.red   = color.red;
  133.     cpinfo.theColor.color.rgb.green = color.green;
  134.     cpinfo.theColor.color.rgb.blue  = color.blue;
  135.     for (i = 1; i < objc; i += 2) {
  136. int index;
  137. const char *option, *value;
  138. if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option",
  139. TCL_EXACT, &index) != TCL_OK) {
  140.     goto end;
  141. }
  142. if (i + 1 == objc) {
  143.     option = Tcl_GetString(objv[i]);
  144.     Tcl_AppendResult(interp, "value for "", option, "" missing",
  145.     NULL);
  146.     goto end;
  147. }
  148. value = Tcl_GetString(objv[i + 1]);
  149. switch ((enum options) index) {
  150.     case COLOR_INITIAL: {
  151. XColor *colorPtr;
  152. colorPtr = Tk_GetColor(interp, tkwin, value);
  153. if (colorPtr == NULL) {
  154.     goto end;
  155. }
  156. cpinfo.theColor.color.rgb.red   = colorPtr->red;
  157. cpinfo.theColor.color.rgb.green = colorPtr->green;
  158. cpinfo.theColor.color.rgb.blue  = colorPtr->blue;
  159. Tk_FreeColor(colorPtr);
  160. break;
  161.     }
  162.     case COLOR_PARENT: {
  163. parent = Tk_NameToWindow(interp, value, tkwin);
  164. if (parent == NULL) {
  165.     goto end;
  166. }
  167. break;
  168.     }
  169.     case COLOR_TITLE: {
  170. title = value;
  171. break;
  172.     }
  173. }
  174.     }
  175.     cmerr = CMGetDefaultProfileBySpace(cmRGBData, &prof);
  176.     cpinfo.theColor.profile = prof;
  177.     cpinfo.dstProfile = prof;
  178.     cpinfo.flags = kColorPickerDialogIsMoveable | kColorPickerDialogIsModal;
  179.     cpinfo.placeWhere = kCenterOnMainScreen;
  180.     /* Currently, this does not actually change the colorpicker title */
  181.     Tcl_UtfToExternal(NULL, TkMacOSXCarbonEncoding, title, -1, 0, NULL,
  182. StrBody(cpinfo.prompt), 255, &srcRead, &dstWrote, NULL);
  183.     StrLength(cpinfo.prompt) = (unsigned char) dstWrote;
  184.     TkMacOSXTrackingLoop(1);
  185.     err = ChkErr(NPickColor, &cpinfo);
  186.     TkMacOSXTrackingLoop(0);
  187.     cmerr = CMCloseProfile(prof);
  188.     if ((err == noErr) && (cpinfo.newColorChosen != 0)) {
  189. char colorstr[8];
  190. color.red   = cpinfo.theColor.color.rgb.red;
  191. color.green = cpinfo.theColor.color.rgb.green;
  192. color.blue  = cpinfo.theColor.color.rgb.blue;
  193. snprintf(colorstr, 8, "#%02x%02x%02x", color.red >> 8,
  194. color.green >> 8, color.blue >> 8);
  195. Tcl_SetObjResult(interp, Tcl_NewStringObj(colorstr, 7));
  196.     } else {
  197. Tcl_ResetResult(interp);
  198.     }
  199.     result = TCL_OK;
  200. end:
  201.     return result;
  202. }
  203. /*
  204.  *----------------------------------------------------------------------
  205.  *
  206.  * Tk_GetOpenFileObjCmd --
  207.  *
  208.  * This procedure implements the "open file" dialog box for the
  209.  * Mac platform. See the user documentation for details on what
  210.  * it does.
  211.  *
  212.  * Results:
  213.  * A standard Tcl result.
  214.  *
  215.  * Side effects:
  216.  * See user documentation.
  217.  *----------------------------------------------------------------------
  218.  */
  219. int
  220. Tk_GetOpenFileObjCmd(
  221.     ClientData clientData, /* Main window associated with interpreter. */
  222.     Tcl_Interp *interp, /* Current interpreter. */
  223.     int objc, /* Number of arguments. */
  224.     Tcl_Obj *CONST objv[]) /* Argument objects. */
  225. {
  226.     int i, result = TCL_ERROR, multiple = 0;
  227.     OpenFileData ofd;
  228.     Tk_Window parent = NULL;
  229.     CFStringRef message = NULL, title = NULL;
  230.     AEDesc initialDesc = {typeNull, NULL};
  231.     FSRef dirRef;
  232.     AEDesc *initialPtr = NULL;
  233.     AEDescList selectDesc = {typeNull, NULL};
  234.     char *initialFile = NULL, *initialDir = NULL;
  235. #if 0
  236.     Tcl_Obj *typeVariablePtr = NULL;
  237. #endif
  238.     const char *initialtype = NULL;
  239.     static const char *openOptionStrings[] = {
  240. "-defaultextension", "-filetypes", "-initialdir", "-initialfile",
  241. "-message", "-multiple", "-parent", "-title",/* "-typevariable",*/ NULL
  242.     };
  243.     enum openOptions {
  244. OPEN_DEFAULT, OPEN_FILETYPES, OPEN_INITDIR, OPEN_INITFILE,
  245. OPEN_MESSAGE, OPEN_MULTIPLE, OPEN_PARENT, OPEN_TITLE,
  246. /*OPEN_TYPEVARIABLE,*/
  247.     };
  248.     if (!fileDlgInited) {
  249. InitFileDialogs();
  250.     }
  251.     TkInitFileFilters(&ofd.fl);
  252.     ofd.curType = 0;
  253.     ofd.initialType = -1;
  254.     ofd.popupItem = OPEN_POPUP_ITEM;
  255.     ofd.usePopup = 1;
  256.     for (i = 1; i < objc; i += 2) {
  257. char *choice;
  258. int index, choiceLen;
  259. char *string;
  260. char *types;
  261. if (Tcl_GetIndexFromObj(interp, objv[i], openOptionStrings, "option",
  262. TCL_EXACT, &index) != TCL_OK) {
  263.     goto end;
  264. }
  265. if (i + 1 == objc) {
  266.     string = Tcl_GetString(objv[i]);
  267.     Tcl_AppendResult(interp, "value for "", string, "" missing",
  268.     NULL);
  269.     goto end;
  270. }
  271. switch (index) {
  272.     case OPEN_DEFAULT:
  273. break;
  274.     case OPEN_FILETYPES:
  275. types = Tcl_GetString(objv[i + 1]);
  276. if (TkGetFileFilters(interp, &ofd.fl, types, 0) != TCL_OK) {
  277.     goto end;
  278. }
  279. break;
  280.     case OPEN_INITDIR:
  281. initialDir = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  282. /* empty strings should be like no selection given */
  283. if (choiceLen == 0) { initialDir = NULL; }
  284. break;
  285.     case OPEN_INITFILE:
  286. initialFile = Tcl_GetString(objv[i + 1]);
  287. /* empty strings should be like no selection given */
  288. if (choiceLen == 0) { initialFile = NULL; }
  289. break;
  290.     case OPEN_MESSAGE:
  291. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  292. message = CFStringCreateWithBytes(NULL, (unsigned char*)
  293. choice, choiceLen, kCFStringEncodingUTF8, false);
  294. break;
  295.     case OPEN_MULTIPLE:
  296. if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &multiple)
  297. != TCL_OK) {
  298.     goto end;
  299. }
  300. break;
  301.     case OPEN_PARENT:
  302. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  303. parent = Tk_NameToWindow(interp, choice,
  304. (Tk_Window) clientData);
  305. if (parent == NULL) {
  306.     goto end;
  307. }
  308. break;
  309.     case OPEN_TITLE:
  310. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  311. title = CFStringCreateWithBytes(NULL, (unsigned char*)
  312. choice, choiceLen, kCFStringEncodingUTF8, false);
  313. break;
  314. #if 0
  315.     case OPEN_TYPEVARIABLE:
  316.         typeVariablePtr = objv[i + 1];
  317.         break;
  318. #endif
  319. }
  320.     }
  321.     if (HandleInitialDirectory(interp, initialFile, initialDir, &dirRef,
  322.     &selectDesc, &initialDesc) != TCL_OK) {
  323. goto end;
  324.     }
  325.     if (initialDesc.descriptorType == typeFSRef) {
  326. initialPtr = &initialDesc;
  327.     }
  328. #if 0
  329.     if (typeVariablePtr) {
  330. initialtype = Tcl_GetVar(interp, Tcl_GetString(typeVariablePtr), 0);
  331.     }
  332. #endif
  333.     result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, &selectDesc,
  334.     title, message, initialtype, multiple, OPEN_FILE, parent);
  335. #if 0
  336.     if (typeVariablePtr) {
  337. FileFilter *filterPtr = ofd.fl.filters;
  338. int i = ofd.curType;
  339. while (filterPtr && i-- > 0) {
  340.     filterPtr = filterPtr->next;
  341. }
  342. Tcl_SetVar(interp, Tcl_GetString(typeVariablePtr), filterPtr->name, 0);
  343.     }
  344. #endif
  345. end:
  346.     TkFreeFileFilters(&ofd.fl);
  347.     if (initialDesc.dataHandle) {
  348. ChkErr(AEDisposeDesc, &initialDesc);
  349.     }
  350.     if (selectDesc.dataHandle) {
  351. ChkErr(AEDisposeDesc, &selectDesc);
  352.     }
  353.     if (title) {
  354. CFRelease(title);
  355.     }
  356.     if (message) {
  357. CFRelease(message);
  358.     }
  359.     return result;
  360. }
  361. /*
  362.  *----------------------------------------------------------------------
  363.  *
  364.  * Tk_GetSaveFileObjCmd --
  365.  *
  366.  * Same as Tk_GetOpenFileCmd but opens a "save file" dialog box
  367.  * instead
  368.  *
  369.  * Results:
  370.  * A standard Tcl result.
  371.  *
  372.  * Side effects:
  373.  * See user documentation.
  374.  *----------------------------------------------------------------------
  375.  */
  376. int
  377. Tk_GetSaveFileObjCmd(
  378.     ClientData clientData, /* Main window associated with interpreter. */
  379.     Tcl_Interp *interp, /* Current interpreter. */
  380.     int objc, /* Number of arguments. */
  381.     Tcl_Obj *CONST objv[]) /* Argument objects. */
  382. {
  383.     int i, result = TCL_ERROR;
  384.     char *initialFile = NULL;
  385.     Tk_Window parent = NULL;
  386.     AEDesc initialDesc = {typeNull, NULL};
  387.     AEDesc *initialPtr = NULL;
  388.     FSRef dirRef;
  389.     CFStringRef title = NULL, message = NULL;
  390.     OpenFileData ofd;
  391.     static const char *saveOptionStrings[] = {
  392. "-defaultextension", "-filetypes", "-initialdir", "-initialfile",
  393. "-message", "-parent", "-title",/* "-typevariable",*/ NULL
  394.     };
  395.     enum saveOptions {
  396. SAVE_DEFAULT, SAVE_FILETYPES, SAVE_INITDIR, SAVE_INITFILE,
  397. SAVE_MESSAGE, SAVE_PARENT, SAVE_TITLE,/* SAVE_TYPEVARIABLE,*/
  398.     };
  399.     if (!fileDlgInited) {
  400. InitFileDialogs();
  401.     }
  402.     TkInitFileFilters(&ofd.fl);
  403.     ofd.curType = 0;
  404.     ofd.usePopup = 0;
  405.     for (i = 1; i < objc; i += 2) {
  406. char *choice, *string;
  407. int index, choiceLen;
  408. char *types;
  409. if (Tcl_GetIndexFromObj(interp, objv[i], saveOptionStrings, "option",
  410. TCL_EXACT, &index) != TCL_OK) {
  411.     goto end;
  412. }
  413. if (i + 1 == objc) {
  414.     string = Tcl_GetString(objv[i]);
  415.     Tcl_AppendResult(interp, "value for "", string, "" missing",
  416.     NULL);
  417.     goto end;
  418. }
  419. switch (index) {
  420.     case SAVE_DEFAULT:
  421. break;
  422.     case SAVE_FILETYPES:
  423. types = Tcl_GetString(objv[i + 1]);
  424. if (TkGetFileFilters(interp, &ofd.fl, types, 0) != TCL_OK) {
  425.     goto end;
  426. }
  427. break;
  428.     case SAVE_INITDIR:
  429. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  430. /* empty strings should be like no selection given */
  431. if (choiceLen && HandleInitialDirectory(interp, NULL, choice,
  432. &dirRef, NULL, &initialDesc) != TCL_OK) {
  433.     goto end;
  434. }
  435. break;
  436.     case SAVE_INITFILE:
  437. initialFile = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  438. /* empty strings should be like no selection given */
  439. if (choiceLen == 0) {
  440.     initialFile = NULL;
  441. }
  442. break;
  443.     case SAVE_MESSAGE:
  444. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  445. message = CFStringCreateWithBytes(NULL, (unsigned char*)
  446. choice, choiceLen, kCFStringEncodingUTF8, false);
  447. break;
  448.     case SAVE_PARENT:
  449. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  450. parent = Tk_NameToWindow(interp, choice,
  451. (Tk_Window) clientData);
  452. if (parent == NULL) {
  453.     goto end;
  454. }
  455. break;
  456.     case SAVE_TITLE:
  457. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  458. title = CFStringCreateWithBytes(NULL, (unsigned char*)
  459. choice, choiceLen, kCFStringEncodingUTF8, false);
  460. break;
  461. }
  462.     }
  463.     if (initialDesc.descriptorType == typeFSRef) {
  464. initialPtr = &initialDesc;
  465.     }
  466.     result = NavServicesGetFile(interp, &ofd, initialPtr, initialFile, NULL,
  467.     title, message, NULL, false, SAVE_FILE, parent);
  468.     TkFreeFileFilters(&ofd.fl);
  469. end:
  470.     if (initialDesc.dataHandle) {
  471. ChkErr(AEDisposeDesc, &initialDesc);
  472.     }
  473.     if (title) {
  474. CFRelease(title);
  475.     }
  476.     if (message) {
  477. CFRelease(message);
  478.     }
  479.     return result;
  480. }
  481. /*
  482.  *----------------------------------------------------------------------
  483.  *
  484.  * Tk_ChooseDirectoryObjCmd --
  485.  *
  486.  * This procedure implements the "tk_chooseDirectory" dialog box
  487.  * for the Windows platform. See the user documentation for details
  488.  * on what it does.
  489.  *
  490.  * Results:
  491.  * See user documentation.
  492.  *
  493.  * Side effects:
  494.  * A modal dialog window is created.
  495.  *
  496.  *----------------------------------------------------------------------
  497.  */
  498. int
  499. Tk_ChooseDirectoryObjCmd(clientData, interp, objc, objv)
  500.     ClientData clientData; /* Main window associated with interpreter. */
  501.     Tcl_Interp *interp; /* Current interpreter. */
  502.     int objc; /* Number of arguments. */
  503.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  504. {
  505.     int i, result = TCL_ERROR;
  506.     Tk_Window parent = NULL;
  507.     AEDesc initialDesc = {typeNull, NULL}, *initialPtr = NULL;
  508.     FSRef dirRef;
  509.     CFStringRef message = NULL, title = NULL;
  510.     OpenFileData ofd;
  511.     static const char *chooseOptionStrings[] = {
  512. "-initialdir", "-message", "-mustexist", "-parent", "-title", NULL
  513.     };
  514.     enum chooseOptions {
  515. CHOOSE_INITDIR, CHOOSE_MESSAGE, CHOOSE_MUSTEXIST, CHOOSE_PARENT,
  516. CHOOSE_TITLE
  517.     };
  518.     if (!fileDlgInited) {
  519. InitFileDialogs();
  520.     }
  521.     for (i = 1; i < objc; i += 2) {
  522. char *choice;
  523. int index, choiceLen;
  524. char *string;
  525. if (Tcl_GetIndexFromObj(interp, objv[i], chooseOptionStrings, "option",
  526. TCL_EXACT, &index) != TCL_OK) {
  527.     goto end;
  528. }
  529. if (i + 1 == objc) {
  530.     string = Tcl_GetString(objv[i]);
  531.     Tcl_AppendResult(interp, "value for "", string, "" missing",
  532.     NULL);
  533.     goto end;
  534. }
  535. switch (index) {
  536.     case CHOOSE_INITDIR:
  537. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  538. if (choiceLen && HandleInitialDirectory(interp, NULL, choice,
  539. &dirRef, NULL, &initialDesc) != TCL_OK) {
  540.     goto end;
  541. }
  542. break;
  543.     case CHOOSE_MESSAGE:
  544. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  545. message = CFStringCreateWithBytes(NULL, (unsigned char*)
  546. choice, choiceLen, kCFStringEncodingUTF8, false);
  547. break;
  548.     case CHOOSE_PARENT:
  549. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  550. parent = Tk_NameToWindow(interp, choice,
  551. (Tk_Window) clientData);
  552. if (parent == NULL) {
  553.     goto end;
  554. }
  555. break;
  556.     case CHOOSE_TITLE:
  557. choice = Tcl_GetStringFromObj(objv[i + 1], &choiceLen);
  558. title = CFStringCreateWithBytes(NULL, (unsigned char*) choice,
  559. choiceLen, kCFStringEncodingUTF8, false);
  560. break;
  561. }
  562.     }
  563.     TkInitFileFilters(&ofd.fl);
  564.     ofd.usePopup = 0;
  565.     if (initialDesc.descriptorType == typeFSRef) {
  566. initialPtr = &initialDesc;
  567.     }
  568.     result = NavServicesGetFile(interp, &ofd, initialPtr, NULL, NULL, title,
  569.     message, NULL, false, CHOOSE_FOLDER, parent);
  570.     TkFreeFileFilters(&ofd.fl);
  571. end:
  572.     if (initialDesc.dataHandle) {
  573. ChkErr(AEDisposeDesc, &initialDesc);
  574.     }
  575.     if (title) {
  576. CFRelease(title);
  577.     }
  578.     if (message) {
  579. CFRelease(message);
  580.     }
  581.     return result;
  582. }
  583. /*
  584.  *----------------------------------------------------------------------
  585.  *
  586.  * HandleInitialDirectory --
  587.  *
  588.  * Helper for -initialdir setup.
  589.  *
  590.  * Results:
  591.  * Tcl result.
  592.  *
  593.  * Side effects:
  594.  * None.
  595.  *
  596.  *----------------------------------------------------------------------
  597.  */
  598. int
  599. HandleInitialDirectory(
  600.     Tcl_Interp *interp,
  601.     char *initialFile,
  602.     char *initialDir,
  603.     FSRef *dirRef,
  604.     AEDescList *selectDescPtr,
  605.     AEDesc *dirDescPtr)
  606. {
  607.     Tcl_DString ds;
  608.     OSStatus err;
  609.     Boolean isDirectory;
  610.     char *dirName = NULL;
  611.     int result = TCL_ERROR;
  612.     if (initialDir) {
  613. dirName = Tcl_TranslateFileName(interp, initialDir, &ds);
  614. if (dirName == NULL) {
  615.     goto end;
  616. }
  617. err = ChkErr(FSPathMakeRef, (unsigned char*) dirName,
  618. dirRef, &isDirectory);
  619. if (err != noErr) {
  620.     Tcl_AppendResult(interp, "bad directory "", initialDir, """,
  621.     NULL);
  622.     goto end;
  623. }
  624. if (!isDirectory) {
  625.     Tcl_AppendResult(interp, "-intialdir "",
  626.     initialDir, " is a file, not a directory."", NULL);
  627.     goto end;
  628. }
  629. ChkErr(AECreateDesc, typeFSRef, dirRef, sizeof(*dirRef), dirDescPtr);
  630.     }
  631.     if (initialFile && selectDescPtr) {
  632. FSRef fileRef;
  633. AEDesc fileDesc;
  634. char *namePtr;
  635. if (initialDir) {
  636.     Tcl_DStringAppend(&ds, "/", 1);
  637.     Tcl_DStringAppend(&ds, initialFile, -1);
  638.     namePtr = Tcl_DStringValue(&ds);
  639. } else {
  640.     namePtr = initialFile;
  641. }
  642. ChkErr(AECreateList, NULL, 0, false, selectDescPtr);
  643. err = ChkErr(FSPathMakeRef, (unsigned char*) namePtr, &fileRef,
  644. &isDirectory);
  645. if (err != noErr) {
  646.     Tcl_AppendResult(interp, "bad initialfile "", initialFile,
  647.     "" file does not exist.", NULL);
  648.     goto end;
  649. }
  650. ChkErr(AECreateDesc, typeFSRef, &fileRef, sizeof(fileRef), &fileDesc);
  651. ChkErr(AEPutDesc, selectDescPtr, 1, &fileDesc);
  652. ChkErr(AEDisposeDesc, &fileDesc);
  653.     }
  654.     result = TCL_OK;
  655. end:
  656.     if (dirName) {
  657. Tcl_DStringFree(&ds);
  658.     }
  659.     return result;
  660. }
  661. /*
  662.  *----------------------------------------------------------------------
  663.  *
  664.  * InitFileDialogs --
  665.  *
  666.  * Initialize file dialog subsystem.
  667.  *
  668.  * Results:
  669.  * None.
  670.  *
  671.  * Side effects:
  672.  * None.
  673.  *
  674.  *----------------------------------------------------------------------
  675.  */
  676. void
  677. InitFileDialogs(void)
  678. {
  679.     fileDlgInited = 1;
  680.     openFileFilterUPP = NewNavObjectFilterUPP(OpenFileFilterProc);
  681.     openFileEventUPP = NewNavEventUPP(OpenEventProc);
  682. }
  683. /*
  684.  *----------------------------------------------------------------------
  685.  *
  686.  * NavServicesGetFile --
  687.  *
  688.  * Common wrapper for NavServices API.
  689.  *
  690.  * Results:
  691.  * Tcl result.
  692.  *
  693.  * Side effects:
  694.  * None.
  695.  *
  696.  *----------------------------------------------------------------------
  697.  */
  698. int
  699. NavServicesGetFile(
  700.     Tcl_Interp *interp,
  701.     OpenFileData *ofdPtr,
  702.     AEDesc *initialDescPtr,
  703.     char *initialFile,
  704.     AEDescList *selectDescPtr,
  705.     CFStringRef title,
  706.     CFStringRef message,
  707.     const char *initialtype,
  708.     int multiple,
  709.     int isOpen,
  710.     Tk_Window parent)
  711. {
  712.     NavHandlerUserData data;
  713.     NavDialogCreationOptions options;
  714.     NavDialogRef dialogRef = NULL;
  715.     CFStringRef * menuItemNames = NULL;
  716.     OSStatus err;
  717.     Tcl_Obj *theResult = NULL;
  718.     int result = TCL_ERROR;
  719.     bzero(&data, sizeof(data));
  720.     err = NavGetDefaultDialogCreationOptions(&options);
  721.     if (err != noErr) {
  722. return result;
  723.     }
  724.     options.optionFlags = kNavDontAutoTranslate | kNavDontAddTranslateItems
  725.     | kNavSupportPackages | kNavAllFilesInPopup;
  726.     if (multiple) {
  727. options.optionFlags |= kNavAllowMultipleFiles;
  728.     }
  729.     options.modality = kWindowModalityAppModal;
  730.     if (parent && ((TkWindow*)parent)->window != None &&
  731.     TkMacOSXHostToplevelExists(parent)) {
  732. options.parentWindow = TkMacOSXDrawableWindow(Tk_WindowId(parent));
  733. TK_IF_HI_TOOLBOX (5,
  734.     /*
  735.      * Impossible to modify dialog modality with the Cocoa-based
  736.      * NavServices implementation.
  737.      */
  738. ) TK_ELSE_HI_TOOLBOX (5,
  739.     if (options.parentWindow) {
  740. options.modality = kWindowModalityWindowModal;
  741. data.sheet = 1;
  742.     }
  743. ) TK_ENDIF
  744.     }
  745.     /*
  746.      * Now process the selection list. We have to use the popupExtension
  747.      * to fill the menu.
  748.      */
  749.     if (ofdPtr && ofdPtr->usePopup) {
  750. FileFilter *filterPtr;
  751. filterPtr = ofdPtr->fl.filters;
  752. if (filterPtr == NULL) {
  753.     ofdPtr->usePopup = 0;
  754. }
  755.     }
  756.     if (ofdPtr && ofdPtr->usePopup) {
  757. FileFilter *filterPtr;
  758. int index = 0;
  759. ofdPtr->curType = 0;
  760. menuItemNames = (CFStringRef *) ckalloc(ofdPtr->fl.numFilters
  761.     * sizeof(CFStringRef));
  762. for (filterPtr = ofdPtr->fl.filters; filterPtr != NULL;
  763. filterPtr = filterPtr->next, index++) {
  764.     menuItemNames[index] = CFStringCreateWithCString(NULL,
  765.     filterPtr->name, kCFStringEncodingUTF8);
  766.     if (initialtype && strcmp(filterPtr->name, initialtype) == 0) {
  767. ofdPtr->initialType = index;
  768.     }
  769. }
  770. options.popupExtension = CFArrayCreate(NULL,
  771. (const void **) menuItemNames, ofdPtr->fl.numFilters, NULL);
  772.     } else {
  773. options.optionFlags |= kNavNoTypePopup;
  774. options.popupExtension = NULL;
  775.     }
  776.     options.clientName = CFSTR("Wish");
  777.     options.message = message;
  778.     options.windowTitle = title;
  779.     if (initialFile) {
  780. options.saveFileName = CFStringCreateWithCString(NULL,
  781. initialFile, kCFStringEncodingUTF8);
  782.     } else {
  783. options.saveFileName = NULL;
  784.     }
  785.     if (isOpen == OPEN_FILE) {
  786. data.ofdPtr = ofdPtr;
  787. err = ChkErr(NavCreateGetFileDialog, &options, NULL,
  788. openFileEventUPP, NULL, openFileFilterUPP, &data, &dialogRef);
  789.     } else if (isOpen == SAVE_FILE) {
  790. err = ChkErr(NavCreatePutFileDialog, &options, 'TEXT', 'WIsH',
  791. openFileEventUPP, &data, &dialogRef);
  792.     } else if (isOpen == CHOOSE_FOLDER) {
  793. err = ChkErr(NavCreateChooseFolderDialog, &options,
  794. openFileEventUPP, openFileFilterUPP, &data, &dialogRef);
  795.     }
  796.     if (err == noErr && dialogRef) {
  797. if (initialDescPtr) {
  798.     ChkErr(NavCustomControl, dialogRef, kNavCtlSetLocation,
  799. initialDescPtr);
  800. }
  801. if (selectDescPtr && selectDescPtr->descriptorType != typeNull) {
  802.     ChkErr(NavCustomControl, dialogRef, kNavCtlSetSelection,
  803.     selectDescPtr);
  804. }
  805. TkMacOSXTrackingLoop(1);
  806. err = ChkErr(NavDialogRun, dialogRef);
  807. if (err == noErr) {
  808.     if (data.sheet) {
  809. data.dialogWindow = NavDialogGetWindow(dialogRef);
  810. ChkErr(GetWindowModality, data.dialogWindow,
  811. &data.origModality, &data.origUnavailWindow);
  812. ChkErr(SetWindowModality, data.dialogWindow,
  813. kWindowModalityAppModal, NULL);
  814. ChkErr(RunAppModalLoopForWindow, data.dialogWindow);
  815.     }
  816.     err = data.err;
  817. }
  818. TkMacOSXTrackingLoop(0);
  819.     }
  820.     /*
  821.      * Most commands assume that the file dialogs return a single
  822.      * item, not a list. So only build a list if multiple is true...
  823.      */
  824.     if (err == noErr) {
  825. if (multiple) {
  826.     theResult = Tcl_NewListObj(0, NULL);
  827. } else {
  828.     theResult = Tcl_NewObj();
  829. }
  830. if (!theResult) {
  831.     err = memFullErr;
  832. }
  833.     }
  834.     if (err == noErr && data.reply.validRecord) {
  835. AEDesc resultDesc;
  836. long count;
  837. FSRef fsRef;
  838. char pathPtr[PATH_MAX + 1];
  839. err = ChkErr(AECountItems, &data.reply.selection, &count);
  840. if (err == noErr) {
  841.     long i;
  842.     for (i = 1; i <= count; i++) {
  843. err = ChkErr(AEGetNthDesc, &data.reply.selection, i,
  844. typeFSRef, NULL, &resultDesc);
  845. if (err == noErr) {
  846.     err = ChkErr(AEGetDescData, &resultDesc, &fsRef,
  847.     sizeof(fsRef));
  848.     if (err == noErr) {
  849. err = ChkErr(FSRefMakePath, &fsRef, (unsigned char*)
  850. pathPtr, PATH_MAX + 1);
  851. if (err == noErr) {
  852.     int pathValid = 0;
  853.     if (isOpen == SAVE_FILE) {
  854. if (data.saveNameRef) {
  855.     char saveName [PATH_MAX + 1];
  856.     if (CFStringGetCString(data.saveNameRef,
  857.     saveName, PATH_MAX + 1,
  858.     kCFStringEncodingUTF8)) {
  859. if (strlen(pathPtr) + strlen(saveName)
  860. < PATH_MAX) {
  861.     strcat(pathPtr, "/");
  862.     strcat(pathPtr, saveName);
  863.     pathValid = 1;
  864. } else {
  865.     TkMacOSXDbgMsg("Path name too "
  866.     "long");
  867. }
  868.     } else {
  869. TkMacOSXDbgMsg("CFStringGetCString "
  870. "failed");
  871.     }
  872. } else {
  873.     TkMacOSXDbgMsg("NavDialogGetSaveFileName "
  874.     "failed");
  875. }
  876.     } else {
  877. pathValid = 1;
  878.     }
  879.     if (pathValid) {
  880. if (multiple) {
  881.     Tcl_ListObjAppendElement(interp, theResult,
  882. Tcl_NewStringObj(pathPtr, -1));
  883. } else {
  884.     Tcl_SetStringObj(theResult, pathPtr, -1);
  885. }
  886.     }
  887. }
  888.     }
  889.     ChkErr(AEDisposeDesc, &resultDesc);
  890. }
  891.     }
  892. }
  893. Tcl_SetObjResult(interp, theResult);
  894. result = TCL_OK;
  895.     } else if (err == userCanceledErr) {
  896. Tcl_ResetResult(interp);
  897. result = TCL_OK;
  898.     }
  899.     /*
  900.      * Clean up any allocated memory.
  901.      */
  902.     if (data.reply.validRecord) {
  903. ChkErr(NavDisposeReply, &data.reply);
  904.     }
  905.     if (data.saveNameRef) {
  906. CFRelease(data.saveNameRef);
  907.     }
  908.     if (options.saveFileName) {
  909. CFRelease(options.saveFileName);
  910.     }
  911.     if (options.clientName) {
  912. CFRelease(options.clientName);
  913.     }
  914.     if (menuItemNames) {
  915. int i;
  916. for (i = 0; i < ofdPtr->fl.numFilters; i++) {
  917.     CFRelease(menuItemNames[i]);
  918. }
  919. ckfree((void *)menuItemNames);
  920.     }
  921.     if (options.popupExtension) {
  922. CFRelease(options.popupExtension);
  923.     }
  924.     return result;
  925. }
  926. /*
  927.  *----------------------------------------------------------------------
  928.  *
  929.  * OpenEventProc --
  930.  *
  931.  * NavServices event handling callback.
  932.  *
  933.  * Results:
  934.  * None.
  935.  *
  936.  * Side effects:
  937.  * None.
  938.  *
  939.  *----------------------------------------------------------------------
  940.  */
  941. pascal void
  942. OpenEventProc(
  943.     NavEventCallbackMessage callBackSelector,
  944.     NavCBRecPtr callBackParams,
  945.     NavCallBackUserData callBackUD)
  946. {
  947.     NavHandlerUserData *data = (NavHandlerUserData*) callBackUD;
  948.     OpenFileData *ofd = data->ofdPtr;
  949.     switch (callBackSelector) {
  950. case kNavCBStart:
  951.     if (ofd && ofd->initialType >= 0) {
  952. /* Select initial filter */
  953. FileFilter *filterPtr = ofd->fl.filters;
  954. int i = ofd->initialType;
  955. while (filterPtr && i-- > 0) {
  956.     filterPtr = filterPtr->next;
  957. }
  958. if (filterPtr) {
  959.     NavMenuItemSpec selectItem;
  960.     selectItem.version = kNavMenuItemSpecVersion;
  961.     selectItem.menuCreator = 0;
  962.     selectItem.menuType = ofd->initialType;
  963.     selectItem.menuItemName[0] = strlen(filterPtr->name);
  964.     strncpy((char*) &selectItem.menuItemName[1],
  965.     filterPtr->name, 255);
  966.     ChkErr(NavCustomControl, callBackParams->context,
  967.     kNavCtlSelectCustomType, &selectItem);
  968. }
  969.     }
  970.     break;
  971. case kNavCBPopupMenuSelect:
  972.     ofd->curType = ((NavMenuItemSpec *)
  973.     callBackParams->eventData.eventDataParms.param)->menuType;
  974.     break;
  975. case kNavCBAccept:
  976. case kNavCBCancel:
  977.     if (data->sheet) {
  978. ChkErr(QuitAppModalLoopForWindow, data->dialogWindow);
  979. ChkErr(SetWindowModality, data->dialogWindow,
  980. data->origModality, data->origUnavailWindow);
  981.     }
  982.     break;
  983. case kNavCBUserAction:
  984.     if (data->reply.validRecord) {
  985. ChkErr(NavDisposeReply, &data->reply);
  986. data->reply.validRecord = 0;
  987.     }
  988.     data->err = NavDialogGetReply(callBackParams->context,
  989.     &data->reply);
  990.     if (callBackParams->userAction == kNavUserActionSaveAs) {
  991. data->saveNameRef = NavDialogGetSaveFileName(
  992. callBackParams->context);
  993. if (data->saveNameRef) {
  994.     CFRetain(data->saveNameRef);
  995. }
  996.     }
  997.     break;
  998. case kNavCBTerminate:
  999.     NavDialogDispose(callBackParams->context);
  1000.     break;
  1001. case kNavCBEvent:
  1002.     TkMacOSXRunTclEventLoop();
  1003.     break;
  1004.     }
  1005. }
  1006. /*
  1007.  *----------------------------------------------------------------------
  1008.  *
  1009.  * OpenFileFilterProc --
  1010.  *
  1011.  * NavServices file filter callback.
  1012.  *
  1013.  * Results:
  1014.  * Whether to use the file in question.
  1015.  *
  1016.  * Side effects:
  1017.  * None.
  1018.  *
  1019.  *----------------------------------------------------------------------
  1020.  */
  1021. pascal Boolean
  1022. OpenFileFilterProc(
  1023.     AEDesc* theItem, void* info,
  1024.     NavCallBackUserData callBackUD,
  1025.     NavFilterModes filterMode)
  1026. {
  1027.     OpenFileData *ofdPtr = ((NavHandlerUserData*) callBackUD)->ofdPtr;
  1028.     int result = MATCHED;
  1029.     if (ofdPtr && ofdPtr->usePopup) {
  1030. if (ofdPtr->fl.numFilters > 0) {
  1031.     if ((theItem->descriptorType == typeFSS)
  1032.     || (theItem->descriptorType == typeFSRef)) {
  1033. NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo *) info;
  1034. char fileName[256];
  1035. if (!theInfo->isFolder) {
  1036.     OSType fileType;
  1037.     StringPtr fileNamePtr = NULL;
  1038.     Tcl_DString fileNameDString;
  1039.     int i;
  1040.     FileFilter *filterPtr;
  1041.     fileType =
  1042.     theInfo->fileAndFolder.fileInfo.finderInfo.fdType;
  1043.     Tcl_DStringInit (&fileNameDString);
  1044.     if (theItem->descriptorType == typeFSS) {
  1045. int len;
  1046. fileNamePtr = ((FSSpec *) *theItem->dataHandle)->name;
  1047. len = fileNamePtr[0];
  1048. strncpy(fileName, (char*) fileNamePtr + 1, len);
  1049. fileName[len] = '';
  1050. fileNamePtr = (unsigned char*) fileName;
  1051.     } else if ((theItem->descriptorType == typeFSRef)) {
  1052. OSStatus err;
  1053. FSRef *theRef = (FSRef *) *theItem->dataHandle;
  1054. HFSUniStr255 uniFileName;
  1055. err = ChkErr(FSGetCatalogInfo, theRef, kFSCatInfoNone,
  1056. NULL, &uniFileName, NULL, NULL);
  1057. if (err == noErr) {
  1058.     Tcl_UniCharToUtfDString (
  1059.     (Tcl_UniChar *) uniFileName.unicode,
  1060.     uniFileName.length, &fileNameDString);
  1061.     fileNamePtr = (unsigned char*)
  1062.     Tcl_DStringValue(&fileNameDString);
  1063. }
  1064.     }
  1065.     if (ofdPtr->usePopup) {
  1066. i = ofdPtr->curType;
  1067. for (filterPtr = ofdPtr->fl.filters;
  1068. filterPtr && i > 0; i--) {
  1069.     filterPtr = filterPtr->next;
  1070. }
  1071. if (filterPtr) {
  1072.     result = MatchOneType(fileNamePtr, fileType,
  1073.     ofdPtr, filterPtr);
  1074. } else {
  1075.     result = UNMATCHED;
  1076. }
  1077.     } else {
  1078. /*
  1079.  * We are not using the popup menu. In this case, the
  1080.  * file is considered matched if it matches any of
  1081.  * the file filters.
  1082.  */
  1083. result = UNMATCHED;
  1084. for (filterPtr = ofdPtr->fl.filters; filterPtr;
  1085. filterPtr = filterPtr->next) {
  1086.     if (MatchOneType(fileNamePtr, fileType,
  1087.     ofdPtr, filterPtr) == MATCHED) {
  1088. result = MATCHED;
  1089. break;
  1090.     }
  1091. }
  1092.     }
  1093.     Tcl_DStringFree (&fileNameDString);
  1094. }
  1095.     }
  1096. }
  1097.     }
  1098.     return (result == MATCHED);
  1099. }
  1100. /*
  1101.  *----------------------------------------------------------------------
  1102.  *
  1103.  * MatchOneType --
  1104.  *
  1105.  * Match a file with one file type in the list of file types.
  1106.  *
  1107.  * Results:
  1108.  * Returns MATCHED if the file matches with the file type; returns
  1109.  * UNMATCHED otherwise.
  1110.  *
  1111.  * Side effects:
  1112.  * None
  1113.  *
  1114.  *----------------------------------------------------------------------
  1115.  */
  1116. Boolean
  1117. MatchOneType(
  1118.     StringPtr fileNamePtr, /* Name of the file */
  1119.     OSType fileType, /* Type of the file, 0 means there was no
  1120.  * specified type. */
  1121.     OpenFileData *ofdPtr, /* Information about this file dialog */
  1122.     FileFilter *filterPtr) /* Match the file described by pb against this
  1123.  * filter */
  1124. {
  1125.     FileFilterClause *clausePtr;
  1126.     /*
  1127.      * A file matches with a file type if it matches with at least one
  1128.      * clause of the type.
  1129.      *
  1130.      * If the clause has both glob patterns and ostypes, the file must
  1131.      * match with at least one pattern AND at least one ostype.
  1132.      *
  1133.      * If the clause has glob patterns only, the file must match with at least
  1134.      * one pattern.
  1135.      *
  1136.      * If the clause has mac types only, the file must match with at least
  1137.      * one mac type.
  1138.      *
  1139.      * If the clause has neither glob patterns nor mac types, it's
  1140.      * considered an error.
  1141.      */
  1142.     for (clausePtr = filterPtr->clauses; clausePtr;
  1143.     clausePtr = clausePtr->next) {
  1144. int macMatched = 0;
  1145. int globMatched = 0;
  1146. GlobPattern *globPtr;
  1147. MacFileType *mfPtr;
  1148. if (clausePtr->patterns == NULL) {
  1149.     globMatched = 1;
  1150. }
  1151. if (clausePtr->macTypes == NULL) {
  1152.     macMatched = 1;
  1153. }
  1154. for (globPtr = clausePtr->patterns; globPtr;
  1155. globPtr = globPtr->next) {
  1156.     char *q, *ext;
  1157.     if (fileNamePtr == NULL) {
  1158. continue;
  1159.     }
  1160.     ext = globPtr->pattern;
  1161.     if (ext[0] == '') {
  1162. /*
  1163.  * We don't want any extensions: OK if the filename doesn't
  1164.  * have "." in it
  1165.  */
  1166. for (q = (char*) fileNamePtr; *q; q++) {
  1167.     if (*q == '.') {
  1168. goto glob_unmatched;
  1169.     }
  1170. }
  1171. goto glob_matched;
  1172.     }
  1173.     if (Tcl_StringMatch((char*) fileNamePtr, ext)) {
  1174. goto glob_matched;
  1175.     } else {
  1176. goto glob_unmatched;
  1177.     }
  1178. glob_unmatched:
  1179.     continue;
  1180. glob_matched:
  1181.     globMatched = 1;
  1182.     break;
  1183. }
  1184. for (mfPtr = clausePtr->macTypes; mfPtr; mfPtr = mfPtr->next) {
  1185.     if (fileType == mfPtr->type) {
  1186. macMatched = 1;
  1187. break;
  1188.     }
  1189. }
  1190. /*
  1191.  * On Mac OS X, it is not uncommon for files to have NO
  1192.  * file type. But folks with Tcl code on Classic MacOS pretty
  1193.  * much assume that a generic file will have type TEXT. So
  1194.  * if we were strict about matching types when the source file
  1195.  * had NO type set, they would have to add another rule always
  1196.  * with no fileType. To avoid that, we pass the macMatch side
  1197.  * of the test if no fileType is set.
  1198.  */
  1199. if (globMatched && (macMatched || (fileType == 0))) {
  1200.     return MATCHED;
  1201. }
  1202.     }
  1203.     return UNMATCHED;
  1204. }
  1205. /*
  1206.  *----------------------------------------------------------------------
  1207.  *
  1208.  * TkAboutDlg --
  1209.  *
  1210.  * Displays the default Tk About box. This code uses Macintosh
  1211.  * resources to define the content of the About Box.
  1212.  *
  1213.  * Results:
  1214.  * None.
  1215.  *
  1216.  * Side effects:
  1217.  * None.
  1218.  *
  1219.  *----------------------------------------------------------------------
  1220.  */
  1221. void
  1222. TkAboutDlg(void)
  1223. {
  1224.     DialogPtr aboutDlog;
  1225.     WindowRef windowRef;
  1226.     short itemHit = -9;
  1227.     aboutDlog = GetNewDialog(TK_DEFAULT_ABOUT, NULL, (void *) (-1));
  1228.     if (!aboutDlog) {
  1229. return;
  1230.     }
  1231.     windowRef = GetDialogWindow(aboutDlog);
  1232.     SelectWindow(windowRef);
  1233.     TkMacOSXTrackingLoop(1);
  1234.     while (itemHit != 1) {
  1235. ModalDialog(NULL, &itemHit);
  1236.     }
  1237.     TkMacOSXTrackingLoop(0);
  1238.     DisposeDialog(aboutDlog);
  1239.     SelectWindow(ActiveNonFloatingWindow());
  1240. }
  1241. /*
  1242.  *----------------------------------------------------------------------
  1243.  *
  1244.  * Tk_MessageBoxObjCmd --
  1245.  *
  1246.  * Implements the tk_messageBox in native Mac OS X style.
  1247.  *
  1248.  * Results:
  1249.  * A standard Tcl result.
  1250.  *
  1251.  * Side effects:
  1252.  * none
  1253.  *
  1254.  *----------------------------------------------------------------------
  1255.  */
  1256. int
  1257. Tk_MessageBoxObjCmd(
  1258.     ClientData clientData, /* Main window associated with interpreter. */
  1259.     Tcl_Interp *interp, /* Current interpreter. */
  1260.     int objc, /* Number of arguments. */
  1261.     Tcl_Obj *CONST objv[]) /* Argument objects. */
  1262. {
  1263.     Tk_Window tkwin = (Tk_Window) clientData;
  1264.     AlertStdCFStringAlertParamRec paramCFStringRec;
  1265.     AlertType alertType;
  1266.     DialogRef dialogRef;
  1267.     CFStringRef messageTextCF = NULL, finemessageTextCF = NULL;
  1268.     OSStatus err;
  1269.     SInt16 itemHit;
  1270.     Boolean haveDefaultOption = false, haveParentOption = false;
  1271.     char *str;
  1272.     int index, defaultButtonIndex;
  1273.     int defaultNativeButtonIndex; /* 1, 2, 3: right to left */
  1274.     int typeIndex, i, indexDefaultOption = 0, result = TCL_ERROR;
  1275.     static const char *movableAlertStrings[] = {
  1276. "-default",/* "-detail",*/ "-icon", "-message", "-parent", "-title",
  1277. "-type", NULL
  1278.     };
  1279.     static const char *movableTypeStrings[] = {
  1280. "abortretryignore", "ok", "okcancel", "retrycancel", "yesno",
  1281. "yesnocancel", NULL
  1282.     };
  1283.     static const char *movableButtonStrings[] = {
  1284. "abort", "retry", "ignore", "ok", "cancel", "yes", "no", NULL
  1285.     };
  1286.     static const char *movableIconStrings[] = {
  1287. "error", "info", "question", "warning", NULL
  1288.     };
  1289.     enum movableAlertOptions {
  1290. ALERT_DEFAULT,/* ALERT_DETAIL,*/ ALERT_ICON, ALERT_MESSAGE, ALERT_PARENT,
  1291. ALERT_TITLE, ALERT_TYPE
  1292.     };
  1293.     enum movableTypeOptions {
  1294. TYPE_ABORTRETRYIGNORE, TYPE_OK, TYPE_OKCANCEL, TYPE_RETRYCANCEL,
  1295. TYPE_YESNO, TYPE_YESNOCANCEL
  1296.     };
  1297.     enum movableButtonOptions {
  1298. TEXT_ABORT, TEXT_RETRY, TEXT_IGNORE, TEXT_OK, TEXT_CANCEL, TEXT_YES,
  1299. TEXT_NO
  1300.     };
  1301.     enum movableIconOptions {
  1302. ICON_ERROR, ICON_INFO, ICON_QUESTION, ICON_WARNING
  1303.     };
  1304.     /*
  1305.      * Need to map from 'movableButtonStrings' and its corresponding integer,
  1306.      * index to the native button index, which is 1, 2, 3, from right to left.
  1307.      * This is necessary to do for each separate '-type' of button sets.
  1308.      */
  1309.     short buttonIndexAndTypeToNativeButtonIndex[][7] = {
  1310.     /* abort retry ignore ok cancel yes   no */
  1311. {1,    2,    3,    0,  0,    0,    0}, /* abortretryignore */
  1312. {0,    0,    0,    1,  0,    0,    0}, /* ok */
  1313. {0,    0,    0,    1,  2,    0,    0}, /* okcancel */
  1314. {0,    1,    0,    0,  2,    0,    0}, /* retrycancel */
  1315. {0,    0,    0,    0,  0,    1,    2}, /* yesno */
  1316. {0,    0,    0,    0,  3,    1,    2}, /* yesnocancel */
  1317.     };
  1318.     /*
  1319.      * Need also the inverse mapping, from native button (1, 2, 3) to the
  1320.      * descriptive button text string index.
  1321.      */
  1322.     short nativeButtonIndexAndTypeToButtonIndex[][4] = {
  1323. {-1, 0, 1, 2}, /* abortretryignore */
  1324. {-1, 3, 0, 0}, /* ok */
  1325. {-1, 3, 4, 0}, /* okcancel */
  1326. {-1, 1, 4, 0}, /* retrycancel */
  1327. {-1, 5, 6, 0}, /* yesno */
  1328. {-1, 5, 6, 4}, /* yesnocancel */
  1329.     };
  1330.     alertType = kAlertPlainAlert;
  1331.     typeIndex = TYPE_OK;
  1332.     ChkErr(GetStandardAlertDefaultParams, &paramCFStringRec,
  1333.     kStdCFStringAlertVersionOne);
  1334.     paramCFStringRec.movable = true;
  1335.     paramCFStringRec.helpButton = false;
  1336.     paramCFStringRec.defaultButton = kAlertStdAlertOKButton;
  1337.     paramCFStringRec.cancelButton = kAlertStdAlertCancelButton;
  1338.     for (i = 1; i < objc; i += 2) {
  1339. int iconIndex;
  1340. char *string;
  1341. if (Tcl_GetIndexFromObj(interp, objv[i], movableAlertStrings, "option",
  1342. TCL_EXACT, &index) != TCL_OK) {
  1343.     goto end;
  1344. }
  1345. if (i + 1 == objc) {
  1346.     string = Tcl_GetString(objv[i]);
  1347.     Tcl_AppendResult(interp, "value for "", string, "" missing",
  1348.     NULL);
  1349.     goto end;
  1350. }
  1351. switch (index) {
  1352.     case ALERT_DEFAULT:
  1353. /*
  1354.  * Need to postpone processing of this option until we are
  1355.  * sure to know the '-type' as well.
  1356.  */
  1357. haveDefaultOption = true;
  1358. indexDefaultOption = i;
  1359. break;
  1360. #if 0
  1361.     case ALERT_DETAIL:
  1362. str = Tcl_GetString(objv[i + 1]);
  1363. finemessageTextCF = CFStringCreateWithCString(NULL, str,
  1364. kCFStringEncodingUTF8);
  1365. break;
  1366. #endif
  1367.     case ALERT_ICON:
  1368. if (Tcl_GetIndexFromObj(interp, objv[i + 1],
  1369. movableIconStrings, "value", TCL_EXACT, &iconIndex)
  1370. != TCL_OK) {
  1371.     goto end;
  1372. }
  1373. switch (iconIndex) {
  1374.     case ICON_ERROR:
  1375. alertType = kAlertStopAlert;
  1376. break;
  1377.     case ICON_INFO:
  1378. alertType = kAlertNoteAlert;
  1379. break;
  1380.     case ICON_QUESTION:
  1381. alertType = kAlertCautionAlert;
  1382. break;
  1383.     case ICON_WARNING:
  1384. alertType = kAlertCautionAlert;
  1385. break;
  1386. }
  1387. break;
  1388.     case ALERT_MESSAGE:
  1389. str = Tcl_GetString(objv[i + 1]);
  1390. messageTextCF = CFStringCreateWithCString(NULL, str,
  1391. kCFStringEncodingUTF8);
  1392. break;
  1393.     case ALERT_PARENT:
  1394. str = Tcl_GetString(objv[i + 1]);
  1395. tkwin = Tk_NameToWindow(interp, str, tkwin);
  1396. if (tkwin == NULL) {
  1397.     goto end;
  1398. }
  1399. if (((TkWindow*)tkwin)->window != None &&
  1400. TkMacOSXHostToplevelExists(tkwin)) {
  1401.     haveParentOption = true;
  1402. }
  1403. break;
  1404.     case ALERT_TITLE:
  1405. break;
  1406.     case ALERT_TYPE:
  1407. if (Tcl_GetIndexFromObj(interp, objv[i + 1],
  1408. movableTypeStrings, "value", TCL_EXACT, &typeIndex)
  1409. != TCL_OK) {
  1410.     goto end;
  1411. }
  1412. switch (typeIndex) {
  1413.     case TYPE_ABORTRETRYIGNORE:
  1414. paramCFStringRec.defaultText = CFSTR("Abort");
  1415. paramCFStringRec.cancelText = CFSTR("Retry");
  1416. paramCFStringRec.otherText = CFSTR("Ignore");
  1417. paramCFStringRec.cancelButton =
  1418. kAlertStdAlertOtherButton;
  1419. break;
  1420.     case TYPE_OK:
  1421. paramCFStringRec.defaultText = CFSTR("OK");
  1422. break;
  1423.     case TYPE_OKCANCEL:
  1424. paramCFStringRec.defaultText = CFSTR("OK");
  1425. paramCFStringRec.cancelText = CFSTR("Cancel");
  1426. break;
  1427.     case TYPE_RETRYCANCEL:
  1428. paramCFStringRec.defaultText = CFSTR("Retry");
  1429. paramCFStringRec.cancelText = CFSTR("Cancel");
  1430. break;
  1431.     case TYPE_YESNO:
  1432. paramCFStringRec.defaultText = CFSTR("Yes");
  1433. paramCFStringRec.cancelText = CFSTR("No");
  1434. break;
  1435.     case TYPE_YESNOCANCEL:
  1436. paramCFStringRec.defaultText = CFSTR("Yes");
  1437. paramCFStringRec.cancelText = CFSTR("No");
  1438. paramCFStringRec.otherText = CFSTR("Cancel");
  1439. paramCFStringRec.cancelButton =
  1440. kAlertStdAlertOtherButton;
  1441. break;
  1442. }
  1443. break;
  1444. }
  1445.     }
  1446.     if (haveDefaultOption) {
  1447. /*
  1448.  * Any '-default' option needs to know the '-type' option, which is why
  1449.  * we do this here.
  1450.  */
  1451. str = Tcl_GetString(objv[indexDefaultOption + 1]);
  1452. if (Tcl_GetIndexFromObj(interp, objv[indexDefaultOption + 1],
  1453. movableButtonStrings, "value", TCL_EXACT, &defaultButtonIndex)
  1454. != TCL_OK) {
  1455.     goto end;
  1456. }
  1457. /*
  1458.  * Need to map from "ok" etc. to 1, 2, 3, right to left.
  1459.  */
  1460. defaultNativeButtonIndex =
  1461. buttonIndexAndTypeToNativeButtonIndex[typeIndex][defaultButtonIndex];
  1462. if (defaultNativeButtonIndex == 0) {
  1463.     Tcl_SetObjResult(interp,
  1464.     Tcl_NewStringObj("Illegal default option", -1));
  1465.     goto end;
  1466. }
  1467. paramCFStringRec.defaultButton = defaultNativeButtonIndex;
  1468. if (paramCFStringRec.cancelButton == defaultNativeButtonIndex) {
  1469.     paramCFStringRec.cancelButton = 0;
  1470. }
  1471.     }
  1472.     ChkErr(SetThemeCursor, kThemeArrowCursor);
  1473.     if (haveParentOption) {
  1474. AlertHandlerUserData data;
  1475. static EventHandlerUPP handler = NULL;
  1476. WindowRef windowRef;
  1477. const EventTypeSpec kEvents[] = {
  1478.     {kEventClassCommand, kEventProcessCommand}
  1479. };
  1480. bzero(&data, sizeof(data));
  1481. if (!handler) {
  1482.     handler = NewEventHandlerUPP(AlertHandler);
  1483. }
  1484. windowRef = TkMacOSXDrawableWindow(Tk_WindowId(tkwin));
  1485. if (!windowRef) {
  1486.     goto end;
  1487. }
  1488. err = ChkErr(CreateStandardSheet, alertType, messageTextCF,
  1489. finemessageTextCF, &paramCFStringRec, NULL, &dialogRef);
  1490. if(err != noErr) {
  1491.     goto end;
  1492. }
  1493. data.dialogWindow = GetDialogWindow(dialogRef);
  1494. err = ChkErr(ShowSheetWindow, data.dialogWindow, windowRef);
  1495. if(err != noErr) {
  1496.     DisposeDialog(dialogRef);
  1497.     goto end;
  1498. }
  1499. ChkErr(GetWindowModality, data.dialogWindow, &data.origModality,
  1500. &data.origUnavailWindow);
  1501. ChkErr(SetWindowModality, data.dialogWindow, kWindowModalityAppModal,
  1502. NULL);
  1503. ChkErr(InstallEventHandler, GetWindowEventTarget(data.dialogWindow),
  1504. handler, GetEventTypeCount(kEvents), kEvents, &data,
  1505. &data.handlerRef);
  1506. TkMacOSXTrackingLoop(1);
  1507. ChkErr(RunAppModalLoopForWindow, data.dialogWindow);
  1508. TkMacOSXTrackingLoop(0);
  1509. itemHit = data.buttonIndex;
  1510.     } else {
  1511. err = ChkErr(CreateStandardAlert, alertType, messageTextCF,
  1512. finemessageTextCF, &paramCFStringRec, &dialogRef);
  1513. if(err != noErr) {
  1514.     goto end;
  1515. }
  1516. TkMacOSXTrackingLoop(1);
  1517. err = ChkErr(RunStandardAlert, dialogRef, NULL, &itemHit);
  1518. TkMacOSXTrackingLoop(0);
  1519. if (err != noErr) {
  1520.     goto end;
  1521. }
  1522.     }
  1523.     if (err == noErr) {
  1524. int ind;
  1525. /*
  1526.  * Map 'itemHit' (1, 2, 3) to descriptive text string.
  1527.  */
  1528. ind = nativeButtonIndexAndTypeToButtonIndex[typeIndex][itemHit];
  1529. Tcl_SetObjResult(interp, Tcl_NewStringObj(movableButtonStrings[ind],
  1530. -1));
  1531. result = TCL_OK;
  1532.     }
  1533. end:
  1534.     if (finemessageTextCF) {
  1535. CFRelease(finemessageTextCF);
  1536.     }
  1537.     if (messageTextCF) {
  1538. CFRelease(messageTextCF);
  1539.     }
  1540.     return result;
  1541. }
  1542. /*
  1543.  *----------------------------------------------------------------------
  1544.  *
  1545.  * AlertHandler --
  1546.  *
  1547.  * Carbon event handler for the Standard Sheet dialog.
  1548.  *
  1549.  * Results:
  1550.  * OSStatus if event handled or not.
  1551.  *
  1552.  * Side effects:
  1553.  * May set userData.
  1554.  *
  1555.  *----------------------------------------------------------------------
  1556.  */
  1557. OSStatus
  1558. AlertHandler(
  1559.     EventHandlerCallRef callRef,
  1560.     EventRef eventRef,
  1561.     void *userData)
  1562. {
  1563.     AlertHandlerUserData *data = (AlertHandlerUserData *) userData;
  1564.     HICommand cmd;
  1565.     ChkErr(GetEventParameter,eventRef, kEventParamDirectObject, typeHICommand,
  1566.     NULL, sizeof(cmd), NULL, &cmd);
  1567.     switch (cmd.commandID) {
  1568. case kHICommandOK:
  1569.     data->buttonIndex = 1;
  1570.     break;
  1571. case kHICommandCancel:
  1572.     data->buttonIndex = 2;
  1573.     break;
  1574. case kHICommandOther:
  1575.     data->buttonIndex = 3;
  1576.     break;
  1577.     }
  1578.     if (data->buttonIndex) {
  1579. ChkErr(QuitAppModalLoopForWindow, data->dialogWindow);
  1580. ChkErr(RemoveEventHandler, data->handlerRef);
  1581. ChkErr(SetWindowModality, data->dialogWindow,
  1582. data->origModality, data->origUnavailWindow);
  1583.     }
  1584.     return eventNotHandledErr;
  1585. }