# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by  <nick@rosebud> on Fri Apr 20 16:24:32 1990
#
# This archive contains:
#	xchrono	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo mkdir - xchrono
mkdir xchrono

echo x - xchrono/README
cat >xchrono/README <<'@EOF'
File:  xchrono/README

Xchrono is a multi-timezone, multi-face clock program for X Windows.
It has been tested on a Sun 3 and an HP9000s300.

STARTUP
-------

Several cities have been compiled into xchrono, and can be invoked with
command-line arguments, xchrono -help gives:

	Usage: xchrono [-analog] [-bw <pixels>] [-digital]
	       [-fg <color>] [-bg <color>] [-hd <color>]
	       [-hl <color>] [-bd <color>]
	       [-fn <font_name>] [-help] [-padding <pixels>]
	       [-rv] [-update <seconds>] [-display displayname]
	       [-geometry geom]
	       [-width clockWidth] [-height clockHeight] [-local localName]
	       [-boston] [-newyork] [-chicago] [-denver] [-la]
	       [-hawaii] [-tokyo] [-sydney] [-london]
	       [-paris] [-frankfurt] [-rio]

OK, OK, Hawaii isn't a city, but you get the point. The timezones used
are taken from tztab in the SYSV case, and from /usr/lib/zoneinfo
otherwise, and as such may or may not be correct (the TZ variable
definitions or the city->timezone mappings).

The -local <localName> option causes a clock labeled with <localName>
using the value of TZ at startup as it's timezone.

In addition, a GMT clock always appears.


X DEFAULTS
----------

This set of X defaults looks fun on my Sun 3/60:

xchrono.width:		802
xchrono.height:		270
xchrono.x:		20
xchrono.y:		40
xchrono.clockWidth:	125
xchrono.clockHeight:	125
xchrono*font:		ncenr12
xchrono*labelFont:	ncenb14
xchrono*foreground:	black
xchrono*background:	white
xchrono.clockBox.background:	blue
xchrono.clockBox.forceBars:	on
xchrono.clockBox.useLeft:	on
xchrono.clockBox.useRight:	on
xchrono*hands:		green
xchrono*highlight:	black
xchrono*borderColor:	red
xchrono*borderWidth:	2
xchrono.boston:		on
xchrono.newyork:	on
xchrono.chicago:	on
xchrono.denver:		on
xchrono.la:		on
xchrono.hawaii:		on
xchrono.tokyo:		on
xchrono.sydney:		on
xchrono.london:		on
xchrono.paris:		on
xchrono.frankfurt:	on

Note that xchrono is an instance of an XClock application, so that
class defaults (e.g. XClock*foreground) will apply to xchrono.


COMPILATION
-----------

% set topDir = <where you installed X>  ## default is /
% $topDir/usr/bin/X11/imake -I$topDir/usr/lib/X11/config
% make



IMPLEMENTAION
-------------

Xchrono is yet another blatent hack of xclock. I modified the athena
Clock widget to allow for one timer callback to update all instances
of Clock widgets in a process. The class part of the clock widget
maintains a list of all instances of Clock.

The main window of xchrono is just a Box widget with an instance of
Clock for every desired timezone.

This is a hack. Hopefully it will inspire someone with more time and/or
motivation to do it right (and include nice things like a dialog box
to do dynamic configuration, etc...).

I think Ansi has something to say about implementing timezones, but
for now I just hacked in support for tztab (SYSV) and zoneinfo (BSD).
Good enough for now.


WARRANTY
--------

Yeah, right. :-)


Enjoy,

Nick Bender		nick%bischeops@uunet.uu.net

@EOF

chmod 644 xchrono/README

echo x - xchrono/xchrono.c
cat >xchrono/xchrono.c <<'@EOF'
#ifndef lint
static char rcsid[] = "$XConsortium: xclock.c,v 1.28 89/12/11 15:51:32 kit Exp $";
#endif /* lint */

/*
 * xclock --  Hacked from Tony Della Fera's much hacked clock program.
 *
 * xchrono -- Yet another hack by Nick Bender.
 */

/*
 * Copyright 1989 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Box.h>

#include "MultiClock.h"
#include <X11/Xaw/Cardinals.h>
#include "clock.bit"
#include "clmask.bit"

extern void exit();
static void quit();


static struct {
  char *timezone;
  char *name;
} cityInfo[] =
{
#ifdef SYSV
#define BOSTON 0
  { "EST5EDT", "Boston" },
#define NEWYORK (BOSTON+1)
  { "EST5EDT", "New York" },
#define CHICAGO (NEWYORK+1)
  { "CST6CDT", "Chicago" },
#define DENVER (CHICAGO+1)
  { "MST7MDT", "Denver" },
#define LA (DENVER+1)
  { "PST8PDT", "LA" },
#define HAWAII (LA+1)
  { "HST10", "Hawaii" },
#define TOKYO (HAWAII+1)
  { "JST-9", "Tokyo" },
#define SYDNEY (TOKYO+1)
  { "EST-10EDT", "Sydney" },
#define LONDON (SYDNEY+1)
  { "WET0WETDST", "London" },
#define PARIS (LONDON+1)
  { "MET-1METDST", "Paris" },
#define FRANKFURT (PARIS+1)
  { "MEZ-1MESZ", "Frankfurt" },
#define RIO (FRANKFURT+1)
  { "BST3", "Rio de Janeiro" },
#else /* Not SYSV */
#define BOSTON 0
  { "EST5EDT", "Boston" },
#define NEWYORK (BOSTON+1)
  { "EST5EDT", "New York" },
#define CHICAGO (NEWYORK+1)
  { "CST6CDT", "Chicago" },
#define DENVER (CHICAGO+1)
  { "MST7MDT", "Denver" },
#define LA (DENVER+1)
  { "PST8PDT", "LA" },
#define HAWAII (LA+1)
  { "HST", "Hawaii" },
#define TOKYO (HAWAII+1)
  { "Japan", "Tokyo" },
#define SYDNEY (TOKYO+1)
/* This should be NSW (New South Wales)? istead of Queensland,
   but that value looks wrong... 				*/
  { "Australia/Queensland", "Sydney" },
#define LONDON (SYDNEY+1)
  { "GB-Eire", "London" },
#define PARIS (LONDON+1)
  { "MET", "Paris" },
#define FRANKFURT (PARIS+1)
  { "MET", "Frankfurt" },
#define RIO (FRANKFURT+1)
  { "Brazil/East", "Rio de Janeiro" },
#endif /* SYSV */
};

static struct _resource {
  int updateInterval;
  int clockWidth;
  int clockHeight;
  String localName;
  Boolean boston;
  Boolean newyork;
  Boolean chicago;
  Boolean denver;
  Boolean la;
  Boolean hawaii;
  Boolean tokyo;
  Boolean sydney;
  Boolean london;
  Boolean paris;
  Boolean frankfurt;
  Boolean rio;
} resource;

#define offset(field)	XtOffset(struct _resource *, field)

static XtResource application_resources[] = {
  {"updateInterval", "UpdateInterval", XtRInt, sizeof (int),
     offset(updateInterval), XtRString, "60" },
  {"clockWidth", "ClockWidth", XtRInt, sizeof (int),
     offset(clockWidth), XtRString, "150" },
  {"clockHeight", "ClockHeight", XtRInt, sizeof (int),
     offset(clockHeight), XtRString, "200" },
  {"localName", "LocalName", XtRString, sizeof (String),
     offset(localName), XtRString, "" },
  {"boston", "Boston", XtRBoolean, sizeof(Boolean),
     offset(boston), XtRString, "false" },
  {"newyork", "Newyork", XtRBoolean, sizeof(Boolean),
     offset(newyork), XtRString, "false" },
  {"chicago", "Chicago", XtRBoolean, sizeof(Boolean),
     offset(chicago), XtRString, "false" },
  {"denver", "Denver", XtRBoolean, sizeof(Boolean),
     offset(denver), XtRString, "false" },
  {"la", "La", XtRBoolean, sizeof(Boolean),
     offset(la), XtRString, "false" },
  {"hawaii", "Hawaii", XtRBoolean, sizeof(Boolean),
     offset(hawaii), XtRString, "false" },
  {"tokyo", "Tokyo", XtRBoolean, sizeof(Boolean),
     offset(tokyo), XtRString, "false" },
  {"sydney", "Sydney", XtRBoolean, sizeof(Boolean),
     offset(sydney), XtRString, "false" },
  {"london", "London", XtRBoolean, sizeof(Boolean),
     offset(london), XtRString, "false" },
  {"paris", "Paris", XtRBoolean, sizeof(Boolean),
     offset(paris), XtRString, "false" },
  {"frankfurt", "Frankfurt", XtRBoolean, sizeof(Boolean),
     offset(frankfurt), XtRString, "false" },
  {"rio", "Rio", XtRBoolean, sizeof(Boolean),
     offset(rio), XtRString, "false" },
};

/* Command line options table.  Only resources are entered here...there is a
   pass over the remaining options after XtParseCommand is let loose. */

static XrmOptionDescRec options[] = {
{"-chime",	"*clock.chime",		XrmoptionNoArg,		"TRUE"},
{"-hd",		"*clock.hands",		XrmoptionSepArg,	NULL},
{"-hands",	"*clock.hands",		XrmoptionSepArg,	NULL},
{"-hl",		"*clock.highlight",	XrmoptionSepArg,	NULL},
{"-highlight",	"*clock.highlight",	XrmoptionSepArg,	NULL},
{"-update",	"*clock.update",	XrmoptionSepArg,	NULL},
{"-padding",	"*clock.padding",	XrmoptionSepArg,	NULL},
{"-d",		"*clock.analog",	XrmoptionNoArg,		"FALSE"},
{"-digital",	"*clock.analog",	XrmoptionNoArg,		"FALSE"},
{"-analog",	"*clock.analog",	XrmoptionNoArg,		"TRUE"},
{"-update",	"*updateInterval",	XrmoptionSepArg,	NULL},
{"-width", 	"*clockWidth",		XrmoptionSepArg,	NULL},
{"-height", 	"*clockHeight",		XrmoptionSepArg,	NULL},
{"-local",	"*localName",		XrmoptionSepArg,	NULL},
{"-boston",	"*boston",		XrmoptionNoArg,		"on"},
{"-newyork",	"*newyork",		XrmoptionNoArg,		"on"},
{"-chicago",	"*chicago",		XrmoptionNoArg,		"on"},
{"-denver",	"*denver",		XrmoptionNoArg,		"on"},
{"-la",		"*la",			XrmoptionNoArg,		"on"},
{"-hawaii",	"*hawaii",		XrmoptionNoArg,		"on"},
{"-tokyo",	"*tokyo",		XrmoptionNoArg,		"on"},
{"-sydney",	"*sydney",		XrmoptionNoArg,		"on"},
{"-london",	"*london",		XrmoptionNoArg,		"on"},
{"-paris",	"*paris",		XrmoptionNoArg,		"on"},
{"-frankfurt",	"*frankfurt",		XrmoptionNoArg,		"on"},
{"-rio",	"*rio",			XrmoptionNoArg,		"on"},
};


static XtActionsRec xclock_actions[] = {
    { "quit",	quit },
};

static Atom wm_delete_window;

/*
 * Report the syntax for calling xclock.
 */
Syntax(call)
	char *call;
{
	(void) printf ("Usage: %s [-analog] [-bw <pixels>] [-digital]\n", call);
	(void) printf ("       [-fg <color>] [-bg <color>] [-hd <color>]\n");
	(void) printf ("       [-hl <color>] [-bd <color>]\n");
	(void) printf ("       [-fn <font_name>] [-help] [-padding <pixels>]\n");
	(void) printf ("       [-rv] [-update <seconds>] [-display displayname]\n");
	(void) printf ("       [-geometry geom]\n");
	(void) printf ("       [-width clockWidth] [-height clockHeight] [-local localName]\n");
	(void) printf ("       [-boston] [-newyork] [-chicago] [-denver] [-la]\n");
	(void) printf ("       [-hawaii] [-tokyo] [-sydney] [-london]\n");
	(void) printf ("       [-paris] [-frankfurt] [-rio]\n\n");
	exit(1);
}

Arg clockArgs[] =
{
#define LABEL_ARG	0
  { XtNlabel, (XtArgVal) 0 },
#define TIMEZONE_ARG	(LABEL_ARG+1)
  { XtNtimezone, (XtArgVal) 0 },
#define WIDTH_ARG	(TIMEZONE_ARG+1)
  { XtNwidth, (XtArgVal) 0 },
#define HEIGHT_ARG	(WIDTH_ARG+1)
  { XtNheight, (XtArgVal) 0 },
};

void main(argc, argv)
    int argc;
    char **argv;
{
    Widget toplevel;
    Widget clockBox;
    Arg arg;
    Pixmap icon_pixmap = None;
    XtAppContext app_con;

    toplevel = XtAppInitialize (&app_con, "XClock", options, XtNumber(options),
				&argc, argv, NULL, NULL, ZERO);
    if (argc != 1) Syntax(argv[0]);

    XtAppAddActions (app_con, xclock_actions, XtNumber(xclock_actions));

    XtGetApplicationResources (toplevel, &resource, application_resources,
			       XtNumber(application_resources), NULL, 0);

    /*
     * This is a hack so that f.delete will do something useful in this
     * single-window application.
     */
    XtOverrideTranslations(toplevel, 
		    XtParseTranslationTable ("<Message>WM_PROTOCOLS: quit()"));

    XtSetArg(arg, XtNiconPixmap, &icon_pixmap);
    XtGetValues(toplevel, &arg, ONE);
    if (icon_pixmap == None) {
	arg.value = (XtArgVal)XCreateBitmapFromData(XtDisplay(toplevel),
				       XtScreen(toplevel)->root,
				       clock_bits, clock_width, clock_height);
	XtSetValues (toplevel, &arg, ONE);
    }
    XtSetArg(arg, XtNiconMask, &icon_pixmap);
    XtGetValues(toplevel, &arg, ONE);
    if (icon_pixmap == None) {
	arg.value = (XtArgVal)XCreateBitmapFromData(XtDisplay(toplevel),
				       XtScreen(toplevel)->root,
				       clock_mask_bits, clock_mask_width, 
				       clock_mask_height);
	XtSetValues (toplevel, &arg, ONE);
    }


    clockBox = XtCreateManagedWidget ("clockBox", boxWidgetClass, toplevel, NULL, ZERO);

    clockArgs[WIDTH_ARG].value = (XtArgVal) resource.clockWidth;
    clockArgs[HEIGHT_ARG].value = (XtArgVal) resource.clockHeight;

    if (strlen(resource.localName))
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) resource.localName;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) getenv("TZ");
	XtCreateManagedWidget ("local", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.hawaii)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[HAWAII].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[HAWAII].timezone;
	XtCreateManagedWidget ("hawaii", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.la)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[LA].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[LA].timezone;
	XtCreateManagedWidget ("la", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.denver)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[DENVER].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[DENVER].timezone;
	XtCreateManagedWidget ("denver", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.chicago)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[CHICAGO].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[CHICAGO].timezone;
	XtCreateManagedWidget ("chicago", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.newyork)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[NEWYORK].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[NEWYORK].timezone;
	XtCreateManagedWidget ("newyork", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.boston)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[BOSTON].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[BOSTON].timezone;
	XtCreateManagedWidget ("boston", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.rio)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[RIO].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[RIO].timezone;
	XtCreateManagedWidget ("rio", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.london)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[LONDON].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[LONDON].timezone;
	XtCreateManagedWidget ("london", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    clockArgs[LABEL_ARG].value = (XtArgVal) "GMT";
    clockArgs[TIMEZONE_ARG].value = (XtArgVal) "GMT";
    XtCreateManagedWidget ("gmt", clockWidgetClass, clockBox,
			   clockArgs, XtNumber(clockArgs));

    if (resource.paris)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[PARIS].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[PARIS].timezone;
	XtCreateManagedWidget ("paris", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.frankfurt)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[FRANKFURT].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[FRANKFURT].timezone;
	XtCreateManagedWidget ("frankfurt", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.tokyo)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[TOKYO].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[TOKYO].timezone;
	XtCreateManagedWidget ("tokyo", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    if (resource.sydney)
      {
	clockArgs[LABEL_ARG].value = (XtArgVal) cityInfo[SYDNEY].name;
	clockArgs[TIMEZONE_ARG].value = (XtArgVal) cityInfo[SYDNEY].timezone;
	XtCreateManagedWidget ("sydney", clockWidgetClass, clockBox,
			       clockArgs, XtNumber(clockArgs));
      }

    SetClockUpdateInterval (resource.updateInterval);
    XtRealizeWidget (toplevel);

    wm_delete_window = XInternAtom (XtDisplay(toplevel), "WM_DELETE_WINDOW",
				    False);
    (void) XSetWMProtocols (XtDisplay(toplevel), XtWindow(toplevel),
			    &wm_delete_window, 1);
    XtAppMainLoop (app_con);
}


static void quit (w, event, params, num_params)
    Widget w;
    XEvent *event;
    String *params;
    Cardinal *num_params;
{
    if (event->type == ClientMessage &&
	event->xclient.data.l[0] != wm_delete_window) {
	XBell (XtDisplay(w), 0);
	return;
    }
    XCloseDisplay (XtDisplay(w));
    exit (0);
}
@EOF

chmod 644 xchrono/xchrono.c

echo x - xchrono/Imakefile
cat >xchrono/Imakefile <<'@EOF'
       INCLUDES = -I$(INCROOT) -I.
        DEPLIBS = 
      LDOPTIONS = -L $(USRLIBDIR)
LOCAL_LIBRARIES = -lXaw -lXmu -lXt -lX11
  SYS_LIBRARIES = -lm
           SRCS = xchrono.c MultiClock.c
           OBJS = xchrono.o MultiClock.o

ComplexProgramTarget(xchrono)
InstallAppDefaults(XChrono)
@EOF

chmod 644 xchrono/Imakefile

echo x - xchrono/clock.bit
cat >xchrono/clock.bit <<'@EOF'
#define clock_width 32
#define clock_height 32
static char clock_bits[] = {
   0x00, 0xf0, 0x07, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x80, 0x8f, 0xf8, 0x00,
   0xc0, 0x81, 0xc0, 0x01, 0x60, 0x80, 0x00, 0x03, 0x70, 0x00, 0x00, 0x06,
   0x98, 0x00, 0x00, 0x0e, 0x0c, 0xc0, 0x00, 0x19, 0x0c, 0xc0, 0x00, 0x18,
   0x06, 0xc0, 0x00, 0x30, 0x06, 0xc0, 0x00, 0x30, 0x06, 0xc0, 0x00, 0x30,
   0x03, 0xc0, 0x00, 0x60, 0x03, 0xc0, 0x00, 0x60, 0x03, 0xc0, 0x7f, 0x60,
   0xdf, 0xff, 0x7f, 0x7c, 0x03, 0xc0, 0x7f, 0x60, 0x03, 0x00, 0x00, 0x60,
   0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x30,
   0x06, 0x00, 0x00, 0x30, 0x8c, 0x00, 0x00, 0x18, 0x4c, 0x00, 0x00, 0x18,
   0x38, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x06, 0x60, 0x80, 0x00, 0x03,
   0xc0, 0x81, 0xc0, 0x01, 0x80, 0x8f, 0xf8, 0x00, 0x00, 0xfe, 0x3f, 0x00,
   0x00, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00};
@EOF

chmod 644 xchrono/clock.bit

echo x - xchrono/MultiClock.c
cat >xchrono/MultiClock.c <<'@EOF'
#ifndef lint
static char Xrcsid[] = "$XConsortium: Clock.c,v 1.50 89/12/06 15:23:24 kit Exp $";
#endif /* lint */


/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

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 and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#include <X11/Xlib.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/Xaw/XawInit.h>
#include "MultiClockP.h"

extern long time();
static void clock_tic(), updateClocks(), DrawHand(), DrawSecond(), SetSeg(), DrawClockFace();
static erase_hands(), round();
	
/* Private Definitions */

#define VERTICES_IN_HANDS	6	/* to draw triangle */
#define PI			3.14159265358979
#define TWOPI			(2. * PI)

#define SECOND_HAND_FRACT	90
#define MINUTE_HAND_FRACT	70
#define HOUR_HAND_FRACT		40
#define HAND_WIDTH_FRACT	7
#define SECOND_WIDTH_FRACT	5
#define SECOND_HAND_TIME	30

#define ANALOG_SIZE_DEFAULT	164

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define abs(a) ((a) < 0 ? -(a) : (a))


/* Initialization of defaults */

#define offset(field) XtOffset(ClockWidget,clock.field)
#define goffset(field) XtOffset(Widget,core.field)

static XtResource resources[] = {
    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
	goffset(width), XtRImmediate, (caddr_t) 0},
    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
	goffset(height), XtRImmediate, (caddr_t) 0},
    {XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
	goffset(background_pixel), XtRString, "XtdefaultBackground"},
    {XtNupdate, XtCInterval, XtRInt, sizeof(int), 
        offset(update), XtRImmediate, (caddr_t) 60 },
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(fgpixel), XtRString, "XtdefaultForeground"},
    {XtNhand, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(Hdpixel), XtRString, "XtdefaultForeground"},
    {XtNhighlight, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(Hipixel), XtRString, "XtdefaultForeground"},
    {XtNanalog, XtCBoolean, XtRBoolean, sizeof(Boolean),
        offset(analog), XtRImmediate, (caddr_t) TRUE},
    {XtNchime, XtCBoolean, XtRBoolean, sizeof(Boolean),
	offset(chime), XtRImmediate, (caddr_t) FALSE },
    {XtNpadding, XtCMargin, XtRInt, sizeof(int),
        offset(padding), XtRImmediate, (caddr_t) 8},
    {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
        offset(font), XtRString, "fixed"},
    {XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
	offset (reverse_video), XtRImmediate, (caddr_t) FALSE},
    {XtNbackingStore, XtCBackingStore, XtRBackingStore, sizeof (int),
    	offset (backing_store), XtRString, "default"},
    {XtNtimezone, XtCString, XtRString, sizeof (String),
        offset (timezone), XtRString, "EST5EDT"},
    {XtNlabel, XtCString, XtRString, sizeof (String),
        offset (label), XtRString, "Local time"},
    {XtNlabelFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
        offset(labelFont), XtRString, "fixed"},
};

#undef offset
#undef goffset

static void ClassInitialize(), ClassPartInitialize();
static void Initialize(), Realize(), Destroy(), Resize(), Redisplay();
static Boolean SetValues();

ClockClassRec clockClassRec = {
    { /* core fields */
    /* superclass		*/	&widgetClassRec,
    /* class_name		*/	"Clock",
    /* widget_size		*/	sizeof(ClockRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	ClassPartInitialize,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* resource_count		*/	XtNumber(resources),
    /* xrm_class		*/	NULL,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Destroy,
    /* resize			*/	Resize,
    /* expose			*/	Redisplay,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	NULL,
    /* query_geometry           */	XtInheritQueryGeometry,
    /* display_accelerator      */	XtInheritDisplayAccelerator,
    /* extension                */	NULL
    }
};

WidgetClass clockWidgetClass = (WidgetClass) &clockClassRec;

/****************************************************************
 *
 * Private Procedures
 *
 ****************************************************************/

static void ClassInitialize()
{
    XawInitializeWidgetSet();
    XtAddConverter( XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
		    NULL, 0 );
}

#define ClassField(field) clockClassRec.clock_class.field
#define CLOCK_LIST_SIZE	4

static void ClassPartInitialize()
{
  if (! ClassField(updateInterval))
    ClassField(updateInterval) = 60;
  ClassField(intervalId) = (XtIntervalId) 0;
  ClassField(clockList) =
    (ClockWidget *) XtMalloc(sizeof(ClockWidget)*CLOCK_LIST_SIZE);
  ClassField(numberOfClocks) = 0;
  ClassField(numberOfSlots) = CLOCK_LIST_SIZE;
}

static void addClockToList (cw)
     ClockWidget cw;
{
  if (ClassField(numberOfClocks) == ClassField(numberOfSlots))
    {
      ClassField(numberOfSlots) += CLOCK_LIST_SIZE;
      ClassField(clockList) =
	(ClockWidget *) XtRealloc(ClassField(clockList),
				  sizeof(ClockWidget)*ClassField(numberOfSlots));
    }

  ClassField(clockList)[ClassField(numberOfClocks)] = cw;

  if (! ClassField(numberOfClocks))
    ClassField(intervalId) = 
      XtAddTimeOut(ClassField(updateInterval)*1000, updateClocks, NULL);

  ClassField(numberOfClocks)++;
}

static void removeClockFromList (cw)
     ClockWidget cw;
{
  ClockWidget *list = ClassField(clockList);
  int count;

  for (count = 1; *list != cw; list++, count++);
  while (count < ClassField(numberOfClocks))
    { *list = *(list+1); list++; }
  ClassField(numberOfClocks)--;

  if (! ClassField(numberOfClocks) && ClassField(intervalId))
    {
      XtRemoveTimeOut (ClassField(intervalId));
      ClassField(intervalId) = 0;
    }
}

/* A public procedure for changing the update interval */
void SetClockUpdateInterval (interval)
{
  if (interval && interval > 0)
    {
      if (ClassField(intervalId)) XtRemoveTimeOut(ClassField(intervalId));
      ClassField(intervalId) = (XtIntervalId) 0;
      ClassField(updateInterval) = interval;
      updateClocks ((Widget) NULL, 0);
    }
}

/* ARGSUSED */
static void Initialize (request, new)
    Widget request, new;
{
    ClockWidget w = (ClockWidget)new;
    XtGCMask		valuemask;
    XGCValues	myXGCV;
    int min_height, min_width;

    valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
    if (w->clock.font != NULL)
        myXGCV.font = w->clock.font->fid;
    else
        valuemask &= ~GCFont;	/* use server default font */

    min_width = min_height = ANALOG_SIZE_DEFAULT;
    if(!w->clock.analog) {
       char *str;
       struct tm tm, *localtime();
       long time_value;
       (void) time(&time_value);
       tm = *localtime(&time_value);
       str = asctime(&tm);
       if (w->clock.font == NULL)
          w->clock.font = XQueryFont( XtDisplay(w),
				      XGContextFromGC(
					   DefaultGCOfScreen(XtScreen(w))) );
       min_width = XTextWidth(w->clock.font, str, strlen(str)) +
	  2 * w->clock.padding;
       min_height = w->clock.font->ascent +
	  w->clock.font->descent + 2 * w->clock.padding;
    }
    if (w->core.width == 0)
	w->core.width = min_width;
    if (w->core.height == 0)
	w->core.height = min_height;

    myXGCV.foreground = w->clock.fgpixel;
    myXGCV.background = w->core.background_pixel;
    if (w->clock.font != NULL)
        myXGCV.font = w->clock.font->fid;
    else
        valuemask &= ~GCFont;	/* use server default font */
    myXGCV.line_width = 0;
    w->clock.myGC = XtGetGC((Widget)w, valuemask, &myXGCV);

    if (w->clock.labelFont != NULL)
      {
	XtGCMask saveMask = valuemask;
	Font saveFont = myXGCV.font;

	myXGCV.font = w->clock.labelFont->fid;
	valuemask |= GCFont;
	w->clock.labelGC = XtGetGC((Widget)w, valuemask, &myXGCV);

	valuemask = saveMask;
	myXGCV.font = saveFont;
      }
    else w->clock.labelGC = w->clock.myGC;

    valuemask = GCForeground | GCLineWidth ;
    myXGCV.foreground = w->core.background_pixel;
    w->clock.EraseGC = XtGetGC((Widget)w, valuemask, &myXGCV);

    myXGCV.foreground = w->clock.Hipixel;
    w->clock.HighGC = XtGetGC((Widget)w, valuemask, &myXGCV);

    valuemask = GCForeground;
    myXGCV.foreground = w->clock.Hdpixel;
    w->clock.HandGC = XtGetGC((Widget)w, valuemask, &myXGCV);

    if (w->clock.update <= 0)
	w->clock.update = 60;	/* make invalid update's use a default */
    w->clock.show_second_hand = (w->clock.update <= SECOND_HAND_TIME);
    w->clock.numseg = 0;

    if (w->clock.timezone)
      w->clock.timezone =
	strcpy (XtMalloc (strlen (w->clock.timezone) + 1), w->clock.timezone);

    w->clock.label =
      strcpy (XtMalloc (strlen (w->clock.label) + 1), w->clock.label);

    addClockToList (w);
}

static void Realize (gw, valueMask, attrs)
     Widget gw;
     XtValueMask *valueMask;
     XSetWindowAttributes *attrs;
{
     ClockWidget	w = (ClockWidget) gw;
#ifdef notdef
     *valueMask |= CWBitGravity;
     attrs->bit_gravity = ForgetGravity;
#endif
     switch (w->clock.backing_store) {
     case Always:
     case NotUseful:
     case WhenMapped:
     	*valueMask |=CWBackingStore;
	attrs->backing_store = w->clock.backing_store;
	break;
     }
     XtCreateWindow( gw, InputOutput, (Visual *)CopyFromParent,
		     *valueMask, attrs);
     Resize(gw);
}

static void Destroy (gw)
     Widget gw;
{
     ClockWidget w = (ClockWidget) gw;
     XtDestroyGC (w->clock.myGC);
     XtDestroyGC (w->clock.HighGC);
     XtDestroyGC (w->clock.HandGC);
     XtDestroyGC (w->clock.EraseGC);

     removeClockFromList (w);
}

static void Resize (gw) 
    Widget gw;
{
    ClockWidget w = (ClockWidget) gw;
    int usableHeight = (w->core.height + w->clock.padding) -
      (w->clock.font->ascent + w->clock.font->descent + 2) ;

    /* don't do this computation if window hasn't been realized yet. */
    if (XtIsRealized(gw) && w->clock.analog) {
	/* need signed value since Dimension is unsigned */
	int radius = ((int) min(w->core.width, usableHeight) - (int) (2 * w->clock.padding)) / 2;
        w->clock.radius = (Dimension) max (radius, 1);

        w->clock.second_hand_length = ((SECOND_HAND_FRACT * w->clock.radius) / 100);
        w->clock.minute_hand_length = ((MINUTE_HAND_FRACT * w->clock.radius) / 100);
        w->clock.hour_hand_length = ((HOUR_HAND_FRACT * w->clock.radius) / 100);
        w->clock.hand_width = ((HAND_WIDTH_FRACT * w->clock.radius) / 100);
        w->clock.second_hand_width = ((SECOND_WIDTH_FRACT * w->clock.radius) / 100);

        w->clock.centerX = w->core.width / 2;
        w->clock.centerY = (usableHeight / 2) + (w->core.height - usableHeight);
    }
}

/* ARGSUSED */
static void Redisplay (gw, event, region)
    Widget gw;
    XEvent *event;		/* unused */
    Region region;		/* unused */
{
    ClockWidget w = (ClockWidget) gw;
    if (w->clock.analog) {
	if (w->clock.numseg != 0)
	    erase_hands (w, (struct tm *) 0);
        DrawClockFace(w);
    } else {
	w->clock.prev_time_string[0] = '\0';
    }
    updateClocks (w, 0);
}

static char tzEnviron[TZ_BUFLEN+3];

extern char *getenv();

static void updateClocks (client_data, id)
        caddr_t client_data;
        XtIntervalId *id;
{
  ClockWidget w = (ClockWidget) client_data;
  long	time_value;
  char localTimezone[TZ_BUFLEN];
  char *tzenv = getenv("TZ");
  struct tm *localtime();
  struct tm tm; 
  int count = ClassField(numberOfClocks);
  ClockWidget *list = ClassField(clockList);


  if ((id || !ClassField(intervalId)) && count)
    ClassField(intervalId) =
      XtAppAddTimeOut( XtWidgetToApplicationContext( (Widget) *list),
		      ClassField(updateInterval)*1000, updateClocks, NULL );
  (void) time(&time_value);
  if (tzenv) strcpy (localTimezone, tzenv);

  if (w)
    {
      count = 1;
      list = &w;
    }

  while (count--)
    {
      (*list)->clock.show_second_hand =
	(ClassField(updateInterval) <= SECOND_HAND_TIME);
      if (XtIsRealized(*list))
	{
	  if ((*list)->clock.timezone)
	  {
	    sprintf (tzEnviron, "TZ=%s", (*list)->clock.timezone);
	    putenv (tzEnviron);
	    tzset ();
	  }
	  tm = *localtime(&time_value);
	  clock_tic (*list, tm);
	  list++;
	}
    }

  if (tzenv)
    {
      sprintf (tzEnviron, "TZ=%s", localTimezone);
      putenv (tzEnviron);
    }
#ifndef SYSV
  else tzsetwall();
#endif /* not SYSV */
}


/* ARGSUSED */
static void clock_tic(w, tm)
	ClockWidget w;
	struct tm tm; 
{
	char	time_string[28];
	char	*time_ptr = time_string;
        register Display *dpy = XtDisplay(w);
        register Window win = XtWindow(w);
	char    *am_pm;

	/*
	 * Beep on the half hour; double-beep on the hour.
	 */
	if (w->clock.chime == TRUE) {
	    if (w->clock.beeped && (tm.tm_min != 30) &&
		(tm.tm_min != 0))
	      w->clock.beeped = FALSE;
	    if (((tm.tm_min == 30) || (tm.tm_min == 0)) 
		&& (!w->clock.beeped)) {
		w->clock.beeped = TRUE;
		XBell(dpy, 50);	
		if (tm.tm_min == 0)
		  XBell(dpy, 50);
	    }
	}

	{
	  int len = strlen (w->clock.label);
	  int x = (w->core.width -
		   (XTextWidth (w->clock.labelFont, w->clock.label, len))) / 2;
	  int y = 2 + w->clock.font->ascent;

	  if (x < 0) x = 0;
	  XDrawImageString (dpy, win, w->clock.labelGC, x, y,
			    w->clock.label, len);
	}

	if( w->clock.analog == FALSE ) {
	    int	clear_from;
	    int i, len, prev_len;

	    time_ptr = asctime(&tm);
	    len = strlen (time_ptr);
	    if (time_ptr[len - 1] == '\n') time_ptr[--len] = '\0';
	    prev_len = strlen (w->clock.prev_time_string);
	    for (i = 0; ((i < len) && (i < prev_len) && 
	    		 (w->clock.prev_time_string[i] == time_ptr[i])); i++);
	    strcpy (w->clock.prev_time_string+i, time_ptr+i);

	    XDrawImageString (dpy, win, w->clock.myGC,
			      (2+w->clock.padding +
			       XTextWidth (w->clock.font, time_ptr, i)),
			      2+w->clock.font->ascent+w->clock.padding,
			      time_ptr + i, len - i);
	    /*
	     * Clear any left over bits
	     */
	    clear_from = XTextWidth (w->clock.font, time_ptr, len)
 	    		+ 2 + w->clock.padding;
	    if (clear_from < w->core.width)
		XFillRectangle (dpy, win, w->clock.EraseGC,
		    clear_from, 0, w->core.width - clear_from, w->core.height);
	} else {
			/*
			 * The second (or minute) hand is sec (or min) 
			 * sixtieths around the clock face. The hour hand is
			 * (hour + min/60) twelfths of the way around the
			 * clock-face.  The derivation is left as an excercise
			 * for the reader.
			 */

			/*
			 * 12 hour clock.
			 */
			if(tm.tm_hour >= 12)
			  {
			    if (tm.tm_hour > 12)
                              tm.tm_hour -= 12;
			    am_pm = "PM";
			  }
			else am_pm = "AM";

			sprintf (time_string, "%d/%d", tm.tm_mon+1, tm.tm_mday);
			{
			  int len = strlen (time_string);
			  int x = w->core.width -
			    (2 + XTextWidth (w->clock.font, time_string, len));
			  int y = w->core.height - (w->clock.font->descent + 2);

			  XDrawImageString (dpy, win, w->clock.myGC,
					    2, y, am_pm, strlen(am_pm));
			  XDrawImageString (dpy, win, w->clock.myGC,
					    x, y, time_string, len);
			}

			erase_hands (w, &tm);

		    if (w->clock.numseg == 0 ||
			tm.tm_min != w->clock.otm.tm_min ||
			tm.tm_hour != w->clock.otm.tm_hour) {
			    w->clock.segbuffptr = w->clock.segbuff;
			    w->clock.numseg = 0;
			    /*
			     * Calculate the hour hand, fill it in with its
			     * color and then outline it.  Next, do the same
			     * with the minute hand.  This is a cheap hidden
			     * line algorithm.
			     */
			    DrawHand(w,
				w->clock.minute_hand_length, w->clock.hand_width,
				((double) tm.tm_min)/60.0
			    );
			    if(w->clock.Hdpixel != w->core.background_pixel)
				XFillPolygon( dpy,
				    win, w->clock.HandGC,
				    w->clock.segbuff, VERTICES_IN_HANDS,
				    Convex, CoordModeOrigin
				);
			    XDrawLines( dpy,
				win, w->clock.HighGC,
				w->clock.segbuff, VERTICES_IN_HANDS,
				       CoordModeOrigin);
			    w->clock.hour = w->clock.segbuffptr;
			    DrawHand(w, 
				w->clock.hour_hand_length, w->clock.hand_width,
				((((double)tm.tm_hour) + 
				    (((double)tm.tm_min)/60.0)) / 12.0)
			    );
			    if(w->clock.Hdpixel != w->core.background_pixel) {
			      XFillPolygon(dpy,
					   win, w->clock.HandGC,
					   w->clock.hour,
					   VERTICES_IN_HANDS,
					   Convex, CoordModeOrigin
					   );
			    }
			    XDrawLines( dpy,
				       win, w->clock.HighGC,
				       w->clock.hour, VERTICES_IN_HANDS,
				       CoordModeOrigin );

			    w->clock.sec = w->clock.segbuffptr;
		    }
		    if (w->clock.show_second_hand == TRUE) {
			    w->clock.segbuffptr = w->clock.sec;
			    DrawSecond(w,
				w->clock.second_hand_length - 2, 
				w->clock.second_hand_width,
				w->clock.minute_hand_length + 2,
				((double) tm.tm_sec)/60.0
			    );
			    if(w->clock.Hdpixel != w->core.background_pixel)
				XFillPolygon( dpy,
				    win, w->clock.HandGC,
				    w->clock.sec,
				    VERTICES_IN_HANDS -2,
				    Convex, CoordModeOrigin
			    );
			    XDrawLines( dpy,
				       win, w->clock.HighGC,
				       w->clock.sec,
				       VERTICES_IN_HANDS-1,
				       CoordModeOrigin
				        );

			}
			w->clock.otm = tm;
		}
}
	
static erase_hands (w, tm)
ClockWidget	w;
struct tm	*tm;
{
    /*
     * Erase old hands.
     */
    if(w->clock.numseg > 0) {
	Display	*dpy;
	Window	win;

	dpy = XtDisplay (w);
	win = XtWindow (w);
	if (w->clock.show_second_hand == TRUE) {
	    XDrawLines(dpy, win,
		w->clock.EraseGC,
		w->clock.sec,
		VERTICES_IN_HANDS-1,
		CoordModeOrigin);
	    if(w->clock.Hdpixel != w->core.background_pixel) {
		XFillPolygon(dpy,
			win, w->clock.EraseGC,
			w->clock.sec,
			VERTICES_IN_HANDS-2,
			Convex, CoordModeOrigin
			);
	    }
	}
	if(!tm || tm->tm_min != w->clock.otm.tm_min ||
		  tm->tm_hour != w->clock.otm.tm_hour)
 	{
	    XDrawLines( dpy, win,
			w->clock.EraseGC,
			w->clock.segbuff,
			VERTICES_IN_HANDS,
			CoordModeOrigin);
	    XDrawLines( dpy, win,
			w->clock.EraseGC,
			w->clock.hour,
			VERTICES_IN_HANDS,
			CoordModeOrigin);
	    if(w->clock.Hdpixel != w->core.background_pixel) {
		XFillPolygon( dpy, win,
 			      w->clock.EraseGC,
			      w->clock.segbuff, VERTICES_IN_HANDS,
			      Convex, CoordModeOrigin);
		XFillPolygon( dpy, win,
 			      w->clock.EraseGC,
			      w->clock.hour,
			      VERTICES_IN_HANDS,
			      Convex, CoordModeOrigin);
	    }
	}
    }
}

/*
 * DrawLine - Draws a line.
 *
 * blank_length is the distance from the center which the line begins.
 * length is the maximum length of the hand.
 * Fraction_of_a_circle is a fraction between 0 and 1 (inclusive) indicating
 * how far around the circle (clockwise) from high noon.
 *
 * The blank_length feature is because I wanted to draw tick-marks around the
 * circle (for seconds).  The obvious means of drawing lines from the center
 * to the perimeter, then erasing all but the outside most pixels doesn't
 * work because of round-off error (sigh).
 */
static void DrawLine(w, blank_length, length, fraction_of_a_circle)
ClockWidget w;
Dimension blank_length;
Dimension length;
double fraction_of_a_circle;
{
	double dblank_length = (double)blank_length, dlength = (double)length;
	double angle, cosangle, sinangle;
	double cos();
	double sin();
	int cx = w->clock.centerX, cy = w->clock.centerY, x1, y1, x2, y2;

	/*
	 *  A full circle is 2 PI radians.
	 *  Angles are measured from 12 o'clock, clockwise increasing.
	 *  Since in X, +x is to the right and +y is downward:
	 *
	 *	x = x0 + r * sin(theta)
	 *	y = y0 - r * cos(theta)
	 *
	 */
	angle = TWOPI * fraction_of_a_circle;
	cosangle = cos(angle);
	sinangle = sin(angle);

	/* break this out so that stupid compilers can cope */
	x1 = cx + (int)(dblank_length * sinangle);
	y1 = cy - (int)(dblank_length * cosangle);
	x2 = cx + (int)(dlength * sinangle);
	y2 = cy - (int)(dlength * cosangle);
	SetSeg(w, x1, y1, x2, y2);
}

/*
 * DrawHand - Draws a hand.
 *
 * length is the maximum length of the hand.
 * width is the half-width of the hand.
 * Fraction_of_a_circle is a fraction between 0 and 1 (inclusive) indicating
 * how far around the circle (clockwise) from high noon.
 *
 */
static void DrawHand(w, length, width, fraction_of_a_circle)
ClockWidget w;
Dimension length, width;
double fraction_of_a_circle;
{

	register double angle, cosangle, sinangle;
	register double ws, wc;
	Position x, y, x1, y1, x2, y2;
	double cos();
	double sin();

	/*
	 *  A full circle is 2 PI radians.
	 *  Angles are measured from 12 o'clock, clockwise increasing.
	 *  Since in X, +x is to the right and +y is downward:
	 *
	 *	x = x0 + r * sin(theta)
	 *	y = y0 - r * cos(theta)
	 *
	 */
	angle = TWOPI * fraction_of_a_circle;
	cosangle = cos(angle);
	sinangle = sin(angle);
	/*
	 * Order of points when drawing the hand.
	 *
	 *		1,4
	 *		/ \
	 *	       /   \
	 *	      /     \
	 *	    2 ------- 3
	 */
	wc = width * cosangle;
	ws = width * sinangle;
	SetSeg(w,
	       x = w->clock.centerX + round(length * sinangle),
	       y = w->clock.centerY - round(length * cosangle),
	       x1 = w->clock.centerX - round(ws + wc), 
	       y1 = w->clock.centerY + round(wc - ws));  /* 1 ---- 2 */
	/* 2 */
	SetSeg(w, x1, y1, 
	       x2 = w->clock.centerX - round(ws - wc), 
	       y2 = w->clock.centerY + round(wc + ws));  /* 2 ----- 3 */

	SetSeg(w, x2, y2, x, y);	/* 3 ----- 1(4) */
}

/*
 * DrawSecond - Draws the second hand (diamond).
 *
 * length is the maximum length of the hand.
 * width is the half-width of the hand.
 * offset is direct distance from center to tail end.
 * Fraction_of_a_circle is a fraction between 0 and 1 (inclusive) indicating
 * how far around the circle (clockwise) from high noon.
 *
 */
static void DrawSecond(w, length, width, offset, fraction_of_a_circle)
ClockWidget w;
Dimension length, width, offset;
double fraction_of_a_circle;
{

	register double angle, cosangle, sinangle;
	register double ms, mc, ws, wc;
	register int mid;
	Position x, y;
	double cos();
	double sin();

	/*
	 *  A full circle is 2 PI radians.
	 *  Angles are measured from 12 o'clock, clockwise increasing.
	 *  Since in X, +x is to the right and +y is downward:
	 *
	 *	x = x0 + r * sin(theta)
	 *	y = y0 - r * cos(theta)
	 *
	 */
	angle = TWOPI * fraction_of_a_circle;
	cosangle = cos(angle);
	sinangle = sin(angle);
	/*
	 * Order of points when drawing the hand.
	 *
	 *		1,5
	 *		/ \
	 *	       /   \
	 *	      /     \
	 *	    2<       >4
	 *	      \     /
	 *	       \   /
	 *		\ /
	 *	-	 3
	 *	|
	 *	|
	 *   offset
	 *	|
	 *	|
	 *	-	 + center
	 */

	mid = (length + offset) / 2;
	mc = mid * cosangle;
	ms = mid * sinangle;
	wc = width * cosangle;
	ws = width * sinangle;
	/*1 ---- 2 */
	SetSeg(w,
	       x = w->clock.centerX + round(length * sinangle),
	       y = w->clock.centerY - round(length * cosangle),
	       w->clock.centerX + round(ms - wc),
	       w->clock.centerY - round(mc + ws) );
	SetSeg(w, w->clock.centerX + round(offset *sinangle),
	       w->clock.centerY - round(offset * cosangle), /* 2-----3 */
	       w->clock.centerX + round(ms + wc), 
	       w->clock.centerY - round(mc - ws));
	w->clock.segbuffptr->x = x;
	w->clock.segbuffptr++->y = y;
	w->clock.numseg ++;
}

static void SetSeg(w, x1, y1, x2, y2)
ClockWidget w;
int x1, y1, x2, y2;
{
	w->clock.segbuffptr->x = x1;
	w->clock.segbuffptr++->y = y1;
	w->clock.segbuffptr->x = x2;
	w->clock.segbuffptr++->y = y2;
	w->clock.numseg += 2;
}

/*
 *  Draw the clock face (every fifth tick-mark is longer
 *  than the others).
 */
static void DrawClockFace(w)
ClockWidget w;
{
	register int i;
	register int delta = (w->clock.radius - w->clock.second_hand_length) / 3;
	
	w->clock.segbuffptr = w->clock.segbuff;
	w->clock.numseg = 0;
	for (i = 0; i < 60; i++)
		DrawLine(w, ( (i % 5) == 0 ? 
			     w->clock.second_hand_length :
			     (w->clock.radius - delta) ),
			 w->clock.radius, ((double) i)/60.);
	/*
	 * Go ahead and draw it.
	 */
	XDrawSegments(XtDisplay(w), XtWindow(w),
		      w->clock.myGC, (XSegment *) &(w->clock.segbuff[0]),
		      w->clock.numseg/2);
	
	w->clock.segbuffptr = w->clock.segbuff;
	w->clock.numseg = 0;
}

static int round(x)
double x;
{
	return(x >= 0.0 ? (int)(x + .5) : (int)(x - .5));
}

/* ARGSUSED */
static Boolean SetValues (gcurrent, grequest, gnew)
    Widget gcurrent, grequest, gnew;
{
      ClockWidget current = (ClockWidget) gcurrent;
      ClockWidget new = (ClockWidget) gnew;
      Boolean redisplay = FALSE;
      XtGCMask valuemask;
      XGCValues	myXGCV;

      /* first check for changes to clock-specific resources.  We'll accept all
         the changes, but may need to do some computations first. */

      if (new->clock.padding != current->clock.padding)
	   redisplay = TRUE;

      if (new->clock.analog != current->clock.analog)
	   redisplay = TRUE;

       if (new->clock.font != current->clock.font)
	   redisplay = TRUE;

      if ((new->clock.fgpixel != current->clock.fgpixel)
          || (new->core.background_pixel != current->core.background_pixel)) {
          valuemask = GCForeground | GCBackground | GCFont | GCLineWidth;
	  myXGCV.foreground = new->clock.fgpixel;
	  myXGCV.background = new->core.background_pixel;
          myXGCV.font = new->clock.font->fid;
	  myXGCV.line_width = 0;
	  XtDestroyGC (current->clock.myGC);
	  new->clock.myGC = XtGetGC(gcurrent, valuemask, &myXGCV);
	  redisplay = TRUE;
          }

      if (new->clock.Hipixel != current->clock.Hipixel) {
          valuemask = GCForeground | GCLineWidth;
	  myXGCV.foreground = new->clock.fgpixel;
          myXGCV.font = new->clock.font->fid;
	  myXGCV.line_width = 0;
	  XtDestroyGC (current->clock.HighGC);
	  new->clock.HighGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
	  redisplay = TRUE;
          }

      if (new->clock.Hdpixel != current->clock.Hdpixel) {
          valuemask = GCForeground;
	  myXGCV.foreground = new->clock.fgpixel;
	  XtDestroyGC (current->clock.HandGC);
	  new->clock.HandGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
	  redisplay = TRUE;
          }

      if (new->core.background_pixel != current->core.background_pixel) {
          valuemask = GCForeground | GCLineWidth;
	  myXGCV.foreground = new->core.background_pixel;
	  myXGCV.line_width = 0;
	  XtDestroyGC (current->clock.EraseGC);
	  new->clock.EraseGC = XtGetGC((Widget)gcurrent, valuemask, &myXGCV);
	  redisplay = TRUE;
	  }

      if (new->clock.timezone != current->clock.timezone)
	{
	  if ((new->clock.timezone && ! current->clock.timezone) ||
	      (! new->clock.timezone && current->clock.timezone))
	    redisplay = TRUE;
	  else if (new->clock.timezone && current->clock.timezone &&
		   strcmp (new->clock.timezone, current->clock.timezone))
	    redisplay = TRUE;
	  if (current->clock.timezone) XtFree (current->clock.timezone);
	  if (new->clock.timezone)
	    new->clock.timezone =
	      strcpy (XtMalloc (strlen (new->clock.timezone) + 1),
		      new->clock.timezone);
	}

      if (new->clock.label != current->clock.label)
	{
	  if (strcmp (new->clock.label, current->clock.label)) redisplay = TRUE;
	  XtFree (current->clock.label);
	  new->clock.label =
	    strcpy (XtMalloc (strlen (new->clock.label) + 1),
		    new->clock.label);
	}
     
     return (redisplay);

}
@EOF

chmod 644 xchrono/MultiClock.c

echo x - xchrono/MultiClock.h
cat >xchrono/MultiClock.h <<'@EOF'
/*
* $XConsortium: Clock.h,v 1.28 89/07/20 14:54:38 jim Exp $
*/


/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

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 and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#ifndef _XawClock_h
#define _XawClock_h

/***********************************************************************
 *
 * Clock Widget
 *
 ***********************************************************************/

#include <X11/Xmu/Converters.h>

/* Parameters:

 Name		     Class		RepType		Default Value
 ----		     -----		-------		-------------
 analog		     Boolean		Boolean		True
 background	     Background		Pixel		white
 backingStore	     BackingStore	BackingStore	default
 border		     BorderColor	Pixel		Black
 borderWidth	     BorderWidth	Dimension	1
 chime		     Boolean		Boolean		False
 destroyCallback     Callback		Pointer		NULL
 font		     Font		XFontStruct*	fixed
 foreground	     Foreground		Pixel		black
 hand		     Foreground		Pixel		black
 height		     Height		Dimension	164
 highlight	     Foreground		Pixel		black
 mappedWhenManaged   MappedWhenManaged	Boolean		True
 padding	     Margin		int		8
 reverseVideo	     ReverseVideo	Boolean		False
 width		     Width		Dimension	164
 x		     Position		Position	0
 y		     Position		Position	0

*/

/* Resource names used to the clock widget */

		/* color of hands */
#define XtNhand "hands"


		/* Boolean: digital if FALSE */
#define XtNanalog "analog"

		/* Boolean:  */
#define XtNchime "chime"

		/* Int: amount of space around outside of clock */
#define XtNpadding "padding"

#define XtNtimezone		"timezone"
		/* Timezone of clock */
#define XtNlabelFont		"labelFont"
		/* City label font */

typedef struct _ClockRec *ClockWidget;  /* completely defined in ClockPrivate.h */
typedef struct _ClockClassRec *ClockWidgetClass;    /* completely defined in ClockPrivate.h */

extern WidgetClass clockWidgetClass;

extern void SetClockUpdateInterval(/* int interval */);

#endif /* _XawClock_h */
/* DON'T ADD STUFF AFTER THIS #endif */
@EOF

chmod 644 xchrono/MultiClock.h

echo x - xchrono/MultiClockP.h
cat >xchrono/MultiClockP.h <<'@EOF'
/*
* $XConsortium: ClockP.h,v 1.19 89/12/06 15:23:06 kit Exp $
*/


/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

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 and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#ifndef _XawClockP_h
#define _XawClockP_h

#include <X11/Xos.h>		/* Needed for struct tm. */
#include "MultiClock.h"
#include <X11/CoreP.h>

#define SEG_BUFF_SIZE		128
#define ASCII_TIME_BUFLEN	32	/* big enough for 26 plus slop */
#define TZ_BUFLEN		1024    /* huge size for paths */

/* New fields for the clock widget instance record */
typedef struct {
	 Pixel	fgpixel;	/* color index for text */
	 Pixel	Hipixel;	/* color index for Highlighting */
	 Pixel	Hdpixel;	/* color index for hands */
	 XFontStruct	*font;	/* font for text */
	 GC	myGC;		/* pointer to GraphicsContext */
	 GC	EraseGC;	/* eraser GC */
	 GC	HandGC;		/* Hand GC */
	 GC	HighGC;		/* Highlighting GC */
/* start of graph stuff */
	 int	update;		/* update frequence */
	 Dimension radius;		/* radius factor */
	 int	backing_store;	/* backing store type */
	 Boolean reverse_video;
	 Boolean chime;
	 Boolean beeped;
	 Boolean analog;
	 Boolean show_second_hand;
	 Dimension second_hand_length;
	 Dimension minute_hand_length;
	 Dimension hour_hand_length;
	 Dimension hand_width;
	 Dimension second_hand_width;
	 Position centerX;
	 Position centerY;
	 int	numseg;
	 int	padding;
	 XPoint	segbuff[SEG_BUFF_SIZE];
	 XPoint	*segbuffptr;
	 XPoint	*hour, *sec;
	 struct tm  otm ;
	 char prev_time_string[ASCII_TIME_BUFLEN];
	 String timezone;
	 String label;
	 XFontStruct *labelFont;
	 GC	labelGC;
   } ClockPart;

/* Full instance record declaration */
typedef struct _ClockRec {
   CorePart core;
   ClockPart clock;
   } ClockRec;

/* New fields for the Clock widget class record */
typedef struct {
  int updateInterval;
  XtIntervalId intervalId;
  ClockWidget *clockList;
  int numberOfClocks;
  int numberOfSlots;
} ClockClassPart;

/* Full class record declaration. */
typedef struct _ClockClassRec {
   CoreClassPart core_class;
   ClockClassPart clock_class;
   } ClockClassRec;

/* Class pointer. */
extern ClockClassRec clockClassRec;

#endif /* _XawClockP_h */
@EOF

chmod 644 xchrono/MultiClockP.h

echo x - xchrono/clmask.bit
cat >xchrono/clmask.bit <<'@EOF'
#define clock_mask_width 48
#define clock_mask_height 48
static char clock_mask_bits[] = {
   0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00,
   0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x00, 0x00,
   0x00, 0xe0, 0xff, 0xff, 0x03, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x07, 0x00,
   0x00, 0xf8, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x1f, 0x00,
   0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00,
   0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01,
   0xe0, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03,
   0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x1f,
   0xfc, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x1f,
   0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f,
   0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f,
   0xfc, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x1f,
   0xfc, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07,
   0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x03,
   0xe0, 0xff, 0xff, 0xff, 0xff, 0x01, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x01,
   0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00,
   0x00, 0xfe, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x1f, 0x00,
   0x00, 0xf8, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x07, 0x00,
   0x00, 0xe0, 0xff, 0xff, 0x03, 0x00, 0x00, 0x80, 0xff, 0xff, 0x00, 0x00,
   0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00,
   0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, 0x00, 0x00};
@EOF

chmod 644 xchrono/clmask.bit

chmod 755 xchrono

exit 0

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.


