/* 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 "compact.h"
#define FILE_NUM 8
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */

#pragma segment CompMng
/************************************************************************
 * private functions
 ************************************************************************/
	void CompSwitchFields(MessHandle messH, Boolean forward);
	void ScrollWinRect(MyWindowPtr win, Rect *scroll, Rect *good, short offset);
	void DrawCompWindowBars(MyWindowPtr win); /* MJN *//* new routine */
	uLong QueueDate2Secs(UPtr timeStr,UPtr dateStr);
	Rect *CompIconRect(Rect *r,MessHandle messH,short index);

void CompUpdateScore(MyWindowPtr win);
void CopyExtraSigs(MenuHandle onto);
OSErr CleanseStationery(MyWindowPtr win);
short SigIconId(MessHandle messH);
short SigMenuItem(MessHandle messH);
Boolean GetSigRect(MyWindowPtr win,Rect *pr);
Boolean GetAttachRect(MyWindowPtr win,Rect *pr);
#ifdef VCARD
Boolean GetVCardRect (MyWindowPtr win, Rect *pr);
#endif
Boolean GetAnalRect(MyWindowPtr win,Rect *pr);
void AttachMenu(MyWindowPtr win);
void SigMenu(MyWindowPtr win);
void SigMenuHelp(MyWindowPtr win, Rect *r);
void AttMenuHelp(MyWindowPtr win, Rect *r);
void RefreshSigButton(MessHandle messH);
#ifdef VCARD
void VCardBtnHelp(MyWindowPtr win,Rect *r);
#endif
OSErr QueueSizeWarning(MessHandle messH);
uLong SigFIDByName(PStr string);
OSErr SigSpecByName(PStr string,FSSpecPtr spec);
OSErr CompDrop(MyWindowPtr win,DragReference drag);
void AddrSelect(MessHandle messH);
void CompTranslatorHelp(Rect *r,short i);
void CompActivate(MyWindowPtr win);
OSErr AddSigPopup(MessHandle messH);
OSErr AddAttachPopup(MessHandle messH);
#ifdef VCARD
OSErr AddVCardButton (MessHandle messH);
#endif
OSErr AddAnalIcon(MessHandle messH);
void RefreshSigPopup(MyWindowPtr win);
void PersPopup(MyWindowPtr win,Point where);
OSErr CompAddPersPopup(MyWindowPtr win);
pascal Boolean SendStyleWarningFilter(DialogPtr dgPtr,EventRecord *event,short *item);
void CompSendBtnUpdate(MyWindowPtr win);
void RedrawPersPopup(MessHandle messH);
OSErr InsertGraphicFile(MyWindowPtr win,FSSpecPtr spec);
OSErr SendStyleWarning(MessHandle messH);
#ifdef USECMM_BAD
OSErr	CompBuildCMenu( MyWindowPtr win, EventRecord* contextEvent, CMenuInfoHndl* specCMenuInfo );
#endif

/* NOTE: the number of elements in the arrays fBits[] and icons[] must be the same
					as the value of the define ICON_BAR_NUM (defined in compact.h) */
#ifdef TWO
	static long fBits[]={-OPT_COMP_TOOLBAR_VISIBLE,FLAG_CAN_ENC,FLAG_BX_TEXT,FLAG_WRAP_OUT,FLAG_KEEP_COPY,FLAG_RR/*,FLAG_SIGN,FLAG_ENCRYPT*/};
	static short icons[]={FORMATBAR_ICON,QP_SICN,BX_TEXT_SICN,WRAP_SICN,KEEPCOPY_SICN,RR_SICN/*,SIGN_SICN,ENCRYPT_SICN*/};
#else
	static long fBits[]={FLAG_CAN_ENC,FLAG_BX_TEXT,FLAG_WRAP_OUT,FLAG_KEEP_COPY,FLAG_SIG};
	static short icons[]={QP_SICN,BX_TEXT_SICN,WRAP_SICN,KEEPCOPY_SICN,SIGNATURE_SICN};
#endif


/* #define ICON_BAR_NUM (sizeof(fBits)/sizeof(long)) */
/* MJN *//* moved define for ICON_BAR_NUM to compact.h */
#define I_WIDTH 24
#define I_HEIGHT 24 /* MJN *//* text formatting toolbar */

/**********************************************************************
 * CompClose - close a composition window.	save the contents, iff
 * they're dirty.
 **********************************************************************/
Boolean CompClose(MyWindowPtr win)
{
	WindowPtr	winWP = GetMyWindowWindowPtr(win);
	MessType **messH = (MessType **)GetWindowPrivateData(winWP);
	int which;
	Boolean queued = IsQueued((*messH)->tocH,(*messH)->sumNum);
	Boolean nbk = SumOf(messH)->length==0 && !queued;

	if (!GrowZoning) win->isDirty = win->isDirty || PeteIsDirtyList(win->pte);

	if (win->isDirty && !NoSaves && !GrowZoning)
	{
		which = IsWindowVisible (winWP) ? WannaSave(win) : WANNA_SAVE_SAVE;
		if (which==CANCEL_ITEM) return(False);
		else if (which == WANNA_SAVE_SAVE)
		{
			if (!SaveComp(win)) return(False);
			nbk = False;
		}
	}
	
	(*messH)->persGraphic = nil;
	
	if (!GrowZoning && nbk)
		FixSourceStatus((*messH)->tocH,(*messH)->sumNum);	/* in case we're deleting a reply, set orig state back */
	
	LL_Remove(MessList,messH,(MessType **));
	(*(*messH)->tocH)->sums[(*messH)->sumNum].messH = nil;
	
	if (!GrowZoning)
	{
		if (nbk)
		{
			if (MessOptIsSet(messH,OPT_HAS_SPOOL) && !(*messH)->hStationerySpec) RemSpoolFolder(SumOf(messH)->uidHash);
			DeleteSum((*messH)->tocH,(*messH)->sumNum);
		}
	}
	AccuZap((*messH)->extras);
	ZapHandle(messH);
	if (queued) SetSendQueue();
	return(True);
}

/************************************************************************
 * QueueMessage - queue up a message
 ************************************************************************/
OSErr QueueMessage(TOCHandle tocH,short sumNum,SendTypeEnum st,long secs, Boolean noSpell, Boolean analDelay)
{
	MessHandle messH = (*tocH)->sums[sumNum].messH;
	OSErr err = userCancelled;
	short state = secs ? TIMED : QUEUED;
	Boolean stripWithPrejuidice;
	long oldFlags, oldOpts;
	short	historyAB;
	
	// (jp) 7/9/98  Let's cache recently used addresses and save 'em to disk
	if (HasFeature (featureNicknameWatching) && messH && !PrefIsSet (PREF_NICK_CACHE)) {
		CompGatherRecipientAddresses (messH, true);
		historyAB = FindAddressBookType (historyAddressBook);
		if (historyAB >= 0)
			SaveIndNickFile (historyAB, true);
	}
	
	if (messH && (*messH)->hStationerySpec)
	{
		if (SaveComp((*messH)->win)) err = noErr;
		return err;
	}

	if (!secs) secs = GMTDateTime();
	
	if (st!=kEuSendNever && messH)
	{
		// first, figure out where we stand wrt rich text
		oldFlags = SumOf(messH)->flags;
		oldOpts = SumOf(messH)->opts;
		
		SetMessRich(messH);
		ClearMessOpt(messH,OPT_BLOAT);
		ClearMessOpt(messH,OPT_STRIP);
		stripWithPrejuidice = MessOptIsSet(messH,OPT_JUST_EXCERPT);
		if (!stripWithPrejuidice)
		{
			switch (GetPrefLong(PREF_SEND_STYLE))
			{
				case ssBloat: SetMessOpt(messH,OPT_BLOAT); break;
				case ssStrip:	SetMessOpt(messH,OPT_STRIP); break;
			}
		
			// now ask the user if need be
			if (MessIsRich(messH) && PrefIsSet(PREF_WARN_RICH) && !MessOptIsSet(messH,OPT_BULK))
				switch (SendStyleWarning(messH)-1)
				{
					case ssLimit: return(userCanceledErr); break;
					case ssBloat: SetMessOpt(messH,OPT_BLOAT); ClearMessOpt(messH,OPT_STRIP); break;
					case ssStrip: ClearMessOpt(messH,OPT_BLOAT); SetMessOpt(messH,OPT_STRIP); break;
					default: ClearMessOpt(messH,OPT_BLOAT); ClearMessOpt(messH,OPT_STRIP); break;
				}
		}
		
		// if flags changed, re-save
		if (oldFlags != SumOf(messH)->flags || oldOpts != SumOf(messH)->opts)
			(*messH)->win->isDirty = true;
	}
	
	if (st==kEuSendNever)
	{
		if (IsQueued(tocH,sumNum))
			SetState(tocH,sumNum,SENDABLE);
		err = noErr;
	}
	else if (!messH ||
					 (SumOf(messH)->length!=0 && !(*messH)->win->isDirty)
					 || SaveComp((*messH)->win))
	{
		if ((*tocH)->sums[sumNum].state == UNSENDABLE)
		{
			WarnUser(CANT_QUEUE,0);
			err = CANT_QUEUE;
		}
		else
		{
			if (PrefIsSet(PREF_SUBJECT_WARNING) &&
							!*(*tocH)->sums[sumNum].subj &&
							(!Mom(QUEUE_BTN,0,PREF_SUBJECT_WARNING,R_FMT,SUBJECT_WARNING)))
				err = CANT_QUEUE;
			else if (messH && AnalWarning(messH))
				err = CANT_QUEUE;
			else if (messH && QueueSizeWarning(messH))
				err = CANT_QUEUE;
#ifdef WINTERTREE
			else if (HasFeature (featureSpellChecking) && !noSpell && messH && !MessOptIsSet(messH,OPT_BULK) && SpellOptOnQueue && SpellAnyWrongHuh(TheBody) && ComposeStdAlert(Caution,SPELL_WARNING)!=1)
				err = CANT_QUEUE;
#endif
			else if (messH && (*messH)->hTranslators && (EMSR_NOW!=ETLCanTransOut(messH) || 
				ETLQueueMessage(messH) || ((*messH)->win->isDirty && !SaveComp((*messH)->win))))
				err = CANT_QUEUE;
			else if (!messH || CloseMyWindow(GetMyWindowWindowPtr((*messH)->win)))
			{
				// Moodwatch queue delay
				if (analDelay && AnalDelayOutgoing() && state!=TIMED && (st==kEuSendNow || st==kEuSendNext))
				{
					AnalBox(tocH,sumNum,sumNum);
					if ((*tocH)->sums[sumNum].score > GetRLong(ANAL_DELAY_LEVEL))
					{
						st = kEuSendLater;
						state = TIMED;
						secs += 60*GetRLong(ANAL_DELAY_MINUTES);
					}
				}
				SetState(tocH,sumNum,state);
				TimeStamp(tocH,sumNum,secs,ZoneSecs());
				err = noErr;
				if (st==kEuSendNow)
				{
					err = XferMail(False,True,False,False,True,0);
				}
			}
		}
	}
	SetSendQueue();
	return(err);
}

/************************************************************************
 * SendStyleWarning - ask the user what to do about styles
 ************************************************************************/
OSErr SendStyleWarning(MessHandle messH)
{
	Str255 messTitle;
	MyWindowPtr	dlogWin;
	DialogPtr dlog;
	short item;
	DECLARE_UPP(SendStyleWarningFilter,ModalFilter);

	INIT_UPP(SendStyleWarningFilter,ModalFilter);
	MyGetWTitle(GetMyWindowWindowPtr((*messH)->win),messTitle);
	MyParamText(messTitle,nil,nil,nil);

	dlogWin = GetNewMyDialog(SEND_STYLE_ALRT,nil,nil,InFront);
	if (!dlogWin)
	{
		WarnUser(MEM_ERR,ResError());
		return(ssLimit+1);
	}
	
	dlog = GetMyWindowDialogPtr (dlogWin);
	
	SetPort_(GetWindowPort(GetDialogWindow(dlog)));
	
	// Set the default button
	item = GetPrefLong(PREF_SEND_STYLE)+1;
	SetDialogDefaultItem(dlog,item);
			
	/*
	 * put up the alert
	 */
	AutoSizeDialog(dlog);
	StartMovableModal(dlog);
	ShowWindow(GetDialogWindow(dlog));
	SetMyCursor(arrowCursor);
	
	MovableModalDialog(dlog,SendStyleWarningFilterUPP,&item);

	//done
	EndMovableModal(dlog);
	DisposeDialog_(dlog);
	
	return(item);
}

/************************************************************************
 * SendStyleWarningFilter - filter for normal dialogs
 ************************************************************************/
pascal Boolean SendStyleWarningFilter(DialogPtr dgPtr,EventRecord *event,short *item)
{
	Boolean result;
	
	if (event->what==keyDown || event->what==autoKey) SpecialKeys(event);
	if (event->what==keyDown || event->what==autoKey)
	{
		short key = event->message & charCodeMask;
		if (!(event->modifiers&cmdKey))
			switch (key)
			{
				case enterChar:
				case returnChar:
					*item = GetPrefLong(PREF_SEND_STYLE)+1;
					return(True);
					break;
			}
	}
	else if (event->what==activateEvt || event->what==updateEvt)
		return(false);	// avoid hilitebuttonone
	
	result = DlgFilter(dgPtr,event,item);
	if (*item==CANCEL_ITEM) *item = ssLimit+1;
	return(result);
}
	

/**********************************************************************
 * QueueSizeWarning - check the size of a message, and warn the user if excessive
 **********************************************************************/
OSErr QueueSizeWarning(MessHandle messH)
{
	uLong max = GetRLong(MAX_MESSAGE_SIZE);
	uLong size;
	
	
	if (max && max<(size=ApproxMessageSize(messH)))
	{
		Boolean notOK;
		notOK = (Aprintf(YES_CANCEL_ALRT,Caution,SIZE_WARNING,size)!=1);
		if (notOK) return(CANT_QUEUE);
	}
	return(noErr);
}

/**********************************************************************
 * ApproxMessageSize - return approximate size of message, in K
 **********************************************************************/
uLong ApproxMessageSize(MessHandle messH)
{
	long size = (SumOf(messH)->length*41)/40;
	FSSpec spec;
	short index;
	long oneSize;
	
	for (index=1;;index++)
	{
		if (GetIndAttachment(messH,index,&spec,nil)) break;
		if (FSpIsItAFolder(&spec))
			FolderSizeHi(spec.vRefNum,SpecDirId(&spec),&oneSize);
		else
			oneSize = FSpFileSize(&spec);
		size += (4*oneSize)/3;
	}
	
	return(RoundDiv(size,1024));
}


/**********************************************************************
 * CountAttachments - count the attachments in a message
 **********************************************************************/
short CountAttachments(MessHandle messH)
{
	short nAttach;
	FSSpec spec;
	
	for (nAttach=0;1!=GetIndAttachment(messH,nAttach+1,&spec,nil);nAttach++);
	return(nAttach);
}

/************************************************************************
 * CompAttach - attach a document to a message
 ************************************************************************/
void CompAttach(MyWindowPtr win, Boolean insertDefault)
{
	CompAttachNav (win, insertDefault);
}


/************************************************************************
 * InsertGraphicFile - insert a graphic into the current window
 ************************************************************************/
OSErr InsertGraphicFile(MyWindowPtr win,FSSpecPtr spec)
{
	long spot;
	OSErr err = noErr;
	
	// (jp) Be a little more paranoid of the window we're passed, just in case this is _ever_
	//			called for windows other than composition windows
	if (IsMyWindow (GetMyWindowWindowPtr(win)) && win->pte)
	{
		UseFeature (featureStyleGraphics);
		CompReallyPreferBody(win);
		PetePrepareUndo(win->pte,peUndoPaste,-1,-1,&spot,nil);
		
		if (!(err = PeteInsertChar(win->pte,-1,' ',nil)))
			err = PeteFileGraphicRange(win->pte,spot,spot+1,spec,fgDisplayInline);

		if (!err)
		{
			PeteCalcOn(win->pte);
			PeteFinishUndo(win->pte,peUndoPaste,spot,spot+1);
		}
		else
		{
			PeteKillUndo(win->pte);
			WarnUser(PETE_ERR,err);
		}
	}
	return(err);
}

/**********************************************************************
 * CompReallyPreferBody - I'd really rather do something in the body now
 **********************************************************************/
void CompReallyPreferBody(MyWindowPtr win)
{
	if (GetWindowKind(GetMyWindowWindowPtr(win))==COMP_WIN)
	if (CompHeadCurrent(win->pte))
	{
		HeadSpec hs;
		NicknameWatcherFocusChange (win->pte); /* MJN */
		CompHeadFind(Win2MessH(win),0,&hs);
		PeteSelect(win,win->pte,hs.stop,hs.stop);
		RequestTFBEnableCheck(win); /* MJN *//* formatting toolbar */
	}
}

/************************************************************************
 * CompAttachSpec - attach a file to a comp window
 ************************************************************************/
void CompAttachSpec(MyWindowPtr win, FSSpecPtr spec)
{
	Str255 text;
	Str63 scratch;
	MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
	HeadSpec hs;
	short oldStart;

	if (CompHeadFind(messH,ATTACH_HEAD,&hs))
	{
			PeteCalcOff(TheBody);
			ComposeRString(text,ATTACH_FMT,
			GetMyVolName(spec->vRefNum,scratch),
			spec->parID,
			spec->name);
		if (hs.stop!=hs.value)
			CompHeadAppendPtr(TheBody,&hs," ",1);
		oldStart = hs.stop;
		CompHeadAppendPtr(TheBody,&hs,text+1,*text);
#ifndef ATT_ICONS
		AttachSelect(messH);
#endif
		CompHeadFind(messH,ATTACH_HEAD,&hs);
		PeteLock(TheBody,hs.value,hs.stop,peModLock|peSelectLock);
#ifdef ATT_ICONS
		PeteFileGraphicRange(TheBody,oldStart,hs.stop,spec,fgAttachment+fgDontCopyToClip);
#endif
		PeteSetDirty(TheBody);
		win->isDirty = True;
		PeteCalcOn(TheBody);
		PeteKillUndo(TheBody);
	}
}

/**********************************************************************
 * CompUnattach - remove an attachment
 **********************************************************************/
void CompUnattach(MyWindowPtr win)
{
	MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
	
	PETEInsertTextPtr(PETE,TheBody,-1,"",0,nil);
	AttachSelect(messH);
	win->isDirty = True;
}


#ifdef ETL
/**********************************************************************
 * InTranslator - do we need to do this translation?
 **********************************************************************/
Boolean InTranslator(TransInfoHandle translators,long id)
{
	short i;
	
	if (translators)
		for (i=HandleCount(translators);i--;)
			if ((*translators)[i].id==id) return(True);
	return(False);
}

/**********************************************************************
 * AddMessTranslator - add a translator from a message
 **********************************************************************/
OSErr AddMessTranslator(MessHandle messH,long which,Handle properties)
{
	short n;
	TransInfoHandle	localHandle;
	ControlHandle theCtl = FindControlByRefCon((*messH)->win,0xff000000|(which+ICON_BAR_NUM));
	long id = ETLIconToID(which);
	
	if (which==-1) return(fnfErr);
	
	if ((*messH)->hTranslators) n = HandleCount((*messH)->hTranslators);
	else n = 0;
	
	if (!n)
	{
		localHandle = NuHandle(sizeof(TransInfo));
		if (!localHandle) return(MemError());
		(*messH)->hTranslators = localHandle;
	}
	else
	{
		SetHandleBig_((*messH)->hTranslators,(n+1)*sizeof(TransInfo));
		if (MemError()) return(WarnUser(ETL_CANT_ADD_TRANS,MemError()));
	}
	(*(*messH)->hTranslators)[n].id = id;
	(*(*messH)->hTranslators)[n].properties = properties;
	if (theCtl) SetControlValue(theCtl,1);
	(*messH)->win->isDirty = True;
	return(noErr);
}

/**********************************************************************
 * RemoveMessTranslator - add or remove a translator from a message
 **********************************************************************/
OSErr RemoveMessTranslator(MessHandle messH,long which)
{
	short n;
	short i;
	ControlHandle theCtl = FindControlByRefCon((*messH)->win,0xff000000|(which+ICON_BAR_NUM));
	long id = ETLIconToID(which);
	
	if ((*messH)->hTranslators) n = HandleCount((*messH)->hTranslators);
	else n = 0;
	
	for (i=0;i<n;i++)
	{
		if ((*(*messH)->hTranslators)[i].id==id)
		{
			ZapHandle((*(*messH)->hTranslators)[i].properties);
			if (n==1) ZapHandle((*messH)->hTranslators);
			else
			{
				if (i!=n-1) BMD(&(*(*messH)->hTranslators)[i+1],&(*(*messH)->hTranslators)[i],(n-i-1)*sizeof(TransInfo));
				SetHandleBig_((*messH)->hTranslators,(n-1)*sizeof(TransInfo));
				break;
			}
		}
	}
	if (theCtl) SetControlValue(theCtl,0);
	(*messH)->win->isDirty = True;
	return(noErr);
}
#endif

void WarpQueue(uLong secs)
{
  TOCHandle tocH = GetOutTOC();
	MSumPtr sum;
  uLong now = GMTDateTime();
	
	if (tocH)
	{
	  for (sum=(*tocH)->sums;sum<(*tocH)->sums+(*tocH)->count;sum++)
		  if (sum->state == TIMED)
			{
			  if (!secs) sum->seconds = 0;
				else if (sum->seconds<secs+now)
				{
					sum->seconds = now;
					TOCSetDirty(tocH,true);
				}
			}
		SetSendQueue();
	}
}


#ifdef TWO
/************************************************************************
 * AttachDoc - attach a document to the topmost message, or create new if necessary
 ************************************************************************/
OSErr AttachDoc(MyWindowPtr win,FSSpecPtr spec)
{
	WindowPtr	winWP = GetMyWindowWindowPtr (win);
	MessHandle messH;
	HeadSpec hs;
	Str255 longName;
	
	if (winWP) messH = (MessHandle) GetWindowPrivateData (winWP);
	if (!winWP || !IsWindowVisible(winWP) || GetWindowKind(winWP) != COMP_WIN || SENT_OR_SENDING(SumOf(messH)->state))
	{
		win = DoComposeNew(0);
		if (CompHeadFind(Win2MessH(win),SUBJ_HEAD,&hs))
		{
			if (FSpGetLongName(spec,kTextEncodingUnknown,longName))
				PSCopy(longName,spec->name);
			CompHeadSetStr((*Win2MessH(win))->bodyPTE,&hs,longName);
		}
#ifdef TWO
		if (win) ApplyDefaultStationery(win,True,True);
#endif
		if (win) ShowMyWindow(GetMyWindowWindowPtr(win));
	}
	
	if (!win) return(1);	/* sorry, no cigar */
	
	CompAttachSpec(win,spec);
	return(noErr);
}

/************************************************************************
 * ApplyStationery - read and reconstitute a message from stationery
 ************************************************************************/
void ApplyStationery(MyWindowPtr win,FSSpecPtr spec,Boolean dontCleanse,Boolean personality)
{
	ApplyStationeryLo(win,spec,dontCleanse,personality,false);
}

/************************************************************************
 * ApplyStationery - read and reconstitute a message from stationery
 ************************************************************************/
void ApplyStationeryLo(MyWindowPtr win,FSSpecPtr spec,Boolean dontCleanse,Boolean personality,Boolean editStationery)
{
	Handle textH;
	
	UseFeature (featureStationery);

	/*
	 * grab the text
	 */
	if (Snarf(spec,&textH,0)) return;
	
	/*
	 * apply
	 */
	ApplyStationeryHandle(win,textH,dontCleanse,personality,editStationery);
		
	/*
	 * cleanup
	 */
	ZapHandle(textH);
}

/**********************************************************************
 * GetStationerySummary - get message summary from stationery text
 **********************************************************************/
OSErr GetStationerySum(Handle textH,MSumPtr pSum)
{
	UPtr spot, end, nl;

	/*
	 * fetch the summary info (first line)
	 */
	end = LDRef(textH) + GetHandleSize_(textH);
	for (spot=*textH;spot<end && *spot!=' ';spot++);
	spot++;
	for (nl=spot;nl<end && *nl!='\015';nl++);
	if (nl-spot!=2*sizeof(*pSum))
	{
		WarnUser(INVALID_STATIONERY,0);
		return -1;
	}
	Hex2Bytes(spot,nl-spot,(UPtr)pSum);
	return noErr;
}

/**********************************************************************
 * ApplyStationeryHandle - apply a handle as stationery
 **********************************************************************/
void ApplyStationeryHandle(MyWindowPtr win,Handle textH,Boolean dontCleanse,Boolean personality,Boolean editStationery)
{
	MSumType oldSum, newSum;
	MessHandle messH;
	Str255 scratch, origSubj;
	UPtr spot, end;
	long bodySpot;
	long size ;
	UPtr subj;
	Str255 into, sub;
	long newBodySpot;
	HeadSpec hs;
	PersHandle pers=nil;
	FSSpec	toSpec,fromSpec;
	short	label = editStationery ? 0 : pStationeryLabel;	//	Don't apply stationery label to text if opening stationery for edit
	
	if (!dontCleanse) CleanseStationery(win);
	
	/*
	 * fetch the summary info (first line)
	 */
	end = LDRef(textH) + GetHandleSize_(textH);
	if (GetStationerySum(textH,&oldSum))
		return;
	
	/*
	 * copy pertinent stuff
	 */
	messH = Win2MessH(win);
	newSum = *SumOf(messH);
	SumInfoCpy(&newSum,&oldSum);
	if (editStationery)
	{
		//	Editing stationery. Need to keep original uid
		newSum.msgIdHash = oldSum.msgIdHash;
		newSum.uidHash = oldSum.uidHash;
	}
	newSum.seconds = GMTDateTime();
	newSum.state = UNSENDABLE;
	if (!(oldSum.flags&FLAG_OLD_SIG)) newSum.sigId = SIG_NONE;
	*SumOf(messH) = newSum;
	
	if (personality && (pers = FindPersById(oldSum.persId)))
	{
		SetPers((*messH)->tocH,(*messH)->sumNum,pers,False);
		PushPers(pers);
	}

	for (spot = *textH;spot<end && (spot[0]!='\015'||spot[1]!='\015');spot++);
	bodySpot = spot<end-2 ? spot-*textH+2 : end-*textH;
	spot = *textH;

	/*
	 * from?
	 */
	size = bodySpot;
	if ((subj=FindHeaderString(spot,HeaderName(FROM_HEAD),&size,False)) && size)
	{
		Str255 from;
		
		MakePStr(from,subj,size);
		if (IsMe(from)) SetMessText(messH,FROM_HEAD,from+1,*from);
	}
	
	/*
	 * from?
	 */
	size = bodySpot;
	if ((subj=FindHeaderString(spot,HeaderName(TRANSLATOR_HEAD),&size,False)) && size)
	{
		AddTranslatorsFromPtr(messH,subj,size);
	}
	
	/*
	 * now, copy the headers
	 */
	TextFindAndCopyHeader(spot,bodySpot,messH,HeaderName(TO_HEAD),TO_HEAD,label);
	TextFindAndCopyHeader(spot,bodySpot,messH,HeaderName(CC_HEAD),CC_HEAD,label);
	TextFindAndCopyHeader(spot,bodySpot,messH,HeaderName(BCC_HEAD),BCC_HEAD,label);
	TextFindAndCopyHeader(spot,bodySpot,messH,HeaderName(ATTACH_HEAD),ATTACH_HEAD,label);
	
	/*
	 * subject gets special handling
	 */
	CompHeadGetStr(messH,SUBJ_HEAD,origSubj);
	if (*origSubj)
	{
		size = bodySpot;
		if ((subj=FindHeaderString(spot,HeaderName(SUBJ_HEAD),&size,False)) && size)
		{
			MakePStr(sub,subj,size);
			TrimWhite(sub);
			TrimInitialWhite(sub);
			if (*sub)
			{
				ComposeRString(into,STATION_SUBJ_FMT,sub,origSubj);
				SetMessText(messH,SUBJ_HEAD,into+1,*into);
				if (CompHeadFind(messH,SUBJ_HEAD,&hs))
					PeteLabel(TheBody,hs.value,hs.stop,label,label);
			}
			else
				*origSubj = 0;
		}
		else
			*origSubj = 0;
	}
	
	if (!*origSubj)
		TextFindAndCopyHeader(spot,bodySpot,messH,HeaderName(SUBJ_HEAD),SUBJ_HEAD,label);
	

	if (!editStationery && oldSum.opts&OPT_HAS_SPOOL)
	{
		//	Applying stationery. Need to copy stationery's spool folder.
		MakeAttSubFolder(messH,oldSum.uidHash,&fromSpec);
		MakeAttSubFolder(messH,newSum.uidHash,&toSpec);
		FSpDupFolder(&toSpec,&fromSpec,true,false);
	}


	/*
	 * we turn the text block into the body
	 */
	if (*textH+bodySpot<end)
	{
		newBodySpot = PETEGetTextLen(PETE,TheBody);
		if (oldSum.opts & OPT_HTML)
		{
			InsertRich(textH,bodySpot,end-*textH,newBodySpot,false,TheBody,nil,false);
			SetMessOpt(messH,OPT_HTML);
		}
		else
		{
			// This weird little dance avoids bug #3596
			// Sorta hacky?  Yes, indeed...
			Boolean oldSigOpt = MessOptIsSet(messH,OPT_INLINE_SIG);
			ClearMessOpt(messH,OPT_INLINE_SIG);
			
			// append the body
			AppendMessText(messH,0,*textH+bodySpot,end-*textH-bodySpot);
			
			// final steps of the hacky dance.
			if (oldSigOpt) SetMessOpt(messH,OPT_INLINE_SIG);
			
			if (oldSum.flags & FLAG_RICH)
			{
				PeteRich(TheBody,newBodySpot,PETEGetTextLen(PETE,TheBody),True);
				SetMessFlag(messH,FLAG_RICH);
			}
		}
		PeteLabel(TheBody,newBodySpot,PeteLen(TheBody),label,label);
		PeteLock(TheBody,newBodySpot,PeteLen(TheBody),0);
	}
	
	win->isDirty = False;
	PeteCleanList(win->pte);
	UL(textH);
	
	if (UseInlineSig) AddInlineSig(messH);
	
	RefreshSigButton(messH);
	
	UpdateSum(messH,SumOf(messH)->offset,SumOf(messH)->length);
	
	// update the window's idea of what color it is
	(*messH)->win->label = GetSumColor((*messH)->tocH,(*messH)->sumNum);

	InvalTopMargin(win);
	CompIBarUpdate(messH);
	
	if (pers) PopPers();
	
	return;
}

/**********************************************************************
 * 
 **********************************************************************/
OSErr CleanseStationery(MyWindowPtr win)
{
	MessHandle messH = Win2MessH(win);
	long start, stop;
	long len;
	
	/*
	 * remove every run labelled with stationery
	 */
	start = 0;
	while (!PETEFindLabelRun(PETE,TheBody,start,&start,&stop,pStationeryLabel,pStationeryLabel))
	{
		len = stop-start;
		PeteDelete(TheBody,start,stop);
		start = stop-len+1;	// on to next run
	}
	
	// Also, remove inline sig, if any
	RemoveInlineSig(Win2MessH(win));
	
	return(noErr);
}

#pragma segment Balloon
/************************************************************************
 * CompHelp - provide help for composition windows
 ************************************************************************/
void CompHelp(MyWindowPtr win,Point mouse)
{
	MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
	Rect vr;
	short hnum=1;
	ControlHandle sendButton=(*messH)->sendButton;
	short i;
	
	if (GetPriorityRect(win,&vr))
	{
		if (PtInRect(mouse,&vr))
		{
			PriorMenuHelp(win,&vr);
			return;
		}
		
#ifdef TWO
		if (GetSigRect(win,&vr) && PtInRect(mouse,&vr))
		{
			SigMenuHelp(win,&vr);
			return;
		}
#endif
		
		if (GetAttachRect(win,&vr) && PtInRect(mouse,&vr))
		{
			AttMenuHelp(win,&vr);
			return;
		}

#ifdef VCARD
		if (IsVCardAvailable () && !PrefIsSet (PREF_HIDE_VCARD_BUTTON) && GetVCardRect(win,&vr) && PtInRect(mouse,&vr))
		{
			VCardBtnHelp(win,&vr);
			return;
		}
#endif
		
		if ((*messH)->analControl && PtInControl(mouse,(*messH)->analControl))
		{
			if ((*PeteExtra(TheBody))->taeScore)
			{
				Str255 s;
				ComposeRString(s,ANAL_COMP_HELP,OffensivenessStrn+(*PeteExtra(TheBody))->taeScore);
				GetControlBounds((*messH)->analControl,&vr);
				MyBalloon(&vr,90,0,0,0,s);
			}
			return;
		}
		
		GetControlBounds(sendButton,&vr);
		if (PtInRect(mouse,&vr))
		{
			if ((*messH)->hStationerySpec) hnum+=2;
			else if (PrefIsSet(PREF_AUTO_SEND)) hnum++;
			MyBalloon(&vr,90,0,COMP_HELP_STRN+hnum,0,nil);
			return;
		}
		hnum += 3;
		
		if (win->topMargin)
		{
			for (i=0;i<ICON_BAR_NUM+(*messH)->nTransIcons;i++,hnum+=2)
			{
				if (PtInRect(mouse,CompIconRect(&vr,messH,i)))
				{
					switch(i)
					{
						case 0:
							if (MessOptIsSet(messH,OPT_COMP_TOOLBAR_VISIBLE)) hnum++;
							break;
						case 1:
							if (SumOf(messH)->flags&FLAG_CAN_ENC) hnum++;
							break;
						case 2:
							if (SumOf(messH)->flags&FLAG_BX_TEXT) hnum++;
							break;
						case 3:
							if (SumOf(messH)->flags&FLAG_WRAP_OUT) hnum++;
							break;
						case 4:
							if (SumOf(messH)->flags&FLAG_KEEP_COPY) hnum++;
							break;
#ifdef TWO
						case 5:
							if (SumOf(messH)->flags&FLAG_RR) hnum++;
							break;
#else
						case 5:
							if (SumOf(messH)->flags&FLAG_SIG) hnum++;
							break;
#endif
						default:
							CompTranslatorHelp(&vr,i-ICON_BAR_NUM);
							return;
					}
					MyBalloon(&vr,90,0,COMP_HELP_STRN+hnum,0,nil);
					return;
				}
			}
			if (TextFormattingBarHelp(win,mouse)) return;
		}
	}
}

/**********************************************************************
 * CompTranslatorHelp - balloon help for the translators
 **********************************************************************/
void CompTranslatorHelp(Rect *r,short i)
{
	Str255 module, translator, help;
	
	if (!ETLIconToDescriptions(i,module,translator))
	{
		ComposeRString(help,ETL_ICON_HELP_FMT,module,translator);
		MyBalloon(r,90,0,0,0,help);
	}
	else HMRemoveBalloon();
}

#ifdef TWO
/************************************************************************
 * SigMenuHelp - balloon help for the signature menu
 ************************************************************************/
void SigMenuHelp(MyWindowPtr win,Rect *r)
{
	Str255 s1,s2;
	long fid = SumOf(Win2MessH(win))->sigId;
	Boolean sig = SIG_NONE!=(SumOf(Win2MessH(win))->sigId);

	if (!HMIsBalloon())
	{
		GetRString(s1,SIG_MENU_HELP);
		GetRString(s2,sig?(fid?(fid==1?ALT_SIG_HELP:ARB_SIG_HELP):SIG_HELP):NOSIG_HELP);
		PCatC(s1,' ');
		PSCat(s1,s2);
		MyBalloon(r,90,0,0,0,s1);
	}
}
#endif

/************************************************************************
 * AttMenuHelp - balloon help for the signature menu
 ************************************************************************/
void AttMenuHelp(MyWindowPtr win,Rect *r)
{
	Str255 s1,s2;
	long aType = AttachOptNumber((SumOf(Win2MessH(win)))->flags);

	if (!HMIsBalloon())
	{
		GetRString(s1,ATT_HELP_STRN+1);
		GetRString(s2,ATT_HELP_STRN+2+aType);
		PSCat(s1,s2);
		MyBalloon(r,90,0,0,0,s1);
	}
}

#ifdef VCARD
/************************************************************************
 * AttMenuHelp - balloon help for the signature menu
 ************************************************************************/
void VCardBtnHelp(MyWindowPtr win,Rect *r)
{

}
#endif

#pragma segment CompWin

/************************************************************************
 * CompZoomSize - figure the zoomed size of a comp window
 ************************************************************************/
void CompZoomSize(MyWindowPtr win,Rect *zoom)
{
#pragma unused(win)
	short hi,wi;
	
	wi = MessWi(win);
	
	zoom->right = zoom->left + MIN(zoom->right-zoom->left,wi);
	
	if (hi=GetPrefLong(PREF_MHEIGHT))
	{
		hi *= win->vPitch;
		zoom->bottom = MIN(zoom->bottom,zoom->top+hi);
	}
}
	
/**********************************************************************
 * CompDidResize - take care of a resized composition window
 **********************************************************************/
void CompDidResize(MyWindowPtr win, Rect *oldContR)
{
#pragma unused(oldContR)
	WindowPtr	winWP = GetMyWindowWindowPtr(win);
	MessHandle messH = (MessHandle)GetWindowPrivateData(winWP);
	ControlHandle sendButton;
	short i;
	ControlHandle cntl;
	Rect r,rCntl,rPort;
	short wi;
	
	GetPortBounds(GetWindowPort(winWP),&rPort);
	wi = RectWi(rPort);
	/*
	 * text
	 */
	PeteDidResize(TheBody,&win->contR);
	
	/*
	 * the send button
	 */
	if (sendButton = (*messH)->sendButton)
	{
		GetControlBounds(sendButton,&r);
	/* MJN *//* text formatting toolbar */
/*		OFFSET_RECT(&(*sendButton)->contrlRect,
			((GrafPtr)win)->portRect.right-(r.right-r.left)/4-r.right,
			(win->topMargin-3-r.bottom+r.top)/2-r.top); */
		OffsetRect(&r,wi-INSET-r.right,0);
		MySetCntlRect(sendButton,&r);
	}
	
	if ((*messH)->analControl)
	{
		GetAnalRect(win,&r);
		MySetCntlRect((*messH)->analControl,&r);
	}
	
	/*
	 * icon bar buttons
	 */
	for (i=0;i<ICON_BAR_NUM+(*messH)->nTransIcons;i++)
	{
		cntl = FindControlByRefCon(win,0xff000000 | i);
		if (cntl)
		{
			CompIconRect(&r,Win2MessH(win),i);
			MoveMyCntl(cntl,r.left,r.top,RectWi(r),RectHi(r));
		}
	}
	
	if (TextFormattingBarVisible(win))
	{
		// format bar
		if ((cntl=FindControlByRefCon(win,kControlUserPaneProc)))
		{
			GetControlBounds(cntl,&rCntl);
			MoveMyCntl(cntl,2,rCntl.top,wi-4,0);
			SetControlVisibility(cntl,true,false);
		}
			
		// format bar divider
		if (cntl=FindControlByRefCon(win,kSeparatorRefCon))
		{
			GetControlBounds(cntl,&rCntl);
			MoveMyCntl(cntl,INSET,rCntl.top,wi-2*INSET,0);
			SetControlVisibility(cntl,true,false);
		}
	}
	
	// gotta reposition error messages
	PlaceMessErrNote(messH);
	
	//InvalTopMargin(win); 	/* sorry... */
}


/************************************************************************
 * CompGonnaShow - select an appropriate field, setup the icon bar controls
 ************************************************************************/
OSErr CompGonnaShow(MyWindowPtr win)
{
	MessHandle messH = Win2MessH(win);
	HeadSpec hs;
	ControlHandle cntl;
	short i;
	Rect r;
	
	i = GetRLong(COMP_TOP_MARGIN);
	if (TextFormattingBarVisible(win))
		i += GetTxtFmtBarHeight();
	SetTopMargin(win,i);
		
	for (i=0;i<ICON_BAR_NUM;i++)
	{
		CompIconRect(&r,Win2MessH(win),i);
		cntl = GetNewControl(ICON_BAR_CNTL,GetMyWindowWindowPtr(win));
		if (cntl)
		{
			EmbedControl(cntl,win->topMarginCntl);
			SetControlReference(cntl,0xff000000|i);
			SetBevelIcon(cntl,icons[i],0,0,nil);
			if (fBits[i]<0)
				SetControlValue(cntl,MessOptIsSet(Win2MessH(win),(-fBits[i])));
			else
				SetControlValue(cntl,MessFlagIsSet(Win2MessH(win),fBits[i]));
			SetControlVisibility(cntl,true,false);
		}
	}
	
	AddPriorityPopup(messH);
	AddSigPopup(messH);
	AddAttachPopup(messH);
#ifdef VCARD
	if (IsVCardAvailable () && !PrefIsSet (PREF_HIDE_VCARD_BUTTON))
		AddVCardButton (messH);
#endif
	if (BeingAnal()) AddAnalIcon(messH);
	
	// Add error note?
	AddMessErrNote(messH);
		
/* MJN */
	AddTextFormattingBarIcons(win,TextFormattingBarVisible(win),TextFormattingBarOK(win));
#ifdef ETL
	ETLAddIcons(win,ICON_BAR_NUM);
#endif

	win->update = DrawCompWindowBars; /* MJN *//* changed the update proc */
	win->activate = CompActivate;

	if (HasFeature (featureMultiplePersonalities) && PersCount()>1 && !SENT_OR_SENDING(SumOf(messH)->state)) CompAddPersPopup(win);
		
	SetBGColorsByPers(messH);

	PeteCleanList(win->pteList);
	win->isDirty = False;
	win->butch = true;
#ifdef USECMM_BAD
	win->buildCMenu = CompBuildCMenu;
#endif
	
#ifdef ATT_ICONS
	{
		FSSpec spec;
	
		for (i=1;1!=GetIndAttachment(messH,i,&spec,&hs);i++)
			PeteFileGraphicRange(TheBody,hs.start,hs.stop,&spec,fgAttachment);
	}
#endif

	// (jp) We'll supports lots of nickname scanning features for the headers
	SetNickScanning (TheBody, nickHighlight | nickComplete | nickExpand | nickCache | nickSpaces, kNickScanAllAliasFiles, IsHeaderNickField, HiliteCompHeader, GetCompNickFieldRange);
	// (jp) Special, for headers (other fields should leave this up to the nick scanning code)
	PeteNickScan (TheBody);

	// Grab the initial addresses for this message and hang onto them until we queue the message.
	// At that time we'll compare the before and after headers to determine what the user has
	// typed as "recent" (for the purpose of building our recently used addresses).
	CompGatherRecipientAddresses (messH, true);

	i = GetRLong(COMP_EXTRA_LINES)*win->vPitch;
	if (i) PETESetExtraHeight(PETE,TheBody,i);
	
	PeteCalcOn(TheBody);
#ifdef WINTERTREE
	if (HasFeature (featureSpellChecking))
		(*PeteExtra(TheBody))->spelled = 0;	// allow spelling here;
#endif

	CompActivateAppropriate(messH);
		
	PETEScroll(PETE,TheBody,0,0);

	EnableTxtFmtBarIfOK(win); /* MJN *//* text formatting bar */

	if (HasFeature (featureMultiplePersonalities))
		RedrawPersPopup(messH);
	
	if (IsQueued((*messH)->tocH,(*messH)->sumNum)) SetSendQueue();

	(*messH)->openToEnd = false;
	
	(*PeteExtra(TheBody))->taeDesired = !SENT_OR_SENDING(SumOf(messH)->state);
	(*PeteExtra(TheBody))->emoDesired = true;
	
	return(noErr);
}

/************************************************************************
 * AddPriorityPopup - add a priority popup to a window
 ************************************************************************/
OSErr AddPriorityPopup(MessHandle messH)
{
	MyWindowPtr win = (*messH)->win;
	short p = Prior2Display(SumOf(messH)->priority);
	Rect r;
	ControlHandle cntl;

	if (GetPriorityRect(win,&r))
		if (cntl = GetNewControlSmall(PRIOR_CNTL,GetMyWindowWindowPtr(win)))
		{
			SetControlReference(cntl,PRIOR_HIER_MENU);
			MySetCntlRect(cntl,&r);
			EmbedControl(cntl,win->topMarginCntl);
			SetBevelIcon(cntl,p==3 ? PRIORITY_HOLLOW_ICON : PRIOR_SICN_BASE+p-1,0,0,nil);
			SetBevelMenuValue(cntl,p);
			return(noErr);
		}
	return(fnfErr);
}

/************************************************************************
 * AddAttachPopup - add an attachment popup to a window
 ************************************************************************/
OSErr AddAttachPopup(MessHandle messH)
{
	MyWindowPtr win = (*messH)->win;
	short opt = AttachOptNumber(SumOf(messH)->flags);
	Rect r;
	ControlHandle cntl;

	if (GetAttachRect(win,&r))
		if (cntl = GetNewControlSmall(ATTACH_CNTL,GetMyWindowWindowPtr(win)))
		{
			SetControlReference(cntl,ATTACH_MENU);
			MySetCntlRect(cntl,&r);
			EmbedControl(cntl,win->topMarginCntl);
			SetBevelIcon(cntl,ATYPE_SICN_BASE+opt,0,0,nil);
			SetBevelMenuValue(cntl,opt+1);
			return(noErr);
		}
	return(fnfErr);
}

/************************************************************************
 * AddSigPopup - add a priority popup to a window
 ************************************************************************/
OSErr AddSigPopup(MessHandle messH)
{
	MyWindowPtr win = (*messH)->win;
	Rect r;
	ControlHandle cntl;

		if (GetSigRect(win,&r))
			if (cntl = GetNewControlSmall(SIG_CNTL,GetMyWindowWindowPtr(win)))
			{
				SetControlReference(cntl,SIG_HIER_MENU);
				MySetCntlRect(cntl,&r);
				EmbedControl(cntl,win->topMarginCntl);
				SetBevelIcon(cntl,SigIconId(messH),0,0,nil);
			return(noErr);
		}

	return(fnfErr);
}


#ifdef VCARD
/************************************************************************
 * AddVCardButton - add a vCard attachment button to a window
 ************************************************************************/
OSErr AddVCardButton (MessHandle messH)

{
	MyWindowPtr 	win;
	ControlHandle	cntl;
	Rect					r;
	
	// Only addd the vCard buttn if we actually have personal information to share
	if (AnyPersonalNicknames ()) {
		win = (*messH)->win;
		if (GetVCardRect (win, &r))
			if (cntl = NewControl (GetMyWindowWindowPtr (win), &r, "", true, 0, kControlBehaviorPushbutton | kControlContentIconSuiteRes, PERSONAL_NICKNAME_ICON, kControlBevelButtonSmallBevelProc, 0)) {
				SetControlReference(cntl, 'vcrd');
				MySetCntlRect (cntl, &r);
				EmbedControl (cntl, win->topMarginCntl);
				return(noErr);
			}
	}
	return (fnfErr);
}

#endif

/************************************************************************
 * AddAnalIcon - add the text analysis icon control
 ************************************************************************/
OSErr AddAnalIcon(MessHandle messH)
{
	MyWindowPtr win = (*messH)->win;
	Rect r;
	ControlHandle cntl;
	short score = SumOf(messH)->score;

	if (GetAnalRect(win,&r))
		if (cntl = NewControl(GetMyWindowWindowPtr(win),&r,"",false,score ? AnalIcon(score):0,0,0,kControlIconSuiteNoTrackProc,ANAL_ICON_BASE))
		{
			MySetCntlRect(cntl,&r);
			EmbedControl(cntl,win->topMarginCntl);
			(*messH)->analControl = cntl;
			SetControlReference(cntl,'anal');
			if (score) ShowControl((*messH)->analControl);
			return(noErr);
		}
	return(fnfErr);
}

/************************************************************************
 * CompActivateAppropriate - activate a reasonable field
 ************************************************************************/
void CompActivateAppropriate(MessHandle messH)
{
	HeadSpec hs;
	
	if ((*messH)->dontActivate) return;
	
	if (CompHeadFind(messH,TO_HEAD,&hs) && hs.value==hs.stop)
		CompHeadActivate(TheBody,&hs);
	else if (CompHeadFind(messH,SUBJ_HEAD,&hs) && hs.value==hs.stop)
		CompHeadActivate(TheBody,&hs);
	else if (CompHeadFind(messH,0,&hs))
	{
		if ((*messH)->openToEnd) hs.value = hs.stop;
		CompHeadActivate(TheBody,&hs);
	}
}


#define POPUP_FUDGE 40
typedef struct
{
	PETEGraphicInfo gi;
	ControlHandle popup;
} PersPopupType, *PersPopupPtr, **PersPopupHandle;
/**********************************************************************
 * CompAddPersPopup - install the personality popup
 **********************************************************************/
OSErr CompAddPersPopup(MyWindowPtr win)
{
	PersPopupHandle pp;
	MessHandle messH;
	ControlHandle cntl;
	HeadSpec hs;
	PETEStyleEntry pse;
	long junk, len, persLen;
	OSErr err;
	Str255 s;
	short hi;
	PersHandle messPers;
	MenuHandle mh;
	PersHandle pers;
	
	err = noErr;
	if (HasFeature (featureMultiplePersonalities)) {
		SAVE_STUFF;
		pp = NewZH(PersPopupType);
		messH = Win2MessH(win);
		messPers = PERS_FORCE(MESS_TO_PERS(messH));
		mh = GetMHandle(PERS_HIER_MENU);
		
		if (pp && CompHeadFind(messH,FROM_HEAD,&hs))
		{
			Rect r;
			ConfigFontSetup(win);
			len = 0;
			for (pers=PersList;pers;pers=(*pers)->next)
			{
				persLen = StringWidth(PCopy(s,(*pers)->name));
				len = MAX(len,persLen);
			}
			len += INSET;
			SetRect(&r,0,0,len,FontLead+1);
			cntl = NewControlSmall(GetMyWindowWindowPtr(win),&r,"",False,0,PERS_HIER_MENU,0,kControlPopupButtonProc,0);
			if (cntl)
			{
				SetControlMinimum(cntl,1);
				SetControlMaximum(cntl,PersCount()+1);
				SetControlValue(cntl,messPers==PersList ? 1 : Pers2Index(messPers)+2);
				SetControlReference(cntl,'pers');
				LetsGetSmall(cntl);
				ButtonFit(cntl);
				(*pp)->popup = cntl;
				EnableItem(GetMHandle(PERS_HIER_MENU),0);
				(*pp)->gi.width = len+POPUP_FUDGE;
				(*pp)->gi.height = hi = ControlHi(cntl)+3;
				if (hi<FontAscent) (*pp)->gi.descent = 0;
				else if (hi<FontLead) (*pp)->gi.descent = hi - FontAscent;
				else (*pp)->gi.descent = (hi-FontAscent)/2;
				(*pp)->gi.itemProc = PersGraphic;
				(*pp)->gi.cloneOnlyText = true;
				Zero(pse);
				PETEGetStyle(PETE,TheBody,hs.stop,&junk,&pse);
				pse.psStyle.graphicStyle.graphicInfo = (PETEGraphicInfoHandle)pp;
				if (err=PETESetStyle(PETE,TheBody,hs.stop,hs.stop+1,&pse.psStyle,(peAllValid&~peColorValid)|peGraphicValid|peGraphicColorChangeValid))
				{
					DisposeControl(cntl);
					ZapHandle(pp);
				}
				else (*messH)->persGraphic = (Handle)pp;
			}
			else ZapHandle(pp);
		}
		else err = fnfErr;
		REST_STUFF;
	}
	return(err);
}

/**********************************************************************
 * PersGraphic - Callback to handle a graphic that is a personality popup
 **********************************************************************/
pascal OSErr PersGraphic(PETEHandle pte,PETEGraphicInfoHandle graphic,long offset,PETEGraphicMessage message,void *data)
{
	OSErr err = noErr;
	Rect r;
	Handle picture=nil, alias=nil;
	Point pt;
	ControlHandle cntl = (*(PersPopupHandle)graphic)->popup;
	MyWindowPtr win = (*PeteExtra(pte))->win;
	MessHandle messH = Win2MessH(win);
	PETEDocInfo info;
	short track;
			
	switch (message)
	{
		case peGraphicDraw:	/* data is (Point *) with penLoc at baseline left as a point */
			if (cntl)
			{
				Zero(info);
				EnableItem(GetMHandle(PERS_HIER_MENU),0);
				PETEGetDocInfo(PETE,pte,&info);
				if (info.printing) return(noErr); // do nothing when printing
				if (PeteGraphicRect(&r,pte,graphic,offset))
				{
					(*Win2MessH(win))->redrawPersPopup = true;
					return noErr;	// try again at idle
				}
				r.left += 8;
				InsetRect(&r,0,2);
				//r.top += 2;
				MySetCntlRect(cntl,&r);
				SetControlVisibility(cntl,true,false);
				DrawControlInCurrentPort(cntl);
				SetControlVisibility(cntl,false,false);
			}
			break;

		case peGraphicClone:	/* data is (PETEGraphicInfoHandle *) into which to put duplicate */
			//	When user does copy function, make copy of graphic
			err = -108;
			break;
			
		case peGraphicTest:	/* data is (Point *) from top left; return errOffsetIsOutsideOfView if not hit */
			//	Can cancel hit by returning errOffsetIsOutsideOfView
			PeteGraphicRect(&r,pte,graphic,offset);
			pt = *(Point*)data;
			pt.h += r.left;
			pt.v += r.top;
			r.left += 8;
			InsetRect(&r,2,2);
			if (!PtInRect(pt,&r)) err = errOffsetIsOutsideOfView;
			else
			{
				long selStart, selEnd;
				PeteGetTextAndSelection(pte,nil,&selStart,&selEnd);
				(*messH)->testSelStart = selStart;
				(*messH)->testSelEnd = selEnd;
			}
			break;
			
		case peGraphicHit:	/* data is (EventRecord *) if mouse down, nil if time to turn off */
			if (cntl && data)
			{
				PersHandle oldPers = PERS_FORCE(MESS_TO_PERS(messH));
				short item = oldPers==PersList ? 1 : Pers2Index(oldPers)+2;
				PETEDocInfo info;
				
				// Restore the old selection
				PeteSelect(win,pte,(*messH)->testSelStart,(*messH)->testSelEnd);
				
				// activate window instead of hit control?
				PETEGetDocInfo(PETE,pte,&info);
				if (!info.docActive) return(tsmDocNotActiveErr);

				// pass control to control
				pt = ((EventRecord*)data)->where;
				GlobalToLocal(&pt);
				CheckNone(GetMHandle(PERS_HIER_MENU));
				PeteGraphicRect(&r,pte,graphic,offset);
				r.left += 8;
				InsetRect(&r,2,2);
				MySetCntlRect(cntl,&r);
				SetControlVisibility(cntl,true,false);
				SetControlValue(cntl,item);
				DrawControlInCurrentPort(cntl);
				track = TrackControl(cntl,pt,nil);
				SetControlVisibility(cntl,false,false);
				if (track)
				{
					PersHandle pers;
					short newItem;
					
					newItem = GetControlValue(cntl);
						
					if (newItem && newItem != item)
					{
						pers = newItem==1 ? PersList : Index2Pers(newItem-2);
						SetPers((*messH)->tocH,(*messH)->sumNum,pers,True);
					}
				}
			}
			break;		

		case peGraphicRemove:	/* data is nil; just dispose a copy of graphic */
			DisposeHandle((Handle)graphic);
			if (cntl) DisposeControl(cntl);
			break;
			
		case peGraphicResize:	/* data is a (short *) of the max width */
			break;
		
		case peGraphicRegion:	/* data is a RgnHandle which is to be changed to the graphic's region */
			//	Used mostly for changing the cursor
			PeteGraphicRect(&r,pte,graphic,offset);
			RectRgn((RgnHandle)data,&r);
			break;
	}
	
	return(err);
}

/************************************************************************
 * PersPopup - put up the priority menu, and set the priority
 ************************************************************************/
void PersPopup(MyWindowPtr win,Point where)
{
	MenuHandle pmh = GetMHandle(PERS_HIER_MENU);
	MessHandle messH = Win2MessH(win);
	PersHandle oldPers = PERS_FORCE(MESS_TO_PERS(messH));
	PersHandle pers;
	long res;
	short item, newItem;
	
	/*
	 * make sure the right priority gets selected
	 */
	CheckPers(win,False);
	
	/*
	 * use it
	 */
	if (pmh)
	{
		InsertMenu(pmh,-1);
		item = oldPers==PersList ? 1 : Pers2Index(oldPers)+2;
		res = AFPopUpMenuSelect(pmh,where.v,where.h,item);
		newItem = res&0xffff0000 ? res&0xff : 0;
		
		if (newItem && newItem != item)
		{
			pers = newItem==1 ? PersList : Index2Pers(newItem-2);
			SetPers((*messH)->tocH,(*messH)->sumNum,pers,True);
		}
	}
}

/**********************************************************************
 * CompActivate - activate a composition window; really just make sure
 *  we update the title bar
 **********************************************************************/
void CompActivate(MyWindowPtr win)
{
	short cur;
	MessHandle messH = Win2MessH(win);

	if (!win->isActive)
	{
		cur = CompHeadCurrent(TheBody);
		if (cur) CompLeaving(messH,cur);
	}
	else
		RequestTFBEnableCheck(win);
	SetTxtFmtBarEnabled(win,win->isActive?TextFormattingBarOK(win):false); /* MJN *//* text formatting bar */

	CompSendBtnUpdate(win);
	
	if (SENT_OR_SENDING(SumOf(Win2MessH(win))->state))
		DeactivateControl(win->topMarginCntl);
	else
		ActivateControl(win->topMarginCntl);

	if (HasFeature (featureMultiplePersonalities))
		RedrawPersPopup(messH);
}

/**********************************************************************
 * RedrawPersPopup - force the personality popup to redraw
 **********************************************************************/
void RedrawPersPopup(MessHandle messH)
{
	CGrafPtr messWinPort;
	Rect r;

	if (!HasFeature (featureMultiplePersonalities))
		return;
		
	(*messH)->redrawPersPopup = false;
	
	if (messH && TheBody)
	{
		if ((*messH)->persGraphic)
		{
			PETEStyleInfo info;
			long offset;
			
			Zero(info);
			info.graphicStyle.graphicInfo = (void*)(*messH)->persGraphic;
			if (!PETEFindStyle(PETE,TheBody,0,&offset,&info,peGraphicValid))
			{
				RgnHandle newClip = NewRgn();
				RgnHandle oldClip;
				if (newClip)
				{
					messWinPort = GetMyWindowCGrafPtr((*messH)->win);
					oldClip = SavePortClipRegion(messWinPort);
					PeteRect(TheBody,&r);
					r.right -= GROW_SIZE;
					RectRgn(newClip,&r);
					SetPortClipRegion(messWinPort,newClip);
					PushGWorld();
					SetPort_(messWinPort);
					PersGraphic(TheBody,(void*)(*messH)->persGraphic,offset,peGraphicDraw,nil);
					PopGWorld();
					RestorePortClipRegion(messWinPort,oldClip);
					DisposeRgn(newClip);
				}
			}
		}
	}
}
	
/**********************************************************************
 * CompIBarUpdate - update the icon bar
 **********************************************************************/
void CompIBarUpdate(MessHandle messH)
{
	short i;
	ControlHandle cntl;
	
	for (i=0;i<ICON_BAR_NUM;i++)
	{
		if (cntl=FindControlByRefCon((*messH)->win,0xff000000|i))
		{
			if (fBits[i]<0)
				SetControlValue(cntl,MessOptIsSet(messH,(-fBits[i])));
			else
				SetControlValue(cntl,MessFlagIsSet(messH,fBits[i]));
		}
	}
	RefreshSigButton(messH);
}


/**********************************************************************
 * CompClick - handle a click in a comp window.
 **********************************************************************/
void CompClick(MyWindowPtr win, EventRecord *event)
{
	WindowPtr	winWP = GetMyWindowWindowPtr(win);
	MessHandle messH = (MessHandle)GetWindowPrivateData(winWP);
	Point pt;
	short cur = CompHeadCurrent(TheBody);
	short new;
	Rect r;
	
	SetPort(GetWindowPort(winWP));
	pt = event->where;
	GlobalToLocal(&pt);
	
	if (GetSigRect(win,&r) && PtInRect(pt,&r))
	{
		RefreshSigPopup(win);
		HandleControl(pt,win);
	}
	else if (TextFormattingBarClick(win,event,pt)); /* MJN *//* text formatting bar */
	else 
	if (PtInPETE(pt,TheBody))
	{
		long fDirty;
		long fOldDirty = PeteIsDirty(TheBody);

		if (ClickType==Triple && IsAddressHead(CompHeadCurrent(TheBody)))
			AddrSelect(messH);
		else
		{
			PeteEditWFocus(win,win->pte,peeEvent,(void*)event,nil);
			new = CompHeadCurrent(TheBody);
			fDirty = PeteIsDirty(TheBody);
			/* MJN *//* text formatting bar */
			if (new!=cur)
			{
				if (cur!=0 && (*messH)->fieldDirty!=fDirty)
					CompLeaving(messH,cur); /* CompLeaving will call SetTxtFmtBarEnabled */
				else
					EnableTxtFmtBarIfOK(win); /* MJN *//* text formatting bar */
			}
			if (fDirty!=fOldDirty) (*messH)->fieldDirty = -1;
			if (new==ATTACH_HEAD) AttachSelect(messH);
		}
	}
	else if (win->labelCntl && PtInControl(pt,win->labelCntl))
	{
		BoxLabelMenu((*messH)->tocH,(*messH)->sumNum,messH,pt);
	}
	else
	{
		EnableMenuItems(False);
		SetMenuTexts(0,False);
		HandleControl(pt,win);
	}
}

OSErr CompLeaving(MessHandle messH,short head)
{
	short fDirty = PeteIsDirty(TheBody);
	OSErr err = noErr;
	MyWindowPtr win;

	win = (**messH).win;
	
	if ((*messH)->alreadyLeaving) return noErr;
	
	(*messH)->alreadyLeaving = true;

	if (fDirty != (*messH)->fieldDirty)
	{
		PushPers(PERS_FORCE(MESS_TO_PERS(messH)));
		if (IsAddressHead(head))
			err = NickExpandAndCacheHead (messH,head,false);
		UpdateSum(messH,SumOf(messH)->offset,SumOf(messH)->length);
		fDirty = PeteIsDirty(TheBody);
		(*messH)->fieldDirty = fDirty;
		PopPers();
	}
	EnableTxtFmtBarIfOK(win); /* MJN *//* text formatting bar */

	(*messH)->alreadyLeaving = false;
	return(err);
}


OSErr NickExpandAndCacheHead(MessHandle messH,short head,Boolean cacheOnly)
{
	OSErr err=fnfErr;
	HeadSpec hs;
	UHandle text=nil;
	BinAddrHandle raw=nil;
	long len;
	
	if (CompHeadFind(messH,head,&hs) && hs.stop-hs.value)
	{
		if (!(err=CompHeadGetText(TheBody,&hs,&text)))
		{
			if (!(err=SuckAddresses(&raw,text,True,True,False,nil)))
			{
				ZapHandle(text);
				NicknameCachingScan (TheBody, raw);	
				if (PrefIsSet(PREF_NICK_AUTO_EXPAND) && !cacheOnly)
					if (!(err=ExpandAliases(&text,raw,0,True)))
					{
						ZapHandle(raw);
						CommaList(text);
						if (len = GetHandleSize(text)) {
						
						   // See if expansion caused any changes. Don't replace text
						   // if no changes so selection doesn't change
					   	Handle fieldText;
					   	long selStart, selEnd, fieldLen;

					   	PeteGetTextAndSelection(TheBody,&fieldText,&selStart,&selEnd);
   					   fieldLen = hs.stop-hs.value;
						   if (fieldLen != len || memcmp(*text,*fieldText+hs.value,fieldLen))
						   {
						      // Text has changed
   							PetePrepareUndo(TheBody,peCantUndo,hs.value,hs.stop,nil,nil);
   							if (!(err=PeteDelete(TheBody,hs.value,hs.stop)))
   							{
   								if (!(err=PeteInsertPtr(TheBody,hs.value,LDRef (text),GetHandleSize (text))))
   								{
         							// If previous selection was entire field, reselect entire field
   								   Boolean selectAll = selStart==hs.value && selEnd==hs.stop;
   									if (CompHeadCurrent(TheBody)==head && CompHeadFind(messH,head,&hs)) 
   									   PeteSelect((*messH)->win,TheBody,selectAll ? hs.value : hs.stop,hs.stop);
   								}
   								UL(text);
   							}
   							PeteFinishUndo(TheBody,peUndoPaste,hs.value,hs.value+len);
   						}
						}
						CompGatherRecipientAddresses (messH, true);
					}
			}
		}
	}
	ZapHandle(text);
	ZapHandle(raw);
	return(err);
}

#ifdef TWO

/************************************************************************
 * RefreshSigPopup - refresh the signature menu
 ************************************************************************/
void RefreshSigPopup(MyWindowPtr win)
{
	ControlHandle cntl = FindControlByRefCon(win,SIG_HIER_MENU);

	if (cntl)
	{
		MenuHandle mHandle=GetBevelMenu(cntl);
		if (mHandle)
		{
			TrashMenu(mHandle,sigmAlternate+1);
			CopyExtraSigs(mHandle);
			SetBevelMenuValue(cntl,SigMenuItem(Win2MessH(win)));
		}
	}
}

/**********************************************************************
 * SigFIDByName - turn the name of a sig file into the sig's fid
 **********************************************************************/
uLong SigFIDByName(PStr string)
{
	FSSpec spec;
	uLong fid;
	
	SigSpecByName(string,&spec);
	if (FSMakeFID(&spec,&fid)) return(0);
	return(fid);
}

/**********************************************************************
 * SigSpecByName - turn a filename into a signature FSSpec
 **********************************************************************/
OSErr SigSpecByName(PStr string,FSSpecPtr spec)
{
	OSErr err = SubFolderSpec(SIG_FOLDER,spec);
	
	if (!err) err = FSMakeFSSpec(spec->vRefNum,spec->parID,string,spec);
	return(err);
}

/**********************************************************************
 * CopyExtraSigs - copy extra sigs to end of sig menu
 **********************************************************************/
void CopyExtraSigs(MenuHandle onto)
{
	short item, ontoItem, count;
	Str63 string;
	Boolean	multipleSignatures;
	
	multipleSignatures = false;
	count = SignatureCount();
	ontoItem = CountMenuItems(onto);
	for (item=shmAlternate+1;item<=count;item++)
	{
		ontoItem++;
		GetSignatureName(item,string);
		MyAppendMenu(onto,string);
		if (!HasFeature (featureSignatures))
			SetMenuItemBasedOnFeature (onto, ontoItem, featureSignatures, -1);
		else
			SetItemReducedIcon(onto,ontoItem,GetSignatureIcon(item));
		multipleSignatures = true;
	}
	if (multipleSignatures && HasFeature (featureSignatures))
		UseFeature (featureSignatures);
}
		
/**********************************************************************
 * SetSig - set the signature of a message
 **********************************************************************/
#pragma optimization_level 0	// optimizer bug steps on messH here
OSErr SetSig(TOCHandle tocH,short sumNum,long sigId)
{
	MessHandle messH = (*tocH)->sums[sumNum].messH;
	
	if (sigId==-1)
		(*tocH)->sums[sumNum].sigId = SIG_NONE;
	else
		(*tocH)->sums[sumNum].sigId = sigId;

	TOCSetDirty(tocH,true);
	if (messH)
	{
		Boolean peteDirtyWas = PeteIsDirty(TheBody)!=0;
		Boolean winDirtyWas = (*PeteExtra(TheBody))->win->isDirty;
		
		RefreshSigButton(messH);
		
		if ((*messH)->hStationerySpec) (*messH)->win->isDirty = True;
		
		if (MessOptIsSet(messH,OPT_INLINE_SIG)) RemoveInlineSig(messH);
		if (UseInlineSig) AddInlineSig(messH);
		else
		{
			if (!peteDirtyWas) PETEMarkDocDirty(PETE,TheBody,False);
			(*messH)->win->isDirty = winDirtyWas;
		}
	}
	return(noErr);
}
#pragma optimization_level reset

/**********************************************************************
 * RefreshSigButton - make sure the sig button is right
 **********************************************************************/
void RefreshSigButton(MessHandle messH)
{
	ControlHandle cntl;

	if (cntl = FindControlByRefCon((*messH)->win,SIG_HIER_MENU))
	{
		SetBevelMenuValue(cntl,SigMenuItem(messH));
		SetBevelIcon(cntl,SigIconId(messH),0,0,nil);
	}
}

#endif

/************************************************************************
 * AttachMenu - let the user set the attachment types
 ************************************************************************/
void AttachMenu(MyWindowPtr win)
{
	MenuHandle amh = GetMenu(ATTACH_MENU);
	Rect pr;
	MessHandle messH = Win2MessH(win);
	long flags = SumOf(messH)->flags;
	long res;
	short item,oldItem;
	long aType;
	Point p;
	
	if (amh)
	{
		GetAttachRect(win,&pr);
		p.h = pr.left; p.v = pr.top;
		LocalToGlobal(&p);
		
		aType = AttachOptNumber(flags);
		SetItemMark(amh,aType+1,checkMark);
		oldItem = aType+1;
		
		InsertMenu(amh,-1);
		res = PopUpMenuSelect(amh,p.v,p.h,aType+1);
		item = res&0xffff0000 ? res&0xff : 0;
		DeleteMenu(ATTACH_MENU);
		
		if (item && item!=oldItem)
		{
			SetAOptNumber(SumOf(messH)->flags,item-1);
			TOCSetDirty((*messH)->tocH,true);
			InvalWindowRect(GetMyWindowWindowPtr(win),&pr);
			TOCSetDirty((*messH)->tocH,true);
		}
		ReleaseResource_(amh);
	}
}

/**********************************************************************
 * CompMenu - handle menu choice for composition window
 **********************************************************************/
Boolean CompMenu(MyWindowPtr win, int menu, int item, short modifiers)
{
#pragma unused(modifiers)
	MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
	TOCHandle tocH = (*messH)->tocH;
	int sumNum = (*messH)->sumNum;
	uLong when;
	short state;
	Boolean now = False;
	short tableId;
	Boolean swap = False;
	TextAddrHandle addr;

	if (menu==MESSAGE_MENU && item==MESSAGE_QUEUE_ITEM && modifiers&optionKey)
	{
		menu = CHANGE_HIER_MENU;
	  item = CHANGE_QUEUEING_ITEM;
	  swap = True;
	}
	switch (menu)
	{
		case FILE_MENU:
			switch (item)
			{
				case FILE_SAVE_ITEM:
					if (win->isDirty) (void) SaveComp(win);
					return(True);
				case FILE_SAVE_AS_ITEM:
					SaveMessageAs(messH);
					return(True);
				case FILE_PRINT_ITEM:
				case FILE_PRINT_ONE_ITEM:
					PrintOneMessage((*messH)->win,0!=(modifiers&shiftKey),item==FILE_PRINT_ONE_ITEM);
					return(True);
					break;
			}
			break;
		case EDIT_MENU:
			switch (item)
			{
				case EDIT_CLEAR_ITEM:
					if (CompHeadCurrent(TheBody)==ATTACH_HEAD && win->hasSelection)
					{
						CompDelAttachment(messH,nil);
						return(True);
					}
					break;
#ifdef SPEECH_ENABLED
				case EDIT_SPEAK_ITEM:
					if (!(modifiers & shiftKey)) {
						(void) SpeakMessage (nil, (*messH)->tocH, (*messH)->sumNum, speakTo | speakSubject | speakBody, false);
						return (true);
					}
					break;
#endif
			}
			return(False); /* handle elsewhere */
			break;
			
		case MESSAGE_MENU:
			switch (item)
			{
				case MESSAGE_DELETE_ITEM:
#ifdef TWO
					AddXfUndo(tocH,GetTrashTOC(),sumNum);
#endif
					DeleteMessage(tocH,sumNum,(modifiers&(optionKey|shiftKey))==(optionKey|shiftKey));
					if ((*tocH)->win)
						BoxSelectAfter((*tocH)->win,sumNum);
					return(True);
				case MESSAGE_QUEUE_ITEM:
				  when = 0;
					now = PrefIsSet(PREF_AUTO_SEND);
				queueit:
					QueueMessage(tocH,sumNum,now?kEuSendNow:kEuSendNext,when,false,menu==MESSAGE_MENU);
					return(True);
				case MESSAGE_FORWARD_ITEM:
					DoForwardMessage(win,0,True);
					return(True);
					break;
				case MESSAGE_ATTACH_ITEM:
					CompAttach(win,false);
					return(True);
					break;
				case MESSAGE_SALVAGE_ITEM:
					DoSalvageMessage(win,False);
					return(True);
					break;
			}
			break;

		case STATE_HIER_MENU:
#ifdef THREADING_ON
			if (SendThreadRunning && (*tocH)->sums[sumNum].state==BUSY_SENDING)
			{
				WarnUser (SENDING_WARNING, 0);
				return (True);
			}
#endif
			if (item==statmQueued) QueueMessage(tocH,sumNum,kEuSendNext,0,true,false);
			else SetState(tocH,sumNum,Item2Status(item));
			return(True);
			break;

		case PERS_HIER_MENU:
			if (HasFeature (featureMultiplePersonalities))
			{
				Str63 name;
				
				SetPers(tocH,sumNum,FindPersByName(MyGetItem(GetMHandle(menu),item,name)),True);
				return(True);
			}
			break;
		
		case CHANGE_HIER_MENU:
			switch(item)
			{
				case CHANGE_QUEUEING_ITEM:
				  when = (*tocH)->sums[sumNum].seconds;
				  state = (*tocH)->sums[sumNum].state;
				  if (state!=TIMED) when = 0;
					if (ModifyQueue(&state,&when,swap))
				  {
					  if (now = (state==SENT)) state=QUEUED;
					  if (IsQueuedState(state)) goto queueit;
						if (IsQueued(tocH,sumNum))
						  SetState(tocH,sumNum,SENDABLE);
						SetSendQueue();
					}
					return(True);
				default:
					return(False);
			}
			break;
		
		case TLATE_ATT_HIER_MENU:
			ETLAttach(item,win);
			break;
		
		case FORWARD_TO_HIER_MENU:
			DoForwardMessage(win,(addr=MenuItem2Handle(menu,item)),True);
			ZapHandle(addr);
			return(True);
			break;
		case TABLE_HIER_MENU:
			if (Menu2TableId(tocH,GetMHandle(TABLE_HIER_MENU),item,&tableId))
				SetMessTable(tocH,sumNum,tableId);
			return(True);
			break;
		case PRIOR_HIER_MENU:
			SetPriority(tocH,sumNum,NewPrior(item,(*tocH)->sums[sumNum].priority));
			return(True);
			break;
		case LABEL_HIER_MENU:
			item = Menu2Label(item);
			SetSumColor(tocH,sumNum,item);
			return(True);
			break;
		case SPECIAL_MENU:
			switch(AdjustSpecialMenuSelection(item))
			{
				case SPECIAL_MAKE_NICK_ITEM:
					if (modifiers&shiftKey)
						MakeNickFromSelection(win);
					else
#ifdef VCARD
						MakeCompNick(win,nil);
#else
						MakeCompNick(win);
#endif
					return(True);
					break;
				case SPECIAL_MAKEFILTER_ITEM:
					DoMakeFilter(win);
					return(True);
					break;
#ifdef TWO
				case SPECIAL_FILTER_ITEM:
					FilterMessage(flkManual,tocH,sumNum);
					if (FrontWindow_()!=GetMyWindowWindowPtr(win) && (*tocH)->count)
					{
						SelectBoxRange(tocH,sumNum-1,sumNum-1,False,-1,-1);
					}
					return(True);
					break;
#endif
			}
			break;
#ifdef DEBUG
		case DEBUG_MENU:
			if(item == dbTestSpooler)
			{
				SpoolMessage(messH, nil, 0);
				return(True);
			}
			break;
#endif
		default:
			return(TransferMenuChoice(menu,item,tocH,sumNum,modifiers,
							!SENT_OR_SENDING(SumOf(messH)->state) && CompHeadCurrent(TheBody)==BCC_HEAD));
			break;
	}
	return(False);
}

/************************************************************************
 * CompSendBtnUpdate - make sure name is correct on send button
 ************************************************************************/
void CompSendBtnUpdate(MyWindowPtr win)
{
	MessHandle messH = Win2MessH(win);
	Str31 sbName,shdName;

	GetControlTitle((*messH)->sendButton,sbName);
	GetRString(shdName,(*messH)->hStationerySpec ? SAVE_ITEXT	//	"Save" for stationery
		: PrefIsSet(PREF_AUTO_SEND)?SEND_BUTTON:QUEUE_BUTTON);
	if (!StringSame(sbName,shdName)) SetControlTitle((*messH)->sendButton,shdName);
}

/************************************************************************
 * CompUpdateScore - update the TAE score
 ************************************************************************/
void CompUpdateScore(MyWindowPtr win)
{
	MessHandle messH = Win2MessH(win);
	short score = (*PeteExtra(TheBody))->taeScore;
	Boolean scoreDiff = score!=SumOf(messH)->score;
	
	if (SENT_OR_SENDING(SumOf(messH)->state)) return;
	if ((*messH)->analControl && score && (scoreDiff || !IsControlVisible((*messH)->analControl)))
	{
		SetTAEScore((*messH)->tocH,(*messH)->sumNum,score);
		if (gBetterAppearance)
		{
			SetBevelIcon((*messH)->analControl,AnalIcon(score),nil,nil,nil);
			ShowControl((*messH)->analControl);
		}
		else
		{
			DisposeControl((*messH)->analControl);
			(*messH)->analControl = nil;
			AddAnalIcon(messH);
		}
	}
}


/* MJN *//* new routine */
/************************************************************************
 * DrawCompWindowBars - draw the icon bar and any other bars in a window
 ************************************************************************/
void DrawCompWindowBars(MyWindowPtr win)
{
	MessHandle messH = Win2MessH(win);
	
	if ((*messH)->sound)
	{
		Str255 soundName;
		PlayNamedSound(GetRString(soundName,(*messH)->sound));
		(*messH)->sound = 0;
	}
}

/* MJN *//* new routine */
/*****************************************************************************
 * ForceCompWindowRecalcAndRedraw - forces the composition window to
 * recalculate positions for anything that was based on topMargin, and then
 * forces a full redraw.  Currently only used by the text formatting toolbar
 * when its visible status is changed.
 *****************************************************************************/
void ForceCompWindowRecalcAndRedraw(MyWindowPtr win)
{
	MessHandle messH;

	if (HasHelp&&HMIsBalloon()) HMRemoveBalloon(); /* OK to ignore errors from HMRemoveBalloon */
	messH = (MessHandle)GetMyWindowPrivateData(win);
	PeteDidResize(TheBody,&win->contR);
	// gotta reposition error messages
	PlaceMessErrNote(messH);
	InvalContent(win);
}

/**********************************************************************
 * SetAttachmentType - set the type of an attachment
 **********************************************************************/
void SetAttachmentType(TOCHandle tocH, short sumNum, short type)
{
	ControlHandle cntl;
	MessHandle messH = (*tocH)->sums[sumNum].messH;
	
	SetAOptNumber((*tocH)->sums[sumNum].flags,type);
	TOCSetDirty(tocH,true);
	
	if (messH && (cntl=FindControlByRefCon((*messH)->win,ATTACH_MENU)))
	{
		SetBevelIcon(cntl,ATYPE_SICN_BASE+type,0,0,nil);
		SetBevelMenuValue(cntl,type+1);
		if ((*messH)->hStationerySpec) (*messH)->win->isDirty = True;
	}
}						

/************************************************************************
 * GetSigRect - where does the sig thing belong?
 ************************************************************************/
Boolean GetSigRect(MyWindowPtr win,Rect *pr)
{
	if (GetPriorityRect(win,pr))
	{
#ifdef TWO
		OffsetRect(pr,RectWi(*pr)+INSET,0);
#endif
		return(True);
	}
	return(False);
}

/************************************************************************
 * GetAttachRect - where does the attachment thing belong?
 ************************************************************************/
Boolean GetAttachRect(MyWindowPtr win,Rect *pr)
{
	if (GetSigRect(win,pr))
	{
		OffsetRect(pr,RectWi(*pr)+INSET,0);
		return(True);
	}
	return(False);
}

#ifdef VCARD
/************************************************************************
 * GetVCardRect - where does the vCard thing belong?
 ************************************************************************/
Boolean GetVCardRect (MyWindowPtr win, Rect *pr)
{
	if (GetAttachRect(win,pr))
	{
		OffsetRect(pr,RectWi(*pr)+INSET,0);
		return(True);
	}
	return(False);
}
#endif

/************************************************************************
 * GetAnalRect - where does the anal thing belong?
 ************************************************************************/
Boolean GetAnalRect(MyWindowPtr win,Rect *pr)
{
	if (GetSigRect(win,pr))
	{
		Rect	r;
		
		InsetRect(pr,(RectWi(*pr)-16)/2,(RectHi(*pr)-16)/2);
		GetControlBounds((*Win2MessH(win))->sendButton,&r);
		OffsetRect(pr,r.left-pr->right-INSET,0);
		return(True);
	}
	return(False);
}

#ifdef TWO
/************************************************************************
 * SigIconId - return signature icon id for message
 ************************************************************************/
short SigIconId(MessHandle messH)
{
	long sigId = SumOf(messH)->sigId;
	short sicn;
	
	if (sigId==SIG_NONE) sicn = NO_SIG_SICN;
	else sicn = sigId==0 ? SIG_SICN : (sigId==1?ALT_SIG_SICN:N_SIG_SICN);
	return(sicn);
}

/************************************************************************
 * SigMenuItem - return signature menu item for message
 ************************************************************************/
short SigMenuItem(MessHandle messH)
{
	long sigId = SumOf(messH)->sigId;
	short item = sigmNone;
	MenuHandle smh;
	ControlHandle cntl;
	Str31 string;
	
	if (sigId==SIG_NONE)
		item = sigmNone;
	else if (sigId==SIG_STANDARD)
		item = sigmStandard;
	else if (sigId==SIG_ALTERNATE)
		item = sigmAlternate;
	else if (cntl=FindControlByRefCon((*messH)->win,SIG_HIER_MENU))
	{
		smh = GetBevelMenu(cntl);
		for (item=CountMenuItems(smh);item>sigmAlternate;item--)
		{
			MyGetItem(smh,item,string);
			MyLowerStr(string);
			if (sigId==Hash(string)) break;
		}
		if (item==sigmAlternate) item=sigmNone;
	}
	return(item);
}
#endif

/************************************************************************
 * DrawPopIBox - draw a popup box with a SICN in it
 ************************************************************************/
void DrawPopIBox(Rect *r, short sicnId)
{
	Rect ir = *r;
	
	InsetRect(&ir,-2,-2);
	DrawShadowBox(&ir);

	ir = *r;
	ir.right = ir.left+16;
	PlotIconID(&ir,atAbsoluteCenter,ttNone,sicnId);
}

/************************************************************************
 * DrawShadowBox - draw a shadowed box
 ************************************************************************/
void DrawShadowBox(Rect *r)
{
	EraseRect(r);
	FrameRect(r);
	MoveTo(r->left+2,r->bottom);
	Line(r->right-r->left-2,0);
	Line(0,r->top-r->bottom+1);
}

/************************************************************************
 * PlotFlag - plot an icon and associated flag
 ************************************************************************/
void PlotFlag(Rect *r,Boolean on,short which)
{
	Rect r2;
	short fid, fs;
	
	if (on)
	{
		fid = GetPortTextFont(GetQDGlobalsThePort());
		fs = GetPortTextSize(GetQDGlobalsThePort());
		TextFont(systemFont); TextSize(0);
		MoveTo(r->left+4,r->bottom-6);
		DrawChar(checkMark);
		TextFont(fid); TextSize(fs);
	}
	
	SetRect(&r2,r->left+16,r->bottom-20,r->left+32,r->bottom-4);
	PlotIconID(&r2, atAbsoluteCenter, ttNone, which);
}

#ifdef NEVER
		aType = AttachOptNumber(flags);
		switch(aType)
		{
			case 0:	SetItemMark(pmh,pymHQX,checkMark); break;
			case 1:	SetItemMark(pmh,pymDouble,checkMark); break;
#ifdef TWO
			case 2:	SetItemMark(pmh,pymUU,checkMark); break;
#endif
			case 3:	SetItemMark(pmh,pymSingle,checkMark); break;
		}
			else if (pymDouble<=item && item<pymBar2)
			{
				switch(item)
				{
					case pymHQX: aType = 0; break;
					case pymDouble: aType = 1; break;
#ifdef TWO
					case pymUU: aType = 2; break;
#endif
					case pymSingle: aType = 3; break;
				}
				SumOf(messH)->flags &= ~(FLAG_ATYPE_LO|FLAG_ATYPE_HI);
				SumOf(messH)->flags |= aType<<6;
			}
#endif

/**********************************************************************
 * 
 **********************************************************************/
OSErr CompDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag)
{
	OSErr err=noErr;
	Rect r;
	Point mouse;

	if (DragSource==win ||	// don't drag attachments from ourself
			SENT_OR_SENDING(SumOf(Win2MessH(win))->state) ||
			DragIsInteresting(drag,A822_FLAVOR,nil) || // let the address flavor be handled elsewhere
			!DragIsInteresting(drag,flavorTypeHFS,flavorTypePromiseHFS,kStationeryDragType,kPersonalityDragType,nil))	// no file, stationery, or personality; boring
		return(dragNotAcceptedErr);

	SetPort(GetMyWindowCGrafPtr(win));

	if (!(DragOrMods(drag)&cmdKey) && !PrefIsSet(PREF_SEND_ENRICHED_NEW) && DragIsImage(drag))
	{
		GetDragMouse(drag,&mouse,nil);
		GlobalToLocal(&mouse);
		if (PtInPETE(mouse,win->pte))
		{
			if (!PeteDrag(win,which,drag)) return(noErr);
		}
		else PeteDrag(nil,which,drag);
	}
	
	if (which==0xfff) HideDragHilite(drag);
		

	switch (which)
	{
		case kDragTrackingInWindow:
			r = win->contR;
			r.right -= GROW_SIZE;
			ShowDragRectHilite(drag,&r,True);
			break;

		case 0xfff:
			err=CompDrop(win,drag);
			break;
			
		case kDragTrackingLeaveWindow:
			HideDragHilite(drag);
			break;
	}
	
	return(err);

}

/**********************************************************************
 * CompDrop - handle a drop on a composition window
 **********************************************************************/
OSErr CompDrop(MyWindowPtr win,DragReference drag)
{
	short item;
	OSErr err=noErr;
	short n = MyCountDragItems(drag);
	HFSFlavor **data;
	PromiseHFSFlavor **promise;
	FSSpec spec;
	AEDesc dropLocation;
	short nAttach;
	MessHandle messH = Win2MessH(win);
	OSType type;
	
	nAttach = CountAttachments(messH);
	
	NullADList(&dropLocation,nil);
	
	for (item=1;item<=n && !err;item++)	// loop through each item
	{
		FSSpecHandle	hSpec;
		PersHandle		**hPers;

		if (!MyGetDragItemData(drag,item,kStationeryDragType,(void*)&hSpec))
		{
			//	Stationery drag
			RemoveInlineSig(messH);
			ApplyStationery(win,LDRef(hSpec),true,true);
			ZapHandle(hSpec);
		}
		else if (!MyGetDragItemData(drag,item,kPersonalityDragType,(void*)&hPers))
		{
			//	Personality drag
			MessHandle	messH = (MessHandle)GetMyWindowPrivateData(win);

			SetPers((*messH)->tocH,(*messH)->sumNum,**hPers,false);
			ZapHandle(hPers);
		}
		else if (err=MyGetDragItemData(drag,item,flavorTypeHFS,(void*)&data))
		{
			/*
			 * promiseHFS - tell the sender to put it in our spool folder
			 */
			if (!(err=MyGetDragItemData(drag,item,flavorTypePromiseHFS,(void*)&promise)))
			{
				//Dprintf("\p%o",(*promise)->promisedFlavor);
				// first, see if the "promised" file is really there already
				// we only do this if the promised flavor is one that we know is ok, ie
				// from an app that handles promiseHFS in the particular way we need
				if (TypeIsOnList((*promise)->promisedFlavor,PRE_PROMISE_OK))
					err = MyGetDragItemData(drag,item,(*promise)->promisedFlavor,(void*)&data);
				// ok, a real promise.  we only accept these from a few apps
				// that we know aren't going to do really bad things to us
				else if (TypeIsOnList((*promise)->promisedFlavor,PROMISE_OK))
				{
					AliasHandle alias=nil;
					// the data isn't already there.  Set the droplocation and try again
					// create the spool folder
					MakeAttSubFolder(Win2MessH(win),SumOf(Win2MessH(win))->uidHash,&spec);
					if (!(err=NewAlias(nil,&spec,&alias)))
					{
						//set the droplocation to it
						AECreateDesc(typeAlias,LDRef(alias),GetHandleSize_(alias),&dropLocation); ZapHandle(alias);
						SetDropLocation(drag,&dropLocation);
						
						// now find the file
						err = MyGetDragItemData(drag,item,(*promise)->promisedFlavor,(void*)&data);
						DisposeADList(&dropLocation,nil);
					}
				}
				else
					err = dragNotAcceptedErr;
				ZapHandle(promise);
				if (!err)
				{
					spec = **(FSSpecHandle)data;
					ZapHandle(data);
					
					// and attach it
					if (!(err = AttachDoc(win,&spec)))
						nAttach++;
				}
			}
			else
			{
				err=dragNotAcceptedErr;
			}
		}
		else
		{
			/*
			 * real HFS, just attach, unless...
			 */
			spec = (*data)->fileSpec;
			type = (*data)->fileType;
			ZapHandle(data);
			if (type=='fldr' ||
					type=='disk' || type=='srvr')
				return(dragNotAcceptedErr);
			err = AttachDoc(win,&spec);
			nAttach++;
			
			/*
			 * the drag came from one of our windows.  When this is the case
			 * and the source window was incoming, we'd better spool in case
			 * the user deletes message & attachment
			 */
			if (!err && DragSource && GetWindowKind (GetMyWindowWindowPtr (DragSource))==MESS_WIN)
				err=SpoolIndAttachment(messH,nAttach);
		}
	}

	RequestTFBEnableCheck(win); /* MJN *//* formatting toolbar */

	return(err);
}

/**********************************************************************
 * CompKey - react to a keystroke in a composition window
 **********************************************************************/
Boolean CompKey(MyWindowPtr win, EventRecord *event)
{
	MessHandle messH = (MessHandle)GetMyWindowPrivateData(win);
	TOCHandle tocH = (*messH)->tocH;
	long uLetter = UnadornMessage(event)&charCodeMask;
	short c = (event->message & charCodeMask);
	Boolean hadSelection = win->hasSelection;
	HeadSpec hSpec;
	long offset;
	OSErr err;
	short cur = CompHeadCurrent(TheBody);
	Boolean tabSwitch = c==TABKEY &&
											(!win->pte || win->ro || cur!=0 ||
												(event->modifiers&shiftKey) ||
												!PrefIsSetOrNot(PREF_TAB_IN_TEXT,event->modifiers,optionKey));
	long undoStart, undoEnd;
	Boolean excerpt = false;
	Boolean crFunnies = c=='\015' && !cur && !(event->modifiers&(optionKey|shiftKey));
	Boolean atEnd, afterReturn;
	short n;

	if (tabSwitch) event->modifiers &= ~optionKey;
	
	if (leftArrowChar<=uLetter && uLetter<=downArrowChar &&
			IsArrowSwitch(event->modifiers))
	{
		NextMess(tocH,messH,uLetter,event->modifiers,False);
		return(True);
	}
	else if (!(event->modifiers & cmdKey) && tabSwitch)
		CompSwitchFields(messH,!(event->modifiers & shiftKey));
	else if (c=='\015' && (cur!=0) && !PrefIsSet(PREF_HEAD_RETURN))
		CompSwitchFields(messH,!(event->modifiers & shiftKey));
	else if (event->modifiers&cmdKey)
	{
		return(False);
	}
	else
	{
		normalKey:
		//if (c=='\015') event->modifiers |= shiftKey;
		if (cur==ATTACH_HEAD && !(hadSelection && (c==backSpace || c==clearKey || c==delChar) && !SENT_OR_SENDING(SumOf(messH)->state)))
			WarnUser(READ_ONLY,0);
		else
		{
			PeteGetTextAndSelection(TheBody,nil,&undoStart,&undoEnd);
			if (cur==0 && (undoStart!=undoEnd||c==backSpace)) PeteEnsureRangeBreak(TheBody,MAX(undoStart-4,0),undoEnd);
			if (undoEnd<PeteLen(TheBody)-1) undoEnd++; // this avoids weird editor bug
			if (crFunnies && (excerpt=PeteIsExcerptAt(TheBody,undoEnd))) PetePrepareUndo(TheBody,peUndoTyping,-1,undoEnd,&undoStart,nil);
			undoEnd = undoStart;
			if (undoEnd<PeteLen(TheBody)-1) undoEnd++; // this avoids weird editor bug
			if ((err=PeteEdit(win,win->pte,peeEvent,(void*)event))==errAENotModifiable
				&& PETESelectionLocked(PETE,win->pte,-1,-1))
			{
				if (cur==ATTACH_HEAD)
				{
					if (hadSelection && (c==backSpace || c==clearKey || c==delChar))
					{
						CompDelAttachment(messH,nil);
					}
					else WarnUser(READ_ONLY,0);
				}
				else WarnUser(READ_ONLY,0);
			}
			if (hadSelection && !win->hasSelection && !CompHeadCurrent(TheBody) && CompHeadFind(messH,CompHeadCurrent(TheBody),&hSpec))
				if (hSpec.stop==hSpec.value || c!=backSpace && hSpec.stop==hSpec.value+1)
					PetePlainPara(TheBody,PeteParaAt(TheBody,PETEGetTextLen(PETE,TheBody)));
			if (!err) undoEnd++;
			if (crFunnies && !err)
			{
				PETEParaInfo pinfo;
				long pIndex = PeteParaAt(TheBody,-1);
				Zero(pinfo);
				PETEGetParaInfo(PETE,TheBody,pIndex,&pinfo);
				if (pinfo.quoteLevel)
				{
					long paraBefore;
					PETEParaInfo beforeInfo;
					Boolean atReturn;
					UHandle text;
					
					PeteGetTextAndSelection(TheBody,nil,&offset,nil);
					atEnd = offset == PeteLen(TheBody)-1;
					paraBefore = PeteParaAt(TheBody,offset-2);
					Zero(beforeInfo);
					PETEGetParaInfo(PETE,TheBody,paraBefore,&beforeInfo);
					if (!beforeInfo.quoteLevel)
					{
						PetePlainParaAt(TheBody,offset-1,offset-1);
						PetePlain(TheBody,offset-1,offset,peAllValid);
					}
					else				
					{
#ifdef DEBUG
						if (!BUG14)
#endif
							PeteCalcOff(TheBody);
						PeteGetTextAndSelection(TheBody,&text,&offset,nil);
						
						/*
							 Ok, we might have started and ended six ways:
							 
							 	char | char				char newCR | char				
							 	char | return			char newCR | return			atReturn
							 	return | char			return newCR | char			afterReturn
							 	return | return		return newCR | return		atReturn && afterReturn
							 	char | end				char return |	end				atEnd
							 	return | end			return return |	end			atEnd && afterReturn
							
							 We want to end up in one of two ways:
							 
							 	return plainCR | plainCR plainCR
							 	return plainCR | end											atEnd
							 
						*/

						atReturn = offset < PeteLen(TheBody) && (*text)[offset]=='\015';
						afterReturn = (*text)[offset-2]=='\015';	// offset-1 is the cr we just put in
						
						// We need either two or four returns, and we have one already
						n = atEnd ? 1 : 3;	// We have one already.
						if (atReturn) n--; //already there
						if (afterReturn) n--;	//already there
						// insert them
						while (n-->0) {PeteInsertChar(TheBody,offset,CrLf[1],nil);undoEnd++;}
						
						// Ok, we always want two returns before the IP.  This
						// is automatically the case if afterReturn.  Otherwise, bump the IP
						if (!afterReturn) offset++;
						
						// Now we're in one of the two "normal" situations.  All that's left to do
						// Is make sure the proper paragraphs are plain.
						if (atEnd) MessPlainBytes(messH,0,afterReturn&&atReturn?-2:-1);
						else
						{
							PetePlainParaAt(TheBody,offset-1,offset+2);
							PetePlain(TheBody,offset-1,offset+2,peAllValid);
						}
						
						PeteSelect(win,win->pte,offset,offset);
						PeteCalcOn(TheBody);
					}
				}
			}
			if (crFunnies && excerpt) PeteFinishUndo(TheBody,peUndoTyping,undoStart,undoEnd);
		}
		if (cur==ATTACH_HEAD) AttachSelect(messH);
	}
	if (SENT_OR_SENDING(SumOf(messH)->state)) PeteLock(TheBody,kPETECurrentStyle,kPETECurrentStyle,peModLock);
	return(True);
}

/************************************************************************
 * CompDelAttachment - delete selected attachment
 ************************************************************************/
void CompDelAttachment(MessHandle messH,HSPtr hs)
{
	long sel;
	UHandle textH;
	HeadSpec hSpec;
	
	if (hs)
	{
		PeteDelete(TheBody,hs->start,hs->stop);
		sel = hs->start;
		textH = PeteText(TheBody);
	}
	else
	{
		PETEInsertTextPtr(PETE,TheBody,-1,nil,0,nil);
		PeteGetTextAndSelection(TheBody,&textH,&sel,nil);
	}
	CompHeadFind(messH,ATTACH_HEAD,&hSpec);
	if (sel>hSpec.value && (*textH)[sel-1]==' ' && (*textH)[sel-2]==' ')
	{
		PeteDelete(TheBody,sel-1,sel);
		sel--;
		hSpec.stop--;
	}
	if (sel<hSpec.stop && (*textH)[sel]==' ' && (*textH)[sel-1]==' ')
	{
		PeteDelete(TheBody,sel,sel+1);
		hSpec.stop--;
	}
	if (!hs && hSpec.stop==hSpec.value) CompSwitchFields(messH,True);
}


/************************************************************************
 * CompSwitchFields - switch fields in a composition window
 ************************************************************************/
void CompSwitchFields(MessHandle messH, Boolean forward)
{
	MyWindowPtr win = (*messH)->win;
	short current = CompHeadCurrent(TheBody);
	long next;
	long fDirty = PeteIsDirty(TheBody);
	HeadSpec hs;

	if (!current)
		if (forward) next = TO_HEAD;
		else next = BCC_HEAD;
	else if (forward) next = current+1;
	else next = current-1;
	
	if (next==ATTACH_HEAD || next==FROM_HEAD && !PrefIsSet(PREF_EDIT_FROM))
		forward ? next++ : next--;
	if (next>(*PeteExtra(TheBody))->headers) next = 0;

	if (CompHeadFind(messH,next,&hs) || CompHeadFind(messH,0,&hs))
		CompHeadActivate(TheBody,&hs);
	
	CompLeaving(messH,current);
}
/************************************************************************
 * AttachSelect - broaden the current selection to include the entire
 * text of an attachment
 ************************************************************************/
void AttachSelect(MessHandle messH)
{
	long selStart, selEnd;
	long mid;
	HeadSpec hs;
	OSErr err = fnfErr;
	
	if (PeteGetTextAndSelection(TheBody,nil,&selStart,&selEnd)) return;
	
	if (!CompHeadFind(messH,ATTACH_HEAD,&hs)) return;
	
	if (selEnd<hs.value || selStart>hs.stop) return;
	
	mid = (selStart+selEnd)/2;
	if (mid-2>hs.value) err = PETESelectGraphic(PETE,TheBody,mid-2);
	else if (mid+4<hs.stop) err = PETESelectGraphic(PETE,TheBody,mid+4);
	
	if (err) CompSwitchFields(messH,True);
}

/************************************************************************
 * AddrSelect - broaden the current selection to include entire addresses
 ************************************************************************/
void AddrSelect(MessHandle messH)
{
	UHandle theText;
	long selStart, selEnd;
	HeadSpec hs;
	BinAddrHandle addresses;
	long **spots=nil;
	short which;
	
	if (PeteGetTextAndSelection(TheBody,&theText,&selStart,&selEnd)) return;
	
	if (!CompHeadFind(messH,CompHeadCurrent(TheBody),&hs)) return;
	
	if (selEnd<hs.start || selStart>hs.stop) return;
	
	if (SuckPtrAddresses(&addresses,LDRef(theText)+hs.value,hs.stop-hs.value,False,False,False,&spots))
		return;
	UL(theText);
	ZapHandle(addresses);
	
	selStart -= hs.value;
	selEnd -= hs.value;
	which = HandleCount(spots);
	while (which-->1)
	{
		if ((*spots)[which-1]<selEnd && selEnd<(*spots)[which])
			selEnd = (*spots)[which];
		if ((*spots)[which-1]<selStart && selStart<(*spots)[which])
			selStart = (*spots)[which-1];
	}
	selStart += hs.value;
	selEnd += hs.value;
	PeteSelect((*messH)->win,TheBody,selStart,selEnd);
}

/************************************************************************
 * CompSetFormatBarIcon - set the format bar icon's value
 ************************************************************************/
void CompSetFormatBarIcon(MyWindowPtr win, Boolean visible)
{
	ControlHandle cntl = FindControlByRefCon(win,0xff000000);

	if (cntl) SetControlValue(cntl,visible ? 1 : 0);
}

/************************************************************************
 * CompButton - handle the clicking of a button in the comp window
 ************************************************************************/
void CompButton(MyWindowPtr win,ControlHandle buttonHandle,long modifiers,short part)
{
	WindowPtr	winWP = GetMyWindowWindowPtr (win);
	MessHandle messH=Win2MessH(win);
	long which;
	Str63 s;
	Boolean wasCompToolbar = false;
	
	if (buttonHandle==(*messH)->sendButton)
		{if (part==kControlButtonPart) CompMenu(win,MESSAGE_MENU,MESSAGE_QUEUE_ITEM,modifiers);}
	else if (GetControlReference(buttonHandle) == mcMesgErrors)
			return;
	else if (GetControlReference(buttonHandle) == PRIOR_HIER_MENU)
		DoMenu(winWP,PRIOR_HIER_MENU<<16|GetBevelMenuValue(buttonHandle),modifiers);
	else if (GetControlReference(buttonHandle) == SIG_HIER_MENU)
	{
		which = GetBevelMenuValue(buttonHandle);
		if (which > sigmAlternate)
		{
			MyGetItem(GetBevelMenu(buttonHandle),which,s);
			MyLowerStr(s);
			which = Hash(s);
		}
		else
			which -= 2;
		SetSig((*messH)->tocH,(*messH)->sumNum,which);
	}
	else if (GetControlReference(buttonHandle) == ATTACH_MENU)
	{
		which = GetBevelMenuValue(buttonHandle);
		SetAttachmentType((*messH)->tocH,(*messH)->sumNum,which-1);
	}
#ifdef VCARD
	else if (GetControlReference(buttonHandle) == 'vcrd')
	{
		FSSpec	spec;
		short		ab;

		ab = FindAddressBookType (personalAddressBook);
		if (ValidAddressBook (ab))
			if (!MakeVCardFile (&spec, ab, 0))
				CompAttachSpec (win, &spec);
	}
#endif
	else if (buttonHandle == (*messH)->analControl)
		return;
	else
	{
		which = GetControlReference(buttonHandle)&0xff;
		if (0<=which && which<ICON_BAR_NUM)
		{
			if (GetControlValue(buttonHandle))
			{
				if (fBits[which]<0)
					ClearMessOpt(messH,(-fBits[which]));
				else
					ClearMessFlag(messH,fBits[which]);
				SetControlValue(buttonHandle,0);
				if (fBits[which]==-OPT_COMP_TOOLBAR_VISIBLE)
				{
					SetMessOpt(messH,(-fBits[which])); // oops; put it back
					HideTxtFmtBar(win);
					if ((*messH)->hStationerySpec) win->isDirty = True;
				}
				else
						win->isDirty = true;
			}
			else
			{
				if (fBits[which]<0)
					SetMessOpt(messH,(-fBits[which]));
				else
					SetMessFlag(messH,fBits[which]);
				SetControlValue(buttonHandle,1);
				if (fBits[which]==-OPT_COMP_TOOLBAR_VISIBLE)
				{
					ClearMessOpt(messH,(-fBits[which])); // oops; put it back
					ShowTxtFmtBar(win);
					EnableTxtFmtBarIfOK(win);
					if ((*messH)->hStationerySpec) win->isDirty = True;
				}
				else
						win->isDirty = true;
			}
		}
/* MJN *//* formatting toolbar */
		else if (wasCompToolbar=TextFormattingBarButton(win,buttonHandle,modifiers,part,which));
#ifdef ETL
		//	Translator
		else ETLSelect(which-ICON_BAR_NUM,!GetControlValue(buttonHandle),messH);
#endif
	}
	
	// Audit any non-toolbar control hits
	if (!wasCompToolbar)	
		AuditHit((modifiers&shiftKey)!=0, (modifiers&controlKey)!=0, (modifiers&optionKey)!=0, (modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),(GetControlReference(buttonHandle)&0xff)), mouseDown);
}

/* MJN *//* new routine */
/************************************************************************
 * CompIdle - idle proc that gets called periodically
 ************************************************************************/
void CompIdle(MyWindowPtr win)
{
	TextFormattingBarIdle(win);
	CompSendBtnUpdate(win);
	if ((*Win2MessH(win))->redrawPersPopup)
		RedrawPersPopup(Win2MessH(win));
	CompUpdateScore(win);
}

/************************************************************************
 * ModifyQueue - change the queueing of a message
 ************************************************************************/
Boolean ModifyQueue(short *state,uLong *when,Boolean swap)
{
	DialogPtr md;
	MyWindowPtr	mdWin;
	short dItem,item;
	Boolean done;
	Str63 timeStr,dateStr;
	uLong secs = *when ? *when+ZoneSecs() : LocalDateTime();
	extern ModalFilterUPP DlgFilterUPP;

	if ((mdWin=GetNewMyDialog(MODQ_DLOG,nil,nil,(void*)InFront))==nil)
	{
		WarnUser(MEM_ERR,MemError());
		return(False);
	}

	md = GetMyWindowDialogPtr (mdWin);
	
	TimeString(secs,False,timeStr,nil);
	DateString(secs,shortDate,dateStr,nil);
	SetDIText(md,MQDL_TIME,timeStr);
	SetDIText(md,MQDL_DATE,dateStr);
	SelectDialogItemText(md,MQDL_TIME,0,REAL_BIG);

	mdWin->update = DlgUpdate;
	
	if (*when)
	  SetDItemState(md,MQDL_LATER,True);
	else if (swap)
	{
		if (PrefIsSet(PREF_AUTO_SEND))
		  SetDItemState(md,MQDL_QUEUE,True);
		else
			SetDItemState(md,MQDL_NOW,True);
	}
	else
	{
		if (*state==QUEUED)
			SetDItemState(md,MQDL_QUEUE,True);
		else
			SetDItemState(md,MQDL_UNQUEUE,True);
	}

	/*
	 * now, display the dialog
	 */
	StartMovableModal(md);
	ShowWindow(GetDialogWindow(md));
	HiliteButtonOne(md);
	PushCursor(arrowCursor);
	do
	{
		done=False;
		MovableModalDialog(md,DlgFilterUPP,&dItem);
		switch (dItem)
		{
			case MQDL_LATER:
				SelectDialogItemText(md,MQDL_TIME,0,REAL_BIG);
				/* fall-through */
			case MQDL_TIME:
			case MQDL_DATE:
			  dItem = MQDL_LATER;
				/* fall-through */
			default:
				for (item=MQDL_NOW;item<=MQDL_UNQUEUE;item++)
				  if (GetDItemState(md,item)!=(item==dItem))
					  SetDItemState(md,item,item==dItem);
				break;
			case MQDL_OK:
			case MQDL_CANCEL:
			case CANCEL_ITEM:
				done = True;
				break;
		}
		if (done && dItem==MQDL_OK && GetDItemState(md,MQDL_LATER))
		{
			GetDIText(md,MQDL_DATE,dateStr);
			GetDIText(md,MQDL_TIME,timeStr);
			if (!(*when=QueueDate2Secs(timeStr,dateStr))) done=False;
		}
	}
	while(!done);
	PopCursor();
	
	if (done = (dItem!=MQDL_CANCEL && dItem!=CANCEL_ITEM))
	{
	  if (GetDItemState(md,MQDL_UNQUEUE))
		  *state = SENDABLE;
		else
		  *state = GetDItemState(md,MQDL_NOW) ? SENT : QUEUED;
		if (!GetDItemState(md,MQDL_LATER)) *when = 0;
		else *state = TIMED;
	}
	EndMovableModal(md);
	DisposDialog_(md);

  if (done) SetSendQueue();
	return(done);
}

uLong QueueDate2Secs(UPtr timeStr,UPtr dateStr)
{
  uLong when;
	uLong now = LocalDateTime();
  long lenUsed;
	LongDateRec dateLDR,timeLDR;
	DateCacheRecord dc;
	DateTimeRec dtr;
	uShort dateRet=fatalDateTime;
	uShort timeRet=fatalDateTime;
	
	// Incremental forms
	if (dateStr[1]=='+')
	{
		StringToNum(dateStr,&when);
		return now + when * 60*60*24 - ZoneSecs();
	}
	else if (timeStr[1]=='+')
	{
		StringToNum(timeStr,&when);
		return now + when * 60 - ZoneSecs();
	}
	
	InitDateCache(&dc);
	WriteZero(&dateLDR,sizeof(dateLDR));
	WriteZero(&timeLDR,sizeof(timeLDR));
	timeRet = StringToTime(timeStr+1,*timeStr,&dc,&lenUsed,&timeLDR);
	dateRet = StringToDate(dateStr+1,*dateStr,&dc,&lenUsed,&dateLDR);
	if (dateRet > fatalDateTime && timeRet > fatalDateTime)
	{
	  WarnUser(DATE_ERROR,0);
		return(0);
	}
	SecondsToDate(LocalDateTime(),&dtr);
	if (timeRet < fatalDateTime)
	{
	  dtr.hour = timeLDR.od.oldDate.hour;
		dtr.minute = timeLDR.od.oldDate.minute;
		dtr.second = timeLDR.od.oldDate.second;
	}
	if (dateRet < fatalDateTime)
	{
	  dtr.year = dateLDR.od.oldDate.year;
		dtr.month = dateLDR.od.oldDate.month;
		dtr.day = dateLDR.od.oldDate.day;
		dtr.dayOfWeek = dateLDR.od.oldDate.dayOfWeek;
	}
	DateToSeconds(&dtr,&when);
	if (when < now && dateRet >= fatalDateTime && when + 24*3600 > now) when += 24*3600;
	if (when < now) {WarnUser(THE_PAST,0); return(0);}
	return(when-ZoneSecs());
}

/************************************************************************
 * CompIconRect - get the rect for a particular icon in the icon bar
 ************************************************************************/
Rect *CompIconRect(Rect *r,MessHandle messH,short index)
{
	CGrafPtr	messWinPort = GetMyWindowCGrafPtr ((*messH)->win);
	short h1,h2,width,advance;
	Rect tr;
	short n = ICON_BAR_NUM+(*messH)->nTransIcons;
	short bWidth;
	Rect rPort,rCntl;
	
	/*
	 * how big are the buttons?
	 */
	GetAttachRect((*messH)->win,&tr);
	*r = tr;
	bWidth = RectWi(tr);
	
	/*
	 * the left and right margins of the icon area
	 */
	h1 = tr.right + I_WIDTH;
	GetPortBounds(messWinPort,&rPort);
	GetControlBounds((*messH)->sendButton,&rCntl);
	h2 = rPort.right - RectWi(rCntl) - 4*INSET;
	
	/*
	 * how wide is the whole thing?
	 */
	width = h2 - h1;
	advance = bWidth+INSET;
	if (n*advance>width) advance = bWidth;

	/*
	 * center the button group
	 */
	h1 += (width-n*advance)/2;
	
	/*
	 * ok, now we can make an answer
	 */
	r->left = h1 + index*advance;
	r->right = r->left+bWidth;
	
  return(r);
}

/************************************************************************
 * SaveStationeryStuff - save the header line for stationery, and change filetype
 ************************************************************************/
OSErr SaveStationeryStuff(short refN,MessHandle messH)
{
	short err;
	FSSpec spec;
	FInfo info;
	MSumType sum,suminhex[2];
	Str31 scratch;
	long count;
	
	if (!HasFeature (featureStationery))
		return (noErr);
	
	UseFeature (featureStationery);
	
	/*
	 * first, set the filetype correctly
	 */
	if (err = GetFileByRef(refN,&spec)) goto done;
	if (err = FSpGetFInfo(&spec,&info)) goto done;
	info.fdType = STATIONERY_TYPE;
	info.fdCreator = CREATOR;
	if (err = FSpSetFInfo(&spec,&info)) goto done;

	/*
	 * now, write the important info
	 */
	/* header */
	if (err = FSWriteP(refN,GetRString(scratch,X_STUFF))) goto done;

	/* summary */
	sum = *SumOf(messH);
	count = 2*sizeof(MSumType);
	if (sum.sigId==SIG_NONE) sum.flags &= ~FLAG_OLD_SIG; else sum.flags |= FLAG_OLD_SIG;
	if (err = AWrite(refN,&count,Bytes2Hex((UPtr)&sum,sizeof(sum),(UPtr)suminhex))) goto done;
	
	/* and newline */
	err = FSWriteP(refN,Cr);
	
#ifdef ETL
	if (!err && (*messH)->hTranslators) err = WriteTranslators(refN,(*messH)->hTranslators);
#endif

done:
	if (err) FileSystemError(COULDNT_SAVEAS,"",err);
	else if (spec.vRefNum==StationVRef && spec.parID==StationDirId) BuildStationeryList();
	return(err);
}

#ifdef USECMM_BAD
/************************************************************************************************
CompBuildCMenu - build a list of context-specific items to be added to the contextual menu when
the context click occurs over a composition window.  specCMenuInfo does not point to a valid
handle upon calling the function. The handle-style array it points to needs to be allocated
herein.
************************************************************************************************/
OSStatus CompBuildCMenu(MyWindowPtr win, EventRecord* contextEvent, MenuHandle contextMenu)
{
	short				numItems;					//number of items to add put into the list
	static CMenuInfo	locCMInfo[] =				//items to put into the list
		{	MESSAGE_MENU,	MESSAGE_SALVAGE_ITEM,		//"send again" item
			MESSAGE_MENU,	MESSAGE_ATTACH_ITEM,		//"attach document" item
			MESSAGE_MENU,	MESSAGE_DELETE_ITEM,		//"delete" item
			CONTEXT_MENU,	CONTEXT_TERM_ITEM };		//item to mark end of array

	//count the number of items to add
	for( numItems=0; !(locCMInfo[numItems].srcMenuID == CONTEXT_MENU && locCMInfo[numItems].srcMenuItem == CONTEXT_TERM_ITEM); numItems++ )
		;
	
	//allocate memory for as handle-style array
	*specCMenuInfo = (CMenuInfoHndl)NewHandle( sizeof( CMenuInfo ) * ( numItems + 1 ) );
	if( !(*specCMenuInfo) ) return( MemError() );

	//copy data into the array
	BlockMove( locCMInfo, **specCMenuInfo, sizeof( CMenuInfo ) * ( numItems + 1 ) );
	
	return( noErr );
}
#endif

#endif


//
//	Useful for grabbing the To, CC and BCC addresses of a message.
//
//	Base on code in makefilter.c
//
extern OSErr GatherAddresses(MessHandle messH, HeaderEnum header, Handle *dest, Boolean wantComments);

OSErr GatherRecipientAddresses (MessHandle messH, Handle *dest, Boolean wantComments)

{
	OSErr	theError;
	short index,
				headers[] = {TO_HEAD, CC_HEAD, BCC_HEAD};

	theError = noErr;
	for (index = 0; !theError && index < sizeof (headers) / sizeof (short); ++index)
		theError = GatherAddresses (messH, headers[index], dest, wantComments);
	return (theError);
}

