/*
 * settings.c : Set program parameters on a popup panel
 *
 * George Ferguson, ferguson@cs.rochester.edu, 22 Oct 1991.
 *
 */
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <EzMenu/EzMenu.h>	
#include <X11/Xaw/Cardinals.h>
#include "xarchie.h"
#include "types.h"
#include "appres.h"
#include "rdgram.h"
#include "alert.h"
#include "confirm.h"

/*
 * Functions declared in this file
 */
void settingsAction(),initSettingsDefaults();
void applySettingsAction(),defaultSettingsAction(),doneSettingsAction();
void setSearchTypeAction(),setSortTypeAction();
void setHostAction(),setNiceLevelAction();
void setSearchTypeNowAction(),setSortTypeNowAction();
void setHostNowAction(),setNiceLevelNowAction();

static void initSettingsShell(),initSettingsItems();
static void addTextEventHandler(),textEventProc();
static void setCurrentSearchType(),setCurrentSortType();
static void setChangedFlag();

/*
 * Data declared in this file
 */
static Widget settingsShell;
static Widget applyButton;
static Widget searchLabel;
static Widget sortLabel;
static Widget setHostText;
static Widget maxHitsText;
static Widget timeoutText;
static Widget retriesText;
static Widget niceText;
static Widget ftpDirText,ftpTypeText;

static SearchType currentSearchType,defSearchType;
static SortType currentSortType,defSortType;
static char *defArchieHost,*defFtpDir,*defFtpType;
static int defMaxHits,defTimeout,defRetries,defNiceLevel;
static Boolean settingsChanged,isPoppedUp;


#define ACTION_PROC(NAME)	void NAME(w,event,params,num_params) \
					Widget w; \
					XEvent *event; \
					String *params; \
					Cardinal *num_params;

/*	-	-	-	-	-	-	-	-	*/
/* External interface (action) procedure */
/*
 * settingsAction() : Pops up (possibly creating) the settings editor,
 *	and fills it with the information from the current values of
 *	the application settings.
 */
/*ARGSUSED*/
ACTION_PROC(settingsAction)
{
    if (settingsShell == NULL) {
	initSettingsShell();
	initSettingsDefaults();
    }
    if (isPoppedUp) {
	XRaiseWindow(display,XtWindow(settingsShell));
    } else {
	initSettingsItems();
	XtPopup(settingsShell,XtGrabNone);
	isPoppedUp = True;
    }
}

/*	-	-	-	-	-	-	-	-	*/
/* Initialization procedures */
/*
 * initSettingsShell() : Create the popup settings editor.
 */
static void
initSettingsShell()
{
    isPoppedUp = False;
    initWidgetsFromString(appResources.settingsWidgets,".settingsWidgets");
    /* check for the necessary widgets and set the global variables */
    if ((settingsShell=XtNameToWidget(toplevel,"*settingsShell")) == NULL)
        fail0("didn't create widget \"settingsShell\"");
    /* set globals for optional widgets */
    applyButton = XtNameToWidget(toplevel,"settingsShell*applyButton");
    searchLabel = XtNameToWidget(toplevel,"settingsShell*searchLabel");
    sortLabel = XtNameToWidget(toplevel,"settingsShell*sortLabel");
    setHostText = XtNameToWidget(toplevel,"settingsShell*setHostText");
    maxHitsText = XtNameToWidget(toplevel,"settingsShell*maxHitsText");
    timeoutText = XtNameToWidget(toplevel,"settingsShell*timeoutText");
    retriesText = XtNameToWidget(toplevel,"settingsShell*retriesText");
    niceText = XtNameToWidget(toplevel,"settingsShell*niceText");
    ftpDirText = XtNameToWidget(toplevel,"settingsShell*ftpDirText");
    ftpTypeText = XtNameToWidget(toplevel,"settingsShell*ftpTypeText");
    /* add event handler for detecting changes */
    addTextEventHandler(setHostText);
    addTextEventHandler(maxHitsText);
    addTextEventHandler(timeoutText);
    addTextEventHandler(retriesText);
    addTextEventHandler(niceText);
    addTextEventHandler(ftpDirText);
    addTextEventHandler(ftpTypeText);
}

static void
addTextEventHandler(w)
Widget w;
{
    if (w != NULL)
	XtAddEventHandler(w,KeyPressMask|ButtonPressMask,False,
			  textEventProc,(XtPointer)NULL);
}

/*	-	-	-	-	-	-	-	-	*/
/*
 * initSettingsDefaults() : Stores away the values of the application
 *	resources at startup for use by the default-settings() action.
 */
void
initSettingsDefaults()
{
    defSearchType = appResources.searchType;
    defSortType = appResources.sortType;
    defArchieHost = XtNewString(appResources.archieHost);
    defMaxHits = appResources.maxHits;
    defTimeout = appResources.timeout;
    defRetries = appResources.retries;
    defNiceLevel = appResources.niceLevel;
    defFtpDir = XtNewString(appResources.ftpDir);
    defFtpType = XtNewString(appResources.ftpType);
}

/*	-	-	-	-	-	-	-	-	*/
/*
 * initSettingsItems() : Sets the values in the settings editor from the
 *	current state of the application resources.
 */
static void
initSettingsItems()
{
    char buf[8];

    setCurrentSearchType(appResources.searchType);
    setCurrentSortType(appResources.sortType);
    setText(setHostText,appResources.archieHost);
    sprintf(buf,"%d",appResources.maxHits);
    setText(maxHitsText,buf);
    sprintf(buf,"%d",appResources.timeout);
    setText(timeoutText,buf);
    sprintf(buf,"%d",appResources.retries);
    setText(retriesText,buf);
    sprintf(buf,"%d",appResources.niceLevel);
    setText(niceText,buf);
    setText(ftpDirText,appResources.ftpDir);
    setText(ftpTypeText,appResources.ftpType);
    setChangedFlag(False);
}

/*	-	-	-	-	-	-	-	-	*/
/* Callback procedures */
/*
 * applyCallback() : Callback for apply button - Set the application resources
 *	from the items on the settings editor panel. Some of these require
 *	special action when changed, and this routine does that.
 */
/*ARGSUSED*/
ACTION_PROC(applySettingsAction)
{
    Arg args[1];
    char *s;
    int n;

    appResources.searchType = currentSearchType;
    appResources.sortType = currentSortType;
    if (setHostText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(setHostText,args,ONE);
	if (strcmp(appResources.archieHost,s) != 0)
	    appResources.archieHost = XtNewString(s); /* memory leak */
    }
    if (maxHitsText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(maxHitsText,args,ONE);
	appResources.maxHits = atoi(s);
    }
    if (timeoutText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(timeoutText,args,ONE);
	appResources.timeout = atoi(s);
    }
    if (retriesText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(retriesText,args,ONE);
	appResources.retries = atoi(s);
    }
    if (niceText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(niceText,args,ONE);
	n = atoi(s);
	if (n < RDGRAM_MIN_PRI)		/* leave -32766 to -32768 alone */
	    n = RDGRAM_MIN_PRI;
	else if (n > RDGRAM_MAX_SPRI)
	    n = RDGRAM_MAX_PRI;
	appResources.niceLevel = n;
    }
    if (ftpDirText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(ftpDirText,args,ONE);
	if (strcmp(appResources.ftpDir,s) != 0)
	    appResources.ftpDir = XtNewString(s); /* memory leak */
    }
    if (ftpTypeText != NULL) {
	XtSetArg(args[0],XtNstring,&s);
	XtGetValues(ftpTypeText,args,ONE);
	if (strcmp(appResources.ftpType,s) != 0)
	    appResources.ftpType = XtNewString(s); /* memory leak */
    }
    setChangedFlag(False);
}

/*
 * defaultCallback() : Callback for default button - Reset the items
 *      to their default values.
 */
/*ARGSUSED*/
ACTION_PROC(defaultSettingsAction)
{
    char buf[8];

    setCurrentSearchType(defSearchType);
    setCurrentSortType(defSortType);
    setText(setHostText,defArchieHost);
    sprintf(buf,"%d",defMaxHits);
    setText(maxHitsText,buf);
    sprintf(buf,"%d",defTimeout);
    setText(timeoutText,buf);
    sprintf(buf,"%d",defRetries);
    setText(retriesText,buf);
    sprintf(buf,"%d",defNiceLevel);
    setText(niceText,buf);
    setText(ftpDirText,defFtpDir);
    setText(ftpTypeText,defFtpType);
    setChangedFlag(True);
}

/*
 * doneCallback() : Callback for done button - Pop down the editor.
 */
/*ARGSUSED*/
ACTION_PROC(doneSettingsAction)
{
    if (!settingsChanged || confirm0("Discard changes to settings?")) {
	XtPopdown(settingsShell);
	isPoppedUp = False;
    }
}

/*
 * setSearchTypeAction() : Action procedure to set the search type.
 */
/*ARGSUSED*/
ACTION_PROC(setSearchTypeAction)
{
    XrmValue from,to;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-search-type()");
	return;
    }
    from.addr = *params;
    from.size = sizeof(String);
    to.addr = NULL;
    XtConvertAndStore(w,XtRString,&from,GfRSearchType,&to);
    if (to.addr != NULL)
	setCurrentSearchType((SearchType)(*(to.addr)));
    setChangedFlag(True);
}

/*
 * setSortTypeAction() : Action procedure to set the sort type.
 */
/*ARGSUSED*/
ACTION_PROC(setSortTypeAction)
{
    XrmValue from,to;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-sort-type()");
	return;
    }
    from.addr = *params;
    from.size = sizeof(String);
    to.addr = NULL;
    XtConvert(w,XtRString,&from,GfRSortType,&to);
    if (to.addr != NULL)
	setCurrentSortType((SortType)(*(to.addr)));
    setChangedFlag(True);
}

/*
 * setHostAction() : Action procedure to set the host.
 */
/*ARGSUSED*/
ACTION_PROC(setHostAction)
{
    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-host()");
	return;
    }
    if (setHostText == NULL) {
	alert0("set-host() has no effect since setHostText was not created");
	return;
    }
    setText(setHostText,*params);
    setChangedFlag(True);
}

/*
 * setNiceLevelAction() : Action procedure to set rdgram_priority
 */
/*ARGSUSED*/
ACTION_PROC(setNiceLevelAction)
{
    char buf[8];
    int n;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-nice-level()");
	return;
    }
    if (niceText == NULL) {
       alert0("set-nice-level() has no effect since niceText was not created");
	return;
    }
    n = atoi(*params);
    if (n < RDGRAM_MIN_PRI) {
	alert1("Nice level too negative: %d",n);
	sprintf(buf,"%d",RDGRAM_MIN_PRI);
	setText(niceText,buf);
    } else if (n > RDGRAM_MAX_PRI) {
	alert1("Nice level too positive: %d",n);
	sprintf(buf,"%d",RDGRAM_MAX_PRI);
	setText(niceText,buf);
    } else {
	setText(niceText,*params);
    }
    setChangedFlag(True);
}

/*	-	-	-	-	-	-	-	-	*/
/* These actions are like their non-Now counterparts, expect that
 * (a) they set appResources immediately rather than waiting for
 *     apply-settings() to be called, and
 * (b) they do not set the changedFlag since they have made the change
 *     globally already.
 * Still, they really aren't meant to be used when the settingsPanel is
 * being displayed.
 */

/*ARGSUSED*/
ACTION_PROC(setSearchTypeNowAction)
{
    XrmValue from,to;
    SearchType type;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-search-type-now()");
	return;
    }
    from.addr = *params;
    from.size = sizeof(String);
    to.addr = NULL;
    XtConvertAndStore(w,XtRString,&from,GfRSearchType,&to);
    if (to.addr != NULL) {
	type = (SearchType)(*(to.addr));
	appResources.searchType = type;
	status1("Set search type to %s",*params);
	setCurrentSearchType(type);
    }
}

/*ARGSUSED*/
ACTION_PROC(setSortTypeNowAction)
{
    XrmValue from,to;
    SortType type;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-sort-type-now()");
	return;
    }
    from.addr = *params;
    from.size = sizeof(String);
    to.addr = NULL;
    XtConvertAndStore(w,XtRString,&from,GfRSortType,&to);
    if (to.addr != NULL) {
	type = (SortType)(*(to.addr));
	appResources.sortType = type;
	status1("Set sort type to %s",*params);
	setCurrentSortType(type);
    }
}

/*ARGSUSED*/
ACTION_PROC(setHostNowAction)
{
    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-host-now()");
	return;
    }
    /* Memory leak */
    appResources.archieHost = XtNewString(*params);
    status1("Set host to %s",*params);
    setText(setHostText,*params);
}

/*ARGSUSED*/
ACTION_PROC(setNiceLevelNowAction)
{
    int n;

    if (*num_params != ONE) {
	alert0("Incorrect number of arguments to set-nice-level-now()");
	return;
    }
    n = atoi(*params);
    if (n < RDGRAM_MIN_PRI) {
	alert1("Nice level too negative: %d -- not set",n);
    } else if (n > RDGRAM_MAX_PRI) {
	alert1("Nice level too positive: %d -- not set",n);
    } else {
	appResources.niceLevel = n;
	status1("Set niceLevel to %d",n);
	setText(niceText,*params);
    }
}

/*	-	-	-	-	-	-	-	-	*/
/*
 * textEventProc() : Called whenever the user types in any Text item.
 *      Note that this does NOT detect, eg., selection pastes, as
 *	documented in the BUGS section of the man page.
 */
/*ARGSUSED*/
static void
textEventProc(w,client_data,event,continue_flag)
Widget w;
XtPointer client_data;
XEvent *event;
Boolean *continue_flag;
{
    setChangedFlag(True);
}

/*	-	-	-	-	-	-	-	-	*/

static void
setCurrentSearchType(type)
SearchType type;
{
    char *s;

    currentSearchType = type;
    switch (type) {
	case GfExact :   s = GfNExact; break;
	case GfSubstr :  s = GfNSubstr; break;
	case GfSubcase : s = GfNSubcase; break;
	case GfRegexp :  s = GfNRegexp; break;
	case GfExactSubstr :  s = GfNExactSubstr; break;
	case GfExactSubcase : s = GfNExactSubcase; break;
	case GfExactRegexp :  s = GfNExactRegexp; break;
    }
    setLabel(searchLabel,s);
}

static void
setCurrentSortType(type)
SortType type;
{
    char *s;

    currentSortType = type;
    switch (type) {
	case GfDefault : s = GfNDefault; break;
	case GfInvdate : s = GfNInvdate; break;
    }
    setLabel(sortLabel,s);
}

static void
setChangedFlag(value)
Boolean value;
{
    if (applyButton != NULL)
	XtSetSensitive(applyButton,value);
    settingsChanged = value;
}
