pjs/cmd/macfe/central/TSMProxy.cp

702 строки
18 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "TSMProxy.h"
#include "proto.h"
#include "edt.h"
#include "uintl.h"
#include "intl_csi.h"
#include "xp_trace.h"
HoldUpdatesProxy::HoldUpdatesProxy(CEditView &inTextView) :
mTextView(inTextView)
{
mTextView.SetHoldUpdates(this);
mStartY = 0;
mHeight = 0;
}
HoldUpdatesProxy::~HoldUpdatesProxy()
{
mTextView.SetHoldUpdates(nil);
mTextView.DocumentChanged(mStartY, mHeight);
}
void HoldUpdatesProxy::DocumentChanged( int32 iStartY, int32 iHeight )
{
if (mHeight == 0) { // there is no range already
// just set to the new range
mStartY = iStartY;
mHeight = iHeight;
} else if (mHeight == -1) { // the current range already extends to the bottom
// should the top be moved up?
if (iStartY < mStartY)
mStartY = iStartY;
} else if (iHeight == -1) { // the new range extendes all the way to the bottom
// should the top be moved up?
mHeight = iHeight;
if (iStartY < mStartY)
mStartY = iStartY;
} else {
if (iStartY < mStartY) {
// use the new top
if (iStartY + iHeight > mStartY + mHeight) {
// and the new height
mStartY = iStartY;
mHeight = iHeight;
} else {
// but the old height
mHeight += mStartY - iStartY;
mStartY = iStartY;
}
} else {
// use the old top
if (iStartY + iHeight > mStartY + mHeight) {
// but use the new height
mHeight = iStartY + iHeight - mStartY;
}
}
}
}
AEEventHandlerUPP HTMLInlineTSMProxy::sAEHandler = NewAEEventHandlerProc( AEHandlerTSM );
// HTMLInlineTSMProxy *HTMLInlineTSMProxy::sCurrentProxy = NULL;
#if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
void HTMLInlineTSMProxy::PasteFromPtr(const Ptr thedata, int32 len, short hiliteStyle)
{
if (len < 1)
return;
EDT_CharacterData *pData = EDT_NewCharacterData();
if (pData) {
pData->mask = TF_INLINEINPUT | TF_INLINEINPUTTHICK | TF_INLINEINPUTDOTTED;
switch (hiliteStyle) {
case kCaretPosition:
pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK; // this is just a guess actually: FIX ME!!
break;
case kRawText:
pData->values = 0;
break;
case kSelectedRawText:
pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK | TF_INLINEINPUTDOTTED;
break;
default:
XP_ASSERT(false);
case kConvertedText:
pData->values = TF_INLINEINPUT;
break;
case kSelectedConvertedText:
pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK;
break;
}
EDT_SetCharacterData( mContext ,pData );
EDT_FreeCharacterData(pData);
}
// HACK HACK HACK
// ok, so everyone has been really helpful and all but I'm going to put this in as a hack
// rather than try to do it "right": unicodeString will either be "thedata" or the result
// if we need to do unicode conversion. We'll free this below if the pointer address has changed
char *unicodeString = thedata;
INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(mContext);
int16 win_csid = INTL_GetCSIWinCSID(csi);
if ( (win_csid == CS_UTF8) || (win_csid==CS_UTF7) ) {
INTL_Encoding_ID winCSID = ScriptToEncoding( ::GetScriptManagerVariable( smKeyScript ) );
unicodeString = (char *)INTL_ConvertLineWithoutAutoDetect( winCSID, CS_UTF8, (unsigned char *)thedata, len );
len = strlen(unicodeString);
}
if (len < 16) { // can we use a small static buffer?
char smallbuffer[16];
XP_MEMCPY(smallbuffer, unicodeString, len);
smallbuffer[len] = '\0';
EDT_InsertText(mContext, smallbuffer);
} else {
char *verytemp = (char *) XP_ALLOC(len + 1);
if (verytemp) {
XP_MEMCPY(verytemp, unicodeString, len);
verytemp[len] = '\0';
EDT_InsertText(mContext, verytemp);
XP_FREE(verytemp);
}
}
// see hack alert above
if ( unicodeString != thedata )
XP_FREEIF(unicodeString);
}
#endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
HTMLInlineTSMProxy::HTMLInlineTSMProxy( CEditView &inTextView )
: mTextView( inTextView )
{
mTSMDocID = 0;
OSType supportedType = kTextService;
OSErr err = ::NewTSMDocument( 1, &supportedType, &mTSMDocID, (long)(void *)this );
ThrowIfOSErr_(err);
mInputHoleActive = false;
mDocActive = false;
}
HTMLInlineTSMProxy::~HTMLInlineTSMProxy()
{
if ( mDocActive )
Deactivate(); // for a bug in TSM. See TE27
OSErr err = noErr;
if ( mTSMDocID )
err = ::DeleteTSMDocument(mTSMDocID);
mTSMDocID = 0;
// Assert_(err == noErr);
}
void
HTMLInlineTSMProxy::Activate( void )
{
OSErr err = noErr;
Assert_( mDocActive == false );
InstallTSMHandlers();
// sCurrentProxy = this;
#ifdef Debug_Signal
// check to see if a bug in TSM will be encountered
ProcessSerialNumber psn,
csn;
err = GetCurrentProcess(&psn);
// ThrowIfOSErr_(err);
err = GetFrontProcess(&csn);
// ThrowIfOSErr_(err);
Assert_((psn.highLongOfPSN == csn.highLongOfPSN) && (psn.lowLongOfPSN == csn.lowLongOfPSN));
#endif
if ( mTSMDocID )
err = ::ActivateTSMDocument( mTSMDocID );
else
err = ::UseInputWindow(NULL, true);
// ThrowIfOSErr_(err);
if ( err == noErr )
mDocActive = true;
}
void
HTMLInlineTSMProxy::Deactivate( void )
{
OSErr err = noErr;
Assert_( mDocActive );
RemoveTSMHandlers();
// sCurrentProxy = NULL;
err = ::DeactivateTSMDocument( mTSMDocID );
if (err != tsmDocNotActiveErr) // this just seems to happen too much -- it is okay if it happens
{
Assert_( err == noErr );
}
mDocActive = false;
}
void
HTMLInlineTSMProxy::FlushInput( void )
{
OSErr err = noErr;
Assert_( mTSMDocID != 0 );
if ( mTSMDocID != 0 )
{
err = ::FixTSMDocument( mTSMDocID );
}
}
void
HTMLInlineTSMProxy::InstallTSMHandlers( void )
{
OSErr err = noErr;
err = ::AEInstallEventHandler(kTextServiceClass, kUpdateActiveInputArea, sAEHandler, kUpdateActiveInputArea, false);
ThrowIfOSErr_(err);
err = ::AEInstallEventHandler(kTextServiceClass, kPos2Offset, sAEHandler, kPos2Offset, false);
ThrowIfOSErr_(err);
err = ::AEInstallEventHandler(kTextServiceClass, kOffset2Pos, sAEHandler, kOffset2Pos, false);
ThrowIfOSErr_(err);
}
void
HTMLInlineTSMProxy::RemoveTSMHandlers( void )
{
OSErr err = noErr;
err = ::AERemoveEventHandler(kTextServiceClass, kUpdateActiveInputArea, sAEHandler, false);
ThrowIfOSErr_(err);
err = ::AERemoveEventHandler(kTextServiceClass, kPos2Offset, sAEHandler, false);
ThrowIfOSErr_(err);
err = ::AERemoveEventHandler(kTextServiceClass, kOffset2Pos, sAEHandler, false);
ThrowIfOSErr_(err);
}
pascal OSErr
HTMLInlineTSMProxy::AEHandlerTSM( const AppleEvent *inAppleEvent, AppleEvent *outReply, Int32 inRefCon )
{
// XP_Trace("begin HTMLInlineTSMProxy::AEHandlerTSM\n");
OSErr err = noErr;
THz oldZone = ::LMGetTheZone(), // Apple bug #115424?
appZone = ::LMGetApplZone();
::LMSetTheZone(appZone);
#if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
try
{
Assert_( sCurrentProxy != NULL );
StHandleLocker lock(inAppleEvent->dataHandle);
AESubDesc appleEvent;
AEDescToSubDesc(inAppleEvent, &appleEvent);
AESubDesc keySubDesc;
AEGetKeySubDesc( &appleEvent, keyAETSMDocumentRefcon, &keySubDesc );
long len;
Int32 *tsmdocrefcon = (Int32*)AEGetSubDescData( &keySubDesc, &len );
ThrowIf_(NULL == tsmdocrefcon);
// XP_Trace("try to get keyAETSMDocumentRefcon\n");
HTMLInlineTSMProxy *proxy = (HTMLInlineTSMProxy *)(*tsmdocrefcon);
AEStream replyStream;
err = AEStream_Open( &replyStream);
err = AEStream_OpenRecord( &replyStream, typeAERecord );
if ( proxy != NULL )
{
switch( inRefCon )
{
case kUpdateActiveInputArea:
// XP_Trace("kUpdateActiveInputArea\n");
proxy->AEUpdate(appleEvent);
break;
case kPos2Offset:
XP_Trace("kPos2Offset\n");
proxy->AEPos2Offset(appleEvent, replyStream);
break;
case kOffset2Pos:
XP_Trace("kOffset2Pos\n");
proxy->AEOffset2Pos(appleEvent, replyStream);
break;
default:
XP_Trace("AppleEvent %c%c%c%c \n", (char)(inRefCon >> 24), (char)((inRefCon >> 16) & 0xff), (char)((inRefCon >> 8) & 0xff), (char)(inRefCon & 0xff) );
Assert_(0);
break;
}
}
// XP_Trace("AEStream_CloseRecord\n");
err = AEStream_CloseRecord( &replyStream );
// Transfer reply parameters to the real reply (hopefully MacOS 8 will have a way around this)
// ie, can simply say:
//
// replyStream.Close(outReply);
//
StAEDescriptor reply;
// XP_Trace("AEStream_Close\n");
err = AEStream_Close( &replyStream, reply );
AESubDesc replySD;
AEDescToSubDesc(reply, &replySD);
AEKeyword key;
int32 upperBound = AECountSubDescItems( &replySD );
for (long i = 1; i <= upperBound; i++) {
StAEDescriptor parm;
AESubDesc nthSubDesc;
err = AEGetNthSubDesc( &replySD, i, &key, &nthSubDesc );
err = AESubDescToDesc( &nthSubDesc, key, &parm.mDesc );
// replySD.NthItem(i, &key).ToDesc(&parm.mDesc);
err = ::AEPutParamDesc(outReply, key, &parm.mDesc);
ThrowIfOSErr_(err);
}
}
catch ( ExceptionCode inErr )
{
err = inErr;
}
catch ( ... )
{
err = paramErr;
}
#endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
::LMSetTheZone(oldZone); // Apple bug #115424?
// XP_Trace ("end HTMLInlineTSMProxy::AEHandlerTSM\n");
return err;
}
#if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
void HTMLInlineTSMProxy::AEUpdate(
const AESubDesc &inAppleEvent )
{
OSErr err;
CEditView::OutOfFocus(&mTextView);
HoldUpdatesProxy stopUpdatesProxy(mTextView);
// if we don't already have an input hole, remember where we are
if (!mInputHoleActive) {
mInputHoleActive = true;
mInputHoleStart = EDT_GetInsertPointOffset(mContext);
mInputHoleLen = 0;
}
AESubDesc keySubDesc;
long textlen;
long dummylen;
// Get keyAETheData
AEGetKeySubDesc( &inAppleEvent, keyAETheData, &keySubDesc );
Ptr thedata = (char *) AEGetSubDescData( &keySubDesc, &textlen );
// Get keyAEFixLength
AEGetKeySubDesc( &inAppleEvent, keyAEFixLength, &keySubDesc );
Int32 fixLength = *(Int32 *) AEGetSubDescData( &keySubDesc, &dummylen );
if (fixLength < 0) // special signal to fix it all!!
fixLength = textlen;
// Get keyAEScriptTag
// Currently do not depend on it.
// Get [optional] keyAEUpdateRange
// Currently do not depend on it.
// Get [optional] keyAEPinRange
// Currently do not depend on it.
// Get [optional] keyAEClauseOffsets
// Currently do not depend on it.
mTextView.EraseCaret();
mTextView.HideCaret(true);
// if we do already have an input hole, select all the text and delete so that we start fresh
if (mInputHoleLen) {
EDT_CharacterData *temp = EDT_GetCharacterData( mContext );
EDT_SetInsertPointToOffset(mContext, mInputHoleStart, mInputHoleLen);
EDT_DeletePreviousChar(mContext);
if (temp) {
if (textlen) // if len == 0, then don't bother setting the character data because there is nothing left!
EDT_SetCharacterData( mContext, temp );
EDT_FreeCharacterData( temp );
}
}
// we will handle this special case because it makes the algorithm easier to understand.
// the input hole is going away because we are going to fix everything...
if (fixLength == textlen)
{
PasteFromPtr(thedata, fixLength, kRawText);
mInputHoleActive = false;
CEditView::OutOfFocus(&mTextView);
mTextView.HideCaret(false);
return;
}
// we have already selected the old data, now paste in anything that needs to be fixed
if (fixLength) {
PasteFromPtr(thedata, fixLength, kRawText);
mInputHoleStart = EDT_GetInsertPointOffset(mContext); // a new starting point for our input hole
}
// Get [optional] keyAEHiliteRange
err = AEGetKeySubDesc( &inAppleEvent, keyAEHiliteRange, &keySubDesc );
XP_ASSERT( keySubDesc != NULL && err == noErr );
if ( err == noErr) {
// if (inAppleEvent.KeyExists(keyAEHiliteRange)) {
// AESubDesc hiliteSD( hiliteRangeSubDesc, typeTextRangeArray );
// TextRangeArrayPtr p = (TextRangeArrayPtr)hiliteSD.GetDataPtr();
TextRangeArrayPtr p = (TextRangeArrayPtr)AEGetSubDescData( &keySubDesc, &dummylen );
for (Int32 i = 0; i < p->fNumOfRanges; i++) {
TextRange record;
// we don't care about any extra information which is supposed to be encoded in the sign of any of these numbers
record.fStart = abs(p->fRange[i].fStart);
record.fEnd = abs(p->fRange[i].fEnd);
record.fHiliteStyle = abs(p->fRange[i].fHiliteStyle);
PasteFromPtr(thedata + fixLength + record.fStart, record.fEnd - record.fStart, record.fHiliteStyle);
}
}
mInputHoleLen = EDT_GetInsertPointOffset(mContext) - mInputHoleStart; // a new length for our input hole
// output
mTextView.HideCaret(false);
CEditView::OutOfFocus(&mTextView);
}
// so which is it?
#define keyAELeadingEdge keyAELeftSide
void HTMLInlineTSMProxy::AEPos2Offset(
const AESubDesc &inAppleEvent,
AEStream &inStream) const
{
// input
Point* pWhere;
Point where;
Boolean dragging;
long len;
OSErr err;
AESubDesc subdesc;
err = AEGetKeySubDesc( &inAppleEvent, keyAECurrentPoint, &subdesc );
pWhere = (Point *)AEGetSubDescData( &subdesc, &len );
where.h = pWhere->h;
where.v = pWhere->v;
// inAppleEvent.KeyedItem(keyAECurrentPoint).ToPtr(typeQDPoint, &where, sizeof(where));
err = AEGetKeySubDesc( &inAppleEvent, keyAEDragging, &subdesc );
if ( AEGetSubDescType( &subdesc ) != typeNull )
dragging = *(Boolean *)AEGetSubDescData( &subdesc, &len );
else
dragging = false;
// AESubDesc sd = inAppleEvent.KeyedItem(keyAEDragging);
// if (sd.GetType() != typeNull) // keyAEdragging is optional
// dragging = sd.ToBoolean();
// process
CEditView::OutOfFocus(&mTextView);
mTextView.FocusDraw(); // for GlobalToLocal
::GlobalToLocal(&where);
CEditView::OutOfFocus(&mTextView);
SPoint32 where32;
mTextView.LocalToImagePoint(where, where32);
LO_HitResult result;
LO_Hit(mContext, where32.h, where32.v, false, &result, nil);
if (result.type != LO_HIT_ELEMENT ||
// result.lo_hitElement.region != LO_HIT_ELEMENT_REGION_MIDDLE ||
result.lo_hitElement.position.element->type != LO_TEXT) {
err = AEStream_WriteKey( &inStream, keyAEOffset );
// inStream.WriteKey(keyAEOffset);
Int32 offset = -1;
err = AEStream_WriteDesc( &inStream, typeLongInteger, &offset, sizeof(offset) );
// inStream.WriteDesc(typeLongInteger, &offset, sizeof(offset));
err = AEStream_WriteKey( &inStream, keyAERegionClass );
// inStream.WriteKey(keyAERegionClass);
short aShort = kTSMOutsideOfBody;
err = AEStream_WriteDesc( &inStream, typeShortInteger, &aShort, sizeof(aShort) );
// inStream.WriteDesc(typeShortInteger, &aShort, sizeof(aShort));
return;
}
ED_BufferOffset newPosition = EDT_LayoutElementToOffset( mContext, result.lo_hitElement.position.element, result.lo_hitElement.position.position);
/*
ED_BufferOffset saveSelStart, saveSelEnd;
EDT_GetSelectionOffsets(mContext, &saveSelStart, &saveSelEnd); // remember position
EDT_PositionCaret(mContext, where32.h, where32.v );
ED_BufferOffset newPosition = EDT_GetInsertPointOffset(mContext);
EDT_SetInsertPointToOffset(mContext, saveSelStart, saveSelEnd - saveSelStart); // restore position
*/
// restrict to the active range if you are dragging
if (dragging) {
if (newPosition < mInputHoleStart) newPosition = mInputHoleStart;
if (newPosition > mInputHoleStart + mInputHoleLen) newPosition = mInputHoleStart + mInputHoleLen;
}
// output
err = AEStream_WriteKey( &inStream, keyAEOffset );
// inStream.WriteKey(keyAEOffset);
Int32 offset = newPosition;
offset -= mInputHoleStart;
err = AEStream_WriteDesc( &inStream, typeLongInteger, &offset, sizeof(offset) );
// inStream.WriteDesc(typeLongInteger, &offset, sizeof(offset));
err = AEStream_WriteKey( &inStream, keyAERegionClass );
// inStream.WriteKey(keyAERegionClass);
short aShort = kTSMOutsideOfBody;
SDimension32 sizeImage;
SDimension16 sizeFrame;
mTextView.GetImageSize(sizeImage);
mTextView.GetFrameSize(sizeFrame);
if ((0 <= where32.h) && (where32.h < sizeFrame.width) && (0 <= where32.v) && (where.v < sizeImage.height))
{
if (offset >= 0 && offset <= mInputHoleLen)
aShort = kTSMInsideOfActiveInputArea;
else
aShort = kTSMInsideOfBody;
}
err = AEStream_WriteDesc( &inStream, typeShortInteger, &aShort, sizeof(aShort) );
// inStream.WriteDesc(typeShortInteger, &aShort, sizeof(aShort));
}
void HTMLInlineTSMProxy::AEOffset2Pos(
const AESubDesc &inAppleEvent,
AEStream &inStream) const
{
OSErr err;
// input
AESubDesc subdesc;
err = AEGetKeySubDesc( &inAppleEvent, keyAEOffset, &subdesc );
long len;
Int32 offset = *(Int32 *)AEGetSubDescData( &subdesc, &len );
offset += mInputHoleStart;
LO_Element * element;
int32 caretPos;
EDT_OffsetToLayoutElement(mContext, offset, &element, &caretPos);
SPoint32 where32;
int32 veryTemp;
GetCaretPosition( mContext, element, caretPos, &where32.h, &veryTemp, &where32.v );
Point where;
mTextView.ImageToLocalPoint(where32, where);
CEditView::OutOfFocus(&mTextView);
mTextView.FocusDraw(); // for LocalToGlobal
::LocalToGlobal(&where);
// output
err = AEStream_WriteKey( &inStream, keyAEPoint );
err = AEStream_WriteDesc( &inStream, typeQDPoint, &where, sizeof(where) );
// inStream.WriteKey(keyAEPoint);
// inStream.WriteDesc(typeQDPoint, &where, sizeof(where));
}
#endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_