/*
 * This file contains the Sun specific bits of TeXtool, a TeX previewer
 * derived from the texview posted on the network about spring 86, which
 * in turn was derived from an earlier version and so on right back to
 * a version for the BBN Bitgraph.
 *
 * This stuff deals with all SunView related things and calls things
 * in the dvistuff file to do the DVI file and font file stuff, which
 * in turn calls things in here like SetChar and SetRule to drive the
 * screen.
 */

/*
 * Version 2.1 of 30 Oct 87 -- first real outside release
 *
 * -> V2.2, 27 Nov 87 -- fixed bug where .dvi file was opened twice,
 *						once from FrameDoneProc() and once by the WIN_REPAINT
 *						case in CanvasEventProc(). This ran us out of fds.
 * -> V2.3, 29 Mar 88 -- fixed sloppy code in skip_specials() in
pkstuff.c
  *                                            that caused specials
to be missed and treated as chars.

 */

/* include the various global #defines */
#include "defs.h"

/* now some definitions for the menu items */
/*
 * NOTE: The menu items must be in the positions in the menu given by
 * these values. This is not done automatically!
 */
#define M_NULL 0
#define M_RESTART 1
#define M_NEWPAGE 2
#define M_NEWFILE 3
#define M_CD 4
#define M_TEX 5
#define M_LATEX 6
#define M_EDIT 7
#define M_SHELL 8
#define SM_TOGGLE_P 0x11
#define SM_TOGGLE_BIGP 0x12
#define SM_TOGGLE_B 0x13
#define SM_TOGGLE_M 0x14
#define DM_TOGGLE_D 0x21
#define DM_TOGGLE_X 0x22
#define DM_SHOW_FD 0x23
#define DM_SHOW_PXL_CACHE 0x24

/* now some Sun include files */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <stdio.h>
#include <pwd.h>
#include <pixrect/pixrect_hs.h>
#include <suntool/sunview.h>
#include <suntool/icon_load.h>
#include <suntool/canvas.h>
#include <suntool/scrollbar.h>
#include <suntool/panel.h>
#include <suntool/tty.h>

/* general external procedures */
char *getenv();
char *index();
char *rindex();
char *sprintf();
char *strcpy();
char *strcat();
int strlen();
char *getwd();

/* procedures from dvistuff */
extern void		CloseDviFile();
extern void		DoPage();
extern Boolean	DviFileChanged();
extern void		FreeFontStorage();
extern void		OpenDviFile();
#ifdef DEBUG
extern void		ShowPxlFd();
extern void		ShowPxlCache();
#endif

/* procedures from pxlstuff or pkstuff */
extern void		LoadAChar();

/* procedures in this file */
void		BatchEnd();
void		BatchStart();
void		CanvasEventProc();
Notify_value CanvasInterposer();
void		CheckFiles();
void		ErrorEventProc();
char		*ExpandTilde();
void		Fatal();
void		FrameDoneProc();
void		InitWindows();
void		MenuCd();
void		MenuEdit();
Menu		MenuGenerate();
void		MenuLatex();
void		MenuNewFile();
void		MenuNewPage();
void		MenuRestart();
void		MenuShell();
void		MenuShortPage();
void		MenuTex();
void		MenuToggleFlag();
void		ProcessPage();
void		RestartDviFile();
void		SetBop();
void		SetChar();
void		SetFrameLabel();
void		SetPageMenu();
void		SetRule();
Menu		ShiftMenuGenerate();
void		SplitFilename();
void		TextInputProc();
void		Warning();
#ifdef DEBUG
Menu		DebugMenuGenerate();
void		ShowFd();
#endif

/* now include real global variables needed by both files */
#define _DEFINE
#include "globals.h"

/* now the "globals" only needed here */

static char progname[STRSIZE];			/* program name */
static char ttyinbuf[STRSIZE];			/* for input to ttysubwindow */
static char dirbuf[STRSIZE];			/* current working dir. */
static char framelabelbuf[STRSIZE];		/* for frame label */
static char curarea[STRSIZE];			/* current file area */
static char curext[STRSIZE];			/* current file extension */
static char curname[STRSIZE];			/* current file name */
static char textinputbuffer[STRSIZE];	/* for copying text panel item value */
static char dviname[STRSIZE];			/* current complete DVI filename */
static short textinput_state;

/* display related stuff */

static int hh;						/* current horizontal position in pixels */
static int vv;						/* current vertical position in pixels */
static int current_page;			/* pagepointers index for current page */

static int xcanvasoffset = RESOLUTION;	/* 1 inch offset to match TeX's */
static int ycanvasoffset = RESOLUTION;	/* 1 inch margin on paper */

static Boolean keyscroll;			/* bodge to say h,j,k or l typed */

/* SunView defs */
Frame frame;
Icon icon;
Canvas canvas;
Pixwin *pw;
Scrollbar vsb, hsb;
Cursor canvas_cursor;
Menu menu, tenpagemenu, shiftmenu;
Menu_item shortpageitem, longpageitem;
Frame ttyframe;
Tty ttysubwindow;
Frame errorframe;
Panel errorpanel;
Panel_item errorheader, errormessage;
Panel_item textinput;
#ifdef DEBUG
Menu debugmenu;
#endif

/* now the real code */

main(argc, argv)
int argc;
char *argv[];

{

	extern int optind;
	extern char *optarg;
	int c;

	strcpy(progname, argv[0]);

								/* initialse some globals */
	Debug = FALSE;
	ExtraDebug = FALSE;
	PreLoad = FALSE;
	BigPreLoad = FALSE;
	Batching = TRUE;
	main_state = SB_INITIAL;
	Mflag = FALSE;
	pagepointers = NULL;		/* we haven't malloc'd them yet */

	InitWindows(&argc, argv);		/* setup windows and consume -W args */

	current_page = 0;				/* start at the beginning */

   /*  get enviroment variables for different PXL directories */

   if ((PXLpath=getenv("PXLPATH")) == NULL)
		PXLpath = PXLFONTAREA;
   if ((PKpath=getenv("PKPATH")) == NULL)
		PKpath = PKFONTAREA;

	while ((c = getopt(argc, argv, "dxpPbm:")) != EOF)
			switch(c) {
				case 'd':		/* d selects Debug output */
					Debug = TRUE;
					break;

				case 'x':		/* extra debug info for pxl files */
					ExtraDebug = TRUE;
					break;

				case 'p':		/* p enables font pre-loading */
					PreLoad = TRUE;
					break;

				case 'P':		/* P enables char pixrect pre-loading */
					BigPreLoad = TRUE;
					break;

				case 'b':		/* turn batching off for slowness */
					Batching = FALSE;
					break;

				case 'm':		/* specify mag to override that in DVI file */
					Mflag = TRUE;
					user_mag = atoi(optarg);
					break;

				case '?':
				default:
					fprintf(stderr,
						"usage: textool [-pPb] [-m <mag>] [+n] file\n");
					exit(1);
			}

	if ((optind < argc) && (argv[optind][0] == '+'))
										/* we have starting page number */
		sscanf(&argv[optind][1], "%d", &current_page);

	if (optind >= argc) {
		main_state = SB_NOFILE;				/* cancel SB_INITIAL status */
		SplitFilename("<no_file>");		/* _ as icon labels don't like space */
	}
	else
		SplitFilename(argv[optind]);
	SetFrameLabel();

#ifdef DEBUG
	if (Debug) fprintf(stderr,"going into window_main_loop()\n");
#endif
	window_main_loop(frame);
}


/*-->InitWindows*/

void
InitWindows(argcptr, argv)		/* initialize SunView stuff */
int *argcptr;
char **argv;

{
Pixrect *pr1;
char error[IL_ERRORMSG_SIZE];
int i;

	pr1 = icon_load_mpr("/u/tex/textool/textool.icon", error); 
	if (pr1 == NULL)				/* couldn't load icon */
		fprintf(stderr, "%s\n", error);
								/* not Fatal() as windows are not yet active */
	icon = icon_create(ICON_IMAGE, pr1,
						0);

	frame = window_create(0,FRAME,
						WIN_WIDTH, 900,
						WIN_HEIGHT, 900,
						WIN_X, 100,
						WIN_Y, 0,
						WIN_SHOW, FALSE,
						FRAME_LABEL, "TeXtool",
						FRAME_ICON, icon,
						FRAME_ARGC_PTR_ARGV, argcptr, argv,
						0);

	vsb = scrollbar_create(SCROLL_LINE_HEIGHT, 20,
						SCROLL_ADVANCED_MODE, TRUE,
						0);
	hsb = scrollbar_create(SCROLL_LINE_HEIGHT, 20,
						SCROLL_ADVANCED_MODE, TRUE,
						0);

	canvas = window_create(frame,CANVAS,
						 CANVAS_AUTO_SHRINK, FALSE,
						 CANVAS_AUTO_EXPAND, FALSE,
						 CANVAS_WIDTH, (17 * RESOLUTION)/2, /* 8.5in */
						 CANVAS_HEIGHT, 12 * RESOLUTION, /* 12in */
						 WIN_VERTICAL_SCROLLBAR, vsb,
						 WIN_HORIZONTAL_SCROLLBAR, hsb,
						 WIN_CONSUME_KBD_EVENT, WIN_ASCII_EVENTS,
						 WIN_EVENT_PROC, CanvasEventProc,
						 0);
	pw = canvas_pixwin(canvas);

								/* interpose before canvas for pageing */
	(void)notify_interpose_event_func(canvas, CanvasInterposer, NOTIFY_SAFE);

	scrollbar_scroll_to(hsb, (long)RESOLUTION/2);
	scrollbar_scroll_to(vsb, (long)RESOLUTION);

								/* now make the menu for canvas */
	tenpagemenu = menu_create(MENU_ACTION_PROC, MenuShortPage, 0);
	menu_set(tenpagemenu,
				MENU_STRING_ITEM, "1", 1,
				MENU_STRING_ITEM, "2", 2,
				MENU_STRING_ITEM, "3", 3,
				MENU_STRING_ITEM, "4", 4,
				MENU_STRING_ITEM, "5", 5,
				MENU_STRING_ITEM, "6", 6,
				MENU_STRING_ITEM, "7", 7,
				MENU_STRING_ITEM, "8", 8,
				MENU_STRING_ITEM, "9", 9,
				MENU_STRING_ITEM, "10", 10,
				0);

	shortpageitem = menu_create_item(MENU_PULLRIGHT_ITEM, "go to page",
								tenpagemenu, 0);
	longpageitem = menu_create_item(MENU_ACTION_ITEM, "go to page",
								MenuNewPage, 0);

	menu = menu_create(MENU_GEN_PROC, MenuGenerate,
						MENU_ITEM,
						MENU_ACTION_ITEM, "restart DVI file", MenuRestart,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "new file", MenuNewFile,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "change directory", MenuCd,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "run tex", MenuTex,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "run latex", MenuLatex,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "edit .tex file", MenuEdit,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "shell", MenuShell,
						0,0);
	menu_set(menu, MENU_INSERT, 1, shortpageitem, 0);

								/* now a second menu for shift-right button */
	shiftmenu = menu_create(MENU_GEN_PROC, ShiftMenuGenerate,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, SM_TOGGLE_P,
						0,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, SM_TOGGLE_BIGP,
						0,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, SM_TOGGLE_B,
						0,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, SM_TOGGLE_M,
						0,0);

#ifdef DEBUG
								/* now a debug menu for the meta key */
	debugmenu = menu_create(MENU_GEN_PROC, DebugMenuGenerate,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, DM_TOGGLE_D,
						0,
						MENU_ITEM,
						MENU_ACTION_PROC, MenuToggleFlag,
						MENU_VALUE, DM_TOGGLE_X,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "show fd", ShowFd,
						MENU_VALUE, DM_SHOW_FD,
						0,
						MENU_ITEM,
						MENU_ACTION_ITEM, "show PXL cache", ShowPxlCache,
						MENU_VALUE, DM_SHOW_PXL_CACHE,
						0,0);
#endif DEBUG

								/* now create a frame and panel for errors */

	errorframe = window_create(frame, FRAME,
								FRAME_NO_CONFIRM, TRUE,
								WIN_SHOW, FALSE,
								WIN_COLUMNS, 80,
								WIN_ROWS, 2,
								WIN_X, 100,
								WIN_Y, 100,
								0);
	errorpanel = window_create(errorframe, PANEL,
								PANEL_BACKGROUND_PROC, ErrorEventProc,
								PANEL_EVENT_PROC, ErrorEventProc,
								0);
	errorheader = panel_create_item(errorpanel, PANEL_MESSAGE,
								PANEL_LABEL_STRING, "TeXtool Warning:",
								0);
	errormessage = panel_create_item(errorpanel, PANEL_MESSAGE,
								PANEL_LABEL_STRING, "dummy message",
								0);
	textinput = panel_create_item(errorpanel, PANEL_TEXT,
								PANEL_LABEL_STRING, "Magnification:",
								PANEL_ITEM_Y, ATTR_ROW(1),
								PANEL_ITEM_X, ATTR_COL(0),
								PANEL_SHOW_ITEM, FALSE,
								PANEL_VALUE_DISPLAY_LENGTH, 40,
								PANEL_NOTIFY_PROC, TextInputProc,
								0);
	window_fit(errorpanel);
	window_fit(errorframe);
										/* create shell window for TeX, etc */
	ttyframe = window_create(frame, FRAME,
								FRAME_LABEL, "TeXtool Shell",
								FRAME_SHOW_LABEL, TRUE,
								FRAME_NO_CONFIRM, TRUE,
								FRAME_DONE_PROC, FrameDoneProc,
								WIN_X, 25,
								WIN_Y, 25,
								WIN_SHOW, FALSE,
								0);
	ttysubwindow = window_create(ttyframe, TTY,
								0);
}


/*-->SetFrameLabel*/

void
SetFrameLabel()
{
	if (pagepointers == NULL)
		sprintf(framelabelbuf,
			"TeXtool: Directory: %s File: %s%s%s",
			getwd(dirbuf), curarea, curname, curext);
	else
		sprintf(framelabelbuf,
			"TeXtool: Directory: %s File: %s%s%s \"Page\": %d \\count0: %d",
			getwd(dirbuf), curarea, curname, curext,
			current_page+1, pagepointers[current_page].count_0);


	window_set(frame, FRAME_LABEL, framelabelbuf, 0);
}


						/*
						 * Now two procs to deal with canvas input.
						 * CanvasInterposer is called first and filters out
						 * the vertical scroll page requests, turning them into
						 * calls of ProcessPage.
						 * CanvasEventProc handles all other input cases.
						 */
/*-->CanvasInterposer*/

Notify_value
CanvasInterposer(object, event, arg, type)
Canvas object;
Event *event;
Notify_arg arg;
Notify_event_type type;
{
	Scroll_motion motion;
	Notify_value retval;

#ifdef DEBUG
	if (Debug) fprintf(stderr,"Interposer called (%d) - ", event_id(event));
	if (Debug) fprintf(stderr,
						"\nobject = %X, *event = %X, arg = %X, type = %X\n",
						object, event, arg, type);
#endif
	if (event_id(event) == SCROLL_REQUEST) {
		if ((Scrollbar)arg == vsb) {
#ifdef DEBUG
			if (Debug) fprintf(stderr,"arg is vsb, ");
#endif
			motion = (Scroll_motion)scrollbar_get(vsb, SCROLL_REQUEST_MOTION);
			if (!keyscroll && ((motion == SCROLL_PAGE_FORWARD)
									|| (motion == SCROLL_PAGE_BACKWARD))) {
#ifdef DEBUG
				if (Debug) fprintf(stderr,"motion is %X (PAGE), ", (int)motion);
#endif
										/* now tell bar to undo scroll */
				scrollbar_set(vsb, SCROLL_VIEW_START,
								scrollbar_get(vsb, SCROLL_LAST_VIEW_START), 0);
				if (motion == SCROLL_PAGE_BACKWARD) {
					if (current_page-- > 0)
						ProcessPage(current_page);
	   				else
						current_page++;
				}
				else {
					if (++current_page < totalpages)
						ProcessPage(current_page);
					else
						current_page--;
				}
#ifdef DEBUG
				if (Debug) fprintf(stderr,"Interposer return (DONE)\n");
#endif
				return (NOTIFY_DONE);
			}
		}
	}
#ifdef DEBUG
	if (Debug) fprintf(stderr,"calling next func\n");
#endif
	retval = (notify_next_event_func(object, event, arg, type));
	if (Debug) fprintf(stderr, "Interposer return (%X)\n", retval);
	return (retval);
}


/*-->CanvasEventProc*/

void
CanvasEventProc(notused, event)
Canvas notused;
Event *event;
{
	static int intval;

#ifdef DEBUG
	if (Debug) fprintf(stderr,"event proc called (id = %d) - ",
						event_id(event));
#endif

		keyscroll = FALSE;
		switch (event_id(event)) {
			case '\n':
			case '\r':
				if (main_state & (SB_PLUSNUMBER | SB_MINUSNUMBER)) {
					if ((main_state & SB_MINUSNUMBER)
						&& (current_page -= intval) < 0)
						current_page = 0;
					if ((main_state & SB_PLUSNUMBER)
						&& (current_page += intval) >= totalpages)
						current_page = totalpages-1;
					main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
					main_state |= SB_INTERPRET;
					ProcessPage(current_page);
					break;
				}				/* else fall into next page case */
			case ' ':				/* normal case - start next page */
			case 'n':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				if (++current_page < totalpages)
					ProcessPage(current_page);
				else
					current_page--;
				break;

			case 'b':
			case 'P':
			case 'p':				/* redisplay from previous page */
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				if (current_page-- > 0)
					ProcessPage(current_page);
				else
					current_page++;
				break;

#ifdef KEYBOARDSCROLL
			case 'j':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				keyscroll = TRUE;
				scrollbar_scroll_to(vsb, (long)600);
				break;

			case 'h':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				keyscroll = TRUE;
				scrollbar_scroll_to(hsb, (long)0);
				break;

			case 'l':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				keyscroll = TRUE;
				scrollbar_scroll_to(hsb, (long)300);
				break;

			case 'k':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				keyscroll = TRUE;
				scrollbar_scroll_to(vsb, (long)0);
				break;

			case 'c':
				main_state &= (~SB_PLUSNUMBER & ~SB_MINUSNUMBER);
				main_state |= SB_INTERPRET;
				keyscroll = TRUE;
				scrollbar_scroll_to(vsb, (long)RESOLUTION);
				scrollbar_scroll_to(hsb, (long)(RESOLUTION/2));
				break;
#endif

#ifdef DEBUG
			case SCROLL_REQUEST:		/* scroll request, after Sb gets it */
				{ int a, b, c, d;
				if (Debug) {
					a = (int)scrollbar_get(vsb, SCROLL_VIEW_START);
					b = (int)scrollbar_get(vsb, SCROLL_LAST_VIEW_START);
					c = (int)scrollbar_get(hsb, SCROLL_VIEW_START);
					d = (int)scrollbar_get(hsb, SCROLL_LAST_VIEW_START);
					fprintf(stderr,"\nVsb scroll from %d to %d\n", b, a);
					fprintf(stderr,"Hsb scroll from %d to %d\n", d, c);
				}
				}
				break;

#endif
			case '-':
				/* read in val */
				main_state |= SB_MINUSNUMBER;
				intval = 0;
				break;

			case '+':
				/* read in val */
				main_state |= SB_PLUSNUMBER;
				intval = 0;
				break;

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (main_state & (SB_PLUSNUMBER | SB_MINUSNUMBER))
					intval = intval*10 + (event_id(event) - '0');
				break;

			case '\b':
			case 127:				/* delete key */
				if (main_state & (SB_PLUSNUMBER | SB_MINUSNUMBER))
					intval /= 10;
				break;

			case MS_RIGHT:
				if (event_is_up(event)) break;
#ifdef DEBUG
				if (event_meta_is_down(event)) {
					(void)menu_show(debugmenu, canvas,
						canvas_window_event(canvas, event), 0);
					break;
				}
#endif
				if (event_shift_is_down(event))
					(void)menu_show(shiftmenu, canvas,
						canvas_window_event(canvas, event), 0);
				else
					(void)menu_show(menu, canvas,
						canvas_window_event(canvas, event), 0);
				break;

			case MS_LEFT:
				canvas_cursor = window_get(canvas, WIN_CURSOR);
				if (event_is_up(event)) {
					cursor_set(canvas_cursor,
								CURSOR_SHOW_CURSOR, TRUE,
								CURSOR_SHOW_CROSSHAIRS, FALSE,
								0);
				}
				else {
										/*
										 * All these except the pixrect op
										 * are defaults but it does not seem
										 * to work unles they are specified
										 */
					cursor_set(canvas_cursor,
								CURSOR_SHOW_CURSOR, FALSE,
								CURSOR_CROSSHAIR_COLOR, 1,
								CURSOR_CROSSHAIR_THICKNESS, 1,
								CURSOR_CROSSHAIR_GAP, 0,
								CURSOR_CROSSHAIR_LENGTH, CURSOR_TO_EDGE,
								CURSOR_CROSSHAIR_OP, PIX_SRC|PIX_DST,
								CURSOR_SHOW_CROSSHAIRS, TRUE,
								0);
				}
				window_set(canvas, WIN_CURSOR, canvas_cursor, 0);
				break;

			case WIN_REPAINT:
				if (main_state & SB_INITIAL) { /* first time of display */
					main_state &= ~SB_INITIAL;
					CheckFiles();		/* sets main_state */
					OpenDviFile(dviname);
					if (main_state & (SB_NOFILE | SB_NODVIFILE))
						break;
					SetPageMenu();
					main_state &= ~SB_NEWFILE;
					main_state |= SB_INTERPRET;
					ProcessPage(current_page);
				}
				break;

			default:
#ifdef DEBUG
				if (Debug) fprintf(stderr,"event proc default case - ");
#endif
		}
#ifdef DEBUG
	if (Debug) fprintf(stderr,"event proc return\n");
#endif
}


						/*
						 * Now the procedures that actually do the work of
						 * drawing on the screen. These get called from
						 * dvistuff to prevent it having to know about
						 * SunView.
						 * SetBop just clears the screen and sets the initial
						 * offset. SetChar and SetRule draw text and lines.
						 */
/*-->SetBop*/

void
SetBop()
{
	pw_writebackground(pw, 0, 0,
		pw->pw_prretained->pr_size.x, pw->pw_prretained->pr_size.y,
		PIX_CLR);
						/*
						 * Start with h and v at offset point rather than 0,0.
						 * This means that offset does not need to be added in
						 * on every call to SetChar and SetRule.
						 */
	h = xcanvasoffset * hconv;
	v = ycanvasoffset * vconv;
}


/*-->SetChar*/

void
SetChar(charptr, Set)
struct char_entry *charptr;
Boolean Set;
{
	hh = PixRound(h, hconv);
	vv = PixRound(v, vconv);
	if (!charptr->where.isloaded) LoadAChar(charptr);
	if (charptr->where.isloaded == TRUE)
		pw_rop(pw, hh-charptr->xOffset,
			vv-charptr->yOffset,
			charptr->width, charptr->height, PIX_SRC | PIX_DST,
			charptr->where.address.pixrectptr, 0, 0);
	if (Set)
		h += charptr->tfmw;
}


/*-->SetRule*/

void
SetRule(a, b, Set)
int a, b;
Boolean Set;

{			/*		 this routine draws \hrule and \vrule */
	int ehh, evv;

	hh = PixRound(h, hconv);
	vv = PixRound(v-a, vconv);
	ehh = PixRound(h + b, hconv);
	evv = PixRound(v, vconv);
	if (hh == ehh) ehh++;
	if (vv == evv) vv--;
	if ((a > 0) && (b > 0))
			pw_rop(pw, hh, vv,
				ehh-hh, evv-vv, PIX_SET, NULL, 0, 0);
	if (Set)
		h += b;
}


						/*
						 * These next two are only procedures here to avoid
						 * having dvistuff.c know about Pixwins.
						 * As they are only called once per page the loss of
						 * speed is not a problem.
						 */
/*-->BatchStart*/

void
BatchStart()
{
	pw_batch_on(pw);
}


/*-->BatchEnd*/

void
BatchEnd()
{
	pw_batch_off(pw);
}


						/*
						 * Now two procedures that call on things in
						 * dvistuff to get pages displayed.
						 */
/*-->ProcessPage*/

void
ProcessPage(page)
int page;
{
	if (DviFileChanged())
		RestartDviFile();
		SetFrameLabel();
	DoPage(page);
}


/*-->RestartDviFile*/

void
RestartDviFile()
{
	CloseDviFile();
	FreeFontStorage();
	main_state &= (~SB_INTERPRET & ~SB_NOFILE);
/*	main_state |= SB_NEWFILE;		/* to allow a try at open */
	CheckFiles();					/* sets main_state */
	OpenDviFile(dviname);
	if (main_state & (SB_NOFILE | SB_NODVIFILE)) /* something wrong */
		return;
	SetPageMenu();
	main_state &= ~SB_NEWFILE;	/* SB_NEWFILE must be set to get to here */
	main_state |= SB_INTERPRET;
}


						/*
						 * Now to the menu generating procedures, one for
						 * each menu. There is also a procedure to set
						 * the "go to page" menu item depending on the
						 * number of pages.
						 * MenuGenerate() deals with most of the interlocking
						 * to avoid illegal menu choices.
						 */
/*-->MenuGenerate*/

Menu
MenuGenerate(object, operation)
Menu object;
Menu_generate operation;
{
	if (operation == MENU_CREATE) {
		menu_set(menu_get(object, MENU_NTH_ITEM, M_RESTART),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_NOFILE | SB_NODVIFILE ) ?
						TRUE : FALSE),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, M_NEWPAGE),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_NOFILE | SB_NODVIFILE) ?
						TRUE : FALSE),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, M_TEX),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_EDITING | SB_SHELL | SB_NOFILE |
								SB_NOTEXFILE) ? TRUE : FALSE),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, M_LATEX),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_EDITING | SB_SHELL | SB_NOFILE |
								SB_NOTEXFILE) ? TRUE : FALSE),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, M_EDIT),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_EDITING | SB_SHELL | SB_NOFILE) ?
														TRUE : FALSE),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, M_SHELL),
				MENU_INACTIVE,
				(main_state & (SB_TEXING | SB_EDITING | SB_SHELL) ?
														TRUE : FALSE),
				0);
	}
	return (object);

}


/*-->ShiftMenuGenerate*/

Menu
ShiftMenuGenerate(object, operation)
Menu object;
Menu_generate operation;
{
	if (operation == MENU_CREATE) {
		menu_set(menu_get(object, MENU_NTH_ITEM, SM_TOGGLE_P & 0xF),
				MENU_STRING, (PreLoad ? "reset -p flag" : "set -p flag"),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, SM_TOGGLE_B & 0xF),
				MENU_STRING, (Batching ? "set -b flag" : "reset -b flag"),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, SM_TOGGLE_BIGP & 0xF),
				MENU_STRING, (BigPreLoad ? "reset -P flag" : "set -P flag"),
				0);
#ifdef USEGLOBALMAG
		menu_set(menu_get(object, MENU_NTH_ITEM, SM_TOGGLE_M & 0xF),
				MENU_STRING, (Mflag ? "reset -m flag" : "set -m flag"),
				0);
#endif
	}
	return (object);
}


#ifdef DEBUG
/*-->DebugMenuGenerate*/

Menu
DebugMenuGenerate(object, operation)
Menu object;
Menu_generate operation;
{
	if (operation == MENU_CREATE) {
		menu_set(menu_get(object, MENU_NTH_ITEM, DM_TOGGLE_D & 0xF),
				MENU_STRING, (Debug ? "reset -d flag" : "set -d flag"),
				0);
		menu_set(menu_get(object, MENU_NTH_ITEM, DM_TOGGLE_X & 0xF),
				MENU_STRING, (ExtraDebug ? "reset -x flag" : "set -x flag"),
				0);
	}
	return (object);
}
#endif DEBUG


/*-->SetPageMenu*/

void
SetPageMenu()
{
	short i;

	if (totalpages > 10)
		menu_set(menu,
				MENU_REPLACE, 2, longpageitem,
				0);
	else {
		menu_set(menu,
				MENU_REPLACE, 2, shortpageitem,
				0);
		for (i = 0; i < 10; i++)
			menu_set(menu_get(tenpagemenu, MENU_NTH_ITEM, i+1, 0),
						MENU_INACTIVE, (i < totalpages ? FALSE : TRUE),
						0);
	}
}


						/*
						 * Now the action procs called after a menu selection
						 * is made.
						 */
/*-->MenuRestart*/

void
MenuRestart(object, item)
Menu object;
Menu_item item;
{
	window_release_event_lock(canvas); /* "I may be some time" */
	current_page = 0;
	RestartDviFile();
	DoPage(current_page);
}


/*-->MenuNewPage*/

void
MenuNewPage(object, item)
Menu object;
Menu_item item;
{
	panel_set(textinput,
				PANEL_LABEL_STRING, "Page number:",
				PANEL_VALUE, "",
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(errorheader,
				PANEL_SHOW_ITEM, FALSE,
				0);
	panel_set(errormessage,
				PANEL_LABEL_STRING, "",
				PANEL_SHOW_ITEM, FALSE,
				0);
	window_fit(errorpanel);
	window_fit(errorframe);
	window_set(errorframe,
				WIN_X, 100,
				WIN_Y, 100,
				WIN_SHOW, TRUE,
				0);
	textinput_state = M_NEWPAGE;
}


/*-->MenuShortPage*/

void
MenuShortPage(object, item)
Menu object;
Menu_item item;
{
		current_page = (int)menu_get(item, MENU_VALUE, 0);
		current_page--;						/* as current_page =0 for page 1 */
		if (current_page >= totalpages)
			current_page = totalpages - 1;
		ProcessPage(current_page);
}


/*-->MenuNewFile*/

void
MenuNewFile(object, item)
Menu object;
Menu_item item;
{
	panel_set(textinput,
				PANEL_LABEL_STRING, "Filename:",
				PANEL_VALUE, "",
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(errorheader,
				PANEL_SHOW_ITEM, FALSE,
				0);
	panel_set(errormessage,
				PANEL_LABEL_STRING, "",
				PANEL_SHOW_ITEM, FALSE,
				0);
	window_fit(errorpanel);
	window_fit(errorframe);
	window_set(errorframe,
				WIN_X, 100,
				WIN_Y, 100,
				WIN_SHOW, TRUE,
				0);
	textinput_state = M_NEWFILE;
}


/*-->MenuCd*/

void
MenuCd(object, item)
Menu object;
Menu_item item;
{
	panel_set(textinput,
				PANEL_LABEL_STRING, "Directory:",
				PANEL_VALUE, "",
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(errorheader,
				PANEL_SHOW_ITEM, FALSE,
				0);
	panel_set(errormessage,
				PANEL_LABEL_STRING, "",
				PANEL_SHOW_ITEM, FALSE,
				0);
	window_fit(errorpanel);
	window_fit(errorframe);
	window_set(errorframe,
				WIN_X, 100,
				WIN_Y, 100,
				WIN_SHOW, TRUE,
				0);
	textinput_state = M_CD;
}


/*-->MenuTex*/

void
MenuTex(object, item)
Menu object;
Menu_item item;
{
	strcpy(ttyinbuf, "cd ");
	strcat(ttyinbuf, getwd(dirbuf));
	strcat(ttyinbuf, "\ntex ");
	strcat(ttyinbuf, curname);
	strcat(ttyinbuf, ".tex\n");
	(void)ttysw_input(ttysubwindow, ttyinbuf, strlen(ttyinbuf));
	main_state |= SB_TEXING;
	window_set(ttyframe, WIN_SHOW, TRUE, 0);
}


/*-->MenuLatex*/

void
MenuLatex(object, item)
Menu object;
Menu_item item;
{
	strcpy(ttyinbuf, "cd ");
	strcat(ttyinbuf, getwd(dirbuf));
	strcat(ttyinbuf, "\nlatex ");
	strcat(ttyinbuf, curname);
	strcat(ttyinbuf, ".tex\n");
	(void)ttysw_input(ttysubwindow, ttyinbuf, strlen(ttyinbuf));
	main_state |= SB_TEXING;
	window_set(ttyframe, WIN_SHOW, TRUE, 0);
}


/*-->MenuEdit*/

void
MenuEdit(object, item)
Menu object;
Menu_item item;
{
	char *tcp;

	if ((tcp = getenv("EDITOR")) == NULL)
		tcp = "vi";
	strcpy(ttyinbuf, tcp);
	strcat(ttyinbuf, " ");
	strcat(ttyinbuf, getwd(dirbuf));
	strcat(ttyinbuf, "/");
	strcat(ttyinbuf, curname);
	strcat(ttyinbuf, ".tex\n");
	(void)ttysw_input(ttysubwindow, ttyinbuf, strlen(ttyinbuf));
	main_state |= SB_EDITING;
	window_set(ttyframe, WIN_SHOW, TRUE, 0);
}


/*-->MenuShell*/

void
MenuShell(object, item)
Menu object;
Menu_item item;
{
	main_state |= SB_SHELL;
	window_set(ttyframe, WIN_SHOW, TRUE, 0);
}


/*-->MenuToggleFlag*/

void
MenuToggleFlag(object, item)
Menu object;
Menu_item item;
{
	int itemvalue;

	itemvalue = (int)menu_get(item, MENU_VALUE, 0);

	switch (itemvalue) {
#ifdef DEBUG
		case DM_TOGGLE_D:
			Debug ^= 1;
			break;
		case DM_TOGGLE_X:
			ExtraDebug ^= 1;
			break;
#endif
		case SM_TOGGLE_P:
			PreLoad ^= 1;
			break;
		case SM_TOGGLE_B:
			Batching ^= 1;
			break;
		case SM_TOGGLE_BIGP:
			BigPreLoad ^= 1;
			break;
#ifdef USEGLOBALMAG
		case SM_TOGGLE_M:
			Mflag ^= 1;
			if (Mflag) {
				panel_set(textinput,
						PANEL_LABEL_STRING, "Magnification:",
						PANEL_VALUE, "",
						PANEL_SHOW_ITEM, TRUE,
						0);
				panel_set(errorheader,
						PANEL_SHOW_ITEM, FALSE,
						0);
				panel_set(errormessage,
						PANEL_LABEL_STRING, "",
						PANEL_SHOW_ITEM, FALSE,
						0);
				window_fit(errorpanel);
				window_fit(errorframe);
				window_set(errorframe,
						WIN_X, 100,
						WIN_Y, 100,
						WIN_SHOW, TRUE,
						0);
				textinput_state = SM_TOGGLE_M;
			}
			break;
#endif
		default:
			break;
	}
}


						/*
						 * The event proc for the panel which contains the
						 * error message item and the text input item.
						 * If the panel event is an ascii one it passes it
						 * on to the text input item. It removes the panel
						 * on a left mouse button.
						 */
/*-->ErrorEventProc*/

void
ErrorEventProc(object, event)
Panel object;
Event *event;
{
	if (event_id(event) <= ASCII_LAST)
		panel_accept_key(textinput, event);
	else if (event_id(event) == MS_LEFT)
		window_set(errorframe,
				WIN_SHOW, FALSE,
				0);
}


						/*
						 * The notify proc for the panel text input item.
						 * This gets things like page numbers and filenames
						 * from the user.
						 */
/*-->TextInputProc*/

void
TextInputProc(item, event)
Panel_item item;
Event *event;
{
#ifdef DEBUG
	if (Debug)
		fprintf(stderr,"TextInputProc(): panel_get_value returns %s\n",
				(char *)panel_get_value(textinput));
#endif
	strcpy(textinputbuffer, (char *)panel_get_value(textinput));
	window_set(errorframe,
				WIN_SHOW, FALSE,
				0);
	if (*textinputbuffer == NULL)
		return;
	switch(textinput_state) {
		case M_NEWPAGE:
			sscanf(textinputbuffer, "%d", &current_page);
			current_page--;
			if (current_page >= totalpages)
				current_page = totalpages - 1;
			ProcessPage(current_page);
			break;

		case M_NEWFILE:
			window_release_event_lock(errorframe);
								/* as this may take some time */
			SplitFilename(ExpandTilde(textinputbuffer));
								/* sets dviname */
			CloseDviFile();		/* from dvifp */
			FreeFontStorage();
			SetBop();			/* clear screen */
			current_page = 0;
			main_state &= (~SB_INTERPRET & ~SB_NOFILE);
			CheckFiles();			/* see what's there */
			OpenDviFile(dviname);				/* sets dvifp */
			SetFrameLabel();
			if (main_state & (SB_NOFILE | SB_NODVIFILE)) /* something wrong */
				break;
			SetPageMenu();
			main_state &= ~SB_NEWFILE;
			main_state |= SB_INTERPRET;
			DoPage(current_page);
			break;

		case M_CD:
			if (chdir(ExpandTilde(textinputbuffer)))
				Warning("Couldn't change directory");
			SetFrameLabel();
			break;

		case SM_TOGGLE_M:
			user_mag = atoi(textinputbuffer);
			break;

		default:
			break;
	}
}


						/*
						 * The done proc for the frame with the tty subwindow.
						 * It removes the frame and adjusts the display if the
						 * DVI file has changed.
						 */
/*-->FrameDoneProc*/

void
FrameDoneProc(object)
Frame object;
{
	int i;

	if (Debug) {
		fprintf(stderr,
				"FrameDoneProc(), object = %X, frame = %X, ttyframe = %X\n",
				object, frame, ttyframe);
		fprintf(stderr,"calling default done proc for object\n");
	}
	frame_default_done_proc(object);
	main_state &= (~SB_TEXING & ~SB_EDITING & ~SB_SHELL);
	CheckFiles();			/* see if anything changed */
	if (main_state & SB_NEWFILE) { /* we haven't seen it before */
		OpenDviFile(dviname);
		if (main_state & SB_NOFILE)/* can't have NODVIFILE as NEWFILE was set */
			return;
		main_state &= ~SB_NEWFILE;
		main_state |= SB_INTERPRET;
		DoPage(current_page);
	}
	else if (DviFileChanged()) {
		RestartDviFile();
		DoPage(current_page);
	}
}


						/*
						 * Now some utility procedures.
						 */
/*-->SplitFilename*/

void
SplitFilename(nameptr)
char *nameptr;
{
	char *tcp, *tcp1;
	Icon temp_icon;				/* for changing label */
								/* process filename */
	tcp = rindex(nameptr, '/');
	if (tcp == NULL) {
				curarea[0] = '\0';
				tcp = nameptr;
	}
	else {
				strncpy(curarea, nameptr, tcp-nameptr+1);
				curarea[tcp-nameptr+1] = '\0'; /* as strncpy will not do this */
				tcp += 1;
	}
	tcp1 = index(tcp, '.');
	if (tcp1 == NULL) {
				strcpy(curname, tcp);
				curext[0] = '\0';
	}
	else {
				strncpy(curname, tcp, tcp1-tcp);
				strcpy(curext, tcp1);
	}

	strcpy(dviname, curarea);
	strcat(dviname, curname);
	if (curext[0] == '\0')
				strcat(dviname, ".dvi");
	else
				strcat(dviname, curext);
#ifdef DEBUG
	if (Debug) {
				fprintf(stderr,"curarea = %s\n", curarea);
				fprintf(stderr,"curname = %s\nsetting icon label to curname\n",
						curname);
				fprintf(stderr,"curext = %s\n", curext);
				fprintf(stderr,"dviname = %s\n", dviname);
		}
#endif
	temp_icon = (Icon)window_get(frame, FRAME_ICON, 0);
	icon_set(temp_icon, ICON_LABEL, curname, 0);
	window_set(frame, FRAME_ICON, temp_icon, 0);
}


						/*
						 * CheckFiles tries access on the .dvi file,
						 * and if that does not exist, on the .tex file
						 * and sets main_state accordingly.
						 */
/*-->CheckFiles*/

void
CheckFiles()
{
	char buf[STRSIZE];

	if (access(dviname, F_OK | R_OK) == 0) { /* .dvi file present */
		if (main_state & SB_NODVIFILE) /* first time .dvi file found */
			main_state |= SB_NEWFILE;
		main_state &= (~SB_NODVIFILE & ~SB_NOTEXFILE);
	}
	else {
		strcpy(buf, curarea);
		strcat(buf, curname);
		strcat(buf, ".tex");
		if (access(buf, F_OK | R_OK) == 0) { /* no .dvi file but .tex file ok */
			main_state |= SB_NODVIFILE;
			main_state &= (~SB_NEWFILE & ~SB_NOTEXFILE);
		}
		else {							/* no .tex file either */
			main_state |= SB_NODVIFILE | SB_NOTEXFILE;
			main_state &= ~SB_NEWFILE;
		}
	}
		
}


/*-->ExpandTilde*/

char *
ExpandTilde(iptr)
char *iptr;
{
	char buff[STRSIZE];
	char *tptr;
	struct passwd *passwdptr;

#ifdef DEBUG
	if (Debug) fprintf(stderr,"ExpandTilde(%s) - ", iptr);
#endif
	if (*iptr == '~') {				/* something to do */
		strcpy(buff, iptr);
		tptr = index(buff, '/');
		if ((strlen(buff) == 1) || ((tptr-buff) == 1))
								/* expand to our $HOME */
			strcpy(iptr, getenv("HOME"));
		else {						/* ~name/... form */
			if (tptr != NULL) *tptr = 0;
			if ((passwdptr = getpwnam(&buff[1])) != NULL)
				strcpy(iptr, passwdptr->pw_dir);
			if (tptr != NULL) *tptr = '/';
 		}
		if (tptr != NULL)
			strcat(iptr, tptr);		/* complete rest of path if there is any */
	}
#ifdef DEBUG
	if (Debug) fprintf(stderr,"returns %s\n", iptr);
#endif
	return (iptr);
}


						/*
						 * Two error message procedures.
						 * Warning just displays the message, Error displays
						 * and then abandons the DVI file and goes to the
						 * NOFILE state.
						 */
/*-->Warning*/

void
Warning(fmt, arg1, arg2, arg3, arg4)  /* issue a warning */
char *fmt;		/* format   */
char *arg1, *arg2, *arg3, *arg4;		/* arguments */
{
	char message[STRSIZE];

	sprintf(message, fmt, arg1, arg2, arg3, arg4);

	panel_set(errorheader,
				PANEL_LABEL_STRING, "TeXtool Warning:",
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(errormessage,
				PANEL_LABEL_STRING, message,
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(textinput,
				PANEL_SHOW_ITEM, FALSE,
				0);
	window_fit(errorpanel);
	window_fit(errorframe);
	window_set(errorframe,
				WIN_X, 100,
				WIN_Y, 100,
				WIN_SHOW, TRUE,
				0);
}


/*-->Fatal*/

void
Fatal(fmt, arg1, arg2, arg3, arg4)/* issue a fatal error message */
char *fmt;		/* format */
char *arg1, *arg2, *arg3, *arg4;		/* arguments */

{
	char message[STRSIZE];

	sprintf(message, fmt, arg1, arg2, arg3, arg4);
	panel_set(errorheader,
				PANEL_LABEL_STRING, "TeXtool Error:",
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(errormessage,
				PANEL_LABEL_STRING, message,
				PANEL_SHOW_ITEM, TRUE,
				0);
	panel_set(textinput,
				PANEL_SHOW_ITEM, FALSE,
				0);
	window_fit(errorpanel);
	window_fit(errorframe);
	window_set(errorframe,
				WIN_X, 100,
				WIN_Y, 100,
				WIN_SHOW, TRUE,
				0);
	CloseDviFile();
	FreeFontStorage();				/* to get to known stable state */
	main_state |= SB_NOFILE;		/* to stop further processing */
	main_state &= (~SB_NEWFILE & ~SB_NODVIFILE & ~SB_NOTEXFILE & ~SB_INTERPRET);
	SplitFilename("<no_file>");
	SetFrameLabel();
}


#ifdef DEBUG
						/*
						 * A procedure to show the fds we are using.
						 * There was a leak of fds somewhere where ptys were
						 * not closed when a frame with a tty subwindow in it
						 * was destroyed and this was used to find it.
						 * The tty subframe is now not destroyed but just
						 * undisplayed to cure the problem.
						 * It also shows the huge number of fds needed by
						 * SunView which cuts down on the number available for
						 * PXL file caching.
						 */
/*-->ShowFd*/

void
ShowFd(object, item)
Menu object;
Menu_item item;
{
	short i;
	struct stat statbuf;

	fprintf(stderr,"frame				%d, win%d\n",
	window_get(frame, WIN_FD), window_get(frame, WIN_DEVICE_NUMBER));
	fprintf(stderr,"canvas				%d, win%d\n",
	window_get(canvas, WIN_FD), window_get(canvas, WIN_DEVICE_NUMBER));
	fprintf(stderr,"ttyframe		%d, win%d\n",
	window_get(ttyframe, WIN_FD), window_get(ttyframe, WIN_DEVICE_NUMBER));
	fprintf(stderr,"ttysubwindow		%d, win%d\n",
	window_get(ttysubwindow, WIN_FD),
				window_get(ttysubwindow, WIN_DEVICE_NUMBER));
	fprintf(stderr,"errorframe		%d, win%d\n",
	window_get(errorframe, WIN_FD), window_get(errorframe, WIN_DEVICE_NUMBER));
	fprintf(stderr,"errorpanel		%d, win%d\n",
	window_get(errorpanel, WIN_FD), window_get(errorpanel, WIN_DEVICE_NUMBER));
	ShowPxlFd();

	for (i=0; i<30; i++)				/* enumerate fds */
		if (fstat(i,&statbuf) == 0)
			fprintf(stderr,"fd %d open, type = %o, inode = %d\n",
					i,
					statbuf.st_mode & S_IFMT,
					statbuf.st_ino);
}
#endif