home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / central / TSMProxy.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  15.7 KB  |  606 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "TSMProxy.h"
  20. #include "proto.h"
  21. #include "edt.h"
  22. #include "uintl.h"
  23. #include "intl_csi.h"
  24.  
  25.  
  26. HoldUpdatesProxy::HoldUpdatesProxy(CEditView &inTextView) :
  27.     mTextView(inTextView)
  28. {
  29.     mTextView.SetHoldUpdates(this);
  30.     mStartY = 0;
  31.     mHeight = 0;
  32. }
  33.  
  34. HoldUpdatesProxy::~HoldUpdatesProxy()
  35. {
  36.     mTextView.SetHoldUpdates(nil);
  37.     mTextView.DocumentChanged(mStartY, mHeight);
  38. }
  39.  
  40. void HoldUpdatesProxy::DocumentChanged( int32 iStartY, int32 iHeight )
  41. {
  42.     if (mHeight == 0) {                        // there is no range already
  43.                                             // just set to the new range
  44.         mStartY = iStartY;
  45.         mHeight = iHeight;
  46.         
  47.     } else if (mHeight == -1) {                // the current range already extends to the bottom
  48.                                             // should the top be moved up?
  49.         if (iStartY < mStartY)
  50.             mStartY = iStartY;
  51.     
  52.     } else if (iHeight == -1) {                // the new range extendes all the way to the bottom
  53.                                             // should the top be moved up?
  54.         mHeight = iHeight;
  55.         if (iStartY < mStartY)
  56.             mStartY = iStartY;
  57.         
  58.     } else {
  59.     
  60.         if (iStartY < mStartY) {
  61.                                             // use the new top
  62.             if (iStartY + iHeight > mStartY + mHeight) {
  63.                                                     // and the new height
  64.                 mStartY = iStartY;
  65.                 mHeight = iHeight;
  66.                 
  67.             } else {
  68.                                                     // but the old height
  69.                 mHeight += mStartY - iStartY;
  70.                 mStartY = iStartY;
  71.                 
  72.             }
  73.             
  74.         } else {
  75.                                             // use the old top
  76.             if (iStartY + iHeight > mStartY + mHeight) {
  77.                                                     // but use the new height
  78.                 mHeight = iStartY + iHeight - mStartY;
  79.                 
  80.             }
  81.             
  82.         }
  83.     }
  84. }
  85.  
  86.  
  87. AEEventHandlerUPP        HTMLInlineTSMProxy::sAEHandler             = NewAEEventHandlerProc( AEHandlerTSM );
  88. HTMLInlineTSMProxy    *HTMLInlineTSMProxy::sCurrentProxy     = NULL;
  89.  
  90.  
  91. #if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  92. void HTMLInlineTSMProxy::PasteFromPtr(const Ptr thedata, int32 len, short hiliteStyle)
  93. {
  94.     if (len < 1)
  95.         return;
  96.     
  97.     EDT_CharacterData *pData = EDT_NewCharacterData();
  98.     if (pData) {
  99.         pData->mask = TF_INLINEINPUT | TF_INLINEINPUTTHICK | TF_INLINEINPUTDOTTED;
  100.         
  101.         switch (hiliteStyle) {
  102.             case kCaretPosition:
  103.                 pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK;        // this is just a guess actually: FIX ME!!
  104.             break;
  105.  
  106.              case kRawText:
  107.                 pData->values = 0;
  108.             break;
  109.  
  110.             case kSelectedRawText:
  111.                 pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK | TF_INLINEINPUTDOTTED;
  112.             break;
  113.  
  114.              default:
  115.                  XP_ASSERT(false);
  116.             case kConvertedText:
  117.                 pData->values = TF_INLINEINPUT;
  118.             break;
  119.  
  120.             case kSelectedConvertedText:
  121.                 pData->values = TF_INLINEINPUT | TF_INLINEINPUTTHICK;
  122.             break;
  123.         }
  124.         
  125.         EDT_SetCharacterData( mContext ,pData );
  126.         EDT_FreeCharacterData(pData);
  127.     }
  128.     
  129.     // HACK HACK HACK
  130.     // ok, so everyone has been really helpful and all but I'm going to put this in as a hack
  131.     // rather than try to do it "right":  unicodeString will either be "thedata" or the result
  132.     // if we need to do unicode conversion.  We'll free this below if the pointer address has changed
  133.     char *unicodeString = thedata;
  134.     INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(mContext);
  135.     int16 win_csid = INTL_GetCSIWinCSID(csi);
  136.     if ( (win_csid == CS_UTF8) || (win_csid==CS_UTF7) ) {
  137.     
  138.         INTL_Encoding_ID winCSID = ScriptToEncoding( ::GetScriptManagerVariable( smKeyScript ) );
  139.         unicodeString = (char *)INTL_ConvertLineWithoutAutoDetect( winCSID, CS_UTF8, (unsigned char *)thedata, len );
  140.            len = strlen(unicodeString);
  141.  
  142.     }
  143.     
  144.     if (len < 16) {                                // can we use a small static buffer?
  145.     
  146.         char smallbuffer[16];
  147.         XP_MEMCPY(smallbuffer, unicodeString, len);
  148.         smallbuffer[len] = '\0';
  149.         EDT_InsertText(mContext, smallbuffer);
  150.         
  151.     } else {
  152.     
  153.         char *verytemp = (char *) XP_ALLOC(len + 1);
  154.         if (verytemp) {
  155.             XP_MEMCPY(verytemp, unicodeString, len);
  156.             verytemp[len] = '\0';
  157.             EDT_InsertText(mContext, verytemp);
  158.             XP_FREE(verytemp);
  159.         }
  160.         
  161.     }
  162.     
  163.     // see hack alert above
  164.     if ( unicodeString != thedata )
  165.         XP_FREEIF(unicodeString);
  166.  
  167. }
  168. #endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  169.  
  170. HTMLInlineTSMProxy::HTMLInlineTSMProxy( CEditView &inTextView )
  171.  
  172.     :    mTextView( inTextView )
  173.  
  174.     {
  175.  
  176.         mTSMDocID = 0;
  177.         
  178.         OSType    supportedType = kTextService;
  179.         OSErr    err = ::NewTSMDocument( 1, &supportedType, &mTSMDocID, (long)(void *)this );
  180.         ThrowIfOSErr_(err);
  181.         mInputHoleActive = false;
  182.         mDocActive = false;
  183.     
  184.     }
  185.  
  186.  
  187. HTMLInlineTSMProxy::~HTMLInlineTSMProxy()
  188.     {
  189.     
  190.         if ( mDocActive )
  191.             Deactivate();    //    for a bug in TSM.  See TE27
  192.  
  193.         OSErr    err = noErr;
  194.     
  195.         if ( mTSMDocID )
  196.             err = ::DeleteTSMDocument(mTSMDocID);
  197.     
  198.         mTSMDocID = 0;
  199.     //    Assert_(err == noErr);
  200.     
  201.     }
  202.     
  203.     
  204. void
  205. HTMLInlineTSMProxy::Activate( void )
  206.     {
  207.             
  208.         OSErr    err = noErr;
  209.     
  210.         Assert_( mDocActive == false );
  211.     
  212.         InstallTSMHandlers();
  213.     
  214.         sCurrentProxy = this;
  215.  
  216.         #ifdef Debug_Signal
  217.             //    check to see if a bug in TSM will be encountered
  218.             ProcessSerialNumber    psn,
  219.                                 csn;
  220.             err = GetCurrentProcess(&psn);
  221. //            ThrowIfOSErr_(err);
  222.             err = GetFrontProcess(&csn);
  223. //            ThrowIfOSErr_(err);
  224.             Assert_((psn.highLongOfPSN == csn.highLongOfPSN) && (psn.lowLongOfPSN == csn.lowLongOfPSN));
  225.         #endif
  226.  
  227.         if ( mTSMDocID )
  228.             err = ::ActivateTSMDocument( mTSMDocID );
  229.         else
  230.             err = ::UseInputWindow(NULL, true);
  231. //        ThrowIfOSErr_(err);
  232.  
  233.         if ( err == noErr )
  234.             mDocActive = true;
  235.  
  236.     }
  237.     
  238.     
  239. void
  240. HTMLInlineTSMProxy::Deactivate( void )
  241.     {
  242.     
  243.         OSErr    err = noErr;
  244.     
  245.         Assert_( mDocActive );
  246.         
  247.         RemoveTSMHandlers();
  248.     
  249.         sCurrentProxy = NULL;
  250.  
  251.         err = ::DeactivateTSMDocument( mTSMDocID );
  252.  
  253.         if (err != tsmDocNotActiveErr)  //    this just seems to happen too much -- it is okay if it happens
  254.             {
  255.             
  256.                 Assert_( err == noErr );
  257.             
  258.             }         
  259.             
  260.         mDocActive = false;
  261.  
  262.     }
  263.     
  264.     
  265. void
  266. HTMLInlineTSMProxy::FlushInput( void )
  267.     {
  268.     
  269.         OSErr    err = noErr;
  270.         
  271.         Assert_( mTSMDocID != 0 );
  272.         
  273.         if ( mTSMDocID != 0 ) 
  274.             {
  275.         
  276.                 err = ::FixTSMDocument( mTSMDocID );
  277.             
  278.             }
  279.     
  280.     }
  281.     
  282.     
  283. void
  284. HTMLInlineTSMProxy::InstallTSMHandlers( void )
  285.     {
  286.     
  287.         OSErr    err = noErr;
  288.         
  289.         err = ::AEInstallEventHandler(kTextServiceClass, kUpdateActiveInputArea, sAEHandler, kUpdateActiveInputArea, false);
  290.         ThrowIfOSErr_(err);
  291.         err = ::AEInstallEventHandler(kTextServiceClass, kPos2Offset, sAEHandler, kPos2Offset, false);
  292.         ThrowIfOSErr_(err);
  293.         err = ::AEInstallEventHandler(kTextServiceClass, kOffset2Pos, sAEHandler, kOffset2Pos, false);
  294.         ThrowIfOSErr_(err);
  295.     
  296.     }
  297.  
  298.     
  299. void
  300. HTMLInlineTSMProxy::RemoveTSMHandlers( void )
  301.     {
  302.     
  303.         OSErr err = noErr;
  304.         
  305.         err = ::AERemoveEventHandler(kTextServiceClass, kUpdateActiveInputArea, sAEHandler, false);
  306.         ThrowIfOSErr_(err);
  307.         err = ::AERemoveEventHandler(kTextServiceClass, kPos2Offset, sAEHandler, false);
  308.         ThrowIfOSErr_(err);
  309.         err = ::AERemoveEventHandler(kTextServiceClass, kOffset2Pos, sAEHandler, false);
  310.         ThrowIfOSErr_(err);
  311.     
  312.     }
  313.     
  314.     
  315. pascal OSErr    
  316. HTMLInlineTSMProxy::AEHandlerTSM( const AppleEvent *inAppleEvent, AppleEvent *outReply, Int32 inRefCon )
  317.     {
  318.     
  319.         OSErr    err = noErr;
  320.         
  321.         THz    oldZone = ::LMGetTheZone(),    //    Apple bug #115424?
  322.             appZone = ::LMGetApplZone();
  323.         ::LMSetTheZone(appZone);
  324.     
  325. #if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  326.         try 
  327.             {
  328.             
  329.                 Assert_( sCurrentProxy != NULL );
  330.                 
  331.                 StHandleLocker    lock(inAppleEvent->dataHandle);
  332.                 LAESubDesc            appleEvent(*inAppleEvent);
  333.                 LAEStream                replyStream;
  334.                 
  335.                 ThrowIf_(((Int32)(void *)sCurrentProxy) != appleEvent.KeyedItem(keyAETSMDocumentRefcon).ToInt32());
  336.                 
  337.                 replyStream.OpenRecord();
  338.                 
  339.                 if ( sCurrentProxy != NULL )
  340.                     {
  341.                             
  342.                         switch( inRefCon )  
  343.                             {
  344.  
  345.                                 case kUpdateActiveInputArea:
  346.                                 
  347.                                     sCurrentProxy->AEUpdate(appleEvent);
  348.                                     
  349.                                 break;
  350.  
  351.                                 case kPos2Offset:
  352.                                 
  353.                                     sCurrentProxy->AEPos2Offset(appleEvent, replyStream);
  354.                                     
  355.                                 break;
  356.  
  357.                                 case kOffset2Pos:
  358.                                 
  359.                                     sCurrentProxy->AEOffset2Pos(appleEvent, replyStream);
  360.                                     
  361.                                 break;
  362.                                 
  363.                             }
  364.                         
  365.                     }
  366.                 
  367.                 replyStream.CloseRecord();
  368.                 
  369.                 //    Transfer reply parameters to the real reply (hopefully MacOS 8 will have a way around this)
  370.                 //    ie, can simply say:
  371.                 //
  372.                 //        replyStream.Close(outReply);
  373.                 //
  374.                 StAEDescriptor    reply;
  375.                 replyStream.Close(reply);
  376.                 LAESubDesc        replySD(reply);
  377.                 AEKeyword        key;
  378.                 for (Int32 i = 1; i <= replySD.CountItems(); i++) {
  379.                     StAEDescriptor    parm;
  380.                     replySD.NthItem(i, &key).ToDesc(&parm.mDesc);
  381.                     err = ::AEPutParamDesc(outReply, key, &parm.mDesc);
  382.                     ThrowIfOSErr_(err);
  383.                 }        
  384.  
  385.             } 
  386.             
  387.         catch ( ExceptionCode inErr ) 
  388.             {
  389.             
  390.                 err = inErr;
  391.             
  392.             } 
  393.             
  394.         catch ( ... ) 
  395.             {
  396.             
  397.                 err = paramErr;
  398.             
  399.             }
  400. #endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  401.  
  402.         ::LMSetTheZone(oldZone);    //    Apple bug #115424?
  403.         
  404.         return err;
  405.         
  406.     }
  407.  
  408.  
  409. #if _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  410. void    HTMLInlineTSMProxy::AEUpdate(
  411.     const LAESubDesc    &inAppleEvent )
  412. {
  413.     CEditView::OutOfFocus(&mTextView);
  414.  
  415.     HoldUpdatesProxy stopUpdatesProxy(mTextView);
  416.     
  417.     // if we don't already have an input hole, remember where we are
  418.     if (!mInputHoleActive) {
  419.         mInputHoleActive = true;
  420.         mInputHoleStart = EDT_GetInsertPointOffset(mContext);
  421.         mInputHoleLen = 0;
  422.     }
  423.     
  424.     // get the text in the input hole
  425.     LAESubDesc    textSD(inAppleEvent.KeyedItem(keyAETheData), typeChar);
  426.     Ptr thedata = (const Ptr)textSD.GetDataPtr();
  427.     int32 len = textSD.GetDataLength();
  428.             
  429.     // fixLength is the number of characters which can be fixed into the buffer.
  430.     Int32    fixLength = inAppleEvent.KeyedItem(keyAEFixLength).ToInt32();
  431.     if (fixLength < 0)            // special signal to fix it all!!
  432.         fixLength = len;
  433.  
  434.     mTextView.EraseCaret();
  435.     mTextView.HideCaret(true);
  436.     
  437.     // if we do already have an input hole, select all the text and delete so that we start fresh
  438.     if (mInputHoleLen) {
  439.     
  440.         EDT_CharacterData    *temp = EDT_GetCharacterData( mContext );
  441.  
  442.         EDT_SetInsertPointToOffset(mContext, mInputHoleStart, mInputHoleLen);
  443.         EDT_DeletePreviousChar(mContext);
  444.         
  445.         if (temp) {
  446.             if (len)    // if len == 0, then don't bother setting the character data because there is nothing left!
  447.                 EDT_SetCharacterData( mContext, temp );
  448.             EDT_FreeCharacterData( temp );
  449.         }
  450.     }
  451.         
  452.     // we will handle this special case because it makes the algorithm easier to understand.
  453.     // the input hole is going away because we are going to fix everything...
  454.     if (fixLength == len) 
  455.     {
  456.         PasteFromPtr(thedata, fixLength, kRawText);        
  457.         mInputHoleActive = false;
  458.         CEditView::OutOfFocus(&mTextView);
  459.         mTextView.HideCaret(false);
  460.         return;
  461.     }
  462.     
  463.     // we have already selected the old data, now paste in anything that needs to be fixed
  464.     if (fixLength) {
  465.         PasteFromPtr(thedata, fixLength, kRawText);
  466.         mInputHoleStart = EDT_GetInsertPointOffset(mContext);    // a new starting point for our input hole
  467.     }
  468.  
  469.     if (inAppleEvent.KeyExists(keyAEHiliteRange)) {
  470.         LAESubDesc    hiliteSD(inAppleEvent.KeyedItem(keyAEHiliteRange), typeTextRangeArray);
  471.         TextRangeArrayPtr    p = (TextRangeArrayPtr)hiliteSD.GetDataPtr();
  472.         for (Int32 i = 0; i < p->fNumOfRanges; i++) {
  473.         
  474.             TextRange    record;
  475.             // we don't care about any extra information which is supposed to be encoded in the sign of any of these numbers
  476.             record.fStart = abs(p->fRange[i].fStart);
  477.             record.fEnd = abs(p->fRange[i].fEnd);
  478.             record.fHiliteStyle = abs(p->fRange[i].fHiliteStyle);
  479.             
  480.             PasteFromPtr(thedata + fixLength + record.fStart, record.fEnd - record.fStart, record.fHiliteStyle);
  481.         }
  482.     }
  483.     
  484.     mInputHoleLen = EDT_GetInsertPointOffset(mContext) - mInputHoleStart;    // a new length for our input hole
  485.  
  486.     
  487.     //    output
  488.     mTextView.HideCaret(false);
  489.     CEditView::OutOfFocus(&mTextView);
  490. }
  491.  
  492. //    so which is it?
  493. #define    keyAELeadingEdge    keyAELeftSide
  494.  
  495. void    HTMLInlineTSMProxy::AEPos2Offset(
  496.     const LAESubDesc    &inAppleEvent,
  497.     LAEStream            &inStream) const
  498. {
  499.     //    input
  500.     Point    where;
  501.     Boolean    dragging = false;
  502.  
  503.     inAppleEvent.KeyedItem(keyAECurrentPoint).ToPtr(typeQDPoint, &where, sizeof(where));
  504.  
  505.     LAESubDesc    sd = inAppleEvent.KeyedItem(keyAEDragging);
  506.     if (sd.GetType() != typeNull)    //    keyAEdragging is optional
  507.         dragging = sd.ToBoolean();
  508.     
  509.     //    process
  510.     CEditView::OutOfFocus(&mTextView);
  511.     mTextView.FocusDraw();    //    for GlobalToLocal
  512.     ::GlobalToLocal(&where);
  513.     CEditView::OutOfFocus(&mTextView);
  514.     SPoint32    where32;
  515.     mTextView.LocalToImagePoint(where, where32);
  516.     
  517.     LO_HitResult result;
  518.     LO_Hit(mContext, where32.h, where32.v, false, &result, nil);
  519.     
  520.     if (result.type != LO_HIT_ELEMENT ||
  521. //        result.lo_hitElement.region != LO_HIT_ELEMENT_REGION_MIDDLE ||
  522.         result.lo_hitElement.position.element->type != LO_TEXT) {
  523.         
  524.             inStream.WriteKey(keyAEOffset);
  525.             Int32    offset = -1;
  526.             inStream.WriteDesc(typeLongInteger, &offset, sizeof(offset));
  527.             
  528.             inStream.WriteKey(keyAERegionClass);
  529.             short    aShort = kTSMOutsideOfBody;
  530.             inStream.WriteDesc(typeShortInteger, &aShort, sizeof(aShort));
  531.             
  532.             return;
  533.         }
  534.         
  535.     ED_BufferOffset newPosition = EDT_LayoutElementToOffset( mContext, result.lo_hitElement.position.element, result.lo_hitElement.position.position);
  536.     
  537. /*
  538.     ED_BufferOffset saveSelStart, saveSelEnd;
  539.     EDT_GetSelectionOffsets(mContext, &saveSelStart, &saveSelEnd);                                        // remember position
  540.     
  541.     EDT_PositionCaret(mContext, where32.h, where32.v );
  542.     ED_BufferOffset newPosition = EDT_GetInsertPointOffset(mContext);
  543.     
  544.     EDT_SetInsertPointToOffset(mContext, saveSelStart, saveSelEnd - saveSelStart);                        // restore position
  545. */    
  546.  
  547.     // restrict to the active range if you are dragging
  548.     if (dragging) {
  549.         if (newPosition < mInputHoleStart) newPosition = mInputHoleStart;
  550.         if (newPosition > mInputHoleStart + mInputHoleLen) newPosition = mInputHoleStart + mInputHoleLen;
  551.     }
  552.  
  553.     //    output
  554.     inStream.WriteKey(keyAEOffset);
  555.     Int32    offset = newPosition;
  556.     offset -= mInputHoleStart;
  557.     inStream.WriteDesc(typeLongInteger, &offset, sizeof(offset));
  558.     
  559.     inStream.WriteKey(keyAERegionClass);
  560.     short    aShort = kTSMOutsideOfBody;
  561.     
  562.     SDimension32 sizeImage;
  563.     SDimension16 sizeFrame;
  564.     mTextView.GetImageSize(sizeImage);
  565.     mTextView.GetFrameSize(sizeFrame);
  566.     
  567.     if ((0 <= where32.h) && (where32.h < sizeFrame.width) && (0 <= where32.v) && (where.v < sizeImage.height))
  568.         {
  569.         if (offset >= 0 && offset <= mInputHoleLen)
  570.             aShort = kTSMInsideOfActiveInputArea;
  571.         else
  572.             aShort = kTSMInsideOfBody;
  573.         }
  574.  
  575.     inStream.WriteDesc(typeShortInteger, &aShort, sizeof(aShort));
  576. }
  577.  
  578.  
  579. void    HTMLInlineTSMProxy::AEOffset2Pos(
  580.     const LAESubDesc    &inAppleEvent,
  581.     LAEStream            &inStream) const
  582. {
  583.     //    input
  584.     Int32    offset = inAppleEvent.KeyedItem(keyAEOffset).ToInt32();
  585.     offset += mInputHoleStart;
  586.     
  587.     LO_Element * element;
  588.     int32 caretPos;
  589.     EDT_OffsetToLayoutElement(mContext, offset, &element, &caretPos);
  590.         
  591.     SPoint32    where32;
  592.     int32        veryTemp;
  593.     GetCaretPosition( mContext, element, caretPos, &where32.h, &veryTemp, &where32.v );
  594.  
  595.     Point        where;
  596.     mTextView.ImageToLocalPoint(where32, where);
  597.     CEditView::OutOfFocus(&mTextView);
  598.     mTextView.FocusDraw();    //    for LocalToGlobal
  599.     ::LocalToGlobal(&where);
  600.     
  601.     //    output
  602.     inStream.WriteKey(keyAEPoint);
  603.     inStream.WriteDesc(typeQDPoint, &where, sizeof(where));
  604. }
  605. #endif _HAVE_FIXES_FOR_REPLACING_AEGIZMOS_
  606.