/*	MacFloater.cpp
 *
 *		This is the implementation of the floating window stuff. This
 *	requires the routine 'XGRDoActivate' to be a valid subroutine which can
 *	be called to activate the window's contents, and this also requires
 *	that the 'activeEvt' of the event loop gets ignored...
 */

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

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

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

#include <XConfig.h>

#if OPT_MACOS

#include "MacFloater.h"						/* Float library support	*/
#include <LowMem.h>							/* LMGetMBarHeight()		*/

/************************************************************************/
/*																		*/
/*	Search Routines														*/
/*																		*/
/************************************************************************/

/*	MFrontWindow
 *
 *		Find the frontmost non-floater. This searches down the linked
 *	list of windows that is maintained internaly by the Macintosh OS
 *	(the linked list kept through the WindowRecord field 'nextWindow')
 *	until I find a window which has the proper type.
 */

WindowPtr MFrontWindow(void)
{
	WindowPeek w;
	
	w = (WindowPeek)FrontWindow();
	for (;;) {
		if (w == NULL) break;
		if ((w->windowKind != KFloatingWindow) && 
				(w->windowKind != KDialogWindow) &&
				(w->windowKind != KTooltipWindow)) {
			if (w->visible) break;
		}
		w = w->nextWindow;					/* Nope, this ain't it. Go on. */
	}
	return (WindowPtr)w;
}

/*	MLastFloater
 *
 *		Get the last floater window position. This is not similar to
 *	anything in the Macintosh OS, but is very useful for figuring out
 *	where new non-floater windows go.
 */

WindowPtr MLastFloater(void)
{
	WindowPeek w;
	WindowPeek prev;
	
	prev = NULL;
	w = (WindowPeek)FrontWindow();
	while (w && ((w->windowKind == KFloatingWindow) || (w->windowKind == KDialogWindow))) {
		prev = w;
		w = w->nextWindow;
	}
	return (WindowPtr)prev;
}

/************************************************************************/
/*																		*/
/*	Selection Routines													*/
/*																		*/
/************************************************************************/

/*	MSelectWindow
 *
 *		Like select window, except for floating windows. If this is called
 *	with a floating window, it brings the floating window to the front of
 *	all of the floating windows. If called on a non-floater, this brings
 *	the non-floating window frontmost.
 */

void MSelectWindow(WindowPtr w)
{
	short isFloater;
	WindowPtr current;
	WindowPtr last;
	
	/*
	 *  Based on what kind of window this is (floating, nonfloating)
	 *	initialize some interesting variables for later
	 */
	
	if (((WindowPeek)w)->windowKind == KFloatingWindow) {
		current = FrontWindow();
		isFloater = 1;
	} else {
		isFloater = 0;
		current = MFrontWindow();
		last = MLastFloater();
	}
	
	/*
	 *  Now if we are not the current window, do the selection
	 */
	
	if (current != w) {
		if (isFloater) {
			/*
			 *  Bringing a floater forward can be done by bringing the
			 *	thing all the way to the front.
			 */
			
			BringToFront(w);
		} else {
			if (last == NULL) {
				/*
				 *  This is a standard window, but there are no floaters
				 */
				
				HiliteWindow(current,0);	/* Unhilite this window		*/
				XGRDoActivate(current,0);		/* And deactivate it		*/
				BringToFront(w);			/* Bring this one frontmost	*/
				HiliteWindow(w,1);
				XGRDoActivate(w,1);			/* ###6 Need System 6 tests.*/
			} else {
				HiliteWindow(current,0);	/* Unhilite this window		*/
				XGRDoActivate(current,0);		/* And deactivate it		*/
				SendBehind(w,last);
				
#ifdef PREVERSION7
				/*
				 *  The following two routines are only needed if this
				 *	were to run under an operating system earlier than
				 *	System 7. But as we are not going to do that, well,
				 *	these routines simply are not needed; the routine
				 *	'SendBehind' does the right thing.
				 */
				
				PaintOne((WindowPeek)w,((WindowPeek)w)->strucRgn);
				CalcVis((WindowPeek)w);
#endif

				HiliteWindow(w,1);
				XGRDoActivate(w,1);			/* ###6 Need System 6 tests.*/
			}								/* PaintOne & CalcVis		*/
		}
	}
}

/*	MDragWindow
 *
 *		Do the drag window thing
 */

void MDragWindow(WindowPtr windowToDrag, Point startPoint, const Rect *draggingBounds)
{
	Rect		dragRect;
	KeyMap		keyMap;
	GrafPtr		savePort;
	GrafPtr		windowManagerPort;
	RgnHandle	dragRegion;
	RgnHandle	windowContentRegion;
	long		dragResult;
	short		topLimit;
	short		newHorizontalWindowPosition;
	short		newVerticalWindowPosition;
	short		horizontalOffset;
	short		verticalOffset;
	Boolean		commandKeyDown = false;
	
	if (WaitMouseUp()) {
		/*
		 * Adjust the top of the dragging rectangle so that itÕs below 
		 * the menu bar
		 */
	
		topLimit = LMGetMBarHeight() + 4;
		dragRect = *draggingBounds;
		if (dragRect.top < topLimit) dragRect.top = topLimit;
	
		/* Set up the Window Manager port. */
	
		GetPort(&savePort);
		GetWMgrPort(&windowManagerPort);
		SetPort(windowManagerPort);
		SetClip(GetGrayRgn());
		
		/*
		 * Check to see if the command key is down.  If it is, donÕt bring the window to the
		 * front after the move.  Trying to do Pascal stuff in C is so much fun.  GetKeys()
		 * is a total pain to try to use properly from C, so IÕm going to hard code where the
		 * command key is in the KeyMap array.
		 */
		 
		GetKeys(keyMap);
		if (keyMap[1] & 0x8000) commandKeyDown = true;
		if ((commandKeyDown == true) || 
				(((WindowPeek)windowToDrag)->windowKind != KFloatingWindow)) {
			
			if (commandKeyDown == false)
	
		/*
		 * If there are floating windows, clip the dragging outline 
		 * to draw behind the floaters.
		 */
		 
				ClipAbove(MFrontWindow());
			else
			
		/*
		 * If the command key was down, clip the outline to draw behind any windows above
		 * the window being dragged.
		 */
		 
				ClipAbove(windowToDrag);
		}
			
		/* Create a region to drag */
	
		dragRegion = NewRgn();
		CopyRgn(((WindowPeek)windowToDrag)->strucRgn, dragRegion);
		
		/* Drag the window around */
	
		dragResult = DragGrayRgn(dragRegion, startPoint, &dragRect, &dragRect, noConstraint, nil);
	
		/* Restore the port for coordinate conversion. */
	
		SetPort(savePort);

		if (dragResult != 0) {
			horizontalOffset = dragResult & 0xFFFF;
			verticalOffset = dragResult >> 16;
	
		/* Only move it if it stayed inside the dragging box. */
	
			if (verticalOffset != -32768) {
				windowContentRegion = ((WindowPeek)windowToDrag)->contRgn;
				newHorizontalWindowPosition = (**windowContentRegion).rgnBBox.left + horizontalOffset;
				newVerticalWindowPosition = (**windowContentRegion).rgnBBox.top + verticalOffset;
				
				MoveWindow((WindowPtr) windowToDrag, newHorizontalWindowPosition, newVerticalWindowPosition, false);
				
			}
		}
	
		/* Bring the window forward if the command key wasnÕt down */
	
		if (commandKeyDown == false)
			MSelectWindow(windowToDrag);
	
		/* Get rid of the dragging region */
	
		DisposeRgn(dragRegion);
	}
}

/*	MGetWindowList
 *
 *		Get the window list top
 */

static WindowPtr MGetWindowList(void)
{
	return (*(WindowPtr *)0x09D6);
}

/*	MValidateWindowList
 *
 *		Validation of the window list's order
 */

static void MValidateWindowList(void)
{
	WindowPtr	currentWindow = MGetWindowList();
	WindowPtr	lastFloatingWindow = MLastFloater();
	WindowPtr	firstFloatingWindow = nil;
	WindowPtr	documentWindowsToMove = nil;
	WindowPtr	lastDocumentWindowAdded = nil;
	WindowPtr	previousWindow = nil;
	
	if (currentWindow) {
	
	/*
	 * First, gather up all the document windows in front of floating windows. We iterate
	 * through the window list until a floating window is encountered.
	 */
		do {
			if (((WindowPeek)currentWindow)->windowKind == KFloatingWindow) {
				firstFloatingWindow = currentWindow;
				break;
			}
			else {
				((WindowPeek)previousWindow)->nextWindow = ((WindowPeek)currentWindow)->nextWindow;

				if (documentWindowsToMove == nil)
					documentWindowsToMove = currentWindow;
				else
					((WindowPeek)lastDocumentWindowAdded)->nextWindow = (WindowPeek)currentWindow;
				lastDocumentWindowAdded = currentWindow;
				
				previousWindow = currentWindow;
				currentWindow = (WindowPtr)(((WindowPeek)currentWindow)->nextWindow);
			}
		} while (currentWindow);
		
	/* Now put them back in their place. */
	
		if (documentWindowsToMove && firstFloatingWindow) {
			((WindowPeek)lastDocumentWindowAdded)->nextWindow = ((WindowPeek)lastFloatingWindow)->nextWindow;
			((WindowPeek)lastFloatingWindow)->nextWindow = (WindowPeek)documentWindowsToMove;
			
	/*
	 * If the first window in the window list was a document window, and there are
	 * floating windows, then make the floating window the first window in the window
	 * list.
	 */
			if (documentWindowsToMove == MGetWindowList())
				(*(WindowPtr *)0x09D6) = firstFloatingWindow;
		}
	}
}

/*	MShowWindow
 *
 *		How to show this window and make it visible
 */

void MShowWindow(WindowPtr windowToShow)
{
	WindowPtr			windowBehind;
	WindowPtr			frontNonFloatingWindow;
	short				windowClass;
	Boolean				windowIsInFront = false;
	
	if (((WindowPeek)windowToShow)->visible != false) return;
		
	windowClass = ((WindowPeek)windowToShow)->windowKind;
	
	/* Handle special case of movable modal dialog. */
	
	if ((windowClass == KDialogWindow) || (windowClass == dialogKind)) {
		/* Unhilite the frontmost document window */
		windowBehind = MFrontWindow();
		if (windowBehind != NULL) {
			HiliteWindow(windowBehind,0);
			XGRDoActivate(windowBehind,0);
		}
		
		((WindowPeek)windowToShow)->hilited = true;	/* Hilite this		*/
		ShowHide((WindowPtr)windowToShow,true);		/* Show this		*/
		XGRDoActivate(windowToShow,1);					/* Activate this	*/
		return;
	}
	
	/*
	 * If the window behind the window to show is currently the frontmost document window,
	 * unhighlight it, and highlight the new front window.
	 */
	 
	if (windowClass != KFloatingWindow) {
		windowBehind = (WindowPtr)(((WindowPeek)windowToShow)->nextWindow);
		if (windowBehind == MFrontWindow()) {
			if (windowBehind != NULL) {
				HiliteWindow(windowBehind,0);
				XGRDoActivate(windowBehind,0);
			}

			/* Set the highlight state so the window appears highlighted from the start. */

			((WindowPeek)windowToShow)->hilited = true;
			windowIsInFront = true;
		}
	} else {
	
	/*
	 * A floating window is about to be shown. Make sure the windows in the 
	 * window list are all in the right place.
	 */
	
		MValidateWindowList();
			
		frontNonFloatingWindow = MFrontWindow();
		((WindowPeek)windowToShow)->hilited = true;
		windowIsInFront = true;
	}
	
	/* Show the window */
	
	ShowHide((WindowPtr) windowToShow, true);
	
	/* If this is the new frontmost document window or a floating window, send it an activate event */
	
	if (windowIsInFront) {
		XGRDoActivate(windowToShow,1);
	}
}

/*	MHideWindow
 *
 *		How to hide this window
 */

void MHideWindow(WindowPtr windowToHide)
{
	WindowPtr			frontFloater;
	WindowPtr			frontNonFloater;
	WindowPtr			lastFloater;
	WindowPtr			windowBehind;
	short				windowClass;
	
	/* DonÕt do anything if the window is already invisible. */
	
	if (((WindowPeek)windowToHide)->visible == false) return;

	/* Handle special case of movable modal dialog */
	
	windowClass = ((WindowPeek)windowToHide)->windowKind;
	if ((windowClass == KDialogWindow) || (windowClass == dialogKind)) {
		/* Hide the window */
		ShowHide(windowToHide,false);
		
		/* Hilite the frontmost document window */
		frontNonFloater = MFrontWindow();
		if (frontNonFloater != NULL) {
			HiliteWindow(frontNonFloater,1);
			XGRDoActivate(frontNonFloater,1);
		}
		return;
	}
	
	/* Get the first visible floating window, if any. */
	
	frontFloater = FrontWindow();
	if (((WindowPeek)frontFloater)->windowKind != KFloatingWindow) frontFloater = nil;
		
	/* Get the first visible document window, if any. */
	
	frontNonFloater = MFrontWindow();
	
	/* Hide the window. */
	
	ShowHide((WindowPtr) windowToHide, false);
	
	/*
	 * If the frontmost floating window is being hidden, move it behind the floating window
	 * behind it, if there is one.
	 */
	if (windowToHide == frontFloater) {
		windowBehind = (WindowPtr)(((WindowPeek)windowToHide)->nextWindow);
		
	/* Only do the rearrangement if thereÕs another floating window. */
	
		if ((windowBehind != nil) && (((WindowPeek)windowBehind)->windowKind == KFloatingWindow)) {
			((WindowPeek)windowToHide)->nextWindow = ((WindowPeek)windowBehind)->nextWindow;
			((WindowPeek)windowBehind)->nextWindow = (WindowPeek)windowToHide;
			(*(WindowPtr *)0x09D6) = windowBehind;
		}
	}
	else {
	
	/* If the frontmost document window is behind hidden, send it behind the window
	 * behind it.
	 */
		if (windowToHide == frontNonFloater) {
			windowBehind = (WindowPtr)(((WindowPeek)windowToHide)->nextWindow);
			
			if (windowBehind != nil) {
				((WindowPeek)windowToHide)->nextWindow = ((WindowPeek)windowBehind)->nextWindow;
				((WindowPeek)windowBehind)->nextWindow = (WindowPeek)windowToHide;
				
	/* Set the next link of the last floating window to point to the previously second
	 * to front document window. If there was no floating window, change the beginning
	 * of the window list.
	 */
				lastFloater = MLastFloater();
				if (lastFloater != nil)
					((WindowPeek)lastFloater)->nextWindow = (WindowPeek)windowBehind;
				else
					(*(WindowPtr *)0x09D6) = windowBehind;
				
	/* The window behind it is now the front document window.  Highlight it and send it
	 * and activate event.
	 */			
				HiliteWindow(windowBehind,1);
				XGRDoActivate(windowBehind,1);
			}
		}
	}
}

/************************************************************************/
/*																		*/
/*	Window Creation														*/
/*																		*/
/************************************************************************/

/*	MNewWindow
 *
 *		Do the same thing as the NewWindow call, but open the window
 *	right behind the floater
 */

WindowPtr MNewWindow(Ptr st, Rect *bounds, unsigned char *title, Boolean vis, short wDef, WindowPtr behind, Boolean goAway, long refCon)
{
	WindowPtr w;
	
	/* Reset the behind pointer depending on the order of this thing */

	if ((wDef >= 1984) && (wDef <= 1999)) {
		/*
		 *  If the 'wDef' parameter (the window definition) is between
		 *	1984 and 1999, this is a floating window definition function.
		 *	(This coorisponds to a 'WDEF' resource of 124--the WDEF that
		 *	Apple's floating window lives at.)
		 */
		
		if (behind == NULL) {
			behind = MLastFloater();
			if (behind == NULL) behind = (WindowPtr)-1L;
		}
	} else if ((wDef == 1) || (wDef == 5)) {
		behind = (WindowPtr)-1L;			/* always in front. */
	} else {
		if (behind == (WindowPtr)-1L) {
			behind = MLastFloater();
			if (behind == NULL) behind = (WindowPtr)-1L;
		}
	}
	
	/* Get the current front window for deactivation, and create it */
	
	w = NewWindow(st,bounds,title,false,wDef,behind,goAway,refCon);
	if (w == NULL) return NULL;
	
	/* Do the activation/deactivation if this window is visible */
	if ((wDef >= 1984) && (wDef <= 1999)) {
		((WindowPeek)w)->windowKind = KFloatingWindow;
	} else if ((wDef == 1) || (wDef == 5)) {
		((WindowPeek)w)->windowKind = KDialogWindow;
	} else {
		((WindowPeek)w)->windowKind = KNormalWindow;
	}
	if (vis) MShowWindow(w);
	return w;
}

/*	MNewCWindow
 *
 *		Do the same thing as the NewWindow call, but open the window
 *	right behind the floater
 */

WindowPtr MNewCWindow(Ptr st, Rect *bounds, unsigned char *title, Boolean vis, short wDef, WindowPtr behind, Boolean goAway, long refCon)
{
	WindowPtr w;
	
	/* Reset the behind pointer depending on the order of this thing */

	if ((wDef >= 1984) && (wDef <= 1999)) {
		/*
		 *  If the 'wDef' parameter (the window definition) is between
		 *	1984 and 1999, this is a floating window definition function.
		 *	(This coorisponds to a 'WDEF' resource of 124--the WDEF that
		 *	Apple's floating window lives at.)
		 */
		
		if (behind == NULL) {
			behind = MLastFloater();
			if (behind == NULL) behind = (WindowPtr)-1L;
		}
	} else if ((wDef == 1) || (wDef == 5)) {
		behind = (WindowPtr)-1L;			/* always in front. */
	} else {
		if (behind == (WindowPtr)-1L) {
			behind = MLastFloater();
			if (behind == NULL) behind = (WindowPtr)-1L;
		}
	}
	
	/* Get the current front window for deactivation, and create it */
	
	w = NewCWindow(st,bounds,title,false,wDef,behind,goAway,refCon);
	if (w == NULL) return NULL;
	
	/* Do the activation/deactivation if this window is visible */
	if ((wDef >= 1984) && (wDef <= 1999)) {
		((WindowPeek)w)->windowKind = KFloatingWindow;
	} else if ((wDef == 1) || (wDef == 5)) {
		((WindowPeek)w)->windowKind = KDialogWindow;
	} else {
		((WindowPeek)w)->windowKind = KNormalWindow;
	}
	if (vis) MShowWindow(w);
	return w;
}

/*	MCloseWindow
 *
 *		This handles closing the window.
 */

void MCloseWindow(WindowPtr w)
{
	if (((WindowPeek)w)->visible) MHideWindow(w);
	CloseWindow(w);
}

/************************************************************************/
/*																		*/
/*	Modeless Dialog Support												*/
/*																		*/
/************************************************************************/

/*	MIsDialogEvent
 *
 *		Is this an event for this dialog box?
 */

Boolean MIsDialogEvent(EventRecord *event)
{
	WindowPtr w;
	short where;
	
	/* Kludge for help balloons */
	IsDialogEvent(event);

	switch (event->what) {
		case mouseDown:
		case mouseUp:
			where = FindWindow(event->where,&w);
			if (where != inContent) w = NULL;
			else if (w != MFrontWindow()) w = NULL;
			break;
		case updateEvt:
		case activateEvt:
			w = (WindowPtr)(event->message);
			break;
		case keyDown:
		case autoKey:
		default:
			w = MFrontWindow();
			break;
	}
	if (w == NULL) return 0;
	if (((WindowPeek)w)->windowKind == dialogKind) return 1;
	return 0;
}

/*	MDialogSelect
 *
 *		Do the dialog select routines. This does all of the dialog
 *	processing stuff for this dialog window if it is one.
 */

Boolean MDialogSelect(EventRecord *event, DialogPtr *dlog, short *item)
{
	WindowPtr w;
	DialogPeek peek;
	short where;
	Handle h;
	Rect r;
	TEHandle text;
	char c;
	short index;
	short length;
	Point pt;
	unsigned long tmp;
	
	/*
	 *  Determine the dialog window that is being used here.
	 */
	
	switch (event->what) {
		case mouseDown:
		case mouseUp:
			where = FindWindow(event->where,&w);
			if (where != inContent) w = NULL;
			break;
		case updateEvt:
		case activateEvt:
			w = (WindowPtr)(event->message);
			break;
		default:
			w = MFrontWindow();
			break;
	}
	if (w == NULL) return 1;			/* Not my event... */
	if (((WindowPeek)w)->windowKind != dialogKind) return 1;
	
	/*
	 *  Now, actually do this event as appropriate
	 */
	
	*dlog = w;							/* The dialog I was handling.	  */
	*item = 0;							/* Default item (0)				  */
	peek = (DialogPeek)w;				/* So I can get at internal stuff */
	switch (event->what) {
	
		/*
		 *  Activate/Deactivate
		 *
		 *		This activates or deactivates the text edit field
		 *	if it is present in the dialog box.
		 */
		
		case activateEvt:
			if (peek->editField != -1) {
				SetPort(w);
				GetDialogItem(w,peek->editField + 1,&where,&h,&r);
				text = peek->textH;
				if (text && ((where & 0x7F) == 16)) {
					if (event->modifiers & activeFlag) {
						TEActivate(text);
					} else {
						TEDeactivate(text);
					}
				}
			}
			return 0;					/* Further processing not needed */
		
		/*
		 *  Update
		 *
		 *		This simply draws the contents of this thing
		 */
		
		case updateEvt:
			SetPort(w);
			BeginUpdate(w);
			UpdateDialog(w,w->visRgn);
			EndUpdate(w);
			return 0;					/* Further processing not needed */
		
		/*
		 *  Null event processing
		 *
		 *		Blink the cursor, adjust the mouse, show the balloon
		 */
		
		default:
		case nullEvent:
			SetPort(w);
			
			/*
			 *  Adjust cursor
			 */
			
			pt = event->where;
			GlobalToLocal(&pt);
			index = FindDialogItem(w,pt)+1;
			if (index != 0) {						/* Not on anything */
				GetDialogItem(w,index,&where,&h,&r);
				where &= 0x7F;
				if (where == 16) {					/* Change to ibeam */
					CursHandle c;
					
					c = GetCursor(iBeamCursor);
					HLock((Handle)c);
					SetCursor(*c);
					HUnlock((Handle)c);
					ReleaseResource((Handle)c);
				} else {
					SetCursor(&qd.arrow);
				}
			} else SetCursor(&qd.arrow);
			
			/*
			 *  Blink cursor
			 */
			
			if (peek->editField != -1) {
				GetDialogItem(w,1+peek->editField,&where,&h,&r);
				text = peek->textH;
				if (text && ((where & 0x7F) == 16)) {
					TEIdle(peek->textH);		/* Selected editable item */
				}
			}
			return 0;					/* Further processing not needed */
		
		/*
		 *	Key events
		 *
		 *		This handles keyboard events by processing the enter/
		 *	escape keys as buttons 1 and 2; otherwise, pass to the
		 *	current edit box, if any.
		 */
		
		case keyDown:
		case autoKey:
			SetPort(w);
			c = (unsigned char)(event->message & charCodeMask);
			
			/*
			 *  Enter, escape processing
			 */
			
			if ((c == 0x03) || (c == 0x0D) || (c == 0x1B)) {				
				if ((c == 0x03) || (c == 0x0D)) {
					*item = 1;
				} else {
					*item = 2;
				}
				
				GetDialogItem(w,*item,&where,&h,&r);
				HiliteControl((ControlHandle)h,kControlButtonPart);
				Delay(5,&tmp);
				HiliteControl((ControlHandle)h,0);
				return 1;				/* Further processing needed */
			}
			
			/*
			 *  Tab processing (default--bounce from edit table to edit table)
			 */
			
			if (c == 0x09) {			/* Horizontal tab key */
				if (peek->editField == -1) return 0; /* Can't deal with it. */
				if (peek->textH == NULL) return 0;	/* Can't deal with it. */
				length = *((short *)(*(peek->items))) + 1;
				index = peek->editField+2;			/* Skip this one */
				
				/*
				 *  Find the next edit item and select it
				 */
				
				while (index <= length) {
					GetDialogItem(w,index,&where,&h,&r);
					if ((0x7F & where) == 16) {		/* This is an edit item */
						SelectDialogItemText(w,index,0,32767);	/* Move edit, select me */
						*item = index;
						return ((where & 0x80) == 0) ? 1 : 0;
					}
					index++;
				}
				index = 1;							/* Wrap to beginning */
				while (index <= length) {
					GetDialogItem(w,index,&where,&h,&r);
					if ((0x7F & where) == 16) {		/* This is an edit item */
						SelectDialogItemText(w,index,0,32767);	/* Move edit, select me */
						*item = index;
						return ((where & 0x80) == 0) ? 1 : 0;
					}
					index++;
				}
				*item = 0;
				return 0;							/* Huh? Dunno... */
			}
			
			/*
			 *  Normal keyboard processing
			 */
			
			text = peek->textH;
			if (text == NULL) return 0;				/* No edit item to do */
			if (peek->editField == -1) return 0;	/* No edit item to do */
			
			*item = peek->editField+1;				/* Item ID we're doing */
			
			GetDialogItem(w,*item,&where,&h,&r);	/* What is this? */
			
			if (text && ((where & 0x7F) == 16)) {
				TEKey(c,text);						/* Do character process */
			
				return ((where & 0x80) == 0) ? 1 : 0;	/* for enable flag */
			} else return 0;						/* Huh? */
		
		/*
		 *  Mouse down:
		 *
		 *		track the controls or the edit fields as appropriate
		 */
		
		case mouseDown:
			SetPort(w);
			pt = event->where;
			GlobalToLocal(&pt);
			
			/*
			 *	Determine where the mouse went down
			 */
			
			index = FindDialogItem(w,pt)+1;
			if (index == 0) return 0;				/* Not on anything */
			GetDialogItem(w,index,&where,&h,&r);
			*item = index;
			
			/*
			 *  Handle inactive objects
			 */
			
			if (where & 0x80) {
				if (where == 0x90) {				/* Edit item here. */
				
					/*
					 *  Handle edit selection even if inactive
					 */
					
					if (peek->editField + 1 != index) {
						SelectDialogItemText(w,index,0,32767);
					}
					TEClick(pt,(event->modifiers & shiftKey) ? 1 : 0,peek->textH);
				}
				return 0;
			}
			
			/*
			 *  Process according to where I'm at
			 */
			
			switch (where) {
				default:
					return 1;						/* Just return the ID. */
				case 4:
				case 5:
				case 6:
				case 7:
					/*
					 *  Click on a standard control object
					 */
					
					if (TrackControl((ControlHandle)h,pt,(ControlActionUPP)-1)) return 1;
					return 0;						/* don't do it. */
				case 16:							/* Handle edit box */
					if (peek->editField + 1 != index) {
						SelectDialogItemText(w,index,0,32767);
					}
					TEClick(pt,(event->modifiers & shiftKey) ? 1 : 0,peek->textH);
					return 1;						/* Return where I am now.  */
			}
	}
	return 0;			/* Not one of these; can't deal with it at all... */
}

#endif // OPT_MACOS
