UNIT cs65;

{*******************************************************************************
Programmer:John Zelle

Description:  This is a Codewarrior unit that defines a set of routines to make
    Codewarrior on the Mac look similar to Turbo Pascal under DOS.  Specifically,
    this unit implements procedures and functions for graphics-oriented programs
    of the type found in "Introductory Computer Science" by A. K. Dewdney.

Intitial Version: Summer 1996.
 
Updates:
 
 9/22/96  Cleaned up code and added some comments.
*******************************************************************************}
 
INTERFACE

FUNCTION Random:REAL;
{ Imitates RANDOM.  Returns a value uniformly distributed between 0 and 1 }

FUNCTION RandomInt(top : INTEGER):INTEGER;
{ Imitates RANDOM(SomeInt).  Returns an integer uniformly distributed in range
 0 to top-1. }

PROCEDURE Randomize;
{ Imitates RANDOMIZE.  Initializes the random number generator to produce
  differing sequences. }
	
PROCEDURE SetGraph;
{ Imitates SetGraph.  Initializes the graphics routines.
 IMPORTANT NOTE: SetGraph is incompatible with and program containing
   READLN orWRITELN.  A graphics program may only use the graphical
   I/O routines described below. }

FUNCTION ButtonPressed : BOOLEAN;
{ Is true anytime after the mouse button has been pressed during program
 execution. }

FUNCTION  Click:BOOLEAN;
{ Like ButtonPressed, but also clears the event so that the next call to
 Click returns false (unless the button has been pressed again). }
  
PROCEDURE ClrScr;
{ Imitates Clrscr.  Clears the graphics screen.}

PROCEDURE OutTextXY(X, Y : INTEGER; Str:STRING);
{ Imitates OutTextXY.  Writes the output string starting at location (X,Y). }

PROCEDURE OutText(Str : STRING);
{ Same as OutTextXY, but writes wherever the current location is.}

PROCEDURE SetColor(C : INTEGER);
{ Imitates SetColor.  Subsequent drawing is donw in the indicated color.  A table of
 color values is given on page 106.  Unlike Turbo Pascal, SetColor should also be used
 to set the color of filled object, replacing SetFillStyle. }
 
PROCEDURE Bar(left,top,right,bottom : INTEGER);
{ Imitates BAR.  Draws a bar in the current color.  }

PROCEDURE Line(left,top,right,bottom : INTEGER);
{ Imitates LINE. Draws a line in the current color. }

PROCEDURE PutPixel(Horiz, Vertical : INTEGER);
{ Imitates PutPixel.  Unlike Turbo Pascal, it always draw the pixel in the
 current color. }

PROCEDURE ReadStr(VAR Str : STRING);
{ Graphics input routine.  Replaces READLN for inputting string values. }

PROCEDURE ReadInt(VAR Value : INTEGER);
{ Graphics input routine.  Replaces READLN for inputting INTEGER values. }

PROCEDURE ReadReal(VAR Value : REAL);
{ Graphics input routine. Replaces READLN for inputting REAL values. }

PROCEDURE Pause;
{ Pauses execution until a key is pressed, or the mouse button is pushed.
 Replaces a bare READLN for "holding" a graphics screen. }

PROCEDURE Delay(Ticks : INTEGER);
{ Imitates Delay.  Pauses execution for a specifified number of ticks.}


IMPLEMENTATION
	

USES	Types, QuickDraw,OSUtils, SegLoad, Fonts, Windows, Menus, TextEdit, Dialogs,
        Processes, Events, QuickDrawText;

{*******************************************************************************}
FUNCTION rand:INTEGER; C; EXTERNAL;           {Borrow randoms from C library}
PROCEDURE srand(seed:INTEGER); C; EXTERNAL;

FUNCTION random:REAL;
BEGIN
   random := rand/32768;
END; { random }

PROCEDURE RANDOMIZE;
VAR seed : longint;
BEGIN	 
   GetDateTime(seed);
   srand(seed);
END; { RANDOMIZE }
   
FUNCTION RandomInt(limit : INTEGER):INTEGER;
BEGIN
   RandomInt := rand MOD limit;
END; { RandomInt }

{*******************************************************************************}
PROCEDURE SetGraph;
{ Standard Macintosh initializations except there is no error checking.  Assumes
 the PowerMac environment at Drake.
 
 The screen is setup as one large graphics window (ala Turbo Pascal).  All
 graphics commands write to this window.
 }

VAR
   mainPtr  : WindowPtr;
   windRect : Rect;
	    
BEGIN
   InitGraf(@qd.thePort);
   InitFonts;
   InitWindows;
   InitMenus;
   TEInit;
   InitDialogs(NIL);
   InitCursor;
   GetDateTime (qd.RandSeed);
   windRect := qd.screenBits.bounds;
   MainPtr := NewWindow(NIL, windRect, 'Graphics', TRUE, documentProc, 
			Pointer(-1), FALSE, 0);
   SetPort(MainPtr);
   MoveTo(0,30);					
END;	{ Initialize }
   
{*******************************************************************************
 Functions for detecting mouse clicks
 }
   
FUNCTION ButtonPressed : BOOLEAN;
{ This one does not clear the event}

BEGIN
   ButtonPressed := Button;
END; { ButtonPressed }

Function Click:BOOLEAN;
VAR
   AnEvent : EventRecord;
BEGIN
   Click := FALSE;
   IF GetNextEvent(EveryEvent,AnEvent) THEN
      IF AnEvent.What = MouseDown THEN
	 Click := TRUE;
END; { Click }
   
{*******************************************************************************}
PROCEDURE ClrScr;
{Clear the screen by painting it white.}
BEGIN
   ForeColor(Whitecolor);
   PaintRect(qd.screenBits.bounds);
   MoveTo(0,30);
   ForeColor(BlackColor);
END; { ClrScr }

{******************************************************************************}
{Routines for outputting text in graphics mode}

PROCEDURE OutTextXY(X, Y : INTEGER; Str:STRING);
BEGIN
   MoveTo(X,Y);
   DrawString(Str);
END;

PROCEDURE OutText(Str:STRING);
BEGIN
   DrawString(Str);
END; { OutText }

{***************************************************************************}
{Routines for Drawing}

PROCEDURE SetColor(C : INTEGER);
BEGIN
   CASE C OF
     0	: ForeColor(BlackColor);
     1	: ForeColor(BlueColor);
     2	: ForeColor(GreenColor);
     3	: ForeColor(CyanColor);
     4	: ForeColor(RedColor);
     5	: ForeColor(MagentaColor);
     14	: ForeColor(yellowColor);
     15	: ForeColor(whiteColor);
   END; { case }
END; { SetColor }


PROCEDURE Bar(left,top,right,bottom:INTEGER);
VAR
   Bar : Rect;
BEGIN  
   setRect(Bar,left,top,right,bottom);
   PaintRect(Bar);
END; { Bar }
   
PROCEDURE Line(left,top,right,bottom:INTEGER);
BEGIN
   MoveTo(left,top);
   LineTo(right,bottom);
END;

PROCEDURE PutPixel(H,V : INTEGER);
BEGIN
   MoveTo(H,V);
   LineTo(H,V);
END;

{******************************************************************************}
{ Routines for Graphics-mode inputs. }

PROCEDURE ReadStr(VAR Str : STRING);
VAR Done   : BOOLEAN;
   AnEvent : EventRecord;
   Key	   : char;
BEGIN	   
   Done := FALSE;
   Str := '';
   REPEAT
      IF getNextEvent(EveryEvent,AnEvent) THEN
	 IF (anEvent.what = keydown) or (anEvent.what = autoKey) THEN
	 BEGIN
	    Key := char(BAnd(anEvent.message, charCodeMask));
	    IF ord(Key) <> 13 THEN
	    BEGIN
	       Str[0] := char(ord(Str[0]) + 1);
	       Str[length(Str)] := Key;
	       DrawChar(Key);
	    END
	    ELSE
	       Done := TRUE;
	 END;		
   UNTIL Done;
END; { ReadStr }
   

PROCEDURE ReadInt(VAR Value : INTEGER);
VAR Done    : BOOLEAN;
   AnEvent  : EventRecord;
   Key	    : char;
   KeyValue : INTEGER;
	    
BEGIN	    
   Done := FALSE;
   Value := 0;
   REPEAT
      IF getNextEvent(EveryEvent,AnEvent) THEN
	 IF (anEvent.what = keydown) or (anEvent.what = autoKey) THEN
	 BEGIN
	    Key := char(BAnd(anEvent.message, charCodeMask));
	    IF (ord(Key) <> 13) THEN
	    BEGIN
	       KeyValue := ord(Key) - ord('0');
	       IF (KeyValue >= 0) and (KeyValue <=9) THEN
	       BEGIN
		  Value := Value*10+KeyValue;
		  DrawChar(Key);
	       END
	    END
	    ELSE
	       Done := TRUE;
	 END;		
   UNTIL Done;
END; { ReadInt }
   
PROCEDURE ReadReal(VAR Value : Real);
VAR Done    : BOOLEAN;
   AnEvent  : EventRecord;
   Key	    : char;
   KeyValue : INTEGER;
   Fraction : REAL;
BEGIN	    
   Done := FALSE;
   Value := 0.0;
   REPEAT
      IF getNextEvent(EveryEvent,AnEvent) THEN
	 IF (anEvent.what = keydown) or (anEvent.what = autoKey) THEN
	 BEGIN
	    Key := char(BAnd(anEvent.message, charCodeMask));
	    IF (Key <> '.') AND (ord(key) <> 13) THEN
	    BEGIN
	       KeyValue := ord(Key) - ord('0');
	       IF (KeyValue >= 0) and (KeyValue <=9) THEN
	       BEGIN
		  Value := Value*10+KeyValue;
		  DrawChar(Key);
	       END
	    END
	    ELSE
	       Done := TRUE;
	 END;		
   UNTIL Done;
   IF Key = '.' THEN
   BEGIN
      DrawChar('.');
      Done := FALSE;
      fraction := 1;
      REPEAT
	 IF getNextEvent(EveryEvent,AnEvent) THEN
	    IF (anEvent.what = keydown) or (anEvent.what = autoKey) THEN
	    BEGIN
	       Key := char(BAnd(anEvent.message, charCodeMask));
	       IF ord(Key) <> 13 THEN
	       BEGIN
		  KeyValue := ord(Key) - ord('0');
		  IF (KeyValue >= 0) and (KeyValue <=9) THEN
		  BEGIN
		     fraction := fraction *10;
		     Value := Value+KeyValue/fraction;
		     DrawChar(Key);
		  END
	       END
	       ELSE
		  Done := TRUE;
	    END;		
      UNTIL Done;
   END;
END; { ReadReal }
   
PROCEDURE Pause;
VAR
   e	: EventRecord;
   Done	: BOOLEAN;
	
BEGIN
   Done := FALSE;
   REPEAT
      IF GetNextEvent(EveryEvent, e) THEN
	 IF e.what IN [keydown,autokey,mousedown] THEN 
	    Done := True;
   UNTIL Done;
END; { Pause }
   

PROCEDURE DELAY(    Ticks : INTEGER);
VAR StopTime : LONGINT;
BEGIN	     
   StopTime := TickCount + Ticks DIV 100;
   REPEAT UNTIL TickCount > StopTime;
END; { DELAY }
   
END. {UNIT cs65}