home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / WASTE 1.1a4 / WASTE Source / WEDrawing.p < prev    next >
Encoding:
Text File  |  1994-11-12  |  31.8 KB  |  1,055 lines  |  [TEXT/PJMM]

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