/* Copyright (c) 2017, Computer History Museum 
All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to 
the limitations in the disclaimer below) 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. 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
   disclaimer in the documentation and/or other materials provided with the distribution. 
 * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products 
   derived from this software without specific prior written permission. 
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE 
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE. */

#include "main.h"

#define FILE_NUM 22
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */

#pragma segment Main

#define TICKS2MINS 3600

	void HouseKeeping(long activeTicks);
	void DoKeyDown(WindowPtr topWinWP,EventRecord *event);
	void DoMouseDown(WindowPtr topWinWP,EventRecord *event);
	Boolean DoUpdate(WindowPtr topWin,EventRecord *event);
	Boolean DoActivate(WindowPtr topWin,EventRecord *event);
	Boolean DoApp1(WindowPtr topWinWP,EventRecord *event);
	void DoContentClick(WindowPtr topWinWP,WindowPtr winWP,EventRecord *event);
	Boolean DoDisk(long message);
	long CloseUnused(void);
	void SetYesterday(void);
long NickDrain(void);
#ifdef DRAG_GETOSEVT
void UpdateAWindow(void);
#endif
#if defined(EXPIRE) || defined(DEMO)
	void ExpireWarning(void);
#endif
void AutoSave(void);

void ConfigureIfNeeded(void);
void UnloadSegments(void);
Boolean MyIsShowContextualMenuClick(EventRecord *event);
void ActiveUserIdleTasks(void);
static void SetupEventHandlers(void);
static pascal OSStatus WheelHandlerProc (EventHandlerCallRef nextHandler,EventRef event,void *data);
static pascal OSStatus ServicesHandlerProc (EventHandlerCallRef nextHandler,EventRef event,void *data);
static void AppendOSTypeToCFArray(CFMutableArrayRef arrayRef,OSType type);
pascal void __start(void);
pascal void SimpleStart(void);
pascal void SimpleStart(void)
{
#ifdef DEBUG
	SLInit();
#endif
	__start();
}

void main()
{
	EventRecord theEvent;
	Boolean active=True;
	long houseTicks=0;
	Boolean lock = False;
	long activeTicks;
	Boolean keyHit;
	//uLong startTicks = 0;

	ActiveTicks = activeTicks = TickCount();
	SyncRW = True;	// protect from RAM Doubler
	
	/*
	 * perform set-up, including initializing mac managers
	 */
#if __profile__
	ProfilerInit(collectDetailed,bestTimeBase,20000,2000);
	ProfilerSetStatus(True);
#endif

	Initialize();

	SetYesterday();
	SeeIfWeShouldPreloadNav ();
		
	CHECK_EXPIRE;

	CHECK_DEMO;		
		
#if defined(EXPIRE) || defined(DEMO)
	ExpireWarning();
#endif

	SetCursorByLocation();

	ConfigureIfNeeded();
	CheckForDefaultMailClient ();
	
	SetupEventHandlers();

	/*
	 * run the event loop
	 */
#if __profile__
	//ProfilerSetStatus(True);
#endif

	while (!EjectBuckaroo && !Done)
	{
		//if (!startTicks) startTicks = TickCount();
		//if (TickCount()-startTicks > 3600) break;
		//ProfilerSetStatus(ActiveSearchCount > 0);
#ifdef DEBUG
	if (BUG3 && !FrontWindow_())
	{
		Str63 s;
		long grow, newFree;
		static long oldFree=1L<<30;
		
		(void) MaxMem(&grow);
		DisposeHandle(NewHandle(400 K));
		newFree = FreeMem();
		if (oldFree>newFree)
		{
			DebugStr(ComposeString(s,"\p#%d;ht;g",oldFree-newFree));
			AlertStr(OK_ALRT,Stop,ComposeString(s,
			 "\pLeak? was %d, now %d (%d)",oldFree,newFree,oldFree-newFree));
		}
		oldFree = newFree;
	}
#endif
		CommandPeriod = NoInitialCheck = False;
		
		/*
		 * handle any events we might come across 
		 */
		if (!Typing) PurgeSpace(&LastTotalSpace,&LastContigSpace);
		//ProfilerSetStatus(False);
		active = GrabEvent(&theEvent) ? HandleEvent(&theEvent) : False;

		CurPers = PersList;	// make darn sure.

		// Did the user tell us to quit?
		if (PleaseQuit)
		{
			PleaseQuit = False;
			DoQuit(false);
		}
		if (Done) break;
		
		keyHit = (theEvent.what==autoKey || theEvent.what==keyDown) && !(theEvent.modifiers&cmdKey);
				
		if (DragTOCFrom) FinishBoxDrag();
		Dragging = 0;
		DragSequence++;
#if __profile__
		//ProfilerSetStatus(Typing);
#endif

		if (ProgressIsOpen()) CloseProgress();
		if (keyHit || theEvent.what==mouseDown || theEvent.what==keyDown || theEvent.what==app1Evt) ActiveTicks = TickCount();
		if (Typing) active = True;
		if (active) activeTicks = TickCount();
		if (!Typing && IsMyWindow(FrontWindow_()) && GetWindowKind(FrontWindow_())==TASKS_WIN)
			ActiveTicks = activeTicks = 0;	// if progress window up, user is idle
		
		TendNotificationManager(active);
		
		Typing = Typing || !HaveTheDiseaseCalledOSX() && OSEventAvail(keyDownMask,&theEvent) && !(theEvent.modifiers&cmdKey);
		
		if (!Typing)
		{			
			WindowPtr winWP = FrontWindow_();

			if (IsMyWindow(winWP) && !InBG)
			{
				if (GetWindowKind(winWP)==CBOX_WIN || GetWindowKind(winWP)==MBOX_WIN)
					RedoTOC((TOCHandle)GetWindowPrivateData(winWP));
				else
				{
					MyWindowPtr	win = GetWindowMyWindowPtr (winWP);
					if (!win || (win && !win->isActive)) ActivateMyWindow(winWP,true);
//					UpdateMyWindow(winWP);
				}
			}
			if (SFWTC) SetCursorByLocation();
			
			
			//	See if any plug-ins need some idle time
			ETLIdle(
				((TickCount()-ActiveTicks<120)||(ModalWindow && !AreAllModalsPlugwindows()) ? EMSFIDLE_QUICK : (InBG ? 0:EMSFIDLE_UI_ALLOWED)) |
				(Offline ? EMSFIDLE_OFFLINE : 0) | 
				(GetNumBackgroundThreads() ? EMSFIDLE_TRANSFERRING : 0));

			if (!active && !TypingRecently)
			{
				URLScan();
				QuoteScan();
#ifdef WINTERTREE
				if (HasFeature (featureSpellChecking) && !PrefIsSet(PREF_NO_WINTERTREE)) SpellScan();
#endif
				if (ActiveSearchCount) SearchAllIdle();
			}
					
			if (TickCount()-houseTicks > 30)
			{
				HouseKeeping(TickCount()-activeTicks);
				houseTicks = TickCount();
			}
			
	//		if (QTMoviesInited)
	//		This can cause movies to draw in the wrong place so don't use it
	//			MoviesTask(nil,50);
	
			GraphicDownloadIdle();	//	See if any graphic downloads need any processing
			PlaySoundIdle();	//	Give time to any sounds that might be playing
#ifdef TWO
			/*
			 * tickle Frontier
			 */
			if (active) CheckSharedMenus(HIER_MENU_LIMIT);
#endif

#ifndef SLOW_CLOSE
			/*
	     * destroy defunct streams
		   */
			TcpFastFlush(False);
#endif
		
			/*
			 * write dirty toc's
			 */
			if (!active) RedoTOCs();
			if (!active && !PrefIsSet(PREF_CORVAIR)) FlushTOCs(False,True);
		
			/*
			 * cursor & help
			 */
			if (SFWTC) SetCursorByLocation();
			if (theEvent.what==nullEvent && HasHelp && !InBG && HMGetBalloons())
				DoHelp(FrontWindow_());
		
			ASSERT(CurResFile()==SettingsRefN);
			UseResFile(SettingsRefN);
			ASSERT(Count1Resources(TOC_TYPE)==0);

					
#ifdef TWO
			/*
			 * toolbar
			 */
			ToolbarBack();
#endif
			/*
			 * all windows are fair game
			 */
			NotUsingAllWindows();
			
			/*
			 * menu bar
			 */
			if (active) EnableMenus(MyFrontWindow(),False);
			NoMenus = false;
			
			/*
			 * check?
			 */
			if (!active && CheckNow)
			{
				if (AutoCheckOKWithDBRead(false))	// make sure we're really allowed to do an automatic check
					XferMail(True,PrefIsSet(PREF_SEND_CHECK),False,False,True,0);

				CheckNow = False;
			}
		}

		/*
		 * clear gworld, personality stack
		 */
		ASSERT(StackPop(nil,GWStack)==fnfErr);
		while (!StackPop(nil,GWStack));
		ASSERT(!PersStack || StackPop(nil,PersStack)==fnfErr);
		while (PersStack && !StackPop(nil,PersStack));
		CurPers = PersList;
		ASSERT(CurThreadGlobals==&ThreadGlobals);
		if (EjectBuckaroo) DoQuit(false);
	}
#ifdef	IMAP
	ZapAllIMAPConnections(true);	// abort all IMAP connections
#endif
	TcpFastFlush(True);	//abort open streams
	CheckSLIP();				//kill macslip
#ifdef	USE_NETWORK_SETUP
	CloseNetworkSetup();
#endif
	// archive the junk mailbox
	if (JunkPrefBoxArchive() && JunkTrimOK()) ArchiveJunk(GetJunkTOC());
#ifdef	IMAP
	// empty only the local trash.  The IMAP trash was taken care of before network was closed down.
	if (PrefIsSet(PREF_AUTO_EMPTY)) EmptyTrash(true,false,false);	
#else
	if (PrefIsSet(PREF_AUTO_EMPTY)) EmptyTrash();
#endif
	FlushTOCs(True,False);

	Cleanup();

#if __profile__
	ProfilerSetStatus(False);
	ProfilerDump("\peudora-profile");
	ProfilerTerm();
#endif
}

void ConfigureIfNeeded(void)
{
	Str255 pop;
	
	if (!*GetPOPPref(pop) && PrefIsSet(PREF_ACAP)) ACAPLoad(True);
	if (!*GetPOPPref(pop) || pop[1]=='@')	// ACAP helped?
	{
		// ask the user if they'd like to import mail from another account ...
		if (gImportersAvailable && ComposeStdAlert(Note,IMPORT_ON_STARTUP)==1)
		{
			DoMailImport();
		}
		else
		{
			DoSettings(0);
		}
	}
}

#if defined(EXPIRE) || defined(DEMO)
/************************************************************************
 * ExpireWarning - warn the user that Eudora will expire in the next 7 days
 * This routine works with an EXPIRE version AND a DEMO version.
 ************************************************************************/
void ExpireWarning(void)
{
	Str255 date;
	DateTimeRec dtr;
	uLong expireSecs;

#ifdef EXPIRE
	Zero(dtr);
	dtr.year = EXP_YEAR;
	dtr.month = EXP_MONTH+1;
	dtr.day = 1;
	
	Date2Secs(&dtr,&expireSecs);
	if (expireSecs - LocalDateTime() < 3600L*24*7)
	{
		DateString(expireSecs,longDate,date,nil);
		if (ComposeStdAlert(Caution,WILL_EXPIRE,date)==kAlertStdAlertOKButton)
			OpenAdwareURL(GetNagState(), UPDATE_SITE, actionUpdate, updateQuery, nil);
	}
#endif

#ifdef DEMO
	if (gTimeBombData.days != TB_DISARMED)		//only warn if the timebomb is not disarmed, 
	{
		long expiresOn = gTimeBombData.date + (gTimeBombData.days * NUMBER_OF_SECS_DAY);
		
		Secs2Date(expiresOn,&dtr);
		Date2Secs(&dtr,&expireSecs);
		if (expireSecs - LocalDateTime() < 3600L*24*7)
		{
			SetMyCursor(arrowCursor);
			DateString(expireSecs,longDate,date,nil);
			MyParamText(date,0,0,0);
			if (Alert(WILL_EXPIRE_ALRT,nil) == BUY_NOW_BUTTON_WILL_EXPIRE)
				PurchaseEudora();
		}
	}
#endif

}
#endif

/************************************************************************
 * HouseKeeping - periodic cleanup functions
 ************************************************************************/
void HouseKeeping(long activeTicks)
{
	static long flushTicks=0;
	uLong checkTicks;
	static long AutoSaveTicks;
	long autoSecs;
	static long spoolCleaned;

	if (activeTicks>10)
	{
		long ticks;
		WindowPtr winWP;
		MyWindowPtr	win;
		static long lastBadgeTOCDirty = -1;

		CurPers = PersList;	// make darn sure

		EnableMenus(MyFrontWindow(),False);
		
		/*
		 * warn the user if we're growing low (get it) on memory
		 */
		MonitorGrow(True);
		
		if (!InBG && TickCount()%20==0) FloatingWinIdle();
		
		if (FixServers)
		{
			RedoTOCs();
			FixMessServerAreas();
			FixServers = False;
		}
		
		/*
		 * When was yesterday?
		 */
		SetYesterday();
		
		if (PrefBadgeDo() && lastBadgeTOCDirty != AnyTOCDirty)
		{
			lastBadgeTOCDirty = AnyTOCDirty;
			BadgeTheSupidDock(GlobalUnreadCount(),nil,AttentionNeeded || MyNMRec);
		}
		
		/*
		 * Do we need to switch modes?
		 */
		if (NewClientModePlusOne) AutoSwitchClientMode();
		
		//
		// Update the emo turd cache
		EmoTurdCache = EmoLitterInternetWithTurds();
		/*
		 * Do we need to nag the user?
		 */
		if (PlaylistNagCount)
		{
			short i;
			Handle res;
			
			for (i=1;res=GetIndResource(NAG_REQUEST_TYPE,i);i++)
			{
				short id;
				OSType tipe;

				GetResInfo(res,&id,&tipe,GlobalTemp);
				DoPlaylistNag(id);
			}
			PlaylistNagCount = 0;
		}
		
		/*
		 * Register?
		 */
#ifndef NAG
		if (!ModalWindow) AutoRegister();
#endif
		
		//if ((win=FrontWindow_()) && IsMyWindow(win) && win->qWindow.windowKind==MESS_WIN)
			//GenerateReceipt(Win2MessH(win),MDN_DISPLAYED,True);
	
#ifdef THREADING_ON
		/*
		 * report fatal thread errors
		 */
		if (CheckThreadError)
		{
			WarnUser(THREAD_CANT_CHECK,CheckThreadError);
			CheckThreadError = noErr;
		}
		if (SendThreadError)
		{
			WarnUser(THREAD_CANT_SEND,SendThreadError);
			SendThreadError = noErr;
		}
#endif

		if ((autoSecs = GetRLong(AUTOSAVE_INTERVAL)) && TickCount()-AutoSaveTicks>autoSecs*60)
		{
			AutoSaveTicks = TickCount();
			AutoSave();
		}
			
		if (NewError && !GetNumBackgroundThreads())
		{
			WindowPtr mywin;
			
			if (!IsTPIdleControlVisible() || PrefIsSet(PREF_TASK_PROGRESS_AUTO))
				OpenTasksWinErrors();
			else
			if ((mywin=FrontWindow_()) && GetWindowKind(mywin)==TASKS_WIN)
			{
				if (!InBG)
					SysBeep(1);
				NewError = false;
			}
		}
		
		/*
		 * save window state if need be
		 */
		if (WashMe && DiskSpunUp()) RememberOpenWindows();
		
		/*
		 * save the mid list
		 */
		OutgoingMIDListSave();
	
		/*
		 * check mail?
		 */
#ifdef THREADING_ON
		// for now, don't start thread if modal window up. this may change...
		if (((!PrefIsSet(PREF_THREADING_OFF) && ThreadsAvailable() && activeTicks>120) || (activeTicks > GetRLong(IDLE_SECS)*60)) && (!ModalWindow || AreAllModalsPlugwindows()) && !CheckThreadRunning && !(PrefIsSet(PREF_POP_SEND) && SendThreadRunning))
#else
		if (activeTicks > GetRLong(IDLE_SECS)*60 && (!ModalWindow || AreAllModalsPlugwindows()))
#endif
		{
			if (CheckOnIdle)
			{
				XferMail((CheckOnIdle&kCheck)!=0,(CheckOnIdle&kSend)!=0,False,True,True,0);
				CheckOnIdle = 0;
				return;
			}
			if (checkTicks = PersCheckTicks())
			{
				if (TickCount() > checkTicks)
				{
					if (!Offline && !(CurrentModifiers()&shiftKey))
					{
						// skip this check if we're not online.  Be anal and slow about checking the connection.
						if (AutoCheckOKWithDBRead(true))
						{
							XferMail(True,PrefIsSet(PREF_SEND_CHECK),False,False,True,0);
							return;
						}
					}
				}
			}
			if (ForceSend && ForceSend <= GMTDateTime() && AutoCheckOK()) {XferMail(False,True,False,False,True,0);return;}
		}
		
#ifdef THREADING_ON
	/* if a send thread couldn't be spawned, try it now */
	if (SendImmediately && !SendThreadRunning)
	{
		XferMail(False,True,False,False,True,0);
		return;
	}
#endif	

		if (OpenAddrErrs && !AmQuitting && !ModalWindow && activeTicks > GetRLong(SHORT_MODAL_IDLE_SECS)*60)
		{
			//	Recently sent one or more messages with addressing errors. Open 'em.
			TOCHandle outTocH;
			short sumNum;

			if (outTocH = GetOutTOC())
				for (sumNum=(*outTocH)->count-1; sumNum>=0; sumNum--)
					if ((*outTocH)->sums[sumNum].flags&FLAG_ADDRERR)
					{
						(*outTocH)->sums[sumNum].flags &= ~FLAG_ADDRERR;
						if (!GetAMessage(outTocH,sumNum,nil,nil,True)) break;
					}
			OpenAddrErrs = false;
		}

	
		/*
		 * close unneeded TOC's
		 */
		if (activeTicks > GetRLong(TOC_SMALLDIRTY))
		{
			TOCHandle tocH;
			for (tocH=TOCList;tocH;tocH=(*tocH)->next)
				(*tocH)->reallyDirty = TOCIsDirty(tocH) || (*tocH)->reallyDirty;
		}
		FlushTOCs(True,True);
		
		/*
		 * just for good measure
		 */
		if (TickCount()-flushTicks > 60*GetRLong(FLUSH_SECS) && DiskSpunUp())
		{
			FlushVol(nil,MailRoot.vRef);
			flushTicks = TickCount();
		}
		
		/*
		 * close speller, if idle for a while
		 */
#ifdef WINTERTREE
		if (HasFeature (featureSpellChecking))
			SpellClose(false);
#endif //WINTERTREE
		
		/*
		 *	Wander over to the speaker and make sure he or she is happy
		 */
#ifdef SPEECH_ENABLED
		(void) SpeechIdle ();
#endif

		/*
		 * Save the personalities
		 */
		if (HasFeature (featureMultiplePersonalities))
			PersSaveAll();
					
		/*
		 * The resolver may have left us little poodle bombs...
		 */
		FlushHIQ();

		/*
		 * and logging...
		 */
#ifdef THREADING_ON
		if (!GetNumBackgroundThreads ())
#endif
		if (!(LogLevel&LOG_EVENT)) CloseLog();
		
		/*
		 * and that pesky settings file
		 */
		MyUpdateResFile(SettingsRefN);
		
#ifdef TWO
		/*
		 * stop MacSLIP?
		 */
		CheckSLIP();
#endif

		/*
		 * close unused IMAP connections
		 */
		CheckIMAPConnections();

		/*
		 * display any pending IMAP warnings
		 */
		IMAPWarnings();

		/*
		 *	Remind the user about marked links, if we're online.
		 */
		if (activeTicks > GetRLong(OFFLINE_LINK_NAG_TIME)) RemindNag();
				
#ifdef NEVER
		if (RunType!=Production)
		{
			Str255 item,item2;
			short i;
			MenuHandle mh;
			
			*item2 = 0;
			for (i=NEW_TO_HIER_MENU;i<=INSERT_TO_HIER_MENU;i++)
			{
				if (mh=GetMHandle(i))
				{
					GetItem(mh,1,item);
					if (*item2 && !StringSame(item,item2))
					{
						PSCat(item,"\p<->");
						PSCat(item,item2);
						DebugStr(item);
						break;
					}
					PCopy(item2,item);
				}
			}
		}
#endif

		/*
		 * fcc?
		 */
		//Folder Carbon Copy - light does not support the FCC feature
		if (HasFeature (featureFcc))
		FccFlop(GetWindowMyWindowPtr(FrontWindow_()));
		
		/*
		 *	Link History Aging
		 */
		if (HasFeature(featureLink)) AgeLinks();
		
		/*
		 * idle routines
		 */
		ticks = TickCount();
		for (winWP = GetWindowList(); winWP && TickCount() - ticks < 10; winWP = GetNextWindow (winWP)) {
			win = GetWindowMyWindowPtr (winWP);
			if (IsKnownWindowMyWindow(winWP) && win && win->idle && ticks-win->lastIdle>win->idleInterval)
			{
				SetPort_(GetWindowPort (winWP));
				win->idle(win);
				// (jp) Handle idle processing for intelligent nick fields
				if (win->pte && HasNickScanCapability (win->pte))
					NicknameWatcherIdle (win->pte);
				win->lastIdle = TickCount();
			}
		}
#ifdef THREADING_ON
#ifndef BATCH_DELIVERY_ON
		if (!ModalWindow || AreAllModalsPlugwindows())	// no modal window uppermost
		if ((NeedToFilterIn && !CheckThreadRunning) || (NeedToFilterOut && !SendThreadRunning)) // we might plausibly have some messages
		if (InBG || ((TickCount()-ActiveTicks) > GetRLong(MODAL_IDLE_SECS)*60))	// the user is not busy in Eudora
			FilterXferMessages ();	
#else
		if (NeedToFilterIn||NeedToFilterOut||NeedToNotify||NeedToFilterIMAP)	// there might be something there
		if (!ModalWindow || AreAllModalsPlugwindows())	// no modal window uppermost
		if (TickCount()-ActiveTicks > GetRLong(SHORT_MODAL_IDLE_SECS)*60)	// user is not busy in Eudora
			FilterXferMessages();
#endif
#endif
		if ((!ModalWindow || AreAllModalsPlugwindows()) && TickCount()-ActiveTicks > GetRLong(SHORT_MODAL_IDLE_SECS)*60 && NoNewMailMe)
		{
			NoNewMailMe = false;
			NotifyNewMail(0,false,nil,nil);
		}
#ifdef	IMAP
		// update any IMAP message windows that need it
		UpdateIMAPWindows();
		
		// do New Mail alert if an IMAP check recently completed
		if ((!ModalWindow || AreAllModalsPlugwindows()) && TickCount()-ActiveTicks > GetRLong(SHORT_MODAL_IDLE_SECS)*60 && !CheckThreadRunning && !IMAPCheckThreadRunning && gNewMessages)
		{
			NotifyNewMail(0,false,nil,nil);
			gNewMessages = 0;
		}
#endif
	}	
		
	/*
	 * toolbar?
	 */
	if (TBarHasChanged)
	{
		TBarHasChanged = False;
		if (PrefIsSet(PREF_TOOLBAR) != ToolbarShowing()) OpenToolbar();
		else if (ToolbarShowing()) {OpenToolbar();OpenToolbar();}
	}
	if (!InBG) ShowToolbar();
	
	/*
	 * run the text analyzer
	 */
	if (!TypingRecently && BeingAnal()) AnalScan();
	
	// and the emoticon scanner
	if (!TypingRecently && HasFeature(featureEmo) && EmoDo()) EmoScan();
	
	/*
	 * do idle tasks that matter only for active users
	 */
	ActiveUserIdleTasks();
	
	AdWinIdle();
	StatsIdle();

	if (gScreenChange && !InBG)	// change in screen size (resolution)
	{
		gScreenChange = false;
		ScreenChange();
	}
	
	/*
	 * Really obno stuff
	 */
	if (InBG || activeTicks > GetRLong(LONGTERM_IDLE)*60)
	{
		// perform any scheduled AddressBook Sync
		OSXAddressBookScheduledSync();
		
		/*
		 * clean up the spool folder
		 */
		if (GetPrefLong(PREF_SPOOL_DESTROY) && GMTDateTime()-spoolCleaned > 4*3600)
			if (!CleanSpoolFolder(GetPrefLong(PREF_SPOOL_DESTROY))) spoolCleaned = GMTDateTime();
		
		// perform IMAP auto expunge.
		IMAPAutoExpunge();

		// any mailboxes need compaction?
		if (!GetNumBackgroundThreads())	// don't compact if threads running; might lead to conflicts
		{
			FSSpec spec;
			if (!StackPop(&spec,CompactStack))
			{
				CompactMailbox(&spec,false);
				if (GetHandleSize(CompactStack)/(*CompactStack)->elSize - (*CompactStack)->elCount > 20)
					StackCompact(CompactStack);
				return;
			}
		}
	}
}

/************************************************************************
 * ActiveUserIdleTasks - do idle tasks we do if the user is active
 ************************************************************************/
void ActiveUserIdleTasks(void)
{
	static uLong stampTicks;
	static uLong netTicks;
	
	// if the user has been idle for a minute or more, stop doing these things
	if (!UserIdle(ALMOST(TICKS2MINS)))
	{		
		/*
		 *	Check on our Network Connection every minute or so
		 */
		 
		if (TickCount() - netTicks > TICKS2MINS) 
		{	
			// update the cached network status
			TCPWillDial(true);
			
			netTicks = TickCount();
		}
		
		/*
		 *	Timestamp and flush the audit log once every two minutes
		 */	
		 
		if (!stampTicks) stampTicks=TickCount();
		else if (FMBMain && (TickCount()-stampTicks > 2  * TICKS2MINS))
		{
			long faceTime, rearTime, connectTime, totalTime;
			FaceMeasureReport(FMBMain,&faceTime, &rearTime, &connectTime, &totalTime);
			AuditTimestamp(faceTime, rearTime, connectTime, totalTime);
			stampTicks = TickCount();
		}
	}
}

/************************************************************************
 * AutoSave - save open windows
 ************************************************************************/
void AutoSave(void)
{
	WindowPtr winWP;
	MyWindowPtr	win;
	Boolean saveMe;
	 
	for (winWP = FrontWindow_(); winWP; winWP = GetNextWindow (winWP)) {
		win = GetWindowMyWindowPtr (winWP);
		if (IsKnownWindowMyWindow(winWP) && win && IsDirtyWindow(win))
		{
			saveMe = false;
			switch(GetWindowKind (winWP))
			{
				case ALIAS_WIN:
				case TEXT_WIN:
				case COMP_WIN:
				case FILT_WIN:
					saveMe = true;
					break;
				case MESS_WIN:
					saveMe = MessOptIsSet(Win2MessH(win),OPT_WRITE);
					break;
			}
			if (saveMe) DoMenu2(winWP,FILE_MENU,FILE_SAVE_ITEM,0);
		}
	}
}

/**********************************************************************
 * CheckSLIP - check SLIP and PPP, hangup if need be
 **********************************************************************/
void CheckSLIP(void)
{
	if (gActiveConnections==0 && !gConnecting && !gStayConnected && !AdWinNeedsNetwork())
	{
		if (gUseOT)
		{
			extern MyOTPPPInfoStruct MyOTPPPInfo;
				
			if (gHasOTPPP && !pendingCloses && MyOTPPPInfo.weConnectedPPP) 
				OTPPPDisconnect(false, true);
		}
	}
}

/**********************************************************************
 * FccFlop - change the transfer menu for Fcc
 **********************************************************************/
void FccFlop(MyWindowPtr win)
{
	WindowPtr	winWP = GetMyWindowWindowPtr (win);
	Str255 s;
	MenuHandle mh;
	static short curName=0;
	short shdName;
	PETEHandle	pte;
	
	//Folder Carbon Copy - Light does not support the FCC feature
	if (!HasFeature (featureFcc))
		return;

	if (win && 
			IsWindowVisible(winWP) &&
			GetWindowKind(winWP)==COMP_WIN &&
			SumOf(Win2MessH(win))->state!=SENT &&
			(pte = (*Win2MessH(win))->bodyPTE) &&
		  	BCC_HEAD==CompHeadCurrent(pte)) 
	{
		shdName = FCC_NAME;
		UseFeature (featureFcc);		
	}
	else
		shdName = TRANSFER_MNAME;
	
	if (shdName!=curName && (mh = GetMHandle(TRANSFER_MENU)))
	{
		MySetMenuTitle(mh,GetRString(s,shdName));
		DrawMenuBar();
		curName = shdName;
		TransferMenuHelp(shdName==FCC_NAME ? FCC_HMNU:TFER_HMNU);
	}
}

/************************************************************************
 * TransferMenuHelp - copy the correct hmnu into our settings file
 ************************************************************************/
void TransferMenuHelp(short id)
{
	Handle h;
	Zap1Resource('hmnu',TRANSFER_MENU);
	ResourceCpy(SettingsRefN,HelpResFile,'hmnu',id);
	{
		if (h=Get1Resource('hmnu',id))
		{
			HNoPurge(h);
			SetResInfo(h,TRANSFER_MENU,"");
		}
	}
}

/**********************************************************************
 * SetYesterday - set our idea of when today begins
 **********************************************************************/
void SetYesterday(void)
{
	DateTimeRec dtr;
	static long ticks;
	static long lastYesterday;
	
	if (TickCount()-ticks>TICKS2MINS || !ticks)
	{
		long secs = LocalDateTime();
		ticks = TickCount();
#ifdef	DEMO
	today = secs;	
#endif
	SecondsToDate(secs,&dtr);
	
	dtr.hour = dtr.minute = dtr.second = 0;
	
	DateToSeconds(&dtr,&secs);
	
	if (secs!=lastYesterday)
	{
		lastYesterday = secs;
		RedoAllTOCs();
	}
	
	Yesterday = LocalDateTime()-secs;
	}
}

/**********************************************************************
 * HandleEvent - handle an event
 **********************************************************************/
Boolean HandleEvent(EventRecord *event)
{
	WindowPtr topWinWP = MyFrontWindow(); 	/* in case we need to know */
	Boolean active=False;
	
	MainEvent = *event;	/* copy it into an accessible place */
			
#ifdef	GX_PRINTING
	if (!isGXDialog && MyIsDialogEvent(event))
#else		//GX_PRINTING
	if (MyIsDialogEvent(event))
#endif	//GX_PRINTING
	{
		DialogPtr	dlgPtr = GetDialogFromWindow(Event2Window(event));
		if (!dlgPtr)
			dlgPtr=GetDialogFromWindow(topWinWP);
		if (IsMyWindow(GetDialogWindow(dlgPtr)))
		  DoModelessEvent(dlgPtr,event);
	  SetPort(InsurancePort);
		return(True);
	}

	
	if (topWinWP)
	{
		SetPort_(GetWindowPort(topWinWP));
		UsingWindow(topWinWP);
	}
	else
		SetPort_(InsurancePort);
	#ifdef USECMM
	//check for contextual menu event
	if( gHasCMM && MyIsShowContextualMenuClick( event ) )
	{
		HandleContextualMenuClick( topWinWP, event );
		return(active);
	}
	#endif
	
	/*
	 * slog through all the events...
	 */
	switch (event->what)
	{
		case keyDown:
		case autoKey:
#ifdef	FLOAT_WIN
			if (keyFocusedFloater) topWinWP = GetMyWindowWindowPtr(keyFocusedFloater);		//FLOAT_WIN
#endif	//FLOAT_WIN
			/*if (HasHelp && HMIsBalloon()) HMRemoveBalloon();*/
			UsingWindow(topWinWP);
			DoKeyDown(topWinWP,event);
			active = True;
			break;
		
		case mouseUp:
		case mouseDown:
			/*if (HasHelp && HMIsBalloon()) HMRemoveBalloon();*/
			DoMouseDown(topWinWP,event);
			active = True;
			break;
			
		case updateEvt:
			UsingWindow(topWinWP);
			DoUpdate(topWinWP,event);
			break;
			
		case activateEvt:
			if (!InBG) DoActivate(topWinWP,event);
			break;
			
		case app4Evt:
			active = DoApp4(topWinWP,event);
			break;
			
		case app1Evt:
			UsingWindow(topWinWP);
			active = DoApp1(topWinWP,event);
			break;
			
		case kHighLevelEvent:
			(void) AEProcessAppleEvent(event);
			break;
	}
	SetPort(InsurancePort);
	if (!ForceSend) SetSendQueue();	/* a message from beyond */
	return(active);
}

/************************************************************************
 * MyIsShowContextualMenuClick - is this a context menu event?
 ************************************************************************/
Boolean MyIsShowContextualMenuClick(EventRecord *event)
{
	uLong opts = GetPrefLong(PREF_DRAG_OPTIONS);
	
	if ((!(opts&2) || !(event->modifiers&controlKey)) && IsShowContextualMenuClick(event)) return(true);
	
	return(false);
}

/**********************************************************************
 * DoApp4 - handle an app4 event; suspend/resume/mouse
 **********************************************************************/
Boolean DoApp4(WindowPtr topWinWP,EventRecord *event)
{
	WindowPtr winWP;
	MyWindowPtr	topWin;

	if (((event->message>>24)&0xff) == 0xfa)
	{
		SFWTC = True;
	}
	else
	{
		ClickType = Single;	// no longer double-clicking if suspend/resume
		
		if (((event->message)>>24)&1 == 1)
		{
			InBG = (event->message&1)==0;
			if (!InBG)
				SFWTC = True;
			topWinWP = FrontWindow_();
#ifdef CTB
			if (CnH) CMResume(CnH,!InBG);
#endif
			InBG = (event->message&1)==0;
			if (InBG) 
			{
				MyHMHideTag();
				HiliteMenu(0);
			}
			else
			{
#ifdef REFRESH_LABELS_MENU
				RefreshLabelsMenu();
#endif // REFRESH_LABELS_MENU
#ifdef	GX_PRINTING
				if (gGXIsPresent) UpdateGXPageSetup();
#endif	//GX_PRINTING
			}
			topWin = GetWindowMyWindowPtr (topWinWP);
			if (topWinWP && IsMyWindow(topWinWP) && InBG==topWin->isActive)
				ActivateMyWindow(topWinWP,!InBG);
			
			//	Show/hide floating/docked windows
			for (winWP = GetWindowList(); winWP; winWP = GetNextWindow (winWP))
				if (IsFloating(winWP))
				{
					topWin = GetWindowMyWindowPtr (winWP);
					if (InBG) StashStructure(topWin);
					ShowHide(winWP,!InBG);
					if (!InBG) StashStructure(topWin);
				}
			if (!InBG)
			{
				EventRecord mouseDownEvent;
				
				//	Make sure docked windows are still appropriately docked
				//	in case user changed screen resolution while Eudora
				//	was in background
				PositionDockedWindows();
				//	Make sure pending mouse downs don't hit a floating
				//	window that has just been made visible	
				if (OSEventAvail(mDownMask,&mouseDownEvent))
				if (FindWindow_(event->where,&winWP) >= inContent)
				if (IsFloating(winWP))
					//	Remove mousedown event for click in this floating window
					GetNextEvent(mDownMask,&mouseDownEvent);
			}
		}
	}
	return(False);
}

/**********************************************************************
 * RefreshLabelsMenu - get a new copy of the Finder's label strings
 **********************************************************************/
void RefreshLabelsMenu(void)
{
	MenuHandle mh = GetMHandle(LABEL_HIER_MENU);
	
	if (mh)
	{
		TrashMenu(mh,3);
		AddFinderItems(mh);
		SetItemMark(mh,1,noMark);
		EnableItem(mh,0);
		EnableItem(mh,1);	//	For some reason OS X may leave this disabled
	}
}

/************************************************************************
 * SetSendQueue - preload the SendQueue global with the number of queued
 * messages.
 ************************************************************************/
void SetSendQueue(void)
{
	TOCHandle tocH;
	int sumNum;
	uLong gmtSecs = GMTDateTime();
	PersHandle pers;
	
//	if (InAThread())
//		return;
	SendQueue = 0;
	ForceSend = 0xffffffff;
	if (!(tocH=GetRealOutTOC())) return;

	for (pers=PersList;pers;pers=(*pers)->next) (*pers)->sendQueue = 0;
	for (sumNum=0; sumNum<(*tocH)->count; sumNum++)
	{
		if ((*tocH)->sums[sumNum].messH) continue;	// skip open messages
		if ((*tocH)->sums[sumNum].state == TIMED)
		{
		  if ((*tocH)->sums[sumNum].seconds &&
			    (*tocH)->sums[sumNum].seconds < ForceSend)
				ForceSend = (*tocH)->sums[sumNum].seconds;
		  if ((*tocH)->sums[sumNum].seconds <= gmtSecs)
		  	SetState(tocH,sumNum,QUEUED);
		}
		if ((*tocH)->sums[sumNum].state == QUEUED)
		{
			SendQueue++;
			pers = TS_TO_PERS(tocH,sumNum);
			if (!pers)
			{
				pers = PersList;
				(*tocH)->sums[sumNum].persId = 0;
				TOCSetDirty(tocH,true);
			}
			(*pers)->sendQueue++;
		}
	}
}

/**********************************************************************
 * DoApp1 - handle an app1 event; scrolling by saratoga keys
 **********************************************************************/
Boolean DoApp1(WindowPtr topWinWP,EventRecord *event)
{
	if (topWinWP && IsMyWindow(topWinWP))
	{
		MyWindowPtr	topWin = GetWindowMyWindowPtr (topWinWP);
		if (topWin->app1 && (*topWin->app1)(topWin,event))
			;
		else if (topWin->pte)
		{
			event->what = keyDown;
			PeteEdit(topWin,topWin->pte,peeEvent,(void*)event);
		}
		else
			return DoApp1NoPTE(topWin,event);
	}
	return(True);
}

/**********************************************************************
 * DoApp1NoPTE - handle an app1 event but ignore window & pte handlers
 **********************************************************************/
Boolean DoApp1NoPTE(MyWindowPtr topWin,EventRecord *event)
{
 		if (topWin->vBar)
			switch(event->message & charCodeMask)
			{
				case homeChar:
					ScrollIt(topWin,REAL_BIG,REAL_BIG);
					break;
				case endChar:
					ScrollIt(topWin,0,-REAL_BIG);
					break;
				case pageUpChar:
					ScrollIt(topWin,0,
						RoundDiv(topWin->contR.bottom-topWin->contR.top,topWin->vPitch)-1);
					break;
				case pageDownChar:
					ScrollIt(topWin,0,
						RoundDiv(topWin->contR.top-topWin->contR.bottom,topWin->vPitch)+1);
					break;
			}
		return true;
}

/**********************************************************************
 * DoKeyDown - handle a keystroke aimed at a window
 **********************************************************************/
void DoKeyDown(WindowPtr topWinWP,EventRecord *event)
{
	long select;
	short c = UnadornMessage(event)&0xff;
	OSErr err=errAEEventNotHandled;
	Boolean done;

	// (jp) Check for cmd-period while speaking
#ifdef SPEECH_ENABLED
	if (event->what==keyDown && event->modifiers&cmdKey && IsCmdChar(event,'.') ||	// command-period
			(event->message&charCodeMask)==escChar)	//esc
		if (CancelSpeech ()) {
			FlushEvents (keyDownMask|keyUpMask|mDownMask|mUpMask,0);	/* kill events on cmd-period */
			return;
		}
#endif

	ClickType = Single;	// can't keydown during a double-click

	if (IsMyWindow(topWinWP))
	{
		MyWindowPtr topWin = GetWindowMyWindowPtr(topWinWP);
		
		SetPort_(GetWindowPort (topWinWP));

		// (jp) If the field supports nick stuff, handle the keystroke
		done = false;
		if (HasNickScanCapability (topWin->pte)) {
			if (!(done = NicknameWatcherKey (topWin->pte, event)))
				if (topWin->key) {
					if ((*topWin->key)(topWin,event))
						done = true;
				}
				else
					if (!(event->modifiers & cmdKey)) {
						(void) PeteEdit (topWin, topWin->pte, peeEvent, (void*) event); // (jp) Just in case no key function is defined (dialogs)
						done = true;
					}
			NicknameWatcherKeyFollowup (topWin->pte);
//			if (IsWazoo (topWin))
//				ValidRect (PeteRect (topWin->pte, &r));		// (jp) Lame, I know... but we have a flicker problem.  :|
																							//			The problem is caused because the nickname watching
																							//			code invalidates the field.  Okay, fair enough...
																							//			But, if the nickname window is wazooed, WazooPreUpdate
																							//			is called which calls Draw1Control on the tab -- which
																							//			erases the text we just finished drawing.  Boo.  Hiss.
																							//			We con't have this problem in either composition windows
																							//			or in windows that not wazooed.  A more general solution
																							//			should eventually replace this hackery.
		}
		if (!done) {
			short charCode = event->message&charCodeMask;

			if (!topWin->pte && charCode==backSpace && event->modifiers==cmdKey)
				DoMenu(topWinWP,(EDIT_MENU<<16)|EDIT_CLEAR_ITEM,0);
			else if (topWin->key && (*topWin->key)(topWin,event))
				;
			else if (event->modifiers&cmdKey)
			{
				if (IsMyWindow(topWinWP) && topWin && topWin->pte)
					err = PeteEdit(topWin,topWin->pte,peeEvent,(void*)event);
				if (err==errAEEventNotHandled)
					if (select=MyMenuKey(event)) DoMenu(topWinWP,select,event->modifiers);
					else SysBeep(20L);
				return;
			}
			else if (topWin->pte)
				PeteKey(topWin,topWin->pte,event);
		}
	}
	else if (event->modifiers&cmdKey)
	{
		if (select=MyMenuKey(event))
			DoMenu(topWinWP,select,event->modifiers);
		else
			SysBeep(20L);
	}
	else
		SysBeep(20L);
}

pascal void Hook(void)
{
	if (RunType != Production && CurrentModifiers()&controlKey)
	{
		Handle fkey3;
		if (fkey3 = GetResource_('FKEY',3))
		{
			(*(ProcPtr)LDRef(fkey3))();
			UL(fkey3);
		}
		else SysBeep(10);
	}
}

/**********************************************************************
 * DoMouseDown - handle a mouse-down event, goodness knows where
 **********************************************************************/
void DoMouseDown(WindowPtr topWinWP,EventRecord *event)
{
	static Point downSpot;	/* Point of previous mouseDown */
	static long upTicks;		/* time of previous mouseUp */
	long dblTime;						/* double-click time from PRAM */
	long wPart; 						/* window part where click occurred */
	MyWindowPtr topWin;
	WindowPtr	winWP;
	MyWindowPtr	win;
	short tolerance;
	long mSelect;

	topWin = GetWindowMyWindowPtr (topWinWP);
	if (event->what==mouseUp)
	{
		upTicks = event->when;
		if (IsMyWindow(topWinWP) && topWin && topWin->pte) PeteEdit(topWin,topWin->pte,peeEvent,event);
		return;
	}
	
	dblTime = GetDblTime();
	tolerance = GetRLong(DOUBLE_TOLERANCE);
	
	/*
	 * figure out whether this is a double or triple click,
	 * and update double and triple click times
	 */
	if (event->when-upTicks<dblTime)
	{
		int dx = event->where.h - downSpot.h;
		int dy = event->where.v - downSpot.v;
		if (ABS(dx)<tolerance && ABS(dy)<tolerance && !(event->modifiers&cmdKey))
		{
			/* upgrade the click */
			ClickType++;
			if (ClickType > Triple) ClickType = Single;
		}
		else
			ClickType = Single;
	}
	else
		ClickType = Single;
		
	upTicks = event->when;
	downSpot = event->where;
	SFWTC = True;
	
	/*
	 * where was the click?
	 */
	wPart = FindWindow_(event->where,&winWP);
	win = GetWindowMyWindowPtr (winWP);
	UsingWindow(winWP);
#ifdef CTB
	if (CnH && winWP && (long)CnH==GetWindowPrivateData(winWP))
		CMEvent(CnH,event);
	else
#endif
	if (ModalWindow && winWP && winWP!=ModalWindow)
		SysBeep(20L);
	else
		switch (wPart)
		{
			case inMenuBar:
				EnableMenuItems(False);
				SetMyCursor(arrowCursor); SFWTC = True;
				SetMenuTexts(event->modifiers,False);
				mSelect = MenuSelect(event->where);
				DoMenu(topWinWP,mSelect,event->modifiers);
				break;

			case inProxyIcon:
				if (win && win->proxy)
				{
					win->proxy(win,event);
					break;
				}
				// fall-through is deliberate
			case inDrag:
#ifdef	FLOAT_WIN
				if (!InBG && event->modifiers & cmdKey && IsTopNonFloater(winWP) && (IsBoxWindow(winWP) || IsMessWindow(winWP)))
#else		//FLOAT_WIN
				if (!InBG && event->modifiers & cmdKey && winWP==topWinWP && (IsBoxWindow(winWP) || IsMessWindow(winWP)))
#endif	//FLOAT_WIN
					PopupMailboxPath(win,nil,0,downSpot);
				else
				{
					DragMyWindow(winWP,event->where);
#ifdef TWO
					if (IsMessWindow(winWP) && ClickType==Double)
					{
						MyWindowPtr	messTocWin = (*(*Win2MessH(win))->tocH)->win;
						WindowPtr		messTocWinWP = GetMyWindowWindowPtr(messTocWin);
						ShowMyWindow(messTocWinWP);
						UserSelectWindow(messTocWinWP);
						SelectMessage((*Win2MessH(win))->tocH,(*Win2MessH(win))->sumNum);
						if (event->modifiers&optionKey) BoxSelectSame((*Win2MessH(win))->tocH,SORT_SUBJECT_ITEM,(*Win2MessH(win))->sumNum);
					}
#endif
				}
				break;
			case inZoomIn:
			case inZoomOut:
				if (IsMyWindow(winWP)) win->saveSize = True;
				ZoomMyWindow(winWP,event->where,wPart);
				break;
			case inGrow:
				if (!(GetWindowKind(winWP)==dialogKind ||
						IsMyWindow(winWP) && win && win->isRunt))
				{
					GrowMyWindow(win,event);
					break;
				}
				/* fall-through is deliberate */
			case inContent:
				DoContentClick(topWinWP,winWP,event);
				break;
			case inGoAway:
				if (event->modifiers & optionKey)
					CloseAll(False);
				else
					GoAwayMyWindow(winWP,event->where);
				break;
		}
}

/**********************************************************************
 * DoContentClick - handle a click in the content region of a window
 **********************************************************************/
void DoContentClick(WindowPtr topWinWP,WindowPtr winWP,EventRecord *event)
{
	MyWindowPtr	win = GetWindowMyWindowPtr (winWP);
	PETEHandle	pte;
	Point				pt;

	// (jp) Ignore clicks if for some reason we don't have a MyWindowPtr
	if (!win)
		return;
		
#ifdef	FLOAT_WIN
	if (IsMyWindow(winWP) && IsFloating(winWP))
		SetKeyFocusedFloater(win);
	else
	{
		SetKeyFocusedFloater(nil);
#endif	//FLOAT_WIN			
		if (winWP != topWinWP)
		{
			SetPort_(GetWindowPort(winWP));
			if (IsMyWindow(winWP) && WinBGDrag(win,event))
				; // leave it alone
			else if (IsMyWindow(winWP) && win->bgClick)
				(*win->bgClick)(win,event);
			else
				SelectWindow_(winWP);
			return;
		}
#ifdef	FLOAT_WIN
	}
#endif	//FLOAT_WIN

	SetPort_(GetWindowPort(winWP));
	pt = event->where;
	GlobalToLocal(&pt);
	if (!win->dontControl && HandleControl(pt,win))
		;
	else if (IsMyWindow(winWP))
	{
		if (!ClickWazoo(win,event,pt,nil))
		if (!ClickSponsorAd(win,event,pt))
		{
			if (win->click)
				(*win->click)(win,event);
			else {
				// (jp) Figure out which PETE actually received the click
				for (pte = win->pteList; pte; pte = PeteNext (pte))
					if (PtInPETE (pt, pte))
						break;
				if (PeteIsValid(pte)) {
					if (pte != win->pte)
						PeteSelect (win, win->pte, 0, 0);
					PeteEditWFocus (win, pte, peeEvent, (void *) event, nil);
				}
			}
		}
	}
}

/**********************************************************************
 * 
 **********************************************************************/
Boolean WinBGDrag(MyWindowPtr win,EventRecord *event)
{
	WindowPtr	winWP = GetMyWindowWindowPtr (win);
	PETEHandle pte;
	Point where;
	Boolean result=False;

	PushGWorld();
	
	SetPort_(GetWindowPort(winWP));
	where = event->where;
	GlobalToLocal(&where);
		
	if (IsWazoo(winWP) && ClickWazoo(win,event,where,&result) && result)
		; // bg wazoo drag; all done now
	else
	{
		for (pte=win->pteList;pte;pte=PeteNext(pte))
		{
			if (PtInPETE(where,pte))
			{
				if (tsmDocNotActiveErr!=PeteEdit(win,pte,peeEvent,event))
					result = True;
				break;
			}
		}
	}

	PopGWorld();
	return(result);
}

/************************************************************************
 * HandleControl
 ************************************************************************/
Boolean HandleControl(Point pt, MyWindowPtr win)
{
	WindowPtr	winWP = GetMyWindowWindowPtr (win);
	int part;
	int oldValue, difference;
	ControlHandle cntl;
	
	if (part = FindControl(pt,winWP,&cntl))
	{
		// Not in scrollbar thumb
		if (part != kControlIndicatorPart)
		{
			part = TrackControl(cntl, pt, (ControlActionUPP)(-1));
			ScrollAction(cntl,0);	// reset scroll throttle when mouse goes up
		}
		
		// Live scroll
		else if (GetControlVariant(cntl) != 0)
		{
			gLastCtlValue = GetControlValue(cntl);
			part = TrackControl(cntl, pt, NewControlActionUPP(AMLiveScrollAction));
		}
		
		// Regular scroll
		else
		{
			oldValue = GetControlValue(cntl);
			part = TrackControl(cntl, pt, nil);
			difference = oldValue - GetControlValue(cntl);
			if (difference)
			{
				if (ScrollIsH(cntl))
					ScrollMyWindow(win,difference,0);
				else
					ScrollMyWindow(win,0,difference);
				UpdateMyWindow(winWP);
			}
		}
		if (part && win->button)
			(*win->button)(win,cntl,CurrentModifiers(),part);
		return(True);
	}
	return(False);
}

/**********************************************************************
 * DoUpdate - handle an update event
 **********************************************************************/
Boolean DoUpdate(WindowPtr topWin,EventRecord *event)
{
#pragma unused(topWin)
	WindowPtr theWindow;
	
	if (theWindow = Event2Window(event))
	{
#ifdef CTB
		if (CnH && GetWindowPrivateData(theWindow)==(long)CnH)
		{
			CMEvent(CnH,event);
			return(False);
		}
#endif
		if (IsMyWindow(theWindow))
		{
			UpdateMyWindow(theWindow);
			return(False);
		}
	}
	return(True);	/* somebody else better do this one */
}

StackHandle ContextStack;
typedef struct {GrafPtr port; short curResFile;} SwitchContext;

/**********************************************************************
 * MightSwitch - protect from the vagaries of other apps
 **********************************************************************/
void MightSwitch(void)
{
	SwitchContext context;
	
	GetPort(&context.port);
	context.curResFile = CurResFile();
	if (!ContextStack) StackInit(sizeof(context),&ContextStack);
	if (!ContextStack) return;
	StackPush(&context,ContextStack);
	SetPort(InsurancePort);
}

/**********************************************************************
 * AfterSwitch - restore things after a switch back in
 **********************************************************************/
void AfterSwitch(void)
{
	SwitchContext context;

	if (!ContextStack) return;
	if (!StackPop(&context,ContextStack))
	{
		SetPort(context.port);
		UseResFile(context.curResFile);
	}
}

/**********************************************************************
 * DoActivate - handle an activate event (and a prior deactivate, to boot)
 **********************************************************************/
Boolean DoActivate(WindowPtr topWin,EventRecord *event)
{
#pragma unused(topWin)
	WindowPtr theWindow;
	Boolean active = !InBG && (event->modifiers&activeFlag)!=0;
	Boolean yours = False;
	
	if (theWindow = Event2Window(event))
	{
		UsingWindow(theWindow);
#ifdef CTB
		if (CnH && (long)CnH==GetWindowPrivateData(theWindow))
			CMActivate(CnH,active);
		else
#endif
		if (IsMyWindow(theWindow))
		{
			if (!CorrectlyActivated(theWindow))
				ActivateMyWindow(theWindow,active!=0);//(event->modifiers&activeFlag)!=0);
		}
		else
			yours=True;
	}
	SFWTC=True;
	return(yours);
}

/**********************************************************************
 * WarnUser - tell the user that something bad is happening
 **********************************************************************/
int WU(AlertType type,int errorStr,int errorNum,int file,int line)
{
	Str255 message;
	Str255 explanation;
	Str63 techStuff;
	AlertStdAlertParamRec param;
	UPtr sec;
	extern ModalFilterUPP DlgFilterUPP;
	short realSettingsRef = SettingsRefN;

	if (errorNum!=userCancelled)
	{
		SettingsRefN = GetMainGlobalSettingsRefN();
		GetRString(message,errorStr);
		if (sec=PIndex(message,'¤'))
		{
			*explanation = *message - (sec-message);
			BMD(sec+1,explanation+1,*explanation);
			*message -= *explanation+1;
		}
		else if (errorNum==-108 && errorStr!=MEM_ERR)
			GetRString(explanation,MEM_ERR);
		else
			*explanation = 0;
		
		if (errorNum) ComposeRString(techStuff,WU_FMT,errorNum,file,line);
		else ComposeRString(techStuff,WU_NOERR_FMT,file,line);
		PCat(explanation,techStuff);
		SettingsRefN = realSettingsRef;

		Zero(param);
		param.movable = True;
		param.filterProc = DlgFilterUPP;
		param.defaultButton = kAlertStdAlertOKButton;
		param.position = kWindowDefaultPosition;
		ReallyStandardAlert(type,message,explanation,&param);
	}
	return(errorNum);
}

/**********************************************************************
 * DieWithError - die with the given error message
 **********************************************************************/
void DWE(int errorStr,int errorNum,int file,int line)
{
	Str255 fatal;
	Str255 message;
	Str255 number;
	Str255 debugStr;
	
	GetRString(fatal,FATAL);
	GetRString(message,errorStr);
	NumToString((long)errorNum,number);
	ComposeRString(debugStr,FILE_LINE_FMT,file,line);
	MyParamText(fatal,message,number,debugStr);
	if (gAppearanceIsLoaded)
		ComposeStdAlert(kAlertStopAlert,ERR_ASTR+ALRTStringsStrn,P1,P2,P3,P4);
	else
		StopAlert(NON_AM_ERR_ALRT, nil);
	Cleanup();
	ExitToShell();
}

/**********************************************************************
 * DumpData - put up an alert that shows some data.
 **********************************************************************/
void DumpData(UPtr description, UPtr data,int length)
{
	Str255 asAscii;
	Str255 asHex;
	static char hex[]="0123456789abcdef";
	char *ac, *hx;
	char *from;
	
	if (length > 255/2) length = 255/2;
	
	/*
	 * prepare display strings
	 */
	*asAscii = 2*length;
	*asHex = 2*length;
	ac=asAscii+1;
	hx=asHex+1;
	for (from=data; from<data+length;from++)
	{
		*ac++ = optSpace;
		if (*from==' ')
			*ac++ = optSpace;
		else if (*from<' ')
			*ac++='.';
		else
			*ac++ = *from;
		*hx++ = hex[((*from)>>4)&0xf];
		*hx++ = hex[(*from)&0xf];
	}
	
	SetDialogFont(kFontIDMonaco);
	MyParamText(description,asAscii,asHex,"");
	switch (Alert(DUMP_ALRT,nil))
	{
		case DEBUG_BUTTON: Debugger(); break;
		case EXIT_BUTTON:  Cleanup(); ExitToShell(); break;
	}
	SetDialogFont(applFont);
} 

/**********************************************************************
 * monitor the heap situation
 **********************************************************************/
long MonitorGrow(Boolean report)
{
	static long memLeft = NSpare*SPARE_SIZE;
	long roomLeft;
	long roomAvailable;
	short i;
	long deficit=0;
	
	if (!DoMonitor) return(0);
	
	roomLeft = 0;
	for (i=0;i<NSpare;i++)
		if (SpareSpace[i]) roomLeft += GetHandleSize_(SpareSpace[i]);
	
	if (roomLeft < memLeft)
	{
#ifdef LDAP_ENABLED
		MonitorLDAPCodeGrow(true);
#endif
		if (report && (memLeft==NSpare*SPARE_SIZE || roomLeft+10 K<memLeft || roomLeft<MEM_CRITICAL))
			MemoryWarning();
	}
		
	if (roomLeft < NSpare*SPARE_SIZE)
	{
		memLeft = roomLeft;
#ifdef LDAP_ENABLED
		MonitorLDAPCodeGrow(true);
#endif
		for (i=0;i<sizeof(SpareSpace)/sizeof(Handle);i++)
		{
			ZapHandle(SpareSpace[i]);
			roomAvailable = CompactMem(SPARE_SIZE);
			if (roomAvailable < SPARE_SIZE) roomAvailable = MaxMem(&roomLeft);
			if (roomAvailable > SPARE_SIZE) roomAvailable = SPARE_SIZE;
			MakeGrow(roomAvailable,i);
			if (SpareSpace[i]) deficit += SPARE_SIZE-GetHandleSize_(SpareSpace[i]);
			else deficit += SPARE_SIZE;
		}
	}

#ifdef LDAP_ENABLED
	MonitorLDAPCodeGrow(false);
#endif
	if (MemLastFailed || deficit)
	{
		long total;
		PurgeSpace(&total,&MemLastFailed);
	}
	
	return(deficit);
}

/************************************************************************
 * MemoryWarning - warn the user about how much memory is left.
 ************************************************************************/
void MemoryWarning(void)
{
	Str255 partitionString;
	long currentSize, estSize, spareSize;
	short i;
	
#ifdef THREADING_ON
	/* just cancel what you're doing and set error flag */
	if (InAThread())
	{
		TaskKindEnum taskKind = GetCurrentTaskKind();
		CommandPeriod=True;	
		if (taskKind==CheckingTask) 
			CheckThreadError = memFullErr;
		else
		if (taskKind==SendingTask)
			SendThreadError = memFullErr;
		return;
	}
#endif	
	currentSize = CurrentSize();
	estSize = EstimatePartitionSize(false);
	
	spareSize = 0;
	for (i=0;i<NSpare;i++)
		if (SpareSpace[i]) spareSize += GetHandleSize_(SpareSpace[i]);
	estSize = MAX(estSize,NSpare*SPARE_SIZE-spareSize+currentSize);
	(void) ComposeRString(partitionString,MEM_PARTITION,currentSize/(1 K),
												estSize/(1 K));
	if (ComposeStdAlert(Stop,MEMORY_ALRT,MEM_LOW,MEM_EXPLANATION,partitionString)==MEMORY_QUIT) CommandPeriod=EjectBuckaroo=True;
}

/************************************************************************
 * CurrentSize - how big is our partition?
 ************************************************************************/
uLong CurrentSize(void)
{
	ProcessInfoRec pi;
	ProcessSerialNumber psn;
	uLong size=0;
	
	pi.processInfoLength = sizeof(pi);
	pi.processName = nil;
	pi.processAppSpec = nil;
	if (!GetProcessInformation(CurrentPSN(&psn),&pi))
	{
		return(pi.processSize-44 K);	/* dunno where finder gets 44K, but... */
	}
	else return(DefaultSize());
}

/************************************************************************
 * DefaultSize - how big is Eudora's normal partition?
 ************************************************************************/
uLong DefaultSize(void)
{
	SizeHandle sizeH;
	uLong size=0;
	
	if (sizeH = GetResource_('SIZE',-1))
	{
		size = (*sizeH)->prefSize;
		ReleaseResource_(sizeH);
	}
	return(size);
}

/************************************************************************
 * EstimatePartitionSize - estimate the "right" size for Eudora
 ************************************************************************/
uLong EstimatePartitionSize(Boolean log)
{
	Str31 suffix;
	uLong drain = 0;
	static short strids[] = {IN,OUT,TRASH};
	short id;
	MyWindowPtr	win;
	WindowPtr winWP;
	uLong dFork = 0;
	uLong size;
	FSSpec spec;
	short tocFactor = PrefIsSet(PREF_NO_RF_TOC_BACKUP) ? 1 : 2;
#ifdef DEBUG
	uLong oldDrain;
#endif
	
	GetRString(suffix,TOC_SUFFIX);
	spec.vRefNum = MailRoot.vRef;
	spec.parID = MailRoot.dirId;
	
	for (id=0;id<sizeof(strids)/sizeof(short);id++)
	{
		GetRString(spec.name,strids[id]);
		if (!PrefIsSet(PREF_NEW_TOC) || !(size=FSpRFSize(&spec)))
		{
			PSCat(spec.name,suffix);
			size = tocFactor*FSpDFSize(&spec);
		}
		drain += size/tocFactor;
#ifdef DEBUG
		if (log) ComposeLogS(-1,nil,"\pMailbox: %p: %d",spec.name,size/tocFactor);
#endif
	}
	
	/*
	 * nicknames
	 */
	drain += NickDrain() + ETLDrain();
#ifdef DEBUG
	if (log) ComposeLogS(-1,nil,"\pNicknames: %d",NickDrain());
	if (log) ComposeLogS(-1,nil,"\pEMSAPI: %d",ETLDrain());
#endif
	
	for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
	{
		win = GetWindowMyWindowPtr (winWP);
#ifdef DEBUG
		oldDrain = drain;
#endif
		drain += sizeof(MyWindow) + 1 K + 5 K;	// 5K seems to cover appearance requirements, give or take
		switch (GetWindowKind(winWP))
		{
			case COMP_WIN:
			case MESS_WIN:
				drain += PETEGetMemInfo(PETE,(*Win2MessH(win))->bodyPTE);
				drain += (*Win2MessH(win))->extras.offset;
				drain += 7 K;	// lots of controls in those windows
				break;
			case TEXT_WIN:
				drain += PETEGetMemInfo(PETE,win->pte);
				break;
			case MBOX_WIN:
			case CBOX_WIN:
				if (!(*(TOCHandle)GetMyWindowPrivateData(win))->which)
					drain += GetHandleSize_((TOCHandle)GetMyWindowPrivateData(win));
				break;
			default:
				drain += 5 K;
		}
#ifdef DEBUG
		if (log)
		{
			Str255 title;
			GetWTitle(winWP,title);
			ComposeLogS(-1,nil,"\pMailbox: %p: %d",title,drain-oldDrain);
		}
#endif
	}

	if (IsPowerNoVM())
	{
		FSSpec appSpec;
		
		if (!GetFileByRef(AppResFile,&appSpec))
		{
			dFork = FSpDFSize(&appSpec);
			// Hack!  Hack!  Hack!
			// Fat version is way big, and ppc is about 5/9 of it.
			// The proper way to calculate this is to walk the cfrg resource,
			// but that looks more complicated than it's worth.  SD
			if (dFork > 3 K K)
#if TARGET_CPU_PPC
				dFork = (dFork*5)/9;
#else
				dFork = (dFork*4)/9;
#endif
		}
	}

#ifdef NEVER
	if (RunType!=Production) drain += 2000 K;
#endif
	
	drain = MAX(0,drain - 1500 K);
	return(DefaultSize()+drain+dFork);
}

/**********************************************************************
 * NickDrain - compute drain due to nicknames
 **********************************************************************/
long NickDrain(void)
{
	Str31 name;
	FSSpec folderSpec;
	CInfoPBRec hfi;
	FSSpec spec;
	long drain = 0;

	/*
	 * add the Eudora Nicknames file
	 */
	FSMakeFSSpec(Root.vRef,Root.dirId,GetRString(name,ALIAS_FILE),&spec);
	drain += FSpRFSize(&spec);
	
#ifdef TWO
	/*
	 * add the Eudora Nicknames Cache file
	 */
	FSMakeFSSpec(Root.vRef,Root.dirId,GetRString(name,CACHE_ALIAS_FILE),&spec);
	drain += FSpRFSize(&spec);

#ifdef VCARD
	/*
	 * add the Personal Nicknames file
	 */
	if (HasFeature (featureVCard)) {
		FSMakeFSSpec(Root.vRef,Root.dirId,GetRString(name,PERSONAL_ALIAS_FILE),&spec);
		drain += FSpRFSize(&spec);
	}
#endif
	/*
	 * is there a nicknames folder?
	 */
	if (!SubFolderSpec(NICK_FOLDER,&folderSpec))
	{		
		/*
		 * iterate through all the files, adding them to the list
		 */
		hfi.hFileInfo.ioNamePtr = name;
		hfi.hFileInfo.ioFDirIndex = 0;
		while (!DirIterate(folderSpec.vRefNum,folderSpec.parID,&hfi))
		{
			SimpleMakeFSSpec(folderSpec.vRefNum,folderSpec.parID,name,&spec);
			drain += FSpRFSize(&spec);
		}
	}
#endif
	return(drain);
}

/**********************************************************************
 * create some space for the grow zone function
 **********************************************************************/
void MakeGrow(long howMuch,short which)
{
	Boolean oldMonitor = DoMonitor;
	
	DoMonitor = False;
	if (SpareSpace[which] = NuHandle(howMuch)) **(long **)SpareSpace[which] = 'SPAR';
	DoMonitor = oldMonitor;
}

/**********************************************************************
 * grow that zone...
 **********************************************************************/
pascal long MyGrowZone(unsigned long needed)
{
	long roomLeft;
	long freed=0;
	long theA5=SetCurrentA5();
	Handle dontMove = (Handle)GZSaveHnd();
	short resFile = CurResFile();
	short i;
	
	MemLastFailed = 1 K;
		
	if (SettingsRefN) UseResFile(SettingsRefN);
	if (!MemCanFail)
	{
		for (i=0;i<NSpare;i++)
			if (dontMove!=(Handle)SpareSpace[i] && (roomLeft = InlineGetHandleSize((Handle)SpareSpace[i])))
			{ 
				freed = needed <= roomLeft ? needed : roomLeft;
				SetHandleSize(SpareSpace[i],roomLeft - freed);
			}
		if (!freed && DamagedTOC && DamagedTOC != (TOCHandle)dontMove)
		{
			freed = InlineGetHandleSize((Handle)DamagedTOC);
			ZapHandle(DamagedTOC);
		}
	}
	UseResFile(resFile);
	
	(void) SetA5(theA5);
	return(freed);
}

/************************************************************************
 * CloseUnused - close unused windows
 ************************************************************************/
long CloseUnused(void)
{
	static short kinds[] = {MB_WIN,PH_WIN,TEXT_WIN,ALIAS_WIN,MESS_WIN,COMP_WIN,FILT_WIN,PICT_WIN,PERS_WIN,SIG_WIN,STA_WIN,LINK_WIN};
	WindowPtr	winWP,deadWinWP=nil;
	MyWindowPtr win;
	short kind;
	
	for (kind=0;kind<sizeof(kinds)/sizeof(short);kind++)
	{
		for (winWP=FrontWindow();winWP;winWP=GetNextWindow(winWP))
		{
			win = GetWindowMyWindowPtr (winWP);
			if (GetWindowKind(winWP)==kinds[kind] && win && !IsDirtyWindow (win) && !win->inUse)
				if (kinds[kind]!=MESS_WIN || win->pte!=(*Win2MessH(win))->subPTE) deadWinWP = winWP;
		}
		if (deadWinWP)
		{
			if (!CloseMyWindow(deadWinWP))
			{
				UsingWindow(deadWinWP);		/* couldn't close it ??? */
				deadWinWP = nil;
				kind--;	/* try again with this kind */
			}
			else
			{
				return(1);
			}
		}
	}
	return(0);
}
		
/**********************************************************************
 * FileSystemError - report an error regarding the file system
 **********************************************************************/
int FSE(int context, UPtr name, int realErr, int file,int line)
{
	Str255 text;
	Str255 contextStr;
	Str63 debugStr;
	int offset = 0;
	AlertStdAlertParamRec param;
	extern ModalFilterUPP DlgFilterUPP;
	OSErr err = realErr;
	short realSettingsRef = SettingsRefN;

	if (err!=userCancelled && err!=userCanceledErr)
	{
		SettingsRefN = GetMainGlobalSettingsRefN();
		ComposeRString(text,FSE_NAME_ERR,name,err);
		if (err < wrPermErr) offset += wrPermErr-dirNFErr-1;
		if (err < volGoneErr) offset += volGoneErr+128;
		err += offset;
		err = dirFulErr - err + 2;
		if (err > 37) err = 37;
		GetRString(contextStr,FILE_STRN+err);
		PCat(text,contextStr);
		ComposeRString(debugStr,FILE_LINE_FMT,file,line);
		PCat(text,debugStr);
		GetRString(contextStr,context);
		SettingsRefN = realSettingsRef;

		Zero(param);
		param.movable = True;
		param.filterProc = DlgFilterUPP;
		param.defaultButton = kAlertStdAlertOKButton;
		param.position = kWindowDefaultPosition;
		ReallyStandardAlert(kAlertCautionAlert,contextStr,text,&param);
	}
	return(realErr);
}

/************************************************************************
 * MiniMainLoop - handle only a limited set of events.	returns the event
 * if someone else needs to handle it
 ************************************************************************/
Boolean MiniMainLoopLo(EventRecord *event);
Boolean MiniMainLoop(EventRecord *event)
{
	Boolean result;
	PushGWorld();
	result = MiniMainLoopLo(event);
	PopGWorld();
	return(result);
}
Boolean MiniMainLoopLo(EventRecord *event)
{
	WindowPtr topWinWP=FrontWindow_();

	switch (event->what)
	{
#ifndef NO_KEYUP
		case keyUp:
#endif
		case keyDown:
			if ((event->modifiers&cmdKey) && IsCmdChar(event,'.') ||
					((event->message&charCodeMask)==escChar) &&
					 (((event->message&keyCodeMask)>>8)==escKey))
			{
				FlushEvents(keyDownMask,0);	/* kill all downs if we have a command-period */
#ifdef SPEECH_ENABLED
				if (CancelSpeech ())
					return (false);
#endif
				CommandPeriod = True;
				if (ProgressIsTop()) PressStop();
				return(True);
			}
			break;
		case app4Evt:
			if (((event->message)>>24)&1 == 1)
				DoApp4(topWinWP,event);
			return(False);
		case updateEvt:
			DoUpdate(topWinWP,event);
			return(False);
			break;
		case activateEvt:
			DoActivate(topWinWP,event);
			return(False);
			break;
		case mouseDown:
			if (ModalWindow && ModalWindow==FrontWindow_()) DoMouseDown(ModalWindow,event);
			break;
	}
	

#ifdef CTB
	if (CnH && topWinWP && GetWindowPrivateData(topWinWP)==(long)CnH)
	{
		CMEvent(CnH,event);
		return(False);
	}
#endif

#ifdef NO_KEYUP
	if (HasCommandPeriod()) return(True);
#endif
	
	return(False); 
}

/**********************************************************************
 * 
 **********************************************************************/
Boolean HasCommandPeriod(void)
{
	if (InAThread()) return false;

	if (!InBG)
	{
		if (CheckEventQueueForUserCancel())
		{
			FlushEvents(keyDownMask|keyUpMask|mDownMask|mUpMask,0);	/* kill events on cmd-period */
			if (CancelSpeech ()) return false;
			CommandPeriod = True;
			if (ProgressIsTop()) PressStop();
			return true;
		}
	}
	return false;
}


/************************************************************************
 *
 ************************************************************************/
void FlushHIQ(void)
{
	HostInfoQHandle hiq, nextHIQ;
	for (hiq=HIQ;hiq;hiq=nextHIQ)
	{
		nextHIQ = (*hiq)->next;
		if ((*hiq)->done && !(*hiq)->inUse)
		{
			LL_Remove(HIQ,hiq,(HostInfoQHandle));
			ZapHandle(hiq);
		}
	}
}

/************************************************************************
 * TendNotificationManager - see if an NM rec is active, and if it should
 * be taken down
 ************************************************************************/
void TendNotificationManager(Boolean isActive)
{
	if (MyNMRec)
	{
		long ticksSince = TickCount()-MyNMRec->nmRefCon;
		if (isActive && ticksSince>10)
		{
			NMRemove(MyNMRec);
			if (MyNMRec->nmIcon) DisposeIconSuite(MyNMRec->nmIcon,True);
			ZapPtr(MyNMRec->nmStr);
			ZapPtr(MyNMRec);
		}
	}
	
	if (isActive) AttentionNeeded = false;
}

/**********************************************************************
 * NuHandle - allocate a handle
 **********************************************************************/
void *NuHandle(long size)
{
	void *h;

	RANDOM_FAILURE;

#ifdef NEVER
	if (h = GetResource(POPD_TYPE,POPD_ID))
		ASSERT(!(GetResAttrs(h)&resChanged) ||!(HGetState(h)&0x40));
#endif
  
#ifdef NEVER
	if (MonitorGrow(False)>0) 
	{
#ifdef DEBUG
		if (RunType!=Production) {SysBeep(10); DebugStr("\p;sc;g");}
#endif
#ifdef DEBUG
		if (RunType==Debugging || size>100)
#endif
		{
			LMSetMemErr(-108);
			return(nil);
		}
	}
#endif

	MemCanFail = True;
	h = NewHandle(size);
	MemCanFail = False;
	if (!h && MemLastFailed == 1 K || MemLastFailed > size) MemLastFailed = MIN(size,1 K);
	return(h);
}

/**********************************************************************
 * NuPtr - allocate a pointer
 **********************************************************************/
void *NuPtr(long size)
{
	void *h;

	RANDOM_FAILURE;
	
	MemCanFail = True;
	MonitorGrow(False);
	h = NewPtr(size);
	MemCanFail = False;
	if (!h && MemLastFailed == 1 K || MemLastFailed > size) MemLastFailed = MIN(size,1 K);
	return(h);
}

/**********************************************************************
 * NuPtrClear - allocate a zeroed pointer
 **********************************************************************/
void *NuPtrClear(long size)
{
	void *h;

	RANDOM_FAILURE;

	MemCanFail = True;
	MonitorGrow(False);
	h = NewPtrClear(size);
	MemCanFail = False;
	return(h);
}


/**********************************************************************
 * get an event, giving time to system tasks as necessary
 **********************************************************************/
Boolean GrabEvent(EventRecord *theEvent)
{
	Boolean result;
	
	/*
	 * get the event
	 */
#ifndef NO_KEYUP
	FlushEvents(keyUpMask,0);
#endif
	DragFxxkOff = False;
	result = WNE(everyEvent,theEvent,InBG ? 300L : ((Typing||ActiveSearchCount)?0:10L));
	DragFxxkOff = theEvent->what!=nullEvent;

	// make sure the rest of Eudora knows if we're typing
	if ((theEvent->what==autoKey || theEvent->what==keyDown))
		if (theEvent->modifiers&cmdKey)
			TypingTicks = 0;
		else
			TypingTicks = TickCount();

	Typing = TickCount()-TypingTicks<GetRLong(TYPING_THRESH);
	TypingRecently = Typing || TickCount()-TypingTicks<GetRLong(TYPING_RECENTLY_THRESH);

	/*
	 * handle special keys
	 */
	if (theEvent->what==keyDown || theEvent->what==autoKey)
		SpecialKeys(theEvent);
	
	/*
	 * global type-2-select buffer
	 */
	Type2Select(theEvent);
	
	/*
	 * gd dialog manager
	 */
	if (!result && MyIsDialogEvent(theEvent))
	{
		Typing = False;
		DoModelessEvent(GetDialogFromWindow(FrontWindow_()),theEvent);
	}

	return(result);
}

/**********************************************************************
 * PantyTrack - handle drags
 **********************************************************************/
pascal OSErr PantyTrack(DragTrackingMessage message, WindowPtr qWinWP, Ptr handlerRfCon, DragReference drag)
{
	OSErr err=dragNotAcceptedErr;
	MyWindowPtr qWin = GetWindowMyWindowPtr(qWinWP);

	if (DragFxxkOff) return(dragNotAcceptedErr);
	
	Dragging++;
	if (IsMyWindow(qWinWP))
	{	
		if (!DraggingWazoo(qWin,message,drag,&err))
			if (qWin->drag) err = (*qWin->drag)(qWin,message,drag);
		if (err==dragNotAcceptedErr) err = PeteDrag(qWin,message,drag);
		else if (!qWin->butch) PeteDrag(nil,message,drag);
	}
	else if (IsPlugwindow(qWinWP))
		err = PlugwindowDrag(qWinWP,message,drag);
	else err = PeteDrag(nil,message,drag);
	Dragging--;
	
	return(err==kAcceptableGraphicDrag ? noErr : err);
}
pascal OSErr PantyReceive(WindowPtr qWin, Ptr handlerRfCon, DragReference drag)
{
	OSErr err;
	DECLARE_UPP(PantyTrack,DragTrackingHandler);

	INIT_UPP(PantyTrack,DragTrackingHandler);
	Dragging++;
	err = PantyTrack(0x0fff,qWin,handlerRfCon,drag);
	Dragging--;
	return(err==kAcceptableGraphicDrag ? noErr : err);
}

/************************************************************************
 * WNE - call WaitNextEvent.	Make sure not to call from thread.
 ************************************************************************/
Boolean WNE(short eventMask,EventRecord *event,long sleep)
{
	EventRecord localEvt;
	Boolean result;
#ifdef THREADING_ON
	static Boolean lastResult;
#endif
	
#ifdef THREADING_ON
	if (InAThread ())
	{
		MyYieldToAnyThread();
		return false;
	}
	else
		if (!lastResult && !Typing)
			MyYieldToAnyThread ();		
#endif
	
#ifdef THREADING_ON
		result = WNELo(eventMask,&localEvt,GetNumBackgroundThreads() ? 0 : sleep);
#else
		result = WNELo(eventMask,&localEvt,sleep);
#endif

#ifdef THREADING_ON
	lastResult = result;
#endif

	ThreadYieldTicks = TickCount();

	result = FMBEventFilter(&localEvt,result);
	if (ToolbarShowing()) result = TBEventFilter(&localEvt,result);
	if (PETE) PETEEventFilter(PETE,&localEvt);
	if (!PlugwindowEventFilter(&localEvt)) return(false);
#ifdef TWO // frontier
	if (localEvt.what==keyDown && localEvt.modifiers&cmdKey &&
			(localEvt.message&charCodeMask)=='.' && SharedScriptRunning())
	{
		CancelSharedScript();
		return(False);
	}
#endif

	if (event) *event=localEvt;
	
	return(result);
}

/************************************************************************
 * WNELo - call WaitNextEvent and do low-level processing
 ************************************************************************/
Boolean WNELo(short eventMask,EventRecord *event,long sleep)
{
	EventRecord localEvt;
	Boolean result;
	ProcessSerialNumber me, him;
	static Boolean mask;
	Boolean foreground;
#if __profile__
	//short profilerWas = ProfilerGetStatus();
#endif
	static short oldmods;
	
	if (eventMask && event && !sleep) sleep = 4;
#if __profile__
	//ProfilerSetStatus(False);
#endif
	
	MightSwitch();

#ifndef DRAG_GETOSEVT
	if (Dragging)
	{
		result = False;
		Zero(localEvt);
		localEvt.what = nullEvent;
		localEvt.when = TickCount();
	}
#else
	if (Dragging)
	{
		result = GetNextEvent(eventMask,&localEvt);
		if (!result) UpdateAWindow();
	}
#endif
	else
		result = WaitNextEvent(eventMask,&localEvt,sleep,MousePen);
	
	if (localEvt.what!=nullEvent) NonNullTicks = TickCount();

#define HaveGetLastActivity ((GestaltBits(gestaltPowerMgrAttr)&(1<<gestaltPMgrDispatchExists))!=0)
#ifdef HAVE_GETLASTACTIVITY	
	if (localEvt.what==mouseDown || localEvt.what==keyDown || localEvt.what==keyUp || localEvt.what==autoKey)
	{
		if (HaveGetLastActivity) GlobalIdleTicks = 0;
	}
	else
	{
		ActivityInfo info;
		OSErr err;
		
		if (HaveGetLastActivity)
		{
			info.ActivityTime = 0;
			info.ActivityType = UsrActivity;
			if (!(err=GetLastActivity(&info)))
				GlobalIdleTicks = TickCount()-info.ActivityTime;
		}
	}
#endif //HAVE_GETLASTACTIVITY
						
	//if (RunType!=Production && localEvt.what) Dprintf("\p%d %x %x;g",localEvt.what,localEvt.message,localEvt.modifiers);
#if __profile__
	//ProfilerSetStatus(profilerWas);
#endif
	AfterSwitch();
	YieldTicks = TickCount();
	
	if (oldmods!=localEvt.modifiers)
	{
		oldmods = localEvt.modifiers;
		SFWTC = True;
		NonNullTicks = TickCount();
	}
	
	GetCurrentProcess(&me);
	GetFrontProcess(&him);
	SameProcess(&me,&him,&foreground);
	InBG = !foreground;
	NoInitialCheck = NoInitialCheck || (localEvt.what!=keyDown && !InBG && (oldmods&shiftKey)!=0);
	
#ifndef NO_KEYUP
	if (!InBG && !mask)
	{
		mask = True;
		SetEventMask(LMGetSysEvtMask()|keyUpMask);
	}
#endif
	
	if (event) *event=localEvt;
	
	return(result);
}

/**********************************************************************
 * NeedYield
 **********************************************************************/
Boolean NeedYield(void)
{
	short numThreads = GetNumBackgroundThreads();
	long elapsedTicks = TickCount() - ThreadYieldTicks;
	long vFactor = 	(InBG ? 1 : ((VicomIs && VicomFactor>0) ? VicomFactor : 1));
	long yieldInterval = (InBG ? BgYieldInterval : FgYieldInterval) / ((numThreads ? numThreads : 1) * vFactor);
	
#ifdef HAVE_GETLASTACTIVITY
	if (GlobalIdleTicks<60) yieldInterval = 0;
	else if (GlobalIdleTicks<120) yieldInterval /= 2;
#endif //HAVE_GETLASTACTIVITY
	
//	ASSERT(InBG ? (elapsedTicks < 120) : 1); // are we hogging time somewhere?
	if (numThreads && !Typing) ToolbarIdleControls();
	if (EventPending() || (elapsedTicks > yieldInterval))
		return true;
	return false;
}

#ifdef DRAG_GETOSEVT
/************************************************************************
 * UpdateAWindow - update a window
 ************************************************************************/
void UpdateAWindow(void)
{
	WindowPtr theWindow;
	
	for (theWindow=FrontWindow();theWindow;theWindow=GetNextWindow(theWindow))
	{
		if (IsKnownWindowMyWindow(theWindow) && HasUpdateRgn(theWindow))
		{
			UpdateMyWindow(theWindow);
			break;
		}
	}
}
#endif

#ifdef DEBUG

#define MemCorrupt(map,offset,string) MemCorruptLo(map,offset,string,__LINE__)
void MemCorruptLo(UHandle map,long offset,PStr msg,short line);
#define AtOffset(map,offset,type)	*(type*)(*map+offset)

/**********************************************************************
 * CheckHandle - check a handle for validity
 **********************************************************************/
void CheckHandle(UHandle map,long offset,long size,THz hz,PStr string)
{
	Handle h = *(Handle *)(*map+offset);
	OSErr err = noErr;
	
	/*
	 * in range?
	 */
	if (offset+3>GetHandleSize(map)) {MemCorrupt(map,offset,string);return;}
	
	if (!h) return;	/* nil ok */
	
	/*
	 * handle a multiple of four?
	 */
	if ((uLong)h%4) {MemCorrupt(map,offset,string); return;}
	
	/*
	 * handle in the zone?
	 */
	if ((uLong)h<(uLong)&hz->heapData || (uLong)h>(uLong)hz->bkLim) {MemCorrupt(map,offset,string); return;}
	
	if (!*h) return;	/* purged ok */
	
	/*
	 * pointer a mult of four?
	 */
	if ((uLong)*h%4) {MemCorrupt(map,offset,string); return;}
	
	/*
	 * pointer in the zone?
	 */
	if ((uLong)*h<(uLong)&hz->heapData || (uLong)*h>(uLong)hz->bkLim) {MemCorrupt(map,offset,string); return;}
	
	/*
	 * size correct?
	 */
	if (size>=0 && GetHandleSize(h)!=size) {MemCorrupt(map,offset,string);return;}
	
	/*
	 * handle looks ok to me
	 */
	return;
}

/**********************************************************************
 * 
 **********************************************************************/
void MemCorruptLo(UHandle map,long offset,PStr msg,short line)
{
	Str255 db;
	Str255 scratch;
	
	/*
	 * turn on logging
	 */
	DebugStr("\p;log MemCorrupt;g");
	
	/*
	 * display line # & message
	 */
	NumToString(line,scratch);
	PCopy(db,scratch);
	PCatC(db,' ');
	PCat(db,msg);
	PCat(db,"\p;");
	
	/*
	 * display area around the offending data
	 */
	PCat(db,"\pdm #");
	NumToString((uLong)*map+offset,scratch);
	PCat(db,scratch);
	PCat(db,"\p-20 40;#");
	
	/*
	 * print the address itself
	 */
	NumToString((uLong)*map+offset,scratch);
	PCat(db,scratch);
	
	/*
	 * turn off logging
	 */
	PCat(db,"\p;log");
	
	/*
	 * now do it
	 */
	DebugStr(db);
}
#endif

/**********************************************************************
 * SetupEventHandlers - setup carbon event handlers
 **********************************************************************/
static void SetupEventHandlers(void)
{
	// Scroll wheel support
	EventTypeSpec	wheelEventSpec = 
		{ kEventClassMouse,kEventMouseWheelMoved };
	// Services menu support
	EventTypeSpec	servicesEventSpec[] =
	{
		{ kEventClassService, kEventServiceCopy }, 
		{ kEventClassService, kEventServicePaste },
		{ kEventClassService, kEventServiceGetTypes }
	};
	// Application target
	EventTargetRef appTarget = GetApplicationEventTarget();
	
	// Scroll wheel handler
	InstallEventHandler(appTarget,NewEventHandlerUPP(WheelHandlerProc),sizeof(wheelEventSpec)/sizeof(EventTypeSpec),&wheelEventSpec,nil,nil);

	// Services menu handler
	if (!PrefIsSet(PREF_NO_SERVICES)) InstallEventHandler(appTarget,NewEventHandlerUPP(ServicesHandlerProc),sizeof(servicesEventSpec)/sizeof(EventTypeSpec),&servicesEventSpec,nil,nil);
}

/**********************************************************************
 * WheelHandlerProc - carbon event handler for scroll wheel
 **********************************************************************/
static pascal OSStatus WheelHandlerProc (EventHandlerCallRef nextHandler,EventRef event,void *data)
{
#pragma unused(nextHandler,data)
	if ( ++gEnterWheelHandlerCount == 1 ) {
		EventMouseWheelAxis	axis;
		SInt32	deltaH=0,deltaV=0;
		WindowPtr winWP;
		MyWindowPtr	win;
		Boolean	vertical;
		short	lines;
		PETEHandle pte;
		HIPoint	hiPt;
		Point	pt;

		// Find window mouse is over
		GetEventParameter(event,kEventParamMouseLocation,typeHIPoint,nil,sizeof(hiPt),nil,&hiPt);
		pt.h = hiPt.x;
		pt.v = hiPt.y;
		FindWindow(pt,&winWP); 
		win = GetWindowMyWindowPtr(winWP);

		if (win)
		{
			GetEventParameter(event,kEventParamMouseWheelAxis,typeMouseWheelAxis,nil,sizeof(axis),nil,&axis);
			vertical = axis==kEventMouseWheelAxisY;	
			GetEventParameter(event,kEventParamMouseWheelDelta,typeLongInteger,nil,sizeof(deltaH), nil,
				vertical ? &deltaV : &deltaH);
		
			lines = GetRLong(SCROLL_WHEEL_LINES);
			if (lines > 0 && lines <= 100)
			{
				deltaH *= lines;
				deltaV *= lines;
			}
		
			// See if mouse is over a pte
			SetPort(GetWindowPort(winWP));
			GlobalToLocal(&pt);
			for (pte=win->pteList;pte;pte=PeteNext(pte))
			{
				Rect	rPTE;
				
				PeteRect(pte,&rPTE);
				if (PtInRect(pt,&rPTE))
					break;	// Found one!
			}
				
			if (pte)
			{
				short	n,i;
				
				n = deltaV >= 0 ? deltaV : -deltaV;
				for(i=n;i--;)
					PeteScroll(pte,0,deltaV>0?pseLineUp:pseLineDn);
				if (IsBoxWindow(winWP))
				{
					TOCHandle tocH = Win2TOC(win);
					if (pte==(*tocH)->previewPTE && PreviewReadFocus) BeenThereDoneThat(tocH,-1);
				}
			}
			else if (win->pView && deltaV && PtInRect(pt,&win->pView->bounds))
				LVScroll(win->pView,deltaV);
			else if (win->scrollWheel && (win->scrollWheel)(win,pt,deltaH,deltaV))
				; 
			else
				ScrollIt(win,deltaH,deltaV);
		}
	}
	gEnterWheelHandlerCount--;
	return noErr;
}

/**********************************************************************
 * AppendOSTypeToCFArray - add data type to CF array as CF string
 **********************************************************************/
static void AppendOSTypeToCFArray(CFMutableArrayRef arrayRef,OSType type)
{
	CFStringRef typeString = CreateTypeStringWithOSType(type);
	if (typeString)
	{
		CFArrayAppendValue (arrayRef,typeString);
		CFRelease (typeString);
	}	
}

/**********************************************************************
 * ServicesHandlerProc - carbon event handler for Services menu
 **********************************************************************/
static pascal OSStatus ServicesHandlerProc (EventHandlerCallRef nextHandler,EventRef event,void *data)
{
#pragma unused(nextHandler,data)
	WindowPtr winWP = FrontWindow_();
	MyWindowPtr	win = GetWindowMyWindowPtr(winWP);
	OSStatus err = noErr;
	
	if (win && win->pte)
	{	
		UInt32 eventKind = GetEventKind(event);
		long start,stop;
		Boolean	selection;
		CFMutableArrayRef   copyTypes, pasteTypes;
		ScrapRef    serviceScrap;

		selection = !PeteGetTextAndSelection(win->pte,nil,&start,&stop) && stop!=start;
		switch (eventKind)
		{
			case kEventServiceGetTypes:
				// Add the data types that we support to the copy/paste data-type arrays
				if (selection)
				{
					// Do copy only if something is selected
					if (!GetEventParameter(event,kEventParamServiceCopyTypes,typeCFMutableArrayRef, 
						NULL,sizeof (CFMutableArrayRef),NULL,&copyTypes))
					{
						// We copy only text
						AppendOSTypeToCFArray(copyTypes,kScrapFlavorTypeText);
					}
				}
				if (!GetEventParameter(event,kEventParamServicePasteTypes,typeCFMutableArrayRef, 
					NULL,sizeof (CFMutableArrayRef),NULL,&pasteTypes))
				{
					// We paste text, styl, or PICT
					AppendOSTypeToCFArray(pasteTypes,kScrapFlavorTypeText);
					AppendOSTypeToCFArray(pasteTypes,kScrapFlavorTypeTextStyle);
					AppendOSTypeToCFArray(pasteTypes,kScrapFlavorTypePicture);
					AppendOSTypeToCFArray(pasteTypes,'RTF ');
					AppendOSTypeToCFArray(pasteTypes,'TIFF');
				}
				break;

			case kEventServiceCopy:
				if (!selection)
					//	Nothing to copy
					break;
				// Fall through
			case kEventServicePaste:
				// Get the service scrap
				if (!GetEventParameter(event,kEventParamScrapRef,typeScrapRef,
					NULL,sizeof(ScrapRef),NULL,&serviceScrap))
				{
					PETEUseScrap(PETE,serviceScrap);
					// Make sure it's not locked. We allow text services to change
					// read-only messages.
					if (eventKind==kEventServicePaste) PeteLock(win->pte,kPETECurrentSelection,kPETECurrentSelection,peNoLock);
					err = PeteEdit(win,win->pte,eventKind==kEventServiceCopy?peeCopy:peePaste,&MainEvent);
					PETEUseScrap(PETE,nil);
				}
				break;
		}
	}
	return err;
}

#pragma segment MacOS
#pragma segment StdC
#pragma segment Hesiod
#pragma segment MiscLibs
#pragma segment ICGlue
