/*
 * Copyright 1991 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */
/*
 * xditview -- 
 *
 *   Display ditroff output in an X window
 */

#ifndef SABER
#ifndef lint
static char rcsid[] = "$XConsortium: xditview.c,v 1.17 89/12/10 17:05:08 rws Exp $";
#endif /* lint */
#endif /* SABER */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>

#include <stdbool.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>

#include "Dvi.h"
#include "groff_version.h"

#include "xdit.bm"
#include "xdit_mask.bm"

#ifdef NEED_DECLARATION_POPEN
FILE *popen(const char *, const char *);
#endif /* NEED_DECLARATION_POPEN */

#ifdef NEED_DECLARATION_PCLOSE
int pclose (FILE *);
#endif /* NEED_DECLARATION_PCLOSE */

typedef void (*MakePromptFunc)(const char *);

static String fallback_resources[] = {
#include "GXditview-ad.h"
    NULL
};

static struct app_resources {
    char *print_command;
    char *filename;
} app_resources;

#define offset(field) XtOffset(struct app_resources *, field)

/* Application resources. */

static XtResource resources[] = {
  {(String)"printCommand", (String)"PrintCommand", (String)XtRString,
   sizeof(char*), offset(print_command), (String)XtRString, NULL},
  {(String)"filename", (String)"Filename", (String)XtRString,
   sizeof(char*), offset(filename), (String)XtRString, NULL},
};

#undef offset

/* Command line options table.  Only resources are entered here...there is a
   pass over the remaining options after XrmParseCommand is let loose. */

static XrmOptionDescRec options[] = {
{(char *)"-page", (char *)"*dvi.pageNumber",
 XrmoptionSepArg, NULL},
{(char *)"-backingStore", (char *)"*dvi.backingStore",
 XrmoptionSepArg, NULL},
{(char *)"-resolution", (char *)"*dvi.resolution",
 XrmoptionSepArg, NULL},
{(char *)"-printCommand", (char *)".printCommand",
 XrmoptionSepArg, NULL},
{(char *)"-filename", (char *)".filename",
 XrmoptionSepArg, NULL},
{(char *)"-noPolyText", (char *)"*dvi.noPolyText",
 XrmoptionNoArg, (XPointer)"TRUE"},
};

static char current_print_command[1024];

static char	current_file_name[1024];
static FILE	*current_file;

/*
 * Report the syntax for calling xditview.
 */

static void
Syntax(const char *progname, bool had_error)
{
	FILE *stream = stdout;
	if (had_error)
		stream = stderr;
	(void) fprintf (stream, "usage: %s [X-toolkit-option]"
			" [-backingStore backing-store-type]"
			" [-filename file]"
// See draw.c:FlushCharCache().
#if 0
			" [-noPolyText]"
#endif
			" [-page page-number]"
			" [-printCommand command]"
			" [-resolution resolution]"
			" [file]\n", progname);
	(void) fprintf (stream, "usage: %s {-version | --version}\n",
			progname);
	(void) fprintf (stream, "usage: %s {-help | --help}\n",
			progname);
	if (had_error)
		exit(EXIT_FAILURE);
	else
		exit(EXIT_SUCCESS);
}

static void	NewFile (const char *);
static void	SetPageNumber (int);
static Widget	toplevel, paned, viewport, dvi;
static Widget	page;
static Widget	simpleMenu;

static void	NextPage(Widget, XtPointer, XtPointer);
static void	PreviousPage(Widget, XtPointer, XtPointer);
static void	SelectPage(Widget, XtPointer, XtPointer);
static void	OpenFile(Widget, XtPointer, XtPointer);
static void	Quit(Widget, XtPointer, XtPointer);
static void	Print(Widget, XtPointer, XtPointer);

static struct menuEntry {
    const char		*name;
    XtCallbackProc	function;
} menuEntries[] = {
    {"nextPage",	NextPage},
    {"previousPage",	PreviousPage},
    {"selectPage",	SelectPage},
    {"print",		Print},
    {"openFile",	OpenFile},
    {"quit",		Quit},
};

static void	NextPageAction(Widget, XEvent *, String *, Cardinal *);
static void	PreviousPageAction(Widget, XEvent *, String *, Cardinal *);
static void	SelectPageAction(Widget, XEvent *, String *, Cardinal *);
static void	OpenFileAction(Widget, XEvent *, String *, Cardinal *);
static void	QuitAction(Widget, XEvent *, String *, Cardinal *);
static void	AcceptAction(Widget, XEvent *, String *, Cardinal *);
static void	CancelAction(Widget, XEvent *, String *, Cardinal *);
static void	PrintAction(Widget, XEvent *, String *, Cardinal *);
static void	RerasterizeAction(Widget, XEvent *, String *, Cardinal *);

static void	MakePrompt(Widget, const char *, MakePromptFunc, const char *);

XtActionsRec xditview_actions[] = {
    {(String)"NextPage",	NextPageAction},
    {(String)"PreviousPage",	PreviousPageAction},
    {(String)"SelectPage",	SelectPageAction},
    {(String)"Print",		PrintAction},
    {(String)"OpenFile",	OpenFileAction},
    {(String)"Rerasterize",	RerasterizeAction},
    {(String)"Quit",		QuitAction},
    {(String)"Accept",		AcceptAction},
    {(String)"Cancel",		CancelAction},
};

#define MenuNextPage		0
#define MenuPreviousPage	1
#define MenuSelectPage		2
#define MenuPrint		3
#define MenuOpenFile		4
#define	MenuQuit		5

static char	pageLabel[256] = "Page <none>";

int main(int argc, char **argv)
{
    char	    *file_name = 0;
    Cardinal	    i;
    static Arg	    labelArgs[] = {
			{XtNlabel, (XtArgVal) pageLabel},
    };
    XtAppContext    xtcontext;
    Arg		    topLevelArgs[2];
    Widget          entry;
    Arg		    pageNumberArgs[1];
    int		    page_number;

    toplevel = XtAppInitialize(&xtcontext, "GXditview",
			    options, XtNumber (options),
 			    &argc, argv, fallback_resources, NULL, 0);
    if (argc > 2)
	Syntax(argv[0], true /* had error */);
    else if (argc == 2) {
	if ((strcmp(argv[1], "-help") == 0)
	    || (strcmp(argv[1], "--help") == 0))
		Syntax(argv[0], false /* did not have error */);
	else if ((strcmp(argv[1], "-version") == 0)
	    || (strcmp(argv[1], "--version") == 0)) {
		(void) printf("GNU gxditview (groff) version %s\n",
			      Version_string);
		exit(EXIT_SUCCESS);
	}
    }

    XtGetApplicationResources(toplevel, (XtPointer)&app_resources,
			      resources, XtNumber(resources),
			      NULL, (Cardinal) 0);
    if (app_resources.print_command)
	strcpy(current_print_command, app_resources.print_command);

    XtAppAddActions(xtcontext, xditview_actions, XtNumber (xditview_actions));

    XtSetArg (topLevelArgs[0], XtNiconPixmap,
	      XCreateBitmapFromData (XtDisplay (toplevel),
				     XtScreen(toplevel)->root,
				     (char *)xdit_bits,
				     xdit_width, xdit_height));
				    
    XtSetArg (topLevelArgs[1], XtNiconMask,
	      XCreateBitmapFromData (XtDisplay (toplevel),
				     XtScreen(toplevel)->root,
				     (char *)xdit_mask_bits, 
				     xdit_mask_width, xdit_mask_height));
    XtSetValues (toplevel, topLevelArgs, 2);
    if (argc > 1)
	file_name = argv[1];

    /*
     * create the menu and insert the entries
     */
    simpleMenu = XtCreatePopupShell ("menu", simpleMenuWidgetClass, toplevel,
				    NULL, 0);
    for (i = 0; i < XtNumber (menuEntries); i++) {
	entry = XtCreateManagedWidget(menuEntries[i].name, 
				      smeBSBObjectClass, simpleMenu,
				      NULL, (Cardinal) 0);
	XtAddCallback(entry, XtNcallback, menuEntries[i].function, NULL);
    }

    paned = XtCreateManagedWidget("paned", panedWidgetClass, toplevel,
				    NULL, (Cardinal) 0);
    viewport = XtCreateManagedWidget("viewport", viewportWidgetClass, paned,
				     NULL, (Cardinal) 0);
    dvi = XtCreateManagedWidget ("dvi", dviWidgetClass, viewport, NULL, 0);
    page = XtCreateManagedWidget ("label", labelWidgetClass, paned,
					labelArgs, XtNumber (labelArgs));
    XtSetArg (pageNumberArgs[0], XtNpageNumber, &page_number);
    XtGetValues (dvi, pageNumberArgs, 1);
    if (file_name)
	NewFile (file_name);
    /* NewFile modifies current_file_name, so do this here. */
    if (app_resources.filename)
	strcpy(current_file_name, app_resources.filename);
    XtRealizeWidget (toplevel);
    if (file_name)
	SetPageNumber (page_number);
    XtInstallAllAccelerators(paned,paned);
    XtAppMainLoop(xtcontext);
    return 0;
}

static void
SetPageNumber (int number)
{
    Arg	arg[2];
    int	actual_number, last_page;

    XtSetArg (arg[0], XtNpageNumber, number);
    XtSetValues (dvi, arg, 1);
    XtSetArg (arg[0], XtNpageNumber, &actual_number);
    XtSetArg (arg[1], XtNlastPageNumber, &last_page);
    XtGetValues (dvi, arg, 2);
    if (actual_number == 0)
	sprintf (pageLabel, "Page <none>");
    else if (last_page > 0)
	sprintf (pageLabel, "Page %d of %d", actual_number, last_page);
    else
	sprintf (pageLabel, "Page %d", actual_number);
    XtSetArg (arg[0], XtNlabel, pageLabel);
    XtSetValues (page, arg, 1);
}

static void
SelectPageNumber (const char *number_string)
{
	SetPageNumber (atoi(number_string));
}

static int hadFile = 0;

static void
NewFile (const char *name)
{
    Arg	    arg[2];
    char    *n;
    FILE    *new_file;
    Boolean seek = 0;

    if (current_file) {
	if (!strcmp (current_file_name, "-"))
	    ;
	else if (current_file_name[0] == '|')
	    pclose (current_file);
	else
	    fclose (current_file);
    }
    if (!strcmp (name, "-"))
	new_file = stdin;
    else if (name[0] == '|')
	new_file = popen (name+1, "r");
    else {
	new_file = fopen (name, "r");
	seek = 1;
    }
    if (!new_file) {
	/* XXX display error message */
	return;
    }
    XtSetArg (arg[0], XtNfile, new_file);
    XtSetArg (arg[1], XtNseek, seek);
    XtSetValues (dvi, arg, 2);
    if (hadFile || name[0] != '-' || name[1] != '\0') {
	XtSetArg (arg[0], XtNtitle, name);
	if (name[0] != '/' && (n = strrchr (name, '/')))
	    n = n + 1;
	else
	    n = (char *)name;
	XtSetArg (arg[1], XtNiconName, n);
	XtSetValues (toplevel, arg, 2);
    }
    hadFile = 1;
    SelectPageNumber ("1");
    strcpy (current_file_name, name);
    current_file = new_file;
}

static char fileBuf[1024];

static void
ResetMenuEntry (Widget entry)
{
    Arg	arg[1];

    XtSetArg (arg[0], (String)XtNpopupOnEntry, entry);
    XtSetValues (XtParent(entry) , arg, (Cardinal) 1);
}

/*ARGSUSED*/

static void
NextPage (Widget entry, XtPointer name, XtPointer data)
{
    name = name;	/* unused; suppress compiler warning */
    data = data;

    NextPageAction((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
    ResetMenuEntry (entry);
}

static void
NextPageAction (Widget widget, XEvent *event,
		String *params, Cardinal *num_params)
{
    Arg	args[1];
    int	number;

    XtSetArg (args[0], XtNpageNumber, &number);
    XtGetValues (dvi, args, 1);
    SetPageNumber (number+1);

    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;
}

/*ARGSUSED*/

static void
PreviousPage (Widget entry, XtPointer name, XtPointer data)
{
    name = name;	/* unused; suppress compiler warning */
    data = data;

    PreviousPageAction ((Widget)NULL, (XEvent *) 0, (String *) 0,
			(Cardinal *) 0);
    ResetMenuEntry (entry);
}

static void
PreviousPageAction (Widget widget, XEvent *event,
		    String *params, Cardinal *num_params)
{
    Arg	args[1];
    int	number;

    XtSetArg (args[0], XtNpageNumber, &number);
    XtGetValues (dvi, args, 1);
    SetPageNumber (number-1);

    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;
}

/* ARGSUSED */

static void
SelectPage (Widget entry, XtPointer name, XtPointer data)
{
    name = name;	/* unused; suppress compiler warning */
    data = data;

    SelectPageAction ((Widget)NULL, (XEvent *) 0, (String *) 0,
		      (Cardinal *) 0);
    ResetMenuEntry (entry);
}

static void
SelectPageAction (Widget widget, XEvent *event,
		  String *params, Cardinal *num_params)
{
    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;

    MakePrompt (toplevel, "Page number", SelectPageNumber, "");
}


static void
DoPrint (const char *name)
{
    FILE *print_file;
    RETSIGTYPE (*handler)(int);

    /* Avoid dying because of an invalid command. */
    handler = signal(SIGPIPE, SIG_IGN);

    print_file = popen(name, "w");
    if (!print_file)
	/* XXX print error message */
	return;
    DviSaveToFile(dvi, print_file);
    pclose(print_file);
    signal(SIGPIPE, handler);
    strcpy(current_print_command, name);
}

static void
RerasterizeAction (Widget widget, XEvent *event,
		   String *params, Cardinal *num_params)
{
    Arg	args[1];
    int	number;

    if (current_file_name[0] == 0) {
	/* XXX display an error message */
	return;
    } 
    XtSetArg (args[0], XtNpageNumber, &number);
    XtGetValues (dvi, args, 1);
    NewFile(current_file_name);
    SetPageNumber (number);

    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;
}

/* ARGSUSED */

static void
Print (Widget entry, XtPointer name, XtPointer data)
{
    name = name;	/* unused; suppress compiler warning */
    data = data;

    PrintAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
    ResetMenuEntry (entry);
}

static void
PrintAction (Widget widget, XEvent *event,
	     String *params, Cardinal *num_params)
{
    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;

    if (current_print_command[0])
	strcpy (fileBuf, current_print_command);
    else
	fileBuf[0] = '\0';
    MakePrompt (toplevel, "Print command:", DoPrint, fileBuf);
}


/* ARGSUSED */

static void
OpenFile (Widget entry, XtPointer name, XtPointer data)
{
    name = name;	/* unused; suppress compiler warning */
    data = data;

    OpenFileAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
    ResetMenuEntry (entry);
}

static void
OpenFileAction (Widget widget, XEvent *event,
		String *params, Cardinal *num_params)
{
    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;

    if (current_file_name[0])
	strcpy (fileBuf, current_file_name);
    else
	fileBuf[0] = '\0';
    MakePrompt (toplevel, "File to open:", NewFile, fileBuf);
}

/* ARGSUSED */

static void
Quit (Widget entry, XtPointer closure, XtPointer data)
{
    entry = entry;	/* unused; suppress compiler warning */
    closure = closure;
    data = data;

    QuitAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
}

static void
QuitAction (Widget widget, XEvent *event,
	    String *params, Cardinal *num_params)
{
    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;

    exit (0);
}

Widget		promptShell, promptDialog;
MakePromptFunc	promptfunction;

/* ARGSUSED */
static
void CancelAction (Widget widget, XEvent *event,
		   String *params, Cardinal *num_params)
{
    widget = widget;	/* unused; suppress compiler warning */
    event = event;
    params = params;
    num_params = num_params;

    if (promptShell) {
	XtSetKeyboardFocus(toplevel, (Widget) None);
	XtDestroyWidget(promptShell);
	promptShell = (Widget) 0;
    }
}

static
void AcceptAction (Widget widget, XEvent *event,
		   String *params, Cardinal *num_params)
{
    (*promptfunction)(XawDialogGetValueString(promptDialog));
    CancelAction (widget, event, params, num_params);
}

static void
MakePrompt(Widget centerw, const char *prompt,
	   MakePromptFunc func, const char *def)
{
    static Arg dialogArgs[] = {
	{XtNlabel, 0},
	{XtNvalue, 0},
    };
    Arg valueArgs[1];
    Arg centerArgs[2];
    Position	source_x, source_y;
    Position	dest_x, dest_y;
    Dimension center_width, center_height;
    Dimension prompt_width, prompt_height;
    Widget  valueWidget;
    
    CancelAction ((Widget)NULL, (XEvent *) 0, (String *) 0, (Cardinal *) 0);
    promptShell = XtCreatePopupShell ("promptShell", transientShellWidgetClass,
				      toplevel, NULL, (Cardinal) 0);
    dialogArgs[0].value = (XtArgVal)prompt;
    dialogArgs[1].value = (XtArgVal)def;
    promptDialog = XtCreateManagedWidget( "promptDialog", dialogWidgetClass,
		    promptShell, dialogArgs, XtNumber (dialogArgs));
    XawDialogAddButton(promptDialog, "accept", NULL, (XtPointer) 0);
    XawDialogAddButton(promptDialog, "cancel", NULL, (XtPointer) 0);
    valueWidget = XtNameToWidget (promptDialog, "value");
    if (valueWidget) {
    	XtSetArg (valueArgs[0], (String)XtNresizable, TRUE);
    	XtSetValues (valueWidget, valueArgs, 1);
	/*
	 * as resizable isn't set until just above, the
	 * default value will be displayed incorrectly.
	 * rectify the situation by resetting the values
	 */
        XtSetValues (promptDialog, dialogArgs, XtNumber (dialogArgs));
    }
    XtSetKeyboardFocus (promptDialog, valueWidget);
    XtSetKeyboardFocus (toplevel, valueWidget);
    XtRealizeWidget (promptShell);
    /*
     * place the widget in the center of the "parent"
     */
    XtSetArg (centerArgs[0], XtNwidth, &center_width);
    XtSetArg (centerArgs[1], XtNheight, &center_height);
    XtGetValues (centerw, centerArgs, 2);
    XtSetArg (centerArgs[0], XtNwidth, &prompt_width);
    XtSetArg (centerArgs[1], XtNheight, &prompt_height);
    XtGetValues (promptShell, centerArgs, 2);
    source_x = (center_width - prompt_width) / 2;
    source_y = (center_height - prompt_height) / 3;
    XtTranslateCoords (centerw, source_x, source_y, &dest_x, &dest_y);
    XtSetArg (centerArgs[0], XtNx, dest_x);
    XtSetArg (centerArgs[1], XtNy, dest_y);
    XtSetValues (promptShell, centerArgs, 2);
    XtMapWidget(promptShell);
    promptfunction = func;
}

/*
Local Variables:
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 4
c-label-offset: -4
c-tab-always-indent: nil
End:
*/
