#ifdef TALKTOREXX
/*
 *   This program is an example of how to add a Rexx port to a given
 *   program.  I thought it particularly appropriate to grab a program
 *   off Fred Fish Disk 1.  All the REXX stuff is bracketed by `ifdef
 *   TALKTOREXX', so you can identify it easily.  If you compile with
 *   TALKTOREXX unset, you will get the default program with no REXX
 *   port.
 *
 *   The REXX port on this program adds another 3K to the executable
 *   size, but a lot of functionality comes with that.  You can draw
 *   from Rexx, spawn macros from Rexx, etc.  But go to the next
 *   TALKTOREXX for more information.
 *
 *   To run a rexx macro on startup, simply give that rexx macro as
 *   part of the command line, as in
 *
 *      freedraw sample
 *
 *   or
 *
 *      freedraw bspline 20 100 20 20 280 20 280 100
 *
 *   All modifications are by Radical Eye Software, and all modifications
 *   are placed in the public domain.
 */
#endif
/************************************************************************/
/***                FreeDraw - PD graphics for Amiga                  ***/
/***                                                                  ***/
/***      This is an extremely simple graphics editor which works in  ***/
/***   the windowing environment of the Amiga.  It is very limited    ***/
/***   in features, but I hope to add a lot more, and I would be      ***/
/***   happy to receive assistance from anyone who wants to give it.  ***/
/***      The basic idea of this program is to provide some minimal   ***/
/***   image editing functions which can be used to develop images    ***/
/***   for other programs.  I know there will be a lot of Paint type  ***/
/***   type programs avaialable soon, but what are we supposed to use ***/
/***   now?  The most important features to add now will probably be  ***/
/***   those related to "cut and paste", disk srtorage and retrieval, ***/
/***   and "single-pixel" editing like Mac's "fatbits".               ***/
/***      I intend to use the IFF standard for the image storage and  ***/
/***   retrieval and will be coding a "Files" menu soon.  The work    ***/
/***   required for "cut and paste" should be almost trivial, but I   ***/
/***   still may not get to it for a while.  Fatbits editing from one ***/
/***   window to another involves some manipulations of the RastPorts ***/
/***   which still elude me, as I have only recently begun to use the ***/
/***   Amiga and don't yet understand some important details of these ***/
/***   structures.  This would be a great item for one of the genius  ***/
/***   members of the Amiga programming community to provide.         ***/
/***      There are only two menu topics in this version, so using it ***/
/***   is really quite easy.  Boxes are not allowed to be drawn in    ***/
/***   areas outside of the window where border gadgets are located,  ***/
/***   and the pen-draw mode also clips to these same boundaries.  If ***/
/***   you have begun to draw a box by clicking the left button while ***/
/***   the cursor is located in the FreeDraw window, then you can     ***/
/***   cancel that box by clicking the right button.  In the pen mode ***/
/***   pressing and holding the left button will draw.   Colors are   ***/
/***   selected by simply releasing the menu button over the desired  ***/
/***   color in the Color menu.   The erase feature always clears the ***/
/***   window to the currently selected color.                        ***/
/***      This is no gem of programming style, but you're getting it  ***/
/***   for the right price so be patient with its design flaws.  New  ***/
/***   versions will appear here on BIX as soon as I can get them in  ***/
/***   shape for release.  I apologize to anyone who objects to my    ***/
/***   lack of coding grace, but I just want to get the project off   ***/
/***   the ground, and improvements will be forthcoming.  There are   ***/
/***   a lot of comments, but I didn't know what needed to be made    ***/
/***   clear so I just commented everything.                          ***/
/***                                                                  ***/
/***      If you like the idea of a PD graphics program and would be  ***/
/***   interested in doing some development work, then please write   ***/
/***   me at the address listed below, or call if you prefer.  I do   ***/
/***   want to know if there is any interest in such a project, so    ***/
/***   I will be glad to discuss any ideas you might have.  Also, as  ***/
/***   I do not currently use CompuServe or any other major nets, I   ***/
/***   would appreciate if someone would post this listing there.     ***/
/***      I hope somebody enjoys this.  Have Fun.                     ***/
/***                                           Rick Ross 11/14/85     ***/
/***                                                                  ***/
/***        My address:                                               ***/
/***                    Richard M. Ross, Jr.                          ***/
/***                    Eidetic Imaging                               ***/
/***                    740 N. 22nd Street                            ***/
/***                    Philadelphia, PA  19130                       ***/
/***                                                                  ***/
/***                    Phone - (215) 236-7388                        ***/
/************************************************************************/
char *VERSION = "Freedraw 0.01 by Richard M. Ross" ;
/*  compiler directives to fetch the necessary header files */

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfx.h>
#include <graphics/regions.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <graphics/gfxbase.h>
#include <devices/keymap.h>
#include <hardware/blit.h>

/*   These definitions are used by intuition for
 *   calls to OpenLibrary() in order to ensure
 *   that an appropriate ROM revision is
 *   available.
 */

#define INTUITION_REV 1L
#define GRAPHICS_REV  1L

/*   Intuition always wants to see these declarations */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

/*   This is the Window structure declaration.
 *   Nothing fancy is going on here, but note
 *   the Flags and IDCMPFlags members of the
 *   structure define which messages will be
 *   sent by Intuition.  I haven't used them all
 *   and if you want to change the settings you
 *   should probably do it her instead of using
 *   ModifyIDCMP later.
 */

struct NewWindow NewWindow = {
   10,
   10,
   600,
   180,
   0,
   1,
   CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS | MENUPICK
   | NEWSIZE | INACTIVEWINDOW,
   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG
   | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE,
   NULL,
   NULL,
   (UBYTE *)"AMIGA FreeDraw 0.01",
   NULL,
   NULL,
   100, 35,
   -1, -1,
   WBENCHSCREEN,
};
#ifdef TALKTOREXX
/*
 *   We need our include file.
 */
#include "minrexx.h"
/*
 *   These are the REXX functions defined at the bottom of the file.
 */
void rexxcolor(), rexxbox(), rexxfbox(), rexxline(), rexxtofront(),
    rexxtoback(), rexxexit(), rexxversion(), rexxspawn() ;
int disp() ;
/*
 *   Here is our command association list.  Note that in this case,
 *   we are setting the userdata field to be a function to call.
 *   Dispatch will still take place through disp(), so common head
 *   and tail stuff can go there.
 *
 *   Commands are all lower case, so we match either upper or lower.
 *   (This is a requirement of minrexx.)
 */
struct rexxCommandList rcl[] = {
   { "color", (APTR)&rexxcolor },
   { "box", (APTR)&rexxbox },
   { "fbox", (APTR)&rexxfbox },
   { "line", (APTR)&rexxline },
   { "tofront", (APTR)&rexxtofront },
   { "toback", (APTR)&rexxtoback },
   { "exit", (APTR)&rexxexit },
   { "version", (APTR)&rexxversion },
   { "spawn", (APTR)&rexxspawn },
   { NULL, NULL } } ;
#endif
/*******************************************************************/
/*      DrawBox - Simple routine to draw an unfilled rectangle     */
/*   It accepts the  coordinates of the top-left and lower-right   */
/*   points of the rectangle, a pointer to the Window structure,   */
/*   and the color in which to render the rectangle.  The current  */
/*   FgPen color of the window is preserved thru the call.  No     */
/*   clipping is done.                                             */
/*******************************************************************/
void DrawBox( tlx, tly, brx, bry, window, color )
SHORT tlx, tly;                  /* top-left x,y coordinates */
SHORT brx, bry;                  /* lower-right x,y coordinates */
struct Window *window;           /* pointer to target window */
BYTE color;                      /* color to use for render */
   {
   BYTE OldColor = window->RPort->FgPen;   /* save window's FgPen */

   SetAPen( window->RPort, (long)color );        /* set draw color for box  */
   Move(window->RPort, (long)tlx, (long)tly);          /* move to top-left point  */
   Draw(window->RPort, (long)brx, (long)tly);          /* and draw to each of the */
   Draw(window->RPort, (long)brx, (long)bry);          /* four corners of the box */
   Draw(window->RPort, (long)tlx, (long)bry);
   Draw(window->RPort, (long)tlx, (long)tly);
   SetAPen( window->RPort, (long)OldColor );     /* restore old FgPen */
   }


/*********************************************************/
/*                 Color Select Menu                     */
/*                                                       */
/*      This is where the menu for color selection is    */
/*   defined.  It should be flexible enough to allow for */
/*   increased palette sizes, but this version is only   */
/*   for the 4-color mode of the WorkBench screen.       */
/*********************************************************/

/*   A few definitions are needed here.
 *   Note that MAXPAL should be increased
 *   to allow for palette larger than
 *   four colors.    
 */
#define ITEMSTUFF (ITEMENABLED | HIGHBOX)
#define CW 40
#define CH 25
#define MAXPAL 4

/*   declare enough storage for required
 *   number of menu items and associated
 *   images.  This menu will be using
 *   graphics renditions of menu items,
 *   so the Image structures must be
 *   declared.  This menu is modeled after
 *   the one found in the IconEd source.
 */
struct MenuItem coloritem[MAXPAL];
struct Image colorimage[MAXPAL];

/*   array of palette sizes to correspond with
 *   depth of window in bit-planes
 */
SHORT palette[] = { 2, 4, 8, 16, 32 };


/*****************************************************************/
/*    The following function initializes the structure arrays    */
/*   needed to provide the Color menu topic.                     */
/*****************************************************************/
InitColorItems( depth )
SHORT depth;               /* number of bit-planes in window */
   {
   SHORT n, colors;

   colors = palette[depth-1];
   for( n=0; n<colors; n++ )           /* loop for max number of items */
      {
      coloritem[n].NextItem = &coloritem[n+1];
      coloritem[n].ItemFill = (APTR)&colorimage[n];
      /*   the next two items might be changed for
       *   when bit-planes is greater than 2
       */
      coloritem[n].LeftEdge = 2 + CW * (n % 4);
      coloritem[n].TopEdge = CH * (n / 4);
      coloritem[n].Width = CW;
      coloritem[n].Height = CH;
      coloritem[n].Flags = ITEMSTUFF;
      coloritem[n].MutualExclude = 0;
      coloritem[n].SelectFill = NULL;
      coloritem[n].Command = 0;
      coloritem[n].SubItem = NULL;
      coloritem[n].NextSelect = 0;

      colorimage[n].LeftEdge = 1;
      colorimage[n].TopEdge = 1;
      colorimage[n].Width = CW-2;
      colorimage[n].Height = CH-2;
      colorimage[n].Depth = depth;
      colorimage[n].ImageData = NULL;
      colorimage[n].PlanePick = 0;
      colorimage[n].PlaneOnOff = n;
      }
   coloritem[colors-1].NextItem = NULL;   /* needed for last item in list */
   return( 0 );
   }


/*****************************************************/
/*                Draw Mode Menu                     */
/*                                                   */
/*      Here are the code and data declarations for  */
/*   the DrawMode menu.  Current choices are limited */
/*   to Erase, Filled Box, Hollow Box, and PenDraw.  */
/*****************************************************/

/* define maximum number of menu items */
#define DMODEMAX 4

/*   declare storage space for menu items and
 *   their associated IntuiText structures
 */
struct MenuItem DModeItem[DMODEMAX];
struct IntuiText DModeText[DMODEMAX];

/*****************************************************************/
/*    The following function initializes the structure arrays    */
/*   needed to provide the DrawMode menu topic.                  */
/*****************************************************************/
InitDModeItems()
   {
   short n;

   /* initialize each meu item and IntuiText with loop */
   for( n=0; n<DMODEMAX; n++ )
      {
      DModeItem[n].NextItem = &DModeItem[n+1];
      DModeItem[n].LeftEdge = 0;
      DModeItem[n].TopEdge = 10 * n;
      DModeItem[n].Width = 112;
      DModeItem[n].Height = 10;
      DModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
      DModeItem[n].MutualExclude = 0;
      DModeItem[n].ItemFill = (APTR)&DModeText[n];
      DModeItem[n].SelectFill = NULL;
      DModeItem[n].Command = 0;
      DModeItem[n].SubItem = NULL;
      DModeItem[n].NextSelect = 0;

      DModeText[n].FrontPen = 0;
      DModeText[n].BackPen = 1;
      DModeText[n].DrawMode = JAM2;     /* render in fore and background */
      DModeText[n].LeftEdge = 0;
      DModeText[n].TopEdge = 1;
      DModeText[n].ITextFont = NULL;
      DModeText[n].NextText = NULL;
      }
   DModeItem[DMODEMAX-1].NextItem = NULL;

   /* initialize text for specific menu items */
   DModeText[0].IText = (UBYTE *)"Erase All";
   DModeText[1].IText = (UBYTE *)"Hollow Box";
   DModeText[2].IText = (UBYTE *)"Filled Box";
   DModeText[3].IText = (UBYTE *)"Pen Draw";

   return( 0 );
   }


/***************************************************/
/*                Menu Definition                  */
/*                                                 */
/*      This section of code is where the simple   */
/*   menu definition goes.  For now it supports    */
/*   only Color and Drawmode selection, but new    */
/*   choices can easily be added by creating       */
/*   structures and initializations functions      */
/*   similar to those provided above.              */
/***************************************************/

/* current number of available menu topics */
#define MAXMENU 2

/*   declaration of menu structure array for
 *   number of current topics.  Intuition
 *   will use the address of this array to
 *   set and clear the menus associated with
 *   the window.
 */
struct Menu menu[MAXMENU];

/**********************************************************************/
/*   The following function initializes the Menu structure array with */
/*  appropriate values for our simple menu strip.  Review the manual  */
/*  if you need to know what each value means.                        */
/**********************************************************************/
InitMenu()
   {
   menu[0].NextMenu = &menu[1];
   menu[0].LeftEdge = 10;
   menu[0].TopEdge = 0;
   menu[0].Width = 50;
   menu[0].Height = 10;
   menu[0].Flags = MENUENABLED;
   menu[0].MenuName = "Color";           /* text for menu-bar display */
   menu[0].FirstItem = &coloritem[0];    /* pointer to first item in list */

   menu[1].NextMenu = NULL;
   menu[1].LeftEdge = 65;
   menu[1].TopEdge = 0;
   menu[1].Width = 85;
   menu[1].Height = 10;
   menu[1].Flags = MENUENABLED;
   menu[1].MenuName = "DrawMode";        /* text for menu-bar display */
   menu[1].FirstItem = &DModeItem[0];    /* pointer to first item in list */

   return( 0 );
   }


/******************************************************/
/*                   Main Program                     */
/*                                                    */
/*      This is the main body of the program.         */
/******************************************************/

struct Window *Window;              /* ptr to applications window */
SHORT MinX, MinY, MaxX, MaxY;       /* clipping boundary variables */
SHORT KeepGoing = TRUE;             /* main loop control value */

main(argc, argv)
int argc ;
char *argv[] ;
   {
   struct Library *OpenLibrary() ;
   struct Window *OpenWindow() ;
   struct Message *GetMsg() ;
   struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */
   BYTE DrawColor = 1;                 /* initial drawing color */
   SHORT OldBRX = 30, OldBRY = 30;     /* point coords used for boxes */
   SHORT TLX = 20, TLY = 20;           /* initial top-left point coords */
   ULONG class;                        /* used in message monitor loop */
   USHORT code;                        /* used in message monitor loop */
   SHORT x, y, x1, y1, x2, y2;         /* various coordinate variables */
   USHORT MenuNum, ItemNum;
   /*   The following is a set of declarations
    *   for a number of flag values used by the
    *   program.  These would perhaps be better
    *   coded as a bit-field for all the flags,
    *   but I'm lazy, and this is easier.
    */
   SHORT MouseMoved = FALSE;     /* indicates new mouse position ready */
   SHORT ClipIt = FALSE;         /* are new point coords out of bounds? */
   SHORT ClippedLast = FALSE;    /* was last PenDraw operation clipped? */
   SHORT PenMode = FALSE;        /* indicates PenDraw mode is set */      
   SHORT PenDown = FALSE;        /* if mouse moved, then should it draw? */
   SHORT RubberBox = FALSE;      /* are we currently rubberbanding a box? */
   SHORT FilledBox = FALSE;      /* should boxes be filled when drawn? */
#ifdef TALKTOREXX
/*
 *   If we are talking to REXX, we need these two additional locals.
 */
   long rexxbit ;
   char firstcommand[256] ;
#endif

   /* attempt to Open Library to access Intuition */
   IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library", INTUITION_REV);
   if( IntuitionBase == NULL )
      exit(FALSE);

   /* attempt to OpenLibrary to access Graphics functions */
   GfxBase = (struct GfxBase *)
      OpenLibrary("graphics.library",GRAPHICS_REV);
   if( GfxBase == NULL )
      exit(FALSE);


   /* Try to open new window for application */
   if(( Window = OpenWindow(&NewWindow) ) == NULL)
      exit(FALSE);

   /*   set initial clipping boundaries
    *   from the values found in the window
    *   structure for border dimensions
    */
   MinX = Window->BorderLeft;
   MinY = Window->BorderTop;
   MaxX = Window->Width - Window->BorderRight - 1;
   MaxY = Window->Height - Window->BorderBottom - 1;

   InitColorItems( 2 );         /* initialize Color menu arrays */
   InitDModeItems();            /* initialize DrawMode menu arrays */
   InitMenu();                  /* initialize the menu structures */

   /*   Now, having initialized the various arrays
    *   of structures required for menu generation
    *   we can tell Intuition to make our menus
    *   available to the user when this window
    *   is active.
    */
   SetMenuStrip( Window, &menu[0] );

   /* set initial drw mode and color */
   SetDrMd( Window->RPort, JAM1 );
   SetAPen( Window->RPort, DrawColor );
#ifdef TALKTOREXX
/*
 *   For rexx, we open up a Rexx port, and send out the first command,
 *   if there was one.  We send it out asynchronously; no reason not to.
 */
   rexxbit = upRexxPort("freedraw", rcl, "fd", &disp) ;
   firstcommand[0] = 0 ;
   for (x=1; x<argc; x++) {
      strcat(firstcommand, argv[x]) ;
      strcat(firstcommand, " ") ;
   }
   if (firstcommand[0]) {
      asyncRexxCmd(firstcommand) ;
   }
#endif
   /*   Everything the program needs is now
    *   initialized and put in place.  The
    *   program enters the following loop
    *   and processes message continuously as
    *   they are received from Intuition.
    *      I guess this loop is the real workhorse
    *   of the program.  By the way, the loop
    *   control variable KeepGoing remains TRUE
    *   until a CLOSEWINDOW message is received.
    *   At that point it goes FALSE, and the
    *   program cleans up and exits.
    */
   while( KeepGoing )
      {

      /* stay here until a message is received from Intuition */
#ifdef TALKTOREXX
/*
 *   If we're working with Rexx, we wait on the Rexx bit as well.
 *   Then, we handle any Rexx messages.
 */
      Wait( (1L << Window->UserPort->mp_SigBit) | rexxbit);
      dispRexxPort() ;
#else
      Wait( 1L << Window->UserPort->mp_SigBit);
#endif
      MouseMoved = FALSE;    /* clear this flag each time thru loop */

      /*   since more than one message may be waiting
       *   a reply at this point, a loop is used to
       *   process all that have come in until no more
       *   are ready.  Msg received is assigned to
       *   NewMessage from the GetMsg() function.  This
       *   value will be NULL if no message is ready,
       *   and control passes out of the loop at that time
       */
      while( NewMessage=(struct IntuiMessage *)GetMsg(Window->UserPort) )
         {

         /*   copy some values from the message structure
          *   to variables used in the switch statements
          *   below
          */
         class = NewMessage->Class;
         code = NewMessage->Code;
         x = Window->MouseX;
         y = Window->MouseY;

         /*   SIZEVERIFY is a very high priority message
          *   in our loop and requires some immediate
          *   servicing.  Any outstanding draw operations
          *   are immediately cancelled, and the DrawMode
          *   is nulled.  This prevents any attempts to
          *   render outside whatever new Window boundaries
          *   the user chooses.
          *
          *   (not anymore, it don't.  -tgr)

         if( class == SIZEVERIFY )
            {
            PenDown = FALSE;
            if( RubberBox )
               {
               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
               Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
               RubberBox = FALSE;
               }
            }
          */

         /*   we have all the information needed from
          *   the message, so we can now safely reply
          *   to it without losing data
          */
         ReplyMsg( NewMessage );

         /*  Examine point coords from message received
          *  and set the clipping flag if out of bounds.
          *  If user was drawing in PenMode when message
          *  was received, then the ClippedLast flag
          *  should also be set to indicate this to the
          *  next draw operation.
          */
         if(ClipIt = ( x < MinX || x > MaxX || y < MinY || y > MaxY ))
            if( PenDown )
               ClippedLast = TRUE;


         /* enter switch on type of message received */
         switch( class )
         {
            case MOUSEMOVE:
               /*   Don't really do anything with this one
                *   until any other, more important, messages
                *   are received and processed.
                */
               MouseMoved = TRUE;
               break;

            case NEWSIZE:
               /*  set new clipping boundaries */
               MinX = Window->BorderLeft;
               MinY = Window->BorderTop;
               MaxX = Window->Width - Window->BorderRight - 1;
               MaxY = Window->Height - Window->BorderBottom - 1;
               break;

            case CLOSEWINDOW:
               /*   User is ready to quit, so indicate
                *   that execution should terminate
                *   with next iteration of the loop.
                */
               KeepGoing = FALSE;
               break;

            case MOUSEBUTTONS:
               /*   A number of things could have happened
                *   here, and further examination of data
                *   received from message is needed to
                *   determine what action should be taken.
                *   The code variable holds important info
                *   about what actually caused the message
                *   to be sent in the first place.
                */
               switch ( code )
                  {
                  case SELECTUP:
                     /*   User was holding down the left button
                      *   and just released it.  The PenMode
                      *   flag variables are set accordingly.
                      *   The pen can no longer be down, and
                      *   ClippedLast is reset for next time.
                      */
                     PenDown = ClippedLast = FALSE;
                     break;

                  case SELECTDOWN:
                     /*   User has pressed the left button, and
                      *   several differnt actions may need to
                      *   be taken.  If the ClipIt value is TRUE,
                      *   then no action should be taken at all.
                      */
                     if( ClipIt )
                        break;

                     /*   If user is currently in PenMode, then
                      *   set up to draw when MOUSEMOVED messages
                      *   are received until a subsequent SELECTUP
                      *   message comes in.
                      */
                     if( PenMode )
                        {
                        PenDown = TRUE;
                        ClippedLast = FALSE;

                        /* make sure to set appropriate mode */
                        SetDrMd( Window->RPort, JAM1 );

                        /* and establish initial position to draw */
                        Move( Window->RPort, (long)x, (long)y );
                        break;
                        }

                     /*   If user is currently rubberbanding a box,
                      *   then a SELECTDOWN message means it is time
                      *   to stop rubberbanding and actually draw it.
                      *   The following code will be executed if
                      *   this is the case, and it will determine if
                      *   a filled box is needed before rendering.
                      */
                     if( RubberBox )
                        {
                        /*   set draw mode back to JAM1 since
                         *   it is now currently set to COMPLEMENT
                         */
                        SetDrMd( Window->RPort, JAM1 );
                        RubberBox = FALSE;   /* turn off rubberbanding */

                        /*   Restore the condition of the RMBTRAP
                         *   bit in the Window structure's Flags
                         *   member.  Menubutton events will no
                         *   be received by this loop.
                         */
                        Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );

                        /*   RectFill is not condusive to the smooth
                         *   execution of programs iit arguments are
                         *   out of order, sot his code sorts them
                         *   in preparation for the call.
                         */
                        if( FilledBox )
                           {
                           /* first sort the x-coords */
                           if( TLX < OldBRX )  {
                              x1 = TLX;  x2 = OldBRX;  }
                           else  {
                              x1 = OldBRX;  x2 = TLX;  }

                           /* then sort the y-coords */
                           if( TLY < OldBRY )  {
                              y1 = TLY;  y2 = OldBRY;  }
                           else  {
                              y1 = OldBRY;  y2 = TLY;  }

                           /* now generate the filled rectangle */
                           RectFill( Window->RPort, (long)x1, (long)y1,
                                                    (long)x2, (long)y2 );
                           }
                        else
                           {
                           /* FilledBox not set, so draw hollow box */
                           DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
                           }
                        break;
                        }

                     /*   If execution comes here, then PenMode was
                      *   not set and user was not rubberbanding.
                      *   SELECTDOWN therefore indicates to start the
                      *   rubberbanding process at this point.  The
                      *   initial coords are set to the values we
                      *   received when the GetMsg() was executed.
                      */
                     TLX = OldBRX = x;  TLY = OldBRY = y;

                     /* set to render in XOR mode */
                     SetDrMd( Window->RPort, COMPLEMENT );

                     /* set flag to indicate we are now rubberbanding */
                     RubberBox = TRUE;

                     /*   This instruction indicates to Intuition
                      *   that we now wish to receive a message
                      *   each time the Menubutton is pressed.
                      *   This is how we hijack the right button
                      *   for temporary use as a Cancel button
                      *   instead of a Menubutton.
                      */
                     Window->Flags |= RMBTRAP;

                     /* render the initial rubberbox and exit */
                     DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
                     break;

                  case MENUDOWN:
                     /*   WE only receive this message class if
                      *   the RMBTRAP flag bit has been set, so
                      *   it always means that we should cancel
                      *   the box which is currently rubberbanding.
                      */
                     /* turn the flag off */
                     RubberBox = FALSE;

                     /* restore control of menubutton to Intuition */
                     Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );

                     /*   erase (by double XOR'ing) the current
                      *   rubberbox and exit switch.
                      */
                     DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
                     break;

                  default:
                     /*   Something unimportant happened, so just
                      *   continue thru the GetMsg() loop.
                      */
                     continue;
               }
               break;

            case MENUPICK:
               /*   A menu event has taken place and is
                *   ready to be processed.  Examine the
                *   code variable received from the message
                *   to determine what action should be taken.
                *   The first check is for MENUNULL, which
                *   means that nothing should be done at all.
                */
               if( code != MENUNULL )
                  {
                  /* get menu and item numbers from code */
                  MenuNum = MENUNUM( code );
                  ItemNum = ITEMNUM( code );

                  /* determine appropriate action by menu number */
                  switch ( MenuNum )
                     {
                     case 0:
                        /*   Menu 0 is the Color menu.  The
                         *   item number indicates which new
                         *   color to set.
                         */
                        DrawColor = ItemNum;
                        SetAPen( Window->RPort, (long)DrawColor );
                        break;

                     case 1:
                        /*   Menu 1 is the DrawMode menu.  The item
                         *   number indicates what to do.
                         *   NOTE:  Since we cannot have received
                         *   this message if we were rubberbanding,
                         *   then there is no need to clean up before
                         *   changing drawing modes.
                         */
                        switch ( ItemNum )
                           {
                           case 0:
                              /* Erase window to current color */
                              SetDrMd( Window->RPort, JAM1 );
                              RectFill( Window->RPort, (long)MinX, (long)MinY,
                                                       (long)MaxX, (long)MaxY);
                              break;

                           case 1:
                              /* set flag variables for hollow box */
                              PenMode = FALSE;
                              FilledBox = FALSE;
                              break;

                           case 2:
                              /* set flag variables for filled box */
                              PenMode = FALSE;
                              FilledBox = TRUE;
                              break;

                           case 3:
                              /* set flag variables for PenMode */
                              PenMode = TRUE;
                              break;

                           default:
                              /* don't do anything */
                              break;
                           }
                        break;

                     default:
                        /* Menu number unrecognized, do nothing */
                        break;
                     }
                  }
               break;

            case INACTIVEWINDOW:
               /*   User has de-selected our window, so a
                *   little bit of cleaning up may be needed
                *   to prevent untoward events when he comes
                *   back to it.
                */
               /* erase any outstanding rubberbox */
               if( RubberBox )
                  DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );

               /* reset all the flafg variables */
               PenDown = ClippedLast = RubberBox = FALSE;

               /* return possibly diverted menubutton events to Big I */
               Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
               break;

            default:
               /* message class was unrecognized, so do nothing */
               break;
            }
         }   /* this brace ends the while(NewMessage) loop way back when */

      /*   There are no more messages waiting at the
       *   IDCMP port, so we can now proceed to
       *   process any MOUSEMOVED message we may
       *   have received.
       */
      if( MouseMoved && !ClipIt)
         {
         /* the mouse did move, and we don't need to clip */

         /* check first if we are drawing in PenMode */
         if( PenDown )
            {
            /*   We have to examine if we clipped the
             *   last PenMode draw operation.  If we did,
             *   then this is the first move back into
             *   window boundaries, so we mov instead of
             *   drawing.
             */
            if( ClippedLast )
               {
               ClippedLast = FALSE;         /* reset this flag now */
               Move( Window->RPort, (long)x, (long)y );
               }
            else
               Draw( Window->RPort, (long)x, (long)y ); /* draw to x,y coords */
         }
         else
            {
            /*   We weren't in PenMode, but we still might
             *   be rubberbanding a box.  If so, then we
             *   should erase the current rubberbox and
             *   draw a new one with the new mouse coords.
             */
            if( RubberBox )
               {
               /* erase the old rubberbox - draw mode is COMPLEMENT */
               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );

               /* assign new values to box coords */
               OldBRX = x;  OldBRY = y;

               /* and draw the new rubberbox */ 
               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
               }
            }
         }
      }

   /*   It must be time to quit, so we have to clean
    *   up and exit.
    */
#ifdef TALKTOREXX
/*
 *   With Rexx, we need to bring the port down.  You might make this
 *   part of exit() for programs that have multiple paths to exit.
 */
   dnRexxPort() ;
#endif
   ClearMenuStrip( Window );
   CloseWindow( Window );
   exit(TRUE);
   }
#ifdef TALKTOREXX
/*
 *   Now we get into the actual code necessary for our REXX port; functions
 *   that do the real work.  Note that this program was not structured
 *   particularly nicely for Rexx; I had to write each of these functions.
 *   Many programs have these subroutines already in place; they are called
 *   as part of the event loop.  This progam, however, just has one big
 *   switch statement with different actions . . .
 *
 *   First, our locals.
 */
int currrexxcolor = 1 ;    /* what color is *rexx* drawing in? */
int args[4] ;              /* what args did we see to this function? */
int parsed ;               /* was argument parsing successful? */
int userreplied ;          /* has the current message been replied to yet? */
/*
 *   This function takes a pointer to a pointer to a string, grabs the
 *   next number, returns it, and advances the pointer to the string to
 *   point after the number.
 */
int getnm(where)
char **where ;
{
   register char *p = *where ;
   register int val = 0 ;
   int gotone = 0 ;

   while (*p <= ' ' && *p)
      p++ ;
   while ('0' <= *p && *p <= '9') {
      gotone = 1 ;
      val = 10 * val + *p++ - '0' ;
   }
   if (gotone == 0)
      parsed = 0 ;
   *where = p ;
   return(val) ;
}
/*
 *   This function trys to find `n' numeric arguments in the command
 *   string, and stuffs them into the args array.
 */
void parseargs(p, n)
char *p ;
int n ;
{
   register int i ;

   while (*p > ' ' && *p)
      p++ ;
   for (i=0; i<n; i++)
      args[i] = getnm(&p) ;
}
/*
 *   This is our main dispatch function.  We check to make sure a Window
 *   currently exists.  Then, we store away the `current color' and change
 *   it to Rexx's current color, call our handler function, and then restore
 *   the color.  If our handler replied, we return a 1 to indicate that.
 *   If the parse and everything else was successful, we return a 0.
 *   Otherwise, we return a failure of 20 to indicate that the arguments
 *   were messed up.
 */
int disp(msg, dat, p)
register struct RexxMsg *msg ;
register struct rexxCommandList *dat ;
char *p ;
{
   register int t ;

   parsed = 1 ;
   if (Window) {
      userreplied = 0 ;
      t = Window->RPort->FgPen ;
      SetAPen(Window->RPort, (long)currrexxcolor) ;
      ((int (*)())(dat->userdata))(msg, p) ;
      SetAPen(Window->RPort, (long)t) ;
      if (! parsed)
         replyRexxCmd(msg, (long)parsed, 0L, NULL) ;
      return ;
   }
   replyRexxCmd(msg, 20L, 10L, NULL) ;
}
/*
 *   This handler sets the current rexx color.
 */
void rexxcolor(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   parseargs(p, 1) ;
   currrexxcolor = args[0] ;
}
/*
 *   This function silently clips the x and y values at `n' to the
 *   window bounds.
 */
void clipxy(n)
int n ;
{
   if (args[n] < MinX)
      args[n] = MinX ;
   if (args[n] > MaxX)
      args[n] = MaxX ;
   n++ ;
   if (args[n] < MinY)
      args[n] = MinY ;
   if (args[n] > MaxY)
      args[n] = MaxY ;
}
/*
 *   This handler grabs four arguments and draws a box.
 */
void rexxbox(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   parseargs(p, 4) ;
   clipxy(0) ;
   clipxy(2) ;
   DrawBox(args[0], args[1], args[2], args[3], Window, currrexxcolor) ;
}
/*
 *   This handler grabs four arguments and draws a filled box.
 */
void rexxfbox(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   register int t ;

   parseargs(p, 4) ;
   clipxy(0) ;
   clipxy(2) ;
   if (args[0] > args[2]) {
      t = args[0] ; args[0] = args[2] ; args[2] = t ;
   }
   if (args[1] > args[3]) {
      t = args[1] ; args[1] = args[3] ; args[3] = t ;
   }
   RectFill( Window->RPort, (long)args[0], (long)args[1],
                            (long)args[2], (long)args[3]) ;
}
/*
 *   This handler grabs four arguments and draws a line.
 */
void rexxline(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   parseargs(p, 4) ;
   clipxy(0) ;
   clipxy(2) ;
   Move(Window->RPort, (long)args[0], (long)args[1]) ;
   Draw(Window->RPort, (long)args[2], (long)args[3]) ;
}
/*
 *   This handler pops the window to front.
 */
void rexxtofront(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   WindowToFront(Window) ;
}
/*
 *   This handler pops the window to back.
 */
void rexxtoback(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   WindowToBack(Window) ;
}
/*
 *   This handler sets the exit flag.
 */
void rexxexit(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   KeepGoing = 0 ;
}
/*
 *   This handler returns the version of the program.
 */
void rexxversion(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   userreplied = 1 ;
   replyRexxCmd(msg, 0L, 0L, VERSION) ;
}
/*
 *   This handler sends the rest of the command asynchronously,
 *   allowing us to run macros in parallel.
 */
void rexxspawn(msg, p)
struct RexxMsg *msg ;
char *p ;
{
   while (*p <= ' ' && *p)
      p++ ;
   asyncRexxCmd(p) ;
}
#endif
