Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Sources/MSAETextUtils.c
// MSAETextUtils.c |
// |
// Original version by Jon Lansdell and Nigel Humphreys. |
// 4.0 and 3.1 updates by Greg Sutton. |
// ©Apple Computer Inc 1996, all rights reserved. |
/* |
14-Nov-95 : GS : Removed reliance on compiler setting local variable to zero |
in GetInsertDescFromInsertHere(). |
*/ |
#include "MSAETextUtils.h" |
#include "MSAEUtils.h" |
#include "MSWindow.h" // for DPtrFromWindowPtr() |
#include "MSAERecording.h" |
#include <AEPackObject.h> |
// ---------------------------------------------------------------------------------- |
// Name: PutStyledTextFromDescIntoTEHandle |
// Purpose: Takes the text in an AEDesc containing typeIntlText and puts it in |
// a styled text edit record at the current insertion point. |
// Looks for typeIntlText, typeStyledText, typeChar in that order. |
// ---------------------------------------------------------------------------------- |
OSErr PutStyledTextFromDescIntoTEHandle(const AEDesc *sourceTextDesc, TEHandle theHTE) |
{ |
AEDesc styledTextDesc = { typeNull, NULL }; |
AEDesc textStyleDesc = { typeNull, NULL }; |
AEDesc rawTextDesc = { typeNull, NULL }; |
OSErr myErr; |
// Coerce to an AERecord and then extract the parts of the |
// styled text - works for typeIntlText, typeStyledText |
myErr = AECoerceDesc(sourceTextDesc, typeAERecord, &styledTextDesc); |
if (noErr == myErr) |
{ |
myErr = AEGetKeyDesc(&styledTextDesc, keyAEText, |
typeChar, &rawTextDesc); |
myErr = AEGetKeyDesc(&styledTextDesc, keyAEStyles, |
typeScrapStyles, &textStyleDesc); |
} |
else |
{ |
myErr = AECoerceDesc(sourceTextDesc, typeChar, &rawTextDesc); |
textStyleDesc.dataHandle = NULL; // so that TEStylInsert acts like TEInsert |
} |
HLock((Handle)rawTextDesc.dataHandle); |
TEDelete(theHTE); // Insert over current selection |
TEStylInsert((const void *) (*rawTextDesc.dataHandle), |
GetHandleSize(rawTextDesc.dataHandle), |
(StScrpHandle) textStyleDesc.dataHandle, |
theHTE); |
HUnlock((Handle)rawTextDesc.dataHandle); |
(void)AEDisposeDesc(&textStyleDesc); |
(void)AEDisposeDesc(&rawTextDesc); |
(void)AEDisposeDesc(&styledTextDesc); |
return(myErr); |
} |
TEHandle TEHandleFromWindow(WindowPtr theWindow) |
{ |
DPtr docPtr; |
TEHandle result = NULL; |
if (! theWindow) |
return(NULL); |
docPtr = DPtrFromWindowPtr(theWindow); |
if (docPtr) |
result = docPtr->theText; |
return(result); |
} |
TEHandle TEHandleFromTextToken(TextToken* aToken) |
{ |
if (! aToken) |
return(NULL); |
return(TEHandleFromWindow(aToken->tokenWindow)); |
} |
OSErr GetInsertDescFromInsertHere(AEDesc* insertHereDesc, AEDesc* insertDesc, DescType* insertType) |
{ |
AEDesc insertRec = {typeNull, NULL}, |
objectSpec = {typeNull, NULL}; |
DescType returnedType; |
Size actualSize; |
OSErr err = noErr; |
switch (insertHereDesc->descriptorType) |
{ |
case typeInsertionLoc: |
err = AECoerceDesc(insertHereDesc, typeAERecord, &insertRec); |
if (noErr != err) goto done; |
err = AEGetKeyPtr(&insertRec, keyAEPosition, typeEnumeration, &returnedType, |
(Ptr)insertType, sizeof(insertType), &actualSize); |
if (noErr != err) goto done; |
err = AEGetKeyDesc(&insertRec, keyAEObject, typeWildCard, &objectSpec); |
if (objectSpec.descriptorType != typeNull) |
{ |
err = AEResolve(&objectSpec, kAEIDoMinimum, insertDesc); |
if (err != noErr) goto done; |
} |
break; |
case typeObjectSpecifier: |
err = AEResolve(insertHereDesc, kAEIDoMinimum, insertDesc); |
if (noErr != err) goto done; |
*insertType = insertDesc->descriptorType; |
break; |
case typeNull: // No insertion location given |
*insertType = typeNull; |
break; |
case typeType: |
*insertType = *(DescType *)*insertHereDesc->dataHandle; |
break; |
default: // Just copy the descriptor |
err = AEDuplicateDesc(insertHereDesc, insertDesc); |
if (noErr != err) goto done; |
*insertType = insertDesc->descriptorType; |
} |
done: |
if (insertRec.dataHandle) |
AEDisposeDesc(&insertRec); |
if (objectSpec.dataHandle) |
AEDisposeDesc(&objectSpec); |
return(err); |
} |
// This routine returns an enumerated type describing the relative position |
// of one TextToken to another. |
TokenWithinType TokenWithinToken(TextToken* container, TextToken* token, short* numPartial) |
{ |
TokenWithinType result; |
if (token->tokenOffset + token->tokenLength < container->tokenOffset) |
result = kTokenBefore; |
else if (container->tokenOffset + container->tokenLength < token->tokenOffset) |
result = kTokenAfter; |
else if (token->tokenOffset >= container->tokenOffset |
&& token->tokenOffset + token->tokenLength <= container->tokenOffset + container->tokenLength) |
result = kTokenWithin; |
else if (token->tokenOffset < container->tokenOffset) |
{ |
result = kTokenPartialBefore; |
if (numPartial) |
*numPartial = token->tokenOffset + token->tokenLength - container->tokenOffset; |
} |
else |
{ |
result = kTokenPartialAfter; |
if (numPartial) |
*numPartial = container->tokenOffset + container->tokenLength - token->tokenOffset; |
} |
return(result); |
} |
OSErr TextTokenFromDocumentToken(WindowToken* theWindowToken, TextToken* theTextToken) |
{ |
DPtr docPtr; |
docPtr = DPtrFromWindowPtr(theWindowToken->tokenWindow); |
if (! docPtr) |
return(errAENoSuchObject); |
// Create our text token |
theTextToken->tokenWindow = theWindowToken->tokenWindow; |
theTextToken->tokenOffset = 1; // Start at 1 |
theTextToken->tokenLength = (**docPtr->theText).teLength; // through whole length |
return(noErr); |
} |
OSErr TextTokenFromDocumentDesc(AEDesc* windowDesc, TextToken* theToken) |
{ |
AEDesc aDesc = {typeNull, NULL}; |
WindowToken aWindowToken; |
Size actualSize; |
OSErr err; |
err = AECoerceDesc(windowDesc, typeMyDocument, &aDesc); |
if (noErr != err) goto done; |
GetRawDataFromDescriptor(&aDesc, (Ptr)&aWindowToken, |
sizeof(aWindowToken), &actualSize); |
err = TextTokenFromDocumentToken(&aWindowToken, theToken); |
done: |
(void)AEDisposeDesc(&aDesc); |
return(err); |
} |
OSErr TextDescFromDocumentToken(WindowToken* theWindowToken, AEDesc* textDesc) |
{ |
TextToken aToken; |
OSErr err; |
err = TextTokenFromDocumentToken(theWindowToken, &aToken); |
if (noErr != err) goto done; |
err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc); |
done: |
return(err); |
} |
OSErr TextDescFromDocumentDesc(AEDesc* windowDesc, AEDesc* textDesc) |
{ |
TextToken aToken; |
OSErr err; |
err = TextTokenFromDocumentDesc(windowDesc, &aToken); |
if (noErr != err) goto done; |
err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc); |
done: |
return(err); |
} |
void MoveToNonSpace(short *start, short limit, charsHandle myChars) |
// Treats space, comma, full stop, ; and : as space chars |
{ |
short x; |
while (*start <= limit) { |
x = (**myChars)[*start]; |
if (IsWhiteSpace(x)) |
(*start) +=1; |
else |
return; |
} |
} |
void MoveToSpace(short *start, short limit, charsHandle myChars) |
// Treats space,comma, full stop, ; and : as space chars |
{ |
short x; |
while (*start <= limit) |
{ |
x = (**myChars)[*start]; |
if (! IsWhiteSpace(x)) |
(*start)++; |
else |
return; |
} |
} |
void MoveToEndOfParagraph(short *start, short limit, charsHandle myChars) |
// Treats CR as end of paragraph |
{ |
short x; |
while (*start <= limit) |
{ |
x = (**myChars)[*start]; |
if (! IsParagraphDelimiter(x)) // had x != CR |
(*start)++; |
else |
return; |
} |
} |
// This routine counts the given elementType between startAt and |
OSErr CountTextElements(TEHandle inTextHandle, short startAt, |
short forHowManyChars, DescType elementType, short* result) |
{ |
charsHandle theChars; |
short limit, |
start; |
OSErr err = noErr; |
switch (elementType) |
{ |
case cInsertionPoint: // Always one more insertion location than characters |
*result = forHowManyChars + 1; |
break; |
case cChar: // Easy |
*result = forHowManyChars; |
break; |
case cText: |
*result = 1; |
break; |
case cWord: // Cycle through - counting |
case cParagraph: |
theChars = (charsHandle)(**inTextHandle).hText; |
start = startAt - 1; // Convert to zero based |
limit = start + forHowManyChars - 1; // when passed one based |
*result = 0; |
MoveToNonSpace(&start, limit, theChars); |
while (start <= limit) |
{ |
(*result)++; |
switch (elementType) |
{ |
case cWord: |
MoveToSpace(&start, limit, theChars); |
break; |
case cParagraph: |
MoveToEndOfParagraph(&start, limit, theChars); |
break; |
} |
MoveToNonSpace(&start, limit, theChars); |
} |
break; |
default: |
*result = -1; |
err = errAEBadKeyForm; |
} |
return(err); |
} // CountTextElements |
OSErr GetDescOfNthTextElement(short index, DescType elementType, |
TextToken* containerToken, AEDesc* result) |
{ |
DPtr docPtr; |
TextToken theToken; |
short start, |
maxChars, |
elementCount, |
limit, |
elementStart; |
charsHandle theChars; |
OSErr err; |
if (! containerToken) |
return(errAEEmptyListContainer); |
docPtr = DPtrFromWindowPtr(containerToken->tokenWindow); |
start = containerToken->tokenOffset - 1; // Zero based |
maxChars = containerToken->tokenLength; |
err = CountTextElements(docPtr->theText, containerToken->tokenOffset, |
maxChars, elementType, &elementCount); |
if (noErr != err) return(err); |
if (index < 0) // Change a negative index to positive |
index = elementCount + index + 1; |
if (index > elementCount) // Got given an index out of range |
return(errAEIllegalIndex); |
// Set the window that the token relates to |
theToken.tokenWindow = containerToken->tokenWindow; |
switch (elementType) |
{ |
case cInsertionPoint: |
theToken.tokenOffset = start + index - 1; |
theToken.tokenLength = 0; |
break; |
case cChar: // Easy - just the start point + the index |
theToken.tokenOffset = start + index; |
theToken.tokenLength = 1; |
break; |
case cText: |
theToken.tokenOffset = start + index; |
theToken.tokenLength = maxChars; |
break; |
case cWord: |
case cParagraph: |
theChars = (charsHandle)(**(docPtr->theText)).hText; |
limit = start + maxChars - 1; |
MoveToNonSpace(&start, limit, theChars); |
while ((start <= limit) && (index > 0)) |
{ |
index--; |
elementStart = start; |
switch (elementType) |
{ |
case cWord: |
MoveToSpace(&start, limit, theChars); |
break; |
case cParagraph: |
MoveToEndOfParagraph(&start, limit, theChars); |
break; |
} |
theToken.tokenLength = start - elementStart; |
MoveToNonSpace(&start, limit, theChars); |
} |
theToken.tokenOffset = elementStart + 1; // Convert to one based |
break; |
} |
err = AECreateDesc(typeMyText, (Ptr)&theToken, sizeof(theToken), result); |
return(err); |
} |
char GetTEHChar(TEHandle aTEH, short offset) |
{ |
char result; |
offset--; // This is now 0 based |
if (offset < 0 || offset >= (*aTEH)->teLength) |
return('\0'); |
result = *(char *)((*(**aTEH).hText) + offset); |
return(result); |
} |
Boolean IsAtStart(TextToken* theToken) |
{ |
Boolean result; |
// Is at start if offset is at 1 |
result = (theToken->tokenOffset == 1); |
return(result); |
} |
Boolean IsAtEnd(TextToken* theToken) |
{ |
TEHandle aTEH; |
Boolean result; |
aTEH = TEHandleFromTextToken(theToken); |
// Does it go to the end? |
result = (theToken->tokenOffset + theToken->tokenLength >= (**aTEH).teLength); |
return(result); |
} |
Boolean IsWhiteSpace(short aChar) |
{ |
Boolean result; |
result = (aChar == ' ' || aChar == ',' || aChar == '.' |
|| aChar == ':' || aChar == LF || aChar == CR); |
return(result); |
} |
Boolean IsParagraphDelimiter(short aChar) |
{ |
Boolean result; |
result = (aChar == CR); |
return(result); |
} |
Boolean IsContentsToken(TextToken* theToken) |
{ |
return(IsAtStart(theToken) && IsAtEnd(theToken)); |
} |
Boolean IsParagraphToken(TextToken* theToken, short* start, short* end) |
{ |
TEHandle aTEH; |
OSErr err; |
short number; |
Boolean fStart, |
fEnd, |
result; |
aTEH = TEHandleFromTextToken(theToken); |
fStart = IsAtStart(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset - 1)); |
fEnd = IsAtEnd(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength)); |
if (fStart && fEnd) |
{ |
// need to do a count of the paragraphs |
err = CountTextElements(aTEH, theToken->tokenOffset, |
theToken->tokenLength, cParagraph, &number); |
// count text elements before it i.e. offset == 0 limit == theToken->tokenOffset |
if (IsAtStart(theToken)) |
*start = 1; |
else |
{ // From beginning to charracter before start of paragraph |
err = CountTextElements(aTEH, 1,theToken->tokenOffset - 1, cParagraph, start); |
(*start)++; |
} |
*end = *start + number - 1; |
result = true; |
} |
else |
result = false; |
return(result); |
} |
Boolean IsWordToken(TextToken* theToken, short* start, short* end) |
{ |
TEHandle aTEH; |
OSErr err; |
short number; |
Boolean fStart, |
fEnd, |
result; |
aTEH = TEHandleFromTextToken(theToken); |
fStart = IsAtStart(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset - 1)); |
fEnd = IsAtEnd(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength)); |
if (fStart && fEnd) |
{ |
// need to do a count of the words |
err = CountTextElements(aTEH, theToken->tokenOffset, |
theToken->tokenLength, cWord, &number); |
// count text elements before it i.e. offset == 0 limit == theToken->tokenOffset |
if (IsAtStart(theToken)) |
*start = 1; |
else |
{ // From beginning to charracter before start of word |
err = CountTextElements(aTEH, 1, theToken->tokenOffset - 1, cWord, start); |
(*start)++; |
} |
*end = *start + number - 1; |
result = true; |
} |
else |
result = false; |
return(result); |
} |
DescType GetTextTokenType(TextToken* theToken, short* start, short* end) |
{ |
DescType result; |
*start = *end = -1; // Just set to the same value |
if (! theToken->tokenLength) |
{ |
result = cInsertionPoint; |
} |
else if (IsContentsToken(theToken)) |
{ |
result = pContents; |
} |
else if (IsParagraphToken(theToken, start, end)) |
{ |
result = cParagraph; |
} |
else if (IsWordToken(theToken, start, end)) |
{ |
result = cWord; |
} |
else |
{ |
result = cChar; |
*start = theToken->tokenOffset; |
*end = theToken->tokenOffset + theToken->tokenLength - 1; |
} |
return(result); |
} |
OSErr MakeContentsSpecifier(TextToken* theToken, AEDesc* result) |
{ |
AEDesc docSpec = {typeNull, NULL}, |
contentsDesc = {typeNull, NULL}; |
DescType propertyID; |
OSErr err; |
err = MakeDocumentObj(theToken->tokenWindow, &docSpec); |
if (noErr != err) goto done; |
propertyID = pContents; |
err = AECreateDesc(typeType, (Ptr)&propertyID, sizeof(DescType), &contentsDesc); |
if (err != noErr) goto done; |
err = CreateObjSpecifier(cProperty, &docSpec, formPropertyID, &contentsDesc, false, result); |
done: |
(void)AEDisposeDesc(&docSpec); |
(void)AEDisposeDesc(&contentsDesc); |
return(err); |
} |
OSErr MakeAbsoluteTextSpecifier(WindowPtr theWindow, DescType textType, long index, AEDesc* result) |
{ |
AEDesc docSpec = {typeNull, NULL}, |
absoluteDesc = {typeNull, NULL}; |
OSErr err; |
if (theWindow) |
{ |
err = MakeDocumentObj(theWindow, &docSpec); |
if (noErr != err) goto done; |
} |
// else just use the NULL'ed value |
err = AECreateDesc(typeLongInteger, (Ptr)&index, sizeof(index), &absoluteDesc); |
if (err != noErr) goto done; |
err = CreateObjSpecifier(textType, &docSpec, formAbsolutePosition, |
&absoluteDesc, false, result); |
done: |
(void)AEDisposeDesc(&docSpec); |
(void)AEDisposeDesc(&absoluteDesc); |
return(err); |
} |
OSErr MakeInsertionPointSpecifier(TextToken* theToken, AEDesc* result) |
{ |
AEDesc relativeToSpec = {typeNull, NULL}, |
relativeDesc = {typeNull, NULL}; |
DescType relativeType; |
OSErr err; |
if (IsAtStart(theToken)) // Before contents (whether there are any or not) |
{ |
relativeType = kAEPrevious; |
err = MakeContentsSpecifier(theToken, &relativeToSpec); |
} |
else if (IsAtEnd(theToken)) // After last character |
{ |
relativeType = kAENext; |
//err = MakeContentsSpecifier(theToken, &relativeToSpec); |
err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, -1, &relativeToSpec); |
} |
else // Has a character it can go before |
{ |
relativeType = kAEPrevious; |
err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, theToken->tokenOffset, &relativeToSpec); |
} |
if (noErr != err) goto done; |
err = AECreateDesc(typeEnumerated, &relativeType, sizeof(relativeType), &relativeDesc); |
if (noErr != err) goto done; |
err = CreateObjSpecifier(cInsertionPoint, &relativeToSpec, formRelativePosition, |
&relativeDesc, false, result); |
done: |
(void)AEDisposeDesc(&relativeToSpec); |
(void)AEDisposeDesc(&relativeDesc); |
return(err); |
} |
OSErr GetIndexSpecifier(TextToken* theToken, DescType textType, long index, AEDesc* result) |
{ |
OSErr err; |
switch (textType) |
{ |
case cInsertionPoint: |
err = MakeInsertionPointSpecifier(theToken, result); |
break; |
case pContents: |
err = MakeContentsSpecifier(theToken, result); |
break; |
case cParagraph: |
case cWord: |
case cChar: |
err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, textType, index, result); |
break; |
default: |
err = errAETypeError; |
} |
return(err); |
} |
OSErr GetTextTokenObjectSpecifier(TextToken* theToken, AEDesc* result) |
{ |
AEDesc docSpec = {typeNull, NULL}, |
startSpec = {typeNull, NULL}, |
endSpec = {typeNull, NULL}, |
rangeDesc = {typeNull, NULL}; |
DescType textType; |
short start, |
end; |
OSErr err; |
textType = GetTextTokenType(theToken, &start, &end); |
err = GetIndexSpecifier(theToken, textType, start, &startSpec); |
if (noErr != err) goto done; |
if (start != end) // Sort out rest of range specifier |
{ |
err = GetIndexSpecifier(theToken, textType, end, &endSpec); |
if (noErr != err) goto done; |
err = CreateRangeDescriptor(&startSpec, &endSpec, false, &rangeDesc); |
if (noErr != err) goto done; |
err = MakeDocumentObj(theToken->tokenWindow, &docSpec); |
if (noErr != err) goto done; |
err = CreateObjSpecifier(cText, &docSpec, formRange, &rangeDesc, false, result); |
} |
else |
err = AEDuplicateDesc(&startSpec, result); |
done: |
(void)AEDisposeDesc(&docSpec); |
(void)AEDisposeDesc(&startSpec); |
(void)AEDisposeDesc(&endSpec); |
(void)AEDisposeDesc(&rangeDesc); |
return(err); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14