/* 
 * triButton.c --
 *
 *      This module implements a triangle shaped button widget for the
 *      Tk toolkit.  The "triButton" does not have any text or
 *      bitmaps, but it can be of virtually any size, and all of the
 *      standard button options for color, relief, activation, command
 *      execution, etc. are available.
 *
 * moreButtons : button widgets for Tk (developed as a part of...)
 * TclMe -- Tool Command Language Menu System
 * Copyright (C) 1993 Daniel Simmons, Michael Hewitt & Idaho State University
 * For distribution terms see the file COPYING (GNU GPL).
 *
 * Based on code from Tk-3.2:
 *
 * Copyright 1990-1992 Regents of the University of California.
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty. 
 */

#include <tkInt.h>


/*
 * EXAMPLE triButton:
 *        direction = n
 *       
 *             .     ---
 *            . .     ^
 *           .   .    |  length
 *          .     .   v
 *         . . . . . ---
 *
 *         |       |
 *         |<----->|
 *         |       |
 *
 *          breadth
 *
 */


/*
 * A data structure of the following type is kept for each
 * widget managed by this file:
 */

typedef struct {
    Tk_Window tkwin;		/* Window that embodies the button.  NULL
				 * means that the window has been destroyed. */
    Display *display;		/* Display containing widget.  Needed to
				 * free up resources after tkwin is gone. */
    Tcl_Interp *interp;		/* Interpreter associated with button. */


    /*
     * Information used when displaying widget:
     */

    Tk_Uid state;		/* State of button for display
				 * purposes: normal, active, or
				 * disabled. */
    Tk_3DBorder normalFgBorder;	/* Structure used to draw 3-D border
				 * of arrow when window isn't active.
				 * NULL means no such border
				 * exists. */
    Tk_3DBorder activeFgBorder;	/* Structure used to draw 3-D border
				 * of arrow when window is active.
				 * NULL means no such border
				 * exists. */
    Tk_3DBorder normalBgBorder;	/* Structure used to draw background
				 * when window isn't active.  NULL
				 * means no such border exists. */
    Tk_3DBorder activeBgBorder;	/* Structure used to draw background
				 * when window is active.  NULL means
				 * no such border exists. */
    int borderWidth;		/* Width of border. */
    int relief;			/* 3-d effect: TK_RELIEF_RAISED, etc. */
    GC gc;		        /* GC used to copy from off-screen pixmap onto
				 * screen. */
    Tk_Anchor anchor;           /* where to place widget if it doesn't occupy
                                 * the entire window allocated to it */
    int breadth;                /* arrow width */

    /* 
     * Information used to describe the widget: 
     */

    int length;                 /* arrow length */
    Tk_Anchor direction;        /* direction of arrow */
    int fixedSize;              /* whether or not to resize to exactly
				 * occupy the window allocated to it */

    /*
     * Miscellaneous information:
     */

    Cursor cursor;		/* Current cursor for window, or None. */
    char *command;		/* Command to execute when button is
				 * invoked; valid for buttons only.
				 * If not NULL, it's malloc-ed. */
    int flags;			/* Various flags;  see below for
				 * definitions. */
} Button;


/*
 * Flag bits for buttons:
 *
 * REDRAW_PENDING:		Non-zero means a DoWhenIdle handler
 *				has already been queued to redraw
 *				this window.
 * SELECTED:			Non-zero means this button is selected,
 *				so special highlight should be drawn.
 */

#define REDRAW_PENDING		1
#define SELECTED		2


/*
 * Information used for parsing configuration specs:
 */

static Tk_ConfigSpec configSpecs[] = {
    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
	"#ffe4c4", Tk_Offset(Button, activeBgBorder), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
	"black", Tk_Offset(Button, activeBgBorder), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Foreground",
	"#eed5b7", Tk_Offset(Button, activeFgBorder), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Foreground",
	"white", Tk_Offset(Button, activeFgBorder), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
       "center", Tk_Offset(Button, anchor), 0},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	"#ffe4c4", Tk_Offset(Button, normalBgBorder), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
	"white", Tk_Offset(Button, normalBgBorder), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
	(char *) NULL, 0, 0},
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
	(char *) NULL, 0, 0},
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
	"2", Tk_Offset(Button, borderWidth), 0},
    {TK_CONFIG_STRING, "-command", "command", "Command",
	((char *) NULL), Tk_Offset(Button, command), TK_CONFIG_NULL_OK},
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
	((char *) NULL), Tk_Offset(Button, cursor),
	TK_CONFIG_NULL_OK},
    {TK_CONFIG_ANCHOR, "-direction", "direction", "Direction",
        "e", Tk_Offset(Button, direction), 0},
    {TK_CONFIG_BOOLEAN, "-fixedsize", "fixedize", "FixedSize",
        "0", Tk_Offset(Button, fixedSize), 0},
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
	(char *) NULL, 0, 0},
    {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
	"#ffe4c4", Tk_Offset(Button, normalFgBorder), TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
	"white", Tk_Offset(Button, normalFgBorder), TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_INT, "-length", "length", "Length",
	"15", Tk_Offset(Button, length), 0},
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
       "raised", Tk_Offset(Button, relief), 0},
    {TK_CONFIG_UID, "-state", "state", "State",
	"normal", Tk_Offset(Button, state), 0},
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
	(char *) NULL, 0, 0}

};

/*
 * String to print out in error messages, identifying options for
 * the widget command:
 */

static char *optionString = 
        "activate, configure, deactivate, flash, or invoke";


/*
 * Forward declarations for procedures defined later in this file:
 */

static void		triButtonEventProc _ANSI_ARGS_((ClientData clientData,
			    XEvent *eventPtr));
static int		triButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
			    Tcl_Interp *interp, int argc, char **argv));
static void		ComputeTriButtonGeometry _ANSI_ARGS_((
                            Button *butPtr));
static int		ConfigureTriButton _ANSI_ARGS_((Tcl_Interp *interp,
			    Button *butPtr, int argc, char **argv,
			    int flags));
static void		DestroyTriButton _ANSI_ARGS_((ClientData clientData));
static void		DisplayTriButton _ANSI_ARGS_((ClientData clientData));
static int		InvokeTriButton  _ANSI_ARGS_((Button *butPtr));

/*
 *--------------------------------------------------------------
 *
 * triButtonCmd --
 *
 *	This procedure is invoked to process the "triButton", 
 *	Tcl command.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

int
triButtonCmd(clientData, interp, argc, argv)
    ClientData clientData;	/* Main window associated with
				 * interpreter. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    register Button *butPtr;
    Tk_Window tkwin = (Tk_Window) clientData;
    Tk_Window new;

    if (argc < 2) {
	Tcl_AppendResult(interp, "wrong # args:  should be \"",
		argv[0], " pathName ?options?\"", (char *) NULL);
	return TCL_ERROR;
    }

    /*
     * Create the new window.
     */

    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
    if (new == NULL) {
	return TCL_ERROR;
    }

    /*
     * Initialize the data structure for the button.
     */

    butPtr = (Button *) ckalloc(sizeof(Button));
    butPtr->tkwin = new;
    butPtr->display = Tk_Display(new);
    butPtr->interp = interp;
    butPtr->state = tkNormalUid;
    butPtr->normalBgBorder = NULL;
    butPtr->activeBgBorder = NULL;
    butPtr->normalFgBorder = NULL;
    butPtr->activeFgBorder = NULL;
    butPtr->borderWidth = 0;
    butPtr->relief = TK_RELIEF_FLAT;
    butPtr->gc = None;
    butPtr->anchor = TK_ANCHOR_CENTER;
    butPtr->breadth = 0;
    butPtr->length = 0;
    butPtr->direction = TK_ANCHOR_N;
    butPtr->fixedSize = 0;
    butPtr->cursor = None;
    butPtr->command = NULL;
    butPtr->flags = 0;

    Tk_SetClass(new, "TriButton");
    Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask,
	    triButtonEventProc, (ClientData) butPtr);
    Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), triButtonWidgetCmd,
	    (ClientData) butPtr, (void (*)()) NULL);

    if (ConfigureTriButton(interp, butPtr, argc-2, argv+2, 0) != TCL_OK) {
	Tk_DestroyWindow(butPtr->tkwin);
	return TCL_ERROR;
    }

    interp->result = Tk_PathName(butPtr->tkwin);
    return TCL_OK;
}

/*
 *--------------------------------------------------------------
 *
 * triButtonWidgetCmd --
 *
 *	This procedure is invoked to process the Tcl command
 *	that corresponds to a widget managed by this module.
 *	See the user documentation for details on what it does.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	See the user documentation.
 *
 *--------------------------------------------------------------
 */

static int
triButtonWidgetCmd(clientData, interp, argc, argv)
    ClientData clientData;	/* Information about button widget. */
    Tcl_Interp *interp;		/* Current interpreter. */
    int argc;			/* Number of arguments. */
    char **argv;		/* Argument strings. */
{
    register Button *butPtr = (Button *) clientData;
    int result = TCL_OK;
    int length;
    char c;

    if (argc < 2) {
	sprintf(interp->result,
		"wrong # args: should be \"%.50s option [arg arg ...]\"",
		argv[0]);
	return TCL_ERROR;
    }
    Tk_Preserve((ClientData) butPtr);
    c = argv[1][0];
    length = strlen(argv[1]);
    if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) 
    {
	if (argc > 2) 
	{
	    sprintf(interp->result,
		    "wrong # args: should be \"%.50s activate\"",
		    argv[0]);
	    goto error;
	}
	if (butPtr->state != tkDisabledUid) 
	{
	    butPtr->state = tkActiveUid;
	    Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBgBorder);
	    goto redisplay;
	}
    } 
    else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) 
    {
	if (argc == 2) 
	{
	    result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
		    (char *) butPtr, (char *) NULL, 0);
	} 
	else if (argc == 3) 
	{
	    result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
		    (char *) butPtr, argv[2], 0);
	} 
	else 
	{
	    result = ConfigureTriButton(interp, butPtr, argc-2, argv+2,
		    TK_CONFIG_ARGV_ONLY);
	}
    } 
    else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
	    && (length > 2)) 
    {
	if (argc > 2) 
	{
	    sprintf(interp->result,
		    "wrong # args: should be \"%.50s deactivate\"",
		    argv[0]);
	    goto error;
	}
	if (butPtr->state != tkDisabledUid) 
	{
	    butPtr->state = tkNormalUid;
	    Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBgBorder);
	    goto redisplay;
	}
    } 
    else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0))
    {
	int i;

	if (argc > 2) 
	{
	    sprintf(interp->result,
		    "wrong # args: should be \"%.50s flash\"",
		    argv[0]);
	    goto error;
	}
	if (butPtr->state != tkDisabledUid) 
	{
	    for (i = 0; i < 4; i++) 
	    {
		butPtr->state = (butPtr->state == tkNormalUid)
			? tkActiveUid : tkNormalUid;
		Tk_SetBackgroundFromBorder(butPtr->tkwin,
			(butPtr->state == tkActiveUid) ? butPtr->activeBgBorder
			: butPtr->normalBgBorder);
		DisplayTriButton((ClientData) butPtr);
		XFlush(butPtr->display);
		Tk_Sleep(50);
	    }
	}
    } 
    else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0))
    {
	if (argc > 2) 
	{
	    sprintf(interp->result,
		    "wrong # args: should be \"%.50s invoke\"",
		    argv[0]);
	    goto error;
	}
	if (butPtr->state != tkDisabledUid) 
	{
	    result = InvokeTriButton(butPtr);
	}
    } 
    else 
    {
	sprintf(interp->result,
		"bad option \"%.50s\":  must be %s", argv[1],
		optionString);
	goto error;
    }
    Tk_Release((ClientData) butPtr);
    return result;

    redisplay:
    if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
	Tk_DoWhenIdle(DisplayTriButton, (ClientData) butPtr);
	butPtr->flags |= REDRAW_PENDING;
    }
    Tk_Release((ClientData) butPtr);
    return TCL_OK;

    error:
    Tk_Release((ClientData) butPtr);
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * DestroyTriButton --
 *
 *	This procedure is invoked by Tk_EventuallyFree or Tk_Release
 *	to clean up the internal structure of a triButton at a safe time
 *	(when no-one is using it anymore).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Everything associated with the widget is freed up.
 *
 *----------------------------------------------------------------------
 */

static void
DestroyTriButton(clientData)
    ClientData clientData;		/* Info about entry widget. */
{
    register Button *butPtr = (Button *) clientData;

    if (butPtr->normalBgBorder != NULL) 
    {
      Tk_Free3DBorder(butPtr->normalBgBorder);
    }
    if (butPtr->activeBgBorder != NULL) 
    {
      Tk_Free3DBorder(butPtr->activeBgBorder);
    }
    if (butPtr->normalFgBorder != NULL) 
    {
      Tk_Free3DBorder(butPtr->normalFgBorder);
    }
    if (butPtr->activeFgBorder != NULL) 
    {
      Tk_Free3DBorder(butPtr->activeFgBorder);
    }
    if (butPtr->gc != None) 
    {
      Tk_FreeGC(butPtr->display, butPtr->gc);
    }
    if (butPtr->cursor != None) 
    {
      Tk_FreeCursor(butPtr->display, butPtr->cursor);
    }
    if (butPtr->command != NULL) 
    {
      ckfree(butPtr->command);
    }
    ckfree((char *) butPtr);
  }

/*
 *----------------------------------------------------------------------
 *
 * ConfigureTriButton --
 *
 *	This procedure is called to process an argv/argc list, plus
 *	the Tk option database, in order to configure (or
 *	reconfigure) a triButton widget.
 *
 * Results:
 *	The return value is a standard Tcl result.  If TCL_ERROR is
 *	returned, then interp->result contains an error message.
 *
 * Side effects:
 *	Configuration information, such as text string, colors, font,
 *	etc. get set for butPtr;  old resources get freed, if there
 *	were any.  The button is redisplayed.
 *
 *----------------------------------------------------------------------
 */

static int
ConfigureTriButton(interp, butPtr, argc, argv, flags)
    Tcl_Interp *interp;		/* Used for error reporting. */
    register Button *butPtr;	/* Information about widget;  may or may
				 * not already have values for some fields. */
    int argc;			/* Number of valid entries in argv. */
    char **argv;		/* Arguments. */
    int flags;			/* Flags to pass to Tk_ConfigureWidget. */
{
    XGCValues gcValues;
    GC newGC;

    if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
	    argc, argv, (char *) butPtr, flags) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * A few options need special processing, such as setting the
     * background from a 3-D border, or filling in complicated
     * defaults that couldn't be specified to Tk_ConfigureWidget.
     */

    switch (butPtr->direction) 
    {
      case TK_ANCHOR_N: case TK_ANCHOR_S: case TK_ANCHOR_E: case TK_ANCHOR_W:
        break;
      default:
        Tcl_AppendResult(interp, "bad direction value \"", 
			 Tk_NameOfAnchor(butPtr->direction),
			 "\": must be n, s, e, or w", (char *) NULL);
	butPtr->direction = TK_ANCHOR_E;
	return TCL_ERROR;
    }

    if (butPtr->state == tkActiveUid) {
	Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBgBorder);
    } else {
	Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBgBorder);
	if ((butPtr->state != tkNormalUid)
		&& (butPtr->state != tkDisabledUid)) {
	    Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
		    "\":  must be normal, active, or disabled", (char *) NULL);
	    butPtr->state = tkNormalUid;
	    return TCL_ERROR;
	}
    }

    gcValues.foreground = Tk_3DBorderColor(butPtr->normalFgBorder)->pixel;
    gcValues.background = Tk_3DBorderColor(butPtr->normalBgBorder)->pixel;

    /*
     * Note: GraphicsExpose events are disabled in normalTextGC because it's
     * used to copy stuff from an off-screen pixmap onto the screen (we know
     * that there's no problem with obscured areas).
     */

    gcValues.graphics_exposures = False;
    newGC = Tk_GetGC(butPtr->tkwin,
	    GCForeground|GCBackground|GCGraphicsExposures, &gcValues);
    if (butPtr->gc != None) {
	Tk_FreeGC(butPtr->display, butPtr->gc);
    }
    butPtr->gc = newGC;

    ComputeTriButtonGeometry(butPtr);

    /*
     * Lastly, arrange for the button to be redisplayed.
     */

    if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
	Tk_DoWhenIdle(DisplayTriButton, (ClientData) butPtr);
	butPtr->flags |= REDRAW_PENDING;
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * DisplayTriButton --
 *
 *	This procedure is invoked to display a triButton widget.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Commands are output to X to display the button in its
 *	current mode.
 *
 * ALL YE WHO ENTER:
 *           THERE BE CRUFTY DRAGONS HERE (distant cousins to the 
 *                                         dragons found in xterm)
 *
 *----------------------------------------------------------------------
 */

static void
DisplayTriButton(clientData)
    ClientData clientData;	/* Information about widget. */
{
  register Button *butPtr = (Button *) clientData;
  register Tk_Window tkwin = butPtr->tkwin;
  Tk_3DBorder bgBorder, fgBorder;
  Pixmap pixmap;
  XPoint points[3];
  int i, width, height, xOffset, yOffset, length, breadth;
  

/* compute the geometry, then return unless the window is mapped */

  ComputeTriButtonGeometry(butPtr);
  
  butPtr->flags &= ~REDRAW_PENDING;
  if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) 
  {
    return;
  }


/* setup the border */

  if (butPtr->state == tkActiveUid)
  {
    bgBorder = butPtr->activeBgBorder;
    fgBorder = butPtr->activeFgBorder;
  }
  else
  {
    bgBorder = butPtr->normalBgBorder;
    fgBorder = butPtr->normalFgBorder;
  }
    

/* prepare pixmap */

  /*
   * In order to avoid screen flashes, this procedure redraws the
   * button in a pixmap, then copies the pixmap to the screen in a
   * single operation.  This means that there's no point in time where
   * the on-sreen image has been cleared. 
   */

  pixmap = XCreatePixmap(butPtr->display, Tk_WindowId(tkwin),
			 Tk_Width(tkwin), Tk_Height(tkwin), 
			 Tk_Depth(tkwin));
  Tk_Fill3DRectangle(butPtr->display, pixmap, bgBorder,
		     0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 
		     0, TK_RELIEF_FLAT);
  

/* display triangle */

  /* 
   * Calculate basic triangle points from direction, length and breadth.
   * Get actual length and breadth from window size (but maintain shape)
   * unless fixedSize is true in which case just use the length and go home.
   */

  /*****NOTE: Points must be specified COUNTER-CLOCKWISE for relief to 
   *****      work properly  
   */

  switch(butPtr->direction) 
  {
  case TK_ANCHOR_N:
    if (!butPtr->fixedSize)
    {
      length = Tk_Height(tkwin);
      breadth = ((length * 200) - 100) / 170;
      if (breadth > Tk_Width(tkwin))
      {
	breadth = Tk_Width(tkwin);
	length = ((breadth * 170) + 100) / 200;
      }
    }
    else
    {
      length = butPtr->length;
      breadth = butPtr->breadth;
    }
    points[0].x = breadth/2;
    points[0].y = 0;
    points[1].x = 0;
    points[1].y = length;
    points[2].x = breadth;
    points[2].y = length;
    width = breadth;
    height = length;
    break;
  case TK_ANCHOR_W:
    if (!butPtr->fixedSize)
    {
      length = Tk_Width(tkwin);
      breadth = ((length * 200) - 100) / 170;
      if (breadth > Tk_Height(tkwin))
      {
	breadth = Tk_Height(tkwin);
	length = ((breadth * 170) + 100) / 200;
      }
    }
    else
    {
      length = butPtr->length;
      breadth = butPtr->breadth;
    }      
    points[0].x = 0;
    points[0].y = breadth/2;
    points[1].x = length;
    points[1].y = breadth;
    points[2].x = length;
    points[2].y = 0;
    width = length;
    height = breadth;
    break;
  case TK_ANCHOR_S:
    if (!butPtr->fixedSize)
    {
      length = Tk_Height(tkwin);
      breadth = ((length * 200) - 100) / 170;
      if (breadth > Tk_Width(tkwin))
      {
	breadth = Tk_Width(tkwin);
	length = ((breadth * 170) + 100) / 200;
      }
    }
    else
    {
      length = butPtr->length;
      breadth = butPtr->breadth;
    }
    points[0].x = 0;
    points[0].y = 0;
    points[1].x = breadth/2;
    points[1].y = length;
    points[2].x = breadth;
    points[2].y = 0;
    width = breadth;
    height = length;
    break;
  default: /* TK_ANCHOR_E */
    if (!butPtr->fixedSize)
    {
      length = Tk_Width(tkwin);
      breadth = ((length * 200) - 100) / 170;
      if (breadth > Tk_Height(tkwin))
      {
	breadth = Tk_Height(tkwin);
	length = ((breadth * 170) + 100) / 200;
      }
    }
    else
    {
      length = butPtr->length;
      breadth = butPtr->breadth;
    }      
    points[0].x = 0;
    points[0].y = 0;
    points[1].x = 0;
    points[1].y = breadth;
    points[2].x = length;
    points[2].y = breadth/2;
    width = length;
    height = breadth;
    break;
  }    

  /* translate based on the anchor */

  switch(butPtr->anchor)
  {
  case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
    xOffset = (Tk_Width(tkwin) - width) / 2;
    break;
  case TK_ANCHOR_NE: case TK_ANCHOR_E: case TK_ANCHOR_SE:
    xOffset = Tk_Width(tkwin) - width;
    break;
  default:
    xOffset = 0;
    break;
  }

  switch(butPtr->anchor)
  {
  case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
    yOffset = (Tk_Height(tkwin) - height) /2;
    break;
  case TK_ANCHOR_SW: case TK_ANCHOR_S: case TK_ANCHOR_SE:
    yOffset = Tk_Height(tkwin) - height;
    break;
  default:
    yOffset = 0;
  }
  
  for (i = 0; i < 3; i++)
  {
    points[i].x += xOffset;
    points[i].y += yOffset;
  }

  /* draw the triangle onto the pixmap */

  Tk_Fill3DPolygon(butPtr->display, pixmap, fgBorder, points,
		   3, butPtr->borderWidth, butPtr->relief);


/* copy the pixmap onto the screen, then delete it */
  
  XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin), butPtr->gc, 
	    0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  XFreePixmap(butPtr->display, pixmap);
}

/*
 *--------------------------------------------------------------
 *
 * triButtonEventProc --
 *
 *	This procedure is invoked by the Tk dispatcher for various
 *	events on buttons.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	When the window gets deleted, internal structures get
 *	cleaned up.  When it gets exposed, it is redisplayed.
 *
 *--------------------------------------------------------------
 */

static void
triButtonEventProc(clientData, eventPtr)
    ClientData clientData;	/* Information about window. */
    XEvent *eventPtr;		/* Information about event. */
{
    Button *butPtr = (Button *) clientData;
    if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
	if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
	    Tk_DoWhenIdle(DisplayTriButton, (ClientData) butPtr);
	    butPtr->flags |= REDRAW_PENDING;
	}
    } else if (eventPtr->type == DestroyNotify) {
	Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin));
	butPtr->tkwin = NULL;
	if (butPtr->flags & REDRAW_PENDING) {
	    Tk_CancelIdleCall(DisplayTriButton, (ClientData) butPtr);
	}
	Tk_EventuallyFree((ClientData) butPtr, DestroyTriButton);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * ComputeTriButtonGeometry --
 *
 *	After changes in a button's text, tag or bitmap, this procedure
 *	recomputes the button's geometry and passes this information
 *	along to the geometry manager for the window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The button's window may change size.
 *
 * Differences from regular buttons:
 *      Width is always in terms of screen pixels--NOT average text 
 *      character width for the font.  Also, width is a maximum width...
 *
 *----------------------------------------------------------------------
 */

static void
ComputeTriButtonGeometry(butPtr)
    register Button *butPtr;	/* Button whose geometry may have changed. */
{
  int width, height;
  
  butPtr->breadth = ((butPtr->length * 200) - 100) / 170;

  switch(butPtr->direction) 
  {
  case TK_ANCHOR_N:
    width = butPtr->breadth;
    height = butPtr->length;
    break;
  case TK_ANCHOR_W:
    width = butPtr->length;
    height = butPtr->breadth;
    break;
  case TK_ANCHOR_S:
    width = butPtr->breadth;
    height = butPtr->length;
    break;
  default: /* TK_ANCHOR_E */
    width = butPtr->length;
    height = butPtr->breadth;
    break;
  }    
  
  Tk_GeometryRequest(butPtr->tkwin, width, height);
  Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth);
}

/*
 *----------------------------------------------------------------------
 *
 * InvokeTriButton --
 *
 *	This procedure is called to carry out the actions associated
 *	with a button, such as invoking a Tcl command or setting a
 *	variable.  This procedure is invoked, for example, when the
 *	button is invoked via the mouse.
 *
 * Results:
 *	A standard Tcl return value.  Information is also left in
 *	interp->result.
 *
 * Side effects:
 *	Depends on the button and its associated command.
 *
 *----------------------------------------------------------------------
 */

static int
InvokeTriButton(butPtr)
    register Button *butPtr;		/* Information about button. */
{
    if (butPtr->command != NULL) 
    {
	return Tcl_GlobalEval(butPtr->interp, butPtr->command);
    }
    return TCL_OK;
}


