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

* module name:
	xcal_memo.c
* function:
	Deal with popup memo file
	A single popup file is stored in a file called
	memo on the Calendar directory
* history:
	Written December 1990
	Peter Collinson
	Hillside Systems
* (C) Copyright: 1989,1990 Hillside Systems/Peter Collinson
	
	For full permissions and copyright notice - see xcal.c
***/
#include <stdio.h>
#include <ctype.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Dialog.h>
#include "xcal.h"

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

/*
 *	Structure for storing relavant data about the
 *	memo Edit
 */
typedef struct	memoEdit
{
	Widget	m_button;	/* widget of the control button */
	Widget	m_popup;	/* widget of editor popup */
	Widget	m_quit;		/* widget of quit button */
	Widget	m_save;		/* widget of save button */
	Boolean m_savesens;	/* state of the save button */
	Widget	m_display;	/* widget of display title area */
	Widget	m_text;		/* the text area */
	Widget	m_today;	/* today's data */
	Widget	m_weekly;	/* widget of text image of weekly events */
	Cardinal m_size;	/* size of the buffer */
	char	*m_data;	/* pointer to malloc'ed data buffer */
} MemoEdit;

static	MemoEdit memo;

extern	Boolean FoundCalendarDir;               /* whether the Calendar directory exists */

static	String	memoContents;

extern void MemoHelp();				/* look in xcal_help.c */

/*
 *	Internal routines
 */
void	MemoPopup();
static void CleanMemo();
static void MemoCheckExit();
static void MCheckDia();
static Boolean WriteMemoFile();
static int NewlineCount();
static String GetMemoFile();
static void SaveMemoEdits();
static void MemoTextChanged();
static void FinishMemoEditing();
static void YesCheck();
static void NoCheck();

/*
 *	Callback routine to display the memo file
 */

void
DoMemo(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	static Arg args[1];
	
	/*
	 *	Make the button become a finish button
	 */
	memo.m_button = w;
	callbacks[0].callback = FinishMemoEditing;
	callbacks[0].closure = (caddr_t)&memo;
	XtSetArg(args[0], XtNcallback, callbacks);
	XtSetValues(w, args, 1);
	
	MouseShow(w, False);
	/*
	 *	Get existing memo contents
	 *	if the user is polling then re-read the file
	 */
	if (appResources.update && memoContents != NULL)
	{	XtFree(memoContents);
		memoContents = NULL;
	}
	if (memoContents == NULL)
		memoContents = GetMemoFile();

	/*
	 *	Set up the popup widget for editing
	 */
	MemoPopup();
}


/*
 *	Get old contents from a memo file if any
 */
static String
GetMemoFile()
{

	if (FoundCalendarDir && access(appResources.memoFile, F_OK) == 0)
		return ReadCalendarFile(NULL, appResources.memoFile);
	return NULL;
}


/*
 *	Do the biz to popup an edit style window
 */
void
MemoPopup()
{
	Widget		et, lw;
	Widget		frame;
	Arg		args[10];
	Cardinal	nargs;
	String		str;
	MonthEntry	*me;
	Dimension	charHeight;
	char		buf[32];

	/*
	 *	set up edit buffer
	 */
	if (memoContents)
		memo.m_size = appResources.textbufsz + strlen(memoContents) + 1;
	else	
		memo.m_size = appResources.textbufsz;
	memo.m_data = XtMalloc(memo.m_size);
	if (memoContents)
		strcpy(memo.m_data, memoContents);
	else
		*memo.m_data = '\0';
	memo.m_popup = XtCreatePopupShell("memo", topLevelShellWidgetClass, toplevel, NULL, 0);

	/*
	 *	The first title line
	 */
	et = XtCreateManagedWidget("memoPanel", panedWidgetClass, memo.m_popup, NULL, 0);

	nargs = 0;
	XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
	XtSetArg(args[nargs], XtNskipAdjust, True); nargs++;
	XtSetArg(args[nargs], XtNdefaultDistance, 1); nargs++;
	frame = XtCreateManagedWidget("title", formWidgetClass, et, args, nargs);
	/*
	 *	containing some buttons for controlling the world
	 */
	/*
	 *	Take label "quit" from resources
	 */
	callbacks[0].callback = FinishMemoEditing;
	callbacks[0].closure = (caddr_t)&memo;
	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 = memo.m_quit = XtCreateManagedWidget("quit", commandWidgetClass, frame, args, nargs);
	/*
	 *	If we are dealing with  help then do it now
	 */
	if (appResources.giveHelp)
	{	/*
		 *	Take label "help" from resources
		 */
		callbacks[0].callback = MemoHelp;
		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, XtChainLeft); nargs++;
		lw = XtCreateManagedWidget("help", commandWidgetClass, frame, args, nargs);
	}
	/*
	 *	The remaining bit here is a date label
	 */
	PlaceStr(buf, &today, appResources.memoYearIs2);
	nargs = 0;
	XtSetArg(args[nargs], XtNlabel, buf); nargs++;
	XtSetArg(args[nargs], XtNborderWidth, 0); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
	XtSetArg(args[nargs], XtNfromVert, NULL); nargs++;
	XtSetArg(args[nargs], XtNvertDistance, 2); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
	lw = memo.m_display = XtCreateManagedWidget("date", labelWidgetClass, frame, args, nargs);
	/*
	 *	Details for today
	 */
	me = GetMonthEntry(today.year, today.month);
	nargs = 0;
	str = me->me_have[today.day];
	if (str == NULL)
		str = "";
	XtSetArg(args[nargs], XtNstring, str); nargs++;
	XtSetArg(args[nargs], XtNdisplayCaret, False); nargs++;
	XtSetArg(args[nargs], XtNeditType, XawtextRead); nargs++;
	memo.m_today = XtCreateManagedWidget("display", asciiTextWidgetClass, et, args, nargs);
	{	Dimension height;

		XtSetArg(args[0], XtNheight, &height);
		XtGetValues(memo.m_today, args, 1);
		charHeight = height;
		height = height*NewlineCount(str);
		XtSetArg(args[0], XtNheight, height);
		XtSetValues(memo.m_today, args, 1);
	}

	/*
	 *	Weekly details - the data for today + an edit button
	 *	The header to this is a form
	 */
	nargs = 0;
	XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
	XtSetArg(args[nargs], XtNskipAdjust, True); nargs++;
	XtSetArg(args[nargs], XtNdefaultDistance, 1); nargs++;
	frame = XtCreateManagedWidget("weeklyMemo", formWidgetClass, et, args, nargs);

	/*
	 *	Take label "edit" from resources
	 */
	callbacks[0].callback = DoWeekly;
	callbacks[0].closure = (caddr_t)&memo;
	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("weeklyEdit", commandWidgetClass, frame, args, nargs);

	/*
	 *	Say this is a weekly commitment
	 */
	nargs = 0;

	XtSetArg(args[nargs], XtNshowGrip, True); nargs++;
	XtSetArg(args[nargs], XtNborderWidth, 0); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
	XtSetArg(args[nargs], XtNfromVert, NULL); nargs++;
	XtSetArg(args[nargs], XtNvertDistance, 2); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
	lw = XtCreateManagedWidget("weeklyTitle", labelWidgetClass, frame, args, nargs);

	/*
	 *	Details for today
	 */
	nargs = 0;
	str = GetWeeklyFile(today.wday);
	if (str == NULL)
		str = "";
	XtSetArg(args[nargs], XtNstring, str); nargs++;
	XtSetArg(args[nargs], XtNdisplayCaret, False); nargs++;
	XtSetArg(args[nargs], XtNeditType, XawtextRead); nargs++;
	if (charHeight)
		XtSetArg(args[nargs], XtNheight, NewlineCount(str)*charHeight); nargs++;
	memo.m_weekly = XtCreateManagedWidget("display", asciiTextWidgetClass, et, args, nargs);
	/*
	 *	Another form with some buttons
	 */
	nargs = 0;
	XtSetArg(args[nargs], XtNshowGrip, False); nargs++;
	XtSetArg(args[nargs], XtNskipAdjust, True); nargs++;
	XtSetArg(args[nargs], XtNdefaultDistance, 1); nargs++;
	frame = XtCreateManagedWidget("memoMiddle", formWidgetClass, et, args, nargs);
	/*
	 *	Take label "save" from resources
	 */
	callbacks[0].callback = SaveMemoEdits;
	callbacks[0].closure = (caddr_t)&memo;
	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++;
	XtSetArg(args[nargs], XtNsensitive, False); nargs++;
	lw = memo.m_save = XtCreateManagedWidget("save", commandWidgetClass, frame, args, nargs);
	memo.m_savesens = False;
	/*
	 *	Say this is a memo edit
	 */
	nargs = 0;
	XtSetArg(args[nargs], XtNshowGrip, True); nargs++;
	XtSetArg(args[nargs], XtNborderWidth, 0); nargs++;
	XtSetArg(args[nargs], XtNfromHoriz, lw); nargs++;
	XtSetArg(args[nargs], XtNfromVert, NULL); nargs++;
	XtSetArg(args[nargs], XtNvertDistance, 2); nargs++;
	XtSetArg(args[nargs], XtNleft, XtChainLeft); nargs++;
	XtSetArg(args[nargs], XtNright, XtChainRight); nargs++;
	lw = XtCreateManagedWidget("memoTitle", labelWidgetClass, frame, args, nargs);

 	/*
	 *	The text widget is in the pane below
	 *	The Scroll Attributes are controlled from the application
	 *	defaults file
	 */
	callbacks[0].callback = MemoTextChanged;
	callbacks[0].closure = (caddr_t)&memo;
	nargs = 0;
	XtSetArg(args[nargs], XtNstring, memo.m_data); nargs++;
	XtSetArg(args[nargs], XtNeditType, XawtextEdit); nargs++;
	XtSetArg(args[nargs], XtNlength, memo.m_size); nargs++;
	XtSetArg(args[nargs], XtNuseStringInPlace, True); nargs++;
	XtSetArg(args[nargs], XtNcallback, callbacks); nargs++;
	memo.m_text = XtCreateManagedWidget("memoText", asciiTextWidgetClass, et, args, nargs);

	
	XtPopup(memo.m_popup, XtGrabNone);
	
}

/*
 *	Count newlines in a string
 */
static int
NewlineCount(str)
	String	str;
{
	register int sum = 0;

	while (*str)
		if (*str++ == '\n')
			sum++;
	/* Add one line - assume last line does NOT have an nl */
	sum++;
	/* ignore a final newline */
	if (str[-1] == '\n')
		sum--;
	if (sum <= 0) sum = 1;
	return(sum > appResources.maxDisplayLines ? appResources.maxDisplayLines : sum);
}

/*
 *	Entry point from outside when today's text changed
 */
void
UpdateMemo()
{
	Arg		args[1];
	String		str;
	Cardinal	nargs;
	MonthEntry	*me;
	char		buf[32];
	
	/*
	 * if the button widget is zero then we are displaying nothing
	 */
	if (memo.m_button == 0)
		return;

	me = GetMonthEntry(today.year, today.month);
	nargs = 0;
	str = me->me_have[today.day];
	if (str == NULL)
		str = "";
	XtSetArg(args[0], XtNstring, str); nargs++;
	XtSetValues(memo.m_today, args, 1);

	(void) sprintf(buf, "%d %s %d", today.day, appResources.mon[today.month], today.year);
	XtSetArg(args[0], XtNlabel, buf);
	XtSetValues(memo.m_display, args, 1);

	str = GetWeeklyFile(today.wday);
	if (str == NULL)
		str = "";
	XtSetArg(args[0], XtNstring, str);
	XtSetValues(memo.m_weekly, args, 1);

}

/*
 *	Poll call from the alarm timeout
 */
void
MemoPoll()
{
	int	size;
	Arg	args[10];
	int	nargs;
	
	if (memo.m_button == 0)
		return;
	if (memo.m_savesens == True)
		return;

	if (memoContents)
		XtFree(memoContents);
	memoContents = GetMemoFile();
	if (memoContents)
	{	if (strcmp(memoContents, memo.m_data) == 0)
			return;
		size = strlen(memoContents) + 1;
		if (size > memo.m_size)
		{	size += appResources.textbufsz;
			XtFree(memo.m_data);
			memo.m_data = XtMalloc(memo.m_size = size);
		}
		strcpy(memo.m_data, memoContents);
	}
	else
		*memo.m_data = '\0';
		
	nargs = 0;
	XtSetArg(args[nargs], XtNstring, memo.m_data); nargs++;
	XtSetArg(args[nargs], XtNlength, memo.m_size); nargs++;
	XtSetArg(args[nargs], XtNuseStringInPlace, True); nargs++;
	XtSetValues(memo.m_text, args, nargs);
}
		
/*
 *	Call backs for various buttons
 */
/* ARGSUSED */
static void
MemoTextChanged(w, closure, call_data)
	Widget	w;
	caddr_t	closure;
	caddr_t call_data;
{
	register MemoEdit *memo = (MemoEdit *)closure;

	memo->m_savesens = True;
	XtSetSensitive(memo->m_save, True);
}

/*
 *	Callback routines
 */
/* ARGSUSED */
static void
SaveMemoEdits(w, closure, call_data)
	Widget	w;
	caddr_t	closure;
	caddr_t call_data;
{
	MemoEdit	*memo = (MemoEdit *)closure;

	if (WriteMemoFile(memo) == False)
		return;
	if (memoContents)
	{	XtFree(memoContents);
		memoContents = XtNewString(memo->m_data);
	}
	memo->m_savesens = False;
	XtSetSensitive(memo->m_save, False);
}

/*
 *	Write the memo file out
 */
static Boolean
WriteMemoFile(memo)
	MemoEdit	*memo;
{
	Cardinal	len = strlen(memo->m_data);
	String		fname;
	int		fd;
	extern		String MapStem;
	
	if (len == 0)
	{	unlink(appResources.memoFile);
		return(True);
	}
	/*
	 *	First let's see if we have 
	 *	to create the toplevel directory
	 */
	if (!NeedTop())
		return (False);

	fname = appResources.memoFile;

	if ((fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0)
	{	XBell(XtDisplay(toplevel), 0);
		fprintf(stderr, "xcal: Could not open %s/%s for writing.\n", MapStem, fname);
		perror("xcal: open");
		fflush(stderr);
		return(False);
	}

	if (write(fd, memo->m_data, len) != len)
	{	XBell(XtDisplay(toplevel), 0);
		fprintf(stderr, "xcal: Write error %s/%s file.\n", MapStem, fname);
		perror("xcal: write");
		fflush(stderr);
		close(fd);
		return(False);
	}
	close(fd);
	return (True);
}

static void
FinishMemoEditing(w, closure, call_data)
	Widget	w;
	caddr_t	closure;
	caddr_t call_data;
{

	if (memo.m_savesens == True)
	{	MemoCheckExit();
		return;
	}
	CleanMemo();
}

static void
CleanMemo()
{
	static Arg args[1];
	
	callbacks[0].callback = DoMemo;
	callbacks[0].closure = NULL;
	XtSetArg(args[0], XtNcallback, callbacks);
	XtSetValues(memo.m_button, args, 1);

	MouseShow(memo.m_button, True);
	XtPopdown(memo.m_popup);
	XtDestroyWidget(memo.m_popup);
	XtFree(memo.m_data);
	bzero((char *)&memo, sizeof(MemoEdit));

}

static void
MemoCheckExit()
{
	DialogPopup(memo.m_quit, MCheckDia, &memo);
}

static void
MCheckDia(pop, ed)
	Widget	pop;
	MemoEdit *ed;
{
	Widget	dia;

	/* Take "Save file?" from resources */
	dia =  XtCreateManagedWidget("check", dialogWidgetClass, pop, NULL, 0);
	XawDialogAddButton(dia, "yes", YesCheck, ed);
	XawDialogAddButton(dia, "no", NoCheck, ed);
}

/* ARGSUSED */
static void
YesCheck(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	SaveMemoEdits(w, closure, call_data);
	CleanMemo();
	XtDestroyWidget(XtParent(XtParent(w)));

}

/* ARGSUSED */
static void
NoCheck(w, closure, call_data)
	Widget w;
	caddr_t	closure;
	caddr_t call_data;
{
	CleanMemo();
	XtDestroyWidget(XtParent(XtParent(w)));
}
