/* 
 * sxTitle.c --
 *
 *	This file implements title windows using the facilities of the X
 *	window package and the Sx dispatcher.  Titles are rectangular
 *	windows that display up to three pieces of text:  one on the
 *	left, one in the center, and one on the right.  They are
 *	"output-only" windows:  keystrokes and mouse actions have no
 *	impact on them.
 *
 * Copyright (C) 1986, 1988 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.
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/lib/sx/RCS/sxTitle.c,v 1.5 89/05/12 13:16:19 ouster Exp $ SPRITE (Berkeley)";
#endif not lint

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <strings.h>
#include "sx.h"
#include "sxInt.h"

/*
 * Library imports:
 */

extern char *malloc();

/*
 * For each title there is one structure of the type defined below,
 * which is used to hold information about the title.
 */

typedef struct Title {
    Display *display;		/* Connection to X server. */
    Window w;			/* Window corresponding to title. */
    int width, height;		/* Dimensions of w, in pixels. */
    XFontStruct *fontPtr;	/* How to draw text. */
    unsigned long foreground;	/* Color to use for text. */
    unsigned long background;	/* Color to use for background. */
    unsigned long stripe;	/* Color to use for stripe. */
    GC gc;			/* Graphics context used to draw stuff
				 * in the title window. */
    char *leftText;		/* String to display left-justified in
				 * title (space is dynamically allocated). */
    int numLeftChars;		/* Number of non-null chars. in leftText. */
    int leftWidth;		/* Width of left string, in pixels. */
    char *centerText;		/* String to display in center. */
    int numCenterChars;		/* Number of non-null chars. in centerText. */
    int centerWidth;		/* Width of center string, in pixels. */
    char *rightText;		/* String to display on right. */
    int numRightChars;		/* Number of non-null chars. in rightText. */
    int rightWidth;		/* Width of right string, in pixels. */
    int textMargin;		/* Margin to use on either side of text. */
} Title;

/*
 * The context below is used to map from X window ids to Title structures.
 */

static XContext titleContext;
static int init = 0;

/*
 * Margin to use around stripes:
 */

#define STRIPE_MARGIN 2

/*
 * Bitmap to use for striping title windows:
 */

#include "bitmaps/titleStripe"
static Pixmap stripeTile;

/*
 * Forward references:
 */

static void	TitleInit();
static void	TitleEventProc();
static void	TitleRedisplay();

/*
 *----------------------------------------------------------------------
 *
 * Sx_TitleMake --
 *
 *	This procedure turns an ordinary window into a title window.
 *	If the window is already a title window, then its parameters
 *	are changed.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	From now on, this window is a title window.  The given strings
 *	will be displayed in the window, in the given font and colors.
 *
 *----------------------------------------------------------------------
 */

void
Sx_TitleMake(display, w, fontPtr, foreground, background, stripe,
	leftText, centerText, rightText)
    Display *display;			/* Connection to X server. */
    Window w;				/* Window that is to become a title
					 * window.  If this isn't already a
					 * title window, then it shouldn't
					 * be mapped.  If it's already a title
					 * window, its parameters will be
					 * changed and the window will be
					 * redisplayed. */
    XFontStruct *fontPtr;		/* Font in which to display text.  If
					 * NULL, a default font is used. */
    unsigned long foreground;		/* Color in which to display text. */
    unsigned long background;		/* Color for background of title. */
    unsigned long stripe;		/* Color to use for title stripe.  If
					 * same as background, then no stripe
					 * is drawn. */
    char *leftText;			/* Text to display left-justified in
					 * window.  If NULL, no left-justified
					 * text is displayed. */
    char *centerText;			/* Text to display in center of window,
					 * or NULL. */
    char *rightText;			/* Text to display right-justified in
					 * window, or NULL. */
{
    register Title *titlePtr;
    caddr_t data;
    XSetWindowAttributes atts;
    XGCValues values;

    if (!init) {
	TitleInit(display);
    }

    /*
     * See if this window is already a title window.  If so, then
     * prepare it for re-allocation.  If not, then make a new Title
     * structure.
     */

    if (XFindContext(display, w, titleContext, &data) == 0) {
	titlePtr = (Title *) data;
	if (titlePtr->leftText != NULL) {
	    free(titlePtr->leftText);
	}
	if (titlePtr->centerText != NULL) {
	    free(titlePtr->centerText);
	}
	if (titlePtr->rightText != NULL) {
	    free(titlePtr->rightText);
	}
	XFreeGC(display, titlePtr->gc);
    } else {
	Drawable dum1;
	int dum2;

	titlePtr = (Title *) malloc(sizeof(Title));
	titlePtr->display = display;
	titlePtr->w = w;
	XGetGeometry(display, w, &dum1, &dum2, &dum2,
		&titlePtr->width, &titlePtr->height, &dum2, &dum2);
	(void) Sx_HandlerCreate(display, w,
		ExposureMask|StructureNotifyMask, TitleEventProc,
		(ClientData) titlePtr);
	atts.background_pixel = background;
	XChangeWindowAttributes(display, w, CWBackPixel, &atts);
	XSaveContext(display, w, titleContext, (caddr_t) titlePtr);
    }

    /*
     * Initialize the contents of the title.
     */

    if (fontPtr == NULL) {
	fontPtr = Sx_GetDefaultFont(display);
    }
    titlePtr->fontPtr = fontPtr;
    titlePtr->foreground = foreground;
    titlePtr->background = background;
    titlePtr->stripe = stripe;
    values.stipple = stripeTile;
    values.font = fontPtr->fid;
    titlePtr->gc = XCreateGC(display, w, GCStipple|GCFont, &values);
    if (leftText != NULL) {
	titlePtr->numLeftChars = strlen(leftText);
	titlePtr->leftText = (char *)
		malloc((unsigned) (titlePtr->numLeftChars + 1));
	(void) strcpy(titlePtr->leftText, leftText);
	titlePtr->leftWidth = XTextWidth(titlePtr->fontPtr,
		titlePtr->leftText, titlePtr->numLeftChars);
    } else {
	titlePtr->leftText = NULL;
	titlePtr->numLeftChars = titlePtr->leftWidth = 0;
    }
    if (centerText != NULL) {
	titlePtr->numCenterChars = strlen(centerText);
	titlePtr->centerText = (char *)
		malloc((unsigned) (titlePtr->numCenterChars + 1));
	(void) strcpy(titlePtr->centerText, centerText);
	titlePtr->centerWidth = XTextWidth(titlePtr->fontPtr,
		titlePtr->centerText, titlePtr->numCenterChars);
    } else {
	titlePtr->centerText = NULL;
	titlePtr->numCenterChars = titlePtr->centerWidth = 0;
    }
    if (rightText != NULL) {
	titlePtr->numRightChars = strlen(rightText);
	titlePtr->rightText = (char *)
		malloc((unsigned) (titlePtr->numRightChars + 1));
	(void) strcpy(titlePtr->rightText, rightText);
	titlePtr->rightWidth = XTextWidth(titlePtr->fontPtr,
		titlePtr->rightText, titlePtr->numRightChars);
    } else {
	titlePtr->rightText = NULL;
	titlePtr->numRightChars = titlePtr->rightWidth = 0;
    }
    titlePtr->textMargin = XTextWidth(titlePtr->fontPtr, "m", 1); 

    atts.background_pixel = background;
    XChangeWindowAttributes(display, w, CWBackPixel, &atts);

    TitleRedisplay(titlePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Sx_TitleCreate --
 *
 *	This procedure is like Sx_TitleMake except that it creates the
 *	title window and also packs it into a parent.
 *
 * Results:
 *	The return value is a pointer to a newly-created title window,
 *	which will appear on "side" of "parent" and have its geometry
 *	managed by the Sx packer.
 *
 * Side effects:
 *	The given strings will be displayed in the window, in the given
 *	font and colors.
 *
 *----------------------------------------------------------------------
 */

Window
Sx_TitleCreate(display, parent, side, size, borderSize, fontPtr, foreground,
	background, stripe, leftText, centerText, rightText)
    Display *display;			/* Connection to server. */
    Window parent;			/* Parent window in which to create
					 * the new title window. */
    Sx_Side side;			/* Side of parent on which to create
					 * the new title (normally SX_TOP or
					 * SX_BOTTOM). */
    int size;				/* Size of new window perpendicular to
					 * length of side. */
    int borderSize;			/* Width of border for new window. */
    XFontStruct *fontPtr;		/* Font in which to display text.  If
					 * NULL, a default font is used. */
    unsigned long foreground;		/* Color in which to display text and
					 * border. */
    unsigned long background;		/* Color for background of title. */
    unsigned long stripe;		/* Color to use for stripe.  If same
					 * as background, then no stripe is
					 * drawn. */
    char *leftText;			/* Text to display left-justified in
					 * window.  If NULL, no left-justified
					 * text is displayed. */
    char *centerText;			/* Text to display in center of window,
					 * or NULL. */
    char *rightText;			/* Text to display right-justified in
					 * window, or NULL. */
{
    Window w;

    w = Sx_CreatePacked(display, parent, side, size, 0, borderSize,
	    foreground, (Window) 0, background);
    Sx_TitleMake(display, w, fontPtr, foreground, background, stripe,
	    leftText, centerText, rightText);
    return w;
}

/*
 *----------------------------------------------------------------------
 *
 * TitleInit --
 *
 *	This procedure is called once only to initialize shared
 *	variables for the module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Random initializations are performed.  See the code.
 *
 *----------------------------------------------------------------------
 */

static void
TitleInit(display)
    Display *display;		/* Connection to server. */
{
    titleContext = XUniqueContext();
    stripeTile = XCreateBitmapFromData(display,
	    RootWindow(display, DefaultScreen(display)),
	    titleStripe_bits, titleStripe_width, titleStripe_height);
    init = 1;
}

/*
 *----------------------------------------------------------------------
 *
 * TitleRedisplay --
 *
 *	Redraw the information in a title window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The title gets redrawn.
 *
 *----------------------------------------------------------------------
 */

static void
TitleRedisplay(titlePtr)
    register Title *titlePtr;	/* Button to redisplay. */
{
    int y, centerX, x1, x2, stripeHeight, extra;
    XGCValues values;

    XSetForeground(titlePtr->display, titlePtr->gc, titlePtr->background);
    XFillRectangle(titlePtr->display, titlePtr->w, titlePtr->gc, 0, 0,
	    titlePtr->width, titlePtr->height);
    
    /*
     * Draw some sexy stripes in the areas where there's no text
     * (unless stripes have been disabled by using the background
     * color). The stripe pattern must repeat every y_hot pixels,
     * with x_hot pixels of empty space at its end;  use this
     * information to center the stripe in the window.  Draw the
     * stripes from left-to-right in four pieces, one on each side
     * of each (potential) piece of text.
     */

    centerX = (titlePtr->width - titlePtr->centerWidth)/2;
    y = STRIPE_MARGIN;
    stripeHeight = titlePtr->height - 2*STRIPE_MARGIN;
    extra = stripeHeight%titleStripe_y_hot;
    if (extra < titleStripe_x_hot) {
	extra += titleStripe_y_hot;
    }
    extra -= titleStripe_x_hot;
    stripeHeight -= extra;
    y += extra/2;
    if ((titlePtr->stripe != titlePtr->background) && (stripeHeight > 0)) {
	values.foreground = titlePtr->stripe;
	values.fill_style = FillStippled;
	values.ts_y_origin = y;
	XChangeGC(titlePtr->display, titlePtr->gc,
		GCForeground|GCFillStyle|GCTileStipYOrigin, &values);
	
	/*
	 * Stripe to the left of the left text.
	 */

	x1 = titlePtr->textMargin;
	x2 = x1 + 2*titlePtr->textMargin;
	XFillRectangle(titlePtr->display, titlePtr->w, titlePtr->gc,
		x1, y, x2-x1, stripeHeight);
	
	/*
	 * Stripe between left and center text (if there's any center text).
	 */

	x1 = x2;
	if (titlePtr->leftText != NULL) {
	    x1 += 2*titlePtr->textMargin + titlePtr->leftWidth;
	}
	if (titlePtr->centerText != NULL) {
	    x2 = centerX - titlePtr->textMargin;
	    if (x1 < x2) {
		XFillRectangle(titlePtr->display, titlePtr->w, titlePtr->gc,
			x1, y, x2-x1, stripeHeight);
	    }
	    x1 = x2 + 2*titlePtr->textMargin + titlePtr->centerWidth;
	}

	/*
	 * Stripe to left of right text.
	 */

	x2 = titlePtr->width - titlePtr->rightWidth - (5*titlePtr->textMargin);
	if (x1 < x2) {
	    XFillRectangle(titlePtr->display, titlePtr->w, titlePtr->gc,
		    x1, y, x2-x1, stripeHeight);
	}

	/*
	 * Stripe to right of right text.
	 */

	x1 = x2;
	if (titlePtr->rightText != NULL) {
	    x1 += 2*titlePtr->textMargin + titlePtr->rightWidth;
	}
	x2 = titlePtr->width - titlePtr->textMargin;
	XFillRectangle(titlePtr->display, titlePtr->w, titlePtr->gc,
		x1, y, x2-x1, stripeHeight);
	XSetFillStyle(titlePtr->display, titlePtr->gc, FillSolid);
    }
    XSetForeground(titlePtr->display, titlePtr->gc, titlePtr->foreground);
    y = (titlePtr->height + titlePtr->fontPtr->ascent
	    - titlePtr->fontPtr->descent)/2;
    if (titlePtr->leftText != NULL) {
	XDrawString(titlePtr->display, titlePtr->w, titlePtr->gc,
		4*titlePtr->textMargin, y, titlePtr->leftText,
		titlePtr->numLeftChars);
    }
    if (titlePtr->centerText != NULL) {
	XDrawString(titlePtr->display, titlePtr->w, titlePtr->gc,
		centerX, y, titlePtr->centerText, titlePtr->numCenterChars);
    }
    if (titlePtr->rightText != NULL) {
	XDrawString(titlePtr->display, titlePtr->w, titlePtr->gc,
		titlePtr->width - titlePtr->rightWidth -
		4*titlePtr->textMargin, y, titlePtr->rightText,
		titlePtr->numRightChars);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TitleEventProc --
 *
 *	This procedure is invoked automatically by the Sx_ dispatcher
 * 	whenever an event occurs for a title window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on the event.
 *
 *----------------------------------------------------------------------
 */

static void
TitleEventProc(titlePtr, eventPtr)
    Title *titlePtr;			/* Button for which it happened. */
    XEvent *eventPtr;			/* Describes what happened. */
{
    switch (eventPtr->type) {

	case Expose:
	    /*
	     * When expose events come in bunches, ignore all but the
	     * last in the bunch (the whole window gets redisplayed
	     * anyway).
	     */
	    
	    if (eventPtr->xexpose.count == 0) {
		TitleRedisplay(titlePtr);
	    }
	    return;

	case ConfigureNotify:
	    titlePtr->width = eventPtr->xconfigure.width;
	    titlePtr->height = eventPtr->xconfigure.height;
	    return;

	case DestroyNotify:
	    XDeleteContext(titlePtr->display, titlePtr->w, titleContext);
	    XFreeGC(titlePtr->display, titlePtr->gc);
	    if (titlePtr->leftText != NULL) {
		free(titlePtr->leftText);
	    }
	    if (titlePtr->centerText != NULL) {
		free(titlePtr->centerText);
	    }
	    if (titlePtr->rightText != NULL) {
		free(titlePtr->rightText);
	    }
	    free((char *) titlePtr);
	    return;
    }
}
