netscape-revival
lib/mac/VText/V_PowerPlant_Mods/LTextModel.cp
// ===========================================================================
// LTextModel.cp ©1995 Metrowerks Inc. All rights reserved.
// ===========================================================================
#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif
#include "LTextModel.h"
#include <LTextElemAEOM.h>
#include <LStyleSet.h>
#include <AETextSuite.h>
#include <LListIterator.h>
#include <LAEAction.h>
#include <LTextSelection.h>
#include <PP_Messages.h>
#include <LStream.h>
#include <UAEGizmos.h>
#include <UMemoryMgr.h>
#include <UAppleEventsMgr.h>
#include <AEPowerPlantSuite.h>
#ifndef __ERRORS__
#include <Errors.h>
#endif
#pragma warn_unusedarg off
// ===========================================================================
// Maintenance
LTextModel::LTextModel()
: LSelectionModelAEOM(NULL, cText)
{
mTextEngine = NULL;
mTextLink = NULL;
mTextSelection = NULL;
}
/*-
LTextModel::LTextModel(LTextEngine *inTextEngine)
: LSelectionModelAEOM(NULL, cText)
{
mTextEngine = NULL;
mTextLink = NULL;
mTextSelection = NULL;
SetTextEngine(inTextEngine);
Assert_(mTextEngine);
SetDefaultSubModel(new LTextElemAEOM(LTextEngine::sTextStart, cChar, this, this, mTextEngine));
}
*/
LTextModel::~LTextModel()
{
SetTextEngine(NULL);
SetDefaultSubModel(NULL);
}
#ifndef PP_No_ObjectStreaming
void LTextModel::WriteObject(LStream &inStream) const
{
// no simple data
LSelectionModelAEOM::WriteObject(inStream);
inStream << mTextEngine;
}
void LTextModel::ReadObject(LStream &inStream)
{
// no simple data
LSelectionModelAEOM::ReadObject(inStream);
SetTextEngine(ReadStreamable<LTextEngine *>()(inStream));
//- SetDefaultSubModel(MakeNewElem(LTextEngine::sTextStart, cChar, this));
}
#endif
LTextEngine * LTextModel::GetTextEngine(void)
{
return mTextEngine;
}
void LTextModel::SetTextEngine(LTextEngine *inTextEngine)
{
if (mTextEngine != inTextEngine) {
if (mTextEngine)
mTextEngine->AddListener(this);
ReplaceSharable(mTextEngine, inTextEngine, this);
if (mTextEngine)
mTextEngine->AddListener(this);
if (mTextEngine)
SetDefaultSubModel(MakeNewElem(mTextEngine->GetTotalRange(), cChar, this));
else
SetDefaultSubModel(NULL);
}
}
void LTextModel::SetSelection(LSelection *inSelection)
{
inheritAEOM::SetSelection(inSelection);
mTextSelection = dynamic_cast<LTextSelection *>(mSelection);
}
void LTextModel::SetDefaultSubModel(LModelObject *inSubModel)
{
inheritAEOM::SetDefaultSubModel(inSubModel);
if (GetDefaultSubModel()) {
ReplaceSharable(mTextLink, (LTextElemAEOM *)GetDefaultSubModel(), this);
// Assert_(member(mTextLink, LTextElemAEOM));
} else {
ReplaceSharable(mTextLink, (LTextElemAEOM *)NULL, this);
}
}
const TextRangeT& LTextModel::GetRange(void)
{
if (mTextLink)
return mTextLink->GetRange();
else
return LTextEngine::sTextUndefined;
}
// ===========================================================================
// Data tube i/o:
void LTextModel::AddFlavorTypesTo(
const TextRangeT &inRange,
LDataTube *inTube) const
{
if (inRange.Length() >= 0) {
inTube->AddFlavor(typeChar);
if (!inTube->GetOnlyReqdFlavors())
inTube->AddFlavor(typeIntlText);
}
}
Boolean LTextModel::SendFlavorTo(
FlavorType inFlavor,
const TextRangeT &inRange,
LDataTube *inTube) const
{
TextRangeT range = inRange;
Boolean rval = false;
range.Crop(mTextEngine->GetTotalRange());
switch (inFlavor) {
case typeChar:
{
Handle h = mTextEngine->GetTextHandle();
StHandleLocker lock(h);
inTube->SetFlavorData(inFlavor, *h + range.Start(), range.Length());
rval = true;
break;
}
case typeIntlText:
{
LStream &stream = inTube->SetFlavorStreamOpen(typeIntlText);
LStyleSet *style = mTextEngine->GetStyleSet(range);
ScriptCode script = style->GetScript();
LangCode lang = style->GetLanguage();
Handle h = mTextEngine->GetTextHandle();
StHandleLocker lock(h);
stream << script;
stream << lang;
stream.WriteBlock(*h + range.Start(), range.Length());
inTube->SetFlavorStreamClose();
break;
}
}
return rval;
}
FlavorType LTextModel::PickFlavorFrom(
const TextRangeT &inRange,
const LDataTube *inTube) const
/*
typeIntlText currently ignored
*/
{
Assert_(inTube);
FlavorType rval = typeNull,
flavor;
Int32 n = inTube->CountItems();
do {
if (inTube->FlavorExists(typeChar)) {
rval = typeChar;
break;
}
for (Int32 i = 1; i <= n; i++) {
LDataTube subTube = inTube->NthItem(i);
flavor = PickFlavorFrom(inRange, &subTube);
if (flavor == typeNull) {
rval = flavor;
break;
}
if (rval == typeNull) {
rval = flavor;
} else if (rval != flavor) {
rval = typeWildCard;
}
}
if (rval != typeNull) rval = typeWildCard;
} while (false);
return rval;
}
/*
FlavorType LTextModel::PickFlavorFrom(
const TextRangeT &inRange,
const LDataTube *inTube)
{
Assert_(inTube);
FlavorType rval = typeNull,
flavor;
Int32 n = inTube->CountItems();
do {
if (inTube->FlavorExists(typeWildCard)) {
rval = PickFlavorSelf(inTube);
if (rval == typeNull)
break;
}
for (i = 1; i <= n; i++) {
LDataTube subTube = inTube->NthItem(i);
flavor = PickFlavorFrom(inRange, &subTube)
if (flavor == typeNull) {
rval = flavor;
break;
}
if (rval == typeNull) {
rval = flavor;
} else if (rval != flavor) {
rval = typeWildCard;
}
}
} while (false);
}
FlavorType LTextModel::PickFlavorFromSelf(...)
{
if (inTube->FlavorExists(typeChar))
return typeChar;
else
return typeNull;
}
*/
void LTextModel::ReceiveDataFrom(
const TextRangeT &inRange,
LDataTube *inTube)
/*
Script and language code in typeIntlText currently ignored
*/
{
TextRangeT range = inRange;
Size size;
range.Crop(mTextEngine->GetTotalRange());
if (inTube->FlavorExists(typeChar)) {
size = inTube->GetFlavorSize(typeChar);
StPointerBlock data(size);
inTube->GetFlavorData(typeChar, data.mPtr);
mTextEngine->TextReplaceByPtr(range, data.mPtr, size);
range = range.Start() + size;
} else if (inTube->FlavorExists(typeIntlText)) {
LStream &stream = inTube->GetFlavorStreamOpen(typeIntlText);
ScriptCode script;
LangCode lang;
Int32 size = inTube->GetFlavorSize(typeIntlText) - (sizeof(script) + sizeof(lang));
ThrowIf_(size < 0);
StPointerBlock data(size);
stream >> script;
stream >> lang;
stream.ReadBlock(data.mPtr, size);
mTextEngine->TextReplaceByPtr(range, data.mPtr, size);
inTube->GetFlavorStreamClose();
} else {
// Items...
Int32 n = inTube->CountItems();
for (Int32 i = 1; i <= n; i++) {
LDataTube subTube = inTube->NthItem(i);
TextRangeT preTotRange = mTextEngine->GetTotalRange(),
newTotRange;
ReceiveDataFrom(range, &subTube);
newTotRange = mTextEngine->GetTotalRange();
range = range.Start() + (newTotRange.Length() - (preTotRange.Length() - range.Length()));
}
}
}
LTextElemAEOM * LTextModel::MakeNewElem(
const TextRangeT &inRange,
DescType inKind,
const LModelObject *inSuperModel) const
/*
Call this to make text "elems."
You will VERY rarely need to override this method. And, you will only rarely
need to override MakeNewElemSelf.
*/
{
LTextElemAEOM *rval = NULL;
if (inSuperModel) {
// Do explicitly what was indicated
const LTextElemAEOM *super = dynamic_cast<const LTextElemAEOM *>(inSuperModel);
TextRangeT range = inRange;
if (super)
range.Crop(super->GetRange());
rval = MakeNewElemSelf(range, inKind, inSuperModel);
if (super)
Assert_(super->GetRange().Contains(rval->GetRange()));
} else {
// Base off the head model
rval = MakeNewElem(inRange, inKind, mTextLink);
}
return rval;
}
LTextElemAEOM * LTextModel::MakeNewElemSelf(
const TextRangeT &inRange,
DescType inKind,
const LModelObject *inSuperModel
) const
/*
Override this to have the text model instantiate elems of a more specialized
LTextElemAEOM subclass.
*/
{
return new LTextElemAEOM(inRange, inKind, (LModelObject *)inSuperModel, (LTextModel *)this, mTextEngine);
}
LStyleSet * LTextModel::StyleSetForScript(
const TextRangeT &inRange,
ScriptCode inScript)
{
return NULL;
}
void LTextModel::FixLink(void) const
{
if (mTextEngine && mTextLink) {
if (mTextLink->GetRange() != mTextEngine->GetTotalRange()) {
mTextLink->SetRange(mTextEngine->GetTotalRange());
}
}
}
void LTextModel::ListenToMessage(MessageT inMessage, void *ioParam)
{
switch (inMessage) {
case msg_TextFluxed:
{
TextFluxRecordT *p = (TextFluxRecordT *)ioParam;
ThrowIfNULL_(mTextLink);
mTextLink->AdjustRanges(p->range, p->lengthDelta); // true is new 950505
FixLink(); // precautionary test
break;
}
case msg_ModelChanged:
FixLink(); // precautionary test
break;
}
}
// ===========================================================================
// Implementation
#ifdef INCLUDE_TX
#include <LTXStyleSet.h>
#endif
LModelObject* LTextModel::HandleCreateElementEvent(
DescType inElemClass,
DescType inInsertPosition,
LModelObject *inTargetObject,
const AppleEvent &inAppleEvent,
AppleEvent &outAEReply)
{
LModelObject *rval = NULL;
switch(inElemClass) {
#ifdef INCLUDE_TX
case cStyleSet:
{
StAEDescriptor properties;
Int32 i,
count;
StAEDescriptor bogusReply,
propDesc;
AEKeyword keyword;
LStyleSet *newStyleSet;
newStyleSet = new LTXStyleSet();
newStyleSet->SetSuperModel(this);
properties.GetOptionalParamDesc(inAppleEvent, keyAEPropData, typeAERecord);
if (properties.mDesc.descriptorType != typeNull) {
LAESubDesc aProperty,
theProps(properties);
count = theProps.CountItems();
for (i = 1; i <= count; i++) {
aProperty = theProps.NthItem(i, &keyword);
AEDisposeDesc(propDesc);
aProperty.ToDesc(propDesc);
newStyleSet->SetAEProperty(keyword, propDesc, bogusReply);
}
}
rval = newStyleSet;
break;
}
#endif
default:
rval = inheritAEOM::HandleCreateElementEvent(inElemClass, inInsertPosition,
mTextLink, inAppleEvent, outAEReply);
break;
}
return rval;
}
LModelObject * LTextModel::GetModelProperty(
DescType inProperty) const
{
LModelObject *theProperty = nil;
switch (inProperty) {
case pSelection:
{
if (mSelection) {
if (mSelection->ListCount() != 1) {
Assert_(false);
theProperty = inheritAEOM::GetModelProperty(inProperty);
break;
}
LTextElemAEOM *p = (LTextElemAEOM *) mSelection->ListNthItem(1);
// Assert_(member(p, LTextElemAEOM());
theProperty = p;
break;
} else {
// Fall through
}
}
default:
theProperty = inheritAEOM::GetModelProperty(inProperty);
}
return theProperty;
}
void LTextModel::GetAEProperty(
DescType inProperty,
const AEDesc &inRequestedType,
AEDesc &outPropertyDesc) const
{
switch (inProperty) {
case pPPTextAEOMVersion:
{
OSErr err;
Int32 intValue;
intValue = PPTEXTAEOMVERSION;
err = AECreateDesc(typeLongInteger, (Ptr) &intValue, sizeof(intValue), &outPropertyDesc);
ThrowIfOSErr_(err);
break;
}
case pUpdateLevel:
{
Int32 intValue = 0;
OSErr err;
if (mTextEngine) {
intValue = mTextEngine->GetNestingLevel();
}
err = AECreateDesc(typeLongInteger, (Ptr) &intValue, sizeof(intValue), &outPropertyDesc);
ThrowIfOSErr_(err);
break;
}
default:
inheritAEOM::GetAEProperty(inProperty, inRequestedType, outPropertyDesc);
break;
}
}
void LTextModel::SetAEProperty(
DescType inProperty,
const AEDesc &inValue,
AEDesc& outAEReply)
{
switch (inProperty) {
case pUpdateLevel:
{
LAESubDesc valSD(inValue);
Int32 inValue = valSD.ToInt32(),
value;
OSErr err = noErr;
if (mTextEngine) {
value = mTextEngine->GetNestingLevel();
if (value +1 == inValue) {
// increment
mTextEngine->SetNestingLevel(value +1);
mTextEngine->NestedUpdateIn();
} else if (value -1 == inValue) {
// decrement
Try_ {
mTextEngine->NestedUpdateOut();
} Catch_(inErr) {
err = inErr;
} EndCatch_;
mTextEngine->SetNestingLevel(value -1);
Assert_(mTextEngine->GetNestingLevel() >= 0);
ThrowIfOSErr_(err);
} else {
Throw_(paramErr); // should read "you can only increment or decrement the nesting level"
}
}
break;
}
default:
inheritAEOM::SetAEProperty(inProperty, inValue, outAEReply);
break;
}
}
LModelObject * LTextModel::GetInsertionTarget(DescType inInsertPosition) const
{
return inheritAEOM::GetInsertionTarget(inInsertPosition);
}