/* macint.c - macintosh interface routines for xlisp */

#include "string.h"

#include <Types.h>	/* was MacTypes.h -- RBD */
#include <Quickdraw.h>  
#include <Windows.h>	/* was WindowMgr.h -- RBD */
#include <Events.h>	/* was EventMgr.h -- RBD */
#include <Dialogs.h>	/* was DialogMgr.h -- RBD */
#include <Menus.h>	/* was Menus.h -- RBD */
#include <Packages.h>	/* was PackageMgr.h -- RBD */
/* #include <StdFilePkg.h> -- RBD */
#include <Memory.h>	/* was MemoryMgr.h -- RBD */
#include <Desk.h>	/* was DeskMgr.h -- RBD */
#include <Fonts.h>	/* was FontMgr.h -- RBD */
#include <Controls.h>	/* was ControlMgr.h -- RBD */
/* #include <SegmentLdr.h> -- RBD */
#include <Files.h>	/* was FileMgr.h -- RBD */

/* program limits */
#define SCRH		40	/* maximum screen height */
#define SCRW		100	/* maximum screen width */
#define CHARMAX 	100	/* maximum number of buffered characters */
#define TIMEON		40	/* cursor on time */
#define TIMEOFF		20	/* cursor off time */

/* useful definitions */
#define MenuBarHeight	20
#define TitleBarHeight	20
#define SBarWidth	16
#define MinWidth	80
#define MinHeight	40
#define ScreenMargin	2
#define TextMargin	4
#define GHeight		232

/* menu id's */
#define appleID		1
#define fileID		256
#define editID		257
#define controlID	258

/* externals */
extern char *s_unbound;
extern char *PtoCstr();

/* screen dimensions */
int screenWidth;
int screenHeight;

/* command window (normal screen) */
int nHorizontal,nVertical,nWidth,nHeight;

/* command window (split screen) */
int sHorizontal,sVertical,sWidth,sHeight;

/* graphics window */
int gHorizontal,gVertical,gWidth,gHeight;

/* menu handles */
MenuHandle appleMenu;
MenuHandle fileMenu;
MenuHandle editMenu;
MenuHandle controlMenu;

/* misc variables */
OSType filetypes[] = { 'TEXT' };

/* font information */
int tmargin,lmargin;
int xinc,yinc;

/* command window */
WindowRecord cwrecord;
WindowPtr cwindow;

/* graphics window */
WindowRecord gwrecord;
WindowPtr gwindow;

/* window mode */
int splitmode;

/* cursor variables */
long cursortime;
int cursorstate;
int x,y;

/* screen buffer */
char screen[SCRH*SCRW],*topline,*curline;
int scrh,scrw;

/* type ahead buffer */
char charbuf[CHARMAX],*inptr,*outptr;
int charcnt;

macinit()
{
    /* initialize the toolbox */
    InitGraf(&thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(0L);
    InitCursor();

    /* setup the menu bar */
    SetupMenus();

    /* get the size of the screen */
    screenWidth  = screenBits.bounds.right  - screenBits.bounds.left;
    screenHeight = screenBits.bounds.bottom - screenBits.bounds.top;

    /* Create the graphics and control windows */
    gwindow = GetNewWindow(129,&gwrecord,(WindowPtr) -1L);
    cwindow = GetNewWindow(128,&cwrecord,(WindowPtr) -1L);

    /* establish the command window as the current port */
    SetPort(cwindow);

    /* compute the size of the normal command window */
    nHorizontal = ScreenMargin;
    nVertical = MenuBarHeight + TitleBarHeight + ScreenMargin - 2;
    nWidth = screenWidth - (ScreenMargin * 2) - 1;
    nHeight = screenHeight - MenuBarHeight - TitleBarHeight - (ScreenMargin * 2);

    /* compute the size of the split command window */
    sHorizontal = nHorizontal;
    sVertical = nVertical + GHeight + 1;
    sWidth = nWidth;
    sHeight = nHeight - GHeight - 1;

    /* compute the size of the graphics window */
    gHorizontal = nHorizontal;
    gVertical = MenuBarHeight + ScreenMargin;
    gWidth = screenWidth - (ScreenMargin * 2);
    gHeight = GHeight;

    /* move and size the graphics window */
    MoveWindow(gwindow,gHorizontal,gVertical,0);
    SizeWindow(gwindow,gWidth,gHeight,0);

    /* setup the font, size and writing mode for the command window */
    TextFont(monaco); TextSize(9); TextMode(srcCopy);

    /* setup command mode */
    scrsplit(FALSE);

    /* disable the Cursor */
    cursorstate = -1;

    /* setup the input ring buffer */
    inptr = outptr = charbuf;
    charcnt = 0;
    
    /* lock the font in memory */
    SetFontLock(-1);
}

SetupMenus()
{
    appleMenu = GetMenu(appleID);	/* setup the apple menu */
    AddResMenu(appleMenu,'DRVR');
    InsertMenu(appleMenu,0);
    fileMenu = GetMenu(fileID);		/* setup the file menu */
    InsertMenu(fileMenu,0);
    editMenu = GetMenu(editID);		/* setup the edit menu */
    InsertMenu(editMenu,0);
    controlMenu = GetMenu(controlID);	/* setup the control menu */
    InsertMenu(controlMenu,0);
    DrawMenuBar();
}

int scrgetc()
{
    CursorOn();
    while (charcnt == 0)
	DoEvent();
    CursorOff();
    return (scrnextc());
}

int scrnextc()
{
    int ch;
    if (charcnt > 0) {
	ch = *outptr++; charcnt--;
	if (outptr >= &charbuf[CHARMAX])
	    outptr = charbuf;
    }
    else {
	charcnt = 0;
	ch = -1;
    }
    return (ch);
}

scrputc(ch)
  int ch;
{
    switch (ch) {
    case '\r':
	x = 0;
	break;
    case '\n':
	nextline(&curline);
	if (++y >= scrh) {
	    y = scrh - 1;
	    scrollup();
	}
	break;
    case '\t':
	do { scrputc(' '); } while (x & 7);
	break;
    case '\010':
	if (x) x--;
	break;
    default:
	if (ch >= 0x20 && ch < 0x7F) {
	    scrposition(x,y);
	    DrawChar(ch);
	    curline[x] = ch;
	    if (++x >= scrw) {
		nextline(&curline);
		if (++y >= scrh) {
		    y = scrh - 1;
		    scrollup();
		}
		x = 0;
	    }
	}
	break;
    }
}

scrdelete()
{
    scrputc('\010');
    scrputc(' ');
    scrputc('\010');
}

scrclear()
{
    curline = screen;
    for (y = 0; y < SCRH; y++)
	for (x = 0; x < SCRW; x++)
	    *curline++ = ' ';
    topline = curline = screen;
    x = y = 0;
}

scrflush()
{
    inptr = outptr = charbuf;
    charcnt = -1;
    osflush();
}

scrposition(x,y)
  int x,y;
{
    MoveTo((x * xinc) + lmargin,(y * yinc) + tmargin);
}

DoEvent()
{
    EventRecord myEvent;
    
    SystemTask();
    CursorUpdate();

    while (GetNextEvent(everyEvent,&myEvent))
	switch (myEvent.what) {
	    case mouseDown:
		DoMouseDown(&myEvent);
		break;
	    case keyDown:
	    case autoKey:
		DoKeyPress(&myEvent);
		break;
	    case activateEvt:
		DoActivate(&myEvent);
		break;
	    case updateEvt:
		DoUpdate(&myEvent);
		break;
	    }
}

DoMouseDown(myEvent)
  EventRecord *myEvent;
{
    WindowPtr whichWindow;

    switch (FindWindow(myEvent->where,&whichWindow)) {
    case inMenuBar:
	DoMenuClick(myEvent);
	break;
    case inSysWindow:
	SystemClick(myEvent,whichWindow);
	break;
    case inDrag:
	DoDrag(myEvent,whichWindow);
	break;
    case inGoAway:
	DoGoAway(myEvent,whichWindow);
	break;
    case inGrow:
	DoGrow(myEvent,whichWindow);
	break;
    case inContent:
	DoContent(myEvent,whichWindow);
	break;
    }
}

DoMenuClick(myEvent)
  EventRecord *myEvent;
{
    long choice;
    if (choice = MenuSelect(myEvent->where))
	DoCommand(choice);
}

DoDrag(myEvent,whichWindow)
  EventRecord *myEvent;
  WindowPtr whichWindow;
{
    Rect dragRect;
    SetRect(&dragRect,0,MenuBarHeight,screenWidth,screenHeight);
    InsetRect(&dragRect,ScreenMargin,ScreenMargin);
    DragWindow(whichWindow,myEvent->where,&dragRect);
}

DoGoAway(myEvent,whichWindow)
  EventRecord *myEvent;
  WindowPtr whichWindow;
{
    if (TrackGoAway(whichWindow,myEvent->where))
	wrapup();
}

DoGrow(myEvent,whichWindow)
  EventRecord *myEvent;
  WindowPtr whichWindow;
{
    Rect sizeRect;
    long newSize;
    if (whichWindow != FrontWindow() && whichWindow != gwindow)
	SelectWindow(whichWindow);
    else {
	SetRect(&sizeRect,MinWidth,MinHeight,screenWidth,screenHeight-MenuBarHeight);
	newSize = GrowWindow(whichWindow,myEvent->where,&sizeRect);
	if (newSize) {
	    EraseRect(&whichWindow->portRect);
	    SizeWindow(whichWindow,LoWord(newSize),HiWord(newSize),-1);
	    InvalRect(&whichWindow->portRect);
	    SetupScreen();
	    scrflush();
	}
    }
}

DoContent(myEvent,whichWindow)
  EventRecord *myEvent;
  WindowPtr whichWindow;
{
    if (whichWindow != FrontWindow() && whichWindow != gwindow)
	SelectWindow(whichWindow);
}

DoKeyPress(myEvent)
  EventRecord *myEvent;
{
    long choice;
    
    if (FrontWindow() == cwindow) {
	if (myEvent->modifiers & 0x100) {
	    if (choice = MenuKey((char)myEvent->message))
		DoCommand(choice);
	}
	else {
	    if (charcnt < CHARMAX) {
		*inptr++ = myEvent->message & 0xFF; charcnt++;
		if (inptr >= &charbuf[CHARMAX])
		    inptr = charbuf;
	    }
	}
    }
}

DoActivate(myEvent)
  EventRecord *myEvent;
{
    WindowPtr whichWindow;
    whichWindow = (WindowPtr)myEvent->message;
    SetPort(whichWindow);
    if (whichWindow == cwindow)
	DrawGrowIcon(whichWindow);
}

DoUpdate(myEvent)
  EventRecord *myEvent;
{
    WindowPtr whichWindow;
    GrafPtr savePort;
    GetPort(&savePort);
    whichWindow = (WindowPtr)myEvent->message;
    SetPort(whichWindow);
    BeginUpdate(whichWindow);
    EraseRect(&whichWindow->portRect);
    if (whichWindow == cwindow) {
	DrawGrowIcon(whichWindow);
	RedrawScreen();
    }
    EndUpdate(whichWindow);
    SetPort(savePort);
}

DoCommand(choice)
  long choice;
{
    int theMenu,theItem;
    
    /* decode the menu choice */
    theMenu = HiWord(choice);
    theItem = LoWord(choice);
    
    CursorOff();
    HiliteMenu(theMenu);
    switch (theMenu) {
    case appleID:
	DoAppleMenu(theItem);
	break;
    case fileID:
	DoFileMenu(theItem);
	break;
    case editID:
	DoEditMenu(theItem);
	break;
    case controlID:
	DoControlMenu(theItem);
	break;
    }
    HiliteMenu(0);
    CursorOn();
}

pascal Boolean aboutfilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
{
    return (theEvent->what == mouseDown ? -1 : 0);
}

DoAppleMenu(theItem)
  int theItem;
{
    DialogRecord mydialog;
    Str255 name;
    GrafPtr gp;
    short n;

    switch (theItem) {
    case 1:
	GetNewDialog(129,&mydialog,(WindowPtr) -1L);
	ModalDialog(aboutfilter,&n);
	CloseDialog((DialogPtr) &mydialog);
	break;
    default:
	GetItem(appleMenu,theItem,name);
	GetPort(&gp);
	OpenDeskAcc(name);
	SetPort(gp);
	break;
    }
}

pascal Boolean filefilter(pblock)
  ParmBlkPtr pblock;
{
    unsigned char *p; int len;
    p = pblock->fileParam.ioNamePtr; len = *p++ &0xFF;
    return ((len >= 4 && (strncmp((char *) (p+len-4), ".lsp", 4) == 0)) ? 0 : -1);
}

DoFileMenu(theItem)
  int theItem;
{
    SFReply loadfile;
    Point p;

    switch (theItem) {
    case 1:	/* load */
    case 2:	/* load noisily */
	p.h = 100; p.v = 100;
	SFGetFile(p,"\P",filefilter,-1,filetypes,0L,&loadfile);
	if (loadfile.good) {
	    HiliteMenu(0);
	    SetVol(0L,loadfile.vRefNum);
	    if (xlload(PtoCstr(loadfile.fName),1,(theItem == 1 ? 0 : 1)))
		scrflush();
	    else
		xlabort("load error");
	}
	break;
    case 4:	/* quit */
	wrapup();
    }
}

DoEditMenu(theItem)
  int theItem;
{
    switch (theItem) {
    case 1:	/* undo */
    case 3:	/* cut */
    case 4:	/* copy */
    case 5:	/* paste */
    case 6:	/* clear */
	SystemEdit(theItem-1);
	break;
    }
}

DoControlMenu(theItem)
  int theItem;
{
    scrflush();
    HiliteMenu(0);
    switch (theItem) {
    case 1:	/* break */
	xlbreak("user break",s_unbound);
	break;
    case 2:	/* continue */
	xlcontinue();
	break;
    case 3:	/* clean-up error */
	xlcleanup();
	break;
    case 4:	/* Cancel input */
	xlabort("input canceled");
	break;
    case 5:	/* Top Level */
	xltoplevel();
	break;
    case 7:	/* split screen */
	scrsplit(splitmode ? FALSE : TRUE);
	break;
    }
}

scrsplit(split)
  int split;
{
    ShowHide(cwindow,0);
    if (split) {
	CheckItem(controlMenu,7,-1);
	ShowHide(gwindow,-1);
	MoveWindow(cwindow,sHorizontal,sVertical,-1);
	SizeWindow(cwindow,sWidth,sHeight,-1);
	InvalRect(&cwindow->portRect);
	SetupScreen();
    }
    else {
	CheckItem(controlMenu,7,0);
	ShowHide(gwindow,0);
	MoveWindow(cwindow,nHorizontal,nVertical,-1);
	SizeWindow(cwindow,nWidth,nHeight,-1);
	InvalRect(&cwindow->portRect);
	SetupScreen();
    }
    ShowHide(cwindow,-1);
    splitmode = split;
}

SetupScreen()
{
    FontInfo info;
    Rect *pRect;

    /* get font information */
    GetFontInfo(&info);

    /* compute the top and bottom margins */
    tmargin = TextMargin + info.ascent;
    lmargin = TextMargin;

    /* compute the x and y increments */
    xinc = info.widMax;
    yinc = info.ascent + info.descent + info.leading;

    /* compute the character dimensions of the screen */
    pRect = &cwindow->portRect;
    scrh = (pRect->bottom - (2 * TextMargin) - (SBarWidth - 1)) / yinc;
    if (scrh > SCRH) scrh = SCRH;
    scrw = (pRect->right - (2 * TextMargin) - (SBarWidth - 1)) / xinc;
    if (scrw > SCRW) scrw = SCRW;
    
    /* clear the screen */
    scrclear();
}

CursorUpdate()
{
    if (cursorstate != -1)
	if (cursortime < TickCount()) {
	    scrposition(x,y);
	    if (cursorstate) {
		DrawChar(' ');
		cursortime = TickCount() + TIMEOFF;
		cursorstate = 0;
	    }
	    else {
		DrawChar('_');
		cursortime = TickCount() + TIMEON;
		cursorstate = 1;
	    }
	}
}

CursorOn()
{
    cursortime = TickCount();
    cursorstate = 0;
}

CursorOff()
{
    if (cursorstate == 1) {
	scrposition(x,y);
	DrawChar(' ');
    }
    cursorstate = -1;
}

RedrawScreen()
{
    char *Line; int y;
    Line = topline;
    for (y = 0; y < scrh; y++) {
	scrposition(0,y);
	DrawText(Line,0,scrw);
	nextline(&Line);
    }
}

nextline(pline)
  char **pline;
{
    if ((*pline += SCRW) >= &screen[SCRH*SCRW])
	*pline = screen;
}

scrollup()
{
    RgnHandle updateRgn;
    Rect rect;
    int x;
    updateRgn = NewRgn();
    rect = cwindow->portRect;
    rect.bottom -= SBarWidth - 1;
    rect.right -= SBarWidth - 1;
    ScrollRect(&rect,0,-yinc,updateRgn);
    DisposeRgn(updateRgn);
    for (x = 0; x < SCRW; x++)
	topline[x] = ' ';
    nextline(&topline);
}
