/*	RULER
 *
 *      Opens a borderless WorkBench window in which is contained a ruler
 *      for the purpose of aligning or constraining text; the default ruler
 *      is 30 characters with an 8 pixel scale and is intended to help
 *      prevent entering too-long filenames.
 *      
 *      Usage:
 *      
 *      	CLI> RUN  RULER  [ size ] [ scale ]
 *      
 *      where `size' is the number of ruler markings, and `scale' is the
 *      width between each marking (a particular font width for example).
 *      
 *	Evoking the command with an argument of "?", such as:
 *
 *		CLI> RULER ?
 *
 *	will print out a help message detailing what values are valid for
 *	your particular Workbench environment.
 *      
 *
 *	The window, of course, can be moved to any convenient location on the
 *	screen, and resized with an invisible resizing gadget found in the
 *	lower right corner.
 *
 *	Version 4.0   29-Dec-1989  (copyright)1989  Chad Netzer and Thad Floryan
 *
 *		Based upon code, idea, and logic from:
 *
 *	Version 1.0   7-Nov-1988   (copyright)1988 Thad Floryan
 *
 *	Revision history:
 *	    29-December-1989 - (Ver. 4.0) - Added an offset so that you can
 *	        just stick the window on the left edge of the workbench screen,
 *		and it will automatically be lined up with the characters.
 *		Fixed some more minor bugs and rearranged the code a bit.
 *		Added ARP support for those who want it. (CFN)
 *
 *	    09-March-1989 - (Ver. 3.1) - Fixed a visual bug so that scale can
 *		no longer be less than eight. (CFN)
 *
 *	    06-Dec-1988 - (Ver. 3.0) - Added support for measuring fonts of
 *		varying widths (selectable scale). (CFN)
 *
 *	    22-Nov-1988 - (Ver. 2.2) - More adjustments to code, No major
 *	    	changes.  Program size is slightly smaller.  (CFN)
 *
 *	    14-Nov-1988 - (Ver. 2.1) - Fixed a few minor (harmless) bugs.  The
 *		right edge of the ruler is now always redrawn after resizing.
 *		I got rid of that GOTO statement (which in 'C', is considered a
 *		bug. :-)  Version 2.1 now lets you open a ruler to be as low as
 *		12 characters wide, which 2.0 only advertised. (CFN)
 *
 *	    13-Nov-1988 - (Ver. 2.0) - I added minor enhancements, most
 *		noteably, the ability to resize the window, and support for 
 *		overscanned screens.	(CFN)
 *
 *
 *	Feel welcome to use this program for any non-commercial purposes or
 *	for your personal learning.  Commercial users are requested to contact
 *	Thad at either:
 *
 *	UUCP:	thad@cup.portal.com (OR) ..!sun!portal!cup.portal.com!thad
 *	BBS:	BBS-JC, 415/961-7250 (300/1200/2400), "Thad Floryan" | "SYSOP"
 *
 *	or Chad at:
 *
 *	UUCP:	chad@ucscb.ucsc.edu	(during school term)
 *	UUCP:	chad@cup.portal.com	(holidays, summer, etc.)
 *	BBS:	BBS-JC, 415/961-7250 (300/1200/2400), "Chad Netzer"
 *
 *
 *	Building instructions (Manx Aztec C):
 *
 *			************************
 *			***  Non-ARP Version ***
 *			************************
 *
 *			cc ruler4
 *			ln ruler4 -lc
 */

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

#define NEW(typ)		AllocMem((ULONG)sizeof(typ), MEMF_CLEAR)
#define FREE(p,typ)		FreeMem(p,(ULONG)sizeof(typ))

#define	ever (;;)	/* used for infinite loops */

#define FATAL 20	/* exit code */
#define WARN  10	/* exit code */
#define NORMAL 0	/* exit code */

#define LEFT_OFFSET 3L	/* "dead space" on left side of ruler margin */

extern void	*OpenLibrary();
extern void	*OpenWindow();
extern void	*GetMsg();
extern void	*AllocMem();
extern void	ReplyMsg();
void		release_resources();
void		error_exit();

struct NewWindow window_def =
{
	240, 0,			/* Initial LeftEdge, TopEdge */
	241, 25,		/* Default pixel-width, pixel-height */
	0, 1,			/* DetailPen, BlockPen */
	CLOSEWINDOW	|	/* IDCMP flags */
	NEWSIZE,
	WINDOWDRAG	|	/* window flags */
	WINDOWDEPTH	|
	WINDOWCLOSE	|
	WINDOWSIZING	|
	SMART_REFRESH	|
	NOCAREREFRESH	|
	BORDERLESS	|
	RMBTRAP,
	NULL,			/* Gadget list */
	NULL,			/* checkmark stuff */
	(UBYTE *)"",		/* window title (to be filled in later) */
	NULL,			/* custom screen pointer */
	NULL,			/* bitmap pointer */
	97 + LEFT_OFFSET, 0,	/* Minwidth, no MinHeight */
	-1, 0,			/* no MaxWidth, MaxHeight */
	WBENCHSCREEN		/* screen type */
};

struct Window		*window;
struct RastPort 	*rp;
struct Library		*IntuitionBase;
struct Library		*GfxBase;
struct Screen		*screen;
struct IntuiMessage	*sys_message;
ULONG			class;
int			resource_state;

char *version = 
	"Text Ruler   V4.0   29-Dec-89 \xA91989 Thad Floryan and Chad Netzer"
	"                                 "; /* padding for "nice" title */

char *help_text =				/* help message on startup */
	"\n"
	"%40s\n"
	"Opens a borderless WorkBench window in which is contained a ruler\n"
	"for the purpose of aligning or constraining text; the default ruler\n"
	"is 30 characters with an 8 pixel scale and is intended to help\n"
	"prevent entering too-long filenames.\n\n"
	"Usage:\n\n\t"
	"CLI> RUN  %s  [ size ] [ scale ]\n\n"
	"where `size' is the number of ruler markings, and `scale' is the\n"
	"width between each marking (a particular font width for example).\n\n"
	"`size' must be between 12 and %ld inclusive, and defaults to 30.\n"
	"`scale' cannot be greater than %ld or less than 8.  The default is 8."
	"\n\n";

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

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

	long	fivec;		/* pixel size of five chars	*/
	long	fsize;		/* The font size of ruler scale */
	long	max_scale;	/* The max scale size possible  */
	long	max_width;	/* maximum size of ruler	*/
	long	w;		/* character width of ruler	*/
	long	w_lim;		/* pixel width (w * fsize)	*/
	long	x;		/* present window x coordinate	*/
	long	y;		/* present window y coordinate	*/
	char	buf[5];

	long	success;	/* indicates bad return values	*/

	resource_state = 0;

/*
 *	Open Intuition so we can get the screen size limits.
 */
	IntuitionBase = OpenLibrary ("intuition.library", 0L);
	if (IntuitionBase == 0L)
	{
	    error_exit("?Cannot open intuition.library\n");
	}
	++resource_state;

/*
 *	Get WorkBench screen information
 */
	screen = NEW(struct Screen);	/* Allocate a buffer for screen data */
	if (screen == 0L)
	{
	    release_resources();
	    error_exit("?ran out of available memory\n");
	}
	++resource_state;
	success = GetScreenData(screen, (ULONG) sizeof (struct Screen), 
				WBENCHSCREEN, 0L);
	if (success == FALSE)
	{
	    error_exit();
	}
/*
 *	Set up defaults...
 */
	w = 30L;	/* default ruler is 30 chars for a filename */
	fsize = 8L;	/* default font scale is 8 pixels per char  */

/*
 *	find maximum values, adjusted for morerows
 */
	max_width = (screen->Width - LEFT_OFFSET) / 8;
	max_scale = (screen->Width - 2) / 12;

/*
 *	Parse command line to obtain requested parameters.
 */
	if (argc > 1)
	{
	    if (*argv[1] == '?' ||
		*argv[1] == '-' ||
		*argv[1] == 'h' ||
		*argv[1] == 'H'   )
	    {
		printf(	help_text,
			version, argv[0], max_width, max_scale);

		release_resources();
		exit (NORMAL);
	    }
	}

	if (argc > 3)
	{
	    printf("?Too many arguments, ``%s ?'' for help\n", argv[0]);
	    release_resources();
	    exit (WARN);
	}

	if ((argc >= 2) && (argc <= 3))
	{
	    w = atol(argv[1]);
	    if ( w < 12L  ||  w > max_width )
	    {
	        printf("?`size' must be >= 12 and <= %ld\n", max_width);
	        release_resources();
	        exit (WARN);
	    }
	}

	if (argc == 3)
	{ 
	    fsize = atol(argv[2]);
	    if (fsize < 8)
	    {
	    	printf("?Font scale must be at least 8!\n");
		release_resources();
		exit (WARN);
	    }
	    if (fsize > max_scale)
	    {
	        printf("?Font size can be NO larger than %ld!\n", max_scale);
	        release_resources();
	        exit (WARN);
	    }
	}

/*
 *	Now calculate window sizing and placement parameters and setup title.
 */
	w_lim               = w * fsize;
	window_def.Width    = w_lim + LEFT_OFFSET + 1;
	window_def.LeftEdge = (window_def.Width > 300L) ? 0 : 240;
	window_def.Title    = (UBYTE *)version;
	fivec		    = 5L * fsize;
/*
 *	Bomb out if requested ruler size would be larger than the screen.
 */
	if (window_def.Width > screen -> Width )
	{
	    error_exit("?Ruler too large for WorkBench screen\n");
	}

/*
 *	Open up the graphics library.
 */
	GfxBase = OpenLibrary ("graphics.library", 0L);
	if (GfxBase == 0L)
	{
	    error_exit("?Cannot open graphics.library\n");
	}
	++resource_state;

/*
 *	Open the ruler display window.
 */
	window = OpenWindow (&window_def);
	if (window == 0L)
	{
	    error_exit("?Cannot open window\n");
	}
	++resource_state;
/*
 *	Get pointer to raster port.
 */
	rp = window -> RPort;
	SetAPen (rp, 0L);
/*
 *	Use RectFill to blank the window area and set pen for drawing.
 */
	for ever
    	{
	    SetAPen(rp, 0L);
 	    RectFill(rp, 0L, 10L, w_lim + LEFT_OFFSET, 24L);
/*
 *	Now set pen to draw.
 */
	    SetAPen(rp, 1L);
/*
 *	Because we've used a BORDERLESS window, must fill in some of the
 *	title-/drag-bar space near the lower left where the text starts.
 */
	    Move(rp, 28L, 8L);
	    Draw(rp, 31L, 8L);
/*
 *	Because we've used a BORDERLESS window, must draw our own line beneath
 *	the title bar to make the bar the same height as the window gadgets.
 *	The `28' is the pixel position just to the right of the close gadget.
 *	The `53' is the pixel width of the depth-arranging gadgets in the upper
 *	right corner of the window.
 */
	    Move(rp, 28L,           9L);
	    Draw(rp, (w_lim + LEFT_OFFSET - 53L), 9L);

/*
 *	When I use the ruler, I like to jam it up against the left hand side of
 *	my workbench screen, so that it to measure something on the CLI.
 *	However, the CLI is offset by a small amount, so I compensate by
 *	starting the "virtual" area of the ruler a little to the right...
 *
 *	Fill in the offset area that is unused.
 */
 	    RectFill(rp, 0L, 9L, LEFT_OFFSET, 24L);

/*
 *	Shorten virtual ruler size by subtracting LEFT_OFFSET from w_lim.
 */
/* 	    w_lim -= LEFT_OFFSET;*/

/*
 *	Draw ruler and text
 */
	    for (x = 0L; x < (w_lim + fsize); x += fsize)
	    {
		if (x >= w_lim)		/* right edge		 */
		    {
			x = w_lim;
			y = 10L;
		    }
		else if (x == 0L)	   y = 10L;	/* left edge		 */
		else if ((x % fivec) == 0L)  y = 19L;	/* big tic every 5 chars */
		else                       y = 22L;	/* small tic every char  */
		Move (rp, x + LEFT_OFFSET, y);
		Draw (rp, x + LEFT_OFFSET, 24L);
/*
 *	Label ruler.
 *	The test for position `fivec' (in the Move()) is for centering ``5''
 *	differently than the centering for two-digit numbers.
 */
		if ((x > 0L) && (x < w_lim) && ((x % fivec) == 0L))
		{
		    sprintf (buf, "%2ld", (x / fsize));
		    Move (rp, (x == fivec) ? (x - 12L + LEFT_OFFSET)
		    			   : (x -  8L + LEFT_OFFSET), 17L);
		    Text (rp, buf, (long) strlen (buf));
		}
	    }
/*
 *	Wait for gadget activation.  No busy-/spin-/wait-loops here!
 */
	    Wait (1L << window -> UserPort -> mp_SigBit);
/*
 *	Retrieve LAST message in Message Port
 */
	    sys_message = GetMsg(window -> UserPort);
	    class = sys_message -> Class;
	    ReplyMsg(sys_message);
/*
 *	Dump uneeded messages (respond only to the last on LIFO queue).
 */
	    while (sys_message = GetMsg(window -> UserPort))
	    {
		ReplyMsg(sys_message);
	    }

	    if (class == CLOSEWINDOW)
	    {
		release_resources();
		exit (NORMAL);
	    }

	    if (class == NEWSIZE)
	    {
		w_lim = (window -> Width) - 1 - LEFT_OFFSET;
	    }
	}
}

/**********************************************************************
 *
 *	cleanup routine
 *
 *	The ``switch'' on resource_state is to assure we don't attempt to
 *	free something we don't have.  Items are released in the reverse
 *	order they were obtained (by "falling thru" the cases).
 */
void release_resources()
{
	switch (resource_state)
	{
	    case 4: CloseWindow (window);
	    case 3: CloseLibrary(GfxBase);
	    case 2: FREE (screen, struct Screen);
	    case 1: CloseLibrary(IntuitionBase);
	}
}

/**********************************************************************
 *
 *	error handler
 */
void error_exit(message)
	char	*message;
{
	if (message == 0L)
	{
	    printf("Internal failure, exiting gracefully...\n");
	}
	else
	{
	    printf(message);
	}
	release_resources();
	exit (FATAL);
}
