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

* module name:
	xcal_strip.c
* function:
	Deal with the popup strip calendars obtained either by
	selection and the middle button, or by the < and > buttons
	on each strip.
* history:
	Written November 1989
	Peter Collinson
	Hillside Systems
* (C) Copyright: 1989 Hillside Systems/Peter Collinson
	
	For full permissions and copyright notice - see xcal.c
***/
#include <stdio.h>
#include <ctype.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/AsciiText.h>
#include "xcal.h"

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

Date	callb;		/* contains date when calendar day button pressed */

/*
 *	Forward routines local to this file
 */
void MakeMonth();
void	DayBack();
#ifndef LONG_IS_32_BITS
void	YmBack();
#endif
void	StripQuit();
void	StripHelp();
void	WeeklyHelp();
void	MakeNewMonth();

Cardinal DateSum();
Cardinal NumberOfDays();
Cardinal FirstDay();
Cardinal JanuaryOne();

/*
 *	Local routines
 */
static void	CreateActionBar();
static void	CreateWeeklyActionBar();

/*
*	Start a strip calendar happening
 *	a callback of left button 
 */
/* ARGSUSED */
void
DoCalendar(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	NewMonthStrip(&today);		/* today is global */
}

/* ARGSUSED */
void
DoWeekly(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	Date	thisday;

	thisday.day = 0;
	thisday.month = 0;
	thisday.year = 0;
	thisday.wday = 0;
	NewMonthStrip(&thisday);	/* today is global */
}


/*
 *	Start a strip calendar happening
 *	a callback of the > or < buttons in another strip
 */
/* ARGSUSED */
static void
MakeNewMonth(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	Date	thisday;

	thisday.year = YrUnpack((Cardinal)closure);
	thisday.month = MoUnpack((Cardinal)closure);
	thisday.day = today.day;
	NewMonthStrip(&thisday);
}

/* 
 *	Do all the X stuff to popup a Strip calendar
 *	A calendar strip is:
 *
 *	Popup ("<month year>")		// Name is the month and the year
 *	    Paned ("<month>")		// Name is the month
 *		Label ("header")	// optional contains Month Year
 *		Form ("action")		// < Quit >
 *		    Command ("back")	// contains < 
 *		    Label ("quit")	// contains Quit
 *		    Command ("next")	// contains >
 *	(Then many of..)
 *		Form ("<dd DDD>")	// where dd is the day number, DDD is the
 *					// day of the week
 *		    Label ("label")	// contains the string above
 *		    Command ("info")	// contains the text from the file
 *		
 */
void
NewMonthStrip(td)
	Date	*td;
{
	Widget		shell, mon, dw, lw, lwi, form;
	Arg		args[15];
	char		nbuf[256];
	char		iconName[80];
	int		type;
	MonthEntry	*me;
	Instance	*ins;
	register int	i;
	register Cardinal nargs;
	Cardinal	thisDay;
	Cardinal	startLoop;
	String		dayStr;
	Cardinal	numberOfDays;
	Boolean		defaultsAreSet = False;
	Boolean		markThisMonth = False;
	Cardinal	adjustLabelY;
	Cardinal	adjustInfoY;
	Dimension	labelH, infoH;
	Dimension	width;
	Dimension	totalWidth;

	type = (td->day == 0) ? ME_WEEKLY : ME_MONTHLY;
	
	/*
	 *	There are lots of differences between
	 *	Months and weekly strips here.
	 *	Later tests are done using a switch structure
	 */
	switch (type)
	{
	case ME_MONTHLY:
		(void) sprintf(iconName, "%s %d", appResources.smon[td->month], td->year);
		XtSetArg(args[0], XtNiconName, iconName);
		shell = XtCreatePopupShell(XtNewString(iconName), topLevelShellWidgetClass, toplevel, args, 1);
		ins = RegisterMonth(td->year, td->month, shell);	
		mon = XtCreateManagedWidget(appResources.mon[td->month], panedWidgetClass, shell, NULL, 0);
		thisDay = FirstDay(td->month, td->year);
		numberOfDays = NumberOfDays(td->month, td->year);
		startLoop = 1;
		/*
		 *	Get the map for this year
		 */
		me = GetMonthEntry(td->year, td->month);
		me->me_type = type;
		/*
		 *	Title bar is month and date
		 */
		(void) sprintf(nbuf, "%s %d", appResources.mon[td->month], td->year);
		/*
		 *	see if we will need to worry about marking today's entry
		 */
		if (appResources.markToday && td->year == today.year && td->month == today.month)
			markThisMonth = True;
		break;
	case ME_WEEKLY:
		(void) strcpy(iconName, appResources.weekly);
		XtSetArg(args[0], XtNiconName, iconName);
		shell = XtCreatePopupShell(XtNewString(iconName), topLevelShellWidgetClass, toplevel, args, 1);
		ins = RegisterMonth(0, 0, shell);	
		mon = XtCreateManagedWidget(iconName, panedWidgetClass, shell, NULL, 0);
		thisDay = 0;
		numberOfDays = 6;	/* test is <= */
		startLoop = 0;
		/*
		 *	Get the map for this year
		 */
		me = GetWeeklyEntry();
		me->me_type = type;
		/*
		 *	Title bar is from the resources
		 */
		strcpy(nbuf, iconName);
		/*
		 *	see if we will need to worry about marking today's entry
		 */
		if (appResources.markToday)
			markThisMonth = True;
		break;
	}
	/*
	 *	Find size of title bar
	 *	by creating the widget and then throwing it away
	 */
	XtSetArg(args[0], XtNlabel, "mmmmmmmmm NNNN");
	lw = XtCreateManagedWidget("sizer", labelWidgetClass, shell, args, 1);
	XtSetArg(args[0], XtNwidth, &totalWidth);
	XtGetValues(lw, args, 1);
	XtDestroyWidget(lw);
	/*
	 *	Width is affected by a resource value
	 */
	if (appResources.minstripwidth && appResources.minstripwidth > totalWidth)
		totalWidth = appResources.minstripwidth;
	/*
	 *	Now set the title bar should we need it
	 */
	if (appResources.useWmTitle)
	{	XtSetArg(args[0], XtNlabel, XtNewString(nbuf));
		(void) XtCreateManagedWidget("header", labelWidgetClass, mon, args, 1);
	}
		
	/*
	 *	Action bar
	 */
	nargs = 0;
	XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
	XtSetArg(args[nargs], XtNdefaultDistance, 2); nargs++;
	dw = XtCreateManagedWidget("action", formWidgetClass, mon, args, nargs);

	switch (type)
	{
	case ME_MONTHLY:
		CreateActionBar(shell, dw, mon, td);
		break;
	case ME_WEEKLY:
		CreateWeeklyActionBar(shell, dw);
		break;
	}

#ifdef	LONG_IS_32_BITS
	callbacks[0].callback = DayBack;
#else
	callbacks[0].callback = YmBack;
	callbacks[1].callback = DayBack;
#endif
	for (i = startLoop; i <= numberOfDays; i++)
	{
		dayStr = appResources.sday[thisDay];
		switch(type)
		{
		case ME_MONTHLY:
			(void) sprintf(nbuf, "%2d %s", i, dayStr);
			break;
		case ME_WEEKLY:
			(void) strcpy(nbuf, dayStr);
			break;
		}
#ifdef LONG_IS_32_BITS
		callbacks[0].closure = (caddr_t)DatePack(thisDay, i, td->month, td->year);
#else
		callbacks[0].closure = (caddr_t)DatePack(td->month, td->year);
		callbacks[1].closure = (caddr_t)DayPack(thisDay, i);
#endif		

		thisDay = (thisDay+1)%7;
		
		/*
		 *	Each line in the strip is
		 *	form containing
		 *	label - command
		 */
		nargs = 0;
		XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
		XtSetArg(args[nargs], XtNdefaultDistance, 0); nargs++;
		form = XtCreateManagedWidget(dayStr, formWidgetClass, mon, args, nargs);
		
		nargs = 0;
		XtSetArg(args[nargs], XtNlabel, XtNewString(nbuf)); nargs++;
						/* a little naughty here */
						/* this string memory is lost */
						/* on quit */
		XtSetArg(args[nargs], XtNborderWidth, 0); nargs++;
		XtSetArg(args[nargs], XtNjustify, XtJustifyLeft); nargs++;
		XtSetArg(args[nargs], XtNfromHoriz, NULL); nargs++;
		XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
		XtSetArg(args[nargs], XtNright, XtChainLeft); nargs++;

		ins->i_day_label[i] = lw = XtCreateManagedWidget("label", labelWidgetClass, form, args, nargs);

		/*
		 *	To get a handle on the old values which are lost by
		 *	highlighting we get them after we have created the
		 *	widget. Then we highlight today.
		 */
		if (markThisMonth &&
		    ((type == ME_MONTHLY && today.day == i) ||
		     (type == ME_WEEKLY && today.wday == i)))
		{
			nargs = 0;
			XtSetArg(args[nargs], XtNforeground, &ins->i_col.fg); nargs++;
			XtSetArg(args[nargs], XtNbackground, &ins->i_col.bg); nargs++;
			XtSetArg(args[nargs], XtNfont, &ins->i_font); nargs++;
			XtGetValues(lw, args, nargs);

			nargs = 0;
			XtSetArg(args[nargs], XtNforeground, appResources.today.fg); nargs++;
			XtSetArg(args[nargs], XtNbackground, appResources.today.bg); nargs++;
			XtSetArg(args[nargs], XtNfont, appResources.fontToday); nargs++;
			XtSetValues(lw, args, nargs);
		}
		/*
		 *	Done the first time through
		 *	Gets the width of the line we have just made
		 */
		if (defaultsAreSet == False)
		{	/* compute text width */
			nargs = 0;
			XtSetArg(args[nargs], XtNwidth, &width); nargs++;
			XtSetArg(args[nargs], XtNheight, &labelH); nargs++;
			XtGetValues(lw, args, nargs);
			defaultsAreSet = True;
		}
		/*
		 *	Start processing the RHS of the line
		 *	This contains text from the file
		 *	should any exist
		 */
		nargs = 0;
		XtSetArg(args[nargs], XtNborderWidth, 0); nargs++;
		XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
		XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
		XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
		XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
		XtSetArg(args[nargs], XtNjustify, XtJustifyLeft); nargs++;
		XtSetArg(args[nargs], XtNwidth, totalWidth - width); nargs++;

		if (me->me_have[i])
		{	XtSetArg(args[nargs], XtNforeground, appResources.marked.fg); nargs++;
			XtSetArg(args[nargs], XtNbackground, appResources.marked.bg); nargs++;
			XtSetArg(args[nargs], XtNlabel, me->me_have[i]); nargs++;
		}
		else
		{	XtSetArg(args[nargs], XtNlabel, "    "); nargs++;	}
		ins->i_day_info[i] = lwi = XtCreateManagedWidget("info", commandWidgetClass, form, args, nargs);

		/* deal with height */
		XtSetArg(args[0], XtNheight, &infoH);
		XtGetValues(lwi, args, 1);
		if (labelH < infoH)
		{	adjustLabelY = ((infoH-labelH)/2);
			/* fix up widget */
			nargs = 0;
			XtSetArg(args[nargs], XtNvertDistance, adjustLabelY); nargs++;
                        XtSetArg(args[nargs], XtNfromVert, NULL); nargs++;
			XtSetValues(lw, args, nargs);
		}
		else
		if (labelH > infoH)
		{	adjustInfoY = ((labelH - infoH)/2);
			/* fix up widget 1 */
			nargs = 0;
			XtSetArg(args[nargs], XtNvertDistance, adjustInfoY); nargs++;
                        XtSetArg(args[nargs], XtNfromVert, NULL); nargs++;
			XtSetValues(lwi, args, nargs);
		}

		/*
		 *	cope with 1752
		 */
		if (td->year == 1752 && td->month == 8 && i == 2)
		{	i = 13;
			numberOfDays += 11;	/* giving back the 11 days */
		}
	}
	ClearCallbacks();

	XtPopup(shell, XtGrabNone);
}

/*
 *	Create action bar for normal monthly strip
 */
static void
CreateActionBar(shell, dw, mon, td)
	Widget	shell;
	Widget	dw;
	Widget	mon;
	Date	*td;
{
	Widget		lw;
	register Cardinal nargs;
	Arg		args[8];

	/*
	 *	back one month
	 *	label "<" from resources
	 */
	callbacks[0].callback = MakeNewMonth;
	callbacks[0].closure = (caddr_t)DateSum(td, -1);
	nargs = 0;
	XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, NULL); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainLeft); nargs++;
	lw = XtCreateManagedWidget("back", commandWidgetClass, dw, args, nargs);
	ClearCallbacks();

	/*
	 *	Quit button
	 *	label "quit" from resources
	 */
	callbacks[0].callback = StripQuit;
	callbacks[0].closure = (caddr_t)shell;
	nargs = 0;
	XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
	lw = XtCreateManagedWidget("quit", commandWidgetClass, dw, args, nargs);
	ClearCallbacks();

	/*
	 *	On one month
	 *	label ">" from reources
	 */
	callbacks[0].callback = MakeNewMonth;
	callbacks[0].closure = (caddr_t)DateSum(td, 1);
	nargs = 0;
	XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainRight); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
	lw = XtCreateManagedWidget("next", commandWidgetClass, dw, args, nargs);
	ClearCallbacks();

	/*
	 *	Help button
	 *	label help from resources
	 */
	if (appResources.giveHelp)
	{	
		callbacks[0].callback = StripHelp;
		callbacks[0].closure = (caddr_t)0;
		nargs = 0;
		XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
		XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
		lw = XtCreateManagedWidget("help", commandWidgetClass, mon, args, nargs);
		ClearCallbacks();
	}
}

/*
 *	Create action bar for normal monthly strip
 */
static void
CreateWeeklyActionBar(shell, dw)
	Widget	shell;
	Widget	dw;
{
	Widget		lw;
	register Cardinal nargs;
	Arg		args[8];

	/*
	 *	Quit button
	 *	label "quit" from resources
	 */
	callbacks[0].callback = StripQuit;
	callbacks[0].closure = (caddr_t)shell;
	nargs = 0;
	XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, NULL); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, appResources.giveHelp ? XtChainLeft: XtChainRight); nargs++;
	lw = XtCreateManagedWidget("quit", commandWidgetClass, dw, args, nargs);
	ClearCallbacks();

	/*
	 *	Help button
	 *	label help from resources
	 */
	if (appResources.giveHelp)
	{	
		callbacks[0].callback = WeeklyHelp;
		callbacks[0].closure = (caddr_t)0;
		nargs = 0;
		XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
		XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
		XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
		XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
		lw = XtCreateManagedWidget("help", commandWidgetClass, dw, args, nargs);
		ClearCallbacks();
	}
}

/*
 *	Called when the date changes to ensure that
 *	the correct day has the appropriate highlights
 */
void
ChangeHighlight(old, new)
	Date	*old;
	Date	*new;
{
	register Instance *ins;
	Arg		args[5];
	Cardinal	nargs;

	for (ins = FindInstanceList(old); ins; ins = ins->i_next)
	{	nargs = 0;	
		XtSetArg(args[nargs], XtNforeground, ins->i_col.fg); nargs++;
		XtSetArg(args[nargs], XtNbackground, ins->i_col.bg); nargs++;
		XtSetArg(args[nargs], XtNfont, ins->i_font); nargs++;
		XtSetValues(ins->i_day_label[old->day], args, nargs);
	}

	for (ins = FindInstanceList(new); ins; ins = ins->i_next)
	{	nargs = 0;
		XtSetArg(args[nargs], XtNforeground, &ins->i_col.fg); nargs++;
		XtSetArg(args[nargs], XtNbackground, &ins->i_col.bg); nargs++;
		XtSetArg(args[nargs], XtNfont, &ins->i_font); nargs++;
		XtGetValues(ins->i_day_label[new->day], args, nargs);

		nargs = 0;	
		XtSetArg(args[nargs], XtNforeground, appResources.today.fg); nargs++;
		XtSetArg(args[nargs], XtNbackground, appResources.today.bg); nargs++;
		XtSetArg(args[nargs], XtNfont, appResources.fontToday); nargs++;
		XtSetValues(ins->i_day_label[new->day], args, nargs);
	}
}

/*
 *	Call back from a quit button to lose a month strip
 */
/* ARGSUSED */
static void
StripQuit(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	XtPopdown((Widget)closure);
	XtDestroyWidget((Widget)closure);
}
	

/*
 *	Month arithmetic and packing
 */
static Cardinal
DateSum(td, inx)
	Date	*td;
	int	inx;
{
	int	m, y;

	m = td->month;
	y = td->year;
	m += inx;
	if (m < 0)
	{	m = 11;
		y--;
	}
	else
	if (m > 11)
	{	m = 0;
		y++;
	}
#ifdef LONG_IS_32_BITS
	return(DatePack(0, 0, m, y));
#else
	return(DatePack(m, y));
#endif
}

/*
 *	Call back from day selection button press
 *	This is done in two stages if cannot fold dates into a closure
 */
/* ARGSUSED */
static void
DayBack(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
#ifdef LONG_IS_32_BITS
	callb.month = MoUnpack((Cardinal)closure);
	callb.year = YrUnpack((Cardinal)closure);
#endif
	callb.day = DyUnpack((Cardinal)closure);
	callb.wday = WdUnpack((Cardinal)closure);
	StartEditing(w, &callb);
}

#ifndef LONG_IS_32_BITS
/* ARGSUSED */
static void
YmBack(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	callb.month = MoUnpack((Cardinal)closure);
	callb.year = YrUnpack((Cardinal)closure);
}
#endif

/*
 *	Stolen from xcalendar.c
 */
/* taken from cal.c */

char	mon[] = {
   31, 29, 31, 30,
   31, 30, 31, 31,
   30, 31, 30, 31,
};

static Cardinal calInit = 0;

static Cardinal
NumberOfDays(m, y)
	Cardinal m, y;
{
	if(calInit != y)
		(void) FirstDay(m, y); /* set side effect */
	return mon[m];
}

/* should be called first */
static Cardinal
FirstDay(m, y)
     Cardinal m, y;
{
	register d, i;

	calInit = y;
	d = JanuaryOne(y);
	mon[1] = 29;
	mon[8] = 30;
   
	switch((JanuaryOne(y+1)+7-d)%7)
	{
      
	/*
	 *	non-leap year
	 */
	case 1:
		mon[1] = 28;
		break;
      
	/*
	 *	1752
	 */
	default:
		mon[8] = 19;
		break;

	/*
	 *	leap year
	 */
	case 2:
		;
	}
   
	for(i=0; i<m; i++)
		d += mon[i];

	return(d%7);
}

/*
 *	return day of the week
 *	of jan 1 of given year
 */
static Cardinal
JanuaryOne(yr)
	Cardinal yr;
{
	register Cardinal y, d;

/*
 *	normal gregorian calendar
 *	one extra day per four years
 */

	y = yr;
	d = 4+y+(y+3)/4;

/*
 *	julian calendar
 *	regular gregorian
 *	less three days per 400
 */

	if(y > 1800) {
		d -= (y-1701)/100;
		d += (y-1601)/400;
	}

/*
 *	great calendar changeover instant
 */

	if(y > 1752)
		d += 3;

	return(d%7);
}
