eudora-mac/rich.c

1 line
52 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. */
//depot/projects/eudora/mac/Current/rich.c#24 - edit change 4405 (text)
#include "rich.h"
/* Copyright (c) 1995 by QUALCOMM Incorporated */
#define FILE_NUM 76
OSErr UnwindRich(short *counters,StackHandle openStack,StackHandle redoStack,AccuPtr enriched);
OSErr RewindRich(StackHandle openStack,StackHandle redoStack,AccuPtr enriched);
OSErr BuildEnrichedDirectives(PETETextStylePtr oldStyle,PETETextStylePtr newStyle,
PETEParaInfoPtr oldInfo,PETEParaInfoPtr newInfo,
StackHandle openStack,StackHandle redoStack,
AccuPtr enriched);
PStr BuildRichParaParam(PStr directive,PSMPtr marg);
PStr BuildRichFontParam(PStr directive,short fontID);
PStr BuildRichColorParam(PStr directive,RGBColor *color);
void AddToStyle(EnrichedEnum cmd,Boolean neg,long offset,OffsetAndStyleHandle *styles,uLong valid);
OSErr EnrichedToken(UHandle enriched,long maxLen,Boolean headers,short *cmdId,Boolean *neg,long *tStart,long *tStop);
OSErr InsertEnriched(UHandle text, long *textOffset, long textLen, long *offset, Boolean unwrap, PETEHandle pte);
OSErr InsertEnrichedLo(UHandle text, long *textOffset, long textLen, long *inOffset, Boolean unwrap, PETEHandle pte, TextEncoding encoding);
OSErr InsertFlowed(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, Boolean delSP);
OSErr InsertFlowedLo(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, Boolean delSP, TextEncoding encoding);
OSErr InsertFixed(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, TextEncoding encoding);
#define FlushIntlText(pte, offset, converter) PeteInsertIntlText(pte, offset, nil, 0L, 0L, converter, kTextEncodingUnknown, false, true);
Boolean PartIsReferenced(uLong cID,uLong baseID,uLong sourceID,StackHandle partRefStack);
OSErr FakeAttachment(PETEHandle pte,uLong *offset,FSSpecPtr spec);
#pragma segment Enriched
/**********************************************************************
* Text/Enriched building
**********************************************************************/
typedef struct
{
EnrichedEnum directive;
union
{
RGBColor color;
PeteSaneMargin marg;
short fontID;
} param;
} DirectiveType,*DTPtr, **DTHandle;
#define ISSUE_DIRECTIVE \
do { \
ComposeRString(directive,MIME_RICH_ON,EnrichedStrn+pushMe.directive); \
if (err=AccuAddStr(enriched,directive)) goto done; \
if (err=StackPush(&pushMe,openStack)) goto done; \
} while (0)
/**********************************************************************
* BuildEnriched - take a block of text and turn it into text/enriched
**********************************************************************/
OSErr BuildEnriched(AccuPtr enriched,PETEHandle pte,UHandle text,long len,long offset,PETEStyleListHandle pslh,Boolean xrich)
{
long eSize = 0;
long eOffset = 0;
Byte c;
OSErr err = noErr;
Boolean cr = False;
PETEStyleEntry oldStyle, newStyle;
PETEParaInfo oldInfo, newInfo;
long oldPara = -1;
long para;
short paraDiff, styleDiff;
StackHandle openStack=nil;
StackHandle redoStack=nil;
long runLen;
Str255 directive;
DirectiveType pushMe;
Boolean kkk;
UPtr spot;
#ifdef DEBUG
Str255 scratch;
#endif
#ifdef NEVER
EuStyleSheet ess;
#endif
if (pte)
{
oldInfo.tabHandle = nil;
PETEGetParaInfo(PETE,pte,kPETEDefaultPara,&oldInfo);
PeteStyleAt(pte,kPETEDefaultStyle,&oldStyle);
PETEGetRawText(PETE,pte,&text);
}
if (!(err=StackInit(sizeof(DirectiveType),&openStack)))
if (!(err=StackInit(sizeof(DirectiveType),&redoStack)))
{
if (xrich)
{
pushMe.directive = enXRich;
ISSUE_DIRECTIVE;
}
while (offset<len)
{
if (pte)
{
/*
* first check for style changes
*/
PeteGetStyle(pte,offset,&runLen,&newStyle);
runLen = MAX(runLen,1); // we are very scared by very short runs
if (newStyle.psStyle.textStyle.tsFont==FontID) newStyle.psStyle.textStyle.tsFont=kPETEDefaultFont;
#ifdef USEFIXEDDEFAULTFONT
if (newStyle.psStyle.textStyle.tsFont==FixedID) newStyle.psStyle.textStyle.tsFont=kPETEDefaultFixed;
#endif
if (newStyle.psStyle.textStyle.tsSize==0) newStyle.psStyle.textStyle.tsSize=ScriptVar(smScriptSysFondSize);
#ifdef USERELATIVESIZES
if (((newStyle.psStyle.textStyle.tsSize==FontSize) && (newStyle.psStyle.textStyle.tsFont == kPETEDefaultFont)) ||
((newStyle.psStyle.textStyle.tsSize==FixedSize) && (newStyle.psStyle.textStyle.tsFont == kPETEDefaultFixed)))
newStyle.psStyle.textStyle.tsSize=kPETERelativeSizeBase;
#else
if (newStyle.psStyle.textStyle.tsSize==FontSize) newStyle.psStyle.textStyle.tsSize=kPETEDefaultSize;
#endif
runLen -= offset-newStyle.psStartChar;
#ifdef DEBUG
MakePStr(scratch,*text+offset,runLen);
#endif
/*
* check for lily-white style run
*/
kkk = True;
for (spot=*text+offset;spot<*text+offset+runLen;spot++)
{
if (!IsSpace(*spot)) {kkk=False;break;}
}
/*
* what's changed?
*/
styleDiff = PeteTextStyleDiff(&oldStyle.psStyle.textStyle,&newStyle.psStyle.textStyle);
/*
* if all white and underline hasn't changed, pretend no change
*/
if (kkk && !(styleDiff&underline))
{
newStyle = oldStyle;
styleDiff = 0;
}
PETEGetParaIndex(PETE,pte,offset,&para);
if (oldPara!=para)
{
newInfo.tabHandle = nil;
PETEGetParaInfo(PETE,pte,para,&newInfo);
paraDiff = PeteParaInfoDiff(&oldInfo,&newInfo);
oldPara = para;
}
else paraDiff = 0;
/*
* anything changed?
*/
if (paraDiff || styleDiff)
{
if (err=BuildEnrichedDirectives(
styleDiff?&oldStyle.psStyle.textStyle:nil,&newStyle.psStyle.textStyle,
paraDiff?&oldInfo:nil,&newInfo,
openStack,redoStack,
enriched)) goto done;
oldStyle = newStyle;
oldInfo = newInfo;
cr = False;
#ifdef NEVER
if (RunType==Debugging)
{
Zero(ess);
PSCopy(ess.styleName,scratch);
ess.textStyle = newStyle.psStyle.textStyle;
ess.paraInfo = newInfo;
ess.paraValid = peAllParaValid;
ess.textValid = peAllValid;
DebugStr(Style2String(&ess,scratch));
}
#endif
}
}
else
runLen = len-offset;
if (!pte)
{
pushMe.directive = enNoFill;
ISSUE_DIRECTIVE;
err = AccuAddChar(enriched,'\015');
if (err) goto done;
}
runLen += offset;
while (offset<runLen)
{
c = (*text)[offset++];
if (c=='\015' && pte)
{
if (!cr)
{
cr = True;
if (err=AccuAddChar(enriched,c)) goto done;
}
if (err=AccuAddChar(enriched,c)) goto done;
}
else
{
cr = False;
if (c=='<') if (err=AccuAddChar(enriched,c)) goto done;
if (err=AccuAddChar(enriched,c)) goto done;
}
}
}
}
done:
if (!err)
{
// trim off one hard linebreak
if (enriched->offset && (*enriched->data)[enriched->offset-1]=='\015')
enriched->offset--;
if (enriched->offset && (*enriched->data)[enriched->offset-1]=='\015')
enriched->offset--;
// finish up the directives
err = UnwindRich(nil,openStack,nil,enriched);
if (err==fnfErr) err = 0;
// make sure we end with hard linebreak
while (enriched->offset<2) AccuAddChar(enriched,'\015');
if ((*enriched->data)[enriched->offset-1] != '\015') AccuAddChar(enriched,'\015');
if ((*enriched->data)[enriched->offset-2] != '\015') AccuAddChar(enriched,'\015');
}
ZapHandle(openStack);
ZapHandle(redoStack);
if (!err) AccuTrim(enriched);
return(err);
}
/**********************************************************************
* BuildEnrichedDirectives - build directives for moving from one style to another
**********************************************************************/
OSErr BuildEnrichedDirectives(PETETextStylePtr oldStyle,PETETextStylePtr newStyle,
PETEParaInfoPtr oldInfo,PETEParaInfoPtr newInfo,
StackHandle openStack,StackHandle redoStack,
AccuPtr enriched)
{
short styleDiff = oldStyle ? PeteTextStyleDiff(oldStyle,newStyle) : 0;
short paraDiff = oldInfo ? PeteParaInfoDiff(oldInfo,newInfo) : 0;
short counters[enPLeft];
short i;
OSErr err = noErr;
Str255 directive;
DirectiveType pushMe;
short oldInc, newInc, normInc;
PeteSaneMargin marg;
/*
* undo section
*/
// clear counters
for (i=0;i<sizeof(counters)/sizeof(short);i++) counters[i]=0;
// figure out what it is we have to undo
if ((styleDiff & bold) && !(newStyle->tsFace&bold)) counters[enBold]=1;
if ((styleDiff & italic) && !(newStyle->tsFace&italic)) counters[enItalic]=1;
if ((styleDiff & underline) && !(newStyle->tsFace&underline)) counters[enUnderline]=1;
if (styleDiff & peFontValid && oldStyle->tsFont!=kPETEDefaultFont)
{
#ifdef USEFIXEDDEFAULTFONT
if(oldStyle->tsFont == kPETEDefaultFixed)
counters[enFixed]=1;
else
#endif
counters[enFont]=1;
}
if (styleDiff & peColorValid && !IS_DEFAULT_COLOR(oldStyle->tsColor)) counters[enColor]=1;
if ((styleDiff & peSizeValid))
{
oldInc = FindSizeInc(oldStyle->tsSize);
newInc = FindSizeInc(newStyle->tsSize);
normInc = FindSizeInc(FontSize);
if (oldInc==newInc) styleDiff &= ~peSizeValid;
else if (oldInc<newInc)
{
if (oldInc<normInc)
{
while(oldInc<normInc && oldInc<newInc) {counters[enSmaller]++;oldInc++;}
}
}
else if (oldInc>newInc)
{
if (oldInc>normInc)
{
while(oldInc>normInc && oldInc>newInc) {counters[enBigger]++;oldInc--;}
}
}
if (oldInc==newInc) styleDiff &= ~peSizeValid;
}
if ((paraDiff & peQuoteLevelValid) && oldInfo->quoteLevel>newInfo->quoteLevel)
counters[enExcerpt] = oldInfo->quoteLevel-newInfo->quoteLevel;
if (paraDiff & peJustificationValid)
{
if (oldInfo->justification==teCenter)
counters[enCenter] = 1;
else if (oldInfo->justification==teFlushRight)
counters[enRight] = 1;
if (newInfo->justification==teFlushDefault || newInfo->justification==teFlushLeft)
paraDiff &= ~peJustificationValid;
}
if (paraDiff & (peEndMarginValid|peStartMarginValid|peIndentValid))
{
PeteConvert2Marg(oldInfo,&marg);
if (marg.first||marg.second||marg.right) counters[enPara]++;
}
//undo them
if (err=UnwindRich(counters,openStack,redoStack,enriched)) goto done;
//redo the ones we need to redo
if (err=RewindRich(openStack,redoStack,enriched)) goto done;
/*
* do section
*/
if (paraDiff & peQuoteLevelValid)
for(i=oldInfo->quoteLevel;i<newInfo->quoteLevel;i++)
{
pushMe.directive = enExcerpt;
ISSUE_DIRECTIVE;
}
if (paraDiff & peJustificationValid)
{
if (newInfo->justification==teCenter) pushMe.directive = enCenter;
else pushMe.directive = enRight;
ISSUE_DIRECTIVE;
}
if (paraDiff & (peEndMarginValid|peStartMarginValid|peIndentValid))
{
PeteConvert2Marg(newInfo,&marg);
if (marg.first||marg.second||marg.right)
{
pushMe.directive = enPara;
pushMe.param.marg = marg;
ISSUE_DIRECTIVE;
BuildRichParaParam(directive,&marg);
AccuAddStr(enriched,directive);
}
}
if ((styleDiff & bold) && (newStyle->tsFace&bold))
{
pushMe.directive = enBold;
ISSUE_DIRECTIVE;
}
if ((styleDiff & italic) && (newStyle->tsFace&italic))
{
pushMe.directive = enItalic;
ISSUE_DIRECTIVE;
}
if ((styleDiff & underline) && (newStyle->tsFace&underline))
{
pushMe.directive = enUnderline;
ISSUE_DIRECTIVE;
}
if (styleDiff & peFontValid && newStyle->tsFont!=kPETEDefaultFont)
{
#ifdef USEFIXEDDEFAULTFONT
if(newStyle->tsFont==kPETEDefaultFixed)
{
pushMe.directive = enFixed;
ISSUE_DIRECTIVE;
}
else
#endif
{
pushMe.directive = enFont;
pushMe.param.fontID = newStyle->tsFont;
ISSUE_DIRECTIVE;
BuildRichFontParam(directive,newStyle->tsFont);
AccuAddStr(enriched,directive);
}
}
if (styleDiff & peColorValid && !IS_DEFAULT_COLOR(newStyle->tsColor))
{
pushMe.directive = enColor;
pushMe.param.color = newStyle->tsColor;
ISSUE_DIRECTIVE;
BuildRichColorParam(directive,&newStyle->tsColor);
AccuAddStr(enriched,directive);
}
if (styleDiff & peSizeValid)
{
//smaller?
if (oldInc>newInc)
{
pushMe.directive = enSmaller;
while(oldInc>newInc) {ISSUE_DIRECTIVE;oldInc--;}
}
//bigger?
else if (oldInc<newInc)
{
pushMe.directive = enBigger;
while(oldInc<newInc) {ISSUE_DIRECTIVE;oldInc++;}
}
}
done:
return(err);
}
/**********************************************************************
* BuildRichParaParam - build the font parameter
**********************************************************************/
PStr BuildRichParaParam(PStr directive,PSMPtr marg)
{
Str255 guts;
short i;
*guts = 0;
for (i=marg->right;i;i--)
{
PCatR(guts,EnrichedStrn+enPRight);
PCatC(guts,',');
}
if (marg->first>marg->second)
{
for (i=marg->second;i;i--)
{
PCatR(guts,EnrichedStrn+enPLeft);
PCatC(guts,',');
}
for (i=marg->first-marg->second;i;i--)
{
PCatR(guts,EnrichedStrn+enPIn);
PCatC(guts,',');
}
}
else
{
for (i=marg->second;i;i--)
{
PCatR(guts,EnrichedStrn+enPLeft);
PCatC(guts,',');
}
for (i=marg->second-marg->first;i;i--)
{
PCatR(guts,EnrichedStrn+enPOut);
PCatC(guts,',');
}
}
if (*guts)
{
--*guts;
ComposeRString(directive,MIME_RICH_PARAM,guts);
}
else *directive = 0;
return(directive);
}
/**********************************************************************
* BuildRichFontParam - build the font parameter
**********************************************************************/
PStr BuildRichFontParam(PStr directive,short fontID)
{
Str255 scratch;
GetFontName(fontID,scratch);
PTr(scratch," ","_");
ComposeRString(directive,MIME_RICH_PARAM,scratch);
return(directive);
}
/**********************************************************************
* BuildRichColorParam - build the font parameter
**********************************************************************/
PStr BuildRichColorParam(PStr directive,RGBColor *color)
{
Str31 scratch;
Bytes2Hex((void*)&color->red,2,scratch+1);
Bytes2Hex((void*)&color->green,2,scratch+6);
Bytes2Hex((void*)&color->blue,2,scratch+11);
scratch[5] = scratch[10] = ',';
*scratch = 14;
ComposeRString(directive,MIME_RICH_PARAM,scratch);
return(directive);
}
/**********************************************************************
* UnwindRich - unwind richtext to remove certain directives
**********************************************************************/
OSErr UnwindRich(short *counters,StackHandle openStack,StackHandle redoStack,AccuPtr enriched)
{
OSErr err = fnfErr;
DirectiveType top;
Str31 cmd;
long counterCount=0;
short i;
if (counters)
{
counterCount = 0;
for (i=0;i<enPLeft;i++) counterCount += counters[i];
if (!counterCount) return(noErr);
}
while(!StackPop(&top,openStack))
{
ComposeRString(cmd,MIME_RICH_OFF,EnrichedStrn+top.directive);
if (top.directive==enNoFill && (err=AccuAddChar(enriched,'\015'))) goto done;
if (err=AccuAddStr(enriched,cmd)) goto done;
if (counters && counters[top.directive])
{
counters[top.directive]--;
if (!--counterCount) break;
}
else if (redoStack) err = StackPush(&top,redoStack);
}
done:
return(err ? err : (counterCount?fnfErr:noErr));
}
/**********************************************************************
* RewindRich - Reissue a set of directives
**********************************************************************/
OSErr RewindRich(StackHandle openStack,StackHandle redoStack,AccuPtr enriched)
{
OSErr err = noErr;
DirectiveType top;
Str255 cmd;
while(!err && !StackPop(&top,redoStack))
{
ComposeRString(cmd,MIME_RICH_ON,EnrichedStrn+top.directive);
if (err=AccuAddStr(enriched,cmd)) goto done;
switch(top.directive)
{
case enColor:
BuildRichColorParam(cmd,&top.param.color);
if (err=AccuAddStr(enriched,cmd)) goto done;
break;
case enPara:
BuildRichParaParam(cmd,&top.param.marg);
if (err=AccuAddStr(enriched,cmd)) goto done;
break;
case enFont:
BuildRichFontParam(cmd,top.param.fontID);
if (err=AccuAddStr(enriched,cmd)) goto done;
break;
}
err = StackPush(&top,openStack);
}
done:
return(err);
}
/**********************************************************************
* Text/Enriched interpretation
**********************************************************************/
#define enUnknown (0)
#define enText (-1)
#define enCR (-2)
#define enParamText (-3)
#define enDoubleLess (-4)
/************************************************************************
* PeteRich - interpret rich text in a PETE record
************************************************************************/
OSErr PeteRich(PETEHandle pte,long start,long stop,Boolean unwrap)
{
UHandle text=nil;
UHandle enriched=nil;
long len;
OSErr err = noErr;
/*
* grab the text/enriched
*/
PETEGetRawText(PETE,pte,&text);
len = stop ? stop-start : GetHandleSize_(text)-start;
enriched = NuHTempBetter(len);
if (!enriched) err = MemError();
else
{
BMD(*text+start,*enriched,len);
/*
* now remove the rich text from the edit record
*/
PeteDelete(pte,start,start+len);
/*
* now put it back, with interpretation
*/
err = InsertRich(enriched,0,-1,start,unwrap,pte,nil,false);
ZapHandle(enriched);
}
return(err);
}
/************************************************************************
* InsertRich - insert some text that contains markup
************************************************************************/
OSErr InsertRich(UHandle text,long textOffset,long textLen,long offset,Boolean unwrap,PETEHandle pte,StackHandle partStack,Boolean delSP)
{
return InsertRichLo(text, textOffset, textLen, offset, false, unwrap, pte, partStack, nil, delSP);
}
/************************************************************************
* InsertRichLo - insert some text that contains markup
************************************************************************/
OSErr InsertRichLo(UHandle text,long textOffset,long textLen,long offset,Boolean headers,Boolean unwrap,PETEHandle pte,StackHandle partStack,MessHandle messH,Boolean delSP)
{
short cmdId;
Boolean neg;
long tStart,tStop, tempiOffset, tempoOffset, tempStart;
OSErr err = noErr;
Str255 scratch;
PartDesc pd;
Boolean wasRel = False;
Boolean paraMe = false;
long realOffset;
StackHandle partRefStack;
if (!text) return(noErr);
if (textLen==-1) textLen = GetHandleSize(text);
if (textLen && (*text)[textLen-1]=='\015') textLen--; // trim one trailing newline
tStart = tStop = textOffset;
StackInit(sizeof(uLong),&partRefStack);
while (!err && !(err=EnrichedToken(text,textLen,headers,&cmdId,&neg,&tStart,&tStop)))
{
TextEncoding encoding;
encoding = kTextEncodingUnknown;
TryItAgain :
if(!neg) {
if((tempiOffset = offset) == -1) PeteGetTextAndSelection(pte,nil,&tempiOffset,nil);
tempStart = tStart;
switch(cmdId) {
default :
goto JustText;
case enXRich :
err = InsertEnrichedLo(text,&tStart,textLen-tStart,&offset,unwrap,pte, encoding);
break;
case enXCharset :
err = InsertFixed(text,&tStart,textLen-tStart,&offset,pte,encoding);
break;
case enXFlowed :
err = InsertFlowedLo(text,&tStart,textLen-tStart,&offset,pte,delSP,encoding);
break;
case enXHTML :
case enHTML :
err = InsertHTMLLo(text,&tStart,textLen-tStart,&offset,pte,encoding,0,partRefStack);
}
if(EncodingError(err) && (encoding == kTextEncodingUnknown) && (messH != nil))
{
if(!(*(*messH)->tocH)->sums[(*messH)->sumNum].mesgErrH) {
AddMesgError((*messH)->tocH, (*messH)->sumNum, GetRString(scratch,BAD_CHARSET_ERR), err);
}
if((tempoOffset = offset) == -1) PeteGetTextAndSelection(pte,nil,&tempoOffset,nil);
offset = tempiOffset;
PeteDelete(pte, tempiOffset, tempoOffset);
encoding = CreateSystemRomanEncoding();
tStart = tempStart;
goto TryItAgain;
}
tStop = tStart;
wasRel = false;
paraMe = true;
}
else
{
JustText :
MakePStr(scratch,*text+tStart,tStop-tStart);
if (!RelLine2Spec(scratch,&pd.spec,&pd.cid,&pd.relURL,&pd.absURL))
{
if (partStack && PartIsReferenced(pd.cid,pd.relURL,pd.absURL,partRefStack))
StackQueue(&pd,partStack);
else
{
err = FakeAttachment(pte,&offset,&pd.spec);
if (err) break;
paraMe = false;
}
wasRel = True;
}
else if (!wasRel)
{
if (paraMe) // make sure we start a new para?
{
if (offset==-1) PeteGetTextAndSelection(pte,nil,&realOffset,nil);
else realOffset = offset;
}
if(headers)
{
err = PeteInsertHeader(pte,&offset,text,tStop-tStart,tStart);
if(EncodingError(err)) err = noErr;
else if(err) break;
if((tStop-tStart == 2) && ((*text)[tStart] == 13) && ((*text)[tStart+1] == 13))
headers = false;
}
else
{
err = PETEInsertTextHandle(PETE,pte,offset,text,tStop-tStart,tStart,nil);
if(err) break;
if (offset!=-1) offset += tStop-tStart;
}
// plain, please
if (paraMe)
{
long oldLen = PeteLen(pte);
PeteEnsureCrAndBreakLo(pte,realOffset,&realOffset,&paraMe);
PetePlainParaAt(pte,realOffset,realOffset+tStop-tStart);
if (offset==-1) PeteSelect(nil,pte,realOffset+tStop-tStart,realOffset+tStop-tStart);
else offset += PeteLen(pte)-oldLen;
}
}
else wasRel = false; // skip only one token
paraMe = false;
}
}
ZapHandle(partRefStack);
return(err==eofErr ? noErr : err);
}
/************************************************************************
* PartIsReferenced - is this part actually referenced by anything?
************************************************************************/
Boolean PartIsReferenced(uLong cID,uLong baseID,uLong sourceID,StackHandle partRefStack)
{
uLong id;
short item;
if (!partRefStack) return false;
for (item=0;item<(*partRefStack)->elCount;item++)
{
StackItem(&id,item,partRefStack);
if (id==cID || id==baseID || id==sourceID) return true;
}
return false;
}
/************************************************************************
* FakeAttachment - an html part wasn't referenced by anything; pretend
* it's an attachment
************************************************************************/
OSErr FakeAttachment(PETEHandle pte,uLong *offset,FSSpecPtr spec)
{
OSErr err;
Str255 scratch;
// snide comment
GetRString(scratch,UNREFERENCED_PART);
err = PeteInsertPtr(pte,*offset,scratch+1,*scratch);
if (err) return err;
if (*offset!=-1) *offset += *scratch;
// attachment
AttachNoteLo(spec,scratch);
err = PeteInsertPtr(pte,*offset,scratch+1,*scratch);
if (err) return err;
if (*offset!=-1) *offset += *scratch;
return noErr;
}
/************************************************************************
* InsertFlowed - insert some format=flowed, until we run out
************************************************************************/
OSErr InsertFlowed(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, Boolean delSP)
{
return InsertFlowedLo(text, textOffset, textLen, inOffset, pte, delSP, kTextEncodingUnknown);
}
/************************************************************************
* InsertFlowed - insert some format=flowed, until we run out
************************************************************************/
OSErr InsertFlowedLo(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, Boolean delSP, TextEncoding encoding)
{
long tStart, tStop, cStart;
Str31 notFlowed, testMe;
Boolean flowNext = false;
short size;
long offset = *inOffset;
OSErr err = noErr;
Boolean interpret = UseFlowIn;
Boolean excerpt = UseFlowInExcerpt;
long quoteLevel, oldQuoteLevel=0;
long lastCR;
Str15 sigSep;
#ifdef DEBUG
Str255 line;
#endif
IntlConverter converter;
err = CreateIntlConverter(&converter, encoding);
if(err) return err;
textLen += *textOffset; // reset to absolute offset, please
if (offset==-1) PeteGetTextAndSelection(pte,nil,&offset,nil);
err = PeteEnsureCrAndBreak(pte,lastCR=offset,&offset);
if(err) return err;
ComposeRString(notFlowed,MIME_RICH_OFF,EnrichedStrn+enXFlowed);
GetRString(sigSep,SIG_SEPARATOR);
// skip token
for (cStart = -1, tStart = *textOffset;tStart<textLen;tStart++)
{
if ((*text)[tStart]==' ') cStart = tStart + 1;
if ((*text)[tStart]=='>') break;
}
if((cStart > 0) && (encoding == kTextEncodingUnknown))
{
MakePStr(testMe, (*text) + cStart, tStart - cStart);
UpdateIntlConverter(&converter, testMe);
}
tStart++;
/*
* process input one line at a time
*/
while(!err && tStart<textLen)
{
// gather line
for (tStop=tStart;tStop<textLen&&(*text)[tStop]!='\015';tStop++);
#ifdef DEBUG
MakePStr(line,*text+tStart,tStop-tStart);
#endif
// is it the magic turn-off?
if (tStop-tStart==*notFlowed)
{
MakePStr(testMe,*text+tStart,tStop-tStart);
if (StringSame(testMe,notFlowed))
{
if (flowNext) // add back in newline we removed
{
err = FlushIntlText(pte, &offset, &converter);
if(err) break;
if (excerpt)
{
err = PeteEnsureBreak(pte,lastCR);
if(err) break;
}
err = PeteInsertChar(pte,offset,'\015',nil);
if(err) break;
offset++;
if (excerpt) err = PeteSetExcerptLevelAt(pte,lastCR=offset,quoteLevel);
}
break;
}
}
if (interpret)
{
// figure current quotelevel
for (quoteLevel=tStart;quoteLevel<tStop&&(*text)[quoteLevel]=='>';quoteLevel++);
quoteLevel -= tStart;
// if quotelevel changes, make hard newline
if (oldQuoteLevel!=quoteLevel && flowNext)
{
err = FlushIntlText(pte, &offset, &converter);
if(err) break;
if (excerpt)
{
err = PeteEnsureBreak(pte,lastCR);
if(err) break;
}
err = PeteInsertChar(pte,offset,'\015',nil);
if(err) break;
++offset;
if (excerpt) err = PeteSetExcerptLevelAt(pte,lastCR=offset,oldQuoteLevel);
flowNext = false;
}
oldQuoteLevel = quoteLevel;
// undo space-stuffing?
if ((*text)[tStart]==' ') tStart++;
// remove quotes
else if ((excerpt||flowNext) && (*text)[tStart]=='>')
{
while (tStart<tStop&&(*text)[tStart]=='>') tStart++;
if (tStart<tStop && (*text)[tStart]==' ') tStart++; // more space-stuffing
}
// how about next time around?
flowNext = false;
if (tStart<tStop)
{
flowNext = (*text)[tStop-1]==' ';
if (flowNext && tStart<tStop-2)
flowNext = (*text)[tStop-3]!=' ' || (*text)[tStop-2]!=' ';
if (flowNext && tStop-tStart==*sigSep)
{
LDRef(text);
flowNext = 0!=memcmp(sigSep+1,*text+tStart,*sigSep);
UL(text);
}
}
}
// how much do we insert?
size = tStop-tStart;
// include return if next line not flowed
if (!flowNext && tStop<textLen) size++; // add return
// trim space if we're doing that sort of thing
if (flowNext && delSP && (*text)[tStop-1]==' ') size--;
// and insert
if (size>0)
{
Boolean excerptChange;
excerptChange = excerpt && !flowNext && quoteLevel!=PeteIsExcerptAt(pte,lastCR);
err = PeteInsertIntlText(pte,&offset,text,tStart,tStart+size,&converter,kTextEncodingUnknown,false,excerptChange);
if (!err && excerptChange)
{
err = PeteEnsureBreak(pte,lastCR);
if(!err) err = PeteSetExcerptLevelAt(pte,offset,quoteLevel);
}
if (!flowNext) lastCR = offset;
}
tStart = tStop+1;
}
if (*inOffset!=-1) *inOffset = offset; // record where we ended
else PeteSelect(nil,pte,offset,offset); // if at insertion point, make sure we end insertion point at end of text
*textOffset = tStop;
DisposeIntlConverter(converter);
return(err);
}
OSErr InsertFixed(UHandle text, long *textOffset, long textLen, long *inOffset, PETEHandle pte, TextEncoding encoding)
{
long tStart, tStop, cStart, textStart;
Str31 notCharset, testMe;
long offset = *inOffset;
OSErr err = noErr;
IntlConverter converter;
err = CreateIntlConverter(&converter, encoding);
if(err) return err;
textLen += *textOffset; // reset to absolute offset, please
if (offset==-1) PeteGetTextAndSelection(pte,nil,&offset,nil);
PeteEnsureCrAndBreak(pte,offset,&offset);
ComposeRString(notCharset,MIME_RICH_OFF,EnrichedStrn+enXCharset);
// skip token
for (cStart = -1, tStart = *textOffset;tStart<textLen;tStart++)
{
if ((*text)[tStart]==' ') cStart = tStart + 1;
if ((*text)[tStart]=='>') break;
}
if((cStart > 0) && (encoding == kTextEncodingUnknown))
{
MakePStr(testMe, (*text) + cStart, tStart - cStart);
UpdateIntlConverter(&converter, testMe);
if(err) goto DoDispose;
}
textStart = ++tStart;
/*
* Look for end one line at a time
*/
while(tStart<textLen)
{
// gather line
for (tStop=tStart;tStop<textLen&&(*text)[tStop]!='\015';tStop++);
// is it the magic turn-off?
if (tStop-tStart==*notCharset)
{
MakePStr(testMe,*text+tStart,tStop-tStart);
if (StringSame(testMe,notCharset)) break;
}
tStart = tStop + 1;
}
err = PeteInsertIntlText(pte,&offset,text,textStart,tStart,&converter,kTextEncodingUnknown,false,true);
if (*inOffset!=-1) *inOffset = offset; // record where we ended
else PeteSelect(nil,pte,offset,offset); // if at insertion point, make sure we end insertion point at end of text
*textOffset = tStop;
DoDispose :
DisposeIntlConverter(converter);
return err;
}
/************************************************************************
* InsertEnriched - insert some text/enriched, until we run out
************************************************************************/
OSErr InsertEnriched(UHandle text, long *textOffset, long textLen, long *inOffset, Boolean unwrap, PETEHandle pte)
{
return InsertEnrichedLo(text,textOffset,textLen,inOffset,unwrap,pte,kTextEncodingUnknown);
}
/************************************************************************
* InsertEnriched - insert some text/enriched, until we run out
************************************************************************/
OSErr InsertEnrichedLo(UHandle text, long *textOffset, long textLen, long *inOffset, Boolean unwrap, PETEHandle pte, TextEncoding encoding)
{
//variables related to the current command
long origStart = *inOffset;
long start;
long tStart, tStop;
short cmdId;
Boolean neg;
short lastCmd;
//interpreter state variables
short noFill = 0;
short inRich = 0;
short inParam = 0;
short sizeInc = 0;
short styleCounters[3];
StackHandle fontStack=nil;
StackHandle justStack=nil;
StackHandle margStack=nil;
StackHandle colorStack=nil;
Str255 paramText;
//global prefs
uLong validMask = ~GetPrefLong(PREF_INTERPRET_ENRICHED);
uLong validParaMask = ~GetPrefLong(PREF_INTERPRET_PARA);
//scratch variables
OSErr err = noErr;
PETETextStyle style;
Str255 scratch;
PETEParaInfo pinfo;
PeteSaneMargin marg, curMarg;
PETEStyleEntry pse;
short exLevel = 0;
long offset;
Boolean cr, needSpace;
long pIndex;
UHandle origText;
IntlConverter converter;
err = CreateIntlConverter(&converter, encoding);
if(err) return err;
if (!(validParaMask&1)) validParaMask &= ~7L; // if "margins" turned off, turn off all indents
tStart = tStop = *textOffset;
/*
* initialize state
*/
PeteGetTextAndSelection(pte,&origText,(origStart==-1)?&origStart:nil,nil);
start = origStart;
cr = (!start || (*origText)[start-1]=='\015');
needSpace = false;
styleCounters[0] = styleCounters[1] = styleCounters[2] = 0;
if (!(err=StackInit(sizeof(style.tsFont),&fontStack)) &&
!(err=StackInit(sizeof(pinfo.justification),&justStack)) &&
!(err=StackInit(sizeof(PeteSaneMargin),&margStack)) &&
!(err=StackInit(sizeof(RGBColor),&colorStack)))
{
/*
* make sure we start at a paragraph
*/
PeteParaConvert(pte,start,start);
PETESelect(PETE,pte,start,start);
/*
* make the paragraph normal.
*/
Zero(marg);
PeteConvertMarg(pte,kPETEDefaultPara,&marg,&pinfo);
PeteGetTextAndSelection(pte,nil,nil,&offset);
pIndex = PeteParaAt(pte,offset);
PETESetParaInfo(PETE,pte,pIndex,&pinfo,
peStartMarginValid|peIndentValid|peEndMarginValid);
PETEGetStyle(PETE,pte,kPETEDefaultStyle,nil,&pse);
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,&pse.psStyle.textStyle,peAllValid);
/*
* operate on tokens from the text/enriched
*/
tStart = tStop = *textOffset;
while (!err && !(err=EnrichedToken(text,*textOffset+textLen,false,&cmdId,&neg,&tStart,&tStop)))
{
#ifdef DEBUG
MakePStr(scratch,*text+tStart,tStop-tStart);
#endif
CycleBalls();
if (!inRich && (cmdId!=enXRich || neg)) cmdId = enText;
if (inParam && cmdId!=enParam) cmdId = enParamText;
switch (cmdId)
{
case enXRich:
if (neg) inRich = MAX(inRich-1,0);
else inRich++;
if (!inRich)
{
tStart = tStop;
goto end;
}
break;
case enCR:
needSpace = false;
if (!noFill)
{
if (unwrap && tStop-tStart==1)
{
// PeteInsertChar(pte,-1L,' ',nil);
needSpace = true;
cr = False;
}
else
{
if (unwrap) tStart++;
for (;tStart<tStop;tStart++)
{
FlushIntlText(pte, nil, &converter);
PeteInsertChar(pte,-1,'\015',nil);
PeteGetTextAndSelection(pte,nil,&start,nil);
PETEInsertParaBreak(PETE,pte,start);
cr = True;
}
}
break;
}
cr = True; // Set cr to true when inserting a nofill cr - pr 9/24/96
// fall through to enText if noFill
DoText :
case enText:
err = PeteInsertIntlText(pte, nil, text, tStart, tStop, &converter, kTextEncodingUnknown, needSpace, false);
needSpace = false;
if(cmdId == enText) cr = False; // Reset cr to false after inserted text - pr 9/24/96
break;
case enDoubleLess:
// err = PeteInsertChar(pte,-1L,'<',nil);
cr = False; // Reset cr to false after inserted text - pr 9/24/96
++tStart;
goto DoText;
// break;
case enUnderline:
case enItalic:
case enBold:
if (neg)
styleCounters[cmdId-1] = MAX(0,styleCounters[cmdId-1]-1);
else
styleCounters[cmdId-1]++;
style.tsFace = styleCounters[cmdId-1] ? 1<<(cmdId-1) : 0;
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,(1<<(cmdId-1))&validMask);
break;
case enColor:
if (neg) StackPop(nil,colorStack); // drop top one
if (StackTop(&style.tsColor,colorStack))
{
PeteStyleAt(pte,kPETEDefaultStyle,&pse);
style.tsColor = pse.psStyle.textStyle.tsColor;
}
if (!neg) StackPush(&style.tsColor,colorStack);
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,peColorValid&validMask);
break;
case enSmaller:
case enBigger:
if (cmdId==enSmaller&&neg || cmdId==enBigger&&!neg)
sizeInc++;
else
sizeInc--;
#ifdef USERELATIVESIZES
style.tsSize = kPETERelativeSizeBase+sizeInc;
#else
style.tsSize = IncrementTextSize(FontSize,sizeInc);
#endif
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,peSizeValid&validMask);
break;
case enFont:
case enFixed:
if (neg)
{
StackPop(nil,fontStack); //dump current one
if (StackTop(&style.tsFont,fontStack)) style.tsFont = kPETEDefaultFont;
}
else
{
if (cmdId==enFont)
{
if (StackTop(&style.tsFont,fontStack))
style.tsFont = kPETEDefaultFont;
}
else
style.tsFont = GetFontID(GetRString(scratch,FIXED_FONT));
StackPush(&style.tsFont,fontStack);
}
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,peFontValid&validMask);
break;
case enNoFill:
/* Added to ensure break around nofill - pr 9/24/96 */
PeteEnsureBreak(pte,origStart);
if (!cr)
{
FlushIntlText(pte, nil, &converter);
PeteInsertChar(pte,-1L,'\015',nil);
PeteGetTextAndSelection(pte,nil,&start,nil);
PETEInsertParaBreak(PETE,pte,start);
cr = True;
}
/* End added section */
if (neg) noFill = MAX(noFill-1,0);
else noFill++;
break;
case enLeft:
case enCenter:
case enRight:
PeteEnsureBreak(pte,origStart);
if (!cr)
{
FlushIntlText(pte, nil, &converter);
PeteInsertChar(pte,-1L,'\015',nil);
PeteGetTextAndSelection(pte,nil,&start,nil);
PETEInsertParaBreak(PETE,pte,start);
cr = True;
}
Zero(pinfo);
if (neg)
{
StackPop(nil,justStack); // dump current one
if (StackTop(&pinfo.justification,justStack)) pinfo.justification = teFlushDefault;
}
else
{
switch(cmdId)
{
case enLeft: pinfo.justification = teFlushLeft; break;
case enCenter: pinfo.justification = teCenter; break;
case enRight: pinfo.justification = teFlushRight; break;
}
StackPush(&pinfo.justification,justStack);
}
PeteGetTextAndSelection(pte,nil,nil,&offset);
pIndex = PeteParaAt(pte,offset);
PETESetParaInfo(PETE,pte,pIndex,&pinfo,validParaMask&peJustificationValid);
break;
case enExcerpt:
PeteEnsureBreak(pte,origStart);
if (!cr)
{
FlushIntlText(pte, nil, &converter);
PeteInsertChar(pte,-1L,'\015',nil);
PeteGetTextAndSelection(pte,nil,&start,nil);
PETEInsertParaBreak(PETE,pte,start);
cr = True;
}
Zero(pinfo);
if (neg)
exLevel = MAX(0,exLevel-1); // dump current one
else
exLevel++;
pinfo.quoteLevel = exLevel;
PeteGetTextAndSelection(pte,nil,nil,&offset);
pIndex = PeteParaAt(pte,offset);
PETESetParaInfo(PETE,pte,pIndex,&pinfo,validParaMask&peQuoteLevelValid);
break;
case enPara:
PeteEnsureBreak(pte,origStart);
if (!cr)
{
FlushIntlText(pte, nil, &converter);
PeteInsertChar(pte,-1L,'\015',nil);
PeteGetTextAndSelection(pte,nil,&start,nil);
PETEInsertParaBreak(PETE,pte,start);
cr = True;
}
Zero(pinfo);
if (neg) StackPop(nil,margStack); // drop top one
if (StackTop(&marg,margStack)) Zero(marg);
if (!neg) StackPush(&marg,margStack);
PeteConvertMarg(pte,kPETEDefaultPara,&marg,&pinfo);
PeteGetTextAndSelection(pte,nil,nil,&offset);
pIndex = PeteParaAt(pte,offset);
PETESetParaInfo(PETE,pte,pIndex,&pinfo,validParaMask&(peStartMarginValid|peIndentValid|peEndMarginValid));
break;
case enParam:
if (neg)
{
inParam = MAX(0,inParam-1);
switch(lastCmd)
{
case enFont:
style.tsFont=GetFontID(paramText);
if (style.tsFont==1)
{
PTr(paramText,"_"," ");
style.tsFont = GetFontID(paramText);
}
if (style.tsFont!=1)
{
// *replace* the top font with the new one
StackPop(nil,fontStack);
StackPush(&style.tsFont,fontStack);
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,peFontValid&validMask);
}
break;
case enColor:
if (!ColorParam(&style.tsColor,paramText))
{
// *replace* the top color with the new one
StackPop(nil,colorStack);
StackPush(&style.tsColor,colorStack);
PETESetTextStyle(PETE,pte,kPETECurrentStyle,kPETECurrentStyle,
&style,peColorValid&validMask);
}
break;
case enPara:
PeteEnsureBreak(pte,origStart);
Zero(pinfo);
if (!ParaIndent2Margin(&marg,paramText))
{
// *replace* the top margin with the new one
if (StackPop(&curMarg,margStack)) Zero(curMarg);
marg.first += curMarg.first;
marg.second += curMarg.second;
marg.right += curMarg.right;
PeteConvertMarg(pte,kPETEDefaultPara,&marg,&pinfo);
StackPush(&marg,margStack);
PeteGetTextAndSelection(pte,nil,nil,&offset);
pIndex = PeteParaAt(pte,offset);
PETESetParaInfo(PETE,pte,pIndex,&pinfo,
validParaMask&(peStartMarginValid|peIndentValid|peEndMarginValid));
}
break;
case enXRich:
if(encoding == kTextEncodingUnknown)
{
UpdateIntlConverter(&converter, paramText);
}
}
}
else inParam++;
break;
case enParamText:
MakePStr(scratch,*text+tStart,tStop-tStart);
PSCat(paramText,scratch);
break;
}
if (cmdId!=enParam && cmdId!=enParamText) lastCmd=cmdId;
if (cmdId!=enParamText && !(cmdId==enParam && neg)) *paramText = 0;
}
}
end:
*textOffset = tStart;
if (*inOffset!=-1) PeteGetTextAndSelection(pte,nil,inOffset,nil);
ZapHandle(margStack);
ZapHandle(fontStack);
ZapHandle(justStack);
ZapHandle(colorStack);
DisposeIntlConverter(converter);
return err==eofErr ? noErr : err;
}
/**********************************************************************
* ParaIndent2Margin - convert a paraindent command to one of my margin things
**********************************************************************/
OSErr ParaIndent2Margin(PSMPtr marg,PStr string)
{
Str255 token;
UPtr spot;
OSErr err = fnfErr;
Zero(*marg);
for (spot=string+1;PToken(string,token,&spot,",");)
{
err = noErr;
switch(FindSTRNIndex(EnrichedStrn,token))
{
case enPLeft: marg->second++; marg->first++; break;
case enPRight: marg->right++; break;
case enPIn: marg->first++; break;
case enPOut: marg->second++; break;
default: return(fnfErr);
}
}
return(noErr);
}
/**********************************************************************
* EnrichedToken - get the next token from a text/enriched stream
**********************************************************************/
OSErr EnrichedToken(UHandle enriched,long maxLen,Boolean headers,short *cmdId,Boolean *neg,long *tStart,long *tStop)
{
UPtr start,stop,end;
Str63 cmd;
long len = GetHandleSize(enriched);
if (maxLen<len) len = maxLen;
LDRef(enriched);
*tStart = *tStop;
start = *enriched+*tStart;
end = *enriched+len;
// end of text?
if (*tStart>=len) {UL(enriched); return(eofErr);}
// carriage return?
if (*start=='\015')
{
for (stop=start+1;stop<end && *stop=='\015';stop++);
*cmdId = enCR;
}
// other stuff
else
{
for (stop=start+1; stop<end && (headers || *stop!='<'&&*stop!='>') && (*stop!='\015' || headers&&stop<end-1&&IsWhite(stop[1]));stop++);
if (*start=='<')
{
// "<<"
if (*stop=='<' && stop-start==1)
{
stop++;
*cmdId = enDoubleLess;
}
// a <>-delimited-thing
else if (*stop=='>')
{
UPtr tempStop;
for(tempStop=start+1;tempStop<stop&&*tempStop!=' ';tempStop++);
*neg = start[1]=='/';
if (*neg) MakePStr(cmd,start+2,tempStop-start-2);
else MakePStr(cmd,start+1,tempStop-start-1);
stop++;
*cmdId = FindSTRNIndex(EnrichedStrn,cmd);
}
}
// some random text
else
*cmdId = enText;
}
*tStop = stop-*enriched;
UL(enriched);
return(noErr);
}
/**********************************************************************
*
**********************************************************************/
void AddToStyle(EnrichedEnum cmd,Boolean neg,long offset,OffsetAndStyleHandle *styles,uLong valid)
{
OffsetAndStyle current;
short n;
if (!styles || !*styles) return;
n = HandleCount(*styles);
/*
* fill in current style
*/
if (n==0)
{
Zero(current);
current.style.tsSize = FontSize;
}
else
current = (**styles)[n-1];
/*
* modify
*/
if (neg && cmd==enBigger) cmd=enSmaller;
else if (neg && cmd==enSmaller) cmd=enBigger;
switch (cmd)
{
case enBold:
current.validBits |= peBoldValid;
if (neg) current.style.tsFace &= ~bold;
else current.style.tsFace |= bold;
break;
case enItalic:
current.validBits |= peItalicValid;
if (neg) current.style.tsFace &= ~italic;
else current.style.tsFace |= italic;
break;
case enUnderline:
current.validBits |= peUnderlineValid;
if (neg) current.style.tsFace &= ~underline;
else current.style.tsFace |= underline;
break;
case enSmaller:
#ifdef USERELATIVESIZES
if(current.style.tsSize < 0)
{
if(current.style.tsSize == kPETEDefaultSize)
current.style.tsSize = kPETERelativeSizeBase;
--current.style.tsSize;
} else
#endif
current.style.tsSize = IncrementTextSize(current.style.tsSize,-1);
current.validBits |= peSizeValid;
break;
case enBigger:
#ifdef USERELATIVESIZES
if(current.style.tsSize < 0)
{
if(current.style.tsSize == kPETEDefaultSize)
current.style.tsSize = kPETERelativeSizeBase;
++current.style.tsSize;
} else
#endif
current.style.tsSize = IncrementTextSize(current.style.tsSize,1);
current.validBits |= peSizeValid;
break;
case enFixed:
case enCenter:
case enLeft:
case enRight:
return;
break;
}
current.validBits &= valid;
if (n && offset==current.offset) (**styles)[n-1] = current;
else
{
current.offset = offset;
if (PtrPlusHand(&current,(Handle)*styles,sizeof(current)))
ZapHandle(*styles);
}
}
/**********************************************************************
* IncrementTextSize - make a font size smaller or larger
**********************************************************************/
short IncrementTextSize(short size,short increment)
{
short nSizes = HandleCount(StdSizes);
short sizeIndex = FindSizeInc(size);
sizeIndex += increment;
sizeIndex = MIN(nSizes-1,sizeIndex);
sizeIndex = MAX(0,sizeIndex);
return((*StdSizes)[sizeIndex]);
}
/**********************************************************************
* FindSizeInc - find which increment a size is
**********************************************************************/
short FindSizeInc(short size)
{
short nSizes = HandleCount(StdSizes);
short *std;
short sizeIndex;
short tempSize;
tempSize = size;
if (tempSize < 0) tempSize=FontSize;
sizeIndex = 0;
for (std=(*StdSizes);std<(*StdSizes)+nSizes;std++)
if (tempSize>=*std)
{
sizeIndex = std-(*StdSizes);
if (tempSize==*std) break;
}
else break;
#ifdef USERELATIVESIZES
if ((size < 0) && (size != kPETEDefaultSize)) {
sizeIndex += size - kPETERelativeSizeBase;
if(sizeIndex < 0) sizeIndex = 0;
if(sizeIndex >= nSizes) sizeIndex = nSizes - 1;
}
#endif
return(sizeIndex);
}
/**********************************************************************
* SetMessRich - set the rich text flag for a message
**********************************************************************/
Boolean SetMessRich(MessHandle messH)
{
StyleLevelEnum result;
HeadSpec hSpec;
Boolean html = !PrefIsSet(PREF_SEND_ENRICHED_NEW) || MessOptIsSet(messH,OPT_HTML);
if (!CompHeadFind(messH,0,&hSpec))
{
// No body. Not good.
result = false;
}
else
{
result=HasStyles(TheBody,hSpec.value,hSpec.stop,html);
}
if (result)
{
if (html)
{
SetMessOpt(messH,OPT_HTML);
ClearMessFlag(messH,FLAG_RICH);
}
else
{
SetMessFlag(messH,FLAG_RICH);
ClearMessOpt(messH,OPT_HTML);
}
}
else
{
ClearMessFlag(messH,FLAG_RICH);
ClearMessOpt(messH,OPT_HTML);
}
if (result==hasOnlyExcerpt && UseFlowOutExcerpt)
SetMessOpt(messH,OPT_JUST_EXCERPT);
else
ClearMessOpt(messH,OPT_JUST_EXCERPT);
return(result);
}
/**********************************************************************
* HasStyles - does an edit record use multiple styles?
**********************************************************************/
StyleLevelEnum HasStyles(PETEHandle pte,long from,long to,Boolean allowGraphics)
{
PETEParaInfo defInfo, curInfo;
PETEStyleEntry defStyle, curStyle;
long para;
long len;
StyleLevelEnum result = hasNoStyle;
short paraDiff;
defInfo.tabHandle = curInfo.tabHandle = nil;
PeteGetStyle(pte,kPETEDefaultStyle,nil,&defStyle);
PETEGetParaInfo(PETE,pte,kPETEDefaultPara,&defInfo);
for (;from<to;from=curStyle.psStartChar+MAX(1,len))
{
if (PETEGetParaIndex(PETE,pte,from,&para)) return(hasNoStyle);
if (PETEGetParaInfo(PETE,pte,para,&curInfo)) return(hasNoStyle);
if (paraDiff = PeteParaInfoDiff(&defInfo,&curInfo))
{
if (paraDiff==peQuoteLevelValid) result = hasOnlyExcerpt;
else return(hasTonsOCrap);
}
if (PeteGetStyleLo(pte,from,&len,allowGraphics,&curStyle)) return(hasNoStyle);
if (curStyle.psGraphic)
{
if (!IsEmoticonStyle(&curStyle.psStyle.graphicStyle)) return(hasTonsOCrap); // graphics count
}
else
{
if (curStyle.psStyle.textStyle.tsLabel&pLinkLabel) return(hasTonsOCrap); // links count
#ifdef WINTERTREE
if (HasFeature (featureSpellChecking) && (curStyle.psStyle.textStyle.tsLabel&pSpellLabel)==pSpellLabel) continue; //skip spelling
#endif //WINTERTREE
if (PeteTextStyleDiff(&defStyle.psStyle.textStyle,&curStyle.psStyle.textStyle)) return(hasTonsOCrap);
}
}
return(result);
}
/************************************************************************
* Style2String - encode a style in a string
************************************************************************/
PStr Style2String(ESSPtr ess,PStr string)
{
Str63 s;
*string = 0;
/*
* style name
*/
// style name
PCopy(string,ess->styleName); PCatC(string,',');
/*
* text info
*/
// valid bits
PXCat(string,ess->textValid); PCatC(string,',');
// font name
if ((ess->textValid & peFontValid) && ess->textStyle.tsFont!=kPETEDefaultFont)
{
GetFontName(ess->textStyle.tsFont,s);
PCat(string,s);
}
PCatC(string,',');
// font size
if (ess->textValid & peFontValid)
PLCat(string,FindSizeInc(ess->textStyle.tsSize)-FindSizeInc(FontSize));
PCatC(string,',');
// style
if (ess->textValid & peFaceValid)
PXWCat(string,ess->textStyle.tsFace);
PCatC(string,',');
// color
if (ess->textValid & peColorValid)
{
PXWCat(string,ess->textStyle.tsColor.red);
PCatC(string,',');
PXWCat(string,ess->textStyle.tsColor.green);
PCatC(string,',');
PXWCat(string,ess->textStyle.tsColor.blue);
PCatC(string,',');
}
else
{
PCatC(string,',');
PCatC(string,',');
PCatC(string,',');
}
// language
if (ess->textValid & peLangValid) PLCat(string,ess->textStyle.tsLang);
PCatC(string,',');
// lock
if (ess->textValid & peLockValid) PLCat(string,ess->textStyle.tsLang);
PCatC(string,',');
// do not save label
/*
* paragraph info
*/
// valid bits
PXWCat(string,ess->paraValid); PCatC(string,',');
// margins
if (ess->paraValid & peStartMarginValid) PLCat(string,ess->paraInfo.startMargin);
PCatC(string,',');
if (ess->paraValid & peEndMarginValid) PLCat(string,ess->paraInfo.endMargin);
PCatC(string,',');
if (ess->paraValid & peIndentValid) PLCat(string,ess->paraInfo.indent);
PCatC(string,',');
// direction
if (ess->paraValid & peDirectionValid) PLCat(string,ess->paraInfo.direction);
PCatC(string,',');
// justification
if (ess->paraValid & peJustificationValid) PLCat(string,ess->paraInfo.justification);
PCatC(string,',');
// quote level
if (ess->paraValid & peQuoteLevelValid) PLCat(string,ess->paraInfo.quoteLevel);
PCatC(string,',');
// flags
if (ess->paraValid & peFlagsValid) PXWCat(string,ess->paraInfo.paraFlags);
PCatC(string,',');
/*
* flags for us
*/
PLCat(string,ess->wholePara);
PCatC(string,',');
PLCat(string,ess->formatBar);
PCatC(string,',');
return string;
}
/************************************************************************
* String2Style - decode a style from a string
************************************************************************/
OSErr String2Style(ESSPtr ess,PStr string)
{
return unimpErr;
}