/*
 * tkMacOSXMenus.c --
 *
 *	These calls set up and manage the menubar for the Macintosh version of
 *	Tk.
 *
 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
 * Copyright (c) 2001, Apple Computer, Inc.
 * Copyright (c) 2005-2007 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.
 */

#include "tkMacOSXPrivate.h"

#define kAppleMenu		256
#define kAppleAboutItem		1
#define kFileMenu		2
#define kEditMenu		3

#define kSourceItem		1
#define kDemoItem		2
#define kCloseItem		3

#define EDIT_CUT		1
#define EDIT_COPY		2
#define EDIT_PASTE		3
#define EDIT_CLEAR		4

MenuRef tkAppleMenu;
MenuRef tkFileMenu;
MenuRef tkEditMenu;

static Tcl_Interp *gInterp = NULL;	    /* Standard menu interpreter. */
static EventHandlerRef menuEventHandlerRef = NULL;

static void		GenerateEditEvent(int flag);
static Tcl_Obj *	GetWidgetDemoPath(Tcl_Interp *interp);
static OSStatus		MenuEventHandlerProc(EventHandlerCallRef callRef,
			    EventRef event, void *userData);

/*
 *----------------------------------------------------------------------
 *
 * GetWidgetDemoPath --
 *
 *	Get path to the widget demo.
 *
 * Results:
 *	pathObj with ref count 0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static Tcl_Obj *
GetWidgetDemoPath(
    Tcl_Interp *interp)
{
    Tcl_Obj *libpath, *result = NULL;

    libpath = Tcl_GetVar2Ex(gInterp, "tk_library", NULL, TCL_GLOBAL_ONLY);
    if (libpath) {
	Tcl_Obj *demo[2] = {	Tcl_NewStringObj("demos", 5),
				Tcl_NewStringObj("widget", 6) };
	
	Tcl_IncrRefCount(libpath);
	result = Tcl_FSJoinToPath(libpath, 2, demo);
	Tcl_DecrRefCount(libpath);
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXHandleMenuSelect --
 *
 *	Handles events that occur in the Menu bar.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkMacOSXHandleMenuSelect(
    MenuID theMenu,
    MenuItemIndex theItem,
    int optionKeyPressed)
{
    Tk_Window tkwin;
    Window window;
    TkDisplay *dispPtr;
    Tcl_CmdInfo dummy;
    int code;

    if (theItem == 0) {
	TkMacOSXClearMenubarActive();
	return;
    }

    switch (theMenu) {
    case kAppleMenu:
	switch (theItem) {
	case kAppleAboutItem:
	    if (optionKeyPressed || gInterp == NULL ||
		Tcl_GetCommandInfo(gInterp, "tkAboutDialog", &dummy) == 0) {
		TkAboutDlg();
	    } else {
		code = Tcl_EvalEx(gInterp, "tkAboutDialog", -1,
			TCL_EVAL_GLOBAL);
		if (code != TCL_OK) {
		    Tcl_BackgroundException(gInterp, code);
		}
		Tcl_ResetResult(gInterp);
	    }
	    break;
	}
	break;
    case kFileMenu:
	switch (theItem) {
	case kSourceItem:
	    if (gInterp) {
		if (Tcl_EvalEx(gInterp, "tk_getOpenFile -filetypes {"
			"{{TCL Scripts} {.tcl} TEXT} {{Text Files} {} TEXT}}",
			-1, TCL_EVAL_GLOBAL) == TCL_OK) {
		    Tcl_Obj *path = Tcl_GetObjResult(gInterp);
		    int len;

		    Tcl_GetStringFromObj(path, &len);
		    if (len) {
			Tcl_IncrRefCount(path);
			code = Tcl_FSEvalFile(gInterp, path);
			if (code != TCL_OK) {
			    Tcl_BackgroundException(gInterp, code);
			}
			Tcl_DecrRefCount(path);
		    }
		}
		Tcl_ResetResult(gInterp);
	    }
	    break;
	case kDemoItem:
	    if (gInterp) {
		Tcl_Obj *path = GetWidgetDemoPath(gInterp);

		if (path) {
		    Tcl_IncrRefCount(path);
		    code = Tcl_FSEvalFile(gInterp, path);
		    if (code != TCL_OK) {
			Tcl_BackgroundException(gInterp, code);
		    }
		    Tcl_DecrRefCount(path);
		    Tcl_ResetResult(gInterp);
		}
	    }
	    break;
	case kCloseItem:
	    /* Send close event */
	    window = TkMacOSXGetXWindow(ActiveNonFloatingWindow());
	    dispPtr = TkGetDisplayList();
	    tkwin = Tk_IdToWindow(dispPtr->display, window);
	    TkGenWMDestroyEvent(tkwin);
	    break;
	}
	break;
    case kEditMenu:
	/*
	 * This implementation just send the keysyms Tk thinks are associated
	 * with function keys that do Cut, Copy & Paste on a Sun keyboard.
	 */

	GenerateEditEvent(theItem);
	break;
    default:
	TkMacOSXDispatchMenuEvent(theMenu, theItem);
	break;
    }

    /*
     * Finally we unhighlight the menu.
     */

    HiliteMenu(0);
}

/*
 *----------------------------------------------------------------------
 *
 * MenuEventHandlerProc --
 *
 *	One-time handler of kEventMenuEnableItems for the edit menu.
 *
 * Results:
 *	OS status code.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static OSStatus
MenuEventHandlerProc(
    EventHandlerCallRef callRef,
    EventRef event,
    void *userData)
{
    OSStatus result = eventNotHandledErr, err;
    int menuContext;

    err = ChkErr(GetEventParameter, event, kEventParamMenuContext, typeUInt32,
	    NULL, sizeof(menuContext), NULL, &menuContext);
    if (err == noErr && (menuContext & kMenuContextMenuBarTracking)) {
	if (gInterp) {
	    Tcl_Obj *path = GetWidgetDemoPath(gInterp);

	    if (path) {
		Tcl_IncrRefCount(path);
		if (Tcl_FSAccess(path, R_OK) == 0) {
		    EnableMenuItem(tkFileMenu, kDemoItem);
		}
		Tcl_DecrRefCount(path);
	    }
	}
	ChkErr(RemoveEventHandler, menuEventHandlerRef);
	menuEventHandlerRef = NULL;
	result = noErr;
    }

    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * TkMacOSXInitMenus --
 *
 *	This procedure initializes the Macintosh menu bar.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkMacOSXInitMenus(
    Tcl_Interp *interp)
{
    OSStatus err;
    EventHandlerUPP menuEventHandlerUPP;
    const EventTypeSpec menuEventTypes[] = {
	{kEventClassMenu, kEventMenuEnableItems},
    };

    gInterp = interp;
    if (TkMacOSXUseMenuID(kAppleMenu) != TCL_OK) {
	Tcl_Panic("Menu ID %d is already in use!", kAppleMenu);
    }
    err = ChkErr(CreateNewMenu, kAppleMenu, kMenuAttrDoNotUseUserCommandKeys,
	    &tkAppleMenu);
    if (err != noErr) {
	Tcl_Panic("CreateNewMenu failed !");
    }
    SetMenuTitle(tkAppleMenu, "\p\024");
    InsertMenu(tkAppleMenu, 0);
    AppendMenu(tkAppleMenu, "\pAbout Tcl & Tk\xc9");
    AppendMenu(tkAppleMenu, "\p(-");

    if (TkMacOSXUseMenuID(kFileMenu) != TCL_OK) {
	Tcl_Panic("Menu ID %d is already in use!", kFileMenu);
    }
    err = ChkErr(CreateNewMenu, kFileMenu, kMenuAttrDoNotUseUserCommandKeys,
	    &tkFileMenu);
    if (err != noErr) {
	Tcl_Panic("CreateNewMenu failed !");
    }
    SetMenuTitle(tkFileMenu, "\pFile");
    InsertMenu(tkFileMenu, 0);
    InsertMenuItem(tkFileMenu, "\pSource\xc9", kSourceItem - 1);
    InsertMenuItem(tkFileMenu, "\pRun Widget Demo", kDemoItem - 1);
    InsertMenuItem(tkFileMenu, "\pClose/W", kCloseItem - 1);
    DisableMenuItem(tkFileMenu, kDemoItem);
    menuEventHandlerUPP = NewEventHandlerUPP(MenuEventHandlerProc);
    ChkErr(InstallEventHandler, GetMenuEventTarget(tkFileMenu),
	    menuEventHandlerUPP, GetEventTypeCount(menuEventTypes),
	    menuEventTypes, NULL, &menuEventHandlerRef);
    DisposeEventHandlerUPP(menuEventHandlerUPP);

    if (TkMacOSXUseMenuID(kEditMenu) != TCL_OK) {
	Tcl_Panic("Menu ID %d is already in use!", kEditMenu);
    }
    err = ChkErr(CreateNewMenu, kEditMenu, kMenuAttrDoNotUseUserCommandKeys,
	    &tkEditMenu);
    if (err != noErr) {
	Tcl_Panic("CreateNewMenu failed !");
    }
    SetMenuTitle(tkEditMenu, "\pEdit");
    InsertMenu(tkEditMenu, 0);
    AppendMenu(tkEditMenu, "\pCut/X");
    AppendMenu(tkEditMenu, "\pCopy/C");
    AppendMenu(tkEditMenu, "\pPaste/V");
    AppendMenu(tkEditMenu, "\pClear");
    if (TkMacOSXUseMenuID(kHMHelpMenuID) != TCL_OK) {
	Tcl_Panic("Help menu ID %s is already in use!", kHMHelpMenuID);
    }

    /*
     * Workaround a Carbon bug with kHICommandPreferences: the first call to
     * IsMenuKeyEvent returns false for the preferences menu item key shorcut
     * event (even if the corresponding menu item is dynamically enabled by a
     * kEventCommandUpdateStatus handler), unless the kHICommandPreferences
     * menu item has previously been enabled manually. [Bug 1481503]
     */

    EnableMenuCommand(NULL, kHICommandPreferences);

    DrawMenuBar();
    return;
}

/*
 *----------------------------------------------------------------------
 *
 * GenerateEditEvent --
 *
 *	Takes an edit menu item and posts the corasponding a virtual event to
 *	Tk's event queue.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May place events of queue.
 *
 *----------------------------------------------------------------------
 */

static void
GenerateEditEvent(
    int flag)
{
    XVirtualEvent event;
    int x, y;
    Tk_Window tkwin;
    Window window;
    TkDisplay *dispPtr;

    window = TkMacOSXGetXWindow(ActiveNonFloatingWindow());
    dispPtr = TkGetDisplayList();
    tkwin = Tk_IdToWindow(dispPtr->display, window);
    tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr;
    if (tkwin == NULL) {
	return;
    }

    bzero(&event, sizeof(XVirtualEvent));
    event.type = VirtualEvent;
    event.serial = Tk_Display(tkwin)->request;
    event.send_event = false;
    event.display = Tk_Display(tkwin);
    event.event = Tk_WindowId(tkwin);
    event.root = XRootWindow(Tk_Display(tkwin), 0);
    event.subwindow = None;
    event.time = TkpGetMS();

    XQueryPointer(NULL, None, NULL, NULL,
	    &event.x_root, &event.y_root, &x, &y, &event.state);
    Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y);
    event.same_screen = true;

    switch (flag) {
    case EDIT_CUT:
	event.name = Tk_GetUid("Cut");
	break;
    case EDIT_COPY:
	event.name = Tk_GetUid("Copy");
	break;
    case EDIT_PASTE:
	event.name = Tk_GetUid("Paste");
	break;
    case EDIT_CLEAR:
	event.name = Tk_GetUid("Clear");
	break;
    }
    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
}

/*
 * Local Variables:
 * fill-column: 78
 * c-basic-offset: 4
 * End:
 */