//	DaveUtils.c

#include <ctype.h>
#include <string.h>
#include <stdio.h>

#include "DaveUtils.h"
#include <Packages.h>
#include "offscreen_manager.h"

/*************** this AppleEvent thing originally from THINK REFERENCE **************/

#include <AppleEvents.h>
#include <Processes.h>
#include <Aliases.h>

#define kFinderSig			'FNDR'
#define kAEFinderEvents		'FNDR'
#define kSystemType			'MACS'

#define kAEOpenSelection	'sope'
#define keySelection		'fsel'

///Err OpenExternalDocument(FSSpecPtr theDoc);

// Search through the current process list to find the given application. See
// Using the Process Manager for a similar way of doing this.
static OSErr FindAProcess(OSType typeToFind, OSType creatorToFind,
			ProcessSerialNumberPtr processSN)
{
	ProcessInfoRec	tempInfo;
	FSSpec			procSpec;
	Str31			processName;
	OSErr			err = noErr;

	memset(&tempInfo, 0, sizeof(tempInfo));				// clear out paramblock (dps)

	// start at the beginning of the process list
	processSN->lowLongOfPSN = kNoProcess;
	processSN->highLongOfPSN = kNoProcess;

	// initialize the process information record
	tempInfo.processInfoLength = sizeof(ProcessInfoRec);
	tempInfo.processName = (StringPtr)&processName;
	tempInfo.processAppSpec = &procSpec;

	while ((tempInfo.processSignature != creatorToFind ||
			tempInfo.processType != typeToFind) ||
			err != noErr) {
			
		err = GetNextProcess(processSN);
		if (err == noErr)
			GetProcessInformation(processSN, &tempInfo);
	}
	
	return err;
}


#define ERR2(FUNC)		do { if ((err2 = (FUNC)) && !err) err = err2; } while (0)


// Given a FSSpecPtr to either an application or a document, OpenSelection creates a
// finder Open Selection Apple event for the object described by the FSSpec.
/*
Err OpenExternalDocument(FSSpecPtr theDoc)
{
	AppleEvent			aeEvent;			// the event to create;
	AEDesc				myAddressDesc;		// descriptors for the 
	AEDesc				aeDirDesc;
	AEDesc				listElem;
	AEDesc				fileList;			// our list
	FSSpec				dirSpec;
	AliasHandle			dirAlias = NULL;	// alias to directory with our file
	AliasHandle			fileAlias = NULL;	// alias of the file itself
	ProcessSerialNumber process;			// the finder's psn
	OSErr				err2, err = noErr;

	// Get the psn of the Finder and create the target address
	if (FindAProcess(kFinderSig, kSystemType, &process))
		return procNotFound;

	err = AECreateDesc(typeProcessSerialNumber,(Ptr) &process,
							sizeof(process), &myAddressDesc);
	if (!err) {
		err = AECreateAppleEvent (kAEFinderEvents, kAEOpenSelection,
						&myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID,
						&aeEvent);
		if (!err) {
			// Make an FSSpec and alias for the parent folder, and an alias for the file
			FSMakeFSSpec(theDoc->vRefNum, theDoc->parID, NULL, &dirSpec);
			
			err = NewAlias(NULL, &dirSpec, &dirAlias);
			if (!err) err = NewAlias(NULL, theDoc, &fileAlias);

			if (!err) err = AECreateList(NULL, 0, FALSE, &fileList);
			if (!err) {
				HLock((Handle)dirAlias);
				err = AECreateDesc(typeAlias, (Ptr)*dirAlias, GetHandleSize((Handle) dirAlias),
								&aeDirDesc);
				HUnlock((Handle)dirAlias);
			
				if (!err) err = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc);

				ERR2(AEDisposeDesc(&aeDirDesc));

				if (!err) {
					HLock((Handle)fileAlias);
					err = AECreateDesc(typeAlias, (Ptr)*fileAlias,
							GetHandleSize((Handle)fileAlias), &listElem);
					HUnlock((Handle)fileAlias);
					
					if (!err) {
						if (!err) err = AEPutDesc(&fileList, 0, &listElem);
						ERR2(AEDisposeDesc(&listElem));
					}
				}
				
				if (!err) err = AEPutParamDesc(&aeEvent, keySelection, &fileList);
			
				ERR2(AEDisposeDesc(&fileList));
			}
			
			if (!err) err = AESend(&aeEvent, NULL,
									kAENoReply+kAEAlwaysInteract+kAECanSwitchLayer,
									kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
			
			ERR2(AEDisposeDesc(&aeEvent));
		}
		
		ERR2(AEDisposeDesc(&myAddressDesc));
	}

	if (dirAlias)
		DisposHandle((Handle)dirAlias);
	
	if (fileAlias)
		DisposHandle((Handle)fileAlias);
	
	return err;
}

*/
int			check_key_down(long key_mask)
{
	KeyMap	theKeys;

	GetKeys(theKeys);

	return((theKeys[1] & key_mask) != 0);
}

long	GetModifiers(void);
long	GetModifiers(void)
{
	long	mods = 0;
	KeyMap	theKeys;

	GetKeys(theKeys);
	
	mods |= Button() ? btnState : 0;
	mods |= (theKeys[1] & command_key)	!= 0 ? cmdKey : 0;
	mods |= (theKeys[1] & shift_key)	!= 0 ? shiftKey : 0;
	mods |= (theKeys[1] & caps_lock)	!= 0 ? alphaLock : 0;
	mods |= (theKeys[1] & option_key)	!= 0 ? optionKey : 0;
	mods |= (theKeys[1] & control_key)	!= 0 ? controlKey : 0;
	
	return mods;
}


char	*TextEditToString(TEHandle te, char *string)
{
	char		**charHandle;
	SignedByte	handleState;
	
	charHandle		= TEGetText(te);
	handleState		= HGetState(charHandle);
	HLock(charHandle);
		
	memcpy(string, *charHandle, (**te).teLength);
	
	HSetState(charHandle, handleState);
	
	string[(**te).teLength] = 0;
	
	return(string);
}

void	StringToTextEdit(char *string, TEHandle te)
{
	TESetText(string, strlen(string), te);
}


short
IUCompCString(char *aStr, char *bStr)
{
	return(IUMagString(aStr, bStr, strlen(aStr), strlen(bStr)));
}

static int S_width;
static int S_height;

static pascal void
DontCalcMenuSizeFunc(MenuHandle menu)
{
#ifdef ARCH_MAC
	long		oldA5;
	
	oldA5 = SetCurrentA5();
#endif

	(*menu)->menuWidth = S_width;
	(*menu)->menuHeight = S_height;

#ifdef ARCH_MAC	
	SetA5(oldA5);
#endif
}


/*

	original version

long
DoFixedSizePopMenu(MenuHandle theMenuH, Point loc, short width, short old_item)
{
	long 						new_item;
	UniversalProcPtr			old_addr;
	static UniversalProcPtr		S_trap_upp = NULL;
	
	CalcMenuSize(theMenuH);
	S_width = width;
	S_height = (**theMenuH).menuHeight;
	
	old_addr = NGetTrapAddress(0xA948, ToolTrap);
	
	if (S_trap_upp == NULL) {
		S_trap_upp = old_addr;		// $$$PPC should be NewProcPtr(DontCalcMenuSize)
	}
	
	NSetTrapAddress(S_trap_upp, 0xA948, ToolTrap);
	new_item = PopUpMenuSelect(theMenuH, loc.v, loc.h, old_item);
	NSetTrapAddress(old_addr, 0xA948, ToolTrap);
		
	return new_item;
}
*/


typedef pascal void (*CalcMenuSizeProcPtr)(MenuRef theMenu);

enum {
	uppCalcMenuSizeProcInfo = kPascalStackBased
		 | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(MenuRef)))
};

#if GENERATINGCFM
	typedef UniversalProcPtr CalcMenuSizeUPP;
#else
	typedef MenuDefProcPtr CalcMenuSizeUPP;
#endif

/** DoFixedSizePopMenu

	New version that uses the FlexMDEF to guarantee our font will be used.
	
	Has to communicate: font & font-size, the fact that this is a menu
	that doesn't want to use the first-char-holding-the-modkeys convention.
	
	Since we'll also want to use this for non-fixed-width menus, the width
	param should be optional.  It appears that the font is expected to be set
	in the current port -- that stuff should probably be moved in here,
	along with the InsertMenu/DeleteMenu calls.
	
	Note that none of this is implemented yet -- if our old style holds
	up, then we won't have to.  Otherwise the FlexMDEF will have to be
	updated to honor the info mentioned above, and it must work with
	SICNs for the interp pop-up.

**/
long
DoFixedSizePopMenu(MenuHandle theMenuH, Point loc, short width, short old_item)
{
	long 						new_item;
	UniversalProcPtr			old_addr;
	DECLARE_SRD(S_dontcalcRD, CalcMenuSize, DontCalcMenuSizeFunc);
	
	CalcMenuSize(theMenuH);
	S_width = width;
	S_height = (**theMenuH).menuHeight;
	
	old_addr = NGetTrapAddress(0xA948, ToolTrap);
	
	NSetTrapAddress((UniversalProcPtr)ADDR_OF_SRD(S_dontcalcRD, CalcMenuSize),
						0xA948, ToolTrap);
	new_item = PopUpMenuSelect(theMenuH, loc.v, loc.h, old_item);
	NSetTrapAddress(old_addr, 0xA948, ToolTrap);
		
	return new_item;
}

/*
 * Routines and defines for setting the font to the dialog nine point.
 * Swiped from Illustrator DRL 3/18/93
 */

#define kTwelveMKfoID	256
#define kNineMKfoID		257		/* ID of nine-point MKfo/SICN resources */
#define kMFntID			128		/* ID of the MFnt resource */
#define kFlexMDEF		260		/* ID of the FlexMDEF */

/*----------------------------------------------------------------------------------------
 * This is the structure of a 'Mfnt' "Menu FoNT" resource. This is the resource by which
 * the application communicates its font, size, and symbolic icon preference to the MDEF.
 * This struct definition is shared by the host application. */
typedef struct {
	short		font;			/* The font ID we should be using */
	short		size;			/* The point size we should be using */
	short		modKeyID;		/* ID of the SICN/MKfo resources we'll be using */
} MFntRec, *MFntPtr, **MFntHdl;

static short S_saveSysFontFam, S_saveSysFontSize, S_saveModKeyID, S_saveFont, S_saveSize;

//******************************************************************************
/* Set up the MFnt resource such that all subsequent operations in the MDEF are done using
 * the given font, size, and symbol set ID. Normally, this would only be done just before
 * a call to PopUpMenuSelect. Added by Beaker on 10/27/92 at 4:05 PM. */
static void PopupSetMenuFont(short font, short size, MenuHandle menu)
{
	MFntHdl	menuFontHdl;
	
	menuFontHdl = (MFntHdl)GetResource('MFnt', kMFntID);
	if (menuFontHdl != nil)
	{
		GrafPtr currentPort;
		GetPort(&currentPort);
		
		S_saveFont = currentPort->txFont;
		S_saveSize = currentPort->txSize;
		
		S_saveSysFontFam = LMGetSysFontFam();
		S_saveSysFontSize = LMGetSysFontSize();

		S_saveModKeyID = (*menuFontHdl)->modKeyID;

		TextFont(font);
		TextSize(size);

		LMSetSysFontFam(font);
		LMSetSysFontSize(size);
		LMSetLastSPExtra(-1);

		(*menuFontHdl)->font = font;
		(*menuFontHdl)->size = size;
		(*menuFontHdl)->modKeyID = (size < 12) ? kNineMKfoID : kTwelveMKfoID;
		
		CalcMenuSize(menu);
	}
}

//******************************************************************************
/* Reset the MDEF to use the System font at the default size. Normally you would do this
 * after a call to PopUpMenuSelect to restore the menu font to 12-point Chicago. */
static void PopupRestoreMenuFont(MenuHandle menu)
{
	MFntHdl	menuFontHdl;
	
	menuFontHdl = (MFntHdl)GetResource('MFnt', kMFntID);
	if (menuFontHdl != nil)
	{
		GrafPtr currentPort;

		LMSetSysFontFam(S_saveSysFontFam);		
		LMSetSysFontSize(S_saveSysFontSize);
		LMSetLastSPExtra(-1);

		GetPort(&currentPort);

		TextFont(S_saveFont);
		TextSize(S_saveSize);

		(*menuFontHdl)->font = S_saveSysFontFam;
		(*menuFontHdl)->size = S_saveSysFontSize;		
		(*menuFontHdl)->modKeyID = S_saveModKeyID;
		
		CalcMenuSize(menu);
	}
}

// Set the item in the Flex MDEF -- Cmd keys are put in the first location
// Note:  This modifies <menuString> !
static void FlexSetItemString(MenuHandle menu, short item, StringPtr menuString)
{
	Str255 itemString;

	if (menu)
	{
		GetItem(menu, item, itemString);
		BlockMove(menuString + 1, menuString + 2, *menuString);
		menuString[1] = itemString[1];
		*menuString += 1;
		SetItem(menu, item, menuString);
	}
}

// Set the menu to use the Flex MDEF
static void PopupSetMenuDef(MenuHandle menu)
{
	Handle code;
	
	code = GetResource('MDEF', kFlexMDEF);
	(**menu).menuProc = code;
}

static Boolean isItemEnabled(MenuHandle menu, short item)
{
	Boolean result = true;
	long flag = 1L;

	if (item >= 1 && item <= 31)
		flag = 1L << item;
	if (!((**menu).enableFlags & 1) || !((**menu).enableFlags & flag))
		result = false;
	return result;
}

static short isItemChecked(MenuHandle menu, short item)
{
	short markChar;
	
	GetItemMark(menu, item, &markChar);
	return (markChar != noMark && markChar != hMenuCmd) ? markChar : 0;
}


/** DoFlexSizePopMenu

	Uses the FlexMDEF to popup a menu in the requested font.  Optionally with a
	fixed width.  style is ignored
	
	Note that the orig_menuH doesn't need to be InsertMenu()ed outside of this
	routine, since it's a copy that actually gets popped up.

	from Greg Gilley
	
	Seems to me that we should grab menuID from orig_menuH, instead of having it
	passed in, but I don't know of a modern way of getting the menu id from the
	menu.  -dps

**/
long
DoFlexSizePopMenu(short menuID, MenuHandle orig_menuH, Point loc, short width0, short old_item,
				  short font, short size, short style)
{
	long 						new_item;
	UniversalProcPtr			old_addr;
	MenuHandle					popupMenu;
	long						itemCount;
	long						item;
	short						mark_char;
	Style						item_style;
	Str255						menuString;
	DECLARE_SRD(S_dontcalcRD, CalcMenuSize, DontCalcMenuSizeFunc);

	/*
	 * Make a copy of the menu
	 */
	popupMenu = NewMenu(999, (StringPtr)"\ppopup");
	if (popupMenu == nil)
		return 0;

	itemCount = CountMItems(orig_menuH);
	for (item=1; item <= itemCount; item++) {
		GetItem(orig_menuH, item, menuString);
		AppendMenu(popupMenu, (StringPtr)"\03\0(-");
		FlexSetItemString(popupMenu, item, menuString);

		if (isItemEnabled(orig_menuH, item))
			EnableItem(popupMenu, item);
		else
			DisableItem(popupMenu, item);
			
		mark_char = isItemChecked(orig_menuH, item);
		if (mark_char)
			SetItemMark(popupMenu, item, mark_char);

		GetItemStyle(orig_menuH, item, &item_style);
		SetItemStyle(popupMenu, item, item_style);
	}

	PopupSetMenuDef(popupMenu);
	InsertMenu(popupMenu, hierMenu);
	PopupSetMenuFont(font, size, popupMenu);

	S_width = width0;
	S_height = (**popupMenu).menuHeight;

	if (width0 != 0) {
		old_addr = NGetTrapAddress(0xA948, ToolTrap);
		
		NSetTrapAddress((UniversalProcPtr)ADDR_OF_SRD(S_dontcalcRD, CalcMenuSize),
							0xA948, ToolTrap);
	}

	new_item = PopUpMenuSelect(popupMenu, loc.v, loc.h, old_item);

	if (width0 != 0)
		NSetTrapAddress(old_addr, 0xA948, ToolTrap);

	PopupRestoreMenuFont(popupMenu);

	DeleteMenu(999);
	DisposeMenu(popupMenu);

	/*
	 * set the menu id in the selected item back to the real one
	 */
	if (new_item != 0)
	{
		new_item &= 0xffff;
		new_item |= ((long)menuID) << 16;
	}

	return new_item;
}


long
RoundToNearestMult(register long x, unsigned long m)
{
	if (m == 0) {
		SysBeep(1);
		m = 1;
	}
	
	if (x < 0) {
		x = -x;
		x += m/2;
		x -= x % m;
		x = -x;
	} else {
		x += m/2;
		x -= x % m;
	}
	
	return x;
}


long Gcd(register long u, register long v)	/* uses Euclid's Algorithm */
{
	register long	temp;
	
	if (u <= 0) u = 1;
	if (v <= 0) v = 1;
	
	while (v) {
		temp = u % v;
		u = v;
		v = temp;
	}
	
	return u;
}


void		LocalRectToGlobal(Rect *theRect)
{
	Point 	*a = &topLeft(*theRect),
			*b = &botRight(*theRect);

	LocalToGlobal(a);
	LocalToGlobal(b);
}

void		GlobalRectToLocal(Rect *theRect)
{
	Point 	*a = &topLeft(*theRect),
			*b = &botRight(*theRect);

	GlobalToLocal(a);
	GlobalToLocal(b);
}

Rect	PointToRect(Point thePoint, short size, short extra)
{
	Rect	theRect;
	
	theRect.left	= theRect.right		= thePoint.h;
	theRect.top		= theRect.bottom	= thePoint.v;
	InsetRect(&theRect, -size, -size);
	
	if (extra > 0) {
		theRect.right	+= extra;
		theRect.bottom	+= extra;
	} else {
		theRect.left	+= extra;
		theRect.top		+= extra;
	}
	
	return(theRect);
}

void		RemoveTrailingNumber(char *name)
{
	char	*end;
	
	U_ASSERT(name && name[0] != 0);
	
	end = &(name[strlen(name) - 1]);
	
	while (isspace(*end) || isdigit(*end))
		end--;
	
	end[1] = 0;
}

Boolean		MouseStateChanged(
	Point	startPoint, 
	long	timeOutTicks, 
	short	pixDelta, 
	Boolean	*moved, 
	Boolean	*buttonUp
) {
	long		lastTick;
	Boolean		stateChanged, useButton = timeOutTicks == -2;
	Point		thePoint;
	Rect		theRect			= PointToRect(startPoint, pixDelta, 1);
	
	if (timeOutTicks < 0)
		timeOutTicks = 60 * 60 * 60 * 24 * 30;		//	time out in one month
	
	lastTick = TickCount() + timeOutTicks;
	
	do {
		GetMouse(&thePoint);
		*moved			= !PtInRect(thePoint, &theRect);
		
		if (useButton)
			*buttonUp		= !Button();
		else
			*buttonUp		= !StillDown();

		stateChanged	= *moved || *buttonUp;
	} while (!stateChanged && TickCount() <= lastTick);
	
	return(stateChanged);
}


Boolean		TrackMouse1(	Rect *theRectPtr,
							void (*HilightProc)(Rect *theRect, void *data, Boolean in, Boolean Last),
							void (*TrackingProc)(void *data),
							void *data)
{
	Point		thePoint;
	Boolean		inLastTime = FALSE, inThisTime;

	if (!StillDown()) {			// mouse already up, assume we had a quick-click inside
		(*HilightProc)(theRectPtr, data, TRUE, FALSE);
		if (TrackingProc)
			(*TrackingProc)(data);
		(*HilightProc)(theRectPtr, data, FALSE, TRUE);
		return TRUE;
	}
	
	do {
		GetMouse(&thePoint);
		if (inThisTime = PtInRect(thePoint, theRectPtr)) { /*=*/
			if (!inLastTime)
				(*HilightProc)(theRectPtr, data, inThisTime, FALSE);
			if (TrackingProc != NULL)
				(*TrackingProc)(data);
		} else
			if (inLastTime)
				(*HilightProc)(theRectPtr, data, inThisTime, FALSE);		
		inLastTime = inThisTime;
	} while (StillDown());

	if (inLastTime) {
		(*HilightProc)(theRectPtr, data, !inLastTime, TRUE);
		return(TRUE);
	} else
		return(FALSE);
}		

GWorldPtr		GetPictResOffscreen(short pictResID, short depth)
{
	PicHandle		thePict;
	GWorldPtr		theOffscreen;
	
	FailNILRes(thePict = GetPicture(pictResID));
	FailNIL(theOffscreen =  PICT_to_offscreen(thePict, depth));
	ReleaseResource((Handle)thePict);
	return(theOffscreen);
}

void		InvertHilite(Rect *theRect, void *data, Boolean in, Boolean last)
{
	InvertRect(theRect);
}

StringPtr	CopyC2P(const char *see_string, unsigned char *pee_string)
{
	strcpy((char *)pee_string, see_string);
	c2pstr((char *)pee_string);
	return(pee_string);
}


char		*CopyP2C(const unsigned char *pee_string, char *see_string)
{
	CopyString(pee_string, see_string);
	PtoCstr((unsigned char*)see_string);
	
	return see_string;
}

void			ClearMenu(MenuHandle theMenu)
{
	if (theMenu != NULL) {
		int		items, curItem;
		
		items = CountMItems(theMenu);
		for (curItem = 1; curItem <= items; ++curItem)
			DelMenuItem(theMenu, 1);
	}
}


/******************************************************************************
		DrawOnlyGrowIcon
 ******************************************************************************/
void	DrawOnlyGrowIcon(WindowPtr theWindow)
{
	#define			rectSiz 15
	GrafPtr			savePort;
	Rect 			clippingRect;

	if (theWindow != NULL)
	{
		clippingRect 		= theWindow->portRect;
		clippingRect.left	= clippingRect.right	- rectSiz;
		clippingRect.top	= clippingRect.bottom	- rectSiz;
		GetPort(&savePort);		
		SetPort(theWindow);

		GetClip(gUtilRgn);
		ClipRect(&clippingRect);
		DrawGrowIcon(theWindow);
		SetClip(gUtilRgn);

		SetPort(savePort);
	}
}

/** PlotSIconMaskHandle

	iconHandle is of a SICN resource with icon/mask pairs
	
	index is zero-based, and assumes the mask immediately follows

**/
void		PlotIconMaskHandle(Handle iconHandle, short index, short iconSize, short hPos, short vPos)
{
	BitMap		iconMap, maskMap;
	Rect		destRect;
	long		myGray[2];

	destRect.top = destRect.left = 0;
	destRect.bottom = destRect.right = iconSize;
	iconMap.bounds = destRect;
	OffsetRect(&destRect, hPos, vPos);

	if (iconHandle) {
		LoadResource(iconHandle);
		HLock(iconHandle);
		iconMap.baseAddr	= *iconHandle + (index << 6);			// * 32 * 2 
		iconMap.rowBytes	= 2;
		maskMap = iconMap;
		maskMap.baseAddr	+= 32;
		
		CopyMask(&iconMap, &maskMap, &qd.thePort->portBits, &iconMap.bounds, &maskMap.bounds, &destRect);
		HUnlock(iconHandle);
	} else {
		myGray[0] = myGray[1] = 0xAA55AA55;
		FillRect(&destRect, (ConstPatternParam)&myGray);
	}
}

void		PlotSIconMaskHandle(Handle iconHandle, short index, short hPos, short vPos)
{
	PlotIconMaskHandle(iconHandle, index, 16, hPos, vPos);
}


void		PlotSIcon(short resID, short listID, short hPos, short vPos)
{
	PlotSIconHandle(GetResource('SICN', resID), listID, hPos, vPos);
}

void		PlotIconHandle(Handle iconHandle, short listID, short iconSize, short hPos, short vPos)
{
	BitMap		iconMap;
	Rect		destRect;
	long		myGray[2];

	destRect.top = destRect.left = 0;
	destRect.bottom = destRect.right = iconSize;
	iconMap.bounds = destRect;
	OffsetRect(&destRect, hPos, vPos);

	if (iconHandle != NULL)
	{
		LoadResource(iconHandle);
		HLock(iconHandle);
		iconMap.baseAddr	= *iconHandle + (32 * listID);
		iconMap.rowBytes	= 2;
		CopyBits(&iconMap, &qd.thePort->portBits, &iconMap.bounds, &destRect, qd.thePort->pnMode, NULL);
		HUnlock(iconHandle);
	} else {
		myGray[0] = myGray[1] = 0xAA55AA55;
		FillRect(&destRect, (ConstPatternParam)&myGray);
	}
}

void		PlotSIconHandle(Handle iconHandle, short listID, short hPos, short vPos)
{
	PlotIconHandle(iconHandle, listID, 16, hPos, vPos);
}

void		PlotMIconHandle(Handle iconHandle, short listID, short hPos, short vPos)
{
	PlotIconHandle(iconHandle, listID, 10, hPos, vPos);
}

void		HiliteRect(Rect *theRect)
{
	LMSetHiliteMode(LMGetHiliteMode() & (~(1 << hiliteBit)));	// BCLR	#hiliteBit, HiliteMode;
	InvertRect(theRect);
}

char		*FormatBytes(long bytes, char *buffer)
{
	long	kilos, megs, dec;
	
	megs	=  bytes / 1048576;		//	1024 * 1024;
	bytes	-= megs * 1048576;		//	1024 * 1024;
	kilos	=  bytes / 1024;
	bytes	-= kilos * 1024;
	
	if (megs > 0) {
		if (megs > 99) {
			sprintf(buffer, "%ldM", megs);
		} else {
			dec = kilos / 10;	
			sprintf(buffer, "%.1fM", (float)((float)(megs * 1024 + kilos)/1024));
		}
	} else if (kilos > 0 || (kilos == 0 && bytes == 0)) {
		sprintf(buffer, "%ldK", kilos);
	} else {
		sprintf(buffer, "%ld", bytes);
	}
	
	return(buffer);
}

void	BlitPict(PicHandle	pictHandle, int  hOffset, int  vOffset)
{
	Rect	theRect;
	
	if (!*pictHandle)
		LoadResource((Handle)pictHandle);
	
	HLock((Handle)pictHandle);
	theRect = (**pictHandle).picFrame;
	OffsetRect(&theRect, hOffset - theRect.left, vOffset - theRect.top);
	DrawPicture(pictHandle, &theRect);
	HUnlock((Handle) pictHandle);
}

static Boolean		S_use_timecode = TRUE;



void SetUseTimeCode(Boolean tc)
{
	S_use_timecode = tc;
}



Boolean GetUseTimeCode(void)
{
	return S_use_timecode;
}

static Fixed		S_my_private_fps = M_INT2FIX(30);

short		GetFPS(void)
{
	return(M_FIX2INT_ROUND(S_my_private_fps));
}

void		SetFPS(short fps)
{
	S_my_private_fps = M_INT2FIX(fps);
}


Fixed		GetFixedFPS(void)
{
	return S_my_private_fps;
}

void		SetFixedFPS(Fixed fps)
{
	S_my_private_fps = fps;
}


void
DrawCString(const char *str)
{
	Str255	pstr;
	
	DrawString(CopyC2P(str, pstr));
}


/** GetHotTextRect

	The text box hangs out 3 pixels from each side.  Assumes the string_width passed
	in was that returned by DrawHotText, and is therefore already adjusted for inter-char
	spacing.
	
**/
void		GetHotTextRect(short x, short y, short string_width, Rect *r)
{
	r->right = (r->left = x - 3) + string_width + 6;
	r->top = (r->bottom = y + 4) - 13;
}

/** DrawHotText

	Given the position of the left edge of the baseline, this routine draws the
	given string with an underline three pixels below (i.e. two blank pixels between).
	The underline hangs out an extra pixel at each end.
	
	The remainder of the max_width is erased with a height that works for 9-point Geneva.
	
	Returns the width of the string, which is typically passed to GetHotTextRect come
	DoClick time.

**/
short	DrawHotText(short x, short y, short max_width, const char *str)
{
	Point		pt;
	PenState	save_pen;
	short		width;
	Rect		r;
	
	MoveTo(x, y);
	DrawCString(str);
	GetPen(&pt);
	
	GetPenState(&save_pen);
	PenPat(&qd.gray);

	width = pt.h - x - 1;			// correct for inter-char spacing
	MoveTo(x-1, y+2);
	Line(width+1, 0);

	SetPenState(&save_pen);
	
	// note the here we start the left edge over one pixel so we don't kill the end of that
	//	line we just drew; the DrawCString erased above it anyway
	SetRect(&r, x+width+1, y - 10, x+max_width, y + 3);
	EraseRect(&r);
	
	return width;
}


/** DrawTextNF

	Draws no-flash text that is guaranteed to erase around it, but without flashing.

**/
short	DrawTextNF(short x, short y, short max_width, const char *str, TextAlignmentType align)
{
	u_char		buf[256];
	short		width;
	Rect		r;
	
	width = StringWidth(CopyC2P(str, buf));
	
	switch (align) {
		case kTextAlignLeft:
			MoveTo(x, y);
			DrawString(buf);
			SetRect(&r, x+width, y - 10, x+max_width, y + 3);
			EraseRect(&r);
			break;
		
		default:
			// $$$ implement the others!
			break;
	}
	
	return width;
}




void		EraseInsetRect(Rect r)
{
		InsetRect(&r, 1, 1);
		EraseRect(&r);
}

void		DrawCheck(short checked, Rect *theRect)
{
	FrameRect(theRect);
	
	if (checked)
	{
		MoveTo(theRect->left, theRect->top);
		LineTo(theRect->right - 1, theRect->bottom - 1);
		MoveTo(theRect->right - 1, theRect->top);
		LineTo(theRect->left, theRect->bottom - 1);
	} else {
		EraseInsetRect(*theRect);
	}
}

void		UncheckToCheck(Rect *trackRect, void *data, Boolean in, Boolean last)
{
	Rect		theRect, *frameRect = (Rect *)data;
	
	theRect = *frameRect;
	InsetRect(&theRect, 1, 1);
	PenMode(srcXor);
	FrameRect(&theRect);
	PenMode(srcCopy);
}

void		CheckToUncheck(Rect *trackRect, void *data, Boolean in, Boolean last)
{
	Rect		theRect, *frameRect = (Rect *)data;
	
	theRect = *frameRect;
	InsetRect(&theRect, 1, 1);
	theRect.bottom--;
	theRect.right--;
	PenMode(srcXor);
	MoveTo(theRect.left + 1, theRect.top);
	LineTo(theRect.right - 1, theRect.top);
	MoveTo(theRect.right, theRect.top + 1);
	LineTo(theRect.right, theRect.bottom - 1);
	MoveTo(theRect.right - 1, theRect.bottom);
	LineTo(theRect.left + 1, theRect.bottom);
	MoveTo(theRect.left, theRect.bottom - 1);
	LineTo(theRect.left, theRect.top + 1);
	PenMode(srcCopy);
}

Boolean		TrackCheck(
	Rect	*trackRect, 
	Boolean	checked, 
	void	(*TrackingProc)(void *data),
	void	*data
) {
	Boolean		changed;
	
	U_ASSERT(data);
	
	if (checked)
		changed = TrackMouse1(trackRect, CheckToUncheck, TrackingProc, data);
	else
		changed = TrackMouse1(trackRect, UncheckToCheck, TrackingProc, data);
	
	return(changed);
}

void		BicRect(Rect *theRect)
{
	PenState	savePen;
	
	GetPenState(&savePen);
	PenNormal();
	PenMode(srcBic);
	PenPat(&qd.gray);
	PaintRect(theRect);
	SetPenState(&savePen);
}

/*********************************************************************************************
			ConcatNumToString
 *********************************************************************************************/
void		ConcatNumToString(void *srcStr1, long theNum, void *dstString)
{
	Str255	numStr, final;
	
	CopyString(srcStr1, &final);
	NumToString(theNum, (StringPtr)numStr);
	Concat(&final, &numStr, &final);
	CopyString(&final, dstString);
}

/*********************************************************************************************
			Concat
 *********************************************************************************************/
void		Concat(const void *srcStr1, const void *srcStr2, void *dstString)
{
	int		i, offset;
	Str255	final;

	CopyString(srcStr1, &final);
	offset = final[0];
	for (i = (*((Str255 *)(srcStr2)))[0]; i > 0; --i)
		if (offset + i < 256)
			final[offset + i] = (*((Str255 *)(srcStr2)))[i];
	final[0] = (*((Str255 *)(srcStr2)))[0] + offset;
	CopyString(&final, dstString);
}

/*********************************************************************************************
			CopyString
 *********************************************************************************************/
void		CopyString(const void *srcString, void *dstString)
{
	register	int		i;
	register	char	*src, *dst;
	src = (char*)srcString;
	dst = (char*)dstString;
	
	for (i = (*((Str255 *)(srcString)))[0]; i >= 0; --i)	{
		*dst = *src;
		++src;
		++dst;
	}
}

/****************************************************************/
#if 0
void		ShrinkCString(char *theString, int space_for_string)
{
	c2pstr((char *)theString);
	ShrinkString(theString, space_for_string);
	p2cstr((StringPtr)theString);
}

void		ShrinkString(void *theString, int space_for_string)
{
	unsigned	int	s_pix;
	unsigned	int	s_len;

	if (space_for_string <= 0)
	{
		space_for_string = 0;
		((StringPtr)theString)[0] = 0;
	}
	s_pix = StringWidth((StringPtr)theString);
	if (s_pix > space_for_string)
	{
		s_len = ((StringPtr)theString)[0];
		space_for_string -= CharWidth('É');
		if (space_for_string <= 0)
		{
			space_for_string = 0;
			((StringPtr)theString)[0] = 0;
		} else {
			for (s_len = ((StringPtr)theString)[0]; s_len && s_pix >= space_for_string; s_len--)
				s_pix -= CharWidth(((StringPtr)theString)[s_len]);
			
			if (s_len < 3)
				s_len = 0;
			else
				((StringPtr)theString)[++s_len] = 'É';
			
			((StringPtr)theString)[0] = s_len;
		}
	}
}

#else

void
ShrinkCString(char *str, short space_for_string)
{
	short length = strlen(str);
	
	if (length > 0) {
		TruncText(space_for_string, str, &length, truncEnd);
		
		str[length] = 0;		// TruncText doesn't actually do the truncation
								//  (but it does add the ellipsis)
	}
}

void
ShrinkString(u_char *str, short space_for_string)
{
	PtoCstr(str);
	ShrinkCString((char*)str, space_for_string);
	CtoPstr((char*)str);
}

#endif

void	UnmarkMenuGroup(MenuHandle theMenu, int firstItem, int lastItem)
{
	int	theItem;
	
	if (firstItem == 0)
		firstItem = 1;
	
	if (lastItem == 0)
		lastItem = CountMItems(theMenu);
	
	for (theItem = firstItem ; theItem <= lastItem ; ++theItem)
		SetItemMark(theMenu, theItem, (char)noMark);
}


/** RadioButtonItem

	Put a single checkmark next to one of a group.
	buttonItem is in absolute item coordinates.
	Pass zero for checkmark for default check-type.  (Right now it's a checkmark, but
	some (ie: DaveC) prefer '¥', and others a diamond).
	
	davec: well, check boxes get checked (little x's) and radio buttons get
			buttoned (little filled in o's), so why don't we differentiate with
			menus?  Instead, they both get checked and you can't tell just out
			of context what is a radio group and what is a check group
	
	DaveS: I agree completely; it's just standard vs. non-standard that worries me
	
**/
void		RadioButtonItem(MenuHandle theMenu, int firstItem, int lastItem, int buttonItem,
								char mark)
{
	UnmarkMenuGroup(theMenu, firstItem, lastItem);
	U_ASSERT(mark);
	SetItemMark(theMenu, buttonItem, (mark ? mark : checkMark));
}



short		mandelbrot_iterate(double x, double y, short max_iterations)
{
	double	z_x, z_y, x_2, y_2;
	short	count;
	
	z_x		= 0;
	z_y		= 0;
	x_2		= 0;
	y_2		= 0;
	count	= 0;
	
	do	{
		z_y = x + 2 * z_x * z_y;
		z_x = y + x_2 - y_2;
		x_2 = z_x * z_x;
		y_2 = z_y * z_y;
		++count;
	} while ((count < max_iterations) && ((x_2 + y_2) < 4));

	return(count);
}

void	Translate(TranslatePtr trans)
{
	trans->to = trans->to_min + ((trans->to_max - trans->to_min) * 
			((trans->from - trans->from_min)/(trans->from_max - trans->from_min)));
}



#ifndef		ESC_KEY
#define		ESC_KEY			0x1b
#endif

#ifndef		PERIOD_KEY
#define		PERIOD_KEY		0x2e
#endif

#ifndef		CANCEL_ITEM
#define		CANCEL_ITEM		2
#endif

#ifndef		OK_ITEM
#define		OK_ITEM			1
#endif

#ifndef		ENTER_KEY
#define		ENTER_KEY		0x3
#endif


pascal Boolean AlertFilter(DialogPtr, EventRecord *, short *);


DECLARE_SRD(S_AlertFilterRD, ModalFilter, AlertFilter);

ModalFilterUPP	G_AlertFilterUPP = ADDR_OF_SRD(S_AlertFilterRD, ModalFilter);



/** AlertFilter

	This ModalDialog() filter checks keyDown events and selects the first control
	it finds whose first letter is the same as the depressed key.
	
	It will select any controls so use it with alerts that have buttons only!
	
	code courtesy dh

**/
pascal Boolean
AlertFilter(DialogPtr dlog, EventRecord *evt, short *item_hit)
{
	Boolean 			ans = FALSE,
						doHilite = FALSE;
	short 				type,
						ch;
	Handle				hndl;
	Rect 				box;
	short				count;
	ControlHandle		controlH;
	unsigned char		title[256];
	
	if (evt->what == keyDown) {
		ch = (unsigned char) evt->message;
		if ((ch == '\r') || (ch == ENTER_KEY)) {
			*item_hit = OK_ITEM;
			GetDItem(dlog, *item_hit, &type, &hndl, &box);
			ans = TRUE;
			doHilite = TRUE;
		} else if ((ch == ESC_KEY) || ((ch == PERIOD_KEY) && (evt->modifiers & cmdKey))) {
			*item_hit = CANCEL_ITEM;
			GetDItem(dlog, *item_hit, &type, &hndl, &box);
			ans = TRUE;
			doHilite = TRUE;
		} else if (isalpha(ch)) {		// look for first letters
			controlH = ((WindowPeek) dlog)->controlList;
			for (; controlH && !ans; controlH = (*controlH)->nextControl) {
				// $$$ assumes that all controls are buttons
				GetCTitle(controlH, title);
				if (title[0] && tolower(title[1]) == tolower(ch)) {
					count = 0;
					while (hndl != (Handle) controlH)
						GetDItem(dlog, count++, &type, &hndl, &box);
	
					// the above while must succeed!
					*item_hit = count - 1;
					ans = TRUE;
					doHilite = TRUE;
				}
			}
		}
	}
	if (doHilite) {
		// to get in here, GetDItem() above must have worked
		if (type == (btnCtrl + ctrlItem)) {
			long soon = TickCount() + 7;
			HiliteControl((ControlHandle) hndl, 1);
			while (TickCount() < soon) ;			// hilite for a bit
			HiliteControl((ControlHandle) hndl, 0);	// then unhilite
		}
	}
	
	return ans;
}

	
/** TranslateKeyToCommand

	Given a Mac Event, & translation table this routine tries to convert it to a command.
	
	keys involving control and/or option must use a keycode (negative)
	
	returns zero if nothing found
	
**/
long
TranslateKeyToCommand(const register KeyCmd *kc, const register EventRecord *evt)
{	
	short		mask;
	char		c = evt->message & charCodeMask;
	short		keycode = (evt->message & keyCodeMask) >> 8;
	long		cmd = 0;
	
	if (isalpha(c))
		c = toupper(c);
	
	for (; kc->cmd && cmd == 0; kc++) {
		mask = Mod_CSOc & (~kc->mask);			// kc->mask specifies which bits are optional
												// mask specifies which bits must match
		if ((evt->modifiers & mask) == (kc->modifiers & mask)) {
			if (kc->chr <= 0) {
				if (-kc->chr == keycode) {
					cmd = kc->cmd;
				}
			} else if (kc->chr == c) {
				cmd = kc->cmd;
			}
		}
	}

	return cmd;	
}


/*****************************************/

long	DoLongLookup(long key, short items, const LongLookup *table)
{
	short	i;
	long	v;
	
	v = table->value;			// first entry is default
	
	for (i=0; i<items; i++) {
		if (table->key == key) {
			v = table->value;
			break;
		}
		
		++table;
	}
	
	return v;
}


long	DoReverseLongLookup(long value, short items, const LongLookup *table)
{
	short	i;
	long	key;
	
	key = table->key;			// first entry is default
	
	for (i=0; i<items; i++) {
		if (table->value == value) {
			key = table->key;
			break;
		}
		
		++table;
	}

	return key;
}

void	GetFontState(NIM_FontState *fontState)
{
	fontState->oldFont	= qd.thePort->txFont;
	fontState->oldSize	= qd.thePort->txSize;
	fontState->oldStyle	= qd.thePort->txFace;
	fontState->oldMode	= qd.thePort->txMode;
}

void	SetFontState(NIM_FontState *fontState)
{
	TextFont(fontState->oldFont);
	TextSize(fontState->oldSize);
	TextFace(fontState->oldStyle);
	TextMode(fontState->oldMode);
}

void	CopyMenu(MenuHandle dest_menu, MenuHandle src_menu)
{
	ClearMenu(dest_menu);

	if (dest_menu != NULL) {
		int		items, curItem;
		Style	style;
		Str255	buf;
		
		items = CountMItems(src_menu);
		
		for (curItem = 1; curItem <= items; ++curItem) {
			AppendMenu(dest_menu, P_MENU_BLANK);
			
			GetMenuItemText(src_menu, curItem, buf);
			SetMenuItemText(dest_menu, curItem, buf);
			
			GetItemStyle(src_menu, curItem, &style);
			SetItemStyle(dest_menu, curItem, style);
		}
	}
}

/** PaintDiamond

	Expects the rect to be 5 x 5.

**/
void	PaintDiamond(const M_Rect *r)
{
	U_ASSERT(r->right - r->left == 5 && r->bottom - r->top == 5);
	
	MoveTo(r->left+2, r->top);
	Line(0, 0);
	Move(-1, 1);
	Line(2, 0);
	Move(-3, 1);
	Line(4, 0);
	Move(-3, 1);
	Line(2, 0);
	Move(-1, 1);
	Line(0, 0);
}

/** Strrstr

	finds the LAST occurence of a substring within a string.

**/
char	*Strrstr(const char *str, const char *substr)
{
	const char 	*last = NULL;
	
	for (;;) {
		str = U_strstr(str, substr);
		if (str == NULL)
			break;
		
		last = str;				// remember this one
		str += strlen(substr);	// skip over it
	}
	
	return (char*)last;
}
	
	
		//	old way
		// if name currently ends in ' Comp #' then we should truncate the number,
		//	otherwise add ' Comp'
/*
		scan = Strrstr(layerName, STR(196));		// ' Comp'
		if (scan) {
			scan += strlen(STR(196));
			suffix = scan;				// hold on to this ptr in case we can truncate
			
			while (isspace(*scan) || isdigit(*scan))
				scan++;
			
			if (*scan) {
				suffix = NULL;		// we reached a non-numeral or space, can't truncate
			} else {
				*suffix = 0;
			}
		} else {
			suffix = NULL;
		}
		
		if (suffix == NULL)
			sprintf(new_name, STR(516), layerName);
		else
			strcpy(new_name, layerName);
*/
	
		//	new way Tuesday, January 3, 1995
		//	always remove the number
		//	if it then does NOT end in ' Comp', then add it



Rect	GetGlobalWindowPos(GrafPtr thePort)
{
	Rect	theRect;
	
	SetPort(thePort);
	SetOrigin(0, 0);
	theRect = thePort->portRect;
	LocalRectToGlobal(&theRect);

	return theRect;
}


void		HiliteGrayFrameRect(Rect *theRect)
{
	PenState		savePen;

	GetPenState(&savePen);
	
	PenMode(patXor);
	PenPat(&qd.gray);
	LMSetHiliteMode(LMGetHiliteMode() & (~(1 << hiliteBit)));	// BCLR	#hiliteBit, HiliteMode;
	FrameRect(theRect);

	SetPenState(&savePen);
}

void		InvertGrayFrameRect(Rect *theRect)
{
	PenState		savePen;

	GetPenState(&savePen);
	
	PenMode(patXor);
	PenPat(&qd.gray);
	FrameRect(theRect);

	SetPenState(&savePen);
}

void		AppendLiteralPMenu(MenuHandle theMenu, u_char *pstring)
{
	AppendMenu(theMenu, P_MENU_BLANK);
	SetMenuItemText(theMenu, CountMItems(theMenu), pstring);
}

void		AppendLiteralCMenu(MenuHandle theMenu, char *cstring)
{
	u_char		pstring[256];
	
	AppendLiteralPMenu(theMenu, CopyC2P(cstring, pstring));
}

short		TruncateShort(long *value)
{
	if (*value > 32000)
		*value = 32000;
	else if (*value < -32000)
		*value = -32000;
	
	return((short)(*value));
}

char		*RemoveBrackets(char *string)
{
	size_t	length = strlen(string);
		
	if (string[0] == '[' && string[length - 1] == ']') {
		length -= 2;
		memmove(string, &(string[1]), length);
		string[length] = 0;
	}
	
	return(string);
}	

float		DB2Percent(float db)
{
	return pow(10, db / 20.0) * 100;
}

#define		kMinPercentage		(1.5849E-05 * 100)

char		*FormatPercentage(float p, char *buf)
{
	int		prec;
	
	if (p <= kMinPercentage) {
		p = 0;
	}
	
	if (p == 0) {
		prec = 0;
	} else if (p < 0.1) {
		prec = 3;
	} else if (p < 1) {
		prec = 2;
	} else if (p < 10) {
		prec = 1;
	} else {
		prec = 0;
	}
	
	sprintf(buf, STR(470), prec, p);
	
	if (prec == 3)
		strcpy(buf, &buf[1]);			// remove leading zero
	
	return buf;
}

