/*****************************************************************************/
/* SketchView.m								     */
/* implementation file for SketchView class of the SketchDemo application    */
/* August 1989 Carl F. Sutter						     */
/*****************************************************************************/

#import "SketchView.h"
#import <appkit/appkit.h>

@implementation SketchView

/*****************************************************************************/
/* newFrame - create an instance of SketchView, and initialize it	     */
/*****************************************************************************/
+ newFrame:(NXRect *)rect
   {
   int		i;
   NXPoint	nxpHotSpot;
   
   /* make a new view, and initialize some flags */
   self = [super newFrame:rect];
   nTool = TOOL_RECT;
   bOpenedWithFile = FALSE;
   nCurPage = 1;   
   
   /* make the open and save-as panels */
   openReq = [OpenPanel new];
   saveReq = [SavePanel new];
   [saveReq setRequiredFileType:"sketchD"];
   
   /* create the bitmaps */
   for (i=0; i<MAXPAGES; i++)
      {
      bmpPage[i] = [Bitmap newSize:bounds.size.width :bounds.size.height
		              type:NX_ALPHABITMAP];
      [bmpPage[i] setFlip:NO];
      }
      
   /* get the drawing cursor and set it's hot spot */
   curDraw = [Cursor newFromMachO:"DrawCursor.tiff"];   
   nxpHotSpot.x = 7.0; nxpHotSpot.y = 7.0;
   [curDraw setHotSpot:&nxpHotSpot];
   
   return( self );
   } /* newFrame 8/16/89 CFS */


/*****************************************************************************/
/* outlet initialization methods 					     */
/*****************************************************************************/
- setNewAlpha:anObject  { newAlpha = anObject;  return( self ); }
- setPageNum:anObject   { pageNum = anObject;   return( self ); }
- setNumToDraw:anObject { numToDraw = anObject; return( self ); }


/*****************************************************************************/
/* appDidInit - delegate message sent after application is set up	     */
/* this is the time for setting fields etc.				     */
/*****************************************************************************/
- appDidInit:(id)sender
   {
   /* clear the pages, unless a file was just loaded in to start the App */
   if (!bOpenedWithFile) [self clearPages:self];
   return( self );
   } /* appDidInit 8/16/89 CFS */


/*****************************************************************************/
/* appAcceptsAnotherFile is an application delegate method which 	     */		
/* returns whether it is OK for the application to try to open more files    */
/* with the appOpenFile:type:						     */
/*****************************************************************************/
- (BOOL)appAcceptsAnotherFile:sender;
   {
   return( YES );
   } /* appAcceptsAnotherFile 11/2/89 CFS */


/*****************************************************************************/
/* appOpenFile:type: - delegate message sent to open the specified file.     */
/* It is normally called by the Application object in response to open       */
/* requests from the Workspace.						     */
/*****************************************************************************/
- (int)appOpenFile:(const char *)filename type:(const char *)aType;
   {
   /* set the app icon as the Miniwindow icon */
   [window setMiniwindowIcon:"app"];
   /* bOpenedWithFile only used for initialization, since if the user double */
   /* clicks a file in the workspace to start the App, appOpenFile arrives */
   /* BEFORE appDidInit! (due to Listener weirdness) */
   bOpenedWithFile = TRUE;
   return( [self openSketchPad:filename] );  
   } /* appOpenFile 9/11/89 CFS */


/*****************************************************************************/
/* newPage - flip to the page entered by the user			     */
/*****************************************************************************/
- newPage:sender
   {
   nCurPage = MAX( 1, MIN( MAXPAGES, [pageNum intValueAt:0] ) );
   [pageNum setIntValue:nCurPage at:0];
   [pageNum selectTextAt:0];
   [self display];
   return( self );
   } /* newPage 8/16/89 CFS */


/*****************************************************************************/
/* clearPages - erase all the pages, and reset tablet			     */
/*****************************************************************************/
- clearPages:sender
   {
   float	flNewAlpha; 
   int 		i;
 
   strcpy( szFileName, UNTITLED );
   [self setTitle];

   /* get the new alpha value */
   [newAlpha selectTextAt:0];
   flNewAlpha = [newAlpha floatValueAt:0];
   /* white out the pages */
   for (i=0; i<MAXPAGES; i++)
      {
      [bmpPage[i] lockFocus];
      PSsetalpha( flNewAlpha );	
      PSsetgray( NX_WHITE );		      
      NXRectFill( &bounds );
      [bmpPage[i] unlockFocus];
      }
   /* set the new page number, and redraw the sheets */
   nCurPage = 1;   
   [pageNum setIntValue:nCurPage at:0];
   [self display];
   return( self );
   } /* clearPages 8/16/89 CFS */


/*****************************************************************************/
/* setTool - set the current drawing tool				     */
/*****************************************************************************/
- setTool:sender
   {
   nTool = [[sender selectedCell] tag];
   return( self );
   } /* setTool 8/16/89 CFS */


/*****************************************************************************/
/* flipUp - flip up one page						     */
/*****************************************************************************/
- flipUp:sender
   {
   if (nCurPage > 1) nCurPage--;
   [pageNum setIntValue:nCurPage at:0];
   [self display];
   return( self );
   } /* flipUp 8/16/89 CFS */


/*****************************************************************************/
/* flipDown - flip down one page					     */
/*****************************************************************************/
- flipDown:sender
   {
   if (nCurPage < MAXPAGES) nCurPage++;
   [pageNum setIntValue:nCurPage at:0];
   [self display];
   return( self );
   } /* flipDown 8/16/89 CFS */


/*****************************************************************************/
/* drawSelf - draw the pages in order up to the current page		     */
/*****************************************************************************/
- drawSelf:(NXRect *)r :(int) count
   {
   int	i;
   int	nStartPage;
      
   nStartPage = MAX( 0, nCurPage - [numToDraw intValue] );

   PSsetgray( NX_WHITE );		      
   NXRectFill( &bounds );
 
   for (i=nStartPage; i<nCurPage; i++)
      [bmpPage[i] composite:NX_SOVER toPoint:&bounds.origin];

   return( self );
   } /* drawSelf 8/16/89 CFS */


/*****************************************************************************/
/* animate - draw the frames back in sequence				     */
/*****************************************************************************/
- animate:sender;
   {
   int i;
    
   [self lockFocus];   
   PSsetgray( NX_WHITE );		      
   NXRectFill( &bounds );
 
   for (i=0; i<nCurPage; i++)
      {
      [bmpPage[i] composite:NX_COPY toPoint:&bounds.origin];
      [[self window] flushWindow];
      }
   [self unlockFocus];
      
   [self display];	 
   return( self );
   } /* animate 8/16/89 CFS */
   

/*****************************************************************************/
/* mouseDown - trap mouse movements, and draw new graphics	   	     */
/* the drawing is very crude here, no pswraps or anything                    */
/*****************************************************************************/
- mouseDown:(NXEvent *)e
   {
   BOOL		bLooping = YES;
   int		nOldMask;
   NXPoint	pA, pB;
   
   /* use standard instance drawing to rubber-band or draw a graphic */
   nOldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
   pA = e->location;
   [self convertPoint:&pA fromView:nil];
   [self lockFocus];
   while (bLooping)
      {
      e = [NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
      pB = e->location;
      [self convertPoint:&pB fromView:nil];
      PSnewinstance();
      if (bLooping = (e->type == NX_MOUSEDRAGGED))
	 switch (nTool)
	    {
	    case TOOL_RECT: /* rubber band a rectangle */
	         PSsetinstance( YES );
	         PSsetgray( NX_BLACK );
	         PSrectstroke( pA.x, pA.y, pB.x - pA.x, pB.y - pA.y );
	         PSsetinstance( NO );
                 break;
		 
	    case TOOL_LINE: /* rubber band a line */
	         PSsetinstance( YES );
	         PSsetgray( NX_BLACK );
		 PSmoveto( pA.x, pA.y );
		 PSlineto( pB.x, pB.y );
		 PSstroke();
	         PSsetinstance( NO );
	         break;
		 
	    case TOOL_PENCIL: /* draw a line right on the current bitmap */
	         PSsetgray( NX_BLACK );
		 PSmoveto( pA.x, pA.y );
		 PSlineto( pB.x, pB.y );
		 PSstroke();
		 [window flushWindow];
                 [bmpPage[nCurPage - 1] lockFocus];
                 PSsetgray( NX_BLACK );
                 PSmoveto( pA.x, pA.y );
                 PSlineto( pB.x, pB.y );
                 PSstroke();
                 [bmpPage[nCurPage - 1] unlockFocus];
		 pA = pB;
                 break;
		 
	    } /* nTool switch */
      }
   [self unlockFocus];
   [window setEventMask:nOldMask];
   
   /* draw the rubber banded rectangle or line on the current bitmap */
   [bmpPage[nCurPage - 1] lockFocus];
   PSsetgray( NX_BLACK );
   if (nTool == TOOL_RECT)
      PSrectstroke( pA.x, pA.y, pB.x - pA.x, pB.y - pA.y );
   if (nTool == TOOL_LINE)
      {
      PSmoveto( pA.x, pA.y );
      PSlineto( pB.x, pB.y );
      PSstroke();
      }
   [bmpPage[nCurPage - 1] unlockFocus];

   [self display];
   
   return( self );   
   } /* mouseDown 8/12/89 CFS */


/*****************************************************************************/
/* showError - display an error message in an alert panel		     */
/*****************************************************************************/
- showError:(char *)errorMessage
   {
   NXRunAlertPanel( "SketchDemo Error", errorMessage, "OK", NULL, NULL );
   } /* showError 9/11/89 CFS */


/*****************************************************************************/
/* openRequest - opens a new file using the open panel			     */
/*****************************************************************************/
- openRequest:sender
   {
   const char *fileName;
   const char *const types[2] = {"sketchD", NULL};

   if ([openReq runModalForTypes:types] && (fileName = [openReq filename]))
      [self openSketchPad:fileName];
   return( self );
   } /* openRequest 9/11/89 CFS */


/*****************************************************************************/
/* openSketchPad - read the file and load the bitmaps			     */
/*****************************************************************************/
- (BOOL)openSketchPad:(const char *)fileName
   {
   NXTypedStream	*ts;
   int			i;

   if (!(ts = NXOpenTypedStreamForFile( fileName, NX_READONLY )))
      {
      [self showError:"Couldn't open file."];
      return( NO );
      }

   /* file opened OK, load in the bitmaps and set the new filename */   
   [NXWait push];
   for (i=0; i<MAXPAGES; i++)
      {
      /*[bmpPage[i] free];*/
      /*bmpPage[i] = [Bitmap newFromStream:ts];*/
      bmpPage[i] = NXReadObject( ts );
      }
   NXCloseTypedStream( ts );
   strcpy( szFileName, fileName );
   [self setTitle];
   [self display];
   [NXWait pop];
   return( YES );
   } /* openSketchPad 9/11/89 CFS */
   

/*****************************************************************************/
/* saveRequest - saves the current window under its default name	     */
/* if the current szFileName is "Untitled" then use saveAsRequest	     */
/*****************************************************************************/
- saveRequest:sender
   {
   if (strcmp( szFileName, UNTITLED ))
      [self saveSketchPad:szFileName];
   else [self saveAsRequest:sender];
   return( self );
   } /* saveRequest 9/11/89 CFS */
   

/*****************************************************************************/
/* saveAsRequest - gives the user a chance to save under a new name. 	     */
/*****************************************************************************/
- saveAsRequest:sender
   {
   const char	*fileName;
   char		*name;
   char		path[120];
   
   /* split the filename into path and name */
   strcpy( path, szFileName );
   name = strrchr( path, '/' );
   if (name) *name++ = '\0';
   else
      {
      strcpy( path, "." );
      name = szFileName;
      }
   
   /* bring up the save panel */
   if (([saveReq runModalForDirectory:path file:name]) && 
       (fileName = [saveReq filename]))
      [self saveSketchPad:fileName];  
   return( self );
   } /* saveAsRequest 9/11/89 CFS */


/*****************************************************************************/
/* saveSketchPad - save the current bitmaps in the named file		     */
/*****************************************************************************/
- (BOOL)saveSketchPad:(const char *)fileName
   {
   NXTypedStream	*ts;
   int			i;
   
   if (!(ts = NXOpenTypedStreamForFile( fileName, NX_WRITEONLY )))
      {
      [self showError:"Couldn't open file."];
      return( NO );
      }

   /* file opened OK, save the bitmaps */
   [NXWait push];
   for (i=0; i<MAXPAGES; i++)
      NXWriteObject( ts, bmpPage[i] );
   NXCloseTypedStream( ts );
   
   strcpy( szFileName, fileName );
   [self setTitle];
   [NXWait pop];
   return( YES );   
   } /* saveSketchPad 9/11/89 CFS */


/*****************************************************************************/
/* resetCursorRects - override the View's method to add our drawing cursor   */
/*****************************************************************************/
- resetCursorRects
   {
   [self addCursorRect:&bounds cursor:curDraw];
   return( self );
   } /* resetCursorRects 11/2/89 CFS */


/*****************************************************************************/
/* setTitle - set the window's title with the current file name 	     */
/* don't be confused by the name of this method - a Window has a setTitle    */
/* method, while a View (this code) does NOT.  This is just an internal one. */
/*****************************************************************************/
- setTitle
   {
   const char	*first;
   char		szTitle[80];
   
   /* get rid of any path info, if there is any */
   first = strrchr( szFileName, '/' );
   if (first == NULL) first = szFileName;
   else first++; /* character after last forward slash */
   
   sprintf( szTitle, "Animation Sketch Pad - %s", first );
   [window setTitle:szTitle];
   } /* setTitle 11/9/89 CFS */
   

@end
