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

  1. // { WASTE PROJECT: }
  2. // { Low-Level Editing Routines }
  3.  
  4. // { Copyright © 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. #include "WASTEIntf.h"
  8.  
  9. pascal Boolean _WEIsWordRange(long rangeStart, long rangeEnd, WEHandle hWE)
  10. {
  11.     // { _WEIsWordRange returns TRUE if the specified range is a word range, }
  12.     // { i.e. if it would be possible to select it by double-clicking and (optionally) dragging. }
  13.  
  14.     long wordStart, wordEnd;
  15.  
  16.     // { determine if rangeStart is at the beginning of a word }
  17.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  18.     if (rangeStart == wordStart) 
  19.     {
  20.  
  21.         // { determine if rangeEnd is at the end of a word }
  22.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  23.         return (rangeEnd == wordEnd);
  24.     }
  25.     return false;
  26. } // { _WEIsWordRange }
  27.  
  28. pascal Boolean _WEIsPunct(long offset, WEHandle hWE)
  29. {
  30.     short cType;
  31.     
  32.     cType = WECharType(offset, hWE);
  33.     if ((cType & smcTypeMask) == smCharPunct) 
  34.     {
  35.         cType = cType & smcClassMask;
  36.         if (cType == smPunctNormal || cType == smPunctBlank) 
  37.             return true;
  38.     }
  39.     return false;
  40. }  // { _WEIsPunct }
  41.  
  42. pascal void _WEIntelligentCut(long *rangeStart, long *rangeEnd, WEHandle hWE)
  43. {
  44.  
  45.     // { _WEIntelligentCut is called by other WASTE routines to determine the actual }
  46.     // { range to be deleted when weFIntCutAndPaste is enabled. }
  47.     // { On entry, rangeStart and rangeEnd specify the selection range visible to the user. }
  48.     // { On exit, rangeStart and rangeEnd specify the actual range to be removed. }
  49.  
  50.     // { do nothing if the intelligent cut-and-paste feature is disabled }
  51.     if (!BTST((*hWE)->flags, weFIntCutAndPaste)) 
  52.         return;
  53.  
  54.     // { intelling cut-&-paste rules should be applied only to word ranges... }
  55.     if (_WEIsWordRange(*rangeStart, *rangeEnd, hWE) == false) 
  56.         return;
  57.  
  58.     // { ...without punctuation characters at the beginning or end }
  59.     if (_WEIsPunct(*rangeStart, hWE)) 
  60.         return;
  61.     if (_WEIsPunct(*rangeEnd - 1, hWE)) 
  62.         return;
  63.  
  64.     // { if the character preceding the selection range is a space, discard it }
  65.     if (WEGetChar(*rangeStart - 1, hWE) == ' ') 
  66.         *rangeStart = *rangeStart - 1;
  67.     // { else, if the character following the selection range is a space, discard it }
  68.     else if (WEGetChar(*rangeEnd, hWE) == ' ')
  69.         *rangeEnd = *rangeEnd + 1;
  70.  
  71. } // { _WEIntelligentCut }
  72.  
  73. pascal short _WEIntelligentPaste(long rangeStart, long rangeEnd, WEHandle hWE)
  74. {
  75.     short retval;
  76.     
  77.     // { _WEIntelligentPaste is called by other WASTE routines to determine whether }
  78.     // { an additional space character should be added (before or after) after inserting }
  79.     // { new text (usually from the Clipboard or from a drag). }
  80.  
  81.     retval = weDontAddSpaces;
  82.  
  83.     // { do nothing unless the intelligent cut-and-paste feature is enabled }
  84.     if (!BTST((*hWE)->flags, weFIntCutAndPaste)) 
  85.         return retval;
  86.  
  87.     // { extra spaces will be added only if the pasted text looks like a word range, }
  88.     // { without punctuation characters at the beginning or at the end }
  89.     if (_WEIsPunct(rangeStart, hWE))
  90.         return retval;
  91.     if (_WEIsPunct(rangeEnd - 1, hWE))
  92.         return retval;
  93.  
  94.     // { if the character on the left of the pasted text is a punctuation character }
  95.     // { and the character on the right isn't,  add a space on the right, and vice versa }
  96.     if (_WEIsPunct(rangeStart - 1, hWE))
  97.     {
  98.         if (_WEIsPunct(rangeEnd, hWE) == false) 
  99.             retval = weAddSpaceOnRightSide;
  100.     }
  101.     else if (_WEIsPunct(rangeEnd, hWE))
  102.         retval = weAddSpaceOnLeftSide;
  103.  
  104.     return retval;
  105. } // { _WEIntelligentPaste }
  106.  
  107. pascal OSErr _WEInsertRun(long runIndex, long offset, long styleIndex, WEPtr pWE)
  108. {
  109.  
  110.     // { Insert a new element in the style run array, at the specified runIndex position. }
  111.     // { The new element consists of the pair <offset, styleIndex>. }
  112.  
  113.     RunArrayElement element;
  114.     OSErr err;
  115.  
  116.     // { prepare the element record to be inserted in the array }
  117.     element.runStart = offset;
  118.     element.styleIndex = styleIndex;
  119.  
  120.     // { do the insertion }
  121.     err = _WEInsertSlot((Handle)pWE->hRuns, (Ptr)&element, runIndex + 1, sizeof(element));
  122.     if (err != noErr) 
  123.         return err;
  124.  
  125.     // { increment style run count }
  126.     pWE->nRuns = pWE->nRuns + 1;
  127.  
  128.     // { increment the reference count field of the style table element }
  129.     // { referenced by the newly inserted style run }
  130.     (*pWE->hStyles)[styleIndex].refCount = (*pWE->hStyles)[styleIndex].refCount + 1;
  131.  
  132.     return noErr;
  133. } // { _WEInsertRun }
  134.  
  135. pascal OSErr _WERemoveRun(long runIndex, WEPtr pWE)
  136. {
  137.     // { remove the specified element from the style run array }
  138.  
  139.     long styleIndex;
  140.     OSErr retval;
  141.     
  142.     styleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  143.  
  144.     // { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  145.     retval = _WERemoveSlot((Handle)pWE->hRuns, runIndex, sizeof(RunArrayElement));
  146.  
  147.     // { decrement style run count }
  148.     pWE->nRuns = pWE->nRuns - 1;
  149.  
  150.     // { decrement the reference count field of the style table element }
  151.     // { that was referenced by the style run we have just removed }
  152.     (*pWE->hStyles)[styleIndex].refCount =(*pWE->hStyles)[styleIndex]. refCount - 1;
  153.  
  154.     // { dispose of embedded object, if any }
  155.     if ((*pWE->hStyles)[styleIndex].refCount == 0) 
  156.         if (_WEFreeObject((WEObjectDescHandle)((*pWE->hStyles)[styleIndex].info.runStyle.tsObject))
  157.             != noErr) 
  158.             { ; }
  159.     
  160.     return retval;
  161. } // { _WERemoveRun }
  162.  
  163. pascal void _WEChangeRun(long runIndex, long newStyleIndex, Boolean keepOld, WEPtr pWE)
  164. {
  165.  
  166.     // { change the styleIndex field of the specified element of the style run array }
  167.  
  168.     long oldStyleIndex;
  169.     WEObjectDescHandle hObjectDesc;
  170.  
  171.     // { do the change }
  172.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  173.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  174.     
  175.     // { increment the reference count field of the new style table element }
  176.     (*pWE->hStyles)[newStyleIndex].refCount = (*pWE->hStyles)[newStyleIndex].refCount + 1;
  177.     hObjectDesc = (WEObjectDescHandle)((*pWE->hStyles)[newStyleIndex].info.runStyle.tsObject);
  178.     
  179.     // { decrement the reference count field of the old style table element }
  180.     (*pWE->hStyles)[oldStyleIndex].refCount = (*pWE->hStyles)[oldStyleIndex].refCount - 1;
  181.  
  182.     // { dispose of embedded object, if any, unless it is again referenced in the new style }
  183.     if (((*pWE->hStyles)[oldStyleIndex].refCount == 0) && (keepOld == false))
  184.         if ((WEObjectDescHandle)((*pWE->hStyles)[oldStyleIndex].info.runStyle.tsObject) != hObjectDesc) 
  185.             if (_WEFreeObject((WEObjectDescHandle)((*pWE->hStyles)[oldStyleIndex].info.runStyle.tsObject)) != noErr) 
  186.                 { ; }
  187.  
  188. } // { _WEChangeRun }
  189.  
  190. pascal OSErr _WENewStyle(WERunAttributes *ts, long *styleIndex, WEPtr pWE)
  191. {
  192.  
  193.     // { given the specified WERunAttributes record, find the corresponding entry }
  194.     // { in the style table (create a new entry if necessary), and return its index }
  195.  
  196.  
  197.     StyleTablePtr pTable;
  198.     StyleTableElement element;
  199.     long index, unusedIndex;
  200.     OSErr err;
  201.  
  202.     // _WENewStyle = noErr;
  203.     pTable = *pWE->hStyles;
  204.  
  205.     // { see if the given style already exists in the style table }
  206.     // { while scanning the table, also remember the position of the first unused style, if any }
  207.     index = 0;
  208.     unusedIndex = -1;
  209.     while (index < pWE->nStyles)
  210.     {
  211.         // { perform a bitwise comparison between the current element and the specified style }
  212.         if (_WEBlockCmp((Ptr)&pTable[index].info, (Ptr)ts, sizeof(WERunAttributes)))
  213.         { 
  214.             *styleIndex = index;        // { found: style already present }
  215.             return noErr;
  216.         }
  217.  
  218.         // { check for entries which aren't referenced and can be recycled }
  219.         if (pTable[index].refCount == 0) 
  220.         {
  221.             unusedIndex = index;
  222.         }
  223.         index = index + 1;
  224.     } // { while }
  225.  
  226.     // { the specified style doesn't exist in the style table }
  227.     // { see if we can recycle an unused entry }
  228.     if (unusedIndex >= 0) 
  229.     {
  230.         index = unusedIndex;
  231.         pTable[index].info = *ts;
  232.     }
  233.     else
  234.     {
  235.         // { no reusable entry: we have to append a new element to the table }
  236.         element.refCount = 0;
  237.         element.info = *ts;
  238.         err = _WEInsertSlot((Handle)pWE->hStyles, (Ptr)&element, index, sizeof(element));
  239.         if (err != noErr) 
  240.         {
  241.             return err;
  242.         }
  243.  
  244.         // { update style count in the WE record }
  245.         pWE->nStyles = index + 1;
  246.     }
  247.  
  248.     // { return the index to the new element }
  249.     *styleIndex = index;
  250.  
  251.     return noErr;
  252. } // { _WENewStyle }
  253.  
  254. pascal OSErr _WERedraw(long rangeStart, long rangeEnd, WEHandle hWE)
  255. {
  256.     // { the WE record is guaranteed to be already locked }
  257.     WEPtr pWE;
  258.     LineArrayPtr pLines;
  259.     long startLine, endLine;
  260.     long oldTextHeight, newTextHeight;
  261.     LongRect r;
  262.     Rect viewRect, updateRect;
  263.     RgnHandle saveClip;
  264.     GrafPtr savePort;
  265.     OSErr err;
  266. #ifdef WEREDRAW_SPEED
  267.     LongRect scrollRect;
  268.     RgnHandle updateRgn,
  269.               utilRgn;
  270. #endif
  271.     
  272.     pWE = *hWE;
  273.  
  274.     // { do nothing if recalculation has been inhibited }
  275.     if (!BTST(pWE->flags, weFInhibitRecal)) 
  276.     {
  277.         // { hide the caret }
  278. #ifdef WEREDRAW_SPEED
  279.         BCLR(pWE->flags, weFCaretVisible);
  280. #else
  281.         if (BTST(pWE->flags, weFCaretVisible))
  282.         { 
  283.             _WEBlinkCaret(hWE);
  284.         }
  285. #endif
  286.  
  287.         // { remember total text height }
  288.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  289.  
  290.         // { find line range affected by modification }
  291.         startLine = WEOffsetToLine(rangeStart, hWE);
  292.         endLine = WEOffsetToLine(rangeEnd, hWE);
  293.  
  294.         // { recalculate line breaks starting from startLine }
  295.         err = _WERecalBreaks(&startLine, &endLine, hWE);
  296.         if (err != noErr) 
  297.         {
  298.             goto cleanup;
  299.         }
  300.  
  301.         // { recalculate slops }
  302.         _WERecalSlops(startLine, endLine, hWE);
  303.  
  304.         // { calculate new total text height }
  305.         newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  306.  
  307.         // { calculate the rectangle to redraw (in long coordinates) }
  308.         r.left = -SHRT_MAX;
  309.         r.right = SHRT_MAX;
  310.         pLines = *pWE->hLines;
  311.         r.top = pLines[startLine].lineOrigin;
  312.  
  313. #ifdef WEREDRAW_SPEED
  314.         // { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  315.         // { otherwise we must redraw all lines from startLine on }
  316.  
  317.         if (endLine < pWE->nLines - 1)
  318.              r.bottom = pLines[endLine + 1].lineOrigin;
  319.         else
  320.             r.bottom = newTextHeight;
  321.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  322.  
  323.         if (newTextHeight == oldTextHeight)
  324.             WELongRectToRect(&r, &updateRect);
  325.         else
  326.         {
  327.             /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  328.              *      use scroll bits to move the displayed text up.
  329.              */
  330.  
  331.             scrollRect = pWE->viewRect;
  332.             if (newTextHeight > oldTextHeight)
  333.                 scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  334.             else
  335.                 scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  336.             WELongRectToRect(&scrollRect, &updateRect);
  337.             updateRgn = NewRgn();
  338.             ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  339.  
  340.             /*      Redraw the exposed region (caused by a scroll up)       */
  341.  
  342.             WELongRectToRect(&r, &updateRect);
  343.             utilRgn = NewRgn();
  344.             RectRgn(utilRgn, &updateRect);
  345.             DiffRgn(updateRgn, utilRgn, updateRgn);
  346.             DisposeRgn(utilRgn);
  347.             WEUpdate(updateRgn, hWE);
  348.             DisposeRgn(updateRgn);
  349.         }
  350. #else
  351.         // { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  352.         // { otherwise we must redraw all lines from startLine on }
  353.         if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1)) 
  354.         {
  355.             r.bottom = pLines[endLine + 1].lineOrigin;
  356.         }
  357.         else if (newTextHeight < oldTextHeight) 
  358.         {
  359.             r.bottom = oldTextHeight;
  360.         }
  361.         else
  362.         {
  363.             r.bottom = newTextHeight;
  364.         }
  365.         
  366.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  367.  
  368.         // { calculate the intersection between this rectangle and the view rectangle }
  369.         WELongRectToRect(&r, &updateRect);
  370. #endif
  371.         WELongRectToRect(&pWE->viewRect, &viewRect);
  372.  
  373.         if (SectRect(&updateRect, &viewRect, &updateRect)) 
  374.         {
  375.             // { set up the port and the clip region }
  376.             GetPort(&savePort);
  377.             SetPort(pWE->port);
  378.  
  379.             // { set the clip region to updateRect }
  380.             saveClip = NewRgn();
  381.             GetClip(saveClip);
  382.             ClipRect(&updateRect);
  383.  
  384.             // { we only really need to redraw the visible lines }
  385.             startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  386.             endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  387.  
  388.             // { redraw the lines (pass TRUE in the doErase parameter) }
  389.             _WEDrawLines(startLine, endLine, true, hWE);
  390.  
  391.             // { erase the portion of the update rectangle below the last line (if any) }
  392.             pLines = *pWE->hLines;
  393.             updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  394.             if (updateRect.top < updateRect.bottom) 
  395.             {
  396.                 EraseRect(&updateRect);
  397.             }
  398.  
  399.             // { restore the clip region }
  400.             SetClip(saveClip);
  401.             DisposeRgn(saveClip);
  402.  
  403.             // { restore the port }
  404.             SetPort(savePort);
  405.  
  406.             // { redraw the caret or the selection range }
  407.             if (pWE->selStart < pWE->selEnd) 
  408.             {
  409.                 _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  410.             }
  411.             else
  412.             {
  413.                 _WEBlinkCaret(hWE);
  414.             }
  415.         } // { if SectRect }
  416.  
  417.         // { scroll the selection range into view }
  418.         WESelView(hWE);
  419.     } // { if recal not inhibited }
  420.  
  421.     // { clear result code }
  422.     err = noErr;
  423.  
  424. cleanup:
  425.     // { return result code }
  426.     return err;
  427. } // { _WERedraw }
  428.  
  429. pascal OSErr _WESetStyleRange(long rangeStart, long rangeEnd, short mode, WETextStyle *ts, WEHandle hWE)
  430. {
  431.     // { alter the style attributes of the specified text range according to ts and mode }
  432.     // { the WE record is guaranteed to be already locked }
  433.  
  434.     WEPtr pWE;
  435.     RunArrayHandle hRuns;
  436.     long offset;
  437.     long runIndex;
  438.     long oldStyleIndex, newStyleIndex;
  439.     WERunInfo runInfo;
  440.     short temp;
  441.     char continuousStyles;
  442.     OSErr err;
  443.  
  444.     pWE = *hWE;
  445.     hRuns = pWE->hRuns;
  446.  
  447.     // { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  448.     // { are continuous over the specified text range: those styles must be turned off }
  449.     if (BTST(mode, kModeToggleFace)) 
  450.     {
  451.         temp = weDoFace;
  452.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  453.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  454.     }
  455.     else
  456.     {
  457.         continuousStyles = 0;
  458.     }
  459.  
  460.     // { find the index to the first style run in the specified range }
  461.     offset = rangeStart;
  462.     runIndex = _WEOffsetToRun(offset, hWE);
  463.  
  464.     // { run thru all the style runs that encompass the selection range }
  465.     do
  466.     {
  467.         // { find style index for this run and retrieve corresponding style attributes }
  468.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  469.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  470.  
  471.         // { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  472.         // { correct this anomaly (which is useful for other purposes, anyway) }
  473.         if (runInfo.runEnd > pWE->textLength) 
  474.         {
  475.             runInfo.runEnd = pWE->textLength;
  476.         }
  477.         
  478.         // { apply changes to existing style attributes as requested }
  479.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  480.  
  481.         // { recalculate font metrics, if necessary }
  482.         if ((mode & (weDoFont + weDoSize + weDoFace + weDoAddSize)) != 0) 
  483.         {
  484.             _WEFillFontInfo(pWE->port, &runInfo.runAttrs);
  485.         }
  486.         
  487.         // { get a style index for the new attributes }
  488.         err = _WENewStyle(&runInfo.runAttrs, &newStyleIndex, pWE);
  489.         if (err != noErr) 
  490.         {
  491.             goto cleanup;
  492.         }
  493.         
  494.         // { if offset falls on a style boundary and this style run has become identical }
  495.         // { to the previous one, merge the two runs together }
  496.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  497.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  498.         {
  499.             err = _WERemoveRun(runIndex, pWE);
  500.             if (err != noErr) 
  501.             {
  502.                 goto cleanup;
  503.             }
  504.             runIndex = runIndex - 1;
  505.         }
  506.  
  507.         // { style index changed? }
  508.         if (oldStyleIndex != newStyleIndex) 
  509.         {
  510.             // { if offset is in the middle of a style run, insert a new style run in the run array }
  511.             if (offset > runInfo.runStart) 
  512.             {
  513.                 err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  514.                 if (err != noErr) 
  515.                 {
  516.                     goto cleanup;
  517.                 }
  518.                 runIndex = runIndex + 1;
  519.             }
  520.             else
  521.             {
  522.                 // { otherwise just change the styleIndex field of the current style run element }
  523.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  524.             }
  525.  
  526.             // { if specified range ends in the middle of a style run, insert yet another element }
  527.             if (rangeEnd < runInfo.runEnd) 
  528.             {
  529.                 err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  530.                 if (err != noErr) 
  531.                 {
  532.                     goto cleanup;
  533.                 }
  534.             }
  535.         } // { if oldStyle != newStyle }
  536.  
  537.         // { go to next style run }
  538.         runIndex = runIndex + 1;
  539.         offset = runInfo.runEnd;
  540.  
  541.     } while (offset < rangeEnd);
  542.  
  543.     // { if the last style run ends exactly at the end of the specified range, }
  544.     // { see if we can merge it with the following style run }
  545.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) && 
  546.         ((*hRuns)[runIndex].styleIndex == newStyleIndex)) 
  547.     {
  548.         err = _WERemoveRun(runIndex, pWE);
  549.         if (err != noErr) 
  550.         {
  551.             goto cleanup;
  552.         }
  553.     }
  554.  
  555.     // { clear result code }
  556.     err = noErr;
  557.  
  558. cleanup:
  559.     // { return result code }
  560.     return err;
  561. } // { _WESetStyleRange }
  562.  
  563. pascal OSErr _WEApplyStyleScrap(long rangeStart, long rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  564. {
  565.     // { apply the given style scrap to the specified text range }
  566.     // { the WE record is guaranteed to be already locked }
  567.  
  568.     WEPtr pWE;
  569.     TEStyleScrapPeek pElement;
  570.     long runStart, runEnd;
  571.     short index, lastElement;
  572.     WETextStyle ts;
  573.     OSErr err;
  574.  
  575.     // _WEApplyStyleScrap = noErr;
  576.     pWE = *hWE;
  577.  
  578.     // { loop through each element of the style scrap }
  579.     lastElement = (*styleScrap)->scrpNStyles - 1;
  580.     for(index = 0; index<=lastElement; index++)
  581.     {
  582.         // { get a pointer to the current scrap element }
  583.         pElement = (TEStyleScrapPeek)&(*styleScrap)->scrpStyleTab[index];
  584.  
  585.         // { calculate text run to which this element is to be applied }
  586.         runStart = rangeStart + pElement->first.scrpStartChar;
  587.         if (index < lastElement) 
  588.         {
  589.             runEnd = rangeStart + pElement->second.scrpStartChar;
  590.         }
  591.         else
  592.         {
  593.             runEnd = rangeEnd;
  594.         }
  595.  
  596.         // { perform some range checking }
  597.         if (runEnd > rangeEnd) 
  598.         {
  599.             runEnd = rangeEnd;
  600.         }
  601.         if (runStart >= runEnd) 
  602.         {
  603.             continue;
  604.         }
  605.  
  606.         // { copy style to a local variable in case memory moves }
  607.         *(TextStyle *)&ts = pElement->first.scrpTEAttrs.runTEStyle;
  608.         // { apply the specified style to the range }
  609.         err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE);
  610.         if (err != noErr) 
  611.         {
  612.             return err;
  613.         }
  614.     }
  615.     return noErr;
  616. } // { _WEApplyStyleScrap }
  617.  
  618. pascal OSErr _WEApplySoup(long offset, Handle hSoup, WEHandle hWE)
  619. {
  620.     WESoupPtr pSoup;
  621.     long pSoupEnd;
  622.     WETextStyle ts;
  623.     Handle hObjectData;
  624.     long objectOffset;
  625.     Boolean saveWELock;
  626.     OSErr err;
  627.  
  628.     _WEBlockClr((Ptr)&ts, sizeof(ts));
  629.     hObjectData = nil;
  630.  
  631.     // { lock the WE record }
  632.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  633.  
  634.     // { lock the soup in high heap }
  635.     HLockHi(hSoup);
  636.     pSoup = (WESoupPtr)*hSoup;
  637.     pSoupEnd = (long)pSoup + GetHandleSize(hSoup);
  638.  
  639.     // { loop through each object descriptor in the soup }
  640.     while((long)pSoup < pSoupEnd)
  641.     {
  642.  
  643.         // { create a new relocatable block the hold the object data }
  644.         err = _WEAllocate(pSoup->soupDataSize, kAllocTemp, &hObjectData);
  645.         if (err != noErr) 
  646.             goto cleanup;
  647.  
  648.         // { copy the object data to this block }
  649.         BlockMoveData((Ptr)(pSoup + 1), *hObjectData, pSoup->soupDataSize);
  650.  
  651.         // { create a new object out of the tagged data }
  652.         err = _WENewObject(pSoup->soupType, hObjectData, hWE, (WEObjectDescHandle *)&ts.tsObject);
  653.         if (err != noErr) 
  654.             goto cleanup;
  655.  
  656.         // { record a reference to the object descriptor in the style table }
  657.         objectOffset = pSoup->soupOffset + offset;
  658.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  659.         hObjectData = nil;
  660.         ts.tsObject = nil;
  661.         if (err != noErr) 
  662.             goto cleanup;
  663.  
  664.         // { advance soup pointer }
  665.         pSoup = (WESoupPtr)((long)pSoup + sizeof(WESoup) + pSoup->soupDataSize);
  666.  
  667.     } // { while }
  668.  
  669.     // { clear result code }
  670.     err = noErr;
  671.  
  672. cleanup:
  673.     // { clean up }
  674.     HUnlock(hSoup);
  675.     _WEForgetHandle(&ts.tsObject);
  676.     _WEForgetHandle(&hObjectData);
  677.  
  678.     // { unlock the WE record }
  679.     _WESetHandleLock((Handle)hWE, saveWELock);
  680.  
  681.     // { return result code }
  682.     return err;
  683.  
  684. } // { _WEApplySoup }
  685.  
  686. pascal void _WEBumpRunStart(long runIndex, long deltaRunStart, WEPtr pWE)
  687. {
  688.     // { add deltaLineStart to the lineStart field of all line records }
  689.     // { starting from lineIndex }
  690.  
  691.     long *pStart;
  692.     long nRuns;
  693.  
  694.     pStart = &(*pWE->hRuns)[runIndex].runStart;
  695.     nRuns = pWE->nRuns;
  696.  
  697.     // { loop through the style run array adjusting the runStart fields }
  698.     while (runIndex <= nRuns)
  699.     {
  700.         *pStart = *pStart + deltaRunStart;
  701.         pStart = (long *)((long)(pStart) + sizeof(RunArrayElement));
  702.         runIndex = runIndex + 1;
  703.     }
  704. } // { _WEBumpRunStart }
  705.  
  706. pascal OSErr _WERemoveRunRange(long rangeStart, long rangeEnd, WEHandle hWE)
  707. {
  708.     // { the range of text between rangeStart and rangeEnd is being deleted }
  709.     // { update the style run array (and the style table) accordingly }
  710.     // { the WE handle must be locked on entry }
  711.  
  712.     WEPtr pWE;
  713.     RunArrayPeek pRuns;
  714.     long startRun, endRun;
  715.     OSErr err;
  716.  
  717.     pWE = *hWE;
  718.  
  719.     // { find the index to the first and last style runs in the specified range }
  720.     startRun = _WEOffsetToRun(rangeStart, hWE);
  721.     endRun = _WEOffsetToRun(rangeEnd, hWE) - 1;
  722.  
  723.     // { remove all style runs between startRun and endRun }
  724.     while (endRun > startRun)
  725.     {
  726.         err = _WERemoveRun(endRun, pWE);
  727.         if (err != noErr) 
  728.         {
  729.             goto cleanup;
  730.         }
  731.         endRun = endRun - 1;
  732.     }
  733.     
  734.     // { move back all subsequent style runs }
  735.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  736.  
  737.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  738.     { 
  739.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[endRun];
  740.         pRuns->second.runStart = rangeStart;
  741.     }
  742.  
  743.     // { remove the first style run if is has become zero length }
  744.     pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  745.     if (pRuns->first.runStart == pRuns->second.runStart) 
  746.     {
  747.         err = _WERemoveRun(startRun, pWE);
  748.         if (err != noErr) 
  749.         {
  750.             goto cleanup;
  751.         }
  752.         startRun = startRun - 1;
  753.     }
  754.  
  755.     // { merge the first and last runs if they have the same style index }
  756.     if (startRun >= 0) 
  757.     {
  758.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  759.         if (pRuns->first.styleIndex == pRuns->second.styleIndex) 
  760.         {
  761.             err = _WERemoveRun(startRun + 1, pWE);
  762.             if (err != noErr) 
  763.             {
  764.                 goto cleanup;
  765.             }
  766.         }
  767.     }
  768.     // { clear result code }
  769.     err = noErr;
  770.  
  771. cleanup:
  772.     // { return result code }
  773.     return err;
  774. } // { _WERemoveRunRange }
  775.  
  776. pascal void _WEBumpLineStart(long lineIndex, long deltaLineStart, WEPtr pWE)
  777. {
  778.     // { add deltaLineStart to the lineStart field of all line records }
  779.     // { starting from lineIndex }
  780.  
  781.     long *pStart;
  782.     long nLines;
  783.  
  784.     pStart = &(*pWE->hLines)[lineIndex].lineStart;
  785.     nLines = pWE->nLines;
  786.  
  787.     // { loop through the line array adjusting the lineStart fields }
  788.     while (lineIndex <= nLines)
  789.     {
  790.         *pStart = *pStart + deltaLineStart;
  791.         pStart = (long *)((long)pStart + sizeof(LineRec));
  792.         lineIndex = lineIndex + 1;
  793.     }
  794. } // { _WEBumpLineStart }
  795.  
  796. pascal OSErr _WERemoveLineRange(long rangeStart, long rangeEnd, WEHandle hWE)
  797. {
  798.     // { the range of text between rangeStart and rangeEnd is being deleted }
  799.     // { update the line array accordingly }
  800.     // { the WE handle must be locked on entry }
  801.  
  802.     WEPtr pWE;
  803.     long startLine, endLine;
  804.     OSErr err;
  805.     
  806.     // _WERemoveLineRange = noErr;
  807.     pWE = *hWE;
  808.  
  809.     // { remove all line records between rangeStart and rangeEnd }
  810.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  811.     endLine = WEOffsetToLine(rangeEnd, hWE);
  812.     while (endLine >= startLine)
  813.     {
  814.         err = _WERemoveLine(endLine, pWE);
  815.         if (err != noErr) 
  816.         {
  817.             return err;
  818.         }
  819.         endLine = endLine - 1;
  820.     } // { while }
  821.  
  822.     // { update the lineStart field of all the line records that follow }
  823.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  824.  
  825.     return noErr;
  826. }
  827.  
  828. pascal OSErr _WEDeleteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  829. {
  830.     // { used internally to delete a text range }
  831.     // { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  832.     // { the WE record is guaranteed to be already locked }
  833.  
  834.     WEPtr pWE;
  835.     WERunInfo runInfo;
  836.     long oldTextLength, newTextLength;
  837.     long pText;
  838.     OSErr err;
  839.  
  840.     pWE = *hWE;
  841.  
  842.     if (rangeEnd>pWE->textLength) rangeEnd = pWE->textLength;
  843.     
  844.     // { do nothing if the specified range is empty }
  845.     if (rangeStart == rangeEnd) 
  846.     {
  847.         goto cleanup1;
  848.     }
  849.     
  850.     // { save the first style in the specified range in nullStyle }
  851.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  852.     pWE->nullStyle = runInfo.runAttrs;
  853.     BSET(pWE->flags, weFUseNullStyle);
  854.  
  855.     // { special case: if we're deleting up to the end of the text, see whether }
  856.     // { there's an embedded object at the very end and remove it }
  857.     if (rangeEnd == pWE->textLength) 
  858.     {
  859.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  860.         if (runInfo.runAttrs.runStyle.tsObject != nil) 
  861.         {
  862.             runInfo.runAttrs.runStyle.tsObject = nil;
  863.             err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE);
  864.             if (err != noErr) 
  865.                 goto cleanup2;
  866.         }
  867.     }
  868.  
  869.     // { remove all line records between rangeStart and rangeEnd }
  870.     err = _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  871.     if (err != noErr) 
  872.     {
  873.         goto cleanup2;
  874.     }
  875.     
  876.     // { remove all style runs between rangeStart and rangeEnd }
  877.     err = _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  878.     if (err != noErr) 
  879.     {
  880.         goto cleanup2;
  881.     }
  882.     
  883.     // { calculate old and new text length }
  884.     oldTextLength = pWE->textLength;
  885.     newTextLength = oldTextLength - (rangeEnd - rangeStart);
  886.  
  887.     // { move the end of the text backwards over the old selection range }
  888.     pText = (long)(*pWE->hText);
  889.     BlockMoveData((Ptr)pText + rangeEnd, (Ptr)pText + rangeStart, oldTextLength - rangeEnd);
  890.  
  891.     // { compact the text handle }
  892.     SetHandleSize((Handle)pWE->hText, newTextLength);
  893.     err = MemError();
  894.     if (err != noErr) 
  895.     {
  896.         goto cleanup2;
  897.     }
  898.  
  899.     // { update textLength field }
  900.     pWE->textLength = newTextLength;
  901.  
  902. cleanup1:
  903.     // { clear result code }
  904.     err = noErr;
  905.  
  906. cleanup2:
  907.     // { return result code }
  908.     return err;
  909. } // { _WEDeleteRange }
  910.  
  911. pascal OSErr _WEInsertText(long offset, Ptr textPtr, long textLength, WEHandle hWE)
  912. {
  913.     // { this routine assumes that the WE record is already locked }
  914.  
  915.     WEPtr pWE;
  916.     long oldTextLength, newTextLength;
  917.     long pInsPoint;
  918.     OSErr err;
  919.     short mode = 0;
  920.  
  921.     pWE = *hWE;
  922.  
  923.     // { do nothing if textLength is zero or negative }
  924.     if (textLength <= 0) 
  925.     {
  926.         goto cleanup1;
  927.     }
  928.     
  929.     // { calculate old and new length of text handle }
  930.     oldTextLength = pWE->textLength;
  931.     newTextLength = oldTextLength + textLength;
  932.  
  933.     // { leng the raw text handle }
  934.     SetHandleSize(pWE->hText, newTextLength);
  935.     err = MemError();
  936.     if (err != noErr) 
  937.         goto cleanup2;
  938.  
  939.     SetHandleSize((Handle)pWE->hText, newTextLength);
  940.     err = MemError();
  941.     if (err != noErr) 
  942.     {
  943.         goto cleanup2;
  944.     }
  945.     
  946.     // { calculate ptr to insertion point }
  947.     pInsPoint = (long)(*pWE->hText) + offset;
  948.  
  949.     // { make room for the new text }
  950.     BlockMoveData((Ptr)pInsPoint, (Ptr)(pInsPoint + textLength), oldTextLength - offset);
  951.  
  952.     // { insert new text at the insertion point }
  953.     BlockMoveData(textPtr, (Ptr)pInsPoint, textLength);
  954.  
  955.     // { update the lineStart fields of all lines following the insertion point }
  956.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  957.  
  958.     // { update the runStart fields of all style runs following the insertion point }
  959.     _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  960.  
  961.     // { update various fields in the WE record }
  962.     pWE->textLength = newTextLength;
  963.  
  964.     // { make sure the newly inserted text doesn't reference any embedded object }
  965.     pWE->nullStyle.runStyle.tsObject = nil;
  966.     mode = weDoObject;
  967.  
  968.     // { if there is a valid null style, apply it to the newly inserted text }
  969.     if (BTST(pWE->flags, weFUseNullStyle))
  970.         mode = mode + (weDoAll + weDoReplaceFace);
  971.  
  972.     err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE);
  973.     if (err != noErr)
  974.     {
  975.         goto cleanup2;
  976.     }
  977.  
  978. cleanup1:
  979.     // { clear result code }
  980.     err = noErr;
  981.  
  982. cleanup2:
  983.     // { return result code }
  984.     return err;
  985. } // { _WEInsertText }
  986.