/*	RULER - Workbench Version
 *
 *	See Ruler.doc for directions
 *
 *	Revision History:
 *	-----------------
 *
 *	Version 5.0   21-Apr-1991  ©1991 Dave Schreiber, Chad Netzer, and
 *					 Thad Floryan
 *		Based upon code from:
 *
 *	Version 4.0   29-Dec-1989  ©1989  Chad Netzer and Thad Floryan
 *
 *		Based upon code, idea, and logic from:
 *
 *	Version 1.0   7-Nov-1988   ©1988 Thad Floryan
 *
 *	Revision history:
 *	    21-April-1991 - (Ver. 5.00) (DKS):
 *		1 Window is sized to make room for > 8 point fonts in the
 *		  title bar and main window (for compatibility with AmigaOS
 *		  2.x)
 *		2 Added the ability to put Ruler's window on any screen at
 *		  will.
 *		3 Added the ability to get the font scale from any window.
 *		4 Added menus for:
 *		  * About
 *		  * Quit
 *		  * Change Screen (#2)
 *		  * Change Font (#3)
 *		  These menus are layed out by the 2.x function
 *		  LayoutMenusA().  This function is called only if Ruler
 *		  is running under 2.0 or later (i.e. Ruler is 1.3
 *		  compatible).
 *
 *	    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"
 *
 *	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"
 *
 *	or Dave at:
 *
 *	UUCP:	davids@ucscb.ucsc.edu
 *	BBS:	BBS-JC, "Dave Schreiber"
 *
 *
 *	Building instructions (SAS/C V5.10):
 *
 *	      lc -Lcd -v Ruler
 *
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <workbench/startup.h>
#include <clib/wb_protos.h>

#define AMIGA_2.0 1	/*Delete if include files for 2.0 or later are*/
			/*not available*/

#ifdef AMIGA_2.0

#include <utility/tagitem.h>

struct TagItem menuTag[] =
{
   {TAG_DONE,NULL}
};

#endif

#include "GUI.h"

   /*Some constants*/
#define HEIGHT titleBarHeight+textHeight-2
#define DEF_HEIGHT 12L+HEIGHT
#define RULER_MARK_Y 11L+HEIGHT
#define OFFSET_START_Y 2L+titleBarHeight
#define BIG_TIC_Y 5L+HEIGHT
#define SMALL_TIC_Y 9L+HEIGHT
#define TEXT_Y 3L+HEIGHT
#define DRAW_AREA_Y 3L+titleBarHeight
#define DRAW_AREA_HEIGHT 24L+textHeight

#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();

/* Prototypes for functions defined in ruler5.c */
void __regargs release_resources(void);
void __regargs error_exit(void);
int  __regargs handleMenu(USHORT NUM);
void __regargs printAboutWindow(void);
void __regargs changeFSize(void);
void __regargs changeScreen(void);
void __regargs readWBArgs(void);

UWORD		titleBarHeight,textHeight,textWidth,screenWidth;

struct NewWindow window_def =
{
	0, 0,		      /* Initial LeftEdge, TopEdge */
	241, 17,		/* Default pixel-width, pixel-height */
	0, 1,			/* DetailPen, BlockPen */
	CLOSEWINDOW	|	/* IDCMP flags */
	MENUPICK	|
	NEWSIZE,
	WINDOWDRAG	|	/* window flags */
	WINDOWDEPTH	|
	WINDOWCLOSE	|
	WINDOWSIZING	|
	ACTIVATE	|
	SMART_REFRESH	|
	NOCAREREFRESH,
	NULL,			/* Gadget list */
	NULL,			/* checkmark stuff */
	NULL,			/* 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 */
	CUSTOMSCREEN		/* screen type */
};

struct Window		*window;
struct RastPort 	*rp;
struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*GadToolsBase;
struct Library		*IconBase;

struct Screen		*screen;
struct IntuiMessage	*sys_message;
ULONG			class;
USHORT			code;
int			resource_state;
long	fsize;		/* The font size of ruler scale */
long	w;		/* character width of ruler	*/

APTR			visualInfo;

char *version =
	"Text Ruler   V5.00 (Workbench)   21-Apr-91  ©1991                            ";

extern struct WBStartup *WBenchMsg;

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

_main ()
{

	long	fivec;		/* pixel size of five chars	*/
	long	w_lim;		/* pixel width (w * fsize)      */
	long	x;		/* present window x coordinate	*/
	long	y;		/* present window y coordinate	*/

	char	buf[5];

	int	redoWindow;	/*Boolean flag*/

	resource_state = 0;

/*
 *	Open Intuition and Graphics so we can get screen size and
 *	default font information.
 */
	if((IntuitionBase = OpenLibrary ("intuition.library", NULL))==NULL)
	    error_exit();
	++resource_state;

	if((GfxBase = OpenLibrary ("graphics.library", NULL))==NULL)
	    error_exit();
	++resource_state;

	GadToolsBase = OpenLibrary("gadtools.library",NULL);
	if(GadToolsBase)
	   window_def.IDCMPFlags|=(ACTIVEWINDOW|INACTIVEWINDOW);

/*
 *	Get WorkBench screen information
 */
	screen=window_def.Screen=IntuitionBase->ActiveScreen;

	w = 30L;	/* default ruler is 30 chars for a filename */
	 /*Get default scale*/
	fsize=textWidth=GfxBase->DefaultFont->tf_XSize;

	readWBArgs();
	w_lim = w * fsize;

/*
 *	Main loop
 */

	for ever
	{

/*	Get width of screen
 */
	    screenWidth=screen->Width;

/*	Determine spacing from current font information
 */
	    titleBarHeight = screen->Font->ta_YSize;
	    textHeight = GfxBase->DefaultFont->tf_YSize;

/*
 *	Now calculate window sizing and placement parameters and setup title.
 */
	    window_def.Width	= w_lim + LEFT_OFFSET + 1;
	    window_def.Height	= DEF_HEIGHT;
	    window_def.Title	= (UBYTE *)version;
/*
 *	Shorten the window if it would be wider than the screen
 */
	    if (window_def.Width > screenWidth )
	    {
	       window_def.Width = screenWidth;
	       w_lim = (window_def.Width) - 1 - LEFT_OFFSET;
	    }

/*
 *	Open the ruler display window.
 */
	    window = OpenWindow (&window_def);

	    if (window == NULL)
	    {
		error_exit();
	    }
	    ++resource_state;

/*
 *	Set screen and window titles
 */
	    SetWindowTitles(window,version,"Text Ruler V5.00");

/*
 *	If running under 2.0 or later, adjust menu spacing information
 *	to compensate for a non-8 point font size.
 */
	    if(GadToolsBase != NULL)
	    {
	       visualInfo = (APTR)GetVisualInfoA(screen,menuTag);
	       LayoutMenusA(&Menu1,visualInfo,menuTag);
	       FreeVisualInfo(visualInfo);
	    }

/*
 *	Install menus
 */
	    SetMenuStrip(window,&Menu1);
/*
 *	Get pointer to raster port.
 */
	    rp = window -> RPort;

/*
 *	Inner loop (loop until the window needs to be closed and another
 *	opened).
 */
	    redoWindow=FALSE;

	    while(!redoWindow)
	    {
	    redoWindow=FALSE;
	    fivec		= 5L * fsize;
/*
 *	Use RectFill to blank the window area and set pen for drawing.
 */
		SetAPen(rp, NULL);
		RectFill(rp, LEFT_OFFSET+1, DRAW_AREA_Y, w_lim + LEFT_OFFSET-1,
		      DRAW_AREA_Y+DRAW_AREA_HEIGHT);
/*
 *	Now set pen to draw.
 */
		SetAPen(rp, 1L);
/*
 *	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, NULL, OFFSET_START_Y, LEFT_OFFSET,
		    DRAW_AREA_Y+DRAW_AREA_HEIGHT+1);

/*
 *	Draw ruler and text
 */
		for (x = NULL; x < (w_lim + fsize); x += fsize)
		{
			/*big tic every 5 chars*/
		    if ((x % fivec) == NULL)  y = BIG_TIC_Y;
		    else y = SMALL_TIC_Y;     /* small tic every char  */
		    Move (rp, x + LEFT_OFFSET, y);
		    Draw (rp, x + LEFT_OFFSET, RULER_MARK_Y);
/*
 *	Label ruler.
 *	The test for position `fivec' (in the Move()) is for centering ``5''
 *	differently than the centering for two-digit numbers.
 */
		    if ((x > NULL) && (x < w_lim) && ((x % fivec) == NULL))
		    {
			stci_d(buf,x/fsize);
			Move (rp, (x == fivec)
			   ? (x - textWidth + (textWidth/2) + LEFT_OFFSET)
			   : (x -  textWidth + LEFT_OFFSET),
			   TEXT_Y);
			Text (rp, buf, (long) strlen (buf));
		    }
		}

		SetAPen(rp,1);
		Move(rp,w_lim+LEFT_OFFSET,titleBarHeight);
		Draw(rp,w_lim+LEFT_OFFSET,DEF_HEIGHT);

/*
 *	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;
		code = sys_message -> Code;
		ReplyMsg(sys_message);
/*
 *	Dump uneeded messages (respond only to the last on LIFO queue).
 */
		while (sys_message = GetMsg(window -> UserPort))
		{
		    ReplyMsg(sys_message);
		}

		     /*Handle various IDCMP events*/
		switch(class)
		{
		    case CLOSEWINDOW:
			release_resources();
			exit (NORMAL);

		    case NEWSIZE:
			w_lim = (window -> Width) - 1 - LEFT_OFFSET;
			break;

		    case INACTIVEWINDOW:
			SetWindowTitles(window,version,"Text Ruler V5.00");
			break;

		    case MENUPICK:
			redoWindow=handleMenu(code);
		}
	    }
	}
}

/**********************************************************************
 *
 *	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 __regargs release_resources()
{
	switch (resource_state)
	{
	    case 3: ClearMenuStrip(window);
		    CloseWindow (window);
	    case 2: CloseLibrary(GfxBase);
	    case 1: CloseLibrary(IntuitionBase);
	}
	if(GadToolsBase != NULL)
	   CloseLibrary(GadToolsBase);
}

/**********************************************************************
 *
 *	error handler
 */
void __regargs error_exit()
{
	release_resources();
	exit (FATAL);
}

/*
 *	Decode menu choice
 */

int __regargs handleMenu(USHORT NUM)
{
   struct MenuItem *item;
   USHORT menuNum,itemNum;

   while(NUM != MENUNULL)
   {
      menuNum=MENUNUM(NUM);
      itemNum=ITEMNUM(NUM);
      item=(struct MenuItem *)ItemAddress(&Menu1,NUM);

      if(menuNum==0)
	 if(itemNum==0)
	    printAboutWindow();     /*About*/
	 else
	 {
	    release_resources();    /*Quit*/
	    exit(NORMAL);
	 }
      else
	 if(itemNum==0)
	 {
	    changeFSize();          /*Change Scale*/
	    return(FALSE);
	 }
	 else
	 {
	    changeScreen();         /*Change Screen*/
	    return(TRUE);
	 }

      NUM=item->NextSelect;
   }
   return(0);
}

/*
 *	Display the About window
 */
void __regargs printAboutWindow()
{
   struct Window *aboutWdw;

   if((NewAboutWindow.LeftEdge=(screenWidth-NewAboutWindow.Width)/2) > 200)
      NewAboutWindow.LeftEdge = 200;

/*
 *	Determine About window position
 */
   NewAboutWindow.TopEdge=(screen->Height-NewAboutWindow.Height)/5;
   NewAboutWindow.Height=titleBarHeight-15+ABOUT_WDW_HEIGHT;

/*
 *	Get the screen the about window will appear on (same as the
 *	screen the main window is on).
 */
   NewAboutWindow.Screen = (struct Screen *) screen;

/*
 *	Open window
 */

   aboutWdw=(struct Window *)OpenWindow(&NewAboutWindow);
   if(aboutWdw==NULL)
      return;

/*
 *	Print window text
 */

   PrintIText(aboutWdw->RPort,&IText11,0,titleBarHeight-15);

/*
 *	Wait for input close gadget to be pressed, then close window and
 *	return.
 */
   Wait(1<<aboutWdw->UserPort->mp_SigBit);

   CloseWindow(aboutWdw);
   return;
}

/*
 *	Change scale
 */
void __regargs changeFSize()
{
   ULONG IDCMPFlags;

   IDCMPFlags=window_def.IDCMPFlags;
   ModifyIDCMP(window,INACTIVEWINDOW);

   for ever
   {
       /*Wait for IDCMP event*/
       Wait (1L << window -> UserPort -> mp_SigBit);
       sys_message = GetMsg(window -> UserPort);
       class = sys_message -> Class;
       ReplyMsg(sys_message);

/*
 *     If another window was clicked, get the horizontal font size of
 *     the current font in that window
 */

       if(class==INACTIVEWINDOW)
       {
	 if((IntuitionBase->ActiveWindow->RPort->Font->tf_Flags & FPF_PROPORTIONAL) == 0)
	    fsize=IntuitionBase->ActiveWindow->RPort->Font->tf_XSize;
	 else
	 {
	    /*If the window is using a proportional font, print an error*/
	    SetWindowTitles(window,version,"Can't change scale:  window font is proportional.");
	    DisplayBeep(window->WScreen);
	 }
	 ModifyIDCMP(window,IDCMPFlags);
	 return;
       }
   }
}

/*
 *	Change the screen that the window is displayed on.
 */

void __regargs changeScreen()
{
   for ever
   {
/*
 *	Wait for user to click on another window (hopefully in another
 *	screen).
 */
       ModifyIDCMP(window,INACTIVEWINDOW);
       Wait (1L << window -> UserPort -> mp_SigBit);
       sys_message = GetMsg(window -> UserPort);
       class = sys_message -> Class;
       ReplyMsg(sys_message);
       if(class==INACTIVEWINDOW)
       {
	 /*Get active screen*/
	 window_def.Screen=screen=IntuitionBase->ActiveScreen;

	 /*Clear menu strip and close window*/
	 ClearMenuStrip(window);
	 CloseWindow(window);
	 resource_state--;

	 return;
       }
   }
}

 /*Get arguments from Ruler's icon*/
void __regargs readWBArgs()
{
   char  *Arg;
   struct DiskObject *obj;

      /*Open library*/
   IconBase=(struct IconBase *)OpenLibrary("icon.library",0L);
   if(IconBase == NULL)
   {
      release_resources();
      exit(200);
   }

      /*Get the icon's information*/
   obj=GetDiskObject(WBenchMsg->sm_ArgList[0].wa_Name);

      /*Check for size tool type*/
   Arg=FindToolType(obj->do_ToolTypes,"size");
   if(Arg != NULL)
      w=atol(Arg);

      /*Check for scale tool type*/
   Arg=FindToolType(obj->do_ToolTypes,"scale");
   if(Arg != NULL)
      fsize=atol(Arg);

      /*Check to make sure everything's in bounds*/
   if(fsize < 5 || fsize > 50 || w < 12)
      exit(300);

      /*Free the allocated disk object*/
   FreeDiskObject(obj);

   CloseLibrary(IconBase); /*Close the icon library*/
   return;
}

