netscape-revival
lib/mac/VText/V_PowerPlant_Mods/LAETypingAction.cp
// ===========================================================================
// LAETypingAction.cp ©1995 Metrowerks Inc. All rights reserved.
// ===========================================================================
#ifdef PowerPlant_PCH
#include PowerPlant_PCH
#endif
#include "LAETypingAction.h"
#include <UAppleEventsMgr.h>
#include <LTextEngine.h>
#include <LTextSelection.h>
#include <LTextModel.h>
#include <LTextElemAEOM.h>
#include <UMemoryMgr.h>
#include <PP_Resources.h>
#include <LDynamicArray.h>
#include <PP_KeyCodes.h>
#ifndef __AEREGISTRY__
#include <AERegistry.h>
#endif
#ifndef __AEOBJECTS__
#include <AEObjects.h>
#endif
#pragma warn_unusedarg off
LAETypingAction::LAETypingAction()
{
Assert_(false); // Parameters required
}
LAETypingAction::LAETypingAction(
LTextEngine *inTextEngine
)
: LAEAction(STRx_RedoEdit, str_Typing)
{
mTextEngine = inTextEngine;
ThrowIfNULL_(mTextEngine);
mAccumulator = mTextEngine;
mTextSelection = NULL;
mFinalized = false;
mLastRange = LTextEngine::sTextUndefined;
mOldRange = LTextEngine::sTextUndefined;
mOldRangeOrig = LTextEngine::sTextUndefined;
SetPreRecordSelection(false);
SetRecordOnlyFinalState(true);
}
LAETypingAction::~LAETypingAction()
{
}
void LAETypingAction::SetSelection(
LSelection *inSelection,
DescType inRedoSelectionModifier,
DescType inUndoSelectionModifier)
{
inherited::SetSelection(inSelection, inRedoSelectionModifier, inUndoSelectionModifier);
if (mSelection)
// if (mSelection && member(mSelection, LTextSelection))
mTextSelection = (LTextSelection *) mSelection;
else
mTextSelection = NULL;
}
void LAETypingAction::FinishCreateSelf(void)
{
ThrowIfNULL_(mTextEngine);
ThrowIfNULL_(mTextSelection);
mTextSelection->SetTypingAction(this);
if (mTextSelection->ListCount() != 1)
Throw_(paramErr);
mOldRangeOrig = mTextSelection->GetSelectionRange();
mOldTotalRange = mTextEngine->GetTotalRange();
LModelObject *elem = mTextSelection->ListNthItem(1);
elem->MakeSpecifier(mOldOSpec.mDesc);
}
void LAETypingAction::RedoSelf(void)
{
if (mFinalized) {
inherited::RedoSelf();
} else {
if (!mIsDone) {
// Redo typing
mTextEngine->TextReplaceByPtr(mOldRange,
(Ptr) mNewText.GetOffsetPtr(0), mNewText.GetSize());
mIsDone = true;
// Update selection
TextRangeT range(mOldRange.Start() + mNewText.GetSize());
mTextSelection->SetSelectionRange(range);
}
}
}
void LAETypingAction::UndoSelf(void)
{
if (mFinalized) {
inherited::UndoSelf();
} else {
if (mIsDone) {
// remember new text ospec now for use later...
TextRangeT newTextRange(mOldRange);
newTextRange.SetLength(mNewText.GetSize());
LModelObject *elem = mTextSelection->GetTextModel()->MakeNewElem(newTextRange, cChar);
AEDisposeDesc(&mNewOSpec.mDesc);
elem->MakeSpecifier(mNewOSpec.mDesc);
// Undo typing
mTextEngine->TextReplaceByPtr(newTextRange,
(Ptr) mOldText.GetOffsetPtr(0), mOldText.GetSize());
mIsDone = false;
// Update selection
mTextSelection->SetSelectionRange(mOldRangeOrig);
}
}
}
Boolean LAETypingAction::CanRedo(void) const
{
return !mIsDone && TypingBuffered();
}
Boolean LAETypingAction::CanUndo(void) const
{
return mIsDone && TypingBuffered();
}
Int32 LAETypingAction::TypingBuffered(void) const
{
return mNewText.GetSize() || mOldText.GetSize();
}
Boolean LAETypingAction::NewTypingLocation(void)
{
return mFinalized ||
!mIsDone ||
mLastRange != mTextSelection->GetSelectionRange();
}
void LAETypingAction::UpdateTypingLocation(void)
{
mLastRange = mTextSelection->GetSelectionRange();
}
void LAETypingAction::PrimeFirstRemember(const TextRangeT &inRange)
{
TextRangeT superRange = mTextSelection->GetTextModel()->GetTextLink()->GetRange();
Assert_(mOldRange == LTextEngine::sTextUndefined);
Assert_(mOldText.GetSize() == 0);
mOldRange = inRange;
mOldRange.WeakCrop(superRange);
// Find mOldContIdx and mOldCharIdx
{
DescType partType;
Int32 oldTextIndex1,
oldTextIndex2;
mOldContType = cParagraph;
mOldContRange = mOldRange;
partType = mTextEngine->FindPart(superRange, &mOldContRange, mOldContType);
if (partType == typeNull)
mOldContRange = superRange;
// container range
oldTextIndex1 = oldTextIndex2 = 1;
partType = mTextEngine->FindSubRangePartIndices(
superRange, mOldContRange, mOldContType,
&oldTextIndex1, &oldTextIndex2);
mOldContIdx.SetStart(oldTextIndex1);
if (oldTextIndex2 > oldTextIndex1) {
mOldContIdx.SetLength(oldTextIndex2 - oldTextIndex1 + 1);
} else {
if (mOldRange.Length()) {
mOldContIdx.SetLength(1);
} else {
mOldContIdx.SetInsertion(mOldRange.Insertion());
}
}
// characters being replaced
oldTextIndex1 = oldTextIndex2 = 1;
partType = mTextEngine->FindSubRangePartIndices(
mOldContRange, mOldRange, cChar,
&oldTextIndex1, &oldTextIndex2);
mOldCharIdx.SetStart(oldTextIndex1);
if (oldTextIndex2 > oldTextIndex1) {
mOldCharIdx.SetLength(oldTextIndex2 - oldTextIndex1 + 1);
} else {
if (mOldRange.Length())
mOldCharIdx.SetLength(1);
else
mOldCharIdx.SetInsertion(mOldRange.Insertion());
}
}
if (!(mOldRange == mOldRangeOrig))
AEDisposeDesc(&mOldOSpec.mDesc);
}
void LAETypingAction::KeyStreamRememberNew(const TextRangeT &inRange)
/*
Remember newly typed characters as indicated by inRange.
*/
{
TextRangeT range = inRange;
Assert_(!mFinalized);
if (range.Length()) {
Ptr destination = (Ptr) mNewText.Append(NULL, range.Length());
ThrowIfNULL_(destination);
mTextEngine->TextGetThruPtr(range, (Ptr)destination);
}
}
void LAETypingAction::KeyStreamRememberAppend(const TextRangeT &inRange)
/*
Append text to mOldText (initial selection removal or forward delete)
InRange indicates text to remember. Text is appended to mOldText.
NOTE: This routine requires modification for typing undo for styled
text. mOldText must be augmented by a mechanism to
contain style information.
*/
{
TextRangeT range = inRange;
Assert_(!mFinalized);
if (mOldRange == LTextEngine::sTextUndefined) {
PrimeFirstRemember(range);
} else {
if (range.Length()) {
Int32 rangeCharCount = mTextEngine->CountParts(range, cChar);
DescType partType;
AEDisposeDesc(&mOldOSpec.mDesc);
if (mOldRange.End() == mOldContRange.End()) {
// update container range
TextRangeT extensionRange = range;
partType = mTextEngine->FindPart(
mTextSelection->GetTextModel()->GetTextLink()->GetRange(),
&extensionRange, mOldContType);
if (partType == typeNull)
Throw_(paramErr);
// The container found may have been the result of a join so be sure only the
// new part of the extension is included.
extensionRange.MoveStart(inRange.Start());
Int32 extensionParts = mTextEngine->CountParts(extensionRange, mOldContType);
if (!extensionParts)
Throw_(paramErr);
mOldContRange.SetLength(mOldContRange.Length() + extensionRange.Length());
mOldContIdx.SetLength(mOldContIdx.Length() + extensionParts);
}
// update old range
mOldRange.SetLength(mOldRange.Length() + range.Length());
mOldCharIdx.SetLength(mOldCharIdx.Length() + rangeCharCount);
Assert_(mOldCharIdx.Length());
}
}
if (range.Length()) {
Ptr destination = (Ptr) mOldText.Append(NULL, range.Length());;
ThrowIfNULL_(destination);
mTextEngine->TextGetThruPtr(range, destination);
}
}
void LAETypingAction::KeyStreamRememberPrepend(const TextRangeT &inRange)
/*
Prepend text to mOldText (initial selection removal or backspace)
InRange indicates text to remember. Text is prepended to mOldText.
Before any text is actually prepended to mOldText, characters will first
be removed and forgotten from mNewText. Only when mNewText is empty will
characters be prepended.
NOTE: This routine requires modification for typing undo for styled
text. mOldText must be augmented by a mechanism to
contain style information.
*/
{
TextRangeT range = inRange;
Assert_(!mFinalized);
if (mOldRange == LTextEngine::sTextUndefined) {
PrimeFirstRemember(range);
} else {
// First forget newly typed characters
if (mNewText.GetSize()) {
Int32 n = URange32::Min(range.Length(), mNewText.GetSize());
mNewText.Delete(max_Int32, n);
range.SetLength(range.Length() - n);
}
// Then remember old characters
if (range.Length()) {
if (range.End() != mOldRange.Start())
Throw_(paramErr);
Int32 rangeCharCount = mTextEngine->CountParts(range, cChar);
DescType partType;
AEDisposeDesc(&mOldOSpec.mDesc);
if (range.Start() < mOldContRange.Start()) {
// update container range
TextRangeT extensionRange = range;
partType = mTextEngine->FindPart(
mTextSelection->GetTextModel()->GetTextLink()->GetRange(),
&extensionRange, mOldContType);
if ((partType == typeNull) || (extensionRange.End() != mOldContRange.Start()))
Throw_(paramErr);
Int32 extensionChars = mTextEngine->CountParts(extensionRange, cChar),
extensionParts = mTextEngine->CountParts(extensionRange, mOldContType);
if (!extensionChars || !extensionParts)
Throw_(paramErr);
mOldContRange.MoveStart(extensionRange.Start());
mOldContIdx.MoveStart(mOldContIdx.Start() - extensionParts);
// and fix char idx
mOldCharIdx.SetStart(mOldCharIdx.Start() + extensionChars);
}
// update old range
mOldRange.MoveStart(range.Start());
mOldCharIdx.MoveStart(mOldCharIdx.Start() - rangeCharCount);
Assert_(mOldCharIdx.Length());
}
}
if (range.Length()) {
Ptr destination = (Ptr) mOldText.Prepend(NULL, range.Length());
ThrowIfNULL_(destination);
mTextEngine->TextGetThruPtr(range, destination);
}
}
void LAETypingAction::Finalize(void)
{
if (!mFinalized) {
if (mTextSelection) {
if (mTextSelection->GetTypingAction() == this)
mTextSelection->SetTypingAction(NULL);
}
mFinalized = true;
{
AEDisposeDesc(&mOriginalSelection.mDesc);
LAEStream ae;
WriteOldTarget(ae);
ae.Close(&mOriginalSelection.mDesc);
SetPreRecordSelection(true);
}
// ¥¥ Doer
if (mNewText.GetSize()) {
// ¥ Build a CreateElement event
LAEStream ae(kAECoreSuite, kAECreateElement);
// keyAEObjectClass
ae.WriteKey(keyAEObjectClass);
ae.WriteTypeDesc(cChar);
// keyAEInsertHere
ae.WriteKey(keyAEInsertHere);
ae.WriteSpecifier(mSelection);
// keyAEData
ae.WriteKey(keyAEData);
WriteTypingData(ae);
ae.Close(&mRedoEvent.mDesc);
} else {
// ¥ Build Delete event
LAEStream ae(kAECoreSuite, kAEDelete);
ae.WriteKey(keyDirectObject);
ae.WriteSpecifier(mSelection);
ae.Close(&mRedoEvent.mDesc);
}
// ¥¥ Undoer
if (mIsDone) {
TextRangeT newTextRange(mOldRange.Start());
newTextRange.SetLength(mNewText.GetSize());
LModelObject *elem = mTextSelection->GetTextModel()->MakeNewElem(newTextRange, cChar);
AEDisposeDesc(&mNewOSpec.mDesc);
elem->MakeSpecifier(mNewOSpec.mDesc);
} else {
Assert_(mNewOSpec.mDesc.descriptorType != typeNull);
}
if (mOldText.GetSize()) {
// ¥ Build a CreateElement event
LAEStream ae(kAECoreSuite, kAECreateElement);
// keyAEObjectClass
ae.WriteKey(keyAEObjectClass);
ae.WriteTypeDesc(cChar);
// keyAEData
ae.WriteKey(keyAEData);
{
Size size = mOldText.GetSize();
StHandleLocker lock(mOldText.mData);
ae.WriteDesc(typeChar, *(mOldText.mData), size);
}
// keyAEInsertHere
ae.WriteKey(keyAEInsertHere);
ae.WriteDesc(mNewOSpec);
ae.Close(&mUndoEvent.mDesc);
} else {
// ¥ Build Delete event
LAEStream ae(kAECoreSuite, kAEDelete);
ae.WriteKey(keyDirectObject);
ae.WriteDesc(mNewOSpec);
ae.Close(&mUndoEvent.mDesc);
}
}
inherited::Finalize();
}
void LAETypingAction::WriteTypingData(LAEStream &inStream) const
{
Size size = mNewText.GetSize();
StHandleLocker lock(mNewText.mData);
inStream.WriteDesc(typeChar, *(mNewText.mData), size);
}
void LAETypingAction::WriteOldTarget(LAEStream &inStream) const
/*
Write ospec for text to be replaced by typing to inStream.
Handles whether the
target can simply be the selection via mInsertTextHere, or
must fabricate a special character based range ospec.
*/
{
if (mOldOSpec.mDesc.descriptorType != typeNull) {
inStream.WriteDesc(mOldOSpec.mDesc);
} else {
if (mOldCharIdx.Length() <= 0)
Throw_(paramErr); // should never happen!
// mOldSpec should still be valid above!
inStream.OpenRecord(typeObjectSpecifier);
inStream.WriteKey(keyAEDesiredClass);
inStream.WriteTypeDesc(cChar);
inStream.WriteKey(keyAEContainer);
inStream.OpenRecord(typeObjectSpecifier);
inStream.WriteKey(keyAEDesiredClass);
inStream.WriteTypeDesc(mOldContType);
inStream.WriteKey(keyAEContainer);
inStream.WriteSpecifier(mTextSelection->GetTextModel());
WriteRangeIndexKey( inStream,
mOldContIdx.Start(), mOldContIdx.End() -1,
mOldContType, mOldContType
);
inStream.CloseRecord();
WriteRangeIndexKey( inStream,
mOldCharIdx.Start(), mOldCharIdx.End() -1,
cChar, cChar
);
inStream.CloseRecord();
}
}
void LAETypingAction::WriteRangeIndexKey(
LAEStream &inStream,
Int32 inIndex1,
Int32 inIndex2,
DescType inType1,
DescType inType2) const
{
if (inIndex2 == inIndex1) {
inStream.WriteKey(keyAEKeyForm);
inStream.WriteEnumDesc(formAbsolutePosition);
inStream.WriteKey(keyAEKeyData);
inStream.WriteDesc(typeLongInteger, &inIndex1, sizeof(inIndex1));
} else if (inIndex2 > inIndex1) {
AEDesc currentContainer = {typeCurrentContainer, NULL};
inStream.WriteKey(keyAEKeyForm);
inStream.WriteEnumDesc(formRange);
inStream.WriteKey(keyAEKeyData);
inStream.OpenRecord(typeRangeDescriptor);
inStream.WriteKey(keyAERangeStart);
inStream.OpenRecord(typeObjectSpecifier);
inStream.WriteKey(keyAEDesiredClass);
inStream.WriteTypeDesc(inType1);
inStream.WriteKey(keyAEContainer);
inStream.WriteDesc(currentContainer);
inStream.WriteKey(keyAEKeyForm);
inStream.WriteEnumDesc(formAbsolutePosition);
inStream.WriteKey(keyAEKeyData);
inStream.WriteDesc(typeLongInteger, &inIndex1, sizeof(inIndex1));
inStream.CloseRecord();
inStream.WriteKey(keyAERangeStop);
inStream.OpenRecord(typeObjectSpecifier);
inStream.WriteKey(keyAEDesiredClass);
inStream.WriteTypeDesc(inType2);
inStream.WriteKey(keyAEContainer);
inStream.WriteDesc(currentContainer);
inStream.WriteKey(keyAEKeyForm);
inStream.WriteEnumDesc(formAbsolutePosition);
inStream.WriteKey(keyAEKeyData);
inStream.WriteDesc(typeLongInteger, &inIndex2, sizeof(inIndex2));
inStream.CloseRecord();
inStream.CloseRecord();
} else {
WriteRangeIndexKey(inStream, inIndex2, inIndex1, inType2, inType1);
}
}