#ifndef lint
static char    *sccsid = "@(#)xcal_memo.c	1.23 (Hillside Systems) 9/13/95";
static char    *copyright = "@(#)Copyright 1989,1990,1993 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() memset((caddr_t)callbacks, '\0', sizeof (callbacks))
#define argLD(N,V) { XtSetArg(args[nargs], N, V); nargs++; }

/*
 * 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_edit;		/* widget of edit button */
	Widget		m_help;		/* widget of help 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 */
	String		m_weeklytext;	/* weekly text */
	Cardinal        m_size;		/* size of the buffer */
	char           *m_data;		/* pointer to malloc'ed data buffer */
} MemoEdit;

static MemoEdit memo;

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 void	EditToday();
static int      NewlineCount();
static String   GetMemoFile();
static void     SaveMemoEdits();
static void     MemoTextChanged();
static void     FinishMemoEditing();
static void     YesCheck();
static void     NoCheck();
static void	AdjustTitleHeight();

/*
 * 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 = NULL;
	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;

	/*
	 * 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;
	argLD(XtNshowGrip, False);
	argLD(XtNskipAdjust, True);
	argLD(XtNdefaultDistance, 1);
	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 = NULL;
	nargs = 0;
	argLD(XtNcallback, callbacks);
	argLD(XtNfromHoriz, NULL);
	argLD(XtNleft, XtChainLeft);
	argLD(XtNright, XtChainLeft);
	lw = memo.m_quit = XtCreateManagedWidget("quit", commandWidgetClass, frame, args, nargs);

	/*
	 * Edit todays file from here as well
	 * Take label from resources
	 */
	if (MyCalendar) {
		callbacks[0].callback = EditToday;
		callbacks[0].closure = (caddr_t) 0;
		nargs = 0;
		argLD(XtNcallback, callbacks);
		argLD(XtNfromHoriz, lw);
		argLD(XtNleft, XtChainLeft);
		argLD( XtNright, XtChainLeft);
		memo.m_edit = lw = XtCreateManagedWidget("edit", 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;
		argLD(XtNcallback, callbacks);
		argLD(XtNfromHoriz, lw);
		argLD(XtNleft, XtChainLeft);
		argLD( XtNright, XtChainLeft);
		memo.m_help = lw = XtCreateManagedWidget("help", commandWidgetClass, frame, args, nargs);
	}

	/*
	 * The remaining bit here is a date label
	 */
	nargs = 0;
	argLD(XtNlabel, date_area);
	argLD(XtNborderWidth, 0);
	argLD(XtNfromHoriz, lw);
	argLD(XtNfromVert, NULL);
	argLD(XtNvertDistance, 2);
	argLD(XtNleft, XtChainLeft);
	argLD(XtNright, XtChainRight);
	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 = "";
	argLD(XtNstring, str);
	argLD(XtNdisplayCaret, False);
	argLD(XtNeditType, XawtextRead);
	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);
	}

	AdjustTitleHeight(memo.m_quit,
			  MyCalendar ? memo.m_edit : NULL,
			  appResources.giveHelp ? memo.m_help : NULL,
			  memo.m_display);

	/*
	 * Weekly details - the data for today + an edit button
	 * The header to this is a form
	 */
	nargs = 0;
	argLD(XtNshowGrip, False);
	argLD(XtNskipAdjust, True);
	argLD(XtNdefaultDistance, 1);
	frame = XtCreateManagedWidget("weeklyMemo", formWidgetClass, et, args, nargs);
	/*
	 * Take label "edit" from resources
	 */
	if (MyCalendar) {
		callbacks[0].callback = DoWeekly;
		callbacks[0].closure = (caddr_t) & memo;
		nargs = 0;
		argLD(XtNcallback, callbacks);
		argLD(XtNfromHoriz, NULL);
		argLD(XtNleft, XtChainLeft);
		argLD(XtNright, XtChainLeft);
		lw = XtCreateManagedWidget("weeklyEdit", commandWidgetClass, frame, args, nargs);
	}
		
	/*
	 * Say this is a weekly commitment
	 */
	nargs = 0;
	argLD(XtNshowGrip, True);
	argLD(XtNborderWidth, 0);
	argLD(XtNfromHoriz, MyCalendar ? lw : NULL);
	argLD(XtNfromVert, NULL);
	argLD(XtNvertDistance, 2);
	argLD(XtNleft, XtChainLeft);
	argLD(XtNright, XtChainRight);
	lw = XtCreateManagedWidget("weeklyTitle", labelWidgetClass, frame, args, nargs);

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


	XtPopup(memo.m_popup, XtGrabNone);

}

/*
 * Adjust title line height
 * possibly 4 objects
 */
static void
AdjustTitleHeight(quit, edit, help, label)
	Widget		quit;
	Widget		edit;
	Widget		help;
	Widget		label;
{
	int		hq, he, hh, hl;
	int		max;

	hq = wHeight(quit);
	he = edit ? wHeight(edit) : 0;
	hh = help ? wHeight(help): 0;
	hl = wHeight(label);

	max = hq;
	max = (he > max) ? he : max;
	max = (hh > max) ? hh : max;
	max = (hl > max) ? hl : max;

	if (hq < max)
		SetWidgetHeightMax(quit, hq, max);
	if (he && he < max)
		SetWidgetHeightMax(edit, he, max);
	if (hh & hh < max)
		SetWidgetHeightMax(help, hh, max);
	if (hl < max)
		SetWidgetHeightMax(label, hl, max);
}

/*
 * This callback starts editing today
 */
static void
EditToday(w, closure, call_data)
	Widget		w;
	caddr_t         closure;
	caddr_t         call_data;
{	

	StartEditing(w, &today, w);
}

/*
 * 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;
	MonthEntry     *me;

	/*
	 * if the button widget is zero then we are displaying nothing
	 */
	if (memo.m_button == 0)
		return;

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

	XtSetArg(args[0], XtNlabel, date_area);
	XtSetValues(memo.m_display, args, 1);

	if (memo.m_weeklytext)
		XtFree(memo.m_weeklytext);
	memo.m_weeklytext = 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;
	argLD(XtNstring, memo.m_data);
	argLD(XtNlength, memo.m_size);
	argLD(XtNuseStringInPlace, True);
	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;

	if (MyCalendar) {
		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;

	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();
	else
		CleanMemo();
}

static void
CleanMemo()
{
	static Arg      args[1];
	static MemoEdit	zerom;

	callbacks[0].callback = DoMemo;
	callbacks[0].closure = NULL;
	XtSetArg(args[0], XtNcallback, callbacks);
	XtSetValues(memo.m_button, args, 1);
	XtSetSensitive(memo.m_button, True);

	MouseShow(memo.m_button, True);
	XtPopdown(memo.m_popup);
	XtDestroyWidget(memo.m_popup);
	XtFree(memo.m_data);
	XtFree(memo.m_weeklytext);
	memo = zerom;
}

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

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

	XtSetSensitive(memo.m_quit, False);
	XtSetSensitive(memo.m_button, False);
	XtSetSensitive(memo.m_save, False);

	/* Take "Save file?" from resources */
	dia = XtCreateManagedWidget("memocheck", 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)));
}

