netscape-revival
lib/mac/VText/V_PowerPlant_Mods/LTextElemAEOM.AEOM.cp
// ===========================================================================
// LTextElemAEOM.cp ©1994 Metrowerks Inc. All rights reserved.
// ===========================================================================
#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif
#include "LTextElemAEOM.h"
#include <AEPowerPlantSuite.h>
#include <AETextSuite.h>
#include <LTextModel.h>
#include <UAppleEventsMgr.h>
#include <UMemoryMgr.h>
#include <LStyleSet.h>
#include <UAEGizmos.h>
#include <LDescTube.h>
#ifndef __AEREGISTRY__
#include <AERegistry.h>
#endif
#ifndef __AEOBJECTS__
#include <AEObjects.h>
#endif
#ifndef __STRING__
#include <string.h>
#endif
#pragma warn_unusedarg off
// ===========================================================================
// AEOM support
Int32 LTextElemAEOM::CountSubModels(DescType inModelID) const
{
switch (inModelID) {
case cParagraph:
case cLine:
case cWord:
case cChar:
return mTextEngine->CountParts(mRange, inModelID);
break;
// case cText: ?
// case cTextFlow: ?
// break;
/*-?
case cStyleSet:
return mTextEngine->CountStyleSets(mRange);
break;
*/
default:
return inherited::CountSubModels(inModelID);
break;
}
}
LModelObject * LTextElemAEOM::GetModelProperty(
DescType inProperty) const
{
LModelObject *theProperty = nil;
switch (inProperty) {
case cParagraph:
case cLine:
case cWord:
case cChar:
{
TextRangeT superRange;
/* 1)
Either of the following would be plausible but, since you can't
record parenthesis easily and subspecs become finer and finer
grained, it makes more sense to use the first.
Ie. return the "absolute" paragraph, line, word or character. */
#if 1
superRange = mTextEngine->GetTotalRange();
#else
superRange = GetSuperRange();
#endif
Assert_(
superRange.Contains(mRange)
|| ( !superRange.Length() &&
!mRange.Length() &&
(superRange.Start() == mRange.Start())
)
);
TextRangeT newRange = mRange;
DescType newType = inProperty;
newType = mTextEngine->FindPart(superRange, &newRange, inProperty);
// Again, because of (1) above, the "correct" super of the property is
// the entire text model (instead of the later).
#if 1
theProperty = mTextModel->MakeNewElem(newRange, newType);
#else
theProperty = mTextModel->MakeNewElem(newRange, newType, super);
#endif
break;
}
case pBeginning:
case pEnding:
case pBefore:
case kAEBefore:
case pAfter:
case kAEAfter:
{
theProperty = GetInsertionElement(inProperty);
break;
}
default:
theProperty = inheritAEOM::GetModelProperty(inProperty);
}
return theProperty;
}
LModelObject * LTextElemAEOM::GetInsertionElement(DescType inInsertPosition) const
{
TextRangeT range;
FindInsertionRange(inInsertPosition, &range);
if (range == mRange) {
return (LModelObject *)this;
} else {
if (range.Length()) {
return mTextModel->MakeNewElem(range, GetModelKind());
} else {
return mTextModel->MakeNewElem(range, cChar);
}
}
}
void LTextElemAEOM::GetSubModelByPosition(
DescType inModelID,
Int32 inPosition,
AEDesc &outToken) const
{
switch (inModelID) {
case cParagraph:
case cLine:
case cWord:
case cChar:
{
TextRangeT range;
LTextElemAEOM *subModel = NULL;
DescType partFoundType; // Maybe unnecessary since submodel is based
// off this rather than mTextLink. But, it
// is safer.
partFoundType = mTextEngine->FindNthPart(mRange, inModelID, inPosition, &range);
subModel = mTextModel->MakeNewElem(range, partFoundType, (LModelObject *)this);
PutInToken(subModel, outToken);
break;
}
/*-?
case cStyleSet:
{
LStyleSet *subModel = mTextEngine->GetStyleSet(mRange, inPosition);
PutInToken(subModel, outToken);
break;
}
*/
default:
inherited::GetSubModelByPosition(inModelID, inPosition, outToken);
break;
}
}
void LTextElemAEOM::GetModelByRelativePosition(
DescType inModelID,
OSType inRelativePosition,
AEDesc &outToken) const
{
switch (inRelativePosition) {
case kAEBeginning: // Doesn't record well
case kAEEnd: // Doesn't record well
case kAEBefore:
case kAEAfter:
case kAEPrevious:
case kAENext:
{
TextRangeT range = mRange;
DescType partType = cChar;
LTextElemAEOM *p;
switch (inRelativePosition) {
case kAEBeginning:
case kAEBefore:
range.Front();
break;
case kAEEnd:
case kAEAfter:
range.Back();
break;
case kAEPrevious:
{
do {
range.Before();
partType = mTextEngine->FindPart(mTextEngine->GetTotalRange(), &range, inModelID);
if (partType == typeNull)
break;
} while (partType != inModelID);
if (partType == typeNull)
Throw_(errAENoSuchObject);
break;
}
case kAENext:
{
do {
range.After();
partType = mTextEngine->FindPart(mTextEngine->GetTotalRange(), &range, inModelID);
if (partType == typeNull)
break;
} while (partType != inModelID);
if (partType == typeNull)
Throw_(errAENoSuchObject);
break;
}
}
p = mTextModel->MakeNewElem(range, partType);
PutInToken(p, outToken);
break;
}
default:
inheritAEOM::GetModelByRelativePosition(inModelID, inRelativePosition, outToken);
break;
}
}
void LTextElemAEOM::GetSubModelByComplexKey(
DescType inModelID,
DescType inKeyForm,
const AEDesc &inKeyData,
AEDesc &outToken) const
{
StAEDescriptor ospec1,
ospec2;
StAEDescriptor token1,
token2;
LTextElemAEOM *submodel = NULL,
*obj1 = NULL,
*obj2 = NULL;
TextRangeT range,
range1,
range2;
DescType rangePartType,
partType1,
partType2;
switch (inKeyForm) {
case formRange:
{
// Load boundary data
// See IM-IAC 6-20 for (AERecord *) coercion information).
LTextElemAEOM *obj1 = NULL,
*obj2 = NULL;
LAESubDesc rangeRec(inKeyData),
start = rangeRec.KeyedItem(keyAERangeStart),
stop = rangeRec.KeyedItem(keyAERangeStop);
obj1 = (LTextElemAEOM *)start.ToModelObject();
// Assert_(member(obj1, LTextElemAEOM));
obj2 = (LTextElemAEOM *)stop.ToModelObject();
// Assert_(member(obj2, LTextElemAEOM));
range1 = obj1->GetRange();
range2 = obj2->GetRange();
partType1 = obj1->GetModelKind();
partType2 = obj2->GetModelKind();
// Make corresponding LTextElemAEOM submodel.
range = TextRangeT(range1.Start(), range2.End());
if (partType1 == partType2)
rangePartType = partType1;
else
rangePartType = cChar;
submodel = mTextModel->MakeNewElem(range, rangePartType, (LModelObject *)this);
PutInToken(submodel, outToken);
break;
}
case formTest:
default:
inheritAEOM::GetSubModelByComplexKey(inModelID, inKeyForm, inKeyData, outToken);
break;
}
}
Int32 LTextElemAEOM::GetPositionOfSubModel(
DescType inModelID,
const LModelObject *inSubModel) const
{
switch (inModelID) {
case cParagraph:
case cLine:
case cWord:
case cChar:
{
const LTextElemAEOM* p = (LTextElemAEOM *)inSubModel;
TextRangeT partRange;
Int32 index1, index2;
// Assert_(member(p, LTextElemAEOM));
partRange = p->GetRange();
mTextEngine->FindSubRangePartIndices(mRange, partRange, inModelID, &index1, &index2);
return index1;
break;
}
default:
return inherited::GetPositionOfSubModel(inModelID, inSubModel);
break;
}
}
void LTextElemAEOM::MakeSpecifier(AEDesc &outSpecifier) const
{
LAEStream stream;
WriteSpecifier(&stream);
stream.Close(&outSpecifier);
}
Boolean LTextElemAEOM::sTerminalSpecWritten = false;
void LTextElemAEOM::WriteSpecifier(LAEStream *outStream) const
{
if (!sTerminalSpecWritten) {
StValueChanger<Boolean> change(sTerminalSpecWritten, true);
if (!mRange.Length()) {
WriteInsertionSpecifierSelf(outStream);
} else {
WriteSpecifierSelf(outStream);
}
} else {
if (this == mTextModel->GetTextLink()) {
WriteSuperSpecifier(outStream);
} else {
WriteSpecifierSelf(outStream);
}
}
}
void LTextElemAEOM::WriteSuperSpecifier(LAEStream *outStream) const
{
if (this == mTextModel->GetTextLink()) {
StAEDescriptor spec;
GetSuperModel()->MakeSpecifier(spec.mDesc);
outStream->WriteDesc(spec.mDesc);
} else {
LTextElemAEOM *super = (LTextElemAEOM *)GetSuperModel();
// Assert_(member(super, LTextElemAEOM));
super->WriteSpecifier(outStream);
}
}
void LTextElemAEOM::WriteSpecifierSelf(LAEStream *outStream) const
{
outStream->OpenRecord(typeObjectSpecifier);
// class
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(GetModelKind());
// container
outStream->WriteKey(keyAEContainer);
WriteSuperSpecifier(outStream);
// key form and key data
{
TextRangeT superRange = GetSuperRange();
DescType partKind = GetModelKind(),
partKindTry2;
Int32 index1,
index2;
partKind = mTextEngine->FindSubRangePartIndices(superRange, mRange, partKind, &index1, &index2);
ThrowIf_(partKind == typeNull);
if (partKind != GetModelKind()) {
partKindTry2 = mTextEngine->FindSubRangePartIndices(superRange, mRange, partKind, &index1, &index2);
Assert_(partKindTry2 == partKind);
}
if (index2 > index1) {
AEDesc currentContainer = {typeCurrentContainer, NULL};
// A range
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formRange);
outStream->WriteKey(keyAEKeyData);
outStream->OpenRecord(typeRangeDescriptor);
outStream->WriteKey(keyAERangeStart);
outStream->OpenRecord(typeObjectSpecifier);
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(partKind);
outStream->WriteKey(keyAEContainer);
outStream->WriteDesc(currentContainer);
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formAbsolutePosition);
outStream->WriteKey(keyAEKeyData);
outStream->WriteDesc(typeLongInteger, &index1, sizeof(index1));
outStream->CloseRecord();
outStream->WriteKey(keyAERangeStop);
outStream->OpenRecord(typeObjectSpecifier);
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(partKind);
outStream->WriteKey(keyAEContainer);
outStream->WriteDesc(currentContainer);
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formAbsolutePosition);
outStream->WriteKey(keyAEKeyData);
outStream->WriteDesc(typeLongInteger, &index2, sizeof(index2));
outStream->CloseRecord();
outStream->CloseRecord();
} else {
// An absolute position
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formAbsolutePosition);
outStream->WriteKey(keyAEKeyData);
outStream->WriteDesc(typeLongInteger, &index1, sizeof(index1));
}
}
outStream->CloseRecord();
}
void LTextElemAEOM::WriteInsertionSpecifierSelf(LAEStream *outStream) const
{
Assert_(mRange.Length() == 0);
outStream->OpenRecord(typeObjectSpecifier);
{
TextRangeT superRange = GetSuperRange();
// An insertion
DescType relType = typeNull;
TextRangeT front = superRange,
back = superRange;
front.Front();
back.Back();
if ((mRange == front) || mRange.IsBefore(superRange))
relType = pBeginning;
else if ((mRange == back) || mRange.IsAfter(superRange))
relType = pEnding;
if (relType != typeNull) {
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(GetModelKind());
outStream->WriteKey(keyAEContainer);
WriteSuperSpecifier(outStream);
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formPropertyID);
outStream->WriteKey(keyAEKeyData);
outStream->WriteTypeDesc(relType);
} else {
DescType partKind = cChar,
partKindTry2;
Int32 index1,
index2;
TextRangeT range(mRange.Start());
partKind = mTextEngine->FindSubRangePartIndices(superRange, range, partKind, &index1, &index2);
ThrowIf_(partKind == typeNull);
if (partKind != GetModelKind()) {
partKindTry2 = mTextEngine->FindSubRangePartIndices(superRange, range, partKind, &index1, &index2);
Assert_(partKindTry2 == partKind);
}
relType = pBeginning;
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(GetModelKind());
outStream->WriteKey(keyAEContainer);
outStream->OpenRecord(typeObjectSpecifier);
outStream->WriteKey(keyAEDesiredClass);
outStream->WriteTypeDesc(cChar);
outStream->WriteKey(keyAEContainer);
WriteSuperSpecifier(outStream);
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formAbsolutePosition);
outStream->WriteKey(keyAEKeyData);
outStream->WriteDesc(typeLongInteger, &index1, sizeof(index1));
outStream->CloseRecord();
outStream->WriteKey(keyAEKeyForm);
outStream->WriteEnumDesc(formPropertyID);
outStream->WriteKey(keyAEKeyData);
outStream->WriteTypeDesc(relType);
}
}
outStream->CloseRecord();
}
void LTextElemAEOM::GetAEProperty(
DescType inProperty,
const AEDesc &inRequestedType,
AEDesc &outPropertyDesc) const
{
LStyleSet *style = NULL;
switch (inProperty) {
case pFont:
case pPointSize:
case pTextColor:
case pTextStyles:
case pScriptTag:
style = mTextEngine->GetStyleSet(mRange);
style->GetAEProperty(inProperty, inRequestedType, outPropertyDesc);
break;
/*
case pUniformStyles:
{
Int32 i;
for (i = 1; i <= mTextEngine->CountStylesSets(mRange); i++) {
// loop through styles
GetRangeAEProperty(mRange, inProperty, inRequestedType, outPropertyDesc);
}
break;
}
*/
// use a getdatasize event?
case pLength:
{
LAEStream stream;
Int32 len = mRange.Length();
stream.WriteDesc(typeLongInteger, &len, sizeof(len));
stream.Close(&outPropertyDesc);
break;
}
case pOffset:
{
LAEStream stream;
Int32 start = mRange.Start();
stream.WriteDesc(typeLongInteger, &start, sizeof(start));
stream.Close(&outPropertyDesc);
break;
}
case pContents:
{
LAESubDesc typeList(inRequestedType);
DescType type;
// set default type
if (mTextEngine->GetAttributes() & textEngineAttr_MultiByte)
type = typeIntlText;
else
type = typeChar;
// look for a recognized specific type in the type list
for (Int16 i = 1; i <= typeList.CountItems(); i++) {
DescType testType = typeList.NthItem(i).ToType();
if ( testType == typeObjectSpecifier
|| testType == typeIntlText
|| testType == typeChar
) {
type = testType;
break;
}
}
LAEStream stream;
switch (type) {
case typeObjectSpecifier:
stream.WriteSpecifier(this);
break;
case typeIntlText:
{
style = mTextEngine->GetStyleSet(mRange);
ScriptCode script = style->GetScript();
LangCode lang = style->GetLanguage();
Handle h = mTextEngine->GetTextHandle();
StHandleLocker lock(h);
stream.OpenDesc(typeIntlText);
stream.WriteData(&script, sizeof(script));
stream.WriteData(&lang, sizeof(lang));
stream.WriteData((void *) (*h + mRange.Start()), mRange.Length());
stream.CloseDesc();
break;
}
case typeChar:
{
Handle h = mTextEngine->GetTextHandle();
StHandleLocker lock(h);
stream.WriteDesc(typeChar, (void *) (*h + mRange.Start()), mRange.Length());
break;
}
default:
Throw_(errAEWrongDataType); // actually a programmer error
break;
}
stream.Close(&outPropertyDesc);
break;
}
default:
inherited::GetAEProperty(inProperty, inRequestedType, outPropertyDesc);
break;
}
}
void LTextElemAEOM::SetAEProperty(
DescType inProperty,
const AEDesc &inValue,
AEDesc& outAEReply)
{
switch (inProperty) {
case pFont:
case pPointSize:
case pScriptTag:
case pTextStyles:
case pUniformStyles:
Assert_(false);
// SetRangeAEProperty(mRange, inProperty, inValue, outAEReply);
break;
case pContents:
Throw_(errAEEventNotHandled);
break;
default:
inherited::SetAEProperty(inProperty, inValue, outAEReply);
break;
}
}
// ===========================================================================
// Implementation
void LTextElemAEOM::GetRangeAEProperty(
const TextRangeT &inRange,
DescType inProperty,
const AEDesc &inRequestedType,
AEDesc &outPropertyDesc) const
{
}
void LTextElemAEOM::SetRangeAEProperty(
const TextRangeT &inRange,
DescType inProperty,
const AEDesc &inValue,
AEDesc& outAEReply) const
{
}
// ===========================================================================
// Verb handling...
void LTextElemAEOM::HandleDelete(
AppleEvent &outAEReply,
AEDesc &outResult)
{
StRecalculator changeMe(mTextEngine);
TextRangeT holeRange(mRange.Start()),
range = mRange;
mTextEngine->TextDelete(range);
// Make the extra deletion "hole" location parameter for the reply.
// This extra ae reply parameter helps PowerPlant with undo. And
// is keyed with keyAEInsertHere.
{
StAEDescriptor holeDesc;
LTextElemAEOM *hole = mTextModel->MakeNewElem(holeRange, cChar);
StSharer lock(hole);
hole->MakeSpecifier(holeDesc.mDesc);
UAEDesc::AddKeyDesc(&outAEReply, keyAEInsertHere, holeDesc.mDesc);
}
}
void LTextElemAEOM::HandleMove(
const AppleEvent &inAppleEvent,
AppleEvent &outAEReply,
AEDesc &outResult)
{
StRecalculator changeMe(mTextEngine);
inherited::HandleMove(inAppleEvent, outAEReply, outResult);
}
LModelObject * LTextElemAEOM::HandleCreateElementEvent(
DescType inElemClass,
DescType inInsertPosition,
LModelObject *inTargetObject,
const AppleEvent &inAppleEvent,
AppleEvent &outAEReply)
{
LTextElemAEOM *result = NULL;
switch (inElemClass) {
case cChar:
case cWord:
case cLine:
case cParagraph:
break;
default:
return inherited::HandleCreateElementEvent(inElemClass, inInsertPosition,
inTargetObject, inAppleEvent, outAEReply);
break;
}
StRecalculator changeMe(mTextEngine);
LAESubDesc ae(inAppleEvent),
props = ae.KeyedItem(keyAEPropData),
data;
// AERegistry indicates the data from KeyAEPropData takes precedence...
if (props.GetType() != typeNull)
data = props.KeyedItem(pContents);
if (data.GetType() == typeNull)
data = ae.KeyedItem(keyAEData);
if (data.GetType() == typeNull)
Throw_(errAEEventFailed);
StAEDescriptor desc; // data
if (data.GetType() == typeAERecord) {
data.ToDesc(&desc.mDesc);
} else {
// Fabricate a "timi" descriptor of just the text...
LAEStream ds;
ds.OpenRecord();
ds.WriteKey(data.GetType());
ds.WriteSubDesc(data);
ds.CloseRecord();
ds.Close(&desc.mDesc);
}
result = (LTextElemAEOM *) inTargetObject->GetInsertionElement(inInsertPosition);
// Assert_(member(result, LTextElemAEOM));
LDescTube tube(desc.mDesc);
result->ReceiveDataFrom(&tube);
return result;
}
void LTextElemAEOM::FindInsertionRange(
DescType inInsertPosition,
TextRangeT *outRange,
Boolean *outInclusive
) const
/*
"This" is the "target" object.
*/
{
TextRangeT range = mRange;
// outRange
switch (inInsertPosition) {
case pBeginning:
range.Front();
break;
case pBefore:
case kAEBefore:
range.Front();
range.SetInsertion(rangeCode_Before);
break;
case pEnding:
range.Back();
break;
case pAfter:
case kAEAfter:
range.Back();
range.SetInsertion(rangeCode_After);
break;
case pSame:
break;
default:
Throw_(errAEBadKeyForm);
break;
}
ThrowIfNULL_(outRange);
*outRange = range;
// outInclusive
if (outInclusive) {
switch (inInsertPosition) {
case pBeginning:
case pEnding:
case pSame:
*outInclusive = true;
break;
default:
*outInclusive = false;
break;
}
}
}
Boolean LTextElemAEOM::CompareToModel(
DescType inComparisonOperator,
LModelObject* inCompareModel
) const
{
if (!IsSubModelOf(mTextModel))
// if (!member(inCompareModel, LTextElemAEOM))
ThrowOSErr_(errAEEventNotHandled);
LTextElemAEOM *bModel = (LTextElemAEOM *)inCompareModel; // unsafe type cast
Int32 alen = mRange.Length(),
blen = bModel->GetRange().Length();
Ptr a = GetTextPtr(),
b = bModel->GetTextPtr();
return CompareSelf(inComparisonOperator, a, alen, b, blen);
}
Boolean LTextElemAEOM::CompareToDescriptor(
DescType inComparisonOperator,
const AEDesc& inCompareDesc
) const
{
LAESubDesc dataSD(inCompareDesc, typeChar);
Int32 alen = mRange.Length(),
blen = dataSD.GetDataLength();
Ptr a = GetTextPtr(),
b = (Ptr) dataSD.GetDataPtr();
return CompareSelf(inComparisonOperator, a, alen, b, blen);
}
Boolean LTextElemAEOM::CompareSelf(
DescType inComparisonOperator,
const Ptr inTextA,
Int32 inLengthA,
const Ptr inTextB,
Int32 inLengthB
) const
{
Int32 len = URange32::Max(inLengthA, inLengthB);
Int16 result;
Boolean rval = false;
switch (inComparisonOperator) {
case kAEGreaterThan:
case kAEGreaterThanEquals:
case kAEEquals:
case kAELessThan:
case kAELessThanEquals:
case kAEBeginsWith:
result = memcmp(inTextA, inTextB, len);
switch (inComparisonOperator) {
case kAEGreaterThan:
rval = result > 0;
break;
case kAEGreaterThanEquals:
rval = result >= 0;
break;
case kAEBeginsWith:
rval = result == 0;
break;
case kAEEquals:
rval = (result == 0) && (inLengthA == inLengthB);
break;
case kAELessThan:
rval = result < 0;
break;
case kAELessThanEquals:
rval = result <= 0;
break;
}
break;
case kAEEndsWith:
if (inLengthA >= inLengthB) {
result = memcmp(inTextA + (inLengthA - inLengthB), inTextB, inLengthB);
rval = result == 0;
}
break;
case kAEContains:
// case kAEGrep:
default:
ThrowOSErr_(errAEEventNotHandled);
break;
}
return rval;
}