#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>

/*
**	   S C R E E N   I M A G E   P R I N T E R (SCRIMPER)
**
**	         Copyright 1986 By Perry S. Kivolowitz
**
**	The author grants permission to duplicate and reproduce
**	this software  provided  that no commercial gain can be
**	had as a consequence of  use  or  reproduction  of this
**	software and that  this and other identifying informat-
**	be left intact.
**
**	This software is provided as  a  service to the readers
**	of Amazing Computing and does not imply  any commitment
**	on behalf of the author to  provide support nor can the
**	author be held liable  for  the  consequences of use or
**	misuse of this software.
**
*/

/*
**	Overview
**
**	This program dynamically builds a  list of screens each time its
**	title bar is selected (note that the window this program creates
**	is only as large as its title bar).  The list of screens is dis-
**	played when the right mouse button  is  depressed (and sometimes
**	when it's just feeling lonely too). Select a screen as you would
**	select an item from a menu.
**
**	Something to note is the way the  screen  selection menu centers
**	each item. This is done by  the  routine ``massage_left_edges.''
**
**	After a screen has been  selected,  the screen selection menu is
**	replaced with a function  selection  menu. There  are  currently
**	three things which scrimper allows you to do:
**
**	(1)	Bring the selected screen to front.
**
**		This is  kind of  neat  especially  when  you  have  many
**		screens open  at once. This  gives  you a sort of  direct
**		access capability instead of many repititions of ``screen
**		to front.''
**
**		When the selected screen  comes  to the front if you push 
**		the left mouse button, the  current front most screen be-
**		comes the actual front screen.
**
**		If you click the right mouse  button, the screen SCRIMPER
**		was run from will  come  back  to the front. This is very
**		useful for quickly checking the status of program running
**		on another  screen  and   whipping   back to the original
**		screen.
**
**	(2)	The selected screen can be printed.
**
**		The selected screen will be brought to the front for half
**		a second (literally) just  for  you to make sure you sel-
**		ected the right screen. Then the screen  SCRIMPER was run
**		from comes back to the front and a requester asks if  you
**		really want to print the screen.
**
**		If yes, SCRIMPER will try to  create a copy of the screen
**		in its own dynamically  allocated  memory. If it can, you
**		are free to modify or even delete  the screen without af-
**		fecting the printout.  If  SCRIMPER can't allocate enough
**		CHIP memory, it  will ask  you  (with a requester) if you
**		still want to print the  screen even though SCRIMPER will
**		have to use the *LIVE* copy.  If that's ok, unpredictable
**		results will occur if  you  modify or  delete  the screen
**		while SCRIMPER is printing.
**
**		What's neat here is  that  you can print any screen. Even
**		if your program has no screen dump capability. If it does
**		(for example, DPaint)  SCRIMPER  is  *still  very useful*
**		since it will baby sit  the  printer  rather than forcing
**		(for example) DPaint to do so.
**
**	(3)	Go Back To Screen Select
**
**		This  is the way to  tell  SCRIMPER  to  forget  that you
**		selected a screen.
**
**	Known Bugs:
**
**	(1)	Assumes 640 wide screen.
**
**	(2)	If printer trouble arises and the user aborts by cancelling
**		the print out from the System Requester,  AmigaDos loses 2K
**		of memory and sometimes tosses its cookies.
**
**	
**	Suggested Improvements
**
**	(1)	Allow variable density printing.  The printer driver sup-
**		ports this directly. Very neat C-A!
**
**	(2)	Allow sections of a screen to  be printed. IE: allow user
**		to sweep out a box  over a  screen  and print only what's
**		inside.
**
**	(3)	Adjust for 320 wide screen dynamically.
**
**	Compilation Information
**
**	This code was developed using Manx C Beta 1.99E, 1.99F, and Manx
**	C 3.20A (a final release).
**
**	Defining PRINTF causes printf's to be loaded  which increases the
**	size of the executable to  9160  from 6520 bytes (approximately). 
**	By not defining PRINTF  you  can be confident of SCRIMPER working
**	from the WORKBENCH.
**
**	Link with printer.o
**
**
**	STRONG SUGGESTION: Avoid burning out your print head and ribbon
**			   by making the background color of the screen
**			   white - I did and got used to it.
*/

#define	WWIDTH		640
#define	MAX_SCREENS	20
#define	SIZE_MI		(sizeof(struct MenuItem))
#define	SIZE_IT		(sizeof(struct IntuiText))

extern void *OpenLibrary();
extern void *GetMsg();
extern void *OpenWindow();
extern void *ItemAddress();

int is_cli = 0;
int which_screen = 0;

char *window_title = "SCReen IMage PrintER V0.6 (PSKivolowitz)";

extern  char *calloc();
struct 	IntuitionBase *IntuitionBase;
struct  GfxBase       *GfxBase;
struct 	Window *w;
struct 	Screen *s[MAX_SCREENS];
char    *stitles[MAX_SCREENS];
struct  IntuiMessage *message;
int 	screen_count = 0;

extern generic_cleanup();
extern screen_init() , screen_pick();
extern func_init() , func_pick();

struct IntuiText print_scrn_it = {
	0 , 1 , JAM2 , 0 , 0 , NULL , (UBYTE *) "print selected screen" , NULL
};

struct IntuiText back_2_scrn_it = {
	0 , 1 , JAM2 , 0 , 0 , NULL , (UBYTE *) "return to screen selection" , NULL
};

struct IntuiText scrn_2_frnt_it = {
	0 , 1 , JAM2 , 0 , 0 , NULL , (UBYTE *) "selected screen to front" , NULL
};

struct MenuItem back_to_screen_select = {
	NULL , -5 , 22 , 0 , 11 , 
	ITEMTEXT | ITEMENABLED | HIGHCOMP ,
	0 , (APTR) &back_2_scrn_it , NULL , NULL , NULL
};

struct MenuItem print_screen = {
	&back_to_screen_select , -5 , 11 , 0 , 11 ,
	ITEMENABLED | ITEMTEXT | HIGHCOMP ,
	0 , (APTR) &print_scrn_it , NULL , NULL , NULL
};

struct MenuItem screen_to_front = {
	&print_screen , -5 , 0 , 0  , 11 ,
	ITEMTEXT | ITEMENABLED | HIGHCOMP ,
	0 , (APTR) &scrn_2_frnt_it , NULL , NULL , NULL
};

struct Menu function_menu = {
	NULL , 
	10 , 0 , 130 , 10 , 
	MENUENABLED ,
	"Select Function" ,
	&screen_to_front
};

struct Menu screen_menu = {
	NULL ,
	10 , 0 , 120 , 10 ,
	MENUENABLED ,
	"Select  Screen" , 
	NULL
};

struct jmptbl {
	struct Menu *menu;
	int (*init)();
	int (*cleanup)();
	int (*pick)();
};

struct jmptbl scrn_jmptbl = {
	&screen_menu ,
	screen_init ,
	generic_cleanup , 
	screen_pick
};

struct jmptbl func_jmptbl = {
	&function_menu , 
	func_init ,
	generic_cleanup , 
	func_pick
};

struct NewWindow nw = {
	0,10,
	WWIDTH,10,
	-1,-1,
	ACTIVEWINDOW | INACTIVEWINDOW | CLOSEWINDOW | MENUPICK ,
	WINDOWDEPTH | WINDOWDRAG | NOCAREREFRESH | WINDOWCLOSE ,
	NULL,NULL,
	NULL , 
	NULL,NULL,0,0,0,0,WBENCHSCREEN
};

#define	IBPtr	struct IntuitionBase * 


alloc_menu_item(p , i , s)
register struct MenuItem **p;
register char *s;
{
	*p = (struct MenuItem *) calloc(1 , SIZE_MI);
	if (*p) {
		(*p)->NextItem = NULL;
		(*p)->Height   = w->WScreen->Font->ta_YSize;
		(*p)->TopEdge  = i * (*p)->Height;
		(*p)->LeftEdge = -5;
		(*p)->Flags    = ITEMTEXT | ITEMENABLED | HIGHCOMP;
		(*p)->Command  = NULL;
		(*p)->SubItem  = NULL;
		(*p)->NextSelect = NULL;
		(*p)->MutualExclude = (long) ~(1 << i);
		alloc_intuitext(&((*p)->ItemFill) , s);
		if (!(*p)->ItemFill) *p = NULL;
		else (*p)->Width = IntuiTextLength((*p)->ItemFill);
	}
}
 
alloc_intuitext(p , s)
register struct IntuiText **p;
register char  *s;
{
	register char *d = NULL;
	register int length = strlen(s) + 1;

	if (length & 1) length++;
	*p = (struct IntuiText *) calloc(1 , SIZE_IT);
	if (*p) {
		d = calloc(1 , length);
		if (d) {
			(*p)->FrontPen  = 0;
			(*p)->BackPen   = 1;
			(*p)->LeftEdge  = 0;
			(*p)->TopEdge   = 0;
			(*p)->DrawMode  = JAM2;
			(*p)->ITextFont = NULL;
			(*p)->NextText  = NULL;
			(*p)->IText     = (UBYTE *) d;
			strcpy(d , s);
		} else *p = NULL;
	}
}

#define	EVEN(S)		((S) & 1 ? (S) + 1 : (S))

get_screens()
{
	register int i = 0;

	Forbid();
	s[0]       = w->WScreen;
	stitles[0] = calloc(1 , EVEN(strlen(s[0]->Title)+1));
	screen_count = 1;
	if (stitles[0]) {
		strcpy(stitles[0] , s[0]->Title);
		while (s[i++]->NextScreen) {
			s[i] = s[i-1]->NextScreen;
			stitles[i] = calloc(1 , EVEN(strlen(s[i]->Title)+1));
			if (stitles[i]) strcpy(stitles[i] , s[i]->Title);
			else {
				Permit();
				return((int) FALSE);
			}
			screen_count++;
		}
	}
	Permit();
	return((int) TRUE);
}

build_menu(menu , scrn)
register struct Menu *menu;
register struct Screen *scrn;
{
	register int i;
	register struct MenuItem *p;

	if (get_screens()) {
		alloc_menu_item(&menu->FirstItem , 0 , stitles[0]);
		if (!menu->FirstItem) return((int) FALSE);
		p = menu->FirstItem;
		for (i = 1; i < screen_count; i++) {
			alloc_menu_item(&(p->NextItem) , i , stitles[i]);
			if (!p->NextItem) return((int) FALSE);
			p = p->NextItem;
		}
		return((int) TRUE);
	} 
	return((int) FALSE);
}
		
main()
{
	extern void massage_left_edges();

	is_cli = 1;
	IntuitionBase = (IBPtr) OpenLibrary("intuition.library" , 0L);
	if (!IntuitionBase) {
#ifdef		PRINTF
		if (is_cli) printf("could not open intuition library\n");
#endif
		exit(1);
	}
	GfxBase = (struct GfxBase *) OpenLibrary("graphics.library" , 0L);
	if (!GfxBase) {
#ifdef		PRINTF
		if (is_cli) printf("could not open graphics library\n");
#endif
		CloseLibrary(IntuitionBase);
		exit(1);
	}
	if (! (w = (struct Window *) OpenWindow(&nw))) {
#ifdef		PRINTF
		if (is_cli) printf("could not open dummy window\n");
#endif
		CloseLibrary(GfxBase);
		CloseLibrary(IntuitionBase);
		exit(1);
	}
	SetWindowTitles(w , window_title ,  -1L);
	set_widths(&function_menu);
	massage_left_edges(&function_menu);
	while (message = (struct IntuiMessage *) GetMsg(w->UserPort)) ReplyMsg(message);
	while (getmenu(&scrn_jmptbl)) ;
out:	while (message = (struct IntuiMessage *) GetMsg(w->UserPort)) ReplyMsg(message);
	SetWindowTitles(w , NULL , NULL);
	CloseWindow(w);
	CloseLibrary(GfxBase);
	CloseLibrary(IntuitionBase);
	exit(0);
}

screen_pick(code)
USHORT code;
{
	which_screen = ITEMNUM(code);
	return(getmenu(&func_jmptbl));
}

	
freeall()
{
	register int i;
	register struct MenuItem *p = screen_menu.FirstItem;
	register struct IntuiText *t;
	register struct MenuItem *pnext;

	for (i = 0; i < screen_count; i++) if (stitles[i]) free(stitles[i]);
	while (p) {
		if (p->ItemFill) {
			t = (struct IntuiText *) p->ItemFill;
			if (t) {
				if (t->IText) free(t->IText);
				free(t);
			}
		}
		pnext = p->NextItem;
		free(p);
		p = pnext;
	}
	screen_menu.FirstItem = NULL;
	screen_count = 0;
}

void massage_left_edges(menu)
register struct Menu *menu;
{
	register int max_width = 0;
	register struct MenuItem *p = menu->FirstItem;

	if (!p) return;
	while (p) {
		if (p->Width > max_width) max_width = p->Width;
		p = p->NextItem;
	}
	p = menu->FirstItem;
	while (p) {
		((struct IntuiText *) (p->ItemFill))->LeftEdge = (max_width - p->Width) / 2;
		p->Width = max_width;
		p = p->NextItem;
	}
}

set_widths(menu)
register struct Menu *menu;
{
	register struct MenuItem *p = menu->FirstItem;

	while (p) {
		p->Width = IntuiTextLength(p->ItemFill);
		p = p->NextItem;
	}
}

func_pick(code)
USHORT code;
{
	register struct Screen *scrn;
	extern struct Screen *verify_screen_pointer();
	register ULONG class;

	if (scrn = verify_screen_pointer())
		switch (ITEMNUM(code)) {

		case 2:	return((int) TRUE);

		case 1: ClearMenuStrip(w);
			dump_screen(scrn,0,0,scrn->Width,scrn->Height);
			return((int) TRUE);

		case 0:	if (scrn == w->WScreen) break;
			ScreenToFront(scrn);
			Wait(1L << w->UserPort->mp_SigBit);
			message = (struct IntuiMessage *) GetMsg(w->UserPort);
			if (message) {
				class = message->Class;
				ReplyMsg(message);
				if (class != INACTIVEWINDOW) ScreenToFront(w->WScreen);
				if (class == CLOSEWINDOW) return((int) FALSE);
			}
			return((int) TRUE);
	}
	DisplayBeep(w->WScreen);
	return((int) TRUE);
}

struct Screen *verify_screen_pointer()
{
	register struct Screen *hoped_for;
	register struct Screen *p;

	if (! (hoped_for = s[which_screen])) return(NULL);
	p = s[0];
	while (p) {
		if (p == hoped_for) return(hoped_for);
		p = p->NextScreen;
	}
	return(NULL);
}

func_init()
{
	ClearMenuStrip(w);
	SetMenuStrip(w , &function_menu);
	return((int) TRUE);
}

generic_cleanup()
{
	ClearMenuStrip(w);
	freeall();
}

screen_init()
{
	if (!build_menu(&screen_menu , NULL)) return((int) FALSE);
	massage_left_edges(&screen_menu);
	SetMenuStrip(w , &screen_menu);
	return((int) TRUE);
}

getmenu(jmptbl)
register struct jmptbl *jmptbl;
{
	register struct MenuItem *mi;
	register ULONG class;
	register USHORT code , code_save;
	register int ret_val;

	
	if (! (*jmptbl->init)()) return((int) FALSE);
	while (1) {
		message = (struct IntuiMessage *) GetMsg(w->UserPort);
		if (!message) {
			Wait(1L << w->UserPort->mp_SigBit);
			continue;
		}
		class = message->Class;
		code  = message->Code;
		ReplyMsg(message);
		switch (class) {

			case CLOSEWINDOW:
				(*jmptbl->cleanup)();
				return((int) FALSE);

			case INACTIVEWINDOW:
			case ACTIVEWINDOW:
				(*jmptbl->cleanup)();
				return((int) TRUE);

			case MENUPICK:
				if (code == MENUNULL) continue;
				while (code != MENUNULL) {
					mi = (struct MenuItem *) ItemAddress(jmptbl->menu , (long) code);
					code_save = code;
					code = mi->NextSelect;
				}
				ret_val = (*jmptbl->pick)(code_save);
				(*jmptbl->cleanup)();
				return(ret_val);
		}
	}
}

