/*     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);
}
