/************************************************************************/ /*** 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 ***/ /************************************************************************/ /* compiler directives to fetch the necessary header files */ #include #include #include #include #include #include #include #include #include #include #include /* These definitions are used by intuition for * calls to OpenLibrary() in order to ensure * that an appropriate ROM revision is * available. */ #define INTUITION_REV 1 #define GRAPHICS_REV 1 /* 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 = { 20, 20, 300, 150, 0, 1, CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS | MENUPICK | NEWSIZE | INACTIVEWINDOW | SIZEVERIFY, WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE, NULL, NULL, "AMIGA FreeDraw 0.01", NULL, NULL, 100, 35, 640, 200, WBENCHSCREEN, }; /*******************************************************************/ /* 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, color ); /* set draw color for box */ Move(window->RPort, tlx, tly); /* move to top-left point */ Draw(window->RPort, brx, tly); /* and draw to each of the */ Draw(window->RPort, brx, bry); /* four corners of the box */ Draw(window->RPort, tlx, bry); Draw(window->RPort, tlx, tly); SetAPen( window->RPort, 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; nBorderLeft; 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 ); /* 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 */ Wait( 1 << Window->UserPort->mp_SigBit); 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. */ 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, x, 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, x1, y1, x2, 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, 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, MinX,MinY,MaxX,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, x, y ); } else Draw( Window->RPort, x, 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. */ ClearMenuStrip( Window ); CloseWindow( Window ); exit(TRUE); }