eudora-mac/compact.c

1 line
97 KiB
C
Executable File

/* 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);
}