#line 15 "postscript.c-nw"
static char copyright[] = "Copyright NBBI, Den Haag, 1995";
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/ScrolledW.h>
#include <Xm/SeparatoG.h>
#include <w3a.h>
#include <str.h>
#include "Ghostview.h"

#define XtSetFloatArg(arg, n, d) \
    do { \
        if (sizeof(float) > sizeof(XtArgVal)) { \
            XtSetArg(arg, n, &(d)); \
        } else { \
            XtArgVal *ld = (XtArgVal *)&(d); \
            XtSetArg(arg, n, *ld); \
        } \
    } while (0)

typedef struct {
    Widget w;                                   /* Ghostview widget */
    char *filename;                             /* Temp file */
    FILE *f;                                    /* Temp file */
    int page;                                   /* Page number */
} *Info;


typedef struct _Assoc {                         /* Keeps id/Info pairs */
    long id;
    Info b;
    struct _Assoc *next;
} *Assoc;

static Assoc assoclist = NULL;


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

/* delete -- delete an ID/Info combination */
static void delete(long id)
{
    Assoc g, h;
    assert(assoclist);
    if (assoclist->id == id) {
        h = assoclist; assoclist = assoclist->next; dispose(h);
    } else {
        assert(assoclist->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 Info associated with an ID */
static Info find(long id)
{
    Assoc h;
    assert(assoclist);
    for (h = assoclist; h->id != id; h = h->next) assert(h->next);
    return h->b;
}


/* destroy_ghost -- ensure that ghostscript is killed. */
static void destroy_ghost(w, client_data, call_data)
    Widget w;
    XtPointer client_data, call_data;
{
    GhostviewDisableInterpreter((Widget) client_data);
}


/* output -- display output from ghostscript */
static void output(Widget w, XtPointer client_data, XtPointer call_data)
{
    XtAppWarning(XtWidgetToApplicationContext(w), (String) call_data);
}


/* show_page -- start rendering a new page */
static void show_page(Info info)
{
#if 0
    struct stat sbuf;
    int i;

    if (!filename) return;

    /* Unmark current_page as current */
    if (toc_text && (current_page >= 0)) {
        int marker = current_page*toc_entry_length + toc_entry_length-2;
        toc_text[marker] = ' ';
        XawTextInvalidate(toc, marker, marker+1);
    }

    /* If the file has changed, rescan it so that offsets into the file
     * are still correct.  If the file is rescanned, we must setup ghostview
     * again.  Also, force a new copy of ghostscript to start. */
    if (psfile) {
        if (!stat(filename, &sbuf) && mtime != sbuf.st_mtime) {
            fclose(psfile);
            psfile = fopen(filename, "r");
            mtime = sbuf.st_mtime;
            if (oldfilename) XtFree(oldfilename);
            oldfilename = XtNewString(filename);
            new_file(number);
        }
    }

    /* Coerce page number to fall in range */
    if (toc_text) {
        if (number >= doc->numpages) number = doc->numpages - 1;
        if (number < 0) number = 0;
    }

    if (set_new_orientation(number) || set_new_pagemedia(number))
        layout_ghostview();

    if (toc_text) {
        int marker;
        current_page = number;
        XawTextUnsetSelection(toc);
        XawTextSetInsertionPoint(toc, current_page * toc_entry_length);
        marker = current_page*toc_entry_length + toc_entry_length-2;
        toc_text[marker] = '<';
        XawTextInvalidate(toc, marker, marker+1);
        if (GhostviewIsInterpreterReady(page)) {
            GhostviewNextPage(page);
        } else {
            GhostviewEnableInterpreter(page);
            GhostviewSendPS(page, psfile, doc->beginprolog,
                            doc->lenprolog, False);
            GhostviewSendPS(page, psfile, doc->beginsetup,
                            doc->lensetup, False);
        }
        if (doc->pageorder == DESCEND)
            i = (doc->numpages - 1) - current_page;
        else
            i = current_page;
        GhostviewSendPS(page, psfile, doc->pages[i].begin,
                        doc->pages[i].len, False);
    } else {
#endif
        if (!GhostviewIsInterpreterRunning(info->w))
            GhostviewEnableInterpreter(info->w);
        else if (GhostviewIsInterpreterReady(info->w))
            GhostviewNextPage(info->w);
        else
            XBell(XtDisplay(info->w), 0);
#if 0
    }

    if (toc_text) {
        XtSetSensitive(prevbutton, current_page != 0);
        XtSetSensitive(nextbutton, current_page != doc->numpages-1);
        XtSetSensitive(showbutton, True);
    }
#endif
}


/* next_page -- callback for next page button */
static void next_page(Widget w, XtPointer client_data, XtPointer call_data)
{
    Info info = (Info) client_data;
    info->page++;
    show_page(info);
}


/* to_start -- callback for to-start button */
static void to_start(Widget w, XtPointer client_data, XtPointer call_data)
{
    Info info = (Info) client_data;

    GhostviewDisableInterpreter(info->w);
    info->page = 0;
    show_page(info);
}


/* zoom_in -- callback for zoom-in button */
static void zoom_in(Widget w, XtPointer client_data, XtPointer call_data)
{
    Info info = (Info) client_data;
    float dpi;
    Arg args[20];
    Cardinal num_args;

    XtVaGetValues(info->w, XtNxdpi, &dpi, NULL);
    dpi *= 1.2;
    num_args = 0;
    XtSetFloatArg(args[num_args], XtNxdpi, dpi); num_args++;
    XtSetFloatArg(args[num_args], XtNydpi, dpi); num_args++;
    XtSetValues(info->w, args, num_args);
    /* show_page(info); */
}


/* zoom_out -- callback for zoom-out button */
static void zoom_out(Widget w, XtPointer client_data, XtPointer call_data)
{
    Info info = (Info) client_data;
    float dpi;
    Arg args[20];
    Cardinal num_args;

    XtVaGetValues(info->w, XtNxdpi, &dpi, NULL);
    dpi /= 1.2;
    num_args = 0;
    XtSetFloatArg(args[num_args], XtNxdpi, dpi); num_args++;
    XtSetFloatArg(args[num_args], XtNydpi, dpi); num_args++;
    XtSetValues(info->w, args, num_args);
    /* show_page(info); */
}


static void nyi(Widget w, XtPointer client_data, XtPointer call_data)
{
    XtAppWarning(XtWidgetToApplicationContext(w),
                 "This function of the Postscript viewer\n\
is not yet implemented,\n\
sorry.");
}


/* initPostScript -- initialize class variables for the viewer */
EXPORT Bool initPostscript(char ***mime_types, int *nrtypes, float **qual)
{
    static char *types[] = {"application/postscript"};
    static float pref[] = {0.5};

    *mime_types = types;
    *nrtypes = XtNumber(types);
    *qual = pref;
    return TRUE;                                /* Nothing to initialize */
}
#line 265 "postscript.c-nw"
/* openPostScript -- open a PostScript viewer */
EXPORT Bool openPostscript(const W3ADocumentInfo doc, W3AWindow area, long id)
{
    Info info;
    Widget form, prev, next, scroll, zoomin, zoomout, tostart, toend;

    new(info);
    store(info, id);
    info->filename = newstring(tmpnam(NULL));
    info->f = fopen(info->filename, "w");
    info->page = 0;

    form = XtVaCreateManagedWidget
        ("postscript", xmFormWidgetClass, area, NULL);
    prev = XtVaCreateManagedWidget
        ("prev", xmPushButtonWidgetClass, form,
         XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, 50,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    next = XtVaCreateManagedWidget
        ("next", xmPushButtonWidgetClass, form,
         XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, prev,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    zoomout = XtVaCreateManagedWidget
        ("zoom-out", xmPushButtonWidgetClass, form,
         XmNrightAttachment, XmATTACH_FORM,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    zoomin = XtVaCreateManagedWidget
        ("zoom-in", xmPushButtonWidgetClass, form,
         XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, zoomout,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    tostart = XtVaCreateManagedWidget
        ("to-start", xmPushButtonWidgetClass, form,
         XmNleftAttachment, XmATTACH_FORM,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    toend = XtVaCreateManagedWidget
        ("to-end", xmPushButtonWidgetClass, form,
         XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, tostart,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
#if 0
    (void) XtVaCreateManagedWidget
        ("separator", xmSeparatorGadgetClass, form,
         XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, next,
         XmNrightAttachment, XmATTACH_WIDGET, XmNleftWidget, tostart,
         XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, next,
         XmNbottomAttachment, XmATTACH_FORM,
         XmNorientation, XmVERTICAL,
         NULL);
    (void) XtVaCreateManagedWidget
        ("separator", xmSeparatorGadgetClass, form,
         XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, toend,
         XmNrightAttachment, XmATTACH_WIDGET, XmNleftWidget, zoomin,
         XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET, XmNtopWidget, next,
         XmNbottomAttachment, XmATTACH_FORM,
         XmNorientation, XmVERTICAL,
         NULL);
#endif
    scroll = XtVaCreateManagedWidget
        ("scrollledwindow", xmScrolledWindowWidgetClass, form,
         XmNtopAttachment, XmATTACH_FORM,
         XmNleftAttachment, XmATTACH_FORM,
         XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, next,
         XmNrightAttachment, XmATTACH_FORM,
         XmNscrollingPolicy, XmAUTOMATIC,
         NULL);
    info->w = XtVaCreateManagedWidget
        ("canvas", ghostviewWidgetClass, scroll,
         XtNfilename, info->filename,
         NULL);
    XtAddCallback(info->w, XtNdestroyCallback, destroy_ghost, info->w);
    XtAddCallback(info->w, XtNoutputCallback, output, NULL);
    XtAddCallback(next, XmNactivateCallback, next_page, info);
    XtAddCallback(prev, XmNactivateCallback, nyi, info);
    XtAddCallback(tostart, XmNactivateCallback, to_start, info);
    XtAddCallback(toend, XmNactivateCallback, nyi, info);
    XtAddCallback(zoomin, XmNactivateCallback, zoom_in, info);
    XtAddCallback(zoomout, XmNactivateCallback, zoom_out, info);
    return TRUE;
}


/* writeExtern -- copy data to external viewer */
EXPORT int writePostscript(long id, const char *buf, size_t nchars)
{
    Info info = find(id);
    int n;

    if (nchars != 0) {
        while ((n = fwrite(buf, 1, nchars, info->f)) == -1 && errno == EINTR) ;
        return n;
    } else {                                    /* End of input */
        
        return fclose(info->f);
    }
}


/* closeExtern -- close external viewer */
EXPORT Bool closePostscript(long id)
{
    Info info = find(id);

    (void) fclose(info->f);
    (void) unlink(info->filename);
    XtDestroyWidget(info->w);
    dispose(info);
    delete(id);
    return TRUE;
}


/* infoExtern -- change document info, if needed */
EXPORT Bool infoPostscript(long id, W3ADocumentInfo *info)
{
    return TRUE;                                /* No changes */
}


EXPORT void eventPostscript(long id, long sourceid, long eventtype, void *params)
{
    /* Doesn't handle W3A events */
}