home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fish 'n' More 2
/
fishmore-publicdomainlibraryvol.ii1991xetec.iso
/
fish
/
libraries
/
rexxlib_463
/
freedraw.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-09
|
39KB
|
1,275 lines
#define TALKTOREXX 1
#ifdef TALKTOREXX
/*
This program is an example of how to add a Rexx implementation to any
program using the rexxapp.library. The original Freedraw program (without
REXX) is from Fred Fish Disk 1. The rexxapp.library is based upon a C module
by Radical Eye Software which was modified and down-coded to a 68000 shared
library.
All the REXX stuff is bracketed by "idef 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 increases the
executable slightly, 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 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. The basic idea of
this program is to provide some minimal image editing functions which can be
used to develop images for other programs.
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.
Rick Ross 11/14/85
*********************************************************************** */
char *VERSION = "Freedraw 0.01 by Richard M. Ross";
#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 for calls to OpenLibrary() in order to ensure
that an appropriate ROM version is available.
*/
#define INTUITION_REV 1L
#define GRAPHICS_REV 1L
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Window *myWindow = 0; /* ptr to applications window */
/*
This is the Window structure declaration.
Nothing fancy is going on here, but note
that 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 here 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 this rexx include file only if we access the RexxMsg or RxsLib */
/*#include <rexx/rxslib.h>*/
/* Where we store the rexxapp.library base */
struct RexxBase *RexxBase = 0;
/* We'll ship off unknown commands as macros to Rexx, and here's the extension */
char myExtension[] = "fd";
/* These are the rexxapp lib functions. */
extern long SetRexxPort();
extern void FreeRexxPort();
extern void ReceiveRexx();
extern struct RexxMsg *SendRexxCmd();
extern struct RexxMsg *SyncRexxCmd();
extern struct RexxMsg *ASyncRexxCmd();
extern void SetupResults();
extern char *RexxErrMsg; /* This comes from RexxInterface.asm */
/* These are the REXX handlers defined at the bottom of the file */
void rexxcolor(), rexxbox(), rexxfbox(), rexxline(), rexxtofront(),
rexxtoback(), rexxexit(), rexxversion(), rexxspawn();
/* This is the Func (Dispatch) function for received Rexxmsgs */
BOOL disp();
/* This is the Error function for the return of Rexxmsgs we send out, and caused an error */
void RexxError();
/* This is the Result function for the return of Rexxmsgs we send out, and were handled successfully */
void RexxResult();
struct CmdEntry {
char *CmdString;
APTR CmdHandler;
};
#define NUMCMDS 10 /* This will be application dependent */
struct RexxData {
struct MsgPort RexxPort;
char *Exten;
APTR Func;
struct RxsLib *RexxLib; /* defined by the ARexx rxslib.h file */
APTR Error;
APTR Result;
ULONG RexxMask;
ULONG UserData; /* to be defined by and used by the application */
/* The command list goes here. It consists of one CmdEntry structure for
each "command" that the application supports. Note that the number of
commands (and therefore the size of the RexxData) will depend upon how
many "commands" the application supports. The list must end with a NULL
entry.
*/
struct CmdEntry AsscList[NUMCMDS];
};
/*
Here is our initialized RexxData structure (for rexxapp lib)
*/
struct RexxData myRexxData = {
{0},
myExtension,
(APTR) disp,
0,
(APTR) RexxError,
(APTR) RexxResult,
0,
0,
/*
Here is our imbedded command association list.
Commands are all lower case, so we match either upper or lower.
(This is a requirement of rexxapp.library.)
*/
{ { "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 },
{ 0, 0 } }
};
/* Note that the buffer for the PortName must be 2 bytes more than the name itself.
THIS IS REQUIRED so that the lib can construct a new name if this one is in use.
Note that is we run two copies of freedraw simultaneously, the first one's port
will be "freedraw", and the second one will be "freedraw2".
*/
char PortName[10] = "freedraw";
#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 );
}
/**********************************************************************/
/* The following function is our exit point for the program. It */
/* closes/frees any allocated resources. */
/**********************************************************************/
doclean()
{
if (myWindow) CloseWindow( myWindow );
if (IntuitionBase) CloseLibrary(IntuitionBase);
if (GfxBase) CloseLibrary(GfxBase);
#ifdef TALKTOREXX
if (RexxBase) CloseLibrary(RexxBase);
#endif
exit(TRUE);
}
/******************************************************/
/* Main Program */
/* */
/* This is the main body of the program. */
/******************************************************/
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 3 additional locals */
ULONG rexxbit;
char firstcommand[256];
struct RexxMsg *rxmsg;
#endif
/* attempt to Open Library to access Intuition */
IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", INTUITION_REV);
if( IntuitionBase == NULL )
doclean();
/* attempt to OpenLibrary to access Graphics functions */
GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",GRAPHICS_REV);
if( GfxBase == NULL )
doclean();
/* Try to open new window for application */
if(( myWindow = OpenWindow(&NewWindow) ) == NULL) doclean();
#ifdef TALKTOREXX
/* Open the rexxapp.library */
if (!(RexxBase = (struct RexxBase *) OpenLibrary("rexxapp.library",0L)))
{
puts("Need the rexxapp.library");
doclean();
}
#endif
/* set initial clipping boundaries
* from the values found in the window
* structure for border dimensions
*/
MinX = myWindow->BorderLeft;
MinY = myWindow->BorderTop;
MaxX = myWindow->Width - myWindow->BorderRight - 1;
MaxY = myWindow->Height - myWindow->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( myWindow, &menu[0] );
/* set initial drw mode and color */
SetDrMd( myWindow->RPort, JAM1 );
SetAPen( myWindow->RPort, DrawColor );
#ifdef TALKTOREXX
/* For rexx, we must open up a Rexx port. */
if (! (rexxbit = SetRexxPort(PortName, &myRexxData)))
{
puts("can't set up RexxPort");
doclean();
}
/* Show the portname in the window titlebar */
SetWindowTitles(myWindow, PortName, 0L);
/*
For FreeDraw, we're going to send out the user's args on the CLI as a Rexx
macro invocation, if there was one. We send it out asynchronously; no reason
not to. This is an example of how your program might invoke a Rexx script.
*/
firstcommand[0] = 0;
for (x=1; x<argc; x++)
{
strcat(firstcommand, argv[x]);
strcat(firstcommand, " ");
}
if (firstcommand[0])
{
/* Send it out. If an error, print out the returned error message */
if (! (rxmsg = ASyncRexxCmd(firstcommand, &myRexxData)))
SetWindowTitles(myWindow, RexxErrMsg, 0L);
}
#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 << myWindow->UserPort->mp_SigBit) | rexxbit);
ReceiveRexx(&myRexxData);
#else
Wait( 1L << myWindow->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(myWindow->UserPort) )
{
/* copy some values from the message structure
* to variables used in the switch statements
* below
*/
class = NewMessage->Class;
code = NewMessage->Code;
x = myWindow->MouseX;
y = myWindow->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 myWindow boundaries
* the user chooses.
*
* (not anymore, it don't. -tgr)
if( class == SIZEVERIFY )
{
PenDown = FALSE;
if( RubberBox )
{
DrawBox( TLX, TLY, OldBRX, OldBRY, myWindow, DrawColor );
myWindow->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 = myWindow->BorderLeft;
MinY = myWindow->BorderTop;
MaxX = myWindow->Width - myWindow->BorderRight - 1;
MaxY = myWindow->Height - myWindow->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( myWindow->RPort, JAM1 );
/* and establish initial position to draw */
Move( myWindow->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( myWindow->RPort, JAM1 );
RubberBox = FALSE; /* turn off rubberbanding */
/* Restore the condition of the RMBTRAP
* bit in the myWindow structure's Flags
* member. Menubutton events will no
* be received by this loop.
*/
myWindow->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( myWindow->RPort, (long)x1, (long)y1,
(long)x2, (long)y2 );
}
else
{
/* FilledBox not set, so draw hollow box */
DrawBox( TLX, TLY, OldBRX, OldBRY, myWindow, 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( myWindow->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.
*/
myWindow->Flags |= RMBTRAP;
/* render the initial rubberbox and exit */
DrawBox( TLX, TLY, OldBRX, OldBRY, myWindow, 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 */
myWindow->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
/* erase (by double XOR'ing) the current
* rubberbox and exit switch.
*/
DrawBox( TLX, TLY, OldBRX, OldBRY, myWindow, 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( myWindow->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( myWindow->RPort, JAM1 );
RectFill( myWindow->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, myWindow, DrawColor );
/* reset all the flafg variables */
PenDown = ClippedLast = RubberBox = FALSE;
/* return possibly diverted menubutton events to Big I */
myWindow->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( myWindow->RPort, (long)x, (long)y );
}
else
Draw( myWindow->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, myWindow, DrawColor );
/* assign new values to box coords */
OldBRX = x; OldBRY = y;
/* and draw the new rubberbox */
DrawBox( TLX, TLY, OldBRX, OldBRY, myWindow, 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, but
don't call it unless you called SetRexxPort() once anyway.
*/
FreeRexxPort(&myRexxData);
#endif
ClearMenuStrip( myWindow );
doclean();
}
#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 in main() with different actions . . .
First, our locals.
*/
ULONG currrexxcolor = 1; /* what color is *rexx* drawing in? */
ULONG args[4]; /* what args did we see to this function? */
ULONG error; /* was argument parsing successful? */
char *ResultStr; /* the Result string to return if any */
/* *********************************************************************
* This function takes a pointer to a pointer to a string, grabs the
* next number (unsigned), returns it, and advances the pointer to the
* string to point after the number.
********************************************************************* */
ULONG getnm(where)
char **where;
{
register char *p = *where;
register ULONG val = 0;
register ULONG gotone = 0;
while (*p <= ' ' && *p)
p++;
while ('0' <= *p && *p <= '9')
{
gotone = 1;
val = 10 * val + *p++ - '0';
}
if (gotone == 0) error = 20; /* Indicate an error level of 20 with rexx arguments */
*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;
UBYTE n;
{
register UBYTE i;
while (*p > ' ' && *p)
p++;
for (i=0; i<n; i++)
args[i] = getnm(&p);
}
/* *************************************************************************
* This is our rexx Func (Dispatch) routine. The rexxapp.library calls this
* when some other program sends us a Rexxmsg that has one of the commands
* in our command association list. 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 no errors, error will be 0. If our handler has any return
* Result string, that is placed in ResultStr. We setup the Results codes
* via SetupResults() and return(TRUE) because we always want the lib to
* reply. If the parse and everything else was successful, we set Results as
* 0. Otherwise, we set Results to the value of error to indicate that
* something failed.
************************************************************************* */
BOOL disp( msg, routine, p, rxdata ) /* NOTE: Don't use register class because */
struct RexxMsg *msg; /* Manx doesn't know where you put things (no pragma) */
void (*routine)();
char *p;
struct RexxData *rxdata;
{
register UBYTE t;
/* Initially, no error or Result string to return */
error = 0;
ResultStr = 0;
if (myWindow)
{
/* Get the current pen color in case this rexx handler alters it */
t = myWindow->RPort->FgPen;
SetAPen(myWindow->RPort, (LONG)currrexxcolor);
/* Call the appropriate handler. The lib has determined which one */
(*routine)(msg, p, rxdata);
/* restore the color */
SetAPen(myWindow->RPort, (LONG)t);
/* Check for any errors */
if (! error)
{
/* No errors. Send back Result1 and Result2 = 0. Also return any
Result string
*/
SetupResults(0L, 0L, ResultStr, msg, rxdata);
}
else
{
/* An error! Return Result1 = error. Must not send back a Result string */
SetupResults(error, 10L, NULL, msg, rxdata);
}
/* We want the lib to do the reply right now */
return(TRUE);
}
SetupResults(error, 10L, msg, NULL, rxdata);
return(TRUE);
}
/* **********************************************************************
* This handler sets the current rexx color. (our 'color' handler)
********************************************************************** */
void rexxcolor(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
parseargs(p, 1);
currrexxcolor = args[0];
}
/* *********************************************************************
* This function silently clips the x and y values at `n' to the
* window bounds.
********************************************************************* */
void clipxy(n)
UBYTE 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, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
parseargs(p, 4);
clipxy(0);
clipxy(2);
DrawBox(args[0], args[1], args[2], args[3], myWindow, currrexxcolor);
}
/* *********************************************************************
* This handler grabs four arguments and draws a filled box.
********************************************************************** */
void rexxfbox(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
register ULONG 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( myWindow->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, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
parseargs(p, 4);
clipxy(0);
clipxy(2);
Move(myWindow->RPort, (long)args[0], (long)args[1]);
Draw(myWindow->RPort, (long)args[2], (long)args[3]);
}
/* *************************************************************************
* This handler pops the window to front.
************************************************************************* */
void rexxtofront(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
WindowToFront(myWindow);
}
/* *************************************************************************
* This handler pops the window to back.
************************************************************************* */
void rexxtoback(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
WindowToBack(myWindow);
}
/* *************************************************************************
* This handler sets the exit flag.
************************************************************************* */
void rexxexit(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
KeepGoing = FALSE;
}
/* *************************************************************************
* This handler sets ResultStr to the version of the program.
************************************************************************* */
void rexxversion(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
ResultStr = VERSION;
}
/* ***********************************************************************
* This handler sends the rest of the command asynchronously,
* allowing us to run macros in parallel.
*********************************************************************** */
void rexxspawn(msg, p, rxdata)
struct RexxMsg *msg;
char *p;
struct RexxData *rxdata;
{
register struct Rexxmsg *rxmsg;
if (! (rxmsg = ASyncRexxCmd(p, rxdata)))
SetWindowTitles(myWindow, RexxErrMsg, 0L);
}
/* ***********************************************************************
* This function is for when a RexxMsg we send out returns with an error.
*********************************************************************** */
void RexxError(error, p, msg, rxdata)
ULONG error;
char *p;
struct RexxMsg *msg;
struct RexxData *rxdata;
{
SetWindowTitles(myWindow, p, 0L);
}
/* ***********************************************************************
* This function is for when a RexxMsg we send out returns successfully.
* Actually, we have nothing to do. We have no function that sends out a
* RexxMsg and expects a returned Result string or Result1 code. We'll just
* print out any result string.
*********************************************************************** */
void RexxResult(error, p, msg, rxdata)
ULONG error;
char *p;
struct RexxMsg *msg;
struct RexxData *rxdata;
{
if (p)
{
SetWindowTitles(myWindow, p, 0L);
}
}
#endif