/*
 * fchooser.c : Xarchie interface to the FileChooser
 *
 * The FileChooser enforces very little policy. This wrapper adds
 * the following:
 * - The FileChooser is created with several other widgets: a Text
 *   widget, and OK button, and a cancel button. These are created in
 *   the sameparent, but other widgets can also be there.
 * - Selecting a file in the FileChooser just puts the string in the
 *   Text widget. You have to hit Ok to use it.
 * - When ok is clicked or Return typed, the directory from the fileChooser
 *   and the file from the Text are concatenated. If the result is a
 *   directory, the fileChooser is reset to that directory. Otherwise
 *   the callback is called with the filename as call_data.
 * - Leading tildes are expanded in directories entered in the Text widget.
 *
 * Oh yeah, if FILECHOOSER is not defined, ignore all that about it. In that
 * case we just have a Text and two buttons.
 *
 * George Ferguson, ferguson@cs.rochester.edu, 23 Apr 1993.
 * 13 May 1993: Fixes for when FILECHOOSER not defined.
 * 14 May 1993: More of the same (find MAXPATHLEN), initialize dir.
 * 30 Jun 1993: Encore umax fixes from Gerry.Tomlinson@newcastle.ac.uk.
 *
 */
#include "config.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef S_ISDIR
# define S_ISDIR(m) (m & S_IFDIR)	/* Encore umax */
#endif
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/keysym.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>
#ifdef FILECHOOSER
#include <FChooser.h>
#endif
#include "fchooser.h"
#include "xarchie.h"
#include "xutil.h"
#include "tilde.h"
#include "status.h"
#include "alert.h"
#include "debug.h"

/*
 * Functions defined here:
 */
FileChooserInfo *createFileChooser(
#if NeedFunctionPrototypes
    Widget shell,
    Widget parent,
    char *basename,
    FileChooserOkProc okCallback,
    FileChooserCancelProc cancelCallback,
    XtPointer client_data
#endif
);

#ifdef FILECHOOSER
static void fileChooserCallback();
#endif
static void okButtonCallback(),cancelButtonCallback();
static void keyPressEventHandler(),nonmaskableEventHandler();

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

FileChooserInfo *
createFileChooser(shell,parent,basename,okCallback,cancelCallback,client_data)
Widget shell,parent;
char *basename;
FileChooserOkProc okCallback;
FileChooserCancelProc cancelCallback;
XtPointer client_data;
{
    FileChooserInfo *info;
    char name[64];
    Arg args[1];

    info = XtNew(FileChooserInfo);
    info->shell = shell;
    info->okCallback = okCallback;
    info->cancelCallback = cancelCallback;
    info->client_data = client_data;
#ifdef FILECHOOSER
    status0("Initializing File Selector...");
    sprintf(name,"%sFileChooser",basename);
    info->fcw =
	XtCreateManagedWidget(name,xfwfFileChooserWidgetClass,parent,NULL,0);
    XtAddCallback(info->fcw,XtNcallback,fileChooserCallback,(XtPointer)info);
    status0("Done.");
    XtSetArg(args[0],XtNfromVert,info->fcw);
#else
    XtSetArg(args[0],XtNfromVert,NULL);
#endif
    sprintf(name,"%sTextLabel",basename);
    (void)XtCreateManagedWidget(name,labelWidgetClass,parent,args,1);
    sprintf(name,"%sText",basename);
    info->text =
	XtCreateManagedWidget(name,asciiTextWidgetClass,parent,args,1);
    sprintf(name,"%sOkButton",basename);
    info->okButton =
	XtCreateManagedWidget(name,commandWidgetClass,parent,NULL,0);
    XtAddCallback(info->okButton,XtNcallback,okButtonCallback,(XtPointer)info);
    /* Allow Return in the Text to be Ok */
    XtAddEventHandler(info->text,KeyPressMask,False,
		      keyPressEventHandler,(XtPointer)info);
    sprintf(name,"%sCancelButton",basename);
    info->cancelButton =
	XtCreateManagedWidget(name,commandWidgetClass,parent,NULL,0);
    XtAddCallback(info->cancelButton,XtNcallback,
		  cancelButtonCallback,(XtPointer)info);
    /* Allow WM_DELETE_WINDOW to the Shell to be Cancel */
    if (info->shell != NULL)
	XtAddEventHandler(info->shell,NoEventMask,True,
			  nonmaskableEventHandler,(XtPointer)info);
    return(info);
}

#ifdef FILECHOOSER
/*
 * When a selection is made, put the string in the Text item. If background
 * selected, clear the Text item.
 */
/*ARGSUSED*/
static void
fileChooserCallback(w,client_data,call_data)
Widget w;
XtPointer client_data;	/* info */
XtPointer call_data;	/* return struct */
{
    FileChooserInfo *fcinfo = (FileChooserInfo *)client_data;
    XfwfFileChooserReturnStruct *ret =
	(XfwfFileChooserReturnStruct *)call_data;
    Arg args[1];

    DEBUG1("fileChooserCallback: fcinfo=0x%lx\n",fcinfo);
    if (fcinfo == NULL || fcinfo->text == NULL)		/* creating */
	return;
    if (ret->file == NULL) {
	XtSetArg(args[0],XtNstring,"");
    } else {
	XtSetArg(args[0],XtNstring,ret->file);
    }
    XtSetValues(fcinfo->text,args,1);
    DEBUG0("fileChooserCallback: done\n");
}
#endif /* FILECHOOSER */

/*
 * If Ok is clicked, get the directory from the FileChooser (if FILECHOOSER)
 * and get the filename from the Text. Do tilde expansion. If result is
 * a directory, use it in the FileChooser, otherwise invoke the callback
 * passing filename and client_data. Finally free the info.
 */
/*ARGSUSED*/
static void
okButtonCallback(w,client_data,call_data)
Widget w;
XtPointer client_data;	/* info */
XtPointer call_data;	/* not used */
{
    FileChooserInfo *fcinfo = (FileChooserInfo *)client_data;
    static char filename[MAXPATHLEN];
    struct stat statbuf;
    char *dir,*file;

    DEBUG1("okButtonCallback: fcinfo=0x%lx\n",fcinfo);
    filename[0] = '\0';
#ifdef FILECHOOSER
    dir = XfwfFileChooserCurrentDirectory((XfwfFileChooserWidget)(fcinfo->fcw));
    DEBUG1("okButtonCallback: dir=\"%s\"\n",dir);
#else
    dir = "";
#endif
    file = getWidgetString(fcinfo->text);
    if (file == NULL || *file == '\0') {
	alert0("No filename specified!");
	return;
    }
    DEBUG1("okButtonCallback: file=\"%s\"\n",file);
    if (*file == '/') {					/* absolute path */
	strcpy(filename,file);
    } else if (*file == '~') {				/* tilde path */
	strcpy(filename,tildeExpand(file));
    } else {						/* relative to fcw */
	sprintf(filename,"%s%s",dir,file);
    }
    DEBUG1("okButtonCallback: filename=\"%s\"\n",filename);
#ifdef FILECHOOSER
    /* If we got a directory, go open it rather than use it */
    if (stat(filename,&statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
	XfwfFileChooserChangeDirectory((XfwfFileChooserWidget)fcinfo->fcw,
				       filename);
	return;
    }
#endif	
    if (fcinfo->okCallback != NULL) {
	DEBUG0("okButtonCallback: calling okCallback...\n");
	(*(fcinfo->okCallback))(fcinfo,filename,fcinfo->client_data);
    }
    DEBUG0("okButtonCallback: done\n");
}

/*
 * If Cancel is clicked, invoke the callback passing client_data.
 * Finally free the info.
 */
/*ARGSUSED*/
static void
cancelButtonCallback(w,client_data,call_data)
Widget w;
XtPointer client_data;	/* fcinfo */
XtPointer call_data;	/* not used */
{
    FileChooserInfo *fcinfo = (FileChooserInfo *)client_data;

    DEBUG1("cancelButtonCallback: fcinfo=0x%lx\n",fcinfo);
    if (fcinfo->cancelCallback != NULL) {
	DEBUG0("cancelButtonCallback: calling cancelCallback\n");
	(*(fcinfo->cancelCallback))(fcinfo,fcinfo->client_data);
    }
    DEBUG0("cancelButtonCallback: done\n");
}

/*
 * KeyPress event handler for Text item: When Return is typed, act as if
 * OK had been clicked. If this event isn't Return, just let it be
 * dispatched normally.
 */
/*ARGSUSED*/
static void
keyPressEventHandler(w,client_data,event,continue_to_dispatch)
Widget w;
XtPointer client_data;
XEvent *event;
Boolean *continue_to_dispatch;
{
    KeySym keysym;
    char str[8];

    DEBUG1("keyPressHandler: w=0x%x\n",w);
    (void)XLookupString((XKeyEvent*)event,str,sizeof(str),&keysym,NULL);
    if (keysym == XK_Return) {
	DEBUG0("keyPressHandler: calling okButtonCallback...\n");
	okButtonCallback(NULL,client_data,NULL);
    }
    DEBUG0("keyPressHandler: done\n");
}

/*
 * Nonmaskable event handler for Shell: If the event is a ClientMessage
 * of WM_PROTOCOLS then act as if Cancel had been clicked.
 */
/*ARGSUSED*/
static void
nonmaskableEventHandler(w,client_data,event,continue_to_dispatch)
Widget w;
XtPointer client_data;
XEvent *event;
Boolean *continue_to_dispatch;
{
    DEBUG1("nonmaskableHandler: w=0x%x\n",w);
    if (event->type == ClientMessage &&
        event->xclient.data.l[0] == WM_DELETE_WINDOW) {
	DEBUG0("nonmaskableHandler: calling cancelButtonCallback\n");
	cancelButtonCallback(NULL,client_data,NULL);
    }
    DEBUG0("nonmaskableHandler: done\n");
}
