/* text.c
   make and manage the text popups */

     /*---------------------------------------------------------------*/
     /* Xgopher        version 1.1     20 April 1991                  */
     /*                version 1.0     04 March 1991                  */
     /* X window system client for the University of Minnesota        */
     /*                                Internet Gopher System.        */
     /* Allan Tuchman, University of Illinois at Urbana-Champaign     */
     /*                Computing Services Office                      */
     /* Copyright 1992 by                                             */
     /*           the Board of Trustees of the University of Illinois */
     /* Permission is granted to freely copy and redistribute this    */
     /* software with the copyright notice intact.                    */
     /*---------------------------------------------------------------*/


#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#include <X11/Xaw/Box.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Shell.h>

#include "KeyWSink.h"

#include "conf.h"
#include "resources.h"
#include "xglobals.h"
#include "misc.h"

#define	PERCENT	'%'

typedef struct _textElement {
		Widget		textShell;
		Widget		textDisplay;
		Boolean		used;
		Boolean		deleteFile;
		char		*displayedFile;
		char		**wordList;
		char		*stringValue;
		struct _textElement *next;
	} textElement, *textElementP;

static	textElementP	textElementHead = NULL;


/* textStringToFile
   if a text widget has a string, rather than file value, copy the
   string to a file, and set the structure to reflect this so things
   will be cleaned up. */

static Boolean
textStringToFile(tep)
textElementP	tep;
{
	char	*tempfile = XtMalloc(sizeof(char) * PATH_NAME_LEN);
	FILE	*fp;

	getTempFile(tempfile);
	if ((fp = fopen (tempfile, "w")) == NULL) {
		return False;
	}
	fwrite (tep->stringValue, sizeof(char),
				strlen(tep->stringValue), fp);
	fclose(fp);
	tep->displayedFile = tempfile;
	tep->deleteFile = True;
	free(tep->stringValue);		/* free(), not XtFree() */
	tep->stringValue = NULL;

	return True;
}


/* pageDownProc
   scroll down a page in a text widget */

void
pageDownProc(w, client_data, call_data)
Widget		w;
XtPointer	client_data, call_data;
{
	Widget	tw = (Widget) client_data;

	XtCallActionProc(tw, "next-page", NULL, NULL, 0);
}


/* pageUpProc
   scroll up a page in a text widget */

void
pageUpProc(w, client_data, call_data)
Widget		w;
XtPointer	client_data, call_data;
{
	Widget	tw = (Widget) client_data;

	XtCallActionProc(tw, "previous-page", NULL, NULL, 0);
}


/* printProc
   send the contents of a text widget to the printer */

void
printProc(w, client_data, call_data)
Widget		w;
XtPointer	client_data, call_data;
{
	textElementP	tep = (textElementP) client_data;
	char		*fileName;
	char		printCommand[PATH_NAME_LEN];
	char		errorMessage[MESSAGE_STRING_LEN];
	char		*out, *in, *f;
	Boolean		hasFile = False;

	if (! appResources->allowPrint) {
		return;
	}

	if (tep->displayedFile == NULL) {
		if (! textStringToFile(tep) ) {
			showError(
"Text cannot be printed because a temporary file cannot be created.");
			return;
		}
	}

	fileName = tep->displayedFile;
	
	/* everywhere the print command string contains the letters
	   '%s', the filename is substituted.  Other uses of '%'
	   are not special.  If the print command does not contain
	   at least one '%s', then the filename is added at the end
	   after a space. */

	out = printCommand,
	in  = appResources->printCommand;
	while(*in != NULL) {
		if (*in != PERCENT) {
			*(out++) = *(in++);
		} else {
			++in;
			if (*in == 's') {
				++in;
				f = fileName;
				while (*f != NULL) {
					*(out++) = *(f++);
				}
				hasFile = True;
			} else {
				*(out++) = PERCENT;
				*(out++) = *(in++);
			}
		}
	}

	if (! hasFile) {
		*(out++) = ' ';
		f = fileName;
		while (*f != NULL) {
			*(out++) = *(f++);
		}
	}

	*(out++) = '\0';

	if (system(printCommand) == 127) {
		sprintf (errorMessage,
			"The print command could not be executed:\n%s",
			printCommand);
		showError(errorMessage);
	}

	return;
}



/* saveProc
   save the contents of a text widget to a file */

void
saveProc(w, client_data, call_data)
Widget		w;
XtPointer	client_data, call_data;
{
	textElementP	tep = (textElementP) client_data;

	if (! appResources->allowSave) {
		return;
	}

	if (tep->displayedFile == NULL) {
		if (! textStringToFile(tep) ) {
			showError(
"Text cannot be saved because a temporary file cannot be created.");
			return;
		}
	}

	if (tep == NULL) return;
	
	saveRequest(tep->textShell, tep->displayedFile);
}


/* doneProc
   Done showing text file in a popup, clean up */

void
doneProc(w, client_data, call_data)
Widget		w;
XtPointer	client_data, call_data;
{
	int		i;
	textElementP	tep = (textElementP) client_data;

	XtPopdown(tep->textShell);

	if (tep == NULL) return;

	if (tep->deleteFile)
		if (unlink(tep->displayedFile) != 0) {
			fprintf (stderr,
		"Warning: a gopher internal file could not be removed.\n");
			fprintf (stderr,
			    "         This may indicate a system error.\n");
		}

	tep->used          = False;
	if (tep->displayedFile != NULL) {
		XtFree(tep->displayedFile);
		tep->displayedFile = NULL;
	}
	freeWordList(tep->wordList);
	tep->wordList      = NULL;
	tep->deleteFile    = False;
	free(tep->stringValue);		/* free(), not XtFree() */
	tep->stringValue   = NULL;

#ifdef XGOPHER_X11R4
	/* the following should not be necessary, but some R4 versions
	   seem to not really free their string value! */

	{
		Cardinal        n;
		Arg             args[3];

		n = 0;
		XtSetArg(args[n], XtNtype, XawAsciiString);  n++;
		XtSetArg(args[n], XtNstring, "");  n++;
		XtSetValues(tep->textDisplay, args, n);
	}
#endif  /* XGOPHER_X11R4 */

	return;
}


/* cleanUpTextProc
   Allow a final chance to remove all the temp files text knows about. */

void
cleanUpTextProc()
{
	int		i;
	textElementP	tep;


	for (tep=textElementHead; tep != NULL; tep = tep->next) 
		if (tep->used && tep->deleteFile)
			(void) unlink(tep->displayedFile);
	
	return;
}


/* createTextShell
   Build a text popup */

textElementP
createTextShell(topLevel)
Widget	topLevel;
{
	Widget		doneButton, pageDownButton, pageUpButton, 
			printButton, saveButton;
	Widget		buttonBox, textForm;
	Arg		args[10];
	Cardinal	n;
	int		i, w, h;
	textElementP	tep;
	Widget		kwSink;


	if ((tep = (textElementP) malloc(sizeof(textElement))) == NULL) {
	    showError("Too many windows are already open to show this file.");
	    return NULL;
	}
	tep->next = textElementHead;
	textElementHead = tep;
	tep->used = True;

	/* create TEXT display popup */
		n=0;
		XtSetArg(args[n], XtNtitle, "Gopher Text");  n++;
	tep->textShell = XtCreatePopupShell("textShell",
					applicationShellWidgetClass,
					topLevel, args, n);
	
	
	/* create TEXT FORM */
		n=0;
	textForm = XtCreateManagedWidget("textForm", formWidgetClass,
					tep->textShell, args, n);

	/* create BUTTON BOX */
		n=0;
	buttonBox = XtCreateManagedWidget("textButtonBox", boxWidgetClass,
					textForm, args, n);

	/* create TEXT display */
		n=0;
		XtSetArg(args[n], XtNstring, "");  n++;
		XtSetArg(args[n], XtNeditType, XawtextRead);  n++;
		XtSetArg(args[n], XtNscrollVertical,
					XawtextScrollWhenNeeded);  n++;
	tep->textDisplay = XtCreateManagedWidget("textDisplay",
					asciiTextWidgetClass,
					textForm, args, n);
		setTextWidgetSize(tep->textDisplay, 80, 24);

		/* add the key word sink */
		{
#define			TAB_COUNT	32	/* same as in AsciiText.c */
			Widget	kwSink;
			int	i, tabs[TAB_COUNT], tab;

			kwSink = XtCreateWidget("kwSink",
					keyWSinkObjectClass,
					tep->textDisplay, args, n);

			for (i=0, tab=0 ; i < TAB_COUNT ; i++)
				tabs[i] = (tab += 8);
			XawTextSinkSetTabs(kwSink, TAB_COUNT, tabs);

			n=0;
			XtSetArg(args[n], XtNtextSink, kwSink);  n++;
			XtSetValues(tep->textDisplay, args, n);
		}


	/* create DONE button */
	
		n=0;
	doneButton = XtCreateManagedWidget("textDone", commandWidgetClass,
					buttonBox, args, n);
		XtAddCallback(doneButton, XtNcallback, doneProc, tep);

	/* create PAGE_DOWN button */
	
		n=0;
#ifndef XGOPHER_X11R4
		XtSetArg(args[n], XtNleftBitmap, pageDownPixmap);  n++;
#endif
	pageDownButton = XtCreateManagedWidget("textPageDown",
					commandWidgetClass,
					buttonBox, args, n);
		XtAddCallback(pageDownButton, XtNcallback,
					pageDownProc, tep->textDisplay);

	/* create PAGE_UP button */
	
		n=0;
#ifndef XGOPHER_X11R4
		XtSetArg(args[n], XtNleftBitmap, pageUpPixmap);  n++;
#endif
	pageUpButton = XtCreateManagedWidget("textPageUp", commandWidgetClass,
					buttonBox, args, n);
		XtAddCallback(pageUpButton, XtNcallback,
					pageUpProc, tep->textDisplay);

	/* create PRINT button */
	
	if (appResources->allowPrint) {
			n=0;
		printButton = XtCreateManagedWidget("textPrint",
						commandWidgetClass,
						buttonBox, args, n);
			XtAddCallback(printButton, XtNcallback,
						printProc, tep);
	}

	/* create SAVE button */
	
	if (appResources->allowSave) {
			n=0;
		saveButton = XtCreateManagedWidget("textSave",
						commandWidgetClass,
						buttonBox, args, n);
			XtAddCallback(saveButton, XtNcallback,
						saveProc, tep);
	}

	return tep;
}


/* displayTempFile
   Load a text popup with a temp file */

void
displayTempFile(topLevel, title, fileName)
Widget	topLevel;
char	*title;
char	*fileName;
{
	Arg		args[10];
	Cardinal	n;
	char		*textFileName;
	textElementP	tep;
	Widget		kwSink;


	for (tep=textElementHead; (tep!=NULL && tep->used); tep = tep->next);

	if (tep == NULL) {
		if ((tep = createTextShell(topLevel)) == NULL) return;
	}

	if ((textFileName = (char *) XtMalloc(strlen(fileName)+1)) ==
								NULL) {
		showError("Unable to allocate additional memory.");
		return;
	}
	strcpy(textFileName, fileName);
	tep->displayedFile = textFileName;
	tep->deleteFile    = True;
	tep->used = True;
	tep->wordList = NULL;
	tep->stringValue = NULL;

	/* set keyword resource in text sink */

	n = 0;
	XtSetArg(args[n], XtNtextSink, &kwSink);  n++;
	XtGetValues(tep->textDisplay, args, n);

	n = 0;
	XtSetArg(args[n], XtNwordList, NULL);  n++;
	XtSetValues(kwSink, args, n);

	/* set title and file name */

	n = 0;
	XtSetArg(args[n], XtNtitle, title);  n++;
	XtSetValues(tep->textShell, args, n);

	n = 0;
	XtSetArg(args[n], XtNtype, XawAsciiFile);  n++;
	XtSetArg(args[n], XtNstring, textFileName);  n++;
	XtSetValues(tep->textDisplay, args, n);

	XtPopup(tep->textShell, XtGrabNone);
	return;
}


/* displayIndexTempFile
   Load a text popup with a temp file and index words for highlighting */

void
displayIndexTempFile(topLevel, title, fileName, indexString)
Widget	topLevel;
char	*title;
char	*fileName;
char	*indexString;
{
	Arg		args[10];
	Cardinal	n;
	char		*textFileName;
	textElementP	tep;
	Widget		kwSink;

	for (tep=textElementHead; (tep!=NULL && tep->used); tep = tep->next);

	if (tep == NULL) {
		if ((tep = createTextShell(topLevel)) == NULL) return;
	}

	if ((textFileName = (char *) XtMalloc(strlen(fileName)+1)) ==
								NULL) {
		showError("Unable to allocate additional memory.");
		return;
	}
	strcpy(textFileName, fileName);
	tep->displayedFile = textFileName;
	tep->deleteFile    = True;
	tep->used = True;
	tep->stringValue = NULL;


	/* set keyword resource in text sink */

	n = 0;
	XtSetArg(args[n], XtNtextSink, &kwSink);  n++;
	XtGetValues(tep->textDisplay, args, n);

	n = 0;
	if (indexString == NULL) {
		tep->wordList = NULL;
		XtSetArg(args[n], XtNwordList, NULL);  n++;
	} else {
		tep->wordList = makeWordList(indexString);
		XtSetArg(args[n], XtNwordList, tep->wordList);  n++;
	}
	XtSetValues(kwSink, args, n);

	/* set title and file name */

	n = 0;
	XtSetArg(args[n], XtNtitle, title);  n++;
	XtSetValues(tep->textShell, args, n);

	n = 0;
	XtSetArg(args[n], XtNtype, XawAsciiFile);  n++;
	XtSetArg(args[n], XtNstring, textFileName);  n++;
	XtSetValues(tep->textDisplay, args, n);

	XtPopup(tep->textShell, XtGrabNone);
	return;
}


/* displayTextString
   Load a text popup with the contents of a text string */

void
displayTextString(topLevel, title, string)
Widget	topLevel;
char	*title;
char	*string;
{
	Arg		args[10];
	Cardinal	n;
	textElementP	tep;
	Widget		kwSink;

	for (tep=textElementHead; (tep!=NULL && tep->used); tep = tep->next);

	if (tep == NULL) {
		if ((tep = createTextShell(topLevel)) == NULL) return;
	}

	tep->displayedFile = NULL;
	tep->deleteFile    = False;
	tep->used	   = True;
	tep->wordList	   = NULL;
	tep->stringValue   = string;

	/* set keyword resource in text sink */

	n = 0;
	XtSetArg(args[n], XtNtextSink, &kwSink);  n++;
	XtGetValues(tep->textDisplay, args, n);

	n = 0;
	XtSetArg(args[n], XtNwordList, NULL);  n++;
	XtSetValues(kwSink, args, n);

	/* set title and file name */

	n = 0;
	XtSetArg(args[n], XtNtitle, title);  n++;
	XtSetValues(tep->textShell, args, n);

	n = 0;
	XtSetArg(args[n], XtNtype, XawAsciiString);  n++;
	XtSetArg(args[n], XtNstring, string);  n++;
	XtSetValues(tep->textDisplay, args, n);

	XtPopup(tep->textShell, XtGrabNone);
	return;
}
