/*******************************************************************
 *
 * TITLE:       print.c
 * AUTHOR:      Ana Maria P. Guerrero, Cassie Mulnix
 *
 * DESCRIPTION:
 *      This module is part of the the XOpps editor: xopps.  It contains
 *      routines which initialize and control the printing.
 *
 *                              CHANGE HISTORY
 *
 * $Log: print.c,v $
 * Revision 1.20  1994/12/31  04:19:29  kevin
 * fixed memory allocation problem after DialogTextGet
 *
 * Revision 1.19  1994/10/14  22:39:07  maxwell
 * ok_button::temp_change was not initialized before use.
 *
 * Revision 1.18  1994/09/12  21:41:07  cassie
 * set radio according to value in options structure
 * use buffer of declared size for sprintf
 *
 * Revision 1.17  1994/08/10  22:04:59  cassie
 * updated to DIALOG_ constants for DialogFlagSet
 *
 * Revision 1.16  1994/07/14  21:08:03  clm
 * fixed postscript options
 *
 * Revision 1.15  1994/06/16  21:57:27  clm
 * updated to new dialog routines - extra parameter to DialogTextCreate
 *
 * Revision 1.14  1994/06/14  22:49:15  clm
 * changed to use dialogs
 *
 * Revision 1.13  1994/06/01  22:31:21  clm
 * updated command so message doesn't appear on screen
 *
 * Revision 1.12  1994/05/20  23:31:43  clm
 * updated with HP, SGI changes
 *
 * Revision 1.11  1994/05/04  23:50:06  clm
 * fixed declarations of brokenpipe
 *
 * Revision 1.10  1994/05/04  23:34:36  clm
 * ported to ANSI C
 *
 * Revision 1.9  1993/05/27  01:05:53  clm
 * fixed modified value so it is not changed
 *
 * Revision 1.8  1993/05/18  22:39:41  clm
 * added meaningful error messages for check_num and check_day
 *
 * Revision 1.7  1993/05/18  20:58:30  clm
 * added new message.c function
 *
 * Revision 1.6  1992/12/04  18:02:32  clm
 * changed MAX_PLN to npln
 *
 * Revision 1.5  1992/11/09  16:32:41  clm
 * added precedence lines
 *
 * Revision 1.4  1992/10/08  14:48:52  clm
 * fixed printing of "all" pages
 *
 * Revision 1.3  1992/09/24  20:19:18  clm
 * added capability to select what pages to print
 *
 * Revision 1.2  1992/08/27  21:47:48  clm
 * fixed printing capabilities for any printer
 *
 * Revision 1.1  1992/08/27  21:23:44  clm
 * Initial revision
 *
 *
 ***********************************************************************
 *
 * WARNINGS:
 *
 * EXTERNAL CALLABLE COMPONENTS (PUBLIC):
 *
 * GLOBALS:
 *
 * WAIVERS:
 *
 * NOTES:
 *
 * MANPAGE:
 *
 ***********************************************************************/

#ifndef lint
static char rcsid[] =
    "$Id: print.c,v 1.20 1994/12/31 04:19:29 kevin OEL $";
#endif

#include "xopps.h"

#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <setjmp.h>

#define SELECT 0
#define ALL 1
#define PRNTR 0
#define PSCRIPT 1

#define BEWARE1 "File %s exists.\n%s"
#define BEWARE2 "Do you wish to overwrite?"

static XtAppContext app;

static DialogRadio *dest, *pagew;
static DialogText *printer, *file, *directory;
static DialogText *from, *to;

static void verify_fun(Widget, XtPointer, XtPointer);
static void ok_button(void);
static void cancel_button(void);
static void edit_print(Widget);			/* manages dialog box */
static void selectCB(Widget, int, XmToggleButtonCallbackStruct *);
static void destination_toggleCB(Widget, int, XmToggleButtonCallbackStruct *);
static void brokenpipe(int);		/* gets called when a pipe breaks */
static void print_to_PostScript(char *, int, int);
static int print_to_laser(int, int);	/* print to the laser printer */

static jmp_buf env;			/* jump buffer */
static Widget popupprint;             /* parent for print popup! */

static struct {				/* place to save printer options */
    int destination;			/* printer, PostScript */
    int pages_to_print;			/* SELECT pages or ALL */
    char directory[_POSIX_PATH_MAX];	/* directory to save file */
    char name[3][32];			/* name of printer device */
    int from;				/* start at this page */
    int to;				/* end at this page */
} options;

static int file_was_printed;		/* Flag to keep or vanish popup */
XmString pages[7];			/* page list */
static FILE *lp;
static int exit_code;

EXTERN void set_dsp_size(void);
EXTERN void loc_pline(int i, int dir);	/* locates plines for changing pages */
/***********************************************************************
 *
 * FUNCTION:
 *	init_print_dialog() 
 *
 * INPUTS:
 *	parent		- (Widget) parent widget, (the application shell)
 *	popup_display	- (Display *) pointer the display
 *	app_value	- (Application Context) used for the event loop
 *	title		- (char *) title text for popup
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	This function initializes the print box options dialog.  It
 *	Creates the shell, form, label, and text widgets needed for a
 *	popup dialog.  It also sets some default values for things like
 *	the printer name, and file names.  All of the widgets are managed
 *	with the exception of the popup shell.  The shell is managed
 *	when the user selects the print button from the file menu.
 */

void 
init_print_dialog(Widget parent, Display *popup_display, XtAppContext app_value,
    char *title)
{ 
    char *stuff;
    static char *dest_list[] = { "Printer", "PostScript" };
    static char *page_list[] = { "Select", "All" };

    app = app_value;

					/* Set printer defaults */
					/* Set PostScript printer name */
    if ((stuff = getenv("LASERPRINTER")) == NULL)  {
    	strcpy(options.name[PRNTR], "lw");
    } else {
       if (strlen(stuff) > 30) {
	    Warning("Warning! LASERPRINTER name too long.");
    	    strcpy(options.name[PRNTR], "lw");
        } else {
    	    strcpy(options.name[PRNTR], stuff);
	}
    }

    /*
     * set directory
     */
    if (getcwd(options.directory, _POSIX_PATH_MAX) == NULL) {
	Warning("Unable to find directory");
	return;
    }
    
    /*
     * Now create the popup shell 
     * You have set the window manager(wm) protocol callback for the wm
     * "Close" button.  The "cancel_button" callback is now tied to the
     * "Close" wm close button.
     */
    popupprint = DialogInit(parent, "Print Dialog", "print_shell_dialog",
        verify_fun, (XtPointer)DIALOG_CANCEL);

    dest = DialogRadioCreate("Destination: ", 2, 0,
        (XtCallbackProc)destination_toggleCB, DIALOG_OPT_STR_ARY,
            dest_list, NULL);

    printer = DialogTextCreate("Printer: ", 18, 25);
    file = DialogTextCreate("File Name: ", 18, 100);

    directory = DialogTextCreate("Directory: ", 38, 100);

    pagew = DialogRadioCreate("Pages to Print: ", 2, 0,
        (XtCallbackProc)selectCB, DIALOG_OPT_STR_ARY, page_list, NULL);

    DialogAddRow(2);
    from = DialogTextCreate("              From Page: ", 3, 3);
    to = DialogTextCreate(" To Page: ", 3, 3);

    DialogStdButtons(verify_fun, (XtPointer)DIALOG_UPDATE, verify_fun,
        (XtPointer)DIALOG_CANCEL, (XtCallbackProc)ShowHelp, "Print");

}

/***********************************************************************
 *
 * FUNCTION:
 *	popup_print_options()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	The purpose of this function is to set the widgets inside
 * 	of the print dialog box to the correct values.  This gets called
 * 	by the print button callback in filemenu.c.  Once everything
 * 	is set, this function calls edit_print(), which actually
 * 	manages the popup shell.
 */

void 
popup_print_options(void)
{
    char buf[256];
    int page;

    page = current_page;
    options.to =  page;
    options.from =  page;

    getfm_text(buf, FM_ROOT);
    (void)sprintf(options.name[PSCRIPT], "%s.ps", buf);

			/* Unmap directory if PRINTER is destination */
    if (options.destination == PRNTR) {
	if (!XtIsManaged(printer->parent)) {
            XtManageChild(printer->parent);
        }
        if (XtIsManaged(directory->parent)) {
            XtUnmanageChild(directory->parent);
        }
        if (XtIsManaged(file->parent)) {
            XtUnmanageChild(file->parent);
        }
    } else {		/* else show directory if PostScript */
        if (XtIsManaged(printer->parent)) {
            XtUnmanageChild(printer->parent);
        }
        if (!XtIsManaged(directory->parent)) {
            XtManageChild(directory->parent);
        }
        if (!XtIsManaged(file->parent)) {
            XtManageChild(file->parent);
        }
        DialogTextSet(directory, options.directory);
	DialogTextSet(file, buf);
    }

    /*
     * load in page information
     */
    options.to = page;
    options.from = page;
    (void)sprintf(buf, "%d", page);
    DialogTextSet(to, buf);
    DialogTextSet(from, buf);

    /* unmap the TO and FROM if ALL is selected */
    if (options.pages_to_print != SELECT) {
	if (XtIsManaged(to->parent)) {
            XtUnmanageChild(to->parent);
        }
        if (XtIsManaged(from->parent)) {
            XtUnmanageChild(from->parent);
        }
    } else {            /* else allow mapping of TO and FROM */
        if (!XtIsManaged(to->parent)) {
            XtManageChild(to->parent);
        }
        if (!XtIsManaged(from->parent)) {
            XtManageChild(from->parent);
        }
    }

   /* set the widgets */
    DialogTextSet(printer, options.name[options.destination]);
    DialogRadioSet(dest, options.destination);
    DialogRadioSet(pagew, options.pages_to_print);

    edit_print(popupprint);		/* call the function to pop it up */

}

/***********************************************************************
 *
 * FUNCTION:
 *      selectCB()
 *
 * INPUTS:
 *      w               - (Widget) toggle widget id
 *      toggle          - (int) Callback data which shows the toggle
 *      call_data       - (XmToggleButtonCallbackStruct) Contains
 *                        the specific toggle button information
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *      Gets called whenever the selection toggle buttons change
 * 	values.
 */

static void 
selectCB(Widget w, int toggle, XmToggleButtonCallbackStruct *call_data)
{

   if (toggle == SELECT) {    /* map TO and FROM if SELECT pages is true */
        if (!XtIsManaged(to->parent)) {
            XtManageChild(to->parent);
        }
        if (!XtIsManaged(from->parent)) {
            XtManageChild(from->parent);
        }
    } else {                    /* else unmap if ALL is true */
        if (XtIsManaged(to->parent)) {
            XtUnmanageChild(to->parent);
        }
        if (XtIsManaged(from->parent)) {
            XtUnmanageChild(from->parent);
        }
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	destination_toggleCB()
 *
 * INPUTS:
 *      w               - (Widget) toggle widget id 
 *	toggle		- (int) Callback data which shows the toggle 
 *	call_data 	- (XmToggleButtonCallbackStruct) Contains
 *			  toggle button information.
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	This function gets called when any of the destination
 * 	toggles change value.
 * 
 */

static void 
destination_toggleCB(Widget w, int toggle, 
    XmToggleButtonCallbackStruct *call_data)
{
    if (toggle == PRNTR) {      /* unmap directory if PRINTER is destination */
        if (XtIsManaged(file->parent)) {
            XtUnmanageChild(file->parent);
        }
        if (XtIsManaged(directory->parent)) {
            XtUnmanageChild(directory->parent);
        }
        if (!XtIsManaged(printer->parent)) {
            XtManageChild(printer->parent);
        }
	DialogTextSet(printer, options.name[toggle]);   /* set the widgets */
    } else {		  /* map directory if PostScript file or Ascii file  */
        if (!XtIsManaged(file->parent)) {
            XtManageChild(file->parent);
        }
        if (!XtIsManaged(directory->parent)) {
            XtManageChild(directory->parent);
        }
        if (XtIsManaged(printer->parent)) {
            XtUnmanageChild(printer->parent);
        }
	DialogTextSet(directory, options.directory);
	DialogTextSet(file, options.name[toggle]);   /* set the widgets */
    }

}

/***********************************************************************
 *
 * FUNCTION:
 *	ok_button()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 * 	Checks the input and calls the correct function
 * 	to output the file.
 */

static void 
ok_button(void)
{
    int frompage, topage;			/* pages to print */
    char name[512];				/* entire name for file */
    char *buf;					/* buffer for beware */
    int val;					/* temporary int */
    char buffer[256];
    struct stat statbuf;			/* for status function*/
    int printed = DIALOG_ERROR;			/* printing flag */
    int temp_change = 0;
				    	    /* set the correct values */
    options.destination = DialogRadioGet(dest);
    options.pages_to_print = DialogRadioGet(pagew);
    if (options.destination == PSCRIPT) {
	buf = DialogTextGet(file);
    } else {
	buf = DialogTextGet(printer);
    }
    strcpy(options.name[options.destination], buf);
    XtFree(buf);
   
    /*
     * don't read "From / To" if it isn't up
     */
    if (options.pages_to_print == SELECT) {
	val = DialogTextGetInt(from);
	if ((val < 1) || (val > num_pages)) {
            Warning("Improper From Page");
            temp_change |= DIALOG_ERROR;
        } else if (val != options.from) {
            temp_change |= DIALOG_UPDATE;
        }
        printed |= temp_change;
        options.from = val;

        val = DialogTextGetInt(to);
        if ((val < 1) || (val > num_pages)) {
            Warning("Improper To Page");
            temp_change |= DIALOG_ERROR;
        } else if (val != options.to) {
            temp_change |= DIALOG_UPDATE;
        }
        printed |= temp_change;
        options.to = val;

        frompage = options.from;
        topage = options.to;
    } else {
	frompage = 1;
	topage = num_pages;
    }
    
    /*
     * don't read directory if it isn't up
     */
    if (options.destination != PRNTR) {
	buf = DialogTextGet(directory);
        strcpy(options.directory, buf);
	XtFree(buf);
    }
    if (options.directory[0] == '\0') {
	(void)strcpy(name, options.name[options.destination]);
    } else {
	(void)sprintf(name, "%s/%s", options.directory, 
	    options.name[options.destination]);
    }
    (void)sprintf(buffer, BEWARE1, options.name[options.destination], BEWARE2);
    switch (options.destination) {
    case PRNTR:			    /* to laser printer */
	printed = print_to_laser(frompage, topage);
	break;
    case PSCRIPT:			    /* to postscript file */
	if ((stat(name, &statbuf)) || (Beware(buffer))) {
	    print_to_PostScript(name, frompage, topage);
	    printed = 1;
 	}
	break;
    } /* end switch */
    file_was_printed = printed; 
    DialogFlagSet(printed);

}

/***********************************************************************
 *
 * FUNCTION:
 *	cancel_button()
 *
 * INPUTS:
 * 	none
 *
 * OUTPUTS:
 * 	none
 *
 * RETURNS:
 * 	none
 *
 * EXTERNALLY READ:
 * 	none
 *
 * EXTERNALLY MODIFIED:
 * 	none
 *
 * DESCRIPTION:
 *	Sets the file_was_printed to QUIT which will flag edit_print()
 * 	to unmanage the printer option dialog shell.
 */

static void 
cancel_button(void)
{
    file_was_printed = DIALOG_CANCEL;		/* set flag */
    DialogFlagSet(DIALOG_CANCEL);
}

/***********************************************************************
 *
 * FUNCTION:
 *	print_to_PostScript()
 *
 * INPUTS:
 *	name		- (char *) name of file
 * 	startpage	- (int) first page to be printed
 *	endpage		- (int) last page to be printed
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	This function outputs to a PostScript file.
 */

static void 
print_to_PostScript(char *name, int startpage, int endpage)
{
    char text[50];		/* temp buffer */
    char cmd[256];		/* temp buffer for filename */
    int i;			/* temp loop index */
    int currpage;		/* current page of display */

    (void)sprintf(cmd, "%s/%s", options.directory, options.name[PSCRIPT]);
    Message("Creating file: %s", cmd);
    if ((lp = fopen(cmd, "w")) == NULL) {
        (void)sprintf(text, "ERROR: Cannot open PS file: %s", cmd);
        Warning(text);
        return;
    }

    /* 
     * save current page since variable must be updated for set_dsp_size
     */
    currpage = current_page;

    /*
     * cycle through pages
     */
    while (startpage <= endpage) {
	current_page = startpage;
	set_dsp_size();
	for (i = 0; i < Hnobj; i++) {
	    (*(Hobjary[i].opsptr->loc))(i, OBJECT);
            (*(Hobjary[i].opsptr->dsp))(i);
        }
        ren_hline();
        for (i = 0; i < nobj; i++) {
            (*(objary[i].opsptr->loc))(i, OBJECT);
            (*(objary[i].opsptr->dsp))(i);
        }
	for (i = 0; i < npln; i++) {
	    loc_pline(i, OBJECT);
	}
	gr_laser_on(lp);
	draw_outline(TRUE);
	gr_laser_off();
	startpage++;
    }

    /* set back to original status */
    current_page = currpage;
    set_dsp_size();
    for (i = 0; i < Hnobj; i++) {
        (*(Hobjary[i].opsptr->loc))(i, OBJECT);
        (*(Hobjary[i].opsptr->dsp))(i);
    }
    ren_hline();
    for (i = 0; i < nobj; i++) {
        (*(objary[i].opsptr->loc))(i, OBJECT);
        (*(objary[i].opsptr->dsp))(i);
    }
    for (i = 0; i < npln; i++) {
	loc_pline(i, OBJECT);
    }
    fclose(lp);
}

/***********************************************************************
 *
 * FUNCTION:
 *	print_to_laser
 *
 * INPUTS:
 *	startpage, endpage	- (int) range of pages to print 
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 *	This function prints to the laser printer.
 */

static int 
print_to_laser(int startpage, int endpage)
{
    char cmd[50];
    char *printer;
    int i;				/* temp loop index */
    int currpage;			/* current page of display */

    exit_code = TRUE;
    (void)signal(SIGPIPE, brokenpipe); 		/* beware of broken pipes */

    if (!setjmp(env)) {
	printer = options.name[PRNTR];
	if ((printer == NULL) || (printer[0] == '\0')) {
	    printer = "lw";
	}
    	(void)sprintf(cmd, "lp -d%s >/dev/null 2>&1", printer);
        /* 
         * save current page since variable must be updated for set_dsp_size
         */
	currpage = current_page;
        /*
         * cycle through pages
         */
        while (startpage <= endpage) {
	    if ((lp = popen(cmd, "w")) == NULL) {
		Warning("ERROR: Could not open printing process");
	 	goto clean_exit;
	    }
	    current_page = startpage;
	    set_dsp_size();
	    for (i = 0; i < Hnobj; i++) {
	        (*(Hobjary[i].opsptr->loc))(i, OBJECT);
                (*(Hobjary[i].opsptr->dsp))(i);
            }
            ren_hline();
            for (i = 0; i < nobj; i++) {
                (*(objary[i].opsptr->loc))(i, OBJECT);
                (*(objary[i].opsptr->dsp))(i);
            }
	    for (i = 0; i < npln; i++) {
	 	loc_pline(i, OBJECT);
	    }
	    gr_laser_on(lp);
 	    draw_outline(TRUE);
	    gr_laser_off();
	    pclose(lp);
	    startpage++;
        }
        /* set back to original status */
        current_page = currpage;
        set_dsp_size();
        for (i = 0; i < Hnobj; i++) {
            (*(Hobjary[i].opsptr->loc))(i, OBJECT);
            (*(Hobjary[i].opsptr->dsp))(i);
        }
        ren_hline();
        for (i = 0; i < nobj; i++) {
            (*(objary[i].opsptr->loc))(i, OBJECT);
            (*(objary[i].opsptr->dsp))(i);
        }
	for (i = 0; i < npln; i++) {
	    loc_pline(i, OBJECT);
	}
    } else {				/* error on print (broken pipe) */ 
	Warning("ERROR - print process failed: %s", cmd);
	gr_laser_off();
	(void)pclose(lp);
	exit_code = DIALOG_ERROR;
    }
clean_exit:
    signal(SIGPIPE, SIG_DFL);
    return(exit_code);
}

/***********************************************************************
 *
 * FUNCTION:
 *	brokenpipe()
 *
 * INPUTS:
 *	dummy		(int)
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 * 	This function is called whenever the process opened by popen
 * fails.  If this function gets called, it returns to 
 * setjmp(env).  This function was mainly written so that if a user
 * entered a bad printer name (which could frequently happen), 
 * the program wouldn't crash.
 */

static void 
brokenpipe(int dummy)
{
    signal(SIGPIPE, SIG_IGN);
    longjmp(env, 1);
}

/***********************************************************************
 *
 * FUNCTION:
 *	edit_print()
 *
 * INPUTS:
 *	box		(Widget)
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	none
 *
 * DESCRIPTION:
 * 	Manages and unmanages the print box popup.
 */

static void 
edit_print(Widget box)
{
    XEvent event;                       /* event structure for loop */
    int i;				/* temp loop index */
    int no_change;			/* hold on to current modified value */

    no_change = modified;
    file_was_printed = DIALOG_ERROR;   /* flag for event loop */
    DialogManage(box);                 /* manage the dialog box */

    do {                                /* make our own event loop and exit */
        XtAppNextEvent(app, &event);    /* only when the user correctly */
        XtDispatchEvent(&event);        /* responds to the dialog box */
    } while(file_was_printed & DIALOG_ERROR);

    XtUnmanageChild(box);                /* unmanage the dialog box */
    modified = no_change;

    for (i = 0; i < num_pages; i++) {
	XmStringFree(pages[i]);
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      verify_fun()
 *
 * INPUTS:
 *      w       (Widget)
 *      x, y    (XtPointer)
 *
 * OUTPUTS:
 *      none
 *
 * RETURNS:
 *      none
 *
 * EXTERNALLY READ:
 *      none
 *
 * EXTERNALLY MODIFIED:
 *      none
 *
 * DESCRIPTION:
 *      validates that the popup dialog can be closed
 */

static void
verify_fun(Widget w, XtPointer x, XtPointer y)
{
    if ((int)x == DIALOG_CANCEL) {
        if (!ShellValidateClose(w)) return;
        cancel_button();
    } else if ((int)x == DIALOG_UPDATE) {
        ok_button();
    }
}

