/* 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 "messact.h" #define FILE_NUM 24 /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */ #pragma segment Messact OSErr MakeSubjectTXE(MessHandle messH); void MessUpdate(MyWindowPtr win); void MessSubjRect(MyWindowPtr win,Rect *r); Boolean MessScroll(MyWindowPtr win,int h,int v); void MessHelp(MyWindowPtr win,Point mouse); short MyFSWrite(short refN,long *bytes,UPtr buffer); Boolean IsAttLine(PStr line,Boolean wantToOpen); OSErr OpenAttLine(PETEHandle pte,PStr line,Boolean finderSelect,Boolean printIt); Boolean GetBlahRect(MyWindowPtr win, short which, Rect *r); Boolean TogglePOPD(OSType theType,short id, MessHandle messH); void SADL_Hilite(DialogPtr dgPtr); void MessButton(MyWindowPtr win,ControlHandle button,long modifiers,short part); void MessTxChanged(MyWindowPtr win,TEHandle teh,short oldNl,short newNl,Boolean scroll); #define I_WIDTH 29 OSErr KillTransAttachments(FSSpecHandle files); Boolean GetTruckRect(MyWindowPtr win,Rect *c); void AddNotifyControls(MessHandle messH); void PlaceNotifyControls(MyWindowPtr win); static short SaveAsToOpenFileLo(short refN,MessHandle messH); void MessProxy(MyWindowPtr win,EventRecord *event); OSErr LopPoundHexDigits(PStr fileName); #ifdef USECMM_BAD OSErr MessBuildCMenu( MyWindowPtr win, EventRecord* contextEvent, CMenuInfoHndl* specCMenuInfo ); #endif /********************************************************************** * MessClose - close a message window **********************************************************************/ Boolean MessClose(MyWindowPtr win) { MessType **messH = (MessType **)GetMyWindowPrivateData(win); TOCType **tocH = (*messH)->tocH; int sumNum = (*messH)->sumNum; if (!GrowZoning && TheBody && (*messH)->subPTE && win->pte==(*messH)->subPTE) MessFocus(messH,TheBody); if (PeteIsDirty((*messH)->subPTE)) MessSaveSub(messH); if (!GrowZoning) win->isDirty = win->isDirty || PeteIsDirtyList(TheBody); if (!NoSaves && !GrowZoning) if (!SaveMessHi(win,True)) return(False); LL_Remove(MessList,messH,(MessType **)); AccuZap((*messH)->extras); AccuZap((*messH)->aSourceMID); if ((*messH)->etlFiles) KillTransAttachments((*messH)->etlFiles); ZapHandle((*messH)->etlFiles); SetMyWindowPrivateData (win, nil); AccuZap((*messH)->newsGroupAcc); ZapHandle(messH); (*tocH)->sums[sumNum].messH = nil; #ifdef IMAP // Cancel the IMAP download if this is an imap message ... if ((*tocH)->imapTOC) IMAPAbortMessageFetch(tocH, sumNum); #endif return(True); } /********************************************************************** * SaveMessHi - save a message, if the user wants and if its dirty **********************************************************************/ Boolean SaveMessHi(MyWindowPtr win,Boolean closing) { short which; if (win->isDirty) { which = WannaSave(win); if (which==CANCEL_ITEM) return(False); else if (which == WANNA_SAVE_SAVE) { if (!SaveMess(win)) return(False); } else if (which == WANNA_SAVE_DISCARD) { if (!closing) ReopenMessage(win); } } return(True); } /********************************************************************** * **********************************************************************/ OSErr KillTransAttachments(FSSpecHandle files) { FSSpec spec; OSErr err, anyErr=noErr; short n; for (n=HandleCount(files);n;n--) { spec = (*files)[n-1]; err = WipeSpec(&spec); if (!anyErr) anyErr = err; } return(anyErr); } /********************************************************************** * NewPrior - compute a new priority from a menu and an old priorty **********************************************************************/ short NewPrior(short item,short prior) { switch(item) { case pymRaise: prior = Prior2Display(prior); prior = MAX(pymHighest,prior-1); prior = Display2Prior(prior); break; case pymLower: prior = Prior2Display(prior); prior = MIN(pymLowest,prior+1); prior = Display2Prior(prior); break; default: prior = Display2Prior(item); break; } return(prior); } /************************************************************************ * TransferMenuChoice - handle a menu choice from a transfer menu ************************************************************************/ Boolean TransferMenuChoice(short menu,short item,TOCHandle tocH,short sumNum,long modifiers,Boolean fcc) { FSSpec spec, toSpec, resolvToSpec; short function=REAL_BIG; if (!IsMailboxChoice(menu,item)) return(False); if (menu==TRANSFER_MENU) function = TRANSFER; else if (IsMailboxSubmenu(menu)) function = (menu-(g16bitSubMenuIDs?BOX_MENU_START:1))/MAX_BOX_LEVELS; if (function == TRANSFER) { spec = GetMailboxSpec(tocH,sumNum); if (GetTransferParams(menu,item,&toSpec,nil) && !SameSpec(&spec,&toSpec)) if (!ResolveAliasOrElse(&toSpec,&resolvToSpec,nil) && !SameSpec(&spec,&resolvToSpec)) { //Folder Carbon Copy - no FCC in Light! if (HasFeature (featureFcc) && fcc) Fcc((*tocH)->sums[sumNum].messH,&toSpec); else if (sumNum>=0) { #ifdef TWO if (!(modifiers&optionKey)) { #ifdef IMAP if (!(*tocH)->imapTOC) #endif AddXfUndo(tocH,TOCBySpec(&toSpec),sumNum); EzOpen(tocH,sumNum,0,modifiers,True,True); } #endif MoveMessage(tocH,sumNum,&toSpec,(modifiers&optionKey)!=0); } else MoveSelectedMessages(tocH,&toSpec,(modifiers&optionKey)!=0); } return(True); } return(False); } /********************************************************************** * TogglePOPD - put a message on or off a list, **********************************************************************/ Boolean TogglePOPD(OSType theType,short id, MessHandle messH) { Boolean was; if (was = IdIsOnPOPD(theType,id,SumOf(messH)->uidHash)) { RemIdFromPOPD(theType,id,SumOf(messH)->uidHash); } else { AddIdToPOPD(theType,id,SumOf(messH)->uidHash,False); } return(!was); } /************************************************************************ * PetePaneDraw - draw a pete record in a user pane ************************************************************************/ pascal void PetePaneDraw(ControlHandle cntl, SInt16 part) { #pragma unused(part) PETEHandle pte = (PETEHandle)GetControlReference(cntl); PeteUpdate(pte); } /************************************************************************ * MakeSubjectTXE - make a TERec for the subject ************************************************************************/ OSErr MakeSubjectTXE(MessHandle messH) { Str63 subject; Rect view; MyWindowPtr win=(*messH)->win; GrafPtr oldPort; OSErr err; PETEDocInitInfo pi; PETEHandle pte; ControlHandle cntl; long offset = 0; DECLARE_UPP(PetePaneDraw,ControlUserPaneDraw); GetPort(&oldPort); SetPort_(GetMyWindowCGrafPtr(win)); PSCopy(subject,SumOf(messH)->subj); MessSubjRect(win,&view); OffsetRect(&view,0,1); DefaultPII(win,False,0,&pi); pi.inRect = view; pi.docWidth = MessWi(win)-9; if (!(err=PeteCreate(win,&pte,peClearAllReturns,&pi))) { (*PeteExtra(pte))->frame = True; if(!MessFlagIsSet(messH, FLAG_UTF8) || !HasUnicode() || EncodingError(err = PeteInsertIntlText(pte,&offset,(Handle)(subject+1),-1,*subject,nil,UTF8_ENCODING,false,true))) { err=PETEInsertTextPtr(PETE,pte,0,subject+1,*subject,nil); } if(MessFlagIsSet(messH, FLAG_UTF8) && !HasUnicode()) { PeteLock(pte, 0, PeteLen(pte), peModLock); } PeteFontAndSize(pte,FontID,FontSize); PETEMarkDocDirty(PETE,pte,False); (*messH)->subPTE = pte; if (cntl=NewControl(GetMyWindowWindowPtr(win),&view,"",True,0,0,0,kControlUserPaneProc,(uLong)pte)) { INIT_UPP(PetePaneDraw,ControlUserPaneDraw); SetControlData(cntl,0,kControlUserPaneDrawProcTag,sizeof(PetePaneDrawUPP),(void*)&PetePaneDrawUPP); EmbedControl(cntl,win->topMarginCntl); } } CleanPII(&pi); if (err) WarnUser(PETE_ERR,err); SetPort_(oldPort); return(!err); } /************************************************************************ * AddXlateTables - add named translate tables to the current menu ************************************************************************/ short AddXlateTables(Boolean isOut,short nowId,Boolean ph,MenuHandle pmh) { Boolean added=CountMenuItems(pmh)==0; short i,n; Handle h; Str31 name; ResType type; short id; short defltId = ph ? 0 : GetPrefLong((isOut ? PREF_OUT_XLATE : PREF_IN_XLATE)); short item = 0; n = CountResources('taBL'); SetResLoad(False); for (i=1;i<=n;i++) if (h=GetIndResource_('taBL',i)) { GetResInfo(h,&id,&type,name); ReleaseResource_(h); if (!ResError() && *name && ((id%2)==0)==isOut) { if (!added) { added = True; AppendMenu(pmh,"\p-"); } MyAppendMenu(pmh,name); if (id==defltId) SetItemStyle(pmh,CountMenuItems(pmh),outline); if (id==nowId || (id==defltId && nowId==DEFAULT_TABLE)) { item = CountMenuItems(pmh); SetItemMark(pmh,CountMenuItems(pmh),checkMark); } } } SetResLoad(True); return(item); } /************************************************************************ * Menu2TableId - what table id does a menu choice represent? ************************************************************************/ Boolean Menu2TableId(TOCHandle tocH,MenuHandle pmh,short item, short *tableId) { short newId; short mark=0; ResType type; Str31 name; Handle h; Boolean isOut = (*tocH)->which == OUT; Boolean makeDefault = 0!=(CurrentModifiers()&shiftKey); if (makeDefault) GetItemStyle(pmh,item,(Style*)&mark); else GetItemMark(pmh,item,&mark); if (mark) newId = NO_TABLE; else { MyGetItem(pmh,item,name); SetResLoad(False); if (h=GetNamedResource('taBL',name)) { GetResInfo(h,&newId,&type,name); if (ResError()) newId = *tableId; /* do nothing */ ReleaseResource_(h); } SetResLoad(True); } if (makeDefault) { NumToString(newId,name); SetPref((isOut?PREF_OUT_XLATE:PREF_IN_XLATE),name); return(False); } else { *tableId = newId; return(True); } } /************************************************************************ * SetSubject - set the subject in a message summary ************************************************************************/ void SetSubject(TOCHandle tocH,short sumNum,PStr sub) { Str63 oldSubj; Str255 title; MessHandle messH = (*tocH)->sums[sumNum].messH; PSCopy(oldSubj,(*tocH)->sums[sumNum].subj); if (!EqualString(oldSubj,sub,True,True)) { PSCopy((*tocH)->sums[sumNum].subj,sub); InvalSum(tocH,sumNum); #ifdef NEVER CalcSumLengths(tocH,sumNum); #endif TOCSetDirty(tocH,true); if (messH) { MakeMessTitle(title,tocH,sumNum,True); SetWTitle_(GetMyWindowWindowPtr((*messH)->win),title); if ((*messH)->subPTE) { if(MessFlagIsSet(messH,FLAG_UTF8) && HasUnicode()) PeteSetIntlText((*messH)->subPTE,sub+1,-1,*sub,nil,UTF8_ENCODING); else PeteSetTextPtr((*messH)->subPTE,sub+1,*sub); } } SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, false); // Notify search window } } /************************************************************************ * SetSender - set the sender in a message summary ************************************************************************/ void SetSender(TOCHandle tocH,short sumNum,PStr sender) { Str63 oldSender; Str255 title; MessHandle messH = (*tocH)->sums[sumNum].messH; PCopy(oldSender,(*tocH)->sums[sumNum].from); if (!EqualString(oldSender,sender,True,True)) { PCopy((*tocH)->sums[sumNum].from,sender); InvalSum(tocH,sumNum); #ifdef NEVER CalcSumLengths(tocH,sumNum); #endif TOCSetDirty(tocH,true); if (messH) { MakeMessTitle(title,tocH,sumNum,True); SetWTitle_(GetMyWindowWindowPtr((*messH)->win),title); } } } /************************************************************************ * SetFlag - set one of the message flags ************************************************************************/ void SetFlag(TOCHandle tocH,short sumNum,long flag,Boolean on) { if (on) (*tocH)->sums[sumNum].flags |= flag; else (*tocH)->sums[sumNum].flags &= ~flag; if ((*tocH)->sums[sumNum].messH) InvalTopMargin((*(*tocH)->sums[sumNum].messH)->win); TOCSetDirty(tocH,true); } /************************************************************************ * SetOpt - set one of the message options ************************************************************************/ void SetOpt(TOCHandle tocH,short sumNum,long flag,Boolean on) { if (on) (*tocH)->sums[sumNum].opts |= flag; else (*tocH)->sums[sumNum].opts &= ~flag; TOCSetDirty(tocH,true); } #ifdef TWO /************************************************************************ * OpenSelectedAtt - open selected attachments (or tell if attachments are selected) ************************************************************************/ Boolean AttIsSelected(MyWindowPtr win,PETEHandle pte,long startWith,long endWith,short what,long *aStart, long *aEnd) { Handle textH; UPtr begin, end; /* beginning and end of all text */ UPtr sBegin, sEnd; /* beginning and end of selected bit */ UPtr lBegin, lEnd; /* beginning and end of current line */ Str255 line; long selStart, selEnd; Byte state; Boolean found = false; FSSpec spec; Boolean colorThem = 0!=(what&attColor); Boolean selectThem = 0!=(what&attSelect); Boolean openThem = 0!=(what&attOpen); Boolean finderSelect = 0!=(what&attFinder); Boolean printThem = 0!=(what&attPrint); if (!PeteIsValid(pte) && win) pte = win->pte; if (!PeteIsValid(pte)) return(False); if (PeteIsValid(pte) && !win) win = (*PeteExtra(pte))->win; if (PeteGetTextAndSelection(pte,&textH,&selStart,&selEnd)) return(False); if (!textH || !GetHandleSize(textH)) return(False); state = HGetState(textH); /* * set initial values */ begin = LDRef(textH); end = begin + GetHandleSize(textH); sBegin = begin + ((startWith<0)?selStart:startWith); sEnd = begin + ((endWith<0)?selEnd:endWith); sBegin = MAX(sBegin,begin); if (sBegin>=end) goto done; sEnd = MIN(end,sEnd); if (sEnd<=begin) goto done; /* * expand "selection" to whole lines */ while (sBegin>begin && sBegin[-1]!='\015') sBegin--; if (selStart!=selEnd && sEnd[-1]=='\015') sEnd--; else while (sEndwin; WindowPtr pteWinWP = GetMyWindowWindowPtr (pteWin); TOCHandle tocH; if (GetWindowKind(pteWinWP)==MESS_WIN || (GetWindowKind(pteWinWP)==MBOX_WIN && // Don't display attachment icons in search windows unless it's in the preview pane (!IsSearchWindow(pteWinWP) || ((tocH=(TOCHandle)GetMyWindowPrivateData(pteWin)) && (*tocH)->previewPTE==pte)))) PeteFileGraphicRange(pte,lBegin-begin,lEnd-begin,&spec,fgAttachment+fgDontCopyToClip+(PrefIsSet(PREF_NO_INLINE)?0:fgDisplayInline)); else #endif URLStyle(pte,lBegin-begin,lEnd-begin,False); } if (selectThem) { PeteSelect(nil,pte,lBegin-begin,lEnd-begin); PeteSetDirty(pte); } if (openThem||printThem) OpenAttLine(pte,line,finderSelect,printThem); if (aStart) *aStart = lBegin-begin; if (aEnd) *aEnd = lEnd-begin; } } done: HSetState(textH,state); return(found); } /************************************************************************ * IsAttLine - is a line an attachment line? ************************************************************************/ Boolean IsAttLine(PStr line,Boolean wantToOpen) { return (!AttLine2Spec(line,nil,wantToOpen)); } /************************************************************************ * OpenAttLine - open an attachment described by a line ************************************************************************/ OSErr OpenAttLine(PETEHandle pte,PStr line,Boolean finderSelect,Boolean printIt) { short err = noErr; FSSpec spec; if (!(err=AttLine2Spec(line,&spec,True))) { #ifdef IMAP // If this is an IMAP attachment, download it. if (IsIMAPAttachmentStub(&spec)) { // fetch the attachment, wait for it to be downloaded. // spec will point to the downloaded attachment. err = FetchIMAPAttachment(pte,&spec,true); } #endif if (err==noErr) err = OpenOtherDoc(&spec,finderSelect,printIt,nil); } return(err); } /************************************************************************ * AttLine2Spec - get an FSSpec from an attachment line * Heuristic * - must end in 8 hex digits in []'s * - must have at least two colons * - text between first two colons must be volume name ************************************************************************/ OSErr AttLine2Spec(PStr line,FSSpecPtr spec,Boolean wantToOpen) { Str31 colostomy; /* bad joke, I know */ Str63 fName; PStr colon1, colon2; UPtr spot; long fid=0; short dig=8; short err; Boolean isFolder = nil!=PFindSub("\p (fldr/macs) (",line); //crude check for our folder creator TrimWhite(line); if (!IsWordChar[line[1]]) return fnfErr; // don't do quoted attachment lines /* * pick out the fid */ if (line[*line] != ']' && line[*line]!=')') return(fnfErr); if (*line<10 || line[*line-9]!='[' && line[*line-9]!='(') return(fnfErr); for (colon1=line+*line-8;dig--;colon1++) if ('0'<=*colon1 && *colon1<='9') fid = (fid<<4) | (*colon1-'0'); else if ('a'<=*colon1 && *colon1<='f') fid = (fid<<4) | (10+*colon1-'a'); else if ('A'<=*colon1 && *colon1<='F') fid = (fid<<4) | (10+*colon1-'A'); else return(fnfErr); /* * pick out the volume name */ colon1 = strchr(line+1,':'); if (!colon1) return(fnfErr); colon2 = strchr(colon1+2,':'); if (!colon2) return(fnfErr); if (spec) { MakePStr(colostomy,colon1+2,colon2-colon1-1); MakePStr(fName,colon2+1,*line-(colon2-line)-23); if (*fName<1 || *fName>31) err = fnfErr; else { if (isFolder) { spec->vRefNum = GetMyVR(colostomy); spec->parID = fid; err = ParentSpec(spec,spec); if (!err) err = GetDirName(nil,spec->vRefNum,fid,spec->name); } else err = FSResolveFID(GetMyVR(colostomy),fid,spec); if (err || !(spot=PPtrFindSub(fName,spec->name+1,*spec->name)) || spot!=spec->name+1) { #ifdef IMAP // // The names of IMAP attachments could get macified once downloaded. // if the name the fileID resolved to is a Mac2OtherName()ed fName, then call it a match. // Str31 otherName; // check only if FSResolveFID succeeded. if (err == noErr) Mac2OtherName(otherName, spec->name); else otherName[0] = 0; // If fName and the Mac2OtherName match, we've found the file. if ((otherName[0]==0) || !(spot=PPtrFindSub(fName,otherName+1,*otherName)) || spot!=otherName+1) { // otherwise, call it a match only if a file with the same name exists in the attachments folder. #endif GetAttFolderSpec(spec); PCopy(spec->name,fName); err = FSpExists(spec); if (err && IsIMAPPers(CurPers)) if (!(err = GetIMAPAttachFolder(spec))) { PCopy(spec->name,fName); err = FSpExists(spec); } // the increasing prevalence of long filenames means we increasingly // cannot find attachments after a disk move. So, if this is a mangled // name, look for the non-hex part as a prefix of a filename in the attachments folder if (err && PrefIsSet(PREF_PROMISCUOUS_REATTACH)) { Str31 stubName; Str31 stubExtension; // split into fershlugginer extension SplitPerfectlyGoodFilenameIntoNameAndQuoteExtensionUnquote(fName,stubName,stubExtension,31); if (!LopPoundHexDigits(stubName)) { // Ok, now we have determined that the filename is of the form: // stubName # hex-digits stubExtension CInfoPBRec hfi; Str31 foundFileName; Str31 possibleFileName; Boolean lastOneMatched = false; Zero(hfi); hfi.hFileInfo.ioNamePtr = possibleFileName; while (!DirIterate(spec->vRefNum,spec->parID,&hfi)) { Str31 possibleStub, possibleExtension; SplitPerfectlyGoodFilenameIntoNameAndQuoteExtensionUnquote(possibleFileName,possibleStub,possibleExtension,31); if (!(err = LopPoundHexDigits(possibleStub))) // a shortened filename if (StringSame(possibleExtension,stubExtension)) if (!striscmp(possibleStub+1,stubName+1) && *possibleStub > 10) { // we have a match! if (lastOneMatched) { // Sadness. We match more than one file. err = dupFNErr; break; } else { PCopy(foundFileName,possibleFileName); lastOneMatched = true; continue; } } // we only get here if the current possible file does NOT match. // if that's so, and the *last* one matched, then there is one and only // one match, and we shout Bingo!, losing our dentures in the process if (lastOneMatched) { PCopy(spec->name,foundFileName); err = noErr; // hurray! break; } } } } } #ifdef IMAP } #endif } if (wantToOpen && err) WarnUser(ATTACH_GONE,err); return(err); } else return(colon2-colon1>1 && colon2-colon1<33 ? noErr : fnfErr); } /************************************************************************ * LopPoundHexDigits - strip the # and hex digits from a shortened filename * returns fnfErr if no # or followed by non-hex digits * returns dupFNErr if remaining filename is very short. * null-terminates the string for convenience' sake ************************************************************************/ OSErr LopPoundHexDigits(PStr fileName) { Str31 hexDigits; PStr poundSign = PIndex(fileName,'#'); short i; if (!poundSign) return fnfErr; MakePStr(hexDigits,poundSign+1,*fileName-(poundSign-fileName+2)); if (poundSign-fileName < 10) return dupFNErr; for (i=1;i<*hexDigits;i++) if (!IsHexDig(hexDigits[i])) return fnfErr; *fileName = poundSign-fileName-1; PTerminate(fileName); return noErr; } /************************************************************************ * RelLine2Spec - get an FSSpec from a "related" line * - begin with "related:" * - must have at least two colons * - text between first two colons must be volume name ************************************************************************/ OSErr RelLine2Spec(PStr line,FSSpecPtr spec,uLong *cid, uLong *relURL, uLong *absURL) { Str255 token; Str31 name; UPtr spot = line+1; Byte colon[2]; short vRef; uLong fid; OSErr err; colon[0] = ':'; colon[1] = 0; // related if (!PToken(line,token,&spot,colon)) return(fnfErr); if (!EqualStrRes(token,MIME_RELATED)) return(fnfErr); // space if (!PToken(line,token,&spot,colon)) return(fnfErr); // volume name if (!PToken(line,token,&spot,colon)) return(fnfErr); vRef = GetMyVR(token); if (vRef==REAL_BIG) return(fnfErr); // filename if (!PToken(line,token,&spot,colon)) return(fnfErr); PSCopy(name,token); FixURLString(name); // fid if (!PToken(line,token,&spot,colon)) return(fnfErr); if (*token!=8) return(fnfErr); Hex2Bytes(token+1,8,(void*)&fid); // cid if (!PToken(line,token,&spot,colon)) return(fnfErr); if (*token!=8) return(fnfErr); if (cid) Hex2Bytes(token+1,8,(void*)cid); // rel if (!PToken(line,token,&spot,colon)) return(fnfErr); if (*token!=8) return(fnfErr); if (relURL) Hex2Bytes(token+1,8,(void*)relURL); // abs if (!PToken(line,token,&spot,colon)) return(fnfErr); if (*token!=8) return(fnfErr); if (absURL) Hex2Bytes(token+1,8,(void*)absURL); if (spec) { err = FSResolveFID(vRef,fid,spec); if (err) { SubFolderSpec(PARTS_FOLDER,spec); PSCopy(spec->name,name); err = FSpExists(spec); if (err==fnfErr) { FixURLString(name); if (*name!=*spec->name) err = FSpExists(spec); } } return(err); } else return(noErr); } #endif #pragma segment Balloon /************************************************************************ * MessHelp - help for the message window ************************************************************************/ void MessHelp(MyWindowPtr win,Point mouse) { Rect subRect,bodRect,priorRect,r; MessHandle messH = Win2MessH(win); Rect blahRect; Rect pencilRect; Rect fixedRect; PETEDocInfo info; ControlHandle cntl,ctlGetGraphics; PETEGetDocInfo(PETE,TheBody,&info); bodRect = info.docRect; PETEGetDocInfo(PETE,(*messH)->subPTE,&info); subRect = info.docRect; if (PtInRect(mouse,&bodRect)) MyBalloon(&bodRect,90,0,MESS_HELP,0,nil); else if (win->topMargin) { GetPriorityRect(win,&priorRect); GetBlahRect(win,1,&blahRect); GetBlahRect(win,0,&pencilRect); if (!FontIsFixed) GetBlahRect(win,2,&fixedRect); if (PtInRect(mouse,&subRect)) MyBalloon(&subRect,90,0,SUB_EDIT_HELP,0,nil); else if (!FontIsFixed && PtInRect(mouse,&fixedRect) && (cntl=FindControlByRefCon(win,mcFixed))) MyBalloon(&fixedRect,90,0, GetControlValue(cntl)?MESS_FIXED_HELP:NO_MESS_FIXED_HELP, 0,nil); else if (PtInRect(mouse,&pencilRect)) MyBalloon(&pencilRect,90,0, MessOptIsSet(Win2MessH(win),OPT_WRITE)?MESS_WRITE_HELP:NO_MESS_WRITE_HELP, 0,nil); else if (PtInRect(mouse,&blahRect)) MyBalloon(&blahRect,90,0, MessFlagIsSet(Win2MessH(win),FLAG_SHOW_ALL)?SHOW_ALL_HELP:NO_SHOW_ALL_HELP, 0,nil); else if (PtInRect(mouse,&priorRect)) PriorMenuHelp(win,&priorRect); else if ((ctlGetGraphics = FindControlByRefCon(win,mcGetGraphics)) && IsControlVisible(ctlGetGraphics) && PtInControl(mouse,ctlGetGraphics)) { Rect getGraphicsRect; GetControlBounds(ctlGetGraphics,&getGraphicsRect); MyBalloon(&getGraphicsRect,90,0, IsControlActive(ctlGetGraphics)?GET_GRAPHICS_HELP:NO_GET_GRAPHICS_HELP, 0,nil); } #ifdef TWO else { GetServerRect(win,3,&r); if (PtInRect(mouse,&r)) MyBalloon(&r,90,0,MESS_DRAG_HELP,0,nil); else { r = win->contR; r.bottom = win->topMargin; r.top = GetRLong(COMP_TOP_MARGIN); if (r.bottom>r.top && PtInRect(mouse,&r) && MessOptIsSet(messH,OPT_RECEIPT)) MyBalloon(&r,90,0,MESS_NOTIFY_HELP,0,nil); else if ((*messH)->hasDelIcon) { GetServerRect(win,1,&r); if (PtInRect(mouse,&r)) { if ((*messH)->hasFetchIcon) MyBalloon(&r,90,0,MESS_FETCH_HELP+(IdIsOnPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),FETCH_ID,SumOf(messH)->uidHash)?0:1),0,nil); } else { GetServerRect(win,2,&r); if (PtInRect(mouse,&r)) MyBalloon(&r,90,0,MESS_DELETE_HELP+(IdIsOnPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),DELETE_ID,SumOf(messH)->uidHash)?0:1),0,nil); else { GetServerRect(win,0,&r); if (PtInRect(mouse,&r)) MyBalloon(&r,90,0,MESS_SERVER_HELP,0,nil); } } } } } #endif } } /************************************************************************ * PriorMenuHelp - help for the priority menu ************************************************************************/ void PriorMenuHelp(MyWindowPtr win,Rect *priorRect) { Str255 s1,s2; short p = Prior2Display((SumOf(Win2MessH(win)))->priority); if (!HMIsBalloon()) { GetRString(s1,PRIOR_MENU_HELP); GetRString(s2,PRIOR_STRN+5+p); PCat(s1,s2); MyBalloon(priorRect,90,0,0,0,s1); } } #pragma segment MessWin /********************************************************************** * SaveMess - save a message into its own mailbox **********************************************************************/ Boolean SaveMess(MyWindowPtr win) { MessHandle messH = Win2MessH(win); MSumPtr oldSum, newSum; TOCHandle tocH = (*messH)->tocH; long fromLen; Handle text=MessText(messH); HeadSpec hSpec; Accumulator enriched; OSErr err = noErr; Boolean richSave = False; Str255 title; Boolean blahBlah = MessFlagIsSet(messH,FLAG_SHOW_ALL); /* * rich text? */ if (!blahBlah) { SetMessRich(messH); Zero(enriched); if (MessIsRich(messH)) { if (!(err=AccuInit(&enriched))) { CompHeadFind(messH,0,&hSpec); if (!(*messH)->extras.offset || !(err=AccuAddHandle(&enriched,(*messH)->extras.data))) if (!(err=AccuAddFromHandle(&enriched,text,0,hSpec.value))) { if (MessOptIsSet(messH,OPT_HTML)) { if (!(err=HTMLPreamble(&enriched,PCopy(title,SumOf(messH)->subj),0,True))) { NumToString(SumOf(messH)->msgIdHash,title); if (!(err=BuildHTML(&enriched,TheBody,nil,GetHandleSize(text),hSpec.value,nil,nil,1,title,nil,nil))) err=HTMLPostamble(&enriched,True); } } else { err=BuildEnriched(&enriched,TheBody,nil,PETEGetTextLen(PETE,TheBody),hSpec.value,nil,True); } if (!err) { AccuTrim(&enriched); err = SaveTextAsMessage(nil,enriched.data,(*messH)->tocH,&fromLen); ZapHandle(enriched.data); if (err) return(False); richSave = True; } } } } } if (err) { WarnUser(CANT_SAVE_RICH,err); ZapHandle(enriched.data); ClearMessFlag(messH,FLAG_RICH); ClearMessOpt(messH,OPT_HTML); } /* * save plain text to the end of the mailbox */ if (!richSave) if (SaveTextAsMessage(!blahBlah && (*messH)->extras.offset ? (*messH)->extras.data:nil,text,(*messH)->tocH,&fromLen)) return(False); /* * copy the offset parameters */ oldSum = &(*tocH)->sums[(*messH)->sumNum]; newSum = &(*tocH)->sums[(*tocH)->count-1]; (*tocH)->usedK -= oldSum->length/1024; /* we're going to waste the old one */ (*tocH)->updateBoxSizes = true; oldSum->offset = newSum->offset; oldSum->length = newSum->length; oldSum->bodyOffset = newSum->bodyOffset; if (newSum->seconds && !(oldSum->flags&FLAG_OUT)) { oldSum->seconds = newSum->seconds; oldSum->origZone = newSum->origZone; } // outgoing IMAP messages have the recipient stored in the From: field of the summary. // Save it. if ((*tocH)->imapTOC && (oldSum->opts&OPT_IMAP_SENT)) { if (*oldSum->from) PCopy(newSum->from,oldSum->from); } else { if (*newSum->from) PCopy(oldSum->from,newSum->from); } InvalSum(tocH,(*messH)->sumNum); // retitle window if ((*messH)->win) { MakeMessTitle(title,tocH,(*messH)->sumNum,true); SetWTitle_(GetMyWindowWindowPtr((*messH)->win),title); } /* * shrink the .toc */ (*tocH)->count--; SetHandleBig_(tocH,GetHandleSize_(tocH)-sizeof(MSumType)); /* * reset this to the right stuff */ (*messH)->weeded = fromLen + (blahBlah ? 0 : (*messH)->extras.offset); /* * rescan for URL's, quotes */ PeteSetURLRescan(TheBody,0); /* * not dirty any more */ PeteCleanList(win->pteList); win->isDirty = False; /* * cache is toast */ ZapHandle(SumOf(messH)->cache); SetMessOpt(messH,OPT_EDITED); ZapHandle((*messH)->etlFiles); // we've recorded them permanently now // let the preview pane know if ((*tocH)->previewID==SumOf(messH)->serialNum) (*tocH)->previewID = 0; return(True); } /********************************************************************** * ShowMessageSeparator - show the message separator **********************************************************************/ void ShowMessageSeparator(PETEHandle pte,Boolean center) { long body = BodyOffset(PeteText(pte)); PeteSelect(nil,pte,body,body); PeteCalcOn(pte); if (center) { PeteScroll(pte,0,0); PeteScroll(pte,0,pseCenterSelection); } else { PeteScroll(pte,0,0x7fff); PeteScroll(pte,0,pseSelection); } } /********************************************************************** * MessMenu - handle menu choices for a message window **********************************************************************/ Boolean MessMenu(MyWindowPtr win,int menu,int item,short modifiers) { MessHandle messH = (MessHandle)GetMyWindowPrivateData(win); TOCHandle tocH = (*messH)->tocH; int sumNum = (*messH)->sumNum; Boolean result = False; short tableId; Boolean turbo = False; uLong ezOpenSerialNum; TextAddrHandle addr=nil; switch (menu) { case FILE_MENU: switch(item) { case FILE_PRINT_ITEM: case FILE_PRINT_ONE_ITEM: MessFocus(messH,TheBody); if ((modifiers&shiftKey) && AttIsSelected(win,win->pte,-1,-1,attOpen+attPrint,nil,nil)) ;// done else PrintOneMessage((*messH)->win,(modifiers&shiftKey)!=0,item==FILE_PRINT_ONE_ITEM); result = True; break; case FILE_SAVE_ITEM: if (win->isDirty) { if ((*messH)->subPTE && PeteIsDirty((*messH)->subPTE)) MessSaveSub(messH); if ((*messH)->subPTE && PeteIsDirty((*messH)->bodyPTE)) SaveMess(win); PeteCleanList(win->pteList); win->isDirty = False; } result = True; break; case FILE_BROWSE_ITEM: ExportHTML(messH); result = True; break; case FILE_SAVE_AS_ITEM: SaveMessageAs(messH); result = True; break; } break; case SERVER_HIER_MENU: #ifdef IMAP ServerMenuChoice((*messH)->tocH,(*messH)->sumNum,item, (modifiers&shiftKey)!=0); #else ServerMenuChoice((*messH)->tocH,(*messH)->sumNum,item); #endif result = True; break; case EDIT_MENU: switch (item) { #ifdef SPEECH_ENABLED case EDIT_SPEAK_ITEM: if (!(modifiers & shiftKey)) { (void) SpeakMessage (nil, (*messH)->tocH, (*messH)->sumNum, speakSender | speakSubject | speakBody, false); return (true); } break; #endif default: return(False); break; } break; case MESSAGE_MENU: MessFocus(messH,TheBody); switch (item) { case MESSAGE_REPLY_ITEM: { Boolean all, quote, self; ReplyDefaults(modifiers,&all,&self,"e); DoReplyMessage(win,all,self,quote,True,0,True,True,True); } result = True; break; case MESSAGE_REDISTRIBUTE_ITEM: DoRedistributeMessage(win,0,PrefIsSetOrNot(PREF_TURBO_REDIRECT,modifiers,optionKey),!(modifiers&shiftKey),True); result = True; break; case MESSAGE_FORWARD_ITEM: DoForwardMessage(win,0,True); result = True; break; case MESSAGE_SALVAGE_ITEM: DoSalvageMessage(win,False); result = True; break; case MESSAGE_JUNK_ITEM: case MESSAGE_NOTJUNK_ITEM: Junk(tocH,sumNum,item==MESSAGE_JUNK_ITEM,true); break; case MESSAGE_DELETE_ITEM: EzOpen((*messH)->openedFromTocH,-1,(*messH)->openedFromSerialNum,modifiers,True,True); NoSaves = !MessOptIsSet(Win2MessH(win),OPT_WRITE); if (CloseMyWindow(GetMyWindowWindowPtr(win))) { #ifdef TWO AddXfUndo(tocH,GetTrashTOC(),sumNum); #endif DeleteMessage(tocH,sumNum,(modifiers&(optionKey|shiftKey))==(optionKey|shiftKey)); } NoSaves = False; result = True; break; } break; case REPLY_WITH_HIER_MENU: //Stationery - no support for this in Light if (HasFeature (featureStationery)) { Boolean all, quote, self; ReplyDefaults(modifiers,&all,&self,"e); DoReplyMessage(win,all,self,quote,True,item,True,True,True); } result = True; break; case FORWARD_TO_HIER_MENU: DoForwardMessage(win,(addr=MenuItem2Handle(menu,item)),True); result = True; break; case REDIST_TO_HIER_MENU: #ifdef TWO turbo = PrefIsSetOrNot(PREF_TURBO_REDIRECT,modifiers,optionKey); if (turbo) EzOpen(tocH,sumNum,0,modifiers,True,True); #endif DoRedistributeMessage(win,(addr=MenuItem2Handle(menu,item)),turbo,!(modifiers&shiftKey),True); result = True; ZapHandle(addr); break; case TABLE_HIER_MENU: if (Menu2TableId(tocH,GetMHandle(TABLE_HIER_MENU),item,&tableId)) SetMessTable(tocH,sumNum,tableId); result = True; break; case STATE_HIER_MENU: SetState(tocH,sumNum,Item2Status(item)); result = True; break; case LABEL_HIER_MENU: item = Menu2Label(item); SetSumColor(tocH,sumNum,item); result = True; break; case SPECIAL_MENU: switch(AdjustSpecialMenuSelection(item)) { case SPECIAL_MAKE_NICK_ITEM: if (modifiers&shiftKey) MakeNickFromSelection(win); else MakeMessNick(win,modifiers); result = True; break; case SPECIAL_MAKEFILTER_ITEM: DoMakeFilter(win); break; #ifdef TWO case SPECIAL_FILTER_ITEM: ezOpenSerialNum = (*messH)->ezOpenSerialNum; FilterMessage(flkManual,tocH,sumNum); if (FrontWindow_()!=GetMyWindowWindowPtr(win) && (*tocH)->count) if (ezOpenSerialNum) EzOpen(tocH,sumNum,ezOpenSerialNum,modifiers,False,False); else BoxSelectAfter((*tocH)->win,sumNum); result = True; break; #endif } break; case PRIOR_HIER_MENU: SetPriority(tocH,sumNum,NewPrior(item,(*tocH)->sums[sumNum].priority)); result = True; break; case PERS_HIER_MENU: //No Personalities menu in Light if (HasFeature (featureMultiplePersonalities)) { Str63 name; SetPers(tocH,sumNum,FindPersByName(MyGetItem(GetMHandle(menu),item,name)),True); return(True); } break; #ifdef DEBUG case DEBUG_MENU: if(item == dbTestSpooler) { SpoolMessage(messH, nil, 0); return(True); } break; #endif default: if ((*messH)->openedFromTocH != (*messH)->tocH) sumNum = FindSumBySerialNum((*messH)->openedFromTocH,(*messH)->openedFromSerialNum); result = TransferMenuChoice(menu,item,(*messH)->openedFromTocH,sumNum,modifiers,False); break; } ZapHandle(addr); return(result); } /************************************************************************ * Fcc - add a mailbox to ************************************************************************/ void Fcc(MessHandle messH,FSSpecPtr box) { Str255 scratch; HeadSpec hs; long start; Str255 path; //Folder Carbon Copy No FCC in Light if (!HasFeature (featureFcc)) return; UseFeature (featureFcc); if (Box2Path(box,path)) PCopy(path,box->name); *scratch = 0; PCatC(scratch,'"'); PCatR(scratch,FCC_PREFIX); PCat(scratch,path); PCatC(scratch,'"'); SetPort(GetMyWindowCGrafPtr((*messH)->win)); if (CompHeadFind(messH,BCC_HEAD,&hs)) { PetePrepareUndo(TheBody,peCantUndo,hs.stop,hs.stop,&start,nil); InsertCommaIfNeedBe(TheBody,&hs); CompHeadAppendStr(TheBody,&hs,scratch); CompHeadFind(messH,BCC_HEAD,&hs); PeteSelect(nil,TheBody,hs.stop,hs.stop); PeteFinishUndo(TheBody,peUndoPaste,start,hs.stop); ClearMessFlag(messH,FLAG_KEEP_COPY); CompIBarUpdate(messH); } } /********************************************************************** * EzOpenFind - find next candidate for EzOpen. If none, return -1 **********************************************************************/ short EzOpenFind(TOCHandle tocH, short origSum) { short newSum; long ez = GetPrefLong(PREF_NO_EZ_OPEN); if (origSum<(*tocH)->count-1) { if (ez==1) return(origSum+1); // say no more if ((*tocH)->sums[origSum+1].state==UNREAD) { if (ez==3 || ez==2) return(origSum+1); // say no more if (ez==4) { Str63 s1, s2; PSCopy(s1,(*tocH)->sums[origSum].subj); PSCopy(s2,(*tocH)->sums[origSum+1].subj); if (!SubjCompare(s1,s2)) return origSum+1; } } } if (ez==2) for (newSum=origSum+2;newSum<(*tocH)->count;newSum++) if ((*tocH)->sums[newSum].state==UNREAD) return(newSum); return(-1); } /************************************************************************ * EzOpen - easy open, if things are right ************************************************************************/ void EzOpen(TOCHandle tocH, short sumNum,uLong serialNum,long modifiers,Boolean hideFront, Boolean willDelete) { WindowPtr oldFrontWP = FrontWindow_(); short newSumNum; Boolean oldVis; short ez; Boolean preview = false; if ((*tocH)->win) { if (sumNum < 0) { sumNum = FindSumBySerialNum(tocH,serialNum); if (sumNum < 0) return; // unable to find message with this serial number } /* * if the frontmost window is our cue, grab its hash value */ if (hideFront) { TOCHandle realTOC; short realSum; realTOC = GetRealTOC(tocH,sumNum,&realSum); if ((*realTOC)->sums[realSum].messH) serialNum = (*(*realTOC)->sums[realSum].messH)->ezOpenSerialNum; else if ((*tocH)->previewPTE && (*tocH)->previewID) { preview = true; serialNum = (*tocH)->ezOpenSerialNum; } } else sumNum=FindSumBySerialNum(tocH,serialNum); /* * does message exist? */ if (serialNum && (newSumNum=FindSumBySerialNum(tocH,serialNum))>=0 && (*tocH)->sums[newSumNum].state==UNREAD) /* we have our man */; else { /* * if we're not hiding the front window, that means it was deleted before * the easy open; back up one */ if (!hideFront) newSumNum = EzOpenFind(tocH,sumNum-1); else newSumNum = EzOpenFind(tocH,sumNum); } /* * did we find one to easy open? */ if (newSumNum>=0) { ez = GetPrefLong(PREF_NO_EZ_OPEN); if (modifiers & shiftKey) ez = 0; SelectBoxRange(tocH,newSumNum,newSumNum,False,-1,-1); if (ez) { if (preview) Preview(tocH,newSumNum); else { MyWindowPtr oldFront = GetWindowMyWindowPtr (oldFrontWP); if (hideFront && oldFront) { oldVis = oldFront->hideme; oldFront->hideme = True; UglyHackFrontWindow = oldFrontWP; } BoxOpen((*tocH)->win); if (hideFront && oldFront) oldFront->hideme = oldVis; UglyHackFrontWindow = nil; } } BoxCenterSelection((*tocH)->win); } else { if (willDelete) newSumNum = sumNum==(*tocH)->count-1 ? sumNum-1 : sumNum+1; else newSumNum = sumNum; newSumNum = MIN(newSumNum,(*tocH)->count-1); newSumNum = MAX(newSumNum,0); SelectBoxRange(tocH,newSumNum,newSumNum,False,-1,-1); } } } /************************************************************************ * SaveMessageAs - save a message in text only form ************************************************************************/ void SaveMessageAs(MessHandle messH) { long creator; short refN; short err; Str31 name; FSSpec spec; Str255 scratch; ModalFilterYDUPP filter=nil; NicknameWatcherFocusChange((**messH).win->pte); /* MJN */ /* * tickle stdfile */ creator = DefaultCreator(); MakeMessFileName((*messH)->tocH,(*messH)->sumNum,spec.name); #ifdef TWO //Stationery - prevent the user from saving a file as stationery Stationery = (HasFeature (featureStationery) && (*(*messH)->tocH)->which==OUT && !DateWarning(false)) ? False : 2; /* 2 is our signal to the filter to hide the item */ #endif if (err=SFPutOpen(&spec,creator,'TEXT',&refN,nil,filter,SAVEAS_DLOG,nil,nil,nil)) return; /* * do it */ if (Stationery) { // Need to save with different msg ID hash TOCHandle tocH = (*messH)->tocH; short sumNum = (*messH)->sumNum; long saveHash = (*tocH)->sums[sumNum].msgIdHash; NewMessageId(scratch); SetHash(tocH,sumNum,MIDHash(scratch+1,*scratch)); err = SaveAsToOpenFile(refN,messH); SetHash(tocH,sumNum,saveHash); } else err = SaveAsToOpenFile(refN,messH); /* * close */ (void) MyFSClose(refN); /* * report error */ if (err) { FileSystemError(COULDNT_SAVEAS,name,err); FSpDelete(&spec); } } /************************************************************************ * SaveAsToOpenFile - continue the message saving process ************************************************************************/ short SaveAsToOpenFile(short refN,MessHandle messH) { OSErr err; unsigned long saveOpts = SumOf(messH)->opts; err = SaveAsToOpenFileLo(refN,messH); if (Stationery && !err && ((saveOpts&OPT_HAS_SPOOL) != (SumOf(messH)->opts&OPT_HAS_SPOOL))) { // The first save didn't save the OPT_HAS_SPOOL flag properly. Save again. // Shameful, but this should happen only the first time a stationery // is saved with graphics. SetFPos(refN,fsFromStart,0); err = SaveAsToOpenFileLo(refN,messH); } return err; } /************************************************************************ * SaveAsToOpenFileLo - continue the message saving process ************************************************************************/ static short SaveAsToOpenFileLo(short refN,MessHandle messH) { long bytes; short err=0; UPtr where; UHandle text; Str63 scratch; Boolean para = PrefIsSet(PREF_PARAGRAPHS); Boolean exclHead = PrefIsSet(PREF_EXCLUDE_HEADERS) || (SumOf(messH)->flags&FLAG_SUBSEQUENT); Boolean isOut = (*(*messH)->tocH)->which==OUT; HeadSpec hs; Accumulator enriched; StackHandle stack = nil; Zero(enriched); SetMessRich(messH); PETEGetRawText(PETE,TheBody,&text); CompHeadFind(messH,0,&hs); #ifdef TWO //Stationery - prevent the user from saving a file as stationery if (HasFeature (featureStationery) && Stationery==True) { if (err = SaveStationeryStuff(refN,messH)) return(err); para = exclHead = False; } #endif /* * build and save date header */ if (!exclHead && isOut && SumOf(messH)->seconds) { BuildDateHeader(scratch,SumOf(messH)->seconds); PCatC(scratch,'\015'); FSWriteP(refN,scratch); } where = LDRef(text); bytes = GetHandleSize(text); if (exclHead) { where = LDRef(text)+hs.value; bytes = hs.stop-hs.value; while (bytes > 1 && *where == '\015') {where++;bytes--;} while (bytes>1&&where[bytes-1]==where[bytes-2]&&where[bytes-1]=='\015') bytes--; } //Stationery - prevent the user from saving a file as stationery if (HasFeature (featureStationery) && Stationery) { if (MessIsRich(messH) && !(err=AccuInit(&enriched))) { AccuAddPtr(&enriched,where,hs.start); AccuAddChar(&enriched,'\015'); if (MessOptIsSet(messH,OPT_HTML)) { if (!(err=StackInit(sizeof(FSSpec),&stack))) if (!(err=HTMLPreamble(&enriched,PCopy(scratch,SumOf(messH)->subj),0,True))) if (!(err=BuildHTML(&enriched,TheBody,nil,PETEGetTextLen(PETE,TheBody),hs.value,nil,nil,1,CompGetMID(messH,scratch),nil,nil))) err=HTMLPostamble(&enriched,True); } else err = BuildEnriched(&enriched,TheBody,nil,PETEGetTextLen(PETE,TheBody),hs.value,nil,True); ZapHandle(stack); if (!err) { AccuTrim(&enriched); text = enriched.data; where = LDRef(enriched.data); bytes = enriched.offset; } } } if (!err && !para) { err = AWrite(refN,&bytes,where); EnsureNewline(refN); UL(text); } else { err = UnwrapSave(where,bytes,0,refN); UL(text); } if (!err) BeenThereDoneThat((*messH)->tocH,(*messH)->sumNum); AccuZap(enriched); return(err); } /************************************************************************ * UnwrapSave - save and unwrap a message ************************************************************************/ #define flushBChars() do { \ if (bytes=bSpot-buffer) \ { \ err = refN ? AWrite(refN,&bytes,buffer) : \ (WrapHandle ? PtrPlusHand_(buffer,WrapHandle,bytes) : noErr);\ if (err) goto done; \ bSpot = buffer; \ } \ } while (0) #define putBChar(theC) do { \ *bSpot++ = theC; \ if (bSpot==bEnd) flushBChars(); \ } while (0) #define UnwrapSpaceRuns ((unwrapPref&1)==0) #define UnwrapCenteredBreak ((unwrapPref&2)==0) #define UnwrapIndent ((unwrapPref&4)==0) #define UnwrapQuote ((unwrapPref&8)==0) #define UnwrapShortLine ((unwrapPref&16)==0) #define UnwrapBlankLine ((unwrapPref&32)==0) int UnwrapSave(UPtr text, long length, long offset, short refN) { struct LineInfo { int length; int indent; int quote; int needReturn; }; struct LineInfo lines[2]; struct LineInfo *this, *last; int flip; int c; UPtr tSpot; int begin; int spaces; UPtr buffer = NuPtr(BUFFER_SIZE); UPtr bSpot, bEnd; int err=0; long bytes; Str31 s; Byte quote; Boolean quoteSpace; short i; uLong unwrapPref = GetPrefLong(PREF_UNWRAP_OPTIONS); quote=GetRString(s,QUOTE_PREFIX)[1]; quoteSpace = s[*s]==' '; if (!buffer) return(MemError()); bEnd = buffer+BUFFER_SIZE; bSpot = buffer; WriteZero(lines,sizeof(lines)); this = lines; last = lines+1; flip=this->length=this->indent=this->needReturn=last->length=last->indent=0; last->needReturn=begin=1; for (tSpot=text+offset;tSpotneedReturn = this->needReturn || (UnwrapCenteredBreak && this->indent>20) || (UnwrapShortLine && this->length<40) || (UnwrapBlankLine && this->length==0); if (this->needReturn) { if (this->length==0) { if (!last->needReturn) putBChar('\015'); for (i=0;iquote;i++) putBChar(quote); } putBChar('\015'); } else putBChar(' '); last = this; flip = 1-flip; this = lines + flip; this->length = this->quote = this->indent = this->needReturn = 0; begin = 1; spaces = 0; } else if (c==' ') { if (begin) this->indent++; else spaces++; } else if (begin && c==quote) { this->quote++; } else { if (!begin) { if (spaces>4 && (void*)SendTrans!=(void*)WrapSendTrans && UnwrapSpaceRuns) { putBChar('\t'); this->needReturn = 1; } else if (spaces) { putBChar(' '); this->length++; } } else if (((UnwrapIndent)&&this->indent>last->indent || UnwrapQuote&&this->quote!=last->quote) && !last->needReturn) { putBChar('\015'); for (i=0;iquote;i++) putBChar(quote); this->length += this->quote; if (quoteSpace) { this->length++; putBChar(' '); } } else if (this->quote && last->needReturn) { for (i=0;iquote;i++) putBChar(quote); this->length += this->quote; if (quoteSpace) { this->length++; putBChar(' '); } } begin = 0; spaces = 0; putBChar(c); this->length++; } } flushBChars(); done: ZapPtr(buffer); return(err); } /************************************************************************ * MessKey - handle a keydown in a message window ************************************************************************/ Boolean MessKey(MyWindowPtr win, EventRecord *event) { MessHandle messH = (MessHandle)GetMyWindowPrivateData(win); TOCHandle tocH = (*messH)->tocH; long uLetter = UnadornMessage(event)&charCodeMask; Boolean bodyEdit = !win->ro && win->pte==TheBody; Boolean shift = 0!=(event->modifiers & shiftKey); if (leftArrowChar<=uLetter && uLetter<=downArrowChar && IsArrowSwitch(event->modifiers)) { NextMess(tocH,messH,uLetter,event->modifiers,False); return(True); } else if ((event->modifiers&cmdKey)!=0 && (uLetter==delChar||uLetter==deleteKey)) { DoMenu2(GetMyWindowWindowPtr(win),MESSAGE_MENU,MESSAGE_DELETE_ITEM,event->modifiers); return(true); } else if (!bodyEdit && (uLetter==tabChar || uLetter==enterChar)) { MessFocus(messH,win->pte==(*messH)->subPTE ? TheBody : (*messH)->subPTE); if (win->pte==(*messH)->subPTE) PeteSelectAll(win,win->pte); } else if (event->modifiers&cmdKey) { return(False); } else if (win->ro && uLetter==' ') { if (PeteScroll(TheBody,0,shift ? psePageUp : psePageDn) && !shift) { NextMess(tocH,messH,downArrowChar,0,True); return(True); } } else if (win->ro && DirtyKey(event->message)) AlertStr(READ_ONLY_ALRT, Stop, nil); else if (!bodyEdit && !win->ro && uLetter==returnChar) SysBeep(20L); else { PeteEdit(win,win->pte,peeEvent,(void*)event); } PeteSetDirty(win->pte); return(True); } /************************************************************************ * NextMess - skip to the next message ************************************************************************/ void NextMess(TOCHandle tocH,MessHandle messH,short whichWay,long modifiers,Boolean ezOpen) { WindowPtr messWinWP = GetMyWindowWindowPtr ((*messH)->win); short next; Boolean close,diffTOC; short sumNum; if (IsArrowSwitch(modifiers)) modifiers &= ~GetPrefLong(PREF_SWITCH_MODIFIERS); close = !(modifiers&optionKey); /* should we close the current one? */ if (ezOpen && tocH==(*messH)->openedFromTocH) { EzOpen(tocH,(*messH)->sumNum,0,0,True,False); CloseMyWindow(messWinWP); return; } if ((*messH)->openedFromTocH && tocH!=(*messH)->openedFromTocH) { tocH = (*messH)->openedFromTocH; if (!FindRealSummary(tocH, (*messH)->openedFromSerialNum, &sumNum)) return; diffTOC = true; } else { sumNum = (*messH)->sumNum; diffTOC = false; } switch (whichWay) { case upArrowChar: case leftArrowChar: next = sumNum-1; break; case downArrowChar: case rightArrowChar: next = sumNum+1; break; default: return; } if (next!=sumNum) { if (close && (*messH)->win->isDirty) /* close before switch if dirty */ { if (!CloseMyWindow(messWinWP)) return; close = False; } if (next>=0 && next<(*tocH)->count) { if ((*tocH)->sums[next].messH) { WindowPtr tocMessWinWP = GetMyWindowWindowPtr ((*(MessHandle)(*tocH)->sums[next].messH)->win); ShowMyWindow(tocMessWinWP); UserSelectWindow(tocMessWinWP); UpdateMyWindow(tocMessWinWP); } else { if (close) (*messH)->win->hideme = true; (void) GetAMessage(tocH,next,nil,nil,True); if (close) (*messH)->win->hideme = false; if (diffTOC) BeenThereDoneThat(tocH,next); // set status to READ in virtual mailbox } if ((*tocH)->sums[next].messH) UpdateMyWindow(GetMyWindowWindowPtr((*(*tocH)->sums[next].messH)->win)); SelectBoxRange(tocH,next,next,False,-1,-1); BoxCenterSelection((*tocH)->win); } } if (close) CloseMyWindow(GetMyWindowWindowPtr((*messH)->win)); } /********************************************************************** * **********************************************************************/ void SADL_Hilite(DialogPtr dgPtr) { short junk; ControlHandle cntl; Rect r; GetDialogItem(dgPtr,SADL_EXCLUDE_HEADERS,&junk,(Handle*)&cntl,&r); HiliteControl(cntl,Stationery==1?255:0); GetDialogItem(dgPtr,SADL_PARAGRAPHS,&junk,(Handle*)&cntl,&r); HiliteControl(cntl,Stationery==1?255:0); GetDialogItem(dgPtr,SADL_STATIONERY_FOLDER,&junk,(Handle*)&cntl,&r); HiliteControl(cntl,Stationery==1?0:255); } /********************************************************************** * MessFind - find in the window **********************************************************************/ Boolean MessFind(MyWindowPtr win,PStr what) { return FindInPTE(win,(*Win2MessH(win))->bodyPTE,what); } /************************************************************************ * MessProxy - handle a click in the proxy icon of a message window ************************************************************************/ void MessProxy(MyWindowPtr win,EventRecord *event) { Point pt = event->where; MessHandle messH = Win2MessH(win); PushGWorld(); SetPort_(GetMyWindowCGrafPtr(win)); GlobalToLocal(&pt); DragXfer(pt,nil,messH); PopGWorld(); } /************************************************************************ * MessClick - handle a click in the message window ************************************************************************/ void MessClick(MyWindowPtr win,EventRecord *event) { Point pt = event->where; PETEHandle pte=nil; MessHandle messH = Win2MessH(win); SetPort_(GetMyWindowCGrafPtr(win)); GlobalToLocal(&pt); if (!win->isActive) { SelectWindow_(GetMyWindowWindowPtr(win)); return; } if (PtInPETE(pt,TheBody)) { pte = TheBody; } else if (PtInPETE(pt,(*messH)->subPTE)) { pte = (*messH)->subPTE; } #ifdef IMAP // Don't allow subjects of undownloaded messages to be changed. if (pte && (!(*((*messH)->tocH))->imapTOC || IMAPMessageDownloaded((*messH)->tocH, (*messH)->sumNum))) #else if (pte) #endif { if (PeteEdit(win,pte,peeEvent,(void*)event)==tsmDocNotActiveErr) { MessFocus(messH,pte); PeteEdit(win,pte,peeEvent,(void*)event); } } else if (win->labelCntl && PtInControl(pt,win->labelCntl)) { BoxLabelMenu((*messH)->tocH,(*messH)->sumNum,messH,pt); } else { EnableMenuItems(False); SetMenuTexts(0,False); HandleControl(pt,win); } } /********************************************************************** * MessButton - click on a button in the message **********************************************************************/ void MessButton(MyWindowPtr win,ControlHandle button,long modifiers,short part) { WindowPtr winWP = GetMyWindowWindowPtr (win); MessHandle messH = Win2MessH(win); short value = GetControlValue(button); PETEDocInfo info; #ifndef USEFIXEDDEFAULTFONT Str63 s; #endif short oldWidth, width, fixedWidth, fixedID, fixedSize, newWidth; Rect rWin; switch (GetControlReference(button)) { case NOTIFY_CNTL: if (!(modifiers&optionKey)) GenerateReceipt(messH,MDN_DISPLAYED,MDN_DISPLAYED_LOCAL, MessOptIsSet(messH,OPT_AUTO_OPENED) ? MDN_AUTO_ACTION:MDN_MAN_ACTION, MDN_MAN_SENT); ClearMessOpt(messH,OPT_RECEIPT); DisposeControl(button); if (button=FindControlByRefCon(win,MDN_REQUEST)) DisposeControl(button); if (button=FindControlByRefCon(win,MDN_REQUEST+1)) DisposeControl(button); SetTopMargin(win,GetRLong(COMP_TOP_MARGIN)); MyWindowDidResize(win,nil); break; case PRIOR_HIER_MENU: DoMenu(winWP,PRIOR_HIER_MENU<<16|GetBevelMenuValue(button),modifiers); break; case mcMesgErrors: break; case mcWrite: MessMakeEditable(win,1-value); break; case mcBlahBlah: PETEGetDocInfo(PETE,TheBody,&info); if (info.dirty) { if (!PrefIsSet(PREF_EZ_SAVE)) { if (!SaveMessHi(win,False)) return; } else if (!SaveMess(win)) return; } SetControlValue(button,1-value); if (value) ClearMessFlag(messH,FLAG_SHOW_ALL); else SetMessFlag(messH,FLAG_SHOW_ALL); ReopenMessage(win); break; case mcFixed: #ifdef USEFIXEDDEFAULTFONT fixedSize = FixedSize; fixedID = FixedID; #else // what size to use? fixedSize = GetRLong(DEF_FIXED_SIZE); if (!fixedSize) fixedSize = FontSize; // what font to use? fixedID = GetFontID(GetRString(s,FIXED_FONT)); if (fixedID==applFont) fixedID = (ScriptVar(smScriptMonoFondSize)>>16)&0xffff; #endif // calculate window widths fixedWidth = GetRLong(DEF_FIXED_MWIDTH)*CharWidthInFont('0',fixedID,fixedSize) + PETE_SCROLLY_DIFFERENCE; width = MessWi(win); newWidth = 0; GetWindowPortBounds(winWP,&rWin); oldWidth = RectWi(rWin); // change font if (GetControlValue(button)) { PeteFontAndSize(TheBody,kPETEDefaultFont,kPETEDefaultSize); PeteFontAndSize((*messH)->subPTE,kPETEDefaultFont,kPETEDefaultSize); if (ABS(oldWidth-fixedWidth)<10) newWidth = width; ClearMessFlag ( messH, FLAG_FIXED_WIDTH ); } else { PeteFontAndSize(TheBody,fixedID,fixedSize); // PeteFontAndSize((*messH)->subPTE,fixedID,fixedSize); if (ABS(oldWidth-width)<10) newWidth = fixedWidth; SetMessFlag ( messH, FLAG_FIXED_WIDTH ); } SetControlValue(button,1-value); // do we need to resize the window? if (newWidth) { SizeWindow(winWP,newWidth,RectHi(rWin),false); MyWindowDidResize(win,nil); } break; case mcFetch: SetControlValue(button,1-value); if (value) { RemIdFromPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),FETCH_ID,SumOf(messH)->uidHash); if (!PrefIsSet(PREF_LMOS)) { RemIdFromPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),DELETE_ID,SumOf(messH)->uidHash); SetControlValue(FindControlByRefCon(win,mcTrash),0); HiliteControl(FindControlByRefCon(win,mcTrash),0); } } else { AddIdToPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),FETCH_ID,SumOf(messH)->uidHash,False); if (!PrefIsSet(PREF_LMOS)) { AddIdToPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),DELETE_ID,SumOf(messH)->uidHash,False); SetControlValue(FindControlByRefCon(win,mcTrash),1); HiliteControl(FindControlByRefCon(win,mcTrash),255); } } InvalTocBox((*messH)->tocH,(*messH)->sumNum,blServer); break; case mcGetGraphics: HiliteControl(FindControlByRefCon(win,mcGetGraphics),255); // Disabling button indicates that graphics should now be downloaded PeteRecalc(win->pte); break; case mcTrash: SetControlValue(button,1-value); if (value) RemIdFromPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),DELETE_ID,SumOf(messH)->uidHash); else AddIdToPOPD(PERS_POPD_TYPE(MESS_TO_PPERS(messH)),DELETE_ID,SumOf(messH)->uidHash,True); InvalTocBox((*messH)->tocH,(*messH)->sumNum,blServer); break; } AuditHit((modifiers&shiftKey)!=0, (modifiers&controlKey)!=0, (modifiers&optionKey)!=0, (modifiers&cmdKey)!=0, false, GetWindowKind(winWP), GetControlReference(button), mouseDown); } /************************************************************************ * MessMakeEditable - flip the pencil for a message ************************************************************************/ OSErr MessMakeEditable(MyWindowPtr win, Boolean value) { PETEDocInfo info; MessHandle messH = Win2MessH(win); PETETextStyle style; if (!value) { PETEGetDocInfo(PETE,TheBody,&info); if (info.dirty) { if (!PrefIsSet(PREF_EZ_SAVE)) { if (!SaveMessHi(win,False)) return userCanceledErr; } else if (!SaveMess(win)) return userCanceledErr; } PETESetCallback(PETE,TheBody,nil,peDocChanged); ClearMessOpt(messH,OPT_WRITE); win->ro = win->pte==TheBody; win->userSave = false; PETEHonorLock(PETE,TheBody,peModLock|peSelectLock); style.tsLock = peModLock; PETESetTextStyle(PETE,TheBody,0L,0x7fffffff,&style,peLockValid); win->isDirty = False; PeteCleanList(win->pteList); } else { SetMessOpt(messH,OPT_WRITE); win->userSave = true; win->ro = False; PETESetCallback(PETE,TheBody,(void*)PeteChanged,peDocChanged); PETEHonorLock(PETE,TheBody,peNoLock); } SetControlValue(FindControlByRefCon(win,mcWrite),value); return noErr; } /************************************************************************ * MessGonnaShow - get ready to show a message window ************************************************************************/ OSErr MessGonnaShow(MyWindowPtr win) { WindowPtr winWP = GetMyWindowWindowPtr (win); MessHandle messH = (MessHandle)GetMyWindowPrivateData(win); short margin; Rect r; ControlHandle blah; Boolean on = MessOnPOPD(POPD_ID,messH); MControlsEnum cIndex; short icon; short value; long scanned; Boolean junk = SumOf(messH)->spamScore >= GetRLong(JUNK_MAILBOX_THRESHHOLD); win->didResize = MessDidResize; win->key = MessKey; win->update = MessUpdate; win->help = MessHelp; win->button = MessButton; win->dontControl = True; win->zoomSize = MessZoomSize; win->label = SumColor(SumOf(messH)); margin = GetRLong(COMP_TOP_MARGIN); if (!MessFlagIsSet(messH,FLAG_OUT) && MessOptIsSet(messH,OPT_RECEIPT) && !GetPrefLong(PREF_RECEIPT)) margin += GROW_SIZE+2*INSET; SetTopMargin(win,margin); if (MakeSubjectTXE(messH)) { win->click = win->bgClick = MessClick; #ifdef USECMM_BAD win->buildCMenu = MessBuildCMenu; #endif } else { PeteCleanList(win->pte); win->isDirty = False; CloseMyWindow(winWP); return(-108); } PeteFocus(win,TheBody,True); (*PeteExtra(TheBody))->containsJunkMail = junk; SetBGColorsByPers(messH); /* * Align headers */ //if (GetPrefLong(PREF_DONT_ALIGN_HEADERS)!=2) AlignHeaders(messH); /* * some attachments */ for (scanned=(*PeteExtra(TheBody))->urlScanned; scanned!=-1 && scanned<3 K; scanned=(*PeteExtra(TheBody))->urlScanned) PeteURLScan(win,TheBody); for (scanned=(*PeteExtra(TheBody))->quoteScanned; scanned!=-1 && scanned<3 K; scanned=(*PeteExtra(TheBody))->quoteScanned) PeteQuoteScan(TheBody); (*PeteExtra(TheBody))->emoDesired = !MessFlagIsSet(messH,FLAG_SHOW_ALL); if ((*PeteExtra(TheBody))->emoDesired && EmoDo()) { for (scanned=(*PeteExtra(TheBody))->emoScanned; scanned!=-1 && scanned<3 K; scanned=(*PeteExtra(TheBody))->emoScanned) EmoScanPete(TheBody,false); } /* * calculate */ PeteCalcOn(TheBody); /* * controls */ Zero(r); r.bottom = r.right = 2; for (cIndex=mcWrite;cIndex<=mcTrash;cIndex++) { if (FontIsFixed && cIndex==mcFixed) continue; // only if default font is proportional blah = GetNewControlSmall(ICON_BAR_CNTL,winWP); if (blah) { EmbedControl(blah,win->topMarginCntl); SetControlReference(blah,cIndex); switch (cIndex) { case mcWrite: icon = PENCIL_SICN; break; case mcBlahBlah: icon = BLAH_SICN; break; case mcFixed: icon = FIXED_WIDTH_ICON; if (!FontIsFixed && !(MessOptIsSet(messH,OPT_HTML)||MessFlagIsSet(messH,FLAG_RICH)) && PrefIsSet(PREF_FIXIFY)) MessButton(win,blah,0,kControlButtonPart); break; case mcFetch: icon = FETCH_SICN; break; case mcGetGraphics: icon = MISSING_IMAGE_ICON; break; case mcTrash: icon = TRASH_SICN; value = MessOnPOPD(DELETE_ID,messH); break; } SetBevelIcon(blah,icon,0,0,nil); } else { NoSaves = True; CloseMyWindow(winWP); NoSaves = False; return(-108); } } AddPriorityPopup(messH); PushGWorld(); SetWindowProxyCreatorAndType(winWP,CREATOR,MAILBOX_TYPE,kOnSystemDisk); win->proxy = MessProxy; PopGWorld(); MessIBarUpdate(messH); /* * notification stuff */ CheckAddNotifyControls(win, messH); // error notification AddMessErrNote(messH); BeenThereDoneThat((*messH)->tocH,(*messH)->sumNum); HiliteOddReply(messH); if (AnalSpeakPhrases()) { (*PeteExtra(TheBody))->taeDesired = true; (*PeteExtra(TheBody))->taeSpeak = true; } if (!win->ro) PETESetCallback(PETE,TheBody,(void*)PeteChanged,peDocChanged); return(noErr); } #define CARBON_NOTIFY_BUTTON_V_ADJUSTMENT 4 /************************************************************************ * CheckAddReturnReceipt - see if we need to add return receipt to this message ************************************************************************/ Boolean CheckAddNotifyControls(MyWindowPtr win, MessHandle messH) { Boolean result = false; if (!MessFlagIsSet(messH,FLAG_OUT) && MessOptIsSet(messH,OPT_RECEIPT) && !FindControlByRefCon(win,NOTIFY_CNTL)) { if (!GetPrefLong(PREF_RECEIPT)) SetTopMargin(win,GetRLong(COMP_TOP_MARGIN) + GROW_SIZE+2*INSET + 2*CARBON_NOTIFY_BUTTON_V_ADJUSTMENT); switch(GetPrefLong(PREF_RECEIPT)) { case 2: (*messH)->sound = NOTIFY_SOUND; GenerateReceipt(messH,MDN_DISPLAYED,MDN_DISPLAYED_LOCAL, MessOptIsSet(messH,OPT_AUTO_OPENED) ? MDN_AUTO_ACTION:MDN_MAN_ACTION, MDN_AUTO_SENT); ClearMessOpt(messH,OPT_RECEIPT); break; case 1: ClearMessOpt(messH,OPT_RECEIPT); break; case 0: AddNotifyControls(messH); result = true; break; } } return result; } /************************************************************************ * function - purpose ************************************************************************/ void AddNotifyControls(MessHandle messH) { ControlHandle blah; MyWindowPtr messWin = (*messH)->win; WindowPtr messWinWP = GetMyWindowWindowPtr (messWin); Rect r; Str255 s; SetRect(&r,2,2,4,4); if (blah = GetNewControlSmall(NOTIFY_CNTL,messWinWP)) { (*messH)->sound = NOTIFY_SOUND; SetControlReference(blah,NOTIFY_CNTL); if (gGoodAppearance) EmbedControl(blah,messWin->topMarginCntl); // add message if (blah = NewControlSmall(messWinWP,&r,"",False,0,0,1,kControlStaticTextProc,MDN_REQUEST)) { SetTextControlText(blah,GetRString(s,MDN_REQUEST),nil); if (gGoodAppearance) EmbedControl(blah,messWin->topMarginCntl); } // add separator if (blah = NewControlSmall(messWinWP,&r,"\p",True,0,0,0,kControlSeparatorLineProc,MDN_REQUEST+1)) if (gGoodAppearance) EmbedControl(blah,messWin->topMarginCntl); PlaceNotifyControls(messWin); } } /************************************************************************ * function - purpose ************************************************************************/ void PlaceNotifyControls(MyWindowPtr win) { WindowPtr winWP = GetMyWindowWindowPtr (win); Rect r, notifyR, rWin; ControlHandle blah; if (blah = FindControlByRefCon(win,NOTIFY_CNTL)) { ButtonFit(blah); GetWindowPortBounds(winWP,&rWin); MoveMyCntl(blah,rWin.right-INSET-ControlWi(blah),win->topMargin-ControlHi(blah)-MAX_APPEAR_RIM-2-CARBON_NOTIFY_BUTTON_V_ADJUSTMENT,0,0); ShowMyControl(blah); GetControlBounds(blah,¬ifyR); // add message r = notifyR; r.right = r.left - INSET; r.left = INSET; InsetRect(&r,0,MAX_APPEAR_RIM-1); if (blah = FindControlByRefCon(win,MDN_REQUEST)) { MySetCntlRect(blah,&r); ShowMyControl(blah); } // add separator r = notifyR; SetRect(&r,INSET,r.top,rWin.right-INSET,r.top+3); OffsetRect(&r,0,-5); if (blah = FindControlByRefCon(win,MDN_REQUEST+1)) { MySetCntlRect(blah,&r); ShowMyControl(blah); } } } /************************************************************************ * AddMessErrNote - add the error note to a message, if need be ************************************************************************/ void AddMessErrNote(MessHandle messH) { Rect r,c; ControlHandle cntl; mesgErrorHandle mesgErrH; MyWindowPtr messWin = (*messH)->win; WindowPtr messWinWP = GetMyWindowWindowPtr (messWin); /* if this message has a mesgErrH or the toc status is x, add note */ if ((mesgErrH=(*(*messH)->tocH)->sums[(*messH)->sumNum].mesgErrH) || ((*(*messH)->tocH)->sums[(*messH)->sumNum].state==MESG_ERR)) { // make divider SetRect(&r,-REAL_BIG,messWin->topMargin-1,REAL_BIG,messWin->topMargin+1); cntl = NewControlSmall(messWinWP,&r,"",True,0,0,0,kControlSeparatorLineProc,nil); EmbedControl(cntl,messWin->topMarginCntl); // add to topmargin SetTopMargin(messWin,messWin->topMargin+GetMesgErrorsHeight(messWin)); GetMesgErrorsRect(messWin,&r); // add separator SetRect(&c,INSET,r.top,messWin->contR.right-INSET,r.top+4); // (jp) 2-6-01 Bug 3637 bottom used to be r.top+3 OffsetRect(&c,0,-5); if (cntl = NewControlSmall(messWinWP,&c,"\p",True,0,0,0,kControlSeparatorLineProc,mcMesgErrSep)) EmbedControl(cntl,messWin->topMarginCntl); // add icon r.top += 3; SetRect(&c,0,0,16,16); CenterRectIn(&c,&r); cntl = NewControlSmall(messWinWP,&c,"",True,MESS_ERR_ICON,0,0,kControlIconSuiteNoTrackProc,mcMesgErrors); if (cntl && GetControlDataHandle(cntl)) { (*messH)->sound = NOTIFY_SOUND; if (gGoodAppearance) EmbedControl(cntl,messWin->topMarginCntl); } // add message r.left = r.right+INSET; r.right = messWin->contR.right-INSET; if (cntl = NewControlSmall(messWinWP,&r,"",False,0,0,1,kControlStaticTextProc,mcMesgErrText)) { /* try mesgErrH first. if not, use generic */ if (mesgErrH) { SetTextControlText(cntl,LDRef(mesgErrH)->errorStr,nil); UL(mesgErrH); } else { Str255 item; SetTextControlText(cntl,GetRString(item,UNKNOWN_ERR_TEXT),nil); } LetsGetSmall(cntl); ShowMyControl(cntl); if (gGoodAppearance) EmbedControl(cntl,messWin->topMarginCntl); } } } /************************************************************************ * PlaceMessErrNote - move the error note to the right spot ************************************************************************/ void PlaceMessErrNote(MessHandle messH) { ControlHandle cntl; MyWindowPtr win = (*messH)->win; Rect r; long junk; short h, dH; if (cntl=FindControlByRefCon(win,mcMesgErrors)) { GetMesgErrorsRect(win,&r); r.left = r.right+INSET; r.right = win->contR.right-INSET; r.top -= 1; if (cntl=FindControlByRefCon(win,mcMesgErrText)) { HideControl(cntl); MySetCntlRect(cntl,&r); SetBevelStyle(cntl,0); GetControlData(cntl,0,kControlStaticTextTextHeightTag,sizeof(short),(void*)&h,&junk); if (h>RectHi(r)) { SetBevelStyle(cntl,condense); GetControlData(cntl,0,kControlStaticTextTextHeightTag,sizeof(short),(void*)&h,&junk); } dH = (h - RectHi(r))/2; if (dH<0) { OffsetRect(&r,0,-dH); r.bottom = r.top + h; MySetCntlRect(cntl,&r); } ShowControl(cntl); } } } /********************************************************************** * MessIBarUpdate - update the icon bar **********************************************************************/ void MessIBarUpdate(MessHandle messH) { MControlsEnum cIndex; ControlHandle blah; long value; Boolean on = MessOnPOPD(POPD_ID,messH); Boolean lmos = PrefIsSet(PREF_LMOS); Boolean fetch = on && MessOnPOPD(FETCH_ID,messH); Boolean del = on && (fetch && !lmos || MessOnPOPD(DELETE_ID,messH)); for (cIndex=mcWrite;cIndex<=mcTrash;cIndex++) if (blah=FindControlByRefCon((*messH)->win,cIndex)) { value = GetControlValue(blah); switch (cIndex) { case mcWrite: value = MessOptIsSet(messH,OPT_WRITE); break; case mcBlahBlah: value = MessFlagIsSet(messH,FLAG_SHOW_ALL); break; case mcFetch: value = fetch; if (!on || !MessFlagIsSet(messH,FLAG_SKIPPED)) HideControl(blah); (*messH)->hasFetchIcon = IsControlVisible(blah); break; case mcGetGraphics: if (!DisplayGetGraphics((*messH)->win)) HideControl(blah); else ShowControl(blah); break; case mcFixed: value = MessFlagIsSet(messH,FLAG_FIXED_WIDTH); break; case mcTrash: value = del; if (!on) HideControl(blah); else { if (!lmos && fetch) DeactivateControl(blah); else ActivateControl(blah); } (*messH)->hasDelIcon = IsControlVisible(blah); break; } SetControlValue(blah,value); } } #ifdef NEVER /********************************************************************** * AlignHeaders - align the header names in a message **********************************************************************/ void AlignHeaders(MessHandle messH) { short len = 0; short thisLen; Handle text; long start, colon, stop, end; short nPara = 0; PETEParaInfo pinfo; PETEGetRawText(PETE,TheBody,&text); end = SumOf(messH)->bodyOffset - (*messH)->weeded; /* * insert all the paragraphs, and store the width of the header name * in the paragraph indent, and keep track of the max width */ for (start=0;startheaders = nPara; while (nPara--) { PETEGetParaInfo(PETE,TheBody,nPara,&pinfo); pinfo.startMargin = (len-pinfo.indent)<<16; pinfo.indent = (-pinfo.indent-FontWidth)<<16; PETESetParaInfo(PETE,TheBody,nPara,&pinfo,peIndentValid|peStartMarginValid); } } #endif /********************************************************************** * ExportHTMLSum - export html from a message summary **********************************************************************/ OSErr ExportHTMLSum(TOCHandle tocH,short sumNum) { MessHandle messH = (*tocH)->sums[sumNum].messH; MyWindowPtr win = nil; OSErr err; // if this is an IMAP message, make sure it's been downloaded. if ((*tocH)->imapTOC) EnsureMsgDownloaded(tocH, sumNum, false); if (!messH) { win = GetAMessage(tocH,sumNum,nil,nil,false); if (!win) return(MemError()); messH = Win2MessH(win); } err = ExportHTML(messH); if (win) CloseMyWindow(GetMyWindowWindowPtr(win)); return(err); } #ifdef USE_FANCY_HTML_EXPORT /********************************************************************** * **********************************************************************/ OSErr ExportHTML(MessHandle messH) { OSErr err; Str31 html; Str31 testMe; Boolean neg; short inHTML = 0; long offset, startOffset, endOffset; FSSpec spec; short refN; UHandle cache; long len = 0; long grandLen; Byte less = '<'; Zero(spec); if (!(err=CacheMessage((*messH)->tocH,(*messH)->sumNum))) { cache = SumOf(messH)->cache; HNoPurge(cache); ComposeString(html,"\p%r>",HTMLDirectiveStrn+htmlXHTMLDir); if (!(err=NewTempExtSpec(Root.vRef,nil,HTML_SUFFIX,&spec)) && !(err=FSpCreate(&spec,0L,'TEXT',smSystemScript)) && !(err=FSpOpenDF(&spec,fsRdWrPerm,&refN))) { LDRef(cache); grandLen = GetHandleSize(cache); startOffset = grandLen; for(offset = SearchPtrHandle(&less,1,cache,0,True,False,nil); offset>0; offset = SearchPtrHandle(&less,1,cache,offset+1,True,False,nil)) { /* * what is the word we're looking at? */ if ((*cache)[offset+1]=='/') { neg = True; MakePStr(testMe,*cache+offset+2,*html); } else { neg = False; MakePStr(testMe,*cache+offset+1,*html); } /* * no-brainers first */ if (!StringSame(html,testMe)) continue; //anything that's not html is insignificant if (neg && !inHTML) continue; //hmmm. in free text. No likee. if (!neg && inHTML) {inHTML++; continue;} // more deeply nested html /* * ok, first let's look at */ if (!neg) { if (!inHTML) startOffset = offset; //html begins inHTML++; } /* * ok, now the fun one: */ else { inHTML--; if (!inHTML) { endOffset = offset+*html+3; endOffset = MIN(endOffset,grandLen); if (startOffsettocH,(*messH)->sumNum))) { cache = SumOf(messH)->cache; HNoPurge(cache); ComposeString(html,"\p%r>",HTMLTagsStrn+htmlTag); if (!(err=NewTempExtSpec(Root.vRef,nil,HTML_SUFFIX,&spec)) && !(err=FSpCreate(&spec,CREATOR,'TEXT',smSystemScript)) && !(err=FSpOpenDF(&spec,fsRdWrPerm,&refN))) { LDRef(cache); grandLen = GetHandleSize(cache); startOffset = grandLen; for(offset = SearchPtrHandle(&less,1,cache,0,True,False,nil); offset>0; offset = SearchPtrHandle(&less,1,cache,offset+1,True,False,nil)) { /* * what is the word we're looking at? */ if ((*cache)[offset+1]=='/') { neg = True; MakePStr(testMe,*cache+offset+2,*html); } else { neg = False; MakePStr(testMe,*cache+offset+1,*html); } /* * no-brainers first */ if (!StringSame(html,testMe)) continue; //anything that's not html is insignificant if (neg && !inHTML) continue; //hmmm. in free text. No likee. if (!neg && inHTML) {inHTML++; continue;} // more deeply nested html /* * ok, first let's look at */ if (!neg) { if (!inHTML) startOffset = offset; //html begins inHTML++; } /* * ok, now the fun one: */ else { inHTML--; if (!inHTML) { endOffset = offset+*html+3; endOffset = MIN(endOffset,grandLen); if (startOffsetsound) { Str255 soundName; PlayNamedSound(GetRString(soundName,(*messH)->sound)); (*messH)->sound = 0; } } } #ifdef TWO /********************************************************************** * MessBuildDragRgn - build the drag region for a message **********************************************************************/ RgnHandle MessBuildDragRgn(MessHandle messH) { WindowPtr messWinWP = GetMyWindowWindowPtr ((*messH)->win); RgnHandle rgn = NewRgn(); if (rgn) { GetWindowRegion(messWinWP,kWindowTitleProxyIconRgn,rgn); LocalizeRgn(rgn); } return(rgn); } /********************************************************************** * GetServerRect - get rectangle for a server icon **********************************************************************/ Boolean GetServerRect(MyWindowPtr win, short which, Rect *r) { CGrafPtr winPort = GetMyWindowCGrafPtr (win); short wi; Rect rPort; GetBlahRect(win,0,r); wi = r->right - r->left; GetPortBounds(winPort,&rPort); r->right = rPort.right-(2-which)*(INSET+wi)-INSET; r->left = r->right-wi; return(True); } #endif /********************************************************************** * GetMesgErrorsHeight - get rectangle for a transport errors icon **********************************************************************/ short GetMesgErrorsHeight(MyWindowPtr win) { short height, fs; FontInfo info; fs = GetPortTextSize(GetQDGlobalsThePort()); TextSize (10); GetFontInfo(&info); TextSize (fs); height = (info.ascent + info.descent + info.leading) * 2; return ((height < MESG_ERR_WIDTH) ? MESG_ERR_WIDTH : height); } /********************************************************************** * GetMesgErrorsRect - get rectangle for a transport errors icon **********************************************************************/ Boolean GetMesgErrorsRect(MyWindowPtr win, Rect *r) { CGrafPtr winPort = GetMyWindowCGrafPtr (win); Rect rPort; GetPortBounds(winPort,&rPort); r->left = rPort.left + INSET; r->right = r->left + MESG_ERR_WIDTH; r->bottom = win->topMargin - MAX_APPEAR_RIM; r->top = r->bottom - MESG_ERR_WIDTH + 6; return(True); } /************************************************************************ * MessFocus - switch txe's in a message window ************************************************************************/ void MessFocus(MessHandle messH,PETEHandle pte) { MyWindowPtr win = (*messH)->win; Boolean wasSub = win->pte==(*messH)->subPTE; PeteFocus(win,pte,True); win->ro = win->pte==TheBody && !MessOptIsSet(messH,OPT_WRITE); if (wasSub && win->pte!=(*messH)->subPTE) MessSaveSub(messH); else win->showInsert = NOOP; } /********************************************************************** * MessSaveSub - save the subject of a message **********************************************************************/ OSErr MessSaveSub(MessHandle messH) { Str255 newSubj; if(MessFlagIsSet(messH, FLAG_UTF8) && HasUnicode()) { OSErr err; long iUsed, oUsed; err = PeteGetUTF8Text((*messH)->subPTE, 0, LONG_MAX, &iUsed, &(newSubj[1]), sizeof(newSubj) - 1, &oUsed); if(err && err != kTECOutputBufferFullStatus) return err; newSubj[0] = oUsed; } else { PeteSString(newSubj,(*messH)->subPTE); } SetSubject((*messH)->tocH,(*messH)->sumNum,newSubj); PETEMarkDocDirty(PETE,(*messH)->subPTE,False); if (PeteIsDirtyList((*messH)->win->pteList)) (*messH)->win->isDirty = True; else (*messH)->win->isDirty = False; return noErr; } /************************************************************************ * MessDidResize - resize a message window ************************************************************************/ void MessDidResize(MyWindowPtr win, Rect *oldContR) { MessHandle messH = (MessHandle)GetMyWindowPrivateData(win); Rect view; Rect paneR; PETEHandle bodTeh = TheBody; PETEHandle subTeh = (*messH)->subPTE; ControlHandle blah, errorCntl = nil; if (!subTeh) TextDidResize(win,oldContR); else { view = win->contR; PeteDidResize(bodTeh,&view); } PlaceMessErrNote(messH); if (!oldContR || oldContR->right-oldContR->left!=win->contR.right-win->contR.left) { SetRect(&view,0,0,REAL_BIG/2,win->topMargin-3); InvalWindowRect(GetMyWindowWindowPtr(win),&view); /* * the icon bar */ if (GetBlahRect(win,0,&view)) { if (blah = FindControlByRefCon(win,mcWrite)) MySetCntlRect(blah,&view); GetBlahRect(win,1,&view); if (blah=FindControlByRefCon(win,mcBlahBlah)) MySetCntlRect(blah,&view); GetBlahRect(win,2,&view); if (blah=FindControlByRefCon(win,mcFixed)) MySetCntlRect(blah,&view); GetServerRect(win,1,&view); if (blah=FindControlByRefCon(win,mcFetch)) MySetCntlRect(blah,&view); GetServerRect(win,1,&view); if (blah=FindControlByRefCon(win,mcGetGraphics)) MySetCntlRect(blah,&view); GetServerRect(win,2,&view); if (blah=FindControlByRefCon(win,mcTrash)) MySetCntlRect(blah,&view); if (blah=FindControlByRefCon(win,NOTIFY_CNTL)) { PlaceNotifyControls(win); } MessSubjRect(win,&view); paneR = view; if (blah=FindControlByRefCon(win,(uLong)subTeh)) { InsetRect(&paneR,-MAX_APPEAR_RIM,-MAX_APPEAR_RIM); SetControlVisibility(blah,false,false); MySetCntlRect(blah,&paneR); SetControlVisibility(blah,true,false); } PETESizeDoc(PETE,subTeh,RectWi(view),RectHi(view)); PETEChangeDocWidth(PETE,win->pte,-1,True); } } } /************************************************************************ * MessSubjRect - find the rect for the subject teh ************************************************************************/ void MessSubjRect(MyWindowPtr win,Rect *r) { MessHandle messH = Win2MessH(win); short hi = ONE_LINE_HI(win); Rect pr, br; GetPriorityRect(win,&pr); GetBlahRect(win,FontIsFixed?1:2,&br); pr.right = br.right; *r = win->contR; r->left += pr.right + INSET+win->hPitch; r->top = pr.top + (RectHi(pr)-hi-1)/2; r->bottom = r->top + hi; #ifdef TWO GetServerRect(win,1,&br); r->right = br.left-INSET; #endif } /************************************************************************ * GetBlahRect - get the rect for the blah blah icon ************************************************************************/ Boolean GetBlahRect(MyWindowPtr win, short which, Rect *r) { if (!GetPriorityRect(win,r)) return(False); OffsetRect(r,(which+1)*(RectWi(*r)+INSET),0); return(True); } /************************************************************************ * MessCursor - set the cursor properly ************************************************************************/ void MessCursor(Point mouse) { MyWindowPtr win=GetWindowMyWindowPtr(FrontWindow_()); MessHandle messH = Win2MessH(win); if (!PeteCursorList(win->pteList,mouse)) SetMyCursor(arrowCursor); } /************************************************************************ * GetPriorityRect - where does the priority thing belong? ************************************************************************/ Boolean GetPriorityRect(MyWindowPtr win,Rect *pr) { if (!win->topMargin) { SetRect(pr,0,0,0,0); return(False); } else { pr->left = INSET; pr->top = 2*MAX_APPEAR_RIM+2; pr->bottom = pr->top+22; pr->right = pr->left+22; return(True); } } /************************************************************************ * DrawPriority - draw the message priority ************************************************************************/ void DrawPriority(Rect *pr,short p) { WindowPtr theWindow = GetWindowFromPort(GetQDGlobalsThePort()); Rect ir; if (GetWindowKind(theWindow)!=CBOX_WIN &&GetWindowKind(theWindow)!=MBOX_WIN) { ir = *pr; InsetRect(&ir,-2,-2); EraseRect(&ir); FrameRect(&ir); MoveTo(ir.left+1,ir.bottom); Line(ir.right-ir.left-1,0); Line(0,ir.top-ir.bottom+1); } p = Prior2Display(p); SetRect(&ir,0,0,16,16); CenterRectIn(&ir,pr); if (p==pymRaise) PlotIconID(&ir,atAbsoluteCenter, ttNone,RAISE_PRIOR_SICN); else if (p==pymLower) PlotIconID(&ir,atAbsoluteCenter, ttNone,LOWER_PRIOR_SICN); else PlotIconID(&ir,atAbsoluteCenter, ttNone,PRIOR_SICN_BASE+p-1); } /************************************************************************ * SetMessTable - set the xlit table for a message ************************************************************************/ void SetMessTable(TOCHandle tocH, short sumNum, short newId) { if ((*tocH)->sums[sumNum].tableId!=newId) { (*tocH)->sums[sumNum].tableId = newId; TOCSetDirty(tocH,true); if ((*tocH)->previewID==(*tocH)->sums[sumNum].serialNum) (*tocH)->previewID = 0; if ((*tocH)->which!=OUT && (*tocH)->sums[sumNum].messH) ReopenMessage((*(*tocH)->sums[sumNum].messH)->win); } } /************************************************************************ * MessZoomSize - figure the size of a message window ************************************************************************/ void MessZoomSize(MyWindowPtr win,Rect *zoom) { long wi, hi; PETEHandle pte = (*Win2MessH(win))->bodyPTE; PETEDocInfo info; wi = MessWi(win); zoom->right = zoom->left + MIN(zoom->right-zoom->left,wi); if (PeteIsValid(pte)) { //PETEGetDocInfo(PETE,pte,&info); wi = zoom->right-zoom->left-PETE_SCROLLY_DIFFERENCE+9; PETEChangeDocWidth(PETE,pte,wi,True); PETEGetDocInfo(PETE,pte,&info); hi = info.docHeight + win->topMargin + win->vPitch; if (hibottom-zoom->top) zoom->bottom = zoom->top + hi; } } /********************************************************************** * MessWi - how wide is a window? **********************************************************************/ short MessWi(MyWindowPtr win) { short wi; if (!(wi=GetPrefLong(PREF_MWIDTH))) wi=GetRLong(DEF_MWIDTH); wi *= win->hPitch; wi += PETE_SCROLLY_DIFFERENCE; return(wi); } /************************************************************************ * MessApp1 - scroll the message, not the subject ************************************************************************/ Boolean MessApp1(MyWindowPtr win,EventRecord *event) { event->what=keyDown; PeteEdit(win,(*Win2MessH(win))->bodyPTE,peeEvent,(void*)event); return(True); } /************************************************************************ * IncrementQuoteLevel - adjust the quote level in a message ************************************************************************/ OSErr IncrementQuoteLevel(PETEHandle pte,long start, long stop,short increment) { OSErr err; short fromPara, toPara; PETEParaInfo pInfo; PETEStyleEntry pse; short level; // I'm only going to tell you this once... pse.psStyle.textStyle.tsLabel = pQuoteLabel; Zero(pInfo); // avoid messy redraw PeteCalcOff(pte); // Expand offsets to para boundaries start = stop = -1; // use selection PeteParaRange(pte,&start,&stop); PeteParaConvert(pte,start,stop); // Undo to para boundaries PetePrepareUndo(pte,peUndoStyleAndPara,start,stop,nil,nil); // Find the start and stop points fromPara = PeteParaAt(pte,start); toPara = PeteParaAt(pte,stop-1); toPara = MAX(toPara,fromPara); // darn fiddly end conditions // Ok, do some work for (;fromPara<=toPara;fromPara++) { // get current quote level if (err = PETEGetParaInfo(PETE,pte,fromPara,&pInfo)) break; // bump it, but don't allow negative or > 15 level = pInfo.quoteLevel + increment; level = MIN(level,15); level = MAX(level,0); pInfo.quoteLevel = level; // and set it if (err = PETESetParaInfo(PETE,pte,fromPara,&pInfo,peQuoteLevelValid)) break; // and give it the quote label if (level) PETESetTextStyle(PETE,pte,pInfo.paraOffset,pInfo.paraOffset+pInfo.paraLength,&pse.psStyle.textStyle,pQuoteLabel<