/*
 * tkMacDialog.c --
 *
 *	Contains the Mac implementation of the common dialog boxes.
 *
 * Copyright (c) 1996 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkMacDialog.c,v 1.7 1999/01/26 04:11:31 jingham Exp $
 *
 */

#include <Gestalt.h>
#include <Aliases.h>
#include <Errors.h>
#include <Strings.h>
#include <MoreFiles.h>
#include <MoreFilesExtras.h>
#include <StandardFile.h>
#include <ColorPicker.h>
#include <Lowmem.h>
#include "tkPort.h"
#include "tkInt.h"
#include "tclMacInt.h"
#include "tkFileFilter.h"

/*
 * The following are ID's for resources that are defined in tkMacResource.r
 */
#define OPEN_BOX        130
#define OPEN_POPUP      131
#define OPEN_MENU       132
#define OPEN_POPUP_ITEM 10

#define SAVE_FILE	0
#define OPEN_FILE	1

#define MATCHED		0
#define UNMATCHED	1

/*
 * The following structure is used in the GetFileName() function. It stored
 * information about the file dialog and the file filters.
 */
typedef struct _OpenFileData {
    Tcl_Interp * interp;
    char * initialFile;			/* default file to appear in the
					 * save dialog */
    char * defExt;			/* default extension (not used on the
					 * Mac) */
    FileFilterList fl;			/* List of file filters. */
    SInt16 curType;			/* The filetype currently being
					 * listed */
    int isOpen;				/* True if this is an Open dialog,
					 * false if it is a Save dialog. */
    MenuHandle menu;			/* Handle of the menu in the popup*/
    short dialogId;			/* resource ID of the dialog */
    int popupId;			/* resource ID of the popup */
    short popupItem;			/* item number of the popup in the
					 * dialog */
    int usePopup;			/* True if we show the popup menu (this
    					 * is an open operation and the
					 * -filetypes option is set)
    					 */
} OpenFileData;

static pascal Boolean	FileFilterProc _ANSI_ARGS_((CInfoPBPtr pb,
			    void *myData));
static int 		GetFileName _ANSI_ARGS_ ((
			    ClientData clientData, Tcl_Interp *interp,
    			    int argc, char **argv, int isOpen ));
static Boolean		MatchOneType _ANSI_ARGS_((CInfoPBPtr pb,
			    OpenFileData * myDataPtr, FileFilter * filterPtr));
static pascal short 	OpenHookProc _ANSI_ARGS_((short item,
			    DialogPtr theDialog, OpenFileData * myDataPtr));
static int 		ParseFileDlgArgs _ANSI_ARGS_ ((Tcl_Interp * interp,
			    OpenFileData * myDataPtr, int argc, char ** argv,
			    int isOpen));

/*
 * Filter and hook functions used by the tk_getOpenFile and tk_getSaveFile
 * commands.
 */

static FileFilterYDUPP openFilter = NULL;
static DlgHookYDUPP openHook = NULL;
static DlgHookYDUPP saveHook = NULL;
  

/*
 *----------------------------------------------------------------------
 *
 * EvalArgv --
 *
 *	Invokes the Tcl procedure with the arguments. argv[0] is set by
 *	the caller of this function. It may be different than cmdName.
 *	The TCL command will see argv[0], not cmdName, as its name if it
 *	invokes [lindex [info level 0] 0]
 *
 * Results:
 *	TCL_ERROR if the command does not exist and cannot be autoloaded.
 *	Otherwise, return the result of the evaluation of the command.
 *
 * Side effects:
 *	The command may be autoloaded.
 *
 *----------------------------------------------------------------------
 */

static int
EvalArgv(
    Tcl_Interp *interp,		/* Current interpreter. */
    char * cmdName,		/* Name of the TCL command to call */
    int argc,			/* Number of arguments. */
    char **argv)		/* Argument strings. */
{
    Tcl_CmdInfo cmdInfo;

    if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
	char * cmdArgv[2];

	/*
	 * This comand is not in the interpreter yet -- looks like we
	 * have to auto-load it
	 */
	if (!Tcl_GetCommandInfo(interp, "auto_load", &cmdInfo)) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "cannot execute command \"auto_load\"",
		NULL);
	    return TCL_ERROR;
	}

	cmdArgv[0] = "auto_load";
	cmdArgv[1] = cmdName;

	if ((*cmdInfo.proc)(cmdInfo.clientData, interp, 2, cmdArgv)!= TCL_OK){ 
	    return TCL_ERROR;
	}

	if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
	    Tcl_ResetResult(interp);
	    Tcl_AppendResult(interp, "cannot auto-load command \"",
		cmdName, "\"",NULL);
	    return TCL_ERROR;
	}
    }

    return (*cmdInfo.proc)(cmdInfo.clientData, interp, argc, argv);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_ChooseColorCmd --
 *
 *	This procedure implements the color dialog box for the Mac
 *	platform. See the user documentation for details on what it
 *	does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *      See the user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tk_ChooseColorCmd(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    char **argv)		/* Argument strings. */
{
    Tk_Window parent = Tk_MainWindow(interp);
    char * colorStr = NULL;
    XColor * colorPtr = NULL;
    char * title = "Choose a color:";
    int i, version;
    long response = 0;
    OSErr err = noErr;
    char buff[40];
    static RGBColor in;
    static inited = 0;

    /*
     * Use the gestalt manager to determine how to bring
     * up the color picker.  If versin 2.0 isn't available
     * we can assume version 1.0 is available as it comes with
     * Color Quickdraw which Tk requires to run at all.
     */
     
    err = Gestalt(gestaltColorPicker, &response); 
    if ((err == noErr) || (response == 0x0200L)) {
    	version = 2;
    } else {
    	version = 1;
    }
 
    for (i=1; i<argc; i+=2) {
        int v = i+1;
	int len = strlen(argv[i]);

        if (strncmp(argv[i], "-initialcolor", len)==0) {
	    if (v==argc) {goto arg_missing;}

	    colorStr = argv[v];
	} else if (strncmp(argv[i], "-parent", len)==0) {
	    if (v==argc) {goto arg_missing;}

	    parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
	    if (parent == NULL) {
		return TCL_ERROR;
	    }
	} else if (strncmp(argv[i], "-title", len)==0) {
	    if (v==argc) {goto arg_missing;}

	    title = argv[v];
	} else {
    	    Tcl_AppendResult(interp, "unknown option \"", 
		    argv[i], "\", must be -initialcolor, -parent or -title",
		    NULL);
	    return TCL_ERROR;
	}
    }

    if (colorStr) {
        colorPtr = Tk_GetColor(interp, parent, colorStr);
        if (colorPtr == NULL) {
            return TCL_ERROR;
        }
    }

    if (!inited) {
        inited = 1;
        in.red = 0xffff;
        in.green = 0xffff;
        in.blue = 0xffff;
    }
    if (colorPtr) {
        in.red   = colorPtr->red;
        in.green = colorPtr->green;
        in.blue  = colorPtr->blue;
    }
        
    if (version == 1) {
        /*
         * Use version 1.0 of the color picker
         */
    	
    	RGBColor out;
    	Str255 prompt;
    	Point point = {-1, -1};
    	
        prompt[0] = strlen(title);
        strncpy((char*) prompt+1, title, 255);
        
        if (GetColor(point, prompt, &in, &out)) {
            /*
             * user selected a color
             */
            sprintf(buff, "#%02x%02x%02x", out.red >> 8, out.green >> 8,
                out.blue >> 8);
            Tcl_SetResult(interp, buff, TCL_VOLATILE);

            /*
             * Save it for the next time
             */
            in.red   = out.red;
            in.green = out.green;
            in.blue  = out.blue;
        } else {
            Tcl_ResetResult(interp);
    	}
    } else {
        /*
         * Version 2.0 of the color picker is available. Let's use it
         */
	ColorPickerInfo cpinfo;

    	cpinfo.theColor.profile = 0L;
    	cpinfo.theColor.color.rgb.red   = in.red;
    	cpinfo.theColor.color.rgb.green = in.green;
    	cpinfo.theColor.color.rgb.blue  = in.blue;
    	cpinfo.dstProfile = 0L;
    	cpinfo.flags = CanModifyPalette | CanAnimatePalette;
    	cpinfo.placeWhere = kDeepestColorScreen;
    	cpinfo.pickerType = 0L;
    	cpinfo.eventProc = NULL;
    	cpinfo.colorProc = NULL;
    	cpinfo.colorProcData = NULL;

        cpinfo.prompt[0] = strlen(title);
        strncpy((char*)cpinfo.prompt+1, title, 255);
        
        if ((PickColor(&cpinfo) == noErr) && cpinfo.newColorChosen) {
            sprintf(buff, "#%02x%02x%02x",
		cpinfo.theColor.color.rgb.red   >> 8, 
                cpinfo.theColor.color.rgb.green >> 8,
		cpinfo.theColor.color.rgb.blue  >> 8);
            Tcl_SetResult(interp, buff, TCL_VOLATILE);
            
            in.blue  = cpinfo.theColor.color.rgb.red;
    	    in.green = cpinfo.theColor.color.rgb.green;
    	    in.blue  = cpinfo.theColor.color.rgb.blue;
          } else {
            Tcl_ResetResult(interp);
        }
    }

    if (colorPtr) {
	Tk_FreeColor(colorPtr);
    }

    return TCL_OK;

  arg_missing:
    Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
	NULL);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetOpenFileCmd --
 *
 *	This procedure implements the "open file" dialog box for the
 *	Mac platform. See the user documentation for details on what
 *	it does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *	See user documentation.
 *----------------------------------------------------------------------
 */

int
Tk_GetOpenFileCmd(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    char **argv)		/* Argument strings. */
{
    return GetFileName(clientData, interp, argc, argv, OPEN_FILE);
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetSaveFileCmd --
 *
 *	Same as Tk_GetOpenFileCmd but opens a "save file" dialog box
 *	instead
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *	See user documentation.
 *----------------------------------------------------------------------
 */

int
Tk_GetSaveFileCmd(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    char **argv)		/* Argument strings. */
{
    return GetFileName(clientData, interp, argc, argv, SAVE_FILE);
}

/*
 *----------------------------------------------------------------------
 *
 * GetFileName --
 *
 *	Calls the Mac file dialog functions for the user to choose a
 *	file to or save.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	If the user selects a file, the native pathname of the file
 *	is returned in interp->result. Otherwise an empty string
 *	is returned in interp->result.
 *
 *----------------------------------------------------------------------
 */

static int
GetFileName(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    char **argv,		/* Argument strings. */
    int isOpen)			/* true if we should call GetOpenFileName(),
				 * false if we should call GetSaveFileName() */
{
    int code = TCL_OK;
    int i;
    OpenFileData myData, *myDataPtr;
    StandardFileReply reply;
    Point mypoint;
    Str255 str;

    myDataPtr = &myData;

    if (openFilter == NULL) {
	openFilter = NewFileFilterYDProc(FileFilterProc);
	openHook = NewDlgHookYDProc(OpenHookProc);
	saveHook = NewDlgHookYDProc(OpenHookProc);
    }

    /*
     * 1. Parse the arguments.
     */
    if (ParseFileDlgArgs(interp, myDataPtr, argc, argv, isOpen) 
	!= TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * 2. Set the items in the file types popup.
     */

    /*
     * Delete all the entries inside the popup menu, in case there's any
     * left overs from previous invocation of this command
     */

    if (myDataPtr->usePopup) {
	FileFilter * filterPtr;

        for (i=CountMItems(myDataPtr->menu); i>0; i--) {
            /*
             * The item indices are one based. Also, if we delete from
             * the beginning, the items may be re-numbered. So we
             * delete from the end
    	     */
    	     DeleteMenuItem(myDataPtr->menu, i);
        }

	if (myDataPtr->fl.filters) {
	    for (filterPtr=myDataPtr->fl.filters; filterPtr;
		    filterPtr=filterPtr->next) {
		strncpy((char*)str+1, filterPtr->name, 254);
		str[0] = strlen(filterPtr->name);
		AppendMenu(myDataPtr->menu, (ConstStr255Param) str);
	    }
	} else {
	    myDataPtr->usePopup = 0;
	}
    }

    /*
     * 3. Call the toolbox file dialog function.
     */
    SetPt(&mypoint, -1, -1);
    TkpSetCursor(NULL);
    
    if (myDataPtr->isOpen) {
        if (myDataPtr->usePopup) {
	    CustomGetFile(openFilter, (short) -1, NULL, &reply, 
	        myDataPtr->dialogId, 
	        mypoint, openHook, NULL, NULL, NULL, (void*)myDataPtr);
	} else {
	    StandardGetFile(NULL, -1, NULL, &reply);
	}
    } else {
	Str255 prompt, def;

	strcpy((char*)prompt+1, "Save as");
	prompt[0] = strlen("Save as");
   	if (myDataPtr->initialFile) {
   	    strncpy((char*)def+1, myDataPtr->initialFile, 254);
	    def[0] = strlen(myDataPtr->initialFile);
        } else {
            def[0] = 0;
        }
   	if (myDataPtr->usePopup) {
   	    /*
   	     * Currently this never gets called because we don't use
   	     * popup for the save dialog.
   	     */
	    CustomPutFile(prompt, def, &reply, myDataPtr->dialogId, mypoint, 
	        saveHook, NULL, NULL, NULL, myDataPtr);
	} else {
	    StandardPutFile(prompt, def, &reply);
	}
    }

    Tcl_ResetResult(interp);    
    if (reply.sfGood) {
        int length;
    	Handle pathHandle = NULL;
    	char * pathName = NULL;
    	
    	FSpPathFromLocation(&reply.sfFile, &length, &pathHandle);

	if (pathHandle != NULL) {
	    HLock(pathHandle);
	    pathName = (char *) ckalloc((unsigned) (length + 1));
	    strcpy(pathName, *pathHandle);
	    HUnlock(pathHandle);
	    DisposeHandle(pathHandle);

	    /*
	     * Return the full pathname of the selected file
	     */

	    Tcl_SetResult(interp, pathName, TCL_DYNAMIC);
	}
    }

  done:
    TkFreeFileFilters(&myDataPtr->fl);
    return code;
}

/*
 *----------------------------------------------------------------------
 *
 * ParseFileDlgArgs --
 *
 *	Parses the arguments passed to tk_getOpenFile and tk_getSaveFile.
 *
 * Results:
 *	A standard TCL return value.
 *
 * Side effects:
 *	The OpenFileData structure is initialized and modified according
 *	to the arguments.
 *
 *----------------------------------------------------------------------
 */

static int
ParseFileDlgArgs(
    Tcl_Interp * interp,		/* Current interpreter. */
    OpenFileData * myDataPtr,		/* Information about the file dialog */
    int argc,				/* Number of arguments */
    char ** argv,			/* Argument strings */
    int isOpen)				/* TRUE if this is an "open" dialog */
{
    int i;

    myDataPtr->interp      	= interp;
    myDataPtr->initialFile 	= NULL;
    myDataPtr->curType		= 0;

    TkInitFileFilters(&myDataPtr->fl);
    
    if (isOpen) {
	myDataPtr->isOpen    = 1;
        myDataPtr->usePopup  = 1;
	myDataPtr->menu      = GetMenu(OPEN_MENU);
	myDataPtr->dialogId  = OPEN_BOX;
	myDataPtr->popupId   = OPEN_POPUP;
	myDataPtr->popupItem = OPEN_POPUP_ITEM;
	if (myDataPtr->menu == NULL) {
	    Debugger();
	}
    } else {
        myDataPtr->isOpen    = 0;
	myDataPtr->usePopup  = 0;
    }

    for (i=1; i<argc; i+=2) {
        int v = i+1;
	int len = strlen(argv[i]);

	if (strncmp(argv[i], "-defaultextension", len)==0) {
	    if (v==argc) {goto arg_missing;}

	    myDataPtr->defExt = argv[v];
	}
	else if (strncmp(argv[i], "-filetypes", len)==0) {
	    if (v==argc) {goto arg_missing;}

	    if (TkGetFileFilters(interp, &myDataPtr->fl,argv[v],0) != TCL_OK) {
		return TCL_ERROR;
	    }
	}
	else if (strncmp(argv[i], "-initialdir", len)==0) {
	    FSSpec dirSpec;
	    char * dirName;
	    Tcl_DString dstring;
	    long dirID;
	    OSErr err;
	    Boolean isDirectory;

	    if (v==argc) {goto arg_missing;}
	    
	    if (Tcl_TranslateFileName(interp, argv[v], &dstring) == NULL) {
	        return TCL_ERROR;
	    }
	    dirName = dstring.string;
	    if (FSpLocationFromPath(strlen(dirName), dirName, &dirSpec) != 
		    noErr) {
		Tcl_AppendResult(interp, "bad directory \"", argv[v],
	            "\"", NULL);
	        return TCL_ERROR;
	    }
	    err = FSpGetDirectoryID(&dirSpec, &dirID, &isDirectory);
	    if ((err != noErr) || !isDirectory) {
		Tcl_AppendResult(interp, "bad directory \"", argv[v],
	            "\"", NULL);
	        return TCL_ERROR;
	    }
	    /*
	     * Make sure you negate -dirSpec.vRefNum because the standard file
	     * package wants it that way !
	     */
	    LMSetSFSaveDisk(-dirSpec.vRefNum);
	    LMSetCurDirStore(dirID);
	    Tcl_DStringFree(&dstring);
    	}
	else if (strncmp(argv[i], "-initialfile", len)==0) {
	    if (v==argc) {goto arg_missing;}
	    
	    myDataPtr->initialFile = argv[v];
	}
	else if (strncmp(argv[i], "-parent", len)==0) {
	    /*
	     * Ignored on the Mac, but make sure that it's a valid window
	     * pathname
	     */
	    Tk_Window parent;

	    if (v==argc) {goto arg_missing;}
	    	    
	    parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
	    if (parent == NULL) {
		return TCL_ERROR;
	    }	    
	}
	else if (strncmp(argv[i], "-title", len)==0) {
	    if (v==argc) {goto arg_missing;}
	    
	    /*
	     * This option is ignored on the Mac because the Mac file
	     * dialog do not support titles.
	     */
	}
	else {
    	    Tcl_AppendResult(interp, "unknown option \"", 
		argv[i], "\", must be -defaultextension, ",
		"-filetypes, -initialdir, -initialfile, -parent or -title",
		NULL);
	    return TCL_ERROR;
	}
    }

    return TCL_OK;

  arg_missing:
    Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
	NULL);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * OpenHookProc --
 *
 *	Gets called for various events that occur in the file dialog box.
 *	Initializes the popup menu or rebuild the file list depending on
 *	the type of the event.
 *
 * Results:
 *	A standard result understood by the Mac file dialog event dispatcher.
 *
 * Side effects:
 *	The contents in the file dialog may be changed depending on
 *	the type of the event.
 *----------------------------------------------------------------------
 */

static pascal short
OpenHookProc(
    short item,			/* Event description. */
    DialogPtr theDialog,	/* The dialog where the event occurs. */
    OpenFileData * myDataPtr)	/* Information about the file dialog. */
{
    short ignore;
    Rect rect;
    Handle handle;
    int newType;

    switch (item) {
	case sfHookFirstCall:
	    if (myDataPtr->usePopup) {
		/*
		 * Set the popup list to display the selected type.
		 */
		GetDialogItem(theDialog, myDataPtr->popupItem,
			&ignore, &handle, &rect);
		SetControlValue((ControlRef) handle, myDataPtr->curType + 1);
	    }
	    return sfHookNullEvent;
      
	case OPEN_POPUP_ITEM:
	    if (myDataPtr->usePopup) {
		GetDialogItem(theDialog, myDataPtr->popupItem,
			&ignore, &handle, &rect);
		newType = GetCtlValue((ControlRef) handle) - 1;
		if (myDataPtr->curType != newType) {
		    if (newType<0 || newType>myDataPtr->fl.numFilters) {
			/*
			 * Sanity check. Looks like the user selected an
			 * non-existent menu item?? Don't do anything.
			 */
		    } else {
			myDataPtr->curType = newType;
		    }
		    return sfHookRebuildList;
		}
	    }  
	    break;
    }

    return item;
}

/*
 *----------------------------------------------------------------------
 *
 * FileFilterProc --
 *
 *	Filters files according to file types. Get called whenever the
 *	file list needs to be updated inside the dialog box.
 *
 * Results:
 *	Returns MATCHED if the file should be shown in the listbox, returns
 *	UNMATCHED otherwise.
 *
 * Side effects:
 *	If MATCHED is returned, the file is shown in the listbox.
 *
 *----------------------------------------------------------------------
 */

static pascal Boolean
FileFilterProc(
    CInfoPBPtr pb,		/* Information about the file */
    void *myData)		/* Client data for this file dialog */
{
    int i;
    OpenFileData * myDataPtr = (OpenFileData*)myData;
    FileFilter * filterPtr;

    if (myDataPtr->fl.numFilters == 0) {
	/*
	 * No types have been specified. List all files by default
	 */
	return MATCHED;
    }

    if (pb->dirInfo.ioFlAttrib & 0x10) {
    	/*
    	 * This is a directory: always show it
    	 */
    	return MATCHED;
    }

    if (myDataPtr->usePopup) {
        i = myDataPtr->curType;
	for (filterPtr=myDataPtr->fl.filters; filterPtr && i>0; i--) {
	    filterPtr = filterPtr->next;
	}
	if (filterPtr) {
	    return MatchOneType(pb, myDataPtr, filterPtr);
	} else {
	    return UNMATCHED;
        }
    } else {
	/*
	 * We are not using the popup menu. In this case, the file is
	 * considered matched if it matches any of the file filters.
	 */

	for (filterPtr=myDataPtr->fl.filters; filterPtr;
		filterPtr=filterPtr->next) {
	    if (MatchOneType(pb, myDataPtr, filterPtr) == MATCHED) {
	        return MATCHED;
	    }
	}
	return UNMATCHED;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * MatchOneType --
 *
 *	Match a file with one file type in the list of file types.
 *
 * Results:
 *	Returns MATCHED if the file matches with the file type; returns
 *	UNMATCHED otherwise.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

static Boolean
MatchOneType(
    CInfoPBPtr pb,		/* Information about the file */
    OpenFileData * myDataPtr,	/* Information about this file dialog */
    FileFilter * filterPtr)	/* Match the file described by pb against
				 * this filter */
{
    FileFilterClause * clausePtr;

    /*
     * A file matches with a file type if it matches with at least one
     * clause of the type.
     *
     * If the clause has both glob patterns and ostypes, the file must
     * match with at least one pattern AND at least one ostype.
     *
     * If the clause has glob patterns only, the file must match with at least
     * one pattern.
     *
     * If the clause has mac types only, the file must match with at least
     * one mac type.
     *
     * If the clause has neither glob patterns nor mac types, it's
     * considered an error.
     */

    for (clausePtr=filterPtr->clauses; clausePtr; clausePtr=clausePtr->next) {
	int macMatched  = 0;
	int globMatched = 0;
	GlobPattern * globPtr;
	MacFileType * mfPtr;

	if (clausePtr->patterns == NULL) {
	    globMatched = 1;
	}
	if (clausePtr->macTypes == NULL) {
	    macMatched = 1;
	}

	for (globPtr=clausePtr->patterns; globPtr; globPtr=globPtr->next) {
	    char filename[256];
	    int len;
	    char * p, *q, *ext;
        
	    if (pb->hFileInfo.ioNamePtr == NULL) {
		continue;
	    }
	    p = (char*)(pb->hFileInfo.ioNamePtr);
	    len = p[0];
	    strncpy(filename, p+1, len);
	    filename[len] = '\0';
	    ext = globPtr->pattern;

	    if (ext[0] == '\0') {
		/*
		 * We don't want any extensions: OK if the filename doesn't
		 * have "." in it
		 */
		for (q=filename; *q; q++) {
		    if (*q == '.') {
			goto glob_unmatched;
		    }
		}
		goto glob_matched;
	    }
        
	    if (Tcl_StringMatch(filename, ext)) {
		goto glob_matched;
	    } else {
		goto glob_unmatched;
	    }

	  glob_unmatched:
	    continue;

	  glob_matched:
	    globMatched = 1;
	    break;
	}

	for (mfPtr=clausePtr->macTypes; mfPtr; mfPtr=mfPtr->next) {
	    if (pb->hFileInfo.ioFlFndrInfo.fdType == mfPtr->type) {
		macMatched = 1;
		break;
	    }
        }

	if (globMatched && macMatched) {
	    return MATCHED;
	}
    }

    return UNMATCHED;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_MessageBoxCmd --
 *
 *	This procedure implements the MessageBox window for the
 *	Mac platform. See the user documentation for details on what
 *	it does.
 *
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *	See user documentation.
 *
 *----------------------------------------------------------------------
 */

int
Tk_MessageBoxCmd(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int argc,			/* Number of arguments. */
    char **argv)		/* Argument strings. */
{
    return EvalArgv(interp, "tkMessageBox", argc, argv);
}
