	Save-to-file `viewer'

	This viewer for W3A doesn't display the document it receives,
	but offers to save it to a local file. When [[openSave]] is
	called, it shows a file selection box in which the user can
	type a file name. All data that comes into [[writeSave]] is
	than written to that file.

	The viewer writes the name of the file in the window that the
	browser passes to [[openSave]]. The window is not used
	otherwise.

	When the user doesn't select a file name, but cancels the
	dialog, the [[openSave]] routines returns [[FALSE]] to the
	browser.

	Bert Bos <bert@let.rug.nl>, 10 Oct 1994

<<*>>=
#include <config.h>
#include <Xm/Xm.h>
#include <Xm/FileSB.h>
#include <w3a.h>
#include <str.h>

#define MAXNAMELEN 1024

typedef struct {
    char *filename;
    FILE *f;
    Bool done;
} *SaveInfo;

typedef struct _Assoc {
    long id;
    SaveInfo b;
    struct _Assoc *next;
} *Assoc;

static Assoc assoclist = NULL;

/* store -- store an ID/Buffer combination */
static void store(SaveInfo b, long id)
{
    Assoc h;
    new(h); h->id = id; h->b = b; h->next = assoclist; assoclist = h;
}

/* delete -- delete an ID/Buffer combination */
static void delete(long id)
{
    Assoc g, h;
    assert(assoclist);
    if (assoclist->id == id) {
	h = assoclist; assoclist = assoclist->next; dispose(h);
    } else {
	assert(h->next);
	for (h = assoclist; h->next->id != id; h = h->next) assert(h->next);
	g = h->next; h->next = g->next; dispose(g);
    }
}

/* find -- find the Buffer associated with an ID */
static SaveInfo find(long id)
{
    Assoc h;
    assert(assoclist);
    for (h = assoclist; h->id != id; h = h->next) assert(h->next);
    return h->b;
}

static Widget dialog = NULL;


EXPORT Bool initSave(char ***mime_types, int *nrtypes, float **prefs)
{
    static char *types[] = {"*/*"};
    static float pref[] = {0.1};

    *mime_types = types;
    *nrtypes = 1;
    *prefs = pref;
    dialog = XmCreateFileSelectionDialog(W3Atoplevel(), "save", NULL, 0);
    return dialog != NULL;
}


static void cb(Widget w, XtPointer client_data, XtPointer call_data)
{
    SaveInfo s = (SaveInfo) client_data;
    XmFileSelectionBoxCallbackStruct *info =
	(XmFileSelectionBoxCallbackStruct *) call_data;

    if (info->reason == XmCR_OK) {
	if (XmStringGetLtoR(info->value, XmFONTLIST_DEFAULT_TAG,
			    &s->filename)) {
	    s->f = fopen(s->filename, "w");
	}
    } else {
	assert(info->reason == XmCR_CANCEL);
    }
    s->done = TRUE;
}
@

	After popping up the file selection dialog, the function
	enters an event loop, to wait until the user has made a
	selection. The event loop only processes X events, no
	alternate-input events. This is a subtle point: [[openSave]]
	might have been called from a input handler callback, and we
	don't want that routine to be called again while it is still
	suspended, waiting for [[openSave]].

<<*>>=
EXPORT Bool openSave(const W3ADocumentInfo info, W3AWindow window, long id)
{
    SaveInfo s;
    char filename[MAXNAMELEN];
    XtAppContext app_context;

    new(s);
    store(s, id);
    s->done = FALSE;
    s->f = NULL;
    s->filename = NULL;
    app_context = XtWidgetToApplicationContext(window);
    XtAddCallback(dialog, XmNokCallback, cb, s);
    XtAddCallback(dialog, XmNcancelCallback, cb, s);
    /*
     * TO DO: suggest file name
     */
    XtManageChild(dialog);
    while (!s->done)
	/* Don't allow file/socket input while waiting for the dialog */
	XtAppProcessEvent(app_context, XtIMXEvent);

    XtUnmanageChild(dialog);
    XtRemoveCallback(dialog, XmNokCallback, cb, s);
    XtRemoveCallback(dialog, XmNokCallback, cb, s);
    if (s->f != NULL)
	return TRUE;
    else {
	dispose(s);
	return FALSE;
    }
}


EXPORT int writeSave(long id, const char *buf, size_t nchars)
{
    SaveInfo s = find(id);
    return fwrite(buf, 1, nchars, s->f);
}


EXPORT Bool closeSave(long id)
{
    SaveInfo s = find(id);
    FILE *f = s->f;

    dispose(s->filename);
    dispose(s);
    return fclose(f) != -1;
}


EXPORT void eventSave(long id, long sourceid, long eventtype, void *params)
{
    /* Skip */
}


EXPORT Bool infoSave(long id, W3ADocumentInfo *info)
{
    /* TO DO: return file name as title */
    return TRUE;
}
