home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 January / macpower199701.bin / AMUG / Programming_10 / WASTE 1.3a1.sit / WASTE 1.3a1 Distribution / WASTE 1.3a1 / WEDrawing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-23  |  26.8 KB  |  949 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEDrawing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing routines and other basic support functions
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14. #include "WASTEIntf.h"
  15.  
  16. #if WASTE_OBJECTS
  17.  
  18. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  19. {
  20.     WEPtr pWE = *hWE;
  21.     WERunInfo runInfo;
  22.  
  23.     // assume current selection is not an embedded object
  24.     *hObjectDesc = nil;
  25.  
  26.     // check selection range
  27.     if (pWE->selEnd - pWE->selStart == 1)
  28.     {
  29.         // check run info
  30.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  31.         if ((*hObjectDesc = runInfo.runAttrs.runStyle.tsObject) != nil)
  32.             return noErr;
  33.     }
  34.     return weObjectNotFoundErr;
  35. }
  36.  
  37. pascal SInt32 WEFindNextObject(SInt32 offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  38. {
  39.     WEPtr pWE = *hWE;
  40.     StyleTablePtr pStyles = *pWE->hStyles;
  41.     WEObjectDescHandle obj;
  42.     RunArrayElementPtr pRun;
  43.  
  44.     *hObjectDesc = nil;
  45.  
  46.     // do nothing if offset is already at the end of the text
  47.     if (offset >= pWE->textLength - 1)
  48.         return kInvalidOffset;
  49.  
  50.     // get a pointer to the run array element immediately following offset
  51.     pRun = *pWE->hRuns + WEOffsetToRun(offset + 1, hWE);
  52.  
  53.     // perform a linear scan of the run array looking for a run whose
  54.     // corresponding style table entry points to an embedded object;
  55.     // the search will stop anyway because the last run array element has styleIndex = -1
  56.     while (pRun->styleIndex >= 0)
  57.     {
  58.         if ((obj = pStyles[pRun->styleIndex].info.runStyle.tsObject) != nil)
  59.         {
  60.             *hObjectDesc = obj;
  61.             return pRun->runStart;
  62.         }
  63.         pRun++;
  64.     }
  65.  
  66.     return kInvalidOffset;
  67.  
  68. }
  69.  
  70. #endif  // WASTE_OBJECTS
  71.  
  72. pascal void _WEContinuousStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode *mode, WETextStyle *ts, WEHandle hWE)
  73. {
  74.     // find out which style attributes are continous over the specified text range
  75.     // on entry, *mode specifies which attributes are to be checked
  76.     // on exit, *mode specifies the continuous attributes, also copied to ts
  77.  
  78.     WEPtr pWE = *hWE;
  79.     SInt32 runIndex;
  80.     WEStyleMode outMode;
  81.     WERunInfo runInfo;
  82.  
  83.     outMode = *mode;
  84.  
  85.     // get style info at the beginning of the specified range
  86.     runIndex = WEOffsetToRun(rangeStart, hWE);
  87.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  88.  
  89.     // copy the specified fields to ts
  90.     _WECopyStyle(&runInfo.runAttrs.runStyle, (WETextStyle *) ts, normal, (outMode & weDoAll) | weDoReplaceFace);
  91.  
  92.     // loop through style runs across the current selection range
  93.     // if we determine that all specified attributes are discontinuous, we exit prematurely
  94.     do
  95.     {
  96.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  97.  
  98.         // determine which attributes have changed, if any
  99.         if (outMode & weDoFont)
  100.         {
  101.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  102.             {
  103.                 outMode &= (~weDoFont);
  104.             }
  105.         }
  106.         if (outMode & weDoFace)
  107.         {
  108.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  109.             {
  110.                 ts->tsFace &= runInfo.runAttrs.runStyle.tsFace;
  111.                 if (ts->tsFace == 0)
  112.                 {
  113.                     outMode &= (~weDoFace);
  114.                 }
  115.             }
  116.         }
  117.         if (outMode & weDoFaceMask)
  118.         {
  119.             ts->tsFlags |= runInfo.runAttrs.runStyle.tsFace;
  120.         }
  121.         if (outMode & weDoSize)
  122.         {
  123.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize)
  124.             {
  125.                 outMode &= (~weDoSize);
  126.             }
  127.         }
  128.         if (outMode & weDoColor)
  129.         {
  130.             if (!_WEBlockCmp(&runInfo.runAttrs.runStyle.tsColor, &ts->tsColor, sizeof(RGBColor)))
  131.             {
  132.                 outMode &= (~weDoColor);
  133.             }
  134.         }
  135.  
  136.         runIndex++;
  137.     } while ((outMode != 0) && (runInfo.runEnd < rangeEnd));
  138.  
  139.     *mode = outMode;
  140. }
  141.  
  142. pascal void _WESynchNullStyle(WEHandle hWE)
  143. {
  144.     // This routine fills the nullStyle field of the WE record with valid information
  145.     // and makes sure that the null style font belongs to the keyboard script.
  146.  
  147.     WEPtr pWE = *hWE;    // assume WE record is already locked
  148.     SInt32 runIndex;
  149.     WERunInfo runInfo;
  150. #if !WASTE_NO_SYNCH
  151.     ScriptCode keyboardScript;
  152.     SInt16 fontID;
  153. #endif
  154.  
  155.     // find the run index of the style run preceding the insertion point
  156.     runIndex = WEOffsetToRun(pWE->selStart - 1, hWE);
  157.  
  158.     // if the nullStyle record is marked as invalid, fill it with the style attributes
  159.     // associated with the character preceding the insertion point, and mark it as valid
  160.     if (!BTST(pWE->flags, weFUseNullStyle))
  161.     {
  162.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  163.         pWE->nullStyle = runInfo.runAttrs;
  164.         BSET(pWE->flags, weFUseNullStyle);
  165.     }
  166.  
  167. #if !WASTE_NO_SYNCH
  168.     // if only the Roman script is installed, we're finished
  169.     if (!BTST(pWE->flags, weFNonRoman))
  170.     {
  171.         return;
  172.     }
  173.  
  174.     // *** FONT / KEYBOARD SYNCHRONIZATION ***
  175.     // get the keyboard script
  176.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  177.  
  178.     // find out what font will be used for the next character typed
  179.     fontID = pWE->nullStyle.runStyle.tsFont;
  180.  
  181.     // do nothing if the font script is the same as the keyboard script
  182.     if (FontToScript(fontID) == keyboardScript) return;
  183.  
  184.     // scan style runs starting from the insertion point backwards,
  185.     // looking for the first font belonging to the keyboard script
  186.     do
  187.     {
  188.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  189.         fontID = runInfo.runAttrs.runStyle.tsFont;
  190.         if (FontToScript(fontID) == keyboardScript) break;
  191.         runIndex--;
  192.     } while (runIndex>=0);
  193.  
  194.     // if no font was ever used for the keyboard script, default to the
  195.     // application font for the script
  196.     if (runIndex < 0)
  197.     {
  198.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  199.     }
  200.  
  201.     // change the font in the null style record
  202.     pWE->nullStyle.runStyle.tsFont = fontID;
  203. #endif
  204. }
  205.  
  206. pascal Boolean WEContinuousStyle(WEStyleMode *mode, TextStyle *ts, WEHandle hWE)
  207. {
  208.     // find out which style attributes are continous over the selection range
  209.     // on entry, the mode bitmap specifies which attributes are to be checked
  210.     // on exit, the mode bitmap specifies the continuous attributes, also copied to ts
  211.     // return true if all specified attributes are continuous
  212.  
  213.     WEPtr pWE;
  214.     WEStyleMode oldMode;
  215.     Boolean continuousStyle;
  216.     Boolean saveWELock;
  217.  
  218.     // lock the WE record
  219.     pWE = *hWE;
  220.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  221.  
  222.     // mask out unused bits in *mode
  223.     *mode &= (weDoAll | weDoFaceMask);
  224.  
  225.     // two rather different paths are taken depending on whether
  226.     // the selection range is empty or not
  227.     if (pWE->selStart == pWE->selEnd)
  228.     {
  229.         // if the selection range is empty, always return true and set ts
  230.         // from the nullStyle record, after having validated it
  231.         continuousStyle = true;
  232.         _WESynchNullStyle(hWE);
  233.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *) ts, normal, (*mode & weDoAll) | weDoReplaceFace);
  234.     }
  235.     else
  236.     {
  237.         // otherwise get the continuous style attributes over the selection range
  238.         oldMode = *mode;
  239.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *) ts, hWE);
  240.  
  241.         // return true if mode hasn't changed
  242.         continuousStyle = (oldMode == *mode);
  243.     }
  244.  
  245.     // unlock the WE record
  246.     _WESetHandleLock((Handle) hWE, saveWELock);
  247.  
  248.     return continuousStyle;
  249. }
  250.  
  251. static pascal Boolean GetSegmentDirection(SInt16 segmentIndex, const struct FormatOrderData *data)
  252. {
  253.     return _WEGetIndDirection(data->firstRun + segmentIndex, data->hWE);
  254. }
  255.  
  256. pascal void _WESegmentLoop(SInt32 firstLine, SInt32 lastLine, SegmentLoopProcPtr callback, void *callbackData, WEHandle hWE)
  257. {
  258.     // For each style segment on every line in the specified range, set up
  259.     // text attributes in the port and call the callback.
  260.     // the WE record must be already locked
  261.  
  262.     WEPtr pWE = *hWE;
  263.     LinePtr pLine;
  264.     Ptr pText;
  265.     SInt32 lineIndex;
  266.     SInt32 firstRun, lastRun;
  267.     SInt32 segmentIndex, segmentCount;
  268.     SInt32 runIndex, currentRunIndex;
  269.     SInt32 lineStart, lineEnd, segmentStart, segmentEnd;
  270.     JustStyleCode styleRunPosition;
  271.     WERunInfo runInfo;
  272.     SInt16 autoOrdering[ kAutoOrderingSize ];
  273.     Handle hTempOrdering = nil;
  274.     SInt16 *pOrdering;
  275.     struct FormatOrderData formatOrderData;
  276.     Boolean isRightToLeft;
  277.     Boolean saveLineLock;
  278.     Boolean saveTextLock;
  279.     QDEnvironment saveEnvironment;
  280.  
  281.     static StyleRunDirectionUPP directionProc = nil;
  282.  
  283.     // create a routine descriptor for GetSegmentDirection, if we haven't already
  284.     if (directionProc == nil)
  285.     {
  286.         directionProc = NewStyleRunDirectionProc(&GetSegmentDirection);
  287.     }
  288.  
  289.     // is right-to-left the dominant line direction?
  290.     isRightToLeft = IsRightToLeft(pWE->direction);
  291.  
  292.     // save the Quickdraw environment
  293.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD) ? true : false, &saveEnvironment);
  294.  
  295.     // make sure firstLine and lastLine are within the allowed range
  296.     lineIndex = pWE->nLines - 1;
  297.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  298.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  299.  
  300.     // lock the line array
  301.     saveLineLock = _WESetHandleLock((Handle) pWE->hLines, true);
  302.     pLine = *pWE->hLines + firstLine;
  303.  
  304.     // lock the text
  305.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  306.     pText = *pWE->hText;
  307.  
  308.     // find the style run index corresponding to the beginning of the first line
  309.     lastRun = WEOffsetToRun(pLine->lineStart, hWE);
  310.     currentRunIndex = -1;
  311.  
  312.     // loop thru the specified lines
  313.     for ( lineIndex = firstLine; lineIndex <= lastLine; lineIndex++ )
  314.     {
  315.         // get line start and line end
  316.         lineStart = pLine[0].lineStart;
  317.         lineEnd = pLine[1].lineStart;
  318.  
  319.         // calculate the indices to the first and the last style run on the line
  320.         firstRun = lastRun;
  321.         lastRun = WEOffsetToRun(lineEnd - 1, hWE);
  322.  
  323.         // calculate the number of segments on this line
  324.         segmentCount = lastRun - firstRun + 1;
  325.  
  326.         if (BTST(pWE->flags, weFBidirectional))
  327.         {
  328.             // SPECIAL PROCESSING FOR BIDIRECTIONAL SCRIPTS
  329.             // we need an array of segmentCount entries for GetFormatOrder
  330.             // in most cases, stack storage will be sufficient
  331.             if (segmentCount <= kAutoOrderingSize)
  332.             {
  333.                 pOrdering = autoOrdering;
  334.             }
  335.             else
  336.             {
  337.                 // in rare cases the ordering array must be allocated dynamically
  338.                 _WEForgetHandle(&hTempOrdering);
  339.                 if (_WEAllocate(segmentCount * sizeof(SInt16), kAllocTemp, &hTempOrdering) != noErr)
  340.                 {
  341.                     // couldn't allocate memory: emergency exit
  342.                     break;
  343.                 }
  344.                 HLock(hTempOrdering);
  345.                 pOrdering = (SInt16 *) *hTempOrdering;
  346.             }
  347.  
  348.             // obtain the correct display order for the segments
  349.             if (segmentCount > 1)
  350.             {
  351.                 formatOrderData.firstRun = firstRun;
  352.                 formatOrderData.hWE = hWE;
  353.                 GetFormatOrder((FormatOrderPtr) pOrdering, 0, segmentCount - 1, isRightToLeft, directionProc, (Ptr) &formatOrderData);
  354.             }
  355.             else
  356.             {
  357.                 pOrdering[0] = 0;
  358.             }
  359.         }
  360.  
  361.         // loop thru each segment on this line
  362.         for ( segmentIndex = 0; segmentIndex < segmentCount; segmentIndex++ )
  363.         {
  364.             if (BTST(pWE->flags, weFBidirectional))
  365.             {
  366.                 runIndex = pOrdering[ segmentIndex ];
  367.             }
  368.             else
  369.             {
  370.                 runIndex = segmentIndex;
  371.             }
  372.             runIndex += firstRun;
  373.  
  374.             if (currentRunIndex != runIndex)
  375.             {
  376.                 // get style run information for the current style run
  377.                 _WEGetIndStyle(runIndex, &runInfo, hWE);
  378.  
  379.                 // set new text attributes
  380.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  381.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  382.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  383.  
  384.                 // remember current run index
  385.                 currentRunIndex = runIndex;
  386.             }
  387.  
  388.             // determine segment boundaries
  389.             segmentStart = (runInfo.runStart <= lineStart) ? lineStart : runInfo.runStart;
  390.             segmentEnd = (runInfo.runEnd >= lineEnd) ? lineEnd : runInfo.runEnd;
  391.  
  392.             // determine the relative position of this segment on the line
  393.             styleRunPosition = 0;        // onlyStyleRun
  394.             if (segmentIndex < segmentCount - 1)
  395.             {
  396.                 styleRunPosition += 1;    // leftStyleRun or middleStyleRun
  397.             }
  398.             if (segmentIndex > 0)
  399.             {
  400.                 styleRunPosition += 2;    // rightStyleRun or middleStyleRun
  401.             }
  402.  
  403.             // do the callback
  404.             if (callback(pLine, &runInfo.runAttrs, pText + segmentStart, segmentStart, segmentEnd - segmentStart, styleRunPosition, hWE, callbackData))
  405.             {
  406.                 break;
  407.             }
  408.         };
  409.         pLine++;
  410.     }
  411.  
  412.     // unlock the text
  413.     _WESetHandleLock(pWE->hText, saveTextLock);
  414.  
  415.     // unlock the line array
  416.     _WESetHandleLock((Handle) pWE->hLines, saveLineLock);
  417.  
  418.     // restore the Quickdraw environment
  419.     _WERestoreQDEnvironment(&saveEnvironment);
  420.  
  421.     // dispose of any temporary storage
  422.     _WEForgetHandle(&hTempOrdering);
  423. }
  424.  
  425. pascal void _WEDrawTSMHilite(Rect *segmentRect, UInt8 tsFlags)
  426. {
  427.     SInt16 underlineHeight;
  428.     RGBColor background, foreground, saveForeground;
  429.     Boolean isColorPort;
  430.     Boolean usingTrueGray;
  431.  
  432.     isColorPort = (((CGrafPtr)(qd.thePort))->portVersion < 0);
  433.     usingTrueGray = false;
  434.  
  435.     // by default, the pen pattern is solid
  436.     PenPat(&qd.black);
  437.  
  438.     // if we're drawing in color, set the foreground color
  439.     if (isColorPort)
  440.     {
  441.         // save foreground color
  442.         GetForeColor(&saveForeground);
  443.  
  444.         // by default, the foreground color is black
  445.         foreground.red = 0;
  446.         foreground.green = 0;
  447.         foreground.blue = 0;
  448.  
  449.         // if we're underlining raw (unconverted) text, see if a "true gray" is available
  450.         if (!BTST(tsFlags, tsTSMConverted))
  451.         {
  452.             GetBackColor(&background);
  453.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  454.         } // if raw text
  455.  
  456.         // set the foreground color
  457.         RGBForeColor(&foreground);
  458.     } // if color graf port
  459.  
  460.     // if we're underlining raw (unconverted) text and no true gray is available,
  461.     // simulate gray with a 50% pattern
  462.     if (!BTST(tsFlags, tsTSMConverted))
  463.     {
  464.         if (!usingTrueGray)
  465.         {
  466.             PenPat(&qd.gray);
  467.         }
  468.     }
  469.     // use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline
  470.     underlineHeight = BTST(tsFlags, tsTSMSelected) ? 2 : 1;
  471.  
  472.     // segmentRect becomes the rectangle to paint
  473.     InsetRect(segmentRect, 1, 0);
  474.     segmentRect->top = segmentRect->bottom - underlineHeight;
  475.  
  476.     // draw the underline
  477.     PaintRect(segmentRect);
  478.  
  479.     // restore the foreground color
  480.     if (isColorPort)
  481.     {
  482.         RGBForeColor(&saveForeground);
  483.     }
  484. }
  485.  
  486. static Boolean SLDraw (LinePtr pLine, const WERunAttributes *pAttrs,
  487.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  488.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  489. {
  490. #pragma unused(segmentStart)
  491.     struct SLDrawData *cd = (struct SLDrawData *) callbackData;
  492.     WEPtr pWE = *hWE;
  493.     Fixed slop;
  494.     Rect segmentRect;
  495.     RGBColor rgbTemp1, rgbTemp2;
  496.  
  497.     // is this the first segment on this line?
  498.     if (IS_LEFTMOST_RUN(styleRunPosition))
  499.     {
  500.         // calculate the line rectangle (the rectangle which completely encloses the current line)
  501.         cd->lineRect.left = pWE->destRect.left;
  502.         cd->lineRect.right = pWE->destRect.right;
  503.         cd->lineRect.top = pWE->destRect.top + pLine[0].lineOrigin;
  504.         cd->lineRect.bottom = pWE->destRect.top + pLine[1].lineOrigin;
  505.  
  506.         // calculate the visible portion of this rectangle
  507.         // we do this by intersecting the line rectangle with the view rectangle
  508.         cd->drawRect = (*pWE->viewRgn)->rgnBBox;
  509.         SectRect(&cd->lineRect, &cd->drawRect, &cd->drawRect);
  510.  
  511.         if (cd->usingOffscreen)
  512.         {
  513.             // calculate the boundary rectangle for the offscreen buffer
  514.             // this is simply drawRect converted to global coordinates
  515.             cd->bounds = cd->drawRect;
  516.             LocalToGlobal((Point *) &cd->bounds.top);
  517.             LocalToGlobal((Point *) &cd->bounds.bottom);
  518.  
  519.             // update the offscreen graphics world for the new bounds (this could fail)
  520.             cd->drawingOffscreen = false;
  521.             if (UpdateGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd->bounds, nil, nil, 0) >= 0)
  522.             {
  523.                 // NOTE: when running on a 68000 machine with the original Quickdraw,
  524.                 // a GWorld is just an extended GrafPort, and GetGWorldPixMap actually
  525.                 // returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap).
  526.                 // An important side-effect of this is that when we call SetOrigin,
  527.                 // only the original portBits is offset, not the copy.
  528.                 // get the pixel map associated with the offscreen graphics world
  529.                 cd->offscreenPixels = GetGWorldPixMap((GWorldPtr) pWE->offscreenPort);
  530.  
  531.                 // lock it down
  532.                 if (LockPixels(cd->offscreenPixels))
  533.                 {
  534.                     // offscreen pixel buffer allocation was successful
  535.                     cd->drawingOffscreen = true;
  536.  
  537.                     // get background color of onscreen port
  538.                     GetBackColor(&rgbTemp1);
  539.  
  540.                     // switch graphics world
  541.                     SetGWorld((GWorldPtr) pWE->offscreenPort, nil);
  542.  
  543.                     // synchronize the coordinate system of the offscreen port with that of the screen port
  544.                     SetOrigin(cd->drawRect.left, cd->drawRect.top);
  545.  
  546.                     // synchronize the background color
  547.                     RGBBackColor(&rgbTemp1);
  548.  
  549.                     // reset the offscreen clip region
  550.                     ClipRect(&cd->drawRect);
  551.                 }
  552.             } // if pixel buffer allocation was successful
  553.         } // if usingOffscreen
  554.  
  555.         // if doErase is true, erase the drawable area before drawing text
  556.         if (cd->doErase)
  557.         {
  558.             CallWEEraseProc(&cd->drawRect, hWE, pWE->eraseHook);
  559.         }
  560.  
  561.         // position the pen
  562.         MoveTo(cd->lineRect.left + _WECalcPenIndent(pLine, pWE->alignment, pWE->direction), cd->lineRect.top + pLine->lineAscent);
  563.     } // if first segment on line
  564.  
  565.     // if drawingOffscreen, switch thePort to the offscreen port
  566.     // and synchronize text attributes
  567.     if (cd->drawingOffscreen)
  568.     {
  569.         SetPort(pWE->offscreenPort);
  570.         TextFont(pAttrs->runStyle.tsFont);
  571.         TextFace(pAttrs->runStyle.tsFace);
  572.         TextSize(pAttrs->runStyle.tsSize);
  573.     } // if drawingOffscreen
  574.  
  575.     // get horizontal coordinate of the pen before drawing the segment
  576.     GetPen((Point *) &segmentRect.top);
  577.  
  578.     // set the foreground color
  579.     if (cd->usingColor)
  580.     {
  581.         RGBForeColor(&pAttrs->runStyle.tsColor);
  582.     }
  583.  
  584. #if WASTE_OBJECTS
  585.     if (pAttrs->runStyle.tsObject != nil)
  586.     {
  587.         _WEDrawObject(pAttrs->runStyle.tsObject);
  588.     }
  589.     else
  590. #endif
  591.     {
  592.         slop = 0;
  593.  
  594.         // calculate the "slop" (extra space) for this text segment (justified text only)
  595.         if (pWE->alignment == weJustify)
  596.         {
  597.             // if this is the last segment on the line, strip trailing spaces
  598.             if (IS_RIGHTMOST_RUN(styleRunPosition))
  599.             {
  600.                 segmentLength = VisibleLength(pSegment, segmentLength);
  601.             }
  602.             // calculate how much extra space is to be applied to this text segment
  603.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  604.                     kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  605.  
  606.         } // if (alignment == weJustify)
  607.  
  608.         // draw the segment
  609.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE->drawTextHook);
  610.     }
  611.  
  612.     // get horizontal coordinate of the pen after drawing the segment
  613.     GetPen((Point *) &segmentRect.bottom);
  614.     segmentRect.bottom = cd->lineRect.bottom;
  615.  
  616.     // if this segment is in the TSM area, underline it in the appropriate way
  617.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite))
  618.     {
  619.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  620.     }
  621.  
  622.     if (cd->drawingOffscreen)
  623.     {
  624.         if (IS_RIGHTMOST_RUN(styleRunPosition))
  625.         {
  626.             // after drawing offscreen the last segment,
  627.             // prepare to copy the offscreen buffer to video RAM
  628.  
  629.             // first set the graphics world to the screen port
  630.             SetGWorld((CGrafPtr) cd->screenPort, cd->screenDevice);
  631.  
  632.             // before calling CopyBits, set the foreground color to black
  633.             // and the background to white to avoid colorization
  634.             if (cd->usingColor)
  635.             {
  636.                 GetBackColor(&rgbTemp1);
  637.                 rgbTemp2.red = 0xFFFF;
  638.                 rgbTemp2.green = 0xFFFF;
  639.                 rgbTemp2.blue = 0xFFFF;
  640.                 RGBBackColor(&rgbTemp2);
  641.                 rgbTemp2.red = 0;
  642.                 rgbTemp2.green = 0;
  643.                 rgbTemp2.blue = 0;
  644.                 RGBForeColor(&rgbTemp2);
  645.             }
  646.  
  647.             // copy the offscreen image of the [visible portion of the] line to the screen
  648.             CopyBits(&pWE->offscreenPort->portBits, &cd->screenPort->portBits,
  649.                      &cd->drawRect, &cd->drawRect, srcCopy, nil);
  650.  
  651.             // restore the original background color in the onscreen port
  652.             if (cd->usingColor)
  653.             {
  654.                 RGBBackColor(&rgbTemp1);
  655.             }
  656.  
  657.             // restore the original offscreen coordinate system and unlock the pixel image
  658.             SetPort(pWE->offscreenPort);
  659.             SetOrigin(0, 0);
  660.             if (cd->usingColor)
  661.             {
  662.                 RGBForeColor(&rgbTemp2);
  663.             }
  664.             UnlockPixels(cd->offscreenPixels);
  665.  
  666.         } // if last segment
  667.  
  668.         // restore the screen port for _WESegmentLoop
  669.         SetPort(cd->screenPort);
  670.     } // if drawingOffscreen
  671.  
  672.     return false;    // keep looping
  673. }
  674.  
  675. pascal void _WEDrawLines (SInt32 firstLine, SInt32 lastLine, Boolean doErase, WEHandle hWE)
  676. {
  677.     // draw the specified range of lines
  678.     // we can safely assume that the WE record is already locked
  679.     // and the port is already set the pWE->port
  680.  
  681.     WEPtr pWE = *hWE;            // assume WE record is locked
  682.     struct SLDrawData cd;        // block of data we'll pass to SLDraw
  683.  
  684.     BLOCK_CLR(cd);
  685.     cd.doErase = doErase;
  686.     cd.usingColor = (BTST(pWE->flags, weFHasColorQD) && !BTST(pWE->features, weFInhibitColor)) ? true : false;
  687.  
  688.     // do nothing if our graphics port is not visible
  689.     if (EmptyRgn(pWE->port->visRgn))
  690.     {
  691.         return;
  692.     }
  693.  
  694.     // save graphics world
  695.     GetGWorld((GWorldPtr *) &cd.screenPort, &cd.screenDevice);
  696.  
  697.     // If doErase is true, we're drawing over old text, so we must erase each line
  698.     // before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw
  699.     // the entire line offscreen and  we copy the image right over the old line,
  700.     // without erasing it, thus achieving a very smooth drawing effect.
  701.  
  702.     if ((doErase) && BTST(pWE->features, weFDrawOffscreen))
  703.     {
  704.         // has an offscreen world already been allocated?
  705.         if (pWE->offscreenPort == nil)
  706.         {
  707.             // nope,  create one; its bounds are set initially to an arbitrary rectangle
  708.             SetRect(&cd.bounds, 0, 0, 1, 1);
  709.  
  710.             // if NewGWorld fails, it will set pWE->offscreenPort to nil
  711.             NewGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd.bounds, nil, nil, pixPurge + noNewDevice + useTempMem);
  712.         }
  713.         cd.usingOffscreen = (pWE->offscreenPort != nil);
  714.     }
  715.  
  716.     _WESegmentLoop(firstLine, lastLine, SLDraw, &cd, hWE);
  717.  
  718.     // restore graphics world
  719.     SetGWorld((GWorldPtr) cd.screenPort, cd.screenDevice);
  720. }
  721.  
  722. pascal SInt16 _WECalcPenIndent(const LineRec *pLine, WEAlignment alignment, WEDirection direction)
  723. {
  724.     SInt16 retval = 0;
  725.  
  726.     switch (alignment)
  727.     {
  728.         case weFlushLeft:
  729.             break;
  730.  
  731.         case weFlushRight:
  732.             retval = pLine->lineSlop;
  733.             break;
  734.  
  735.         case weCenter:
  736.             retval = pLine->lineSlop / 2;
  737.             break;
  738.  
  739.         case weJustify:
  740.             if (pLine->lineJustAmount > 0)
  741.                 break;
  742.             // deliberate fall through to default case for
  743.             // last line of justified paragraph
  744.  
  745.         default:
  746.             if (IsRightToLeft(direction))
  747.             {
  748.                 retval = pLine->lineSlop;
  749.             }
  750.             break;
  751.     }
  752.  
  753.     return retval;
  754. }
  755.  
  756. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *environment)
  757. {
  758.     GetPort(&environment->envPort);
  759.     SetPort(port);
  760.     GetPenState(&environment->envPen);
  761.     PenNormal();
  762.     environment->envStyle.tsFont = port->txFont;
  763.     environment->envStyle.tsFace = port->txFace;
  764.     environment->envStyle.tsFlags = saveColor;        // remember if color was saved
  765.     environment->envStyle.tsSize = port->txSize;
  766.     if (saveColor)
  767.     {
  768.         GetForeColor(&environment->envStyle.tsColor);
  769.     }
  770.     environment->envMode = port->txMode;
  771.     TextMode(srcOr);
  772. }
  773.  
  774. pascal void _WERestoreQDEnvironment(const QDEnvironment *environment)
  775. {
  776.     SetPenState(&environment->envPen);
  777.     TextFont(environment->envStyle.tsFont);
  778.     TextFace(environment->envStyle.tsFace);
  779.     TextSize(environment->envStyle.tsSize);
  780.     TextMode(environment->envMode);
  781.     if (environment->envStyle.tsFlags)
  782.     {
  783.         RGBForeColor(&environment->envStyle.tsColor);
  784.     }
  785.     SetPort(environment->envPort);
  786. }
  787.  
  788. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  789. {
  790.     // given a WERunAttributes record, fill in the runHeight, runAscent fields etc.
  791.     FontInfo fInfo;
  792.     QDEnvironment saveEnvironment;
  793.  
  794.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  795.  
  796.     // we don't want a zero font size; although Quickdraw accepts zero to mean
  797.     // the default font size, it can cause trouble to us when we do calculations
  798.     if (targetStyle->runStyle.tsSize == 0)
  799.         targetStyle->runStyle.tsSize = 12;
  800.  
  801.     // set the text attributes
  802.     TextFont(targetStyle->runStyle.tsFont);
  803.     TextSize(targetStyle->runStyle.tsSize);
  804.     TextFace(targetStyle->runStyle.tsFace);
  805.     GetFontInfo(&fInfo);
  806.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  807.     targetStyle->runAscent = fInfo.ascent;
  808.  
  809.     _WERestoreQDEnvironment(&saveEnvironment);
  810. }
  811.  
  812. pascal void _WECopyStyle(const WETextStyle *sourceStyle, WETextStyle *targetStyle,
  813.                 Style offStyles, WEStyleMode mode)
  814. {
  815.     // Copy some or all of the attributes composing sourceStyle to targetStyle.
  816.     // The mode parameter determines which attributes are to be copied and how.
  817.     // If mode contains weDoToggleFace,  offStyles indicates which
  818.     // Quickdraw styles are to be turned off.
  819.  
  820.     // COPY FONT
  821.     if (mode & weDoFont)
  822.     {
  823.             targetStyle->tsFont = sourceStyle->tsFont;
  824. #if WASTE_RESOLVE_FONT_DESIGNATORS
  825.             if (targetStyle->tsFont == systemFont)
  826.                 targetStyle->tsFont = GetSysFont();
  827.             if (targetStyle->tsFont == applFont)
  828.                 targetStyle->tsFont = GetAppFont();
  829. #endif
  830.     }
  831.  
  832.     // COPY SIZE
  833.     if (mode & (weDoSize | weDoAddSize))
  834.     {
  835.         // copy size to a long variable to avoid integer overflows when doing additions
  836.         SInt32 longSize = sourceStyle->tsSize;
  837.  
  838.         // zero really means 12
  839.         if (longSize == 0)
  840.             longSize = 12;
  841.  
  842.         // if kModeAddSize is set, the source size is added to the target size,
  843.         // otherwise the source size replaces the target size outright
  844.         if (mode & weDoAddSize)
  845.         {
  846.             longSize += targetStyle->tsSize;
  847.         }
  848.         // range-check the resulting size
  849.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  850.         targetStyle->tsSize = longSize;
  851.     } // if alter size
  852.  
  853.     // COPY FACE
  854.     if (mode & (weDoFace | weDoFaceMask))
  855.     {
  856.         Style sourceFace = sourceStyle->tsFace;
  857.         Style targetFace = targetStyle->tsFace;
  858.  
  859.         if (mode & weDoFaceMask)
  860.         {
  861.             // USE MASK
  862.             // if kModeFaceMask is set, copy the Quickdraw styles (tsFace field)
  863.             // using the tsFlags field as a mask specifying which bits in the tsFace
  864.             // field are to be copied.
  865.             Style sourceMask = sourceStyle->tsFlags;
  866.             targetFace = (sourceFace & sourceMask) | (targetFace & (~sourceMask));
  867.         }
  868.         else {
  869.             // IGNORE MASK
  870.             // sourceFace replaces targetFace outright if one or both of these conditions hold:
  871.             // 1. sourceFace is zero (= empty set = plain text)
  872.             // 2. the kModeReplaceFace bit is set
  873.  
  874.             if ((sourceFace == normal) || (mode & weDoReplaceFace))
  875.             {
  876.                 targetFace = sourceFace;
  877.             }
  878.             else {
  879.                 // Otherwise sourceFace is interpreted as a bitmap indicating
  880.                 // which styles are to be altered -- all other styles are left intact.
  881.                 // What exactly happens to the styles indicated in sourceFace
  882.                 // depends on whether the kModeToggleFace bit is set or clear.
  883.  
  884.                 // if kModeToggleFace is set, turn a style off if it's set in offStyles,
  885.                 // else turn it on
  886.                 if (mode & weDoToggleFace)
  887.                 {
  888.                     targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  889.                 }
  890.                 else
  891.                 {
  892.                     // if kModeToggleFace is clear, turn on the styles specified in sourceStyle
  893.                     targetFace |= sourceFace;
  894.                 }
  895.             }
  896.         }
  897.  
  898.         // the condense and extend attributes are mutually exclusive: if one is set
  899.         // in sourceFace, remove it from targetFace
  900.         if (sourceFace & condense)
  901.         {
  902.             targetFace &= (~extend);
  903.         }
  904.         if (sourceFace & extend)
  905.         {
  906.             targetFace &= (~condense);
  907.         }
  908.  
  909.         targetStyle->tsFace = targetFace;
  910.     }
  911.  
  912.     // COPY COLOR
  913.     if (mode & weDoColor)
  914.     {
  915.         targetStyle->tsColor = sourceStyle->tsColor;
  916.     }
  917.  
  918. #if WASTE_OBJECTS
  919.     // if kModeObject is set, copy object descriptor
  920.     if (mode & weDoObject)
  921.     {
  922.         targetStyle->tsObject = sourceStyle->tsObject;
  923.     }
  924. #endif
  925.  
  926.     // always clear targetStyle->tsFlags by default
  927.     targetStyle->tsFlags = 0;
  928.  
  929.     // if kModeFlags is set, copy the tsFlags field
  930.     if (mode & weDoFlags)
  931.     {
  932.         targetStyle->tsFlags = sourceStyle->tsFlags;
  933.     }
  934. }
  935.  
  936. pascal Boolean _WEOffsetInRange(SInt32 offset, WEEdge edge, SInt32 rangeStart, SInt32 rangeEnd)
  937. {
  938.     // return true if the position specified by the pair < offset, edge >
  939.     // is within the specified range
  940.  
  941.     // if edge is kTrailingEdge, offset really refers to the preceding character
  942.     if (edge == kTrailingEdge)
  943.     {
  944.         offset--;
  945.     }
  946.     // return true iff offset is within the specified range
  947.     return ((offset >= rangeStart) && (offset < rangeEnd));
  948. }
  949.