/***********************************************************************

	xscan
	this is a scanner programm for X11 using the athena widget set.
	You can use xscan under the terms of the GPL
			
************************************************************************/

#include "Xscan.h"
#include "XscanArg.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>

#ifdef _HAS_XPM
#include <X11/xpm.h>
#include "xscan.xpm"
#endif

XtAppContext app_context;
Widget topLevel, quit, About;
Widget cell8, cell4, cell2,cellbw;
Widget InputBox,SelectBox,ScanBox;
Widget ShowLabel;
Widget ScanW, SaveW;
Widget Canvas,DrawArea;
Widget BWLabel;
Widget ScanDialog,MessageDialog,AboutDialog;
Widget ScanPshell,SavePshell,MessagePshell,AboutPshell;
Widget SaveDialog;
Pixmap DrawPix;
GC DrawGC, ClrGC;
Dimension pixmap_width = MAXWIDTH;
Dimension pixmap_heigh = MAXSCANLINES;
Dimension slider_width, slider_height;	/* size of canvas */
Position slider_x, slider_y;		/* position of slider in canvas  */
long colors[65];			/* Array for colors (grey)	     */			
int default_depth;
int scaninprog = FALSE;			/* is scanningprocess active ?   */
char ScanLabel[40];
int oldcell  = 1;
int cellsize = 1;		/* number of cells for pseudogrey    */
int bufcnt = 0;			/* Number of bytes read from scanner */
XscanApp XscanAppData;		/* Own resources for scanner, currently */
				/* only one entry				 */ 
Colormap default_cmap;

Display *disp;
Window  win;
Screen *screen;
int screennum;

/********* for Icon if XPM is available *************/
#ifdef _HAS_XPM
Pixmap scan_icon;
XpmAttributes scan_icon_attr;
#endif

static char *RedrawTransl = "#override\n<Expose>: 	RedrawCanvas()";
static char *SaveTransl = "#override\n <Key>Return: 	PopdownSaveDialog()";
static char *RadioTransl= "#override\n\
			   <LeaveWindow>:	unhighlight()\n\
 			   <EnterWindow>:	highlight(Always)\n\
			   <Btn1Down>,<Btn1Up>:	set() notify()";

static XtActionsRec DefaultActions[] = {
	{"PopdownSaveDialog",PopdownSDialog},
	{"RedrawCanvas",RedrawCanvas}
}; 


void FlushEvents(void)	 /* check for outstanding events and process them */
{
	XEvent event;  
	while (XtAppPending(app_context) != 0) {
    		XtAppNextEvent(app_context, &event);
               	XtDispatchEvent(&event);
	}
}

/******		About Dialog		******/

void PopdownAboutDialog(Widget w, XtPointer client_data, XtPointer call_data)
{
	XtPopdown(AboutPshell);
	XtSetSensitive(InputBox, TRUE);
}

void PopupAboutDialog(Widget w, XtPointer client_data, XtPointer call_data)
{
	Position x,y;
	XtTranslateCoords(topLevel, (Position)300, (Position)-20, &x, &y);
	XtVaSetValues(AboutPshell, XtNx, x, XtNy, y, NULL);
	XtSetSensitive(InputBox,FALSE); 
	XtPopup(AboutPshell, XtGrabNonexclusive);
}

/******		message                 ******/

void MessageOff(void)
{
	XtPopdown(MessagePshell);
	XtSetSensitive(InputBox, TRUE);
}

void Message(char *txt)
{
	Position x,y;

	XtVaSetValues(MessageDialog,XtNlabel, txt, XtNwidth,200, NULL);
	XtTranslateCoords(topLevel, (Position)300, (Position)-20, &x, &y);
	XtVaSetValues(MessagePshell, XtNx, x, XtNy, y,XtNwidth, 200,XtNheight,50,NULL);
	
	XtSetSensitive(InputBox,FALSE); 
	XtPopup(MessagePshell, XtGrabNonexclusive);
	FlushEvents();
}

/******		Scanner Dialog		******/

void PopdownScanDialog(Widget w, XtPointer client_data, XtPointer call_data)
{
	XtPopdown(ScanPshell);
	XtSetSensitive(InputBox, TRUE);
	XtRemoveInput(AppId);
	close(fd);
	pixmap_heigh = lines_scanned;
	cellsize = oldcell;
	scaninprog = FALSE;
	Make();
}

void PopupScanDialog(void)
{
	Position x,y;
	XtTranslateCoords(topLevel, (Position)300, (Position)-20, &x, &y);
	XtVaSetValues(ScanPshell, XtNx, x, XtNy, y, NULL);
	XtSetSensitive(InputBox,FALSE); 
	XtPopup(ScanPshell, XtGrabNonexclusive);
}

/****		Save Dialog		*****/

void PopdownSDialog(Widget w, XEvent *unused,String *unused1, Cardinal *unused2)
{
	String string;

	XtPopdown(SavePshell);
	XtSetSensitive(InputBox, TRUE);
	string = XawDialogGetValueString(SaveDialog);
	Save(string);
}

void PopdownSaveDialog(Widget w, XtPointer client_data, XtPointer call_data)
{
	String string;

	XtPopdown(SavePshell);
	XtSetSensitive(InputBox, TRUE);
	string = XawDialogGetValueString(SaveDialog);
	if ((int)client_data) Save(string);
}

void PopupSaveDialog(Widget w, XtPointer client_data, XtPointer call_data)
{
	Position x,y;
	Dimension height;
	if (lines_scanned <= 0) {
		Message("nothing to save");
		FlushEvents();
		sleep(1);
		FlushEvents();
		sleep(5);
		MessageOff();
		return;
	}
	XtVaGetValues(topLevel, XtNheight, &height, NULL);
	XtTranslateCoords(topLevel, (Position)10, (Position)height/3, &x, &y);
	XtVaSetValues(SavePshell, XtNx, x, XtNy, y, XtNwidth,150, NULL);
	XtSetSensitive(InputBox,FALSE);
	XtPopup(SavePshell, XtGrabNonexclusive);
}

/*************** main widget functions **********************/

void Redraw(Widget w, XtPointer p1, XtPointer p2 )  /* redraw viewport widget */
{
	XawPannerReport *prep;

	if (p2 != NULL) {
		prep = (XawPannerReport *)p2;	
		slider_x = prep->slider_x;
		slider_y = prep->slider_y;
		slider_width = prep->slider_width;
		slider_height = prep->slider_height;
	}	
	if (scaninprog) return;
	if (cellsize > 1) 
		XCopyArea(XtDisplay(DrawArea), DrawPix, XtWindow(DrawArea),
			DefaultGCOfScreen(XtScreen(DrawArea)), 0, 0,
			pixmap_width, pixmap_heigh, 0, 0);
	else 
		XCopyPlane(XtDisplay(DrawArea), DrawPix, XtWindow(DrawArea),
			DefaultGCOfScreen(XtScreen(DrawArea)), 0, 0,
			pixmap_width, pixmap_heigh, 0, 0,1);
}

void ClrDraw(void)	/* clear viewport widget and redraw it */
{
	XFillRectangle(disp, DrawPix, ClrGC,0,0, pixmap_width,pixmap_heigh);
	Redraw(NULL,NULL,NULL); 
}

/* change cellsize */

void Cell(Widget w, XtPointer client_data, XtPointer call_data)
{
	int newc = (int)client_data;
	if (newc != cellsize) {
		cellsize = (int)client_data;
		Make();
	} 
}

void Quit(Widget w, XtPointer client_data, XtPointer call_data)
{
	exit(0); 
}

/*	create new pixmap and graphics-context	*/

void MakePixMap(void)
{
	int wi, hi;
	int depth = 1;
	XGCValues val;

	if (cellsize > 1) depth = default_depth; 
	wi = pixmap_width / cellsize;
	hi = pixmap_heigh / cellsize;

	DrawPix = XCreatePixmap(disp,RootWindowOfScreen(XtScreen(DrawArea)),
			        wi, hi, depth);
	val.background = 0;
	val.foreground = 1;
	DrawGC = XCreateGC(disp, DrawPix,(GCForeground | GCBackground), 
			   &val);
	val.background = 1;
	val.foreground = 0;
	ClrGC = XCreateGC(disp, DrawPix,(GCForeground | GCBackground), &val);
	XtVaSetValues(DrawArea,XtNwidth, wi, XtNheight, hi, NULL);
}

void FreePixMap(void)	/* Free pixmap and graphics-context */
{
	XFreeGC(XtDisplay(DrawArea),DrawGC);
	XFreeGC(XtDisplay(DrawArea),ClrGC);
	XFreePixmap(XtDisplay(DrawArea),DrawPix);
}

void main(int argc, char *argv[])
{
	XtTranslations SaveT;
	Widget value;

	int i;
	int bytes_per_line;	/* Bytes per scanline      */
	int dpi;		/* Ressolution of scanner  */

	XVisualInfo visual_info;
	XColor color[65];

	/*	initialize widgets 	*/

	topLevel = XtVaAppInitialize(&app_context, "XScan",NULL,0,&argc,argv,  
			             NULL,NULL); 
			             
	/* get own resources, currently only maximum number of scannlines */

	XtVaGetApplicationResources(topLevel,&XscanAppData,XscanRes,
						XtNumber(XscanRes),NULL);
	if (XscanAppData.MaxScanLines < 100) {
		fprintf(stderr,"xscan: error in resource settings:\n");
		fprintf(stderr,"MaxScanLines must be greater 100\n");
		exit(1);
	}

	linebuf = (char **)XtMalloc(sizeof(int *)*XscanAppData.MaxScanLines);

	/*	generate widgets */

	ScanBox  = XtVaCreateManagedWidget("ScanBox",formWidgetClass,topLevel,
					     NULL,0);	
	SelectBox = XtVaCreateManagedWidget("SelectBox",boxWidgetClass,ScanBox,
					   NULL,0);
	InputBox = XtVaCreateManagedWidget("InputBox",boxWidgetClass,ScanBox,
					    NULL,0);
 
	SaveW = XtVaCreateManagedWidget("Save",commandWidgetClass,InputBox,
					NULL,0); 
	ScanW = XtVaCreateManagedWidget("Scan",commandWidgetClass,InputBox,
					NULL,0); 
	quit = XtVaCreateManagedWidget("Quit",commandWidgetClass,InputBox,
					NULL,0);   
 	ShowLabel= XtVaCreateManagedWidget("Resolution",labelWidgetClass,SelectBox,
				 	NULL,0);             
	BWLabel = XtVaCreateManagedWidget("Pseudo\nGrey",labelWidgetClass,
					InputBox,NULL,0);


	XtAddCallback(quit, XtNcallback, Quit, 0);
	XtAddCallback(SaveW, XtNcallback, PopupSaveDialog, 0);
	XtAddCallback(ScanW, XtNcallback, Scan, 0);

	cell8 = XtVaCreateManagedWidget("8x8",toggleWidgetClass,InputBox,XtNradioGroup,NULL,NULL);
	cell4 = XtVaCreateManagedWidget("4x4",toggleWidgetClass,InputBox,XtNradioGroup,cell8,NULL);
	cell2 = XtVaCreateManagedWidget("2x2",toggleWidgetClass,InputBox,XtNradioGroup,cell4,NULL);
	cellbw= XtVaCreateManagedWidget("BW",toggleWidgetClass,InputBox,XtNradioGroup,cell2,NULL);
	SaveT = XtParseTranslationTable(RadioTransl);  
	XtOverrideTranslations(cell8,SaveT);
	XtOverrideTranslations(cell4,SaveT);
	XtOverrideTranslations(cell2,SaveT);
	XtOverrideTranslations(cellbw,SaveT);

	XtAddCallback(cellbw,XtNcallback, Cell, (XtPointer)1);
	XtAddCallback(cell2, XtNcallback, Cell, (XtPointer)2);
	XtAddCallback(cell4, XtNcallback, Cell, (XtPointer)4);
	XtAddCallback(cell8, XtNcallback, Cell, (XtPointer)8);
	
	Canvas = XtVaCreateManagedWidget("Canvas",viewportWidgetClass,
					  ScanBox,NULL,0);
	DrawArea= XtVaCreateManagedWidget("DrawArea",simpleWidgetClass,
					  Canvas,NULL,0);

	About = XtVaCreateManagedWidget("About",commandWidgetClass,InputBox,
					NULL,0); 
	XtAddCallback(About, XtNcallback, PopupAboutDialog, 0);
	SaveT = XtParseTranslationTable(RedrawTransl);  
	XtOverrideTranslations(DrawArea,SaveT);
	XtSetValues(DrawArea,DrawAreaArg,XtNumber(DrawAreaArg));
	XtSetValues(topLevel,TopLevelArg,XtNumber(TopLevelArg));
	
	XtSetValues(BWLabel,BWLabelArg,XtNumber(BWLabelArg)); 
	XtSetValues(ShowLabel,ShowLabelArg,XtNumber(ShowLabelArg)); 

	XtSetValues(SelectBox,SelectBoxArg,XtNumber(SelectBoxArg));	
	XtSetValues(InputBox,InputBoxArg,XtNumber(InputBoxArg)); 
	XtSetValues(Canvas,CanvasArg,XtNumber(CanvasArg)); 
	XtSetValues(cellbw,RadioSelectArg,XtNumber(RadioSelectArg)); 

	disp = XtDisplay(DrawArea);
	screennum = DefaultScreen(disp);
	screen = XtScreen(DrawArea);
	win = RootWindowOfScreen(screen);

	/*	allocate colors		*/

	default_depth  = DefaultDepth(disp, screennum);
	if (default_depth < 6) {   /* Need Display with 64 colors minimum */
		fprintf(stderr,"minimum depth = 6\n");
		exit(1);
	}
	default_cmap   = DefaultColormap(disp,screennum);

	/* need at minimum a pseudocolor display */
/*
	if (!XMatchVisualInfo(disp, screennum, default_depth, PseudoColor, 
					&visual_info)) {
		fprintf(stderr,"Wrong displaytype\n");
		exit(1);
	}
*/
	for(i = 0; i < 64; i++) {
		color[i].red = i << 10;
		color[i].green = i << 10;
		color[i].blue = i << 10;
		color[i].flags = DoRed | DoGreen | DoBlue;
		if (!XAllocColor(disp,default_cmap, &color[i])) {
			fprintf(stderr,"xscan : error allocating colors\n");
			exit(1);
		}	
		
		colors[i] = color[i].pixel;
	}
	cellsize = 1;

	
	/*	reading informations about scanner 	*/

	ScanInfo(&bytes_per_line, &dpi);
	pixmap_width = bytes_per_line * 8;	
	sprintf(ScanLabel,"Resolution : %d dpi",dpi);

	/*	initializing viewport widget	*/ 
	MakePixMap();	
	XtVaSetValues(ShowLabel,XtNlabel, ScanLabel,NULL);
	XtVaSetValues(InputBox,XtNfromVert, SelectBox, XtNvertDistance,0,
				NULL);
	XtVaSetValues(Canvas, XtNfromHoriz, InputBox, XtNhorizDistance,0,
			      XtNfromVert, SelectBox, XtNvertDistance,0,
				NULL);

	/*		initializing dialoges		*/
	/*       	Message box              	*/

	MessagePshell = XtVaCreatePopupShell("MessagePshell",
					  transientShellWidgetClass,topLevel,
					 NULL);          

	MessageDialog = XtVaCreateManagedWidget("MessageDialog", 
						dialogWidgetClass, 
					        MessagePshell,NULL);        

	/************** 	About dialog-box             *****************/

	AboutPshell = XtVaCreatePopupShell("AboutPshell",
					  transientShellWidgetClass,topLevel,
					 NULL);          

	AboutDialog = XtVaCreateManagedWidget("AboutDialog", dialogWidgetClass, 
					AboutPshell,NULL);        
	XawDialogAddButton(AboutDialog,"OK",PopdownAboutDialog,0);
	XtVaSetValues(AboutDialog, XtNlabel,(XtArgVal)
			"XScan\nWritten by Ulrich Eckhardt\n"
			"Version 1.0\nLast Revision 07.Jul.95",NULL);

	/************** 	Scan dialog-box             *****************/

	ScanPshell = XtVaCreatePopupShell("ScanPshell",
					  transientShellWidgetClass,topLevel,
					 NULL);          

	ScanDialog = XtVaCreateManagedWidget("ScanDialog", dialogWidgetClass, 
					ScanPshell,NULL);        
	XawDialogAddButton(ScanDialog,"stop scanning",PopdownScanDialog,0);
	XtVaSetValues(ScanDialog, XtNlabel,"scanning in progress",NULL);

	/************** 	Save dialog-box             *****************/

	SavePshell = XtVaCreatePopupShell("SavePshell",
					  transientShellWidgetClass,topLevel,
					 NULL);         
	SaveDialog = XtVaCreateManagedWidget("SaveDialog", dialogWidgetClass, 
					SavePshell,NULL);        

	XawDialogAddButton(SaveDialog,"Save",PopdownSaveDialog,(XtPointer)TRUE);
	XawDialogAddButton(SaveDialog,"Cancel",PopdownSaveDialog,
			   (XtPointer)FALSE);

	XtVaSetValues(SaveDialog, XtNlabel,"Save as",XtNvalue,"",NULL);
	value = XtNameToWidget(SaveDialog,"value");
	XtAppAddActions(app_context,DefaultActions,XtNumber(DefaultActions));
	SaveT = XtParseTranslationTable(SaveTransl);  
	XtOverrideTranslations(value,SaveT);


	/***************	Dialog end        ****************/

	XtRealizeWidget(topLevel);

#ifdef _HAS_XPM

	{
		Pixmap		dum = 0;
		Window		iconWindow;
		int		status;

	/*  make a window for the icon */

		iconWindow = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
					 0, 0, 1, 1, 0,
					 BlackPixelOfScreen(screen),
					 BlackPixelOfScreen(screen));
		scan_icon_attr.valuemask = XpmReturnPixels | XpmCloseness;
		scan_icon_attr.colormap = default_cmap;
		/* flag whether or not to free colors when quitting xfig */
		scan_icon_attr.npixels = 0;
		status = XpmCreatePixmapFromData(disp, iconWindow,
			scan, &scan_icon, &dum, &scan_icon_attr);
		if (status == XpmSuccess) {
	    		XResizeWindow(disp, iconWindow,
			  	scan_icon_attr.width,
			  	scan_icon_attr.height);
	    		XSetWindowBackgroundPixmap(disp, iconWindow, scan_icon);
	    		XtVaSetValues(topLevel, XtNiconWindow, iconWindow, NULL);
		}	
	        XtVaSetValues(topLevel, XtNiconWindow, iconWindow, NULL);
	} 
#endif
	XtVaSetValues(ShowLabel, XtNlabel, ScanLabel,
			NULL);
	XtAddCallback(Canvas, XtNreportCallback, Redraw, 0);

	ClrDraw();
	scaninprog = FALSE;

	/*		Main loop 		*/

	XtAppMainLoop(app_context);
}
