home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / WASTE 1.1 / WESelecting.c < prev    next >
Encoding:
Text File  |  1994-11-02  |  41.4 KB  |  1,609 lines  |  [TEXT/MPCC]

  1. // { WASTE PROJECT }
  2. // { Drawing Selections, Activating, Updating, Scrolling, etc. }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. // conversion to C by Dan Crevier
  8.  
  9. #include "WASTEIntf.h"
  10. #include <QDOffscreen.h>
  11.  
  12. // { values for _WEArrowOffset action parameter: }
  13. // { plain arrow keys }
  14. #define kGoLeft            0
  15. #define kGoRight        1
  16. #define kGoUp            2
  17. #define kGoDown            3
  18.  
  19. // { modifiers }
  20. #define kOption            4
  21. #define kCommand        8
  22.  
  23. // { option + arrow combos }
  24. #define kGoWordStart    kGoLeft + kOption
  25. #define kGoWordEnd        kGoRight + kOption
  26. #define kGoTextStart    kGoUp + kOption
  27. #define kGoTextEnd        kGoDown + kOption
  28.  
  29. // { command + arrow combos }
  30. #define kGoLineStart    kGoLeft + kCommand
  31. #define kGoLineEnd        kGoRight + kCommand
  32. #define kGoPageStart    kGoUp + kCommand
  33. #define kGoPageEnd        kGoDown + kCommand
  34.  
  35.  
  36. pascal void ClearHiliteBit(void)
  37. {
  38.     LMSetHiliteMode(LMGetHiliteMode() & 0x7f);
  39. }
  40.  
  41. Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  42.                     long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  43.                     void *callbackData);
  44.  
  45. Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  46.                     long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  47.                     void *callbackData)
  48. {
  49.     struct SLPixelToCharData *p = (struct SLPixelToCharData *) callbackData;
  50.     WEPtr pWE = p->pWE;
  51.     Fixed slop;
  52.     Fixed width;
  53.     short cType;
  54.     
  55.     // { if this is the first style run on the line, subtract pen indent from pixelWidth }
  56.     if (styleRunPosition <= smLeftStyleRun) 
  57.     {
  58.         p->pixelWidth = p->pixelWidth - BSL(_WECalcPenIndent(pLine->lineSlop, pWE->alignment), 16);
  59.     }
  60.  
  61.     if (pAttrs->runStyle.tsObject != nil)
  62.     {
  63.  
  64.         // { EMBEDDED OBJECT }
  65.         // { calculate object width as Fixed }
  66.         width = BSL((*(WEObjectDescHandle)pAttrs->runStyle.tsObject)->objectSize.h, 16);
  67.  
  68.         // { subtract object width from pixelWidth }
  69.         p->pixelWidth = p->pixelWidth - width;
  70.  
  71.         // { if pixelWidth has gone negative, the point is within the object: }
  72.         // { find out whether it's closer to the left edge or to the right edge }
  73.         if ((p->pixelWidth < 0) && (p->pixelWidth + BSR(width, 1) < 0))
  74.         {
  75.             *p->edge = kLeadingEdge;
  76.             p->offset = segmentStart;
  77.         }
  78.         else
  79.         {
  80.             *p->edge = kTrailingEdge;
  81.             p->offset= segmentStart + 1;
  82.         }
  83.     }
  84.     else
  85.     {
  86.     
  87.         // { REGULAR TEXT }
  88.     
  89.         // { if this is the last segment on the line, strip the last blank character (if any), }
  90.         // { unless it is the last non-CR character in the whole text stream }
  91.         if (!(styleRunPosition & 1)) 
  92.         {
  93.             if ((segmentStart + segmentLength < pWE->textLength) ||
  94.                 pSegment[segmentLength - 1] == kEOL)
  95.             {
  96.                 cType = CharType(pSegment, segmentLength - 1);
  97.                 if (cType & (smcTypeMask + smcClassMask) == smCharPunct + smPunctBlank)
  98.                 {
  99.                     if ((cType & smcDoubleMask) == 0)
  100.                         segmentLength = segmentLength - 1;
  101.                     else
  102.                         segmentLength = segmentLength - 2;
  103.                 }
  104.             }
  105.         }
  106.     
  107.         // { calculate slop for this text segment (justified text only) }
  108.         if (pWE->alignment == weJustify) 
  109.         {
  110.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  111.                 *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
  112.                 pLine->lineJustAmount);
  113.         }
  114.         else
  115.         {
  116.             slop = 0;
  117.         }
  118.     
  119.         // { call PixelToChar for this segment }
  120. #ifdef WASTE_TABS
  121.         {
  122.             long    ii,
  123.                     beginChar,
  124.                     offset;
  125.             Fixed    lastWidth,
  126.                     tabWidth;
  127.             
  128.             width = p->pixelWidth;
  129.             beginChar = 0;
  130.             offset = 0;
  131.             for (ii = 0; ii < segmentLength && width > 0; ii++)
  132.                 if (pSegment[ii] == '\t')
  133.                 {
  134.                     /*    Measure this sub-segment    */
  135.     
  136.                     lastWidth = width;
  137.                     offset += NPixel2Char(pSegment + beginChar, ii - beginChar,
  138.                                           slop, lastWidth,
  139.                                           (Boolean *)p->edge, &width,
  140.                                           styleRunPosition,
  141.                                           *(Point *)(&kOneToOneScaling),
  142.                                           *(Point *)(&kOneToOneScaling));
  143.                     if (width >= 0)
  144.                     {
  145.                         p->hPos += lastWidth - width;
  146.         
  147.                         tabWidth = (p->hPos / (WASTE_TAB_SIZE << 16) + 1) * (WASTE_TAB_SIZE << 16)
  148.                             - p->hPos;
  149.                         p->hPos += tabWidth;
  150.                         if (width - tabWidth < 0)
  151.                         {
  152.                             if (width - (tabWidth + (1 << 16))/2 > 0)
  153.                             {
  154.                                 *p->edge = -1;
  155.                                 offset++;
  156.                             }
  157.                             else
  158.                                 *p->edge = 0;
  159.                             width = -1 << 16;
  160.                         }
  161.                         else
  162.                         {
  163.                             offset++;
  164.                             width -= tabWidth;
  165.                         }
  166.         
  167.                         beginChar = ii + 1;
  168.                     }
  169.                 }
  170.             if (width > 0)
  171.             {
  172.                 lastWidth = width;
  173.                 offset += NPixel2Char(pSegment + beginChar, segmentLength - beginChar,
  174.                                       slop, lastWidth,
  175.                                       (Boolean *)p->edge, &width,
  176.                                       styleRunPosition,
  177.                                       *(Point *)(&kOneToOneScaling),
  178.                                       *(Point *)(&kOneToOneScaling));
  179.                 p->hPos += lastWidth - width;
  180.             }
  181.     
  182.             p->offset = segmentStart + offset;
  183.         }
  184. #else
  185.         p->offset = segmentStart + NPixel2Char(pSegment, segmentLength, slop, p->pixelWidth,
  186.             (Boolean *)p->edge, &width, styleRunPosition, *(Point *)(&kOneToOneScaling),
  187.             *(Point *)(&kOneToOneScaling));
  188. #endif
  189.  
  190.         // { update pixelWidth for next iteration }
  191.         p->pixelWidth = width;
  192.     }
  193.     
  194.     // { if pixelWidth has gone negative, we're finished; otherwise go to next run }
  195.     return (p->pixelWidth < 0);
  196. }
  197.  
  198. pascal long WEGetOffset(const LongPt *thePoint, char *edge, WEHandle hWE)
  199. {
  200.     // { given a long point in local coordinates, }
  201.     // { find the text offset corresponding to the nearest glyph }
  202.  
  203.     WEPtr pWE;
  204.     long lineIndex;
  205.     Fixed pixelWidth;
  206.     Boolean saveWELock;
  207.     long retval;
  208.     struct SLPixelToCharData callbackData;
  209.     LongPt tempPoint = *thePoint; // so we don't change original point
  210.  
  211.     // { lock the WE record }
  212.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  213.     pWE = *hWE;
  214.  
  215.     // { offset thePoint so that it is relative to the top left corner of the destination rectangle }
  216.     tempPoint.v = tempPoint.v - pWE->destRect.top;
  217.     tempPoint.h = tempPoint.h - pWE->destRect.left;
  218.  
  219.     // { if the point is above the destination rect, return zero }
  220.     if (tempPoint.v < 0) 
  221.     {
  222.         retval = 0;
  223.         *edge = kTrailingEdge;
  224.     }
  225.     else
  226.     {
  227.         // { if the point is below the last line, return last char offset }
  228.         if (tempPoint.v >= WEGetHeight(0, LONG_MAX, hWE)) 
  229.         {
  230.             retval = pWE->textLength;
  231.             *edge = kLeadingEdge;
  232.         }
  233.         else
  234.         {
  235.             // { find the line index corresponding to the vertical pixel offset }
  236.             lineIndex = _WEPixelToLine(tempPoint.v, hWE);
  237.  
  238.             // { express the horizontal pixel offset as a Fixed value }
  239.             pixelWidth = BSL(tempPoint.h, 16);
  240.  
  241.             // { walk through the segments on this line calling PixelToChar }
  242.             callbackData.pWE = pWE;
  243. #ifdef WASTE_TABS
  244.             callbackData.hPos = 0;
  245. #endif
  246.             callbackData.pixelWidth = pixelWidth;
  247.             callbackData.edge = edge;
  248.             callbackData.offset = 0;
  249.             _WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, (void *)&callbackData, hWE);
  250.             retval = callbackData.offset;
  251.         }
  252.     }
  253.     
  254.     // { unlock the WE record }
  255.     _WESetHandleLock((Handle)hWE, saveWELock);
  256.  
  257.     return retval;
  258. }
  259.  
  260. Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  261.                         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  262.                         void *callbackData);
  263.  
  264. Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  265.                         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  266.                         void *callbackData)
  267. {
  268.     struct SLCharToPixelData *p = (struct SLCharToPixelData *) callbackData;
  269.     WEPtr pWE = p->pWE;
  270.     Fixed slop;
  271.     short width;
  272.     Boolean isInSegment;
  273.  
  274.     // { is offset within this segment? }
  275.     isInSegment = (p->offset < segmentStart + segmentLength);
  276.  
  277.     // { if this is the first style run on the line, add pen indent to thePoint.h }
  278.     if (styleRunPosition <= smLeftStyleRun) 
  279.     {
  280.         p->thePoint->h = p->thePoint->h + _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
  281.     }
  282.  
  283.     if (pAttrs->runStyle.tsObject != nil)
  284.     {
  285.     
  286.         // { EMBEDDED OBJECT }
  287.         if (isInSegment)
  288.             width = 0;
  289.         else
  290.             width = (*(WEObjectDescHandle)pAttrs->runStyle.tsObject)->objectSize.h;
  291.     }
  292.     else
  293.     {
  294.         //{ REGULAR TEXT }
  295.         // { calculate slop for this text segment (justified text only) }
  296.         if (pWE->alignment == weJustify) 
  297.         {
  298.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  299.                 *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
  300.                 pLine->lineJustAmount);
  301.         }
  302.         else
  303.         {
  304.             slop = 0;
  305.         }
  306.         
  307.         // { call CharToPixel to get width of segment up to specified offset }
  308. #ifdef WASTE_TABS
  309.         {
  310.             long    ii,
  311.                     beginChar;
  312.             
  313.             beginChar = 0;
  314.             width = 0;
  315.             for (ii = 0; ii < segmentLength && p->offset > segmentStart + ii; ii++)
  316.                 if (pSegment[ii] == '\t')
  317.                 {
  318.                     p->thePoint->h += NChar2Pixel(pSegment + beginChar, ii - beginChar, slop,
  319.                                                   p->offset - segmentStart - beginChar,
  320.                                                   smHilite, styleRunPosition,
  321.                                                   *(Point *)(&kOneToOneScaling),
  322.                                                   *(Point *)(&kOneToOneScaling));
  323.                     beginChar = ii + 1;
  324.                     p->thePoint->h = (p->thePoint->h / WASTE_TAB_SIZE + 1) * WASTE_TAB_SIZE;
  325.                 }
  326.             p->thePoint->h += NChar2Pixel(pSegment + beginChar, segmentLength - beginChar, slop,
  327.                                           p->offset - segmentStart - beginChar,
  328.                                           smHilite, styleRunPosition,
  329.                                           *(Point *)(&kOneToOneScaling),
  330.                                           *(Point *)(&kOneToOneScaling));
  331.         }
  332. #else
  333.         width = NChar2Pixel(pSegment, segmentLength, slop,
  334.             p->offset - segmentStart, smHilite, styleRunPosition,
  335.             *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling));
  336. #endif
  337.     }
  338.     
  339.     // { advance thePoint.h by the width of this segment }
  340.     p->thePoint->h = p->thePoint->h + width;
  341.  
  342.     // { drop out of loop when we reach offset }
  343.     return isInSegment;
  344. }
  345.  
  346. pascal void WEGetPoint(long offset, LongPt *thePoint, short *lineHeight, WEHandle hWE)
  347. {
  348.     // { given a byte offset into the text, find the corresponding glyph position }
  349.     // { this routine is useful for highlighting the text and for positioning the caret }
  350.  
  351.     WEPtr pWE;
  352.     LinePeek pLine;
  353.     long lineIndex;
  354.     Boolean saveWELock;
  355.     struct SLCharToPixelData callbackData;    
  356.     
  357.     // { lock the WE record }
  358.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  359.     pWE = *hWE;
  360.  
  361.     // { the base point is the top left corner of the destination rectangle }
  362.     *thePoint = *(LongPt *)&pWE->destRect.top;
  363.  
  364.     // { first of all find the line on which the glyph lies }
  365.     lineIndex = WEOffsetToLine(offset, hWE);
  366.  
  367.     // { calculate the vertical coordinate and the line height }
  368.     pLine = (LinePeek)&((*pWE->hLines)[lineIndex]);
  369.     thePoint->v = thePoint->v + pLine->first.lineOrigin;
  370.     *lineHeight = pLine->second.lineOrigin - pLine->first.lineOrigin;
  371.  
  372.     if ((offset == pWE->textLength) && (WEGetChar(offset - 1, hWE) == 0xd /* '\r' */)) 
  373.     {
  374.         // { SPECIAL CASE: if offset is past the last character and }
  375.         // { the last character is a carriage return, return a point below the last line }
  376.  
  377.         thePoint->v = thePoint->v + *lineHeight;
  378.         thePoint->h = thePoint->h + _WECalcPenIndent(pWE->destRect.right - pWE->destRect.left, pWE->alignment);
  379.     }
  380.     else
  381.     {
  382.         callbackData.pWE = pWE;
  383.         callbackData.offset = offset;
  384.         callbackData.thePoint = thePoint;
  385.         // { to get the horizontal coordinate, walk through the style runs on this line }
  386.         _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, (void *)&callbackData, hWE);
  387.     }
  388.  
  389.     // { pin the horizontal coordinate to the destination rectangle }
  390.     thePoint->h = _WEPinInRange(thePoint->h, pWE->destRect.left, pWE->destRect.right);
  391.  
  392.     // { unlock the WE record }
  393.     _WESetHandleLock((Handle)hWE, saveWELock);
  394. }
  395.  
  396. pascal void WEFindLine(long offset, char edge, long *lineStart, long *lineEnd, WEHandle hWE)
  397. {
  398.     WEPtr pWE;
  399.     LineArrayPtr pLine;
  400.     
  401.     pWE = *hWE;
  402.     pLine = &(*pWE->hLines)[WEOffsetToLine(offset, hWE)];
  403.     *lineStart = pLine[0].lineStart;
  404.     *lineEnd = pLine[1].lineStart;
  405. }
  406.  
  407.  
  408. pascal long _WEGetLineStart(long lineNo, WEHandle hWE)
  409. {
  410.     WEPtr pWE;
  411.  
  412.     pWE = *hWE;
  413.     if (lineNo >= pWE->nLines)
  414.         lineNo = pWE->nLines - 1;
  415.     return ((*pWE->hLines)[lineNo].lineStart);
  416. }
  417.  
  418.  
  419. pascal short _WEGetContext(long offset, long *contextStart, long *contextEnd,
  420.                         WEHandle hWE)
  421. {
  422.     // { This function finds a range of characters ("context"), all belonging to the same script }
  423.     // { and centered around the specified offset. }
  424.     // { The function result is the ID of a font belonging to this script. }
  425.     // { Ideally, the context should consist of a whole script run, but in practice the returned }
  426.     // { context can be narrower, for performance and other reasons (see below) }
  427.  
  428.     long index, saveIndex, saveRunEnd;
  429.     WERunInfo runInfo;
  430.     ScriptCode script1, script2;
  431.     short retval;
  432.     
  433.     if (BTST((*hWE)->flags, weFNonRoman))
  434.     { 
  435.         // { if more than one script is installed, limit the search of script run boundaries }
  436.         // { to a single line, for speed's sake }
  437.         WEFindLine(offset, kLeadingEdge, contextStart, contextEnd, hWE);
  438.  
  439.         // { find the style run the specified offset is in }
  440.         index = _WEOffsetToRun(offset, hWE);
  441.         _WEGetIndStyle(index, &runInfo, hWE);
  442.  
  443.         // { return the style run font as function result }
  444.         retval = runInfo.runAttrs.runStyle.tsFont;
  445.  
  446.         // { find the script code associated with this style run }
  447.         script1 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  448.  
  449.         // { save index and runInfo.runEnd for the second while loop }
  450.         saveIndex = index;
  451.         saveRunEnd = runInfo.runEnd;
  452.  
  453.         // { walk backwards across style runs preceding offset, looking for a script run boundary }
  454.         while (runInfo.runStart > *contextStart)
  455.         {
  456.             index = index - 1;
  457.             _WEGetIndStyle(index, &runInfo, hWE);
  458.             script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  459.             if (script1 != script2) 
  460.             {
  461.                 *contextStart = runInfo.runEnd;
  462.                 break;
  463.             }
  464.         }
  465.  
  466.         // { restore index and runInfo.runEnd }
  467.         index = saveIndex;
  468.         runInfo.runEnd = saveRunEnd;
  469.  
  470.         // { walk forward across style runs following offset, looking for a script run boundary }
  471.         while (runInfo.runEnd < *contextEnd)
  472.         {
  473.             index = index + 1;
  474.             _WEGetIndStyle(index, &runInfo, hWE);
  475.             script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
  476.             if (script1 != script2) 
  477.             {
  478.                 *contextEnd = runInfo.runStart;
  479.                 break;
  480.             }
  481.         }
  482.     }
  483.     else
  484.     {
  485.         // { only the Roman script is enabled: the whole text constitutes one script run }
  486.         retval = systemFont;
  487.         *contextStart = 0;
  488.         *contextEnd = (*hWE)->textLength;
  489.     }
  490.  
  491.     // { make sure the range identified by contextStart/contextEnd is contained within }
  492.     // { the 32K byte range centered around the specified offset }
  493.     // { the reason for this is that many Script Manager routines (e.g. FindWord and CharByte) }
  494.     // { only accept 16-bit offsets, rather than 32-bit offsets }
  495.  
  496.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  497.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  498.  
  499.     return retval;
  500. }
  501.  
  502. pascal short _WEGetRestrictedContext(long offset, long *contextStart, long *contextEnd,
  503.                         WEHandle hWE)
  504. {
  505.     // { This function finds a range of characters ("context"), all belonging to the same script }
  506.     // { and centered around the specified offset. }
  507.     // { This function returns a script run subrange and is more efficient than }
  508.     // { _WEGetContext because it doesn't try to find the script boundaries accurately. }
  509.  
  510.     WERunInfo runInfo;
  511.  
  512.     // { just find the style run the specified offset is in }
  513.     WEGetRunInfo(offset, &runInfo, hWE);
  514.     *contextStart = runInfo.runStart;
  515.     *contextEnd = runInfo.runEnd;
  516.  
  517.     // { make sure the range identified by contextStart/contextEnd is contained within }
  518.     // { the 32K byte range centered around the specified offset }
  519.     // { the reason for this is that many Script Manager routines (e.g. FindWord and CharByte) }
  520.     // { only accept 16-bit offsets, rather than 32-bit offsets }
  521.  
  522.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  523.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  524.  
  525.     return runInfo.runAttrs.runStyle.tsFont;
  526. } //  { _WEGetRestrictedContext }
  527.  
  528. pascal void WEFindWord(long offset, char edge, long *wordStart, long *wordEnd, WEHandle hWE)
  529. {
  530.     WEPtr pWE;
  531.     long contextStart, contextEnd;
  532.     Handle hText;
  533.     OffsetTable wordBreaks;
  534.     short saveFont;
  535.     GrafPtr savePort;
  536.     Boolean saveTextLock;
  537.  
  538.     pWE = *hWE;
  539.     hText = pWE->hText;
  540.  
  541.     // { set up the port }
  542.     GetPort(&savePort);
  543.     SetPort(pWE->port);
  544.  
  545.     // { find a script context containing the specified offset }
  546.     // { (words cannot straddle script boundaries) }
  547.     // { and set the port font to the corresponding script font }
  548.  
  549.     saveFont = pWE->port->txFont;
  550.     TextFont(_WEGetContext(offset, &contextStart, &contextEnd, hWE));
  551.  
  552.     // { lock the text }
  553.     saveTextLock = _WESetHandleLock(hText, true);
  554.  
  555.     // { find word breaks }
  556.     FindWord(*hText + contextStart, contextEnd - contextStart, offset - contextStart,
  557.         (Boolean)edge, nil, wordBreaks);
  558.  
  559.     // { unlock the text }
  560.     _WESetHandleLock(hText, saveTextLock);
  561.  
  562.     // { restore font and port }
  563.     TextFont(saveFont);
  564.     SetPort(savePort);
  565.  
  566.     // { calculate wordStart and wordEnd relative to the beginning of the text }
  567.     *wordStart = contextStart + wordBreaks[0].offFirst;
  568.     *wordEnd = contextStart + wordBreaks[0].offSecond;
  569. }
  570.  
  571. pascal short WECharByte(long offset, WEHandle hWE)
  572. {
  573.     WEPtr pWE;
  574.     long contextStart, contextEnd;
  575.     short saveFont;
  576.     GrafPtr savePort;
  577.     short retval;
  578.     
  579.     retval = smSingleByte;
  580.     pWE = *hWE;
  581.  
  582.     // { exit now if there is no double-byte script system installed }
  583.     if (!BTST(pWE->flags, weFDoubleByte))
  584.         return retval;
  585.  
  586.     // { sanity check: make sure offset is within allowed bounds }
  587.     if ((offset < 0) || (offset >= pWE->textLength))
  588.         return retval;
  589.  
  590.     // { save the port }
  591.     GetPort(&savePort);
  592.     SetPort(pWE->port);
  593.  
  594.     // { find a script context containing the specified offset }
  595.     // { and set the port font to the corresponding script font }
  596.     saveFont = pWE->port->txFont;
  597.     TextFont(_WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE));
  598.  
  599.     // { pass CharByte a pointer to the beginning of the style run }
  600.     // { CharByte is guaranteed not to move memory, so the text needn't be locked }
  601.     retval = CharByte(*pWE->hText + contextStart, offset - contextStart);
  602.  
  603.     // { restore the port font }
  604.     TextFont(saveFont);
  605.  
  606.     // { restore the port }
  607.     SetPort(savePort);
  608.  
  609.     return retval;
  610. } // { WECharByte }
  611.  
  612. pascal short WECharType(long offset, WEHandle hWE)
  613. {
  614.     WEPtr pWE;
  615.     long contextStart, contextEnd;
  616.     Handle hText;
  617.     short saveFont;
  618.     GrafPtr savePort;
  619.     Boolean saveTextLock;
  620.     short retval;
  621.     
  622.     retval = 0;
  623.     pWE = *hWE;
  624.     hText = pWE->hText;
  625.  
  626.     // { sanity check: make sure offset is within allowed bounds }
  627.     if ((offset < 0) || (offset >= pWE->textLength))
  628.         return retval;
  629.  
  630.     // { save the port }
  631.     GetPort(&savePort);
  632.     SetPort(pWE->port);
  633.  
  634.     // { find a script context containing the specified offset }
  635.     // { and set the port font to the corresponding script font }
  636.     saveFont = pWE->port->txFont;
  637.     TextFont(_WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE));
  638.  
  639.     // { lock the text (CharType may move memory) }
  640.     saveTextLock = _WESetHandleLock(hText, true);
  641.  
  642.     // { pass CharType a pointer to the beginning of the style run }
  643.     retval = CharType(*hText + contextStart, offset - contextStart);
  644.  
  645.     // { unlock the text }
  646.     _WESetHandleLock(hText, saveTextLock);
  647.  
  648.     // { restore the port font }
  649.     TextFont(saveFont);
  650.  
  651.     // { restore the port }
  652.     SetPort(savePort);
  653.  
  654.     return retval;
  655. } // { WECharType }
  656.  
  657.  
  658. pascal void _WEDrawCaret(long offset, WEHandle hWE)
  659. {
  660.     WEPtr pWE;
  661.     LongPt thePoint;
  662.     Rect caretRect;
  663.     short caretHeight;
  664.     GrafPtr savePort;
  665.     RgnHandle saveClip;
  666.  
  667.     // { the WE record must be already locked }
  668.     pWE = *hWE;
  669.  
  670.     // { find the caret position using WEGetPoint }
  671.     WEGetPoint(offset, &thePoint, &caretHeight, hWE);
  672.     WELongPointToPoint(&thePoint, (Point *)&caretRect.top);
  673.     if (caretRect.left > pWE->destRect.left) 
  674.     {
  675.         caretRect.left = caretRect.left - 1;
  676.     }
  677.     
  678.     // { calculate caret rectangle }
  679.     caretRect.bottom = caretRect.top + caretHeight;
  680.     caretRect.right = caretRect.left + kCaretWidth;
  681.  
  682.     // { set up the port }
  683.     GetPort(&savePort);
  684.     SetPort(pWE->port);
  685.  
  686.     // { clip to the view region }
  687.     saveClip = NewRgn();
  688.     GetClip(saveClip);
  689.     SetClip(pWE->viewRgn);
  690.  
  691.     // { draw the caret }
  692.     InvertRect(&caretRect);
  693.  
  694.     // { restore the clip region }
  695.     SetClip(saveClip);
  696.     DisposeRgn(saveClip);
  697.  
  698.     // { restore the port }
  699.     SetPort(savePort);
  700. }
  701.  
  702. pascal void _WEBlinkCaret(WEHandle hWE)
  703. {
  704.     WEPtr pWE;
  705.  
  706.     // { the WE record must be already locked }
  707.     pWE = *hWE;
  708.  
  709.     // { do nothing if we're not active }
  710.     if (!BTST(pWE->flags, weFActive))
  711.         return;
  712.  
  713.     // { redraw the caret, in XOR mode }
  714.     _WEDrawCaret(pWE->selStart, hWE);
  715.  
  716.     // { keep track of the current caret visibility status }
  717.     pWE->flags = pWE->flags ^ BSL(1, weFCaretVisible);
  718.  
  719.     // { update caretTime }
  720.     pWE->caretTime = TickCount();
  721.  
  722. } // { _WEBlinkCaret }
  723.  
  724. pascal RgnHandle WEGetHiliteRgn(long rangeStart, long rangeEnd, WEHandle hWE)
  725. {
  726.     // { returns the hilite region corresponding to the specified range }
  727.     // { the caller is responsible for disposing of the returned region }
  728.     // { when it's finished with it }
  729.  
  730.     WEPtr pWE;
  731.     RgnHandle hiliteRgn;
  732.     LongRect selRect;
  733.     LongPt firstPoint, lastPoint;
  734.     short firstLineHeight, lastLineHeight;
  735.     Rect r;
  736.     GrafPtr savePort;
  737.     Boolean saveWELock;
  738.  
  739.     // { lock the WE record }
  740.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  741.     pWE = *hWE;
  742.  
  743.     // { set up the port }
  744.     GetPort(&savePort);
  745.     SetPort(pWE->port);
  746.  
  747.     // { make sure rangeStart comes before rangeEnd }
  748.     _WEReorder(&rangeStart, &rangeEnd);
  749.  
  750.     // { calculate pixel location corresponding to rangeStart }
  751.     WEGetPoint(rangeStart, &firstPoint, &firstLineHeight, hWE);
  752.  
  753.     // { calculate pixel location corresponding to rangeEnd }
  754.     WEGetPoint(rangeEnd, &lastPoint, &lastLineHeight, hWE);
  755.  
  756.     // { open a region: rects to be hilited will be accumulated in this }
  757.     OpenRgn();
  758.  
  759.     if (firstPoint.v == lastPoint.v) 
  760.     {
  761.         // { selection range encompasses only one line }
  762.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  763.         WELongRectToRect(&selRect, &r);
  764.         FrameRect(&r);
  765.     }
  766.     else
  767.     {
  768.         // { selection range encompasses more than one line }
  769.         // { hilite the first line }
  770.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, pWE->destRect.right, firstPoint.v + firstLineHeight);
  771.         WELongRectToRect(&selRect, &r);
  772.         FrameRect(&r);
  773.  
  774.         // { any lines between the first and the last one? }
  775.         if (firstPoint.v + firstLineHeight < lastPoint.v) 
  776.         {
  777.             // { hilite all the lines in-between }
  778.             WESetLongRect(&selRect, pWE->destRect.left, firstPoint.v + firstLineHeight, pWE->destRect.right, lastPoint.v);
  779.             WELongRectToRect(&selRect, &r);
  780.             FrameRect(&r);
  781.         }
  782.  
  783.         // { hilite the last line }
  784.         WESetLongRect(&selRect, pWE->destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  785.         WELongRectToRect(&selRect, &r);
  786.         FrameRect(&r);
  787.     }
  788.  
  789.     // { copy the accumulated region into a new region }
  790.     hiliteRgn = NewRgn();
  791.     CloseRgn(hiliteRgn);
  792.  
  793.     // { restrict this region to the view region }
  794.     SectRgn(hiliteRgn, pWE->viewRgn, hiliteRgn);
  795.  
  796.     // { restore the port }
  797.     SetPort(savePort);
  798.  
  799.     // { unlock the WE record }
  800.     _WESetHandleLock((Handle)hWE, saveWELock);
  801.  
  802.     // { return the hilite region }
  803.     return hiliteRgn;
  804. }
  805.  
  806. pascal void _WEHiliteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  807. {
  808.     WEPtr pWE;
  809.     RgnHandle saveClip, auxRgn, hiliteRgn;
  810.     PenState savePen;
  811.     GrafPtr savePort;
  812.  
  813.     // { the WE record must be already locked }
  814.     pWE = *hWE;
  815.  
  816.     // { do nothing if the specified range is empty }
  817.     if (rangeStart == rangeEnd) 
  818.     {
  819.         return;
  820.     }
  821.  
  822.     // { set up the port }
  823.     GetPort(&savePort);
  824.     SetPort(pWE->port);
  825.  
  826.     // { create auxiliary regions }
  827.     saveClip = NewRgn();
  828.     auxRgn = NewRgn();
  829.  
  830.     // { restrict the clip region to the view rectangle }
  831.     GetClip(saveClip);
  832.     SectRgn(saveClip, pWE->viewRgn, auxRgn);
  833.     SetClip(auxRgn);
  834.  
  835.     // { get the hilite region corresponding to the specified range }
  836.     hiliteRgn = WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
  837.  
  838.     // { hilite the region or frame it, depending on the setting of the active flag }
  839.     if (BTST(pWE->flags, weFActive))
  840.     {
  841.         ClearHiliteBit();
  842.         InvertRgn(hiliteRgn);
  843.     }
  844.     else if (BTST(pWE->flags, weFOutlineHilite)) 
  845.     {
  846.         GetPenState(&savePen);
  847.         PenNormal();
  848.         PenMode(patXor);
  849.         ClearHiliteBit();
  850.         FrameRgn(hiliteRgn);
  851.         SetPenState(&savePen);
  852.     }
  853.  
  854.     // { restore the clip region }
  855.     SetClip(saveClip);
  856.  
  857.     // { dispose of all regions }
  858.     DisposeRgn(saveClip);
  859.     DisposeRgn(auxRgn);
  860.     DisposeRgn(hiliteRgn);
  861.  
  862.     // { restore the port }
  863.     SetPort(savePort);
  864. }
  865.  
  866. pascal void WESetSelection(long selStart, long selEnd, WEHandle hWE)
  867. {
  868.     WEPtr pWE;
  869.     long oldSelStart, oldSelEnd;
  870.     Boolean saveWELock;
  871.  
  872.     // { lock the WE record }
  873.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  874.     pWE = *hWE;
  875.  
  876.     // { invalid the null style }
  877.     BCLR(pWE->flags, weFUseNullStyle);
  878.  
  879.     // { hide the caret if it's showing }
  880.     if (BTST(pWE->flags, weFCaretVisible))
  881.     {
  882.             _WEBlinkCaret(hWE);
  883.     }
  884.     
  885.     // { range-check parameters }
  886.     selStart = _WEPinInRange(selStart, 0, pWE->textLength);
  887.     selEnd = _WEPinInRange(selEnd, 0, pWE->textLength);
  888.  
  889.     // { set the weFAnchorIsEnd bit if selStart > selEnd,  reorder the endpoints }
  890.     if (selStart > selEnd) 
  891.     {
  892.         BSET(pWE->flags, weFAnchorIsEnd);
  893.     }
  894.     else
  895.     {
  896.         BCLR(pWE->flags, weFAnchorIsEnd);
  897.     }
  898.     _WEReorder(&selStart, &selEnd);
  899.  
  900.     // { get old selection range }
  901.     oldSelStart = pWE->selStart;
  902.     oldSelEnd = pWE->selEnd;
  903.  
  904.     // { set new selection range }
  905.     pWE->selStart = selStart;
  906.     pWE->selEnd = selEnd;
  907.  
  908.     // { REDRAW THE SELECTION }
  909.     // { skip this section if redrawing has been inhibited }
  910.     if (!BTST(pWE->flags, weFInhibitRecal))
  911.     {
  912.         // { if we're active, invert the exclusive-OR between the old range and the new range. }
  913.         // { if we're inactive, this optimization can't be used because of outline highlighting. }
  914.         if (BTST(pWE->flags, weFActive))
  915.         { 
  916.             _WEReorder(&oldSelStart, &selStart);
  917.             _WEReorder(&oldSelEnd, &selEnd);
  918.             _WEReorder(&oldSelEnd, &selStart);
  919.         }
  920.     
  921.         _WEHiliteRange(oldSelStart, oldSelEnd, hWE);
  922.         _WEHiliteRange(selStart, selEnd, hWE);
  923.     
  924.         if (!BTST(pWE->flags, weFMouseTracking))
  925.         {
  926.             // { redraw the caret immediately, if the selection range is empty }
  927.             if (pWE->selStart == pWE->selEnd)
  928.             { 
  929.                 _WEBlinkCaret(hWE);
  930.             }
  931.             // { clear clickCount, unless we're tracking the mouse }
  932.             pWE->clickCount = 0;
  933.     
  934.             // { scroll the selection into view, unless we're tracking the mouse }
  935.             WESelView(hWE);
  936.     
  937.         }
  938.     } // { if redrawing not inhibited }
  939.  
  940.     // { unlock the WE record }
  941.     _WESetHandleLock((Handle)hWE, saveWELock);
  942. }
  943.  
  944. pascal void WESetAlignment(char alignment, WEHandle hWE)
  945. {
  946.     if ((alignment >= weFlushLeft) && (alignment <= weJustify)) 
  947.     {
  948.         if (alignment != (*hWE)->alignment) 
  949.         {
  950.             (*hWE)->alignment = alignment;
  951.             WEUpdate(nil, hWE);
  952.         }
  953.     }
  954. }
  955.  
  956. pascal long _WEArrowOffset(short action, long offset, WEHandle hWE)
  957. {
  958.     // { given an action code (corresponding to a modifiers + arrow key combo) }
  959.     // { and an offset into the text, find the offset of the new caret position }
  960.  
  961.     LongPt thePoint;
  962.     long textLength, rangeStart, rangeEnd;
  963.     short lineHeight;
  964.     char edge;
  965.  
  966.     textLength = (*hWE)->textLength;
  967.     switch (action)
  968.     {
  969.         case kGoLeft: 
  970.             if (offset > 0)
  971.             {
  972.                 offset = offset - 1;
  973.                 if (WECharByte(offset, hWE) != smSingleByte) 
  974.                 {
  975.                     offset = offset - 1;
  976.                 }
  977.             }
  978.             break;
  979.             
  980.         case kGoRight: 
  981.             if (offset < textLength)
  982.             {
  983.                 if (WECharByte(offset, hWE) != smSingleByte) 
  984.                 {
  985.                     offset = offset + 1;
  986.                 }
  987.                 offset = offset + 1;
  988.             }
  989.             break;
  990.  
  991.         case kGoUp: 
  992.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  993.             thePoint.v = thePoint.v - 1;
  994.             offset = WEGetOffset(&thePoint, &edge, hWE);
  995.             break;
  996.  
  997.         case kGoDown: 
  998.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  999.             thePoint.v = thePoint.v + lineHeight;
  1000.             offset = WEGetOffset(&thePoint, &edge, hWE);
  1001.             break;
  1002.             
  1003.         case kGoWordStart: 
  1004.             WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  1005.             offset = rangeStart;
  1006.             break;
  1007.             
  1008.         case kGoWordEnd: 
  1009.             WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  1010.             offset = rangeEnd;
  1011.             break;
  1012.             
  1013.         case kGoTextStart: 
  1014.             offset = 0;
  1015.             break;
  1016.  
  1017.         case kGoTextEnd: 
  1018.             offset = textLength;
  1019.             break;
  1020.  
  1021.         case kGoLineStart: 
  1022.             WEFindLine(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  1023.             offset = rangeStart;
  1024.             break;
  1025.  
  1026.         case kGoLineEnd: 
  1027.             WEFindLine(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  1028.             offset = rangeEnd;
  1029.             if (offset < textLength) 
  1030.             {
  1031.                 offset = offset - 1;
  1032.                 if (WECharByte(offset, hWE) != smSingleByte) 
  1033.                 {
  1034.                             offset = offset - 1;
  1035.                 }
  1036.             }
  1037.             break;
  1038.             
  1039.         default:
  1040.             break;
  1041.     }
  1042.  
  1043.     return offset;
  1044. }
  1045.  
  1046. pascal void _WEDoArrowKey (short arrow, short modifiers, WEHandle hWE)
  1047. {
  1048.     // { this routine is called by WEKey to handle arrow keys }
  1049.     // { the WE record is guaranteed to be already locked }
  1050.  
  1051.     WEPtr pWE;
  1052.     short action;
  1053.     long selStart, selEnd;
  1054.     long caretLoc, anchor;
  1055.  
  1056.     pWE = *hWE;
  1057.  
  1058.     // { calculate the "action" parameter for _WEArrowOffset from arrow and modifiers }
  1059.     action = arrow - kArrowLeft;            // { possible range: 0..3 }
  1060.     if (modifiers & optionKey)
  1061.     {
  1062.         action = action + kOption;
  1063.     }
  1064.     if (modifiers & cmdKey)
  1065.     {
  1066.         action = action + kCommand;
  1067.     }
  1068.     
  1069.     // { get selection range }
  1070.     selStart = pWE->selStart;
  1071.     selEnd = pWE->selEnd;
  1072.  
  1073.     if ((modifiers & shiftKey) == 0) 
  1074.     {
  1075.         // { if selection range isn't empty, collapse it to one of the endpoints }
  1076.         if (selStart < selEnd) 
  1077.         {
  1078.             if ((arrow == kArrowLeft) || (arrow == kArrowUp)) 
  1079.             {
  1080.                 caretLoc = selStart;
  1081.             }
  1082.             else
  1083.             {
  1084.                 caretLoc = selEnd;
  1085.             }
  1086.         }
  1087.         else
  1088.         {
  1089.             // { otherwise move the insertion point }
  1090.             caretLoc = _WEArrowOffset(action, selStart, hWE);
  1091.         }
  1092.         
  1093.         // { set anchor to caretLoc, so new selection will be empty }
  1094.         anchor = caretLoc;
  1095.     }
  1096.     else
  1097.     {
  1098.         // { shift key was held down: extend the selection rather than replacing it }
  1099.         // { find out which selection boundary is the anchor and which is the free endpoint }
  1100.         if (BTST(pWE->flags, weFAnchorIsEnd)) 
  1101.         {
  1102.             anchor = selEnd;
  1103.             caretLoc = selStart;
  1104.         }
  1105.         else
  1106.         {
  1107.             anchor = selStart;
  1108.             caretLoc = selEnd;
  1109.         }
  1110.         
  1111.         // { move the free endpoint }
  1112.         caretLoc = _WEArrowOffset(action, caretLoc, hWE);
  1113.     }
  1114.  
  1115.     // { select the new selection }
  1116.     WESetSelection(anchor, caretLoc, hWE);
  1117. }
  1118.  
  1119. pascal Boolean WEAdjustCursor(Point mouseLoc, RgnHandle mouseRgn, WEHandle hWE)
  1120. {
  1121.     // { Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle. }
  1122.     // { MouseRgn should be either a valid region handle or NIL. }
  1123.     // { If mouseRgn is supplied (i.e., if it's not NIL), it is intersected with a region }
  1124.     // { in global coordinates within which the cursor is to retain its shape. }
  1125.     // { WEAdjustCursor returns TRUE if the cursor has been set. }
  1126.     // { Your application should set the cursor only if WEAdjustCursor returns FALSE. }
  1127.  
  1128.     WEPtr pWE;
  1129.     RgnHandle auxRgn, hiliteRgn;
  1130.     enum { kIBeam, kArrow} cursorType;
  1131.     Point portDelta;
  1132.     GrafPtr savePort;
  1133.     Boolean saveWELock;
  1134.     Boolean adjustCursor;
  1135.     Point zeroPoint = {0, 0};
  1136.  
  1137.     adjustCursor = false;
  1138.     cursorType = kIBeam;
  1139.  
  1140.     // { lock the WE record }
  1141.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1142.     pWE = *hWE;
  1143.  
  1144.     // { set up the port }
  1145.     GetPort(&savePort);
  1146.     SetPort(pWE->port);
  1147.  
  1148.     // { calculate delta between the local coordinate system and the global one }
  1149.     portDelta = zeroPoint;
  1150.     LocalToGlobal(&portDelta);
  1151.  
  1152.     // { calculate the visible portion of the view rectangle, in global coordinates }
  1153.     auxRgn = NewRgn();
  1154.     CopyRgn(pWE->viewRgn, auxRgn);
  1155.     SectRgn(auxRgn, pWE->port->visRgn, auxRgn);
  1156.     OffsetRgn(auxRgn, portDelta.h, portDelta.v);
  1157.  
  1158.     if (PtInRgn(mouseLoc, auxRgn)) 
  1159.     {
  1160.         // { mouse is within view rectangle: it's up to us to set the cursor }
  1161.         adjustCursor = true;
  1162.  
  1163.         // { if drag-and-drop is enabled, see if the mouse is within current selection }
  1164.         if (BTST(pWE->flags, weFDragAndDrop))
  1165.         {
  1166.             if (pWE->selStart < pWE->selEnd)
  1167.             {
  1168.             
  1169.                 // { get current hilite region in global coordinates }
  1170.                 hiliteRgn = WEGetHiliteRgn(pWE->selStart, pWE->selEnd, hWE);
  1171.                 OffsetRgn(hiliteRgn, portDelta.h, portDelta.v);
  1172.  
  1173.                 // { if mouse is within selection, set cursor to an arrow, else to an I-beam }
  1174.                 // { (actually, we still use an I-beam if less than DoubleTime ticks have elapsed }
  1175.                 // { since the last mouse click, so that the cursor doesn't turn into an arrow while }
  1176.                 // { triple-clicking + dragging a range of lines) }
  1177.  
  1178.                 if (PtInRgn(mouseLoc, hiliteRgn) && (TickCount() > pWE->clickTime + GetDblTime()))
  1179.                 {
  1180.                     cursorType = kArrow;                // { use arrow cursor }
  1181.                     CopyRgn(hiliteRgn, auxRgn);
  1182.                 }
  1183.                 else
  1184.                 {
  1185.                     DiffRgn(auxRgn, hiliteRgn, auxRgn);
  1186.                 }
  1187.                 
  1188.                 // { dispose of the hilite region }
  1189.                 DisposeRgn(hiliteRgn);
  1190.  
  1191.             } // { if drag-and-drop is enabled }
  1192.         }
  1193.         
  1194.         // { set the cursor }
  1195.         if (cursorType == kIBeam)
  1196.             SetCursor(*GetCursor(iBeamCursor));
  1197.         else
  1198.             SetCursor(&qd.arrow);
  1199.  
  1200.         // { set mouseRgn, if provided }
  1201.         if (mouseRgn != nil) 
  1202.         {
  1203.             SectRgn(mouseRgn, auxRgn, mouseRgn);
  1204.         }
  1205.     }
  1206.     else
  1207.     {
  1208.         // { mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn }
  1209.         if (mouseRgn != nil) 
  1210.         {
  1211.             DiffRgn(mouseRgn, auxRgn, mouseRgn);
  1212.         }
  1213.     }
  1214.     // { dispose of the temporary region }
  1215.     DisposeRgn(auxRgn);
  1216.  
  1217.     // { restore the port }
  1218.     SetPort(savePort);
  1219.  
  1220.     // { unlock the WE record }
  1221.     _WESetHandleLock((Handle)hWE, saveWELock);
  1222.     
  1223.     return adjustCursor;
  1224. }
  1225.  
  1226. pascal void WEIdle(long *maxSleep, WEHandle hWE)
  1227. {
  1228.     WEPtr pWE;
  1229.     long caretInterval, sleepTime;
  1230.     Boolean saveWELock;
  1231.  
  1232.     // { lock the WE record }
  1233.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1234.     pWE = *hWE;
  1235.  
  1236.     // { the caret blinks only if we're active and the selection point is empty }
  1237.     if (BTST(pWE->flags, weFActive) && (pWE->selStart == pWE->selEnd)) 
  1238.     {
  1239.         // { the low-memory global variable CaretTime contains the preferred interval }
  1240.         // { between successive inversions of the caret }
  1241.         caretInterval = GetCaretTime();
  1242.  
  1243.         // { calculate how many ticks we can sleep before we need to invert the caret }
  1244.         // { the caretTime field of the WE record contains the time of the last inversion }
  1245.         sleepTime = caretInterval - (TickCount() - pWE->caretTime);
  1246.  
  1247.         // { if sleepTime has gone negative, invert the caret }
  1248.         if (sleepTime <= 0) 
  1249.         {
  1250.             _WEBlinkCaret(hWE);
  1251.             sleepTime = caretInterval;
  1252.         }
  1253.     }
  1254.     else
  1255.     {
  1256.         // { if we don't need to blink the caret, we can sleep forever }
  1257.         sleepTime = LONG_MAX;
  1258.     }
  1259.     
  1260.     // { return sleepTime to the caller if maxSleep isn't NIL }
  1261.     if (maxSleep != nil)
  1262.     {
  1263.         *maxSleep = sleepTime;
  1264.     }
  1265.     
  1266.     // { unlock the WE record }
  1267.     _WESetHandleLock((Handle)hWE, saveWELock);
  1268. }
  1269.  
  1270. pascal void WEUpdate(RgnHandle updateRgn, WEHandle hWE)
  1271. {
  1272.     WEPtr pWE;
  1273.     long firstLine, lastLine;
  1274.     Rect auxRect;
  1275.     RgnHandle saveClip, auxRgn;
  1276.     GrafPtr savePort;
  1277.     Boolean saveWELock;
  1278.  
  1279.     // { lock the WE record }
  1280.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1281.     pWE = *hWE;
  1282.  
  1283.     // { set up the port }
  1284.     GetPort(&savePort);
  1285.     SetPort(pWE->port);
  1286.  
  1287.     // { save the clip region }
  1288.     saveClip = NewRgn();
  1289.     GetClip(saveClip);
  1290.  
  1291.     // { clip to the insersection between updateRgn and the view rectangle }
  1292.     // { (updateRgn may be NIL; in this case, just clip to the view rectangle) }
  1293.     auxRgn = NewRgn();
  1294.     if (updateRgn != nil) 
  1295.     {
  1296.         SectRgn(updateRgn, pWE->viewRgn, auxRgn);
  1297.     }
  1298.     else
  1299.     {
  1300.         CopyRgn(pWE->viewRgn, auxRgn);
  1301.     }
  1302.     SetClip(auxRgn);
  1303.  
  1304.     if (EmptyRgn(auxRgn) == false) 
  1305.     {
  1306.         // { set auxRect to the bounding box of the update region (clipped to the view rectangle) }
  1307.         auxRect = (*auxRgn)->rgnBBox;
  1308.  
  1309.         // { find out which lines need to be redrawn }
  1310.         firstLine = _WEPixelToLine(auxRect.top - pWE->destRect.top, hWE);
  1311.         lastLine = _WEPixelToLine((auxRect.bottom - 1) - pWE->destRect.top, hWE);
  1312.  
  1313.         // { draw them (if updateRgn is NIL, erase each line rectangle before redrawing) }
  1314.         _WEDrawLines(firstLine, lastLine, (updateRgn == nil), hWE);
  1315.  
  1316.         // { hilite the selection range or draw the caret (only if active) }
  1317.         if (pWE->selStart < pWE->selEnd) 
  1318.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1319.         else if (BTST(pWE->flags, weFCaretVisible)) 
  1320.         {
  1321.             _WEBlinkCaret(hWE);
  1322.             BSET(pWE->flags, weFCaretVisible);
  1323.         }
  1324.     }
  1325.  
  1326.     DisposeRgn(auxRgn);
  1327.  
  1328.     // { restore the clip region }
  1329.     SetClip(saveClip);
  1330.     DisposeRgn(saveClip);
  1331.  
  1332.     // { restore the port }
  1333.     SetPort(savePort);
  1334.  
  1335.     // { unlock the WE record }
  1336.     _WESetHandleLock((Handle)hWE, saveWELock);
  1337. }
  1338.  
  1339. pascal void WEDeactivate(WEHandle hWE)
  1340. {
  1341.     WEPtr pWE;
  1342.     Boolean saveWELock;
  1343.  
  1344.     if (!WEIsActive(hWE)) return;
  1345.  
  1346.     // { lock the WE record }
  1347.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1348.     pWE = *hWE;
  1349.  
  1350.     // { hide the selection range or the caret }
  1351.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1352.     if (BTST(pWE->flags, weFCaretVisible))
  1353.     {
  1354.         _WEBlinkCaret(hWE);
  1355.     }
  1356.     
  1357.     // { clear the active flag }
  1358.     BCLR(pWE->flags, weFActive);
  1359.  
  1360.     // { frame the selection }
  1361.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1362.  
  1363.     // { dispose of the offscreen graphics world, if any }
  1364.     if (pWE->offscreenPort != nil) 
  1365.     {
  1366.         DisposeGWorld((GWorldPtr)(pWE->offscreenPort));
  1367.         pWE->offscreenPort = nil;
  1368.     }
  1369.  
  1370.     // { notify Text Services }
  1371.     if (pWE->tsmReference != nil) 
  1372.     {
  1373.         DeactivateTSMDocument(pWE->tsmReference);
  1374.     }
  1375.     
  1376.     // { unlock the WE record }
  1377.     _WESetHandleLock((Handle)hWE, saveWELock);
  1378. }
  1379.  
  1380. pascal void WEActivate(WEHandle hWE)
  1381. {
  1382.     WEPtr pWE;
  1383.     Boolean saveWELock;
  1384.  
  1385.     if (WEIsActive(hWE)) return;
  1386.  
  1387.     // { lock the WE record }
  1388.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1389.     pWE = *hWE;
  1390.  
  1391.     // { remove the selection frame }
  1392.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1393.  
  1394.     // { set the active flag }
  1395.     BSET(pWE->flags, weFActive);
  1396.  
  1397.     // { show the selection range }
  1398.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1399.  
  1400.     // { notify Text Services }
  1401.     if (pWE->tsmReference != nil) 
  1402.     {
  1403.         ActivateTSMDocument(pWE->tsmReference);
  1404.     }
  1405.     
  1406.     // { unlock the WE record }
  1407.     _WESetHandleLock((Handle)hWE, saveWELock);
  1408. }
  1409.  
  1410. pascal Boolean WEIsActive(WEHandle hWE)
  1411. {
  1412.     // { return TRUE iff the specified WE instance is currently active }
  1413.     return BTST((*hWE)->flags, weFActive);
  1414. }
  1415.  
  1416. pascal void WEScroll(long hOffset, long vOffset, WEHandle hWE)
  1417. {
  1418.     WEPtr pWE;
  1419.     Rect viewRect;
  1420.     RgnHandle updateRgn;
  1421.     GrafPtr savePort;
  1422.     Boolean hideOutline, saveWELock;
  1423.  
  1424.     // { do nothing if both scroll offsets are zero }
  1425.     if ((hOffset == 0) && (vOffset == 0))
  1426.         return;
  1427.  
  1428.     // { lock the WE record }
  1429.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1430.     pWE = *hWE;
  1431.  
  1432.     // { hide the caret if it's showing }
  1433.     if (BTST(pWE->flags, weFCaretVisible))
  1434.     {
  1435.         _WEBlinkCaret(hWE);
  1436.     }
  1437.     
  1438. #ifdef WEPINSCROLL
  1439.     // CKT Sep 12 94 Begin - Added PinScroll behavior
  1440.     if(vOffset > 0){
  1441.         // if top of the destRect would be moved below top of the viewRect
  1442.         if(pWE->destRect.top + vOffset > pWE->viewRect.top){
  1443.             vOffset += -((pWE->destRect.top + vOffset) - pWE->viewRect.top);
  1444.         }
  1445.     }else if(vOffset < 0){
  1446.         //if bottom of the destRect would be moved above bottom of the viewRect
  1447.         if(pWE->destRect.bottom + vOffset < pWE->viewRect.bottom){
  1448.             vOffset +=  -((pWE->destRect.bottom + vOffset) - pWE->viewRect.bottom);
  1449.         }
  1450.     }
  1451.     // CKT Sep 12 94 End
  1452. #endif
  1453.  
  1454.     // { set up the port }
  1455.     GetPort(&savePort);
  1456.     SetPort(pWE->port);
  1457.  
  1458.     // { if we're inactive and outline highlighting is on, we have to temporarily }
  1459.     // { hide the selection outline while scrolling to avoid a cosmetic bug }
  1460.     hideOutline = false;
  1461.     if (!BTST(pWE->flags, weFActive))
  1462.         if (BTST(pWE->flags, weFOutlineHilite))
  1463.         {
  1464.             hideOutline = true;
  1465.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1466.             BCLR(pWE->flags, weFOutlineHilite);
  1467.         }
  1468.  
  1469.     // { if we're currently tracking a drag, notify the Drag Manager we're about to scroll }
  1470.     if (pWE->currentDrag != kNullDrag)
  1471.         DragPreScroll(pWE->currentDrag, hOffset, vOffset);
  1472.     
  1473.     viewRect = (*pWE->viewRgn)->rgnBBox;
  1474.     updateRgn = NewRgn();
  1475.  
  1476.     // { offset the destination rectangle by the specified amount }
  1477.     WEOffsetLongRect(&pWE->destRect, hOffset, vOffset);
  1478.  
  1479.     // { scroll the view rectangle }
  1480.     ScrollRect(&viewRect, hOffset, vOffset, updateRgn);
  1481.  
  1482.     // { redraw the exposed region }
  1483.     WEUpdate(updateRgn, hWE);
  1484.     DisposeRgn(updateRgn);
  1485.  
  1486.     // { notify the Drag Manager }
  1487.     if (pWE->currentDrag != kNullDrag)
  1488.         DragPostScroll(pWE->currentDrag);
  1489.  
  1490.     // { redraw the selection outline, if hidden }
  1491.     if (hideOutline)
  1492.     {
  1493.         BSET(pWE->flags, weFOutlineHilite);
  1494.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1495.     }
  1496.  
  1497.     // { restore the port }
  1498.     SetPort(savePort);
  1499.  
  1500.     // { unlock the WE record }
  1501.     _WESetHandleLock((Handle)hWE, saveWELock);
  1502. }
  1503.  
  1504. pascal Boolean _WEScrollIntoView (long offset, WEHandle hWE)
  1505. {
  1506.     WEPtr pWE;
  1507.     LongPt thePoint;
  1508.     short lineHeight;
  1509.     long hScroll, vScroll, temp;
  1510.     Boolean retval;
  1511.     
  1512.     pWE = *hWE;
  1513.  
  1514.     // { do nothing if automatic scrolling is disabled }
  1515.     if (!BTST(pWE->flags, weFAutoScroll)) 
  1516.     {
  1517.         return false;
  1518.     }
  1519.  
  1520.     // { find the selection point }
  1521.     WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  1522.  
  1523.     // { assume no scrolling is needed }
  1524.     retval = false;
  1525.     vScroll = 0;
  1526.     hScroll = 0;
  1527.  
  1528.     // { determine if we need to scroll vertically }
  1529.     if ((thePoint.v < pWE->viewRect.top) || 
  1530.         (thePoint.v + lineHeight >= pWE->viewRect.bottom))
  1531.     {
  1532.         // { calculate the amount of vertical scrolling needed to center the selection into view }
  1533.         vScroll = BSR(pWE->viewRect.top + pWE->viewRect.bottom, 1) - thePoint.v;
  1534.  
  1535.         // { we'd like to superimpose the bottom margins of the dest/view rects, if possible }
  1536.         temp = pWE->viewRect.bottom - pWE->destRect.bottom;
  1537.         if (temp > vScroll) 
  1538.         {
  1539.             vScroll = temp;
  1540.         }
  1541.         // { but we also have to make sure the dest top isn't scrolled below the view top }
  1542.         temp = pWE->viewRect.top - pWE->destRect.top;
  1543.         if (temp < vScroll) 
  1544.         {
  1545.             vScroll = temp;
  1546.         }
  1547.     }
  1548.     
  1549.     // { determine if we need to scroll horizontally }
  1550.     if ((thePoint.h - 1 < pWE->viewRect.left) || (thePoint.h >= pWE->viewRect.right))
  1551.     { 
  1552.         // { calculate the amount of horizontal scrolling needed to center the selection into view }
  1553.         hScroll = BSR(pWE->viewRect.left + pWE->viewRect.right, 1) - thePoint.h;
  1554.  
  1555.         // { we'd like to superimpose the right margins of the dest/view rects, if possible }
  1556.         temp = pWE->viewRect.right - pWE->destRect.right;
  1557.         if (temp > hScroll) 
  1558.         {
  1559.             hScroll = temp;
  1560.         }
  1561.         
  1562.         // { but we also have to make sure the dest left isn't scrolled to the right of the view left }
  1563.         temp = pWE->viewRect.left - pWE->destRect.left;
  1564.         if (temp < hScroll) 
  1565.         {
  1566.             hScroll = temp;
  1567.         }
  1568.     }
  1569.     
  1570.     // { scroll the text if necessary }
  1571.     if ((vScroll != 0) || (hScroll != 0)) 
  1572.     {
  1573.         retval = true;
  1574.         WEScroll(hScroll, vScroll, hWE);
  1575.     }
  1576.  
  1577.     // { call the scroll callback, if any }
  1578.     if (pWE->scrollProc != nil) 
  1579.     {
  1580.         ((WEScrollProcPtr)pWE->scrollProc)(hWE);
  1581.     }
  1582.     return retval;
  1583. }
  1584.  
  1585. pascal void WESelView(WEHandle hWE)
  1586. {
  1587.     WEPtr pWE;
  1588.     long offset;
  1589.     Boolean saveWELock;
  1590.  
  1591.     // { lock the WE record }
  1592.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  1593.     pWE = *hWE;
  1594.  
  1595.     // { scroll the free endpoint of the selection into view }
  1596.     if (BTST(pWE->flags, weFAnchorIsEnd)) 
  1597.     {
  1598.         offset = pWE->selStart;
  1599.     }
  1600.     else
  1601.     {
  1602.         offset = pWE->selEnd;
  1603.     }
  1604.     _WEScrollIntoView(offset, hWE);
  1605.  
  1606.     // { unlock the WE record }
  1607.     _WESetHandleLock((Handle)hWE, saveWELock);
  1608. }
  1609.