#ifndef lint
static char *sccsid = "@(#)xcal.c	3.22 (Hillside Systems) 1/16/91";
static char *copyright = "@(#)Copyright 1989,1990 Peter Collinson, Hillside Systems";
#endif  /* lint */
/***

* program name:
	xcal.c
* function:
	display the current calendar date
	if pressed as a button go into strip calendar mode
* switches:
	-format str	use str as a display format
	-order	ord	set the argument order to this
	-debug		run quickly incrementing time - 1 day per sec
	-alarmscan	print alarm debug info
* libraries used:
	libXaw.a, libXmu.a libXt.a libX11.a
* compile time parameters:
	standard
* history:
	Written November 1989
	Peter Collinson
	Hillside Systems
* (C) Copyright: 1989 Hillside Systems/Peter Collinson
	
	Permission to use, copy, modify, and distribute this software
	and its documentation for any purpose is hereby granted to
	anyone, 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 Peter Collinson not be used in advertising or
	publicity pertaining to distribution of the software without
	specific, written prior permission.  Hillside Systems makes no
	representations about the suitability of this software for any
	purpose.  It is provided "as is" without express or implied
	warranty.
	
	Peter Collinson DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
	SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
	AND FITNESS, IN NO EVENT SHALL Peter Collinson 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 <stdio.h>
#include <time.h>
#include <ctype.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Form.h>
#include "xcal.h"

char	date_area[BUFSIZ];

/*	command line options specific to the application */
static XrmOptionDescRec Options[] = {
{"-debug",	"debug",	XrmoptionNoArg,	(caddr_t)"TRUE"},
{"-alarmscan",	"alarmScan",	XrmoptionNoArg,	(caddr_t)"TRUE"},
{"-format",	"format",	XrmoptionSepArg, NULL},
{"-order",	"order", 	XrmoptionSepArg, NULL},
};

struct resources appResources;

Pixmap	MouseOnPix;
Pixmap	MouseOffPix;

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

static XtResource Resources[] = {
{"debug", "Debug", XtRBoolean, sizeof(Boolean),
	offset(debug), XtRString, "False" },
{"alarmScan", "AlarmScan", XtRBoolean, sizeof(Boolean),
	offset(alarmScan), XtRString, "False" },
{"reverseVideo", "ReverseVideo", XtRBoolean, sizeof(Boolean),
	offset(reverseVideo), XtRString, "False" },
{"xcalendarCompat", "XcalendarCompat", XtRBoolean, sizeof(Boolean),
	offset(calCompat), XtRString, "False" },
{"giveHelp", "GiveHelp", XtRBoolean, sizeof(Boolean),
	offset(giveHelp), XtRString, "True" },
{"useMemo", "UseMemo", XtRBoolean, sizeof(Boolean),
	offset(useMemo), XtRString, "True" },
{"memoLeft", "MemoLeft", XtRBoolean, sizeof(Boolean),
	offset(memoLeft), XtRString, "True" },
{"initialCalendar", "InitialCalendar", XtRBoolean, sizeof(Boolean),
	offset(initialCalendar), XtRString, "False" },
{"initialEdit", "InitialEdit", XtRBoolean, sizeof(Boolean),
	offset(initialEdit), XtRString, "False" },
{"initialMemo", "InitialMemo", XtRBoolean, sizeof(Boolean),
	offset(initialMemo), XtRString, "False" },
{"markForeground", "MarkForeground", XtRPixel, sizeof(Pixel),
	offset(marked.fg), XtRString, "White" },
{"markBackground", "MarkBackground", XtRPixel, sizeof(Pixel),
	offset(marked.bg), XtRString, "Black" },
{"markToday", "MarkToday", XtRBoolean, sizeof(Boolean),
	offset(markToday), XtRString, "True" },
{"fontToday", "FontToday", XtRFontStruct, sizeof(XFontStruct *),
	offset(fontToday), XtRString, "XtDefaultFont"},
{"todayForeground", "TodayForeground", XtRPixel, sizeof(Pixel),
	offset(today.fg), XtRString, "White" },
{"todayBackground", "TodayBackground", XtRPixel, sizeof(Pixel),
	offset(today.bg), XtRString, "Black" },
{"format", "Format",XtRString, sizeof(String),
	offset(opfmt), XtRString, "%s %2d %s %d"},
{"order", "Order", XtRString, sizeof(String),
	offset(order), XtRString, "wdmy"},
{"dateYearIsTwoDigits", "DateYearIsTwoDigits", XtRBoolean, sizeof(Boolean),
	offset(dateYearIs2), XtRString, "False" },
{"editYearIsTwoDigits", "EditYearIsTwoDigits", XtRBoolean, sizeof(Boolean),
	offset(editYearIs2), XtRString, "False" },
{"memoYearIsTwoDigits", "MemoYearIsTwoDigits", XtRBoolean, sizeof(Boolean),
	offset(memoYearIs2), XtRString, "False" },
{"directory", "Directory", XtRString, sizeof(String),
	offset(directory), XtRString, "Calendar"},
{"textBufferSize", "TextBufferSize", XtRInt, sizeof(int),
	offset(textbufsz), XtRString, "2048"},
{"useWmTitle", "UseWmTitle", XtRBoolean, sizeof(Boolean),
	offset(useWmTitle), XtRString, "True"},
{"minStripWidth", "MinStripWidth", XtRDimension, sizeof(Dimension),
	offset(minstripwidth), XtRString, "0"},
{"january", "January", XtRString, sizeof(String),
	offset(mon[0]), XtRString, "January"},
{"february", "February", XtRString, sizeof(String),
	offset(mon[1]), XtRString, "February"},
{"march", "March", XtRString, sizeof(String),
	offset(mon[2]), XtRString, "March"},
{"april", "April", XtRString, sizeof(String),
	offset(mon[3]), XtRString, "April"},
{"may", "May", XtRString, sizeof(String),
	offset(mon[4]), XtRString, "May"},
{"june", "June", XtRString, sizeof(String),
	offset(mon[5]), XtRString, "June"},
{"july", "July", XtRString, sizeof(String),
	offset(mon[6]), XtRString, "July"},
{"august", "August", XtRString, sizeof(String),
	offset(mon[7]), XtRString, "August"},
{"september", "September", XtRString, sizeof(String),
	offset(mon[8]), XtRString, "September"},
{"october", "October", XtRString, sizeof(String),
	offset(mon[9]), XtRString, "October"},
{"november", "November", XtRString, sizeof(String),
	offset(mon[10]), XtRString, "November"},
{"december", "December", XtRString, sizeof(String),
	offset(mon[11]), XtRString, "December"},
{"jan", "Jan", XtRString, sizeof(String),
	offset(smon[0]), XtRString, "Jan"},
{"feb", "Feb", XtRString, sizeof(String),
	offset(smon[1]), XtRString, "Feb"},
{"mar", "Mar", XtRString, sizeof(String),
	offset(smon[2]), XtRString, "Mar"},
{"apr", "Apr", XtRString, sizeof(String),
	offset(smon[3]), XtRString, "Apr"},
{"may", "May", XtRString, sizeof(String),
	offset(smon[4]), XtRString, "May"},
{"jun", "Jun", XtRString, sizeof(String),
	offset(smon[5]), XtRString, "Jun"},
{"jul", "Jul", XtRString, sizeof(String),
	offset(smon[6]), XtRString, "Jul"},
{"aug", "Aug", XtRString, sizeof(String),
	offset(smon[7]), XtRString, "Aug"},
{"sep", "Sep", XtRString, sizeof(String),
	offset(smon[8]), XtRString, "Sep"},
{"oct", "Oct", XtRString, sizeof(String),
	offset(smon[9]), XtRString, "Oct"},
{"nov", "Nov", XtRString, sizeof(String),
	offset(smon[10]), XtRString, "Nov"},
{"dec", "Dec", XtRString, sizeof(String),
	offset(smon[11]), XtRString, "Dec"},
{"sunday", "Sunday", XtRString, sizeof(String),
	offset(day[0]), XtRString, "Sunday"},
{"monday", "Monday", XtRString, sizeof(String),
	offset(day[1]), XtRString, "Monday"},
{"tuesday", "Tuesday", XtRString, sizeof(String),
	offset(day[2]), XtRString, "Tuesday"},
{"wednesday", "Wednesday", XtRString, sizeof(String),
	offset(day[3]), XtRString, "Wednesday"},
{"thursday", "Thursday", XtRString, sizeof(String),
	offset(day[4]), XtRString, "Thursday"},
{"friday", "Friday", XtRString, sizeof(String),
	offset(day[5]), XtRString, "Friday"},
{"saturday", "Saturday", XtRString, sizeof(String),
	offset(day[6]), XtRString, "Saturday"},
{"sun", "Sun", XtRString, sizeof(String),
	offset(sday[0]), XtRString, "Sun"},
{"mon", "Mon", XtRString, sizeof(String),
	offset(sday[1]), XtRString, "Mon"},
{"tue", "Tue", XtRString, sizeof(String),
	offset(sday[2]), XtRString, "Tue"},
{"wed", "Wed", XtRString, sizeof(String),
	offset(sday[3]), XtRString, "Wed"},
{"thu", "Thu", XtRString, sizeof(String),
	offset(sday[4]), XtRString, "Thu"},
{"fri", "Fri", XtRString, sizeof(String),
	offset(sday[5]), XtRString, "Fri"},
{"sat", "Sat", XtRString, sizeof(String),
	offset(sday[6]), XtRString, "Sat"},
{"weekly", "Weekly", XtRString, sizeof(String),
	offset(weekly), XtRString, "Weekly"},
{"alarms", "Alarms", XtRBoolean, sizeof(Boolean),
	offset(alarms), XtRString, "True"},
{"update", "Update", XtRInt, sizeof(int),
	offset(update), XtRString, "0"},
{"volume", "Volume", XtRInt, sizeof(int),
	offset(volume), XtRString, "50"},
{"nbeeps", "Nbeeps", XtRInt, sizeof(int),
	offset(nbeeps), XtRString, "3"},
{"cmd", "Cmd", XtRString, sizeof(String),
	offset(cmd), XtRString, NULL},
{"countdown", "Countdown", XtRString, sizeof(String),
	offset(countdown), XtRString, "10,0"},
{"autoquit", "Autoquit",  XtRInt, sizeof(int),
	offset(autoquit), XtRString, "120"},
{"alarmleft", "Alarmleft",  XtRString, sizeof(String),
	offset(alarmleft), XtRString, "%d minutes before..."},
{"alarmnow", "Alarmnow",  XtRString, sizeof(String),
	offset(alarmnow), XtRString, "Time is now..."},
{"memoFile", "MemoFile", XtRString, sizeof(String),
	offset(memoFile), XtRString, "memo"},
{"maxDisplayLines", "MaxDisplayLines", XtRInt, sizeof(int),
	offset(maxDisplayLines), XtRString, "5"},
};

static XtCallbackRec callbacks[] = {
   {NULL, NULL},
   {NULL, NULL},
};
#define ClearCallbacks() bzero((caddr_t)callbacks, sizeof (callbacks))

static XtActionsRec appActions[]= {
	{"setdate", SetDate},
	{"leave", AskLeave},
	{"SetDateAction", TextCal},
};

static String defTranslations =
	"<Btn2Down>: set()\n\
	<Btn2Up>:setdate() unset()\n\
	<Btn3Down>: set()\n\
	<Btn3Up>: leave() unset()";

static Arg	wargs[7] = {
	XtNlabel, (XtArgVal) date_area,
	XtNcallback, (XtArgVal)callbacks,
};

Widget	toplevel;

Date	today;

/*
 *	Forward routines local to this file
 */
static void	MkDate();
static void	DebugMkDate();
static void	DoTemplate();
static void	DecodeOrder();
static void	PixInit();

#include "mouse.bm"
#include "mouseaway.bm"

void
main(argc, argv)
	int argc;
	char **argv;
{
	Widget parent;
	Widget memo;
	Widget lab;
	

	toplevel = XtInitialize(argv[0], "XCal",
				Options, XtNumber(Options), &argc, argv);

	PixInit(toplevel);
	
	if (argc != 1)
		fprintf(stderr, "Error in arguments\n", argv[0]);

	XtGetApplicationResources(toplevel, (caddr_t)&appResources, Resources,
			      XtNumber(Resources), (ArgList)NULL, 0);

	/*
	 *	If reverse video
	 *	invert default colour settings
	 */
	if (appResources.reverseVideo)
	{	Colour	old;
		old = appResources.marked;
		appResources.marked.fg = old.bg;
		appResources.marked.bg = old.fg;
		old = appResources.today;
		appResources.today.fg = old.bg;
		appResources.today.bg = old.fg;
	}
	
	InitMonthEntries();
	
	DecodeOrder();
	
	DoTemplate();		/* give a maximum initial size of the box */

	/*
	 *	Top level widget is now a form
	 *	assuming that memo is wanted
	 */
	if (appResources.useMemo)
	{	parent = toplevel;
		XtSetArg(wargs[2], XtNborderWidth, 0);
		XtSetArg(wargs[3], XtNdefaultDistance, 0);
		parent = XtCreateManagedWidget("form", formWidgetClass,
					parent, &wargs[2], 3);

		if (appResources.memoLeft)
		{	XtSetArg(wargs[2], XtNfromHoriz, NULL);
			XtSetArg(wargs[3], XtNleft, XtChainLeft);
			XtSetArg(wargs[4], XtNright, XtRubber);
			XtSetArg(wargs[5], XtNborderWidth, 0);
			XtSetArg(wargs[6], XtNbitmap, MouseOnPix);
			callbacks[0].callback = DoMemo;
			memo = XtCreateManagedWidget("today", commandWidgetClass,
					     parent, &wargs[1], 6);
			ClearCallbacks();

			XtSetArg(wargs[2], XtNfromHoriz, memo);
			XtSetArg(wargs[3], XtNleft, XtRubber);
			XtSetArg(wargs[4], XtNright, XtChainRight);
			XtSetArg(wargs[5], XtNborderWidth, 0);
			callbacks[0].callback = DoCalendar;
			lab = XtCreateManagedWidget("date", commandWidgetClass,
					    parent, wargs, 6);
		}
		else
		{
			XtSetArg(wargs[2], XtNfromHoriz, NULL);
			XtSetArg(wargs[3], XtNleft, XtChainLeft);
			XtSetArg(wargs[4], XtNright, XtRubber);
			XtSetArg(wargs[5], XtNborderWidth, 0);
			callbacks[0].callback = DoCalendar;
			lab = XtCreateManagedWidget("date", commandWidgetClass,
					    parent, wargs, 6);
			ClearCallbacks();

			XtSetArg(wargs[2], XtNfromHoriz, lab);
			XtSetArg(wargs[3], XtNleft, XtRubber);
			XtSetArg(wargs[4], XtNright, XtChainRight);
			XtSetArg(wargs[5], XtNborderWidth, 0);
			XtSetArg(wargs[6], XtNbitmap, MouseOnPix);
			callbacks[0].callback = DoMemo;
			memo = XtCreateManagedWidget("today", commandWidgetClass,
					     parent, &wargs[1], 6);
		}
	}
	else
	{
		callbacks[0].callback = DoCalendar;
		lab = XtCreateManagedWidget("date", commandWidgetClass,
					    toplevel, wargs, 2);
	}

	ClearCallbacks();

	XtSetMappedWhenManaged(toplevel, False);

	XtRealizeWidget(toplevel);	/* set the default geometry */
	
	if (appResources.debug)
	{	fprintf(stderr, "Debug ON\n");
		DebugMkDate(lab);
	}
	else	MkDate(lab);

	if (appResources.giveHelp)
	{	printf("\
The small date strip is a button\n\
Enter the button showing the date and use\n\
the mouse buttons to select further actions:\n\
	Left mouse button pops up this month's calendar strip\n\
	Middle mouse button permits date selection\n\
	Right mouse button allows exit\n\
Click with the left mouse button in the small box holding\n\
the mouse icon to edit a memo file\n");
	}
	XtAddActions(appActions, 3);	/* register actions */
	XtAugmentTranslations(lab, XtParseTranslationTable(defTranslations));
	if (appResources.useMemo)
		XtAugmentTranslations(memo, XtParseTranslationTable(defTranslations));

	XtMapWidget(toplevel);

	if (appResources.initialCalendar)
		DoCalendar(lab, NULL, NULL);


	if (appResources.initialEdit)
	{	MonthEntry	*me;

		me = GetMonthEntry(today.year, today.month);
		if (me->me_have[today.day])
			StartEditing(lab, &today);
	}

	if (appResources.useMemo && appResources.initialMemo)
		DoMemo(memo, NULL, NULL);
		
	InitAlarms();
	
	XtMainLoop();
	exit(0);
}

/*
 *	Initialise Pixmaps
 */
static void
PixInit(toplevel)
	Widget	toplevel;
{
	Display	*theDisplay = XtDisplay(toplevel);

	MouseOnPix = XCreateBitmapFromData(theDisplay,
			      DefaultRootWindow(theDisplay),
			      (char *)mouse_bits, mouse_width, mouse_height);
	MouseOffPix = XCreateBitmapFromData(theDisplay,
			      DefaultRootWindow(theDisplay),
			      (char *)mouseaway_bits, mouseaway_width, mouseaway_height);
}

/*
 *	Flip mouse state
 */
void
MouseShow(w, OnOff)
	Widget	w;
	Boolean OnOff;
{
	Arg	arg[1];

	XtSetArg(arg[0], XtNbitmap, OnOff ? MouseOnPix : MouseOffPix);
	XtSetValues(w, arg, 1);
}
 

/*
 *	Exit routine
 */
void
Leave(retval)
	int retval;
{	exit(retval);
}

/************************************************************************/
/*									*/
/*									*/
/*	This deals with the top level date `icon'			*/
/*									*/
/*									*/
/************************************************************************/

/*
 *	Time management code
 *	Set up a Date structure from today's information
 */
static void
ConvDate(tm, dp)
	struct	tm *tm;
	Date *dp;
{
	dp->day = tm->tm_mday;
	dp->month = tm->tm_mon;
	dp->year = tm->tm_year + 1900;
	dp->wday = tm->tm_wday;
}


static void
MkDate(w)
	Widget w;
{	long	ti;
	struct	tm *tm;
	static	timedOut;
	Date	yesterday;
	
	if (timedOut)
		yesterday = today;
		
	(void) time(&ti);
	tm = localtime(&ti);

	ConvDate(tm, &today);

	PlaceStr(date_area, &today, appResources.dateYearIs2);

	XtSetValues(w, wargs, 1);

	if (timedOut)
	{	ChangeHighlight(&yesterday, &today);
		AlarmFilePoll(tm);
		UpdateMemo();
	}

	ti = 24*60*60 - (tm->tm_hour*60*60 + tm->tm_min*60 + tm->tm_sec);
	XtAddTimeOut(ti*1000, MkDate, (caddr_t)w);
	timedOut++;
}

static void
DebugMkDate(w)
	Widget w;
{	static long ti;
	struct	tm *tm;
	static	timedOut;
	Date	yesterday;
	
	if (timedOut)
		yesterday = today;
		
	if (ti == 0)
		(void) time(&ti);
	else	ti += 24*60*60;
	
	tm = localtime(&ti);
	ConvDate(tm, &today);
	
	PlaceStr(date_area, &today, appResources.dateYearIs2);

	XtSetValues(w, wargs, 1);

	if (timedOut)
	{	ChangeHighlight(&yesterday, &today);
		AlarmFilePoll(tm);
		UpdateMemo();
	}

	XtAddTimeOut(2000, DebugMkDate, (caddr_t)w);
	timedOut++;
}

/*
 *	DoTemplate
 *	place an initial string into the date area so that the label
 *	box will always be big enough
 */
static void
DoTemplate()
{	int	max;
	int	i;
	int	len;
	char	trial[BUFSIZ];
	Date	da;

	da.day = 99;
	da.year = 9999;

	for (da.wday = max = i = 0; i < 7; i++)
	{	len = strlen(appResources.day[i]);
		if (len > max)
		{	max = len;
			da.wday = i;
		}
	}

	for (max = i = 0; i < 12; i++)
	{	da.month = i;
		PlaceStr(trial, &da, appResources.dateYearIs2);
		len = strlen(trial);
		if (len > max)
		{	max = len;
			strcpy(date_area, trial);
		}
	}
}
	
/*
 *	decode the order
 */
static void
DecodeOrder()
{
	register char	*p;
	int	order = 0;
	int	addweekday = 0;
	
	p = appResources.order;

	if (*p == 'w')
	{	addweekday = O_WEEKLEFT;
		p++;
	}
	
	if (*p == 'd' && strncmp(p, "dmy", 3) == 0)
		order = O_DMY;
	else
	if (*p == 'y')
	{	if (strncmp(p, "ymd", 3) == 0)
			order = O_YMD;
		else
		if (strncmp(p, "ydm", 3) == 0)
			order = O_YDM;
		else
			order = -1;
	}
	else
	if (*p == 'm' && strncmp(p, "mdy", 3) == 0)
		order = O_MDY;
	else
		order = -1;
	if (order == -1)
	{	fprintf(stderr, "Unknown order: %s\n", p);
		order = O_DMY;
	}

	if (addweekday == 0 && p[3] == 'w')
		addweekday = O_WEEKRIGHT;

	appResources.val_order = order | addweekday;
}
			
/*
 *	make a string
 */
void
PlaceStr(dest, da, is2)
	String	dest;
	Date	*da;
	Boolean is2;
{
	register String fmt;
	int	d = da->day;
	String	m = appResources.mon[da->month];
	int	y = da->year;
	String	w = appResources.day[da->wday];

	if (y > 99 && y < 1900)
		y -= 100;

	if (is2)
		y %= 100;
		
	fmt = appResources.opfmt;
	
	switch (appResources.val_order)
	{
	case O_DMY:		/* default */
		(void) sprintf(dest, fmt, d, m, y);
		break;
	case O_DMY|O_WEEKLEFT:
		(void) sprintf(dest, fmt, w, d, m, y);
		break;
	case O_DMY|O_WEEKRIGHT:
		(void) sprintf(dest, fmt, d, m, y, w);
		break;
	case O_YMD:		/* Year/Month/Day */
		(void) sprintf(dest, fmt, y, m, d);
		break;
	case O_YMD|O_WEEKLEFT:	/* Year/Month/Day */
		(void) sprintf(dest, fmt, w, y, m, d);
		break;
	case O_YMD|O_WEEKRIGHT:	/* Year/Month/Day */
		(void) sprintf(dest, fmt, y, m, d, w);
		break;
	case O_MDY:		/* Month/Day/Year */
		(void) sprintf(dest, fmt, m, d, y);
		break;
	case O_MDY|O_WEEKLEFT:	/* Month/Day/Year */
		(void) sprintf(dest, fmt, w, m, d, y);
		break;
	case O_MDY|O_WEEKRIGHT:	/* Month/Day/Year */
		(void) sprintf(dest, fmt, m, d, y, w);
		break;
	case O_YDM:		/* Year/Day/Month */
		(void) sprintf(dest, fmt, y, d, m);
		break;
	case O_YDM|O_WEEKLEFT:	/* Year/Day/Month */
		(void) sprintf(dest, fmt, w, y, d, m);
		break;
	case O_YDM|O_WEEKRIGHT:	/* Year/Day/Month */
		(void) sprintf(dest, fmt, y, d, m, w);
		break;
	}
}
