/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <Xm/Xm.h>
#include <sys/time.h>
#include "xdir.h"
#include "urbutton.h"

#define NSTOP_FRAMES  2

#define SDELTA 500000   /* Must be less than 1000000 */

static Pixmap stop_pixmap[NSTOP_FRAMES];
static Pixmap armed_stop_pixmap[NSTOP_FRAMES];
static Pixmap llnlxdir_pixmap;
static Pixmap grey_llnlxdir_pixmap;
static Pixmap armed_llnlxdir_pixmap;
static int stop_state;
static int ur_button_sensitivity;
static struct timeval time_of_next_update;
static struct dirwin_st *stop_dirwin = NULL;
static Pixel ur_button_fg;
static Pixel ur_button_bg;
static Pixel ur_button_armed_color;
static Dimension ur_button_width;
static Dimension ur_button_height;
static Dimension ur_button_ht;
static Dimension ur_button_st;
static int nstop_frame;
static int nstop_frames;
static Widget w_abort_dialog;

extern Display *display;
extern XtAppContext app;
extern Window root_window;
extern int depth;
extern int stop_button_blinking;

void cb_about_xdir();
void cb_stop();
void cb_stop_arm();
void cb_stop_disarm();
void cb_stop_expose();
void cb_llnlxdir_arm();
void cb_llnlxdir_expose();
void create_ur_button_pixmaps();
void draw_ur_button();
Widget show_in_progress_dialog();


/****************************************************************************/
/*                                                                          */
/*  Values of stop_state:                                                   */
/*                                                                          */
/*      0 - initial value when stop button enabled                          */  
/*      1 - stop button armed                                               */
/*      2 - stop button pushed but stop() has not returned True             */
/*      3 - stop button pushed and stop() has returned True                 */
/*                                                                          */
/****************************************************************************/


/*
 * initialize_ur_button - Determine colors and geometry of upper-right
 *                        button.  Also, create pixmaps needed to display
 *                        it.  "dirwin" identifies the first directory
 *                        window to be created.
 */
void
initialize_ur_button(dirwin)
struct dirwin_st *dirwin;
{
	Pixmap stop0_pixmap;
	Pixmap armed_stop0_pixmap;
	Pixmap stop1_pixmap;
	Colormap cmap;
	Pixel foreground;
	Pixel top_shadow;
	Pixel bottom_shadow;

	/* Get geometry and colors of upper-right button */
	XtVaGetValues(dirwin->w_urButton,
		XmNcolormap,			&cmap,
		XmNbackground,			&ur_button_bg,
		XmNforeground,			&ur_button_fg,
		XmNheight,				&ur_button_height,
		XmNwidth,				&ur_button_width,
		XmNhighlightThickness,	&ur_button_ht,
		XmNshadowThickness,		&ur_button_st,
		NULL
	);
	XmGetColors(XtScreen(dirwin->w_urButton), cmap, ur_button_bg, &foreground,
		&top_shadow, &bottom_shadow, &ur_button_armed_color);

	/* Create pixmap for LLNL XDIR button */
	create_ur_button_pixmaps(llnlxdir_bits, llnlxdir_width, llnlxdir_height,
		&llnlxdir_pixmap, &grey_llnlxdir_pixmap, &armed_llnlxdir_pixmap);

	/* Create pixmap for first frame of stop button */
	create_ur_button_pixmaps(stop0_bits, stop0_width, stop0_height,
		&stop0_pixmap, NULL, &armed_stop0_pixmap);
	stop_pixmap[0] = stop0_pixmap;
	armed_stop_pixmap[0] = armed_stop0_pixmap;

	/* Create pixmap for second frame of stop button */
	create_ur_button_pixmaps(stop1_bits, stop1_width, stop1_height,
		&stop1_pixmap, NULL, NULL);
	stop_pixmap[1] = stop1_pixmap;
	armed_stop_pixmap[1] = armed_stop0_pixmap;
}


/*
 * add_llnlxdir_button_callbacks - Add LLNL XDIR button callbacks for
 *                                 directory window "dirwin".
 */
void
add_llnlxdir_button_callbacks(dirwin)
struct dirwin_st *dirwin;
{
   	XtAddCallback(dirwin->w_urButton, XmNactivateCallback, cb_about_xdir,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNarmCallback, cb_llnlxdir_arm,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNdisarmCallback, cb_llnlxdir_expose,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNexposeCallback, cb_llnlxdir_expose,
		(XtPointer)dirwin);
}


/*
 * remove_llnlxdir_button_callbacks - Remove LLNL XDIR button callbacks for
 *                                    directory window "dirwin".
 */
void
remove_llnlxdir_button_callbacks(dirwin)
struct dirwin_st *dirwin;
{
	XtRemoveCallback(dirwin->w_urButton, XmNactivateCallback, cb_about_xdir,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNarmCallback, cb_llnlxdir_arm,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNdisarmCallback, cb_llnlxdir_expose,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNexposeCallback, cb_llnlxdir_expose,
		(XtPointer)dirwin);
}


/*
 * add_stop_button_callbacks - Add stop button callbacks for directory
 *                             window "dirwin".
 */
void
add_stop_button_callbacks(dirwin)
struct dirwin_st *dirwin;
{
   	XtAddCallback(dirwin->w_urButton, XmNactivateCallback, cb_stop,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNarmCallback, cb_stop_arm,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNdisarmCallback, cb_stop_disarm,
		(XtPointer)dirwin);
	XtAddCallback(dirwin->w_urButton, XmNexposeCallback, cb_stop_expose,
		(XtPointer)dirwin);
}


/*
 * remove_stop_button_callbacks - Remove stop button callbacks for directory
 *                                window "dirwin".
 */
void
remove_stop_button_callbacks(dirwin)
struct dirwin_st *dirwin;
{
	XtRemoveCallback(dirwin->w_urButton, XmNactivateCallback, cb_stop,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNarmCallback, cb_stop_arm,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNdisarmCallback, cb_stop_disarm,
		(XtPointer)dirwin);
	XtRemoveCallback(dirwin->w_urButton, XmNexposeCallback, cb_stop_expose,
		(XtPointer)dirwin);
}


/*
 * show_stop_button - Replace the "LLNL XDIR" button with the stop button.
 *                    Call hide_stop_button() to restore the "LLNL XDIR"
 *                    button.  stop() must be frequently called to animate
 *                    the stop button.  It is a fatal error not to call
 *                    hide_stop_button() between calls to this function.
 */
void
show_stop_button(dirwin)
struct dirwin_st *dirwin;
{
	struct timeval current_time;
	struct timeval delta;

	/* Sanity check */
	if (stop_dirwin)
		fatal_error(
			"Trouble in show_stop_button() - Operation already in progress.");

	/* Remember which directory window has the stop button */
	stop_dirwin = dirwin;

	/* Remember sensitivity of LLNL XDIR button */
	ur_button_sensitivity = XtIsSensitive(stop_dirwin->w_urButton);
	XtSetSensitive(stop_dirwin->w_urButton, True);

	/* Replace the LLNL XDIR button's callbacks with the stop button's */
	remove_llnlxdir_button_callbacks(stop_dirwin);
	add_stop_button_callbacks(stop_dirwin);

	/* Control blinking */
	if (stop_button_blinking)
		nstop_frames = NSTOP_FRAMES;
	else
		nstop_frames = 1;

	/* Draw first frame of animated stop button */
	draw_ur_button(stop_dirwin, stop_pixmap[0]);
	nstop_frame = 0;

	/* Initialize stop button's state variables */
	stop_state = 0;

	/* Calculate time to update animated stop button with next frame */
	gettimeofday(&current_time, NULL);
	delta.tv_sec = 0;
	delta.tv_usec = SDELTA;
	add_timevals(&current_time, &delta, &time_of_next_update);
}


/*
 * cb_stop - Callback to process stop button.
 */
void
cb_stop(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	/* Sanity check */
	if (stop_state > 1)
		fatal_error("Bug in cb_stop()");

	/* Remember that stop button was pushed */
	stop_state = 2;

	/* Restore the LLNL XDIR button after clearing pending X events */
	remove_stop_button_callbacks(stop_dirwin);
	while (XPending(display))
		XtAppProcessEvent(app, (XtInputMask)XtIMAll);
	add_llnlxdir_button_callbacks(stop_dirwin);
	draw_ur_button(stop_dirwin, llnlxdir_pixmap);
	XtSetSensitive(stop_dirwin->w_urButton, ur_button_sensitivity);
}


/*
 * stop - Returns True if the stop button has been pushed pushed, else
 *        False.  Also, this function takes care of updating the animated
 *        stop button; hence this function should be called frequently
 *        (i.e., about every one-tenth second) when the stop button is
 *        being displayed so that the animation will be smooth.
 */
stop()
{
	struct timeval current_time;
	struct timeval delta;

	/* Sanity check */
	if (!stop_dirwin)
		fatal_error("Bug in stop()");

	switch (stop_state) {
	case 0:   /* Should stop button should be updated */
		gettimeofday(&current_time, NULL);
		if (cmp_timevals(&current_time, &time_of_next_update) > 0) {
			nstop_frame = (nstop_frame+1)%nstop_frames;
			draw_ur_button(stop_dirwin, stop_pixmap[nstop_frame]);
			gettimeofday(&current_time, NULL);
			delta.tv_sec = 0;
			delta.tv_usec = SDELTA;
			add_timevals(&current_time, &delta, &time_of_next_update);
		}
	case 1:   /* Process X events in case stop button has been pushed */
		while (XEventsQueued(display, QueuedAfterReading)) {
			XtAppProcessEvent(app, (XtInputMask)XtIMAll);
			if (stop_state == 2) {
				stop_state = 3;  /* Remember that True was returned */
				return True;
			}
		}
		return False;
	case 2:   /* Stop button has been pushed */
		stop_state = 3;  /* Remember that True was returned */
		return True;
	case 3:   /* Can return True only once after stop button pressed */
		return False;
	}

	/* Should not get here, but make compiler happy */
	return False;
}


/*
 * stop_button_is_showing - Returns True is stop button is showing on
 *                          specified directory window, else False;
 */
stop_button_is_showing(dirwin)
struct dirwin_st *dirwin;
{
	return (dirwin == stop_dirwin);
}


/*
 * hide_stop_button - Replace the stop button with the "LLNL XDIR" button.
 *                    Call show_stop_button() to restore the "LLNL XDIR"
 *                    button.
 */
void
hide_stop_button()
{
	/*  Is the stop button showing? */
	if (!stop_dirwin)
		return;

	/* Restore the LLNL XDIR button after clearing pending X events */
	if (stop_state < 2) {
		remove_stop_button_callbacks(stop_dirwin);
		while (XPending(display))
			XtAppProcessEvent(app, (XtInputMask)XtIMAll);
		add_llnlxdir_button_callbacks(stop_dirwin);
		draw_ur_button(stop_dirwin, llnlxdir_pixmap);
		XtSetSensitive(stop_dirwin->w_urButton, ur_button_sensitivity);
	}

	/* Done with stop button */
	stop_dirwin = NULL;
}


/*
 * cb_llnlxdir_expose - Callback to handle LLNL XDIR button expose events.
 */
void
cb_llnlxdir_expose(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct dirwin_st *dirwin = (struct dirwin_st *)client_data;

	/* Redraw the button */
	if (XtIsSensitive(dirwin->w_urButton))
		draw_ur_button(dirwin, llnlxdir_pixmap);
	else
		draw_ur_button(dirwin, grey_llnlxdir_pixmap);
}


/*
 * cb_stop_expose - Callback to handle stop button expose events.
 */
void
cb_stop_expose(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct dirwin_st *dirwin = (struct dirwin_st *)client_data;

	/* Sanity check */
	if (dirwin != stop_dirwin)
		fatal_error("Bug in cb_stop_expose()");

	/* Redraw the button */
	draw_ur_button(dirwin, stop_pixmap[nstop_frame]);
}


/*
 * cb_llnlxdir_arm - Callback to handle LLNL XDIR button arm events.
 */
void
cb_llnlxdir_arm(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct dirwin_st *dirwin = (struct dirwin_st *)client_data;

	draw_ur_button(dirwin, armed_llnlxdir_pixmap);
}


/*
 * cb_stop_arm - Callback to handle stop button arm events.
 */
void
cb_stop_arm(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct dirwin_st *dirwin = (struct dirwin_st *)client_data;

	draw_ur_button(dirwin, armed_stop_pixmap[nstop_frame]);
	stop_state = 1;   /* Stop button is armed */
}


/*
 * cb_stop_disarm - Callback to handle stop button disarm events.
 */
void
cb_stop_disarm(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	stop_state = 0;   /* Stop button no longer armed */
}


/*
 * create_ur_button_pixmaps - Creates a normal, a greyed-out, and an armed
 *                            pixmap from bitmap data for the "upper-right"
 *                            drawn button and returns them.  "bits", "width",
 *                            and "height" describe the bitmap.
 */
void
create_ur_button_pixmaps(bits, width, height, normal_pixmap, normal_grey_pixmap,
                         armed_pixmap)
char *bits;
int width;
int height;
Pixmap *normal_pixmap;
Pixmap *normal_grey_pixmap;
Pixmap *armed_pixmap;
{
	Pixmap temp_pixmap;
	GC gc;
	int bytes_in_row;
	char *grey_bits;
	unsigned char mask;
	int i;
	int j;
	int indx;
	int offset = ur_button_ht+ur_button_st;
	unsigned int w = ur_button_width-2*offset;
	unsigned int h = ur_button_height-2*offset;

	/* Need a graphics context */
	gc = XCreateGC(display, root_window, (unsigned long)0, NULL);

	/* Create normal pixmap */
	if (normal_pixmap) {
		temp_pixmap = XCreatePixmapFromBitmapData(display, root_window, bits,
			width, height, ur_button_fg, ur_button_bg, depth);
		*normal_pixmap =  XCreatePixmap(display, root_window, w, h, depth);
		XSetForeground(display, gc, ur_button_bg);
		XFillRectangle(display, *normal_pixmap, gc, 0, 0, w+1, h+1);
		XCopyArea(display, temp_pixmap, *normal_pixmap, gc, 0, 0,
			(unsigned int)width, (unsigned int)height,
			(int)((w-width)/2), (int)((h-height)/2));
		XFreePixmap(display, temp_pixmap);
	}

	/* Create an armed pixmap */
	if (armed_pixmap) {
		temp_pixmap = XCreatePixmapFromBitmapData(display, root_window, bits,
			width, height, ur_button_fg, ur_button_armed_color, depth);
		*armed_pixmap =  XCreatePixmap(display, root_window, w, h, depth);
		XSetForeground(display, gc, ur_button_armed_color);
		XFillRectangle(display, *armed_pixmap, gc, 0, 0, w+1, h+1);
		XCopyArea(display, temp_pixmap, *armed_pixmap, gc, 0, 0,
			(unsigned int)width, (unsigned int)height,
			(int)((w-width)/2), (int)((h-height)/2));
		XFreePixmap(display, temp_pixmap);
	}

	/* Create greyed out pixmap */
	if (normal_grey_pixmap) {
		bytes_in_row = (width+7)/8;
		grey_bits = XtMalloc(bytes_in_row*height);
		indx = 0;
		for (j=0; j<height; j++) {
			mask = (j%2)?0xaa:0x55;
			for (i=0; i<bytes_in_row; i++) {
				grey_bits[indx] = bits[indx]&mask;
				indx++;
			}
		}
		temp_pixmap = XCreatePixmapFromBitmapData(display, root_window,
			grey_bits, width, height, ur_button_fg, ur_button_bg, depth);
		XtFree(grey_bits);
		*normal_grey_pixmap =  XCreatePixmap(display, root_window, w, h, depth);
		XSetForeground(display, gc, ur_button_bg);
		XFillRectangle(display, *normal_grey_pixmap, gc, 0, 0, w+1, h+1);
		XCopyArea(display, temp_pixmap, *normal_grey_pixmap, gc, 0, 0,
			(unsigned int)width, (unsigned int)height,
			(int)((w-width)/2), (int)((h-height)/2));
		XFreePixmap(display, temp_pixmap);
	}

	/* Free the graphics context */
	XFreeGC(display, gc);
}


/*
 * draw_ur_button - Copies "pixmap" onto the specified directory window's
 *                  upper-right button.  "pixmap" should have been created
 *                  using create_ur_button_pixmaps().
 */
void
draw_ur_button(dirwin, pixmap)
struct dirwin_st *dirwin;
Pixmap pixmap;
{
	int offset = ur_button_ht+ur_button_st;
	unsigned int w = ur_button_width-2*offset;
	unsigned int h = ur_button_height-2*offset;

	XCopyArea(display, pixmap, XtWindow(dirwin->w_urButton),
		XDefaultGCOfScreen(XtScreen(dirwin->w_urButton)), 0, 0, w, h,
		offset, offset);
	XSync(display, 0);
}


/*
 * show_abort_dialog - Pop up a dialog that displays an abort-in-progress
 *                     message over directory window that contains the
 *                     stop button.
 */
void
show_abort_dialog()
{
	/* Sanity check */
	if (!stop_dirwin)
		fatal_error("Bug in show_abort_dialog()");

	w_abort_dialog = show_in_progress_dialog(stop_dirwin, "Abort in Progress",
		"ABORT OPERATION IN PROGRESS\n\n------------\n\nPLEASE BE PATIENT");
}


/*
 * hide_abort_dialog - Pop down dialog that displays an abort-in-progress
 *                     message.
 */
void
hide_abort_dialog()
{
	hide_in_progress_dialog(w_abort_dialog);
}



syntax highlighted by Code2HTML, v. 0.9.1