home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / NOTEPAD2.ZIP / MDISP.C < prev    next >
C/C++ Source or Header  |  1989-02-08  |  38KB  |  1,206 lines

  1. /*
  2.  * mdisp.c -- Display manager
  3.  *
  4.  * Created by Microsoft Corporation, 1989
  5.  */
  6.  
  7. #define INCL_WIN
  8. #define INCL_GPI
  9. #include <os2.h>
  10. #include <opendlg.h>
  11. #include "mle.h"
  12. #include "mtypes.h"
  13. #include "mfuncs.h"
  14.  
  15. /***********************************************************************
  16. * Private forward declarations
  17. ***********************************************************************/
  18.  
  19. private PIX YFromLine(PED ped, LINE ln);
  20. private LINE LineFromY(PED ped, PIX y);
  21. private VOID DestroyCursor(PED ped);
  22. private BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl);
  23.  
  24.  
  25. /***********************************************************************
  26. *  Initialization and gross change
  27. ***********************************************************************/
  28.  
  29. /*
  30.  * VOID DisplayInit(PED ped)
  31.  *
  32.  * Initializes the display  manager, creating a PS and such as needed.
  33.  */
  34. public VOID DisplayInit(PED ped)
  35. {
  36.         HDC hdc;        // device context for PS
  37.         SIZEL sizl;     // page size for presentation space
  38.         PMR pmr;
  39.  
  40.         /* Create and configure the PS, plus a scratch region handle. */
  41.  
  42.         hdc = WinOpenWindowDC(ped->hwnd);
  43.         sizl.cx = 0L;
  44.         sizl.cy = 0L;
  45.         ped->hps = GpiCreatePS((HAB)NULL, hdc, &sizl, PU_PELS
  46.                                                    | GPIF_DEFAULT
  47.                                                    | GPIT_MICRO
  48.                                                    | GPIA_ASSOC);
  49.         GpiCreateLogColorTable(ped->hps, 0L, LCOLF_RGB, 0L, 0L, 0L);
  50.                      // Specify all colors for this hps as 24-bit RGB values
  51.         GpiSetBackMix(ped->hps, BM_OVERPAINT);
  52.                      // New text should wipe out old
  53.         ped->hrgnT = GpiCreateRegion (ped->hps, 0L, NULL);
  54.  
  55.  
  56.         // set the scrolling to the beginning of the document
  57.         ped->pixCurMax = 1;
  58.         ped->hScroll = 0;
  59.  
  60.         // initialize visible line information
  61.         // assumes NULL == 0
  62.         pmr = InsEndMarker(ped,ped->pprHead,MARKER_DISPLAY);
  63.         LockMarker(pmr,TRUE);
  64.         ped->pmrFVL = pmr;
  65.         ped->iptFVL = 0;
  66.         ped->papmrValid = (PPMR)AllocStore(ped,(sizeof(PMR))*DISPLAY_MAXLINES);
  67.         LFillStruct((PCH)(ped->papmrValid),(sizeof(PMR))*DISPLAY_MAXLINES,(BYTE)0);
  68.  
  69.         // compute current slider positions and notify owner
  70.  
  71.         NotifyScroll(ped, EN_HSCROLL, TRUE);
  72.         NotifyScroll(ped, EN_VSCROLL, TRUE);
  73.  
  74.         ped->fFocus = FALSE;
  75.  
  76.         ped->fFattrDelta = FALSE;
  77.         ped->fHSizeDelta = FALSE;
  78.         ped->fVSizeDelta = FALSE;
  79.         ped->fScrollDelta = FALSE;
  80.  
  81.         ped->TabSize = DEFAULT_TABSIZE;
  82.  
  83.         ped->pcwt = AllocStore(ped, MAXCHAR*sizeof(LONG));
  84.         DispChangeFont(ped,NULL);
  85.  
  86.         ped->pixCurMax = ped->AveCharWidth;  // decent initial value
  87.  
  88.         ped->fExistsCursor = FALSE;
  89.         ped->fInvert = FALSE;
  90. }
  91.  
  92.  
  93. /*
  94.  * VOID SetFocus(PED ped, BOOL fReceiveFocus)
  95.  *
  96.  * Handle setting/losing focus
  97.  */
  98. public VOID SetFocus(PED ped, BOOL fReceiveFocus)
  99. {
  100.         if (fReceiveFocus) {
  101.                 if (!ped->fFocus) {     // if we didn't already have focus...
  102.                         ped->fFocus = TRUE;
  103.                         NotifyOwner(ped, EN_SETFOCUS, NULL);
  104.                 }
  105.         } else { // losing focus
  106.                 DestroyCursor(ped);
  107.                 ped->fFocus = FALSE;
  108.                 NotifyOwner(ped, EN_KILLFOCUS, NULL);
  109.         }
  110. //        ped->fSelDelta = TRUE;
  111. //        DispRefresh(ped, FALSE, ped->rclWnd);
  112. }
  113.  
  114. /*
  115.  * VOID DispChangeFont(PED ped, PFATTRS pfattrs)
  116.  *
  117.  * Change the drawing font
  118.  */
  119. public VOID DispChangeFont(PED ped, PFATTRS pfattrs)
  120. {
  121.         FONTMETRICS fntmet;
  122.         SHORT s;
  123.         PIX pix;
  124.  
  125.         if (pfattrs == NULL ) {         // Reset to the default (system) font
  126.                 GpiSetCharSet (ped->hps, LCID_DEFAULT);
  127.                 if (ped->pfattrs != NULL) {
  128.                         ped->fFattrDelta = TRUE;
  129.                         ped->pfattrs = NULL;
  130.                 }
  131.         } else {
  132.                 /* Use the pfattrs we've been passed to select the font
  133.                  * If we can create the special font, AND if we can set to it,
  134.                  * then reset the entry in the edit record to this pfattrs to
  135.                  * indicate our success.
  136.                  */
  137.  
  138.                 if (GpiCreateLogFont(ped->hps, (PSTR8)"DUMMY",
  139.                         DUMMY_FONT_SETID, pfattrs) &&
  140.                     GpiSetCharSet(ped->hps, DUMMY_FONT_SETID)) {
  141.                         GpiSetCharSet (ped->hps, LCID_DEFAULT);
  142.                         GpiDeleteSetId(ped->hps, DUMMY_FONT_SETID);
  143.                         GpiDeleteSetId(ped->hps, SPECIAL_FONT_SETID);
  144.                         GpiCreateLogFont(ped->hps, (PSTR8)"DUMMY",
  145.                                 SPECIAL_FONT_SETID, pfattrs);
  146.                         GpiSetCharSet(ped->hps, SPECIAL_FONT_SETID);
  147.                         ped->pfattrs = pfattrs;
  148.                 }
  149.                 ped->fFattrDelta = TRUE;
  150.         }
  151.  
  152.         /* Insure the correct character mode */
  153.         GpiSetCharMode (ped->hps, CM_MODE1);
  154.  
  155.         /* Get font size information for the new font */
  156.         GpiQueryFontMetrics(ped->hps,(LONG)sizeof(fntmet),&fntmet);
  157.  
  158.         ped->AveCharWidth = (SHORT) fntmet.lAveCharWidth;
  159.         ped->yCharPels = (SHORT) fntmet.lMaxBaselineExt;
  160.         ped->yDescPels = (SHORT) fntmet.lMaxDescender;
  161.         ped->pixMaxChar = (PIX) fntmet.lMaxCharInc;
  162.  
  163.         /* Get width table information for this font.  Starting from
  164.            MINCHAR, read MAXCHAR widths into the global width table. */
  165.         GpiQueryWidthTable (ped->hps, MINCHAR, MAXCHAR, ped->pcwt);
  166.  
  167.         pix = ped->pixMaxChar;
  168.         for (s=0; s<(MAXCHAR); s++) {
  169.             if (ped->pcwt[s] < pix) {
  170.                 pix = (PIX)ped->pcwt[s];
  171.             }
  172.         }
  173.         ped->pixMinChar = pix;
  174.  
  175.         /* Set the width for a return character to zero */
  176.         ped->pcwt['\n'] = (UCHAR) 0;
  177.  
  178. }
  179.  
  180.  
  181. /*
  182.  * VOID DispChangeSize(PED ped,USHORT xWndPels,USHORT yWndPels)
  183.  *
  184.  * Called at WM_SIZE.  Updates all the window's dimensions (in ped);
  185.  * requests reformatting later.  Note that MLE has the class style
  186.  * CS_SIZEREDRAW, so the window will be redrawn automatically.
  187.  *
  188.  * Assumes font information is correct (DispChangeFont must have been called)
  189.  */
  190. public VOID DispChangeSize (PED ped, USHORT xWndPels, USHORT yWndPels)
  191. {
  192.     LINE lyWndSize;         // text window height in chars
  193.     PIX xTextWidth;         // text window width in pixels
  194.     PIX dxMargLft, dxMargRgt,
  195.           dyMargTop, dyMargBot;   // margins in pixels
  196.     RECTL rclOld;           // old text rectangle
  197.  
  198.     /* Find the rectangle of the window's area, in window coordinates. */
  199.  
  200.     ped->rclWnd.xLeft = 0L;
  201.     ped->rclWnd.yBottom = 0L;
  202.     ped->rclWnd.xRight = xWndPels;
  203.     ped->rclWnd.yTop = yWndPels;
  204.  
  205.     /* Find the size of the margins. */
  206.  
  207.     dxMargLft = (ped->AveCharWidth * MARG_LFTNUM) / MARG_LFTDEN;
  208.     dxMargRgt = (ped->AveCharWidth * MARG_RGTNUM) / MARG_RGTDEN;
  209.     dyMargTop = (ped->yCharPels * MARG_TOPNUM) / MARG_TOPDEN;
  210.     dyMargBot = (ped->yCharPels * MARG_BOTNUM) / MARG_BOTDEN;
  211.  
  212.     /* Find the number of lines we can fit into the window, not counting
  213.      * the margins.
  214.      */
  215.  
  216.     lyWndSize = max((LINE)0,
  217.                    (LINE)((yWndPels-dyMargBot-dyMargTop)/ped->yCharPels));
  218.     ped->lyWndSize =  lyWndSize;
  219.  
  220.  
  221.     /* Now that we know how many characters fit into the window horizontally
  222.        and vertically, we can determine the minimum rectangle that holds those
  223.        characters.  The minimum rectangle may actually be shorter than the
  224.        space between the top and bottom margins, since we rounded down to an
  225.        integral number of lines; we center it in that space.
  226.     */
  227.  
  228.     /* Rectangles:
  229.         There are two rectangles associated with this edit control.  The
  230.         first is the rectangle defining the edit control window, which is
  231.         stored in ped->rclWnd.  Within this, there's a slightly smaller
  232.         text rectangle (ped->rclText) within which all text is displayed,
  233.         thus providing a nice margin.  The width of the text window, in
  234.         pixels, is stored in xTextWidth; its height in lines is stored
  235.         in ped->lyWndSize.
  236.     */
  237.  
  238.     rclOld = ped->rclText;
  239.  
  240.     ped->rclText.xLeft = dxMargLft;
  241.     ped->rclText.xRight = max(xWndPels-dxMargRgt,dxMargLft);
  242.     xTextWidth = (SHORT)ped->rclText.xRight - (SHORT)ped->rclText.xLeft;
  243.     ped->rclText.yBottom = (yWndPels+(dyMargBot-dyMargTop-ped->yCharPels*lyWndSize))/2;
  244.     ped->rclText.yTop = ped->rclText.yBottom+lyWndSize*ped->yCharPels;
  245.  
  246.     ped->fHSizeDelta = ped->fHSizeDelta ||
  247.             (ped->rclText.xLeft != rclOld.xLeft) ||
  248.             (ped->rclText.xRight != rclOld.xRight);
  249.  
  250.     ped->fVSizeDelta = (ped->rclText.yTop != rclOld.yTop) ||
  251.             ped->fVSizeDelta || (ped->rclText.yBottom != rclOld.yBottom);
  252.  
  253. }
  254.  
  255.  
  256. /***********************************************************************
  257. *  Cursor/selection handling
  258. ***********************************************************************/
  259.  
  260. /*
  261.  * VOID DestroyCursor(PED ped)
  262.  *
  263.  * Destroy the cursor.
  264.  */
  265. private VOID DestroyCursor(PED ped)
  266. {
  267.     if (ped->fExistsCursor) {
  268.         WinDestroyCursor(ped->hwnd);
  269.         ped->fExistsCursor = FALSE;
  270.     }
  271. }
  272.  
  273.  
  274. /*
  275.  * VOID PositionCursor(PED ped, PIX pixY, PIX pixX, BOOL fForce)
  276.  *
  277.  * position the cursor to window x,y.  If fForce, force sizing of the
  278.  * cursor; otherwise, just move it if possible.
  279.  */
  280. private VOID PositionCursor(PED ped, PIX pixY, PIX pixX, BOOL fForce)
  281. {
  282.         if (!(ped->fFocus)) {
  283.                 DestroyCursor(ped);
  284.         } else /* if (!(ped->fMouseDown)) */ {
  285.                 WinCreateCursor(ped->hwnd,
  286.                         (SHORT)(pixX+ped->rclText.xLeft-ped->hScroll),
  287.                         (SHORT)pixY,
  288.                         (SHORT)0,
  289.                         (SHORT)(ped->yCharPels),
  290.                         ((ped->fExistsCursor && !fForce)
  291.                                 ? (CURSOR_SETPOS)
  292.                                 : (CURSOR_SOLID | CURSOR_FLASH)),
  293.                         &(ped->rclWnd));
  294.                 WinShowCursor(ped->hwnd, TRUE);
  295.                 ped->fExistsCursor = TRUE;
  296.         }
  297. }
  298.  
  299. /*
  300.  * VOID HideCursor(PED ped)
  301.  *
  302.  * Hide the cursor, if any exists.
  303.  */
  304. private VOID HideCursor(PED ped)
  305. {
  306.     if (ped->fExistsCursor) {
  307.         WinShowCursor(ped->hwnd, FALSE);
  308.     }
  309. }
  310.  
  311.  
  312. /***********************************************************************
  313. *  Text drawing helpers
  314. ***********************************************************************/
  315.  
  316.  
  317. /*
  318.  * VOID SetInvert(PED ped, BOOL fInvert)
  319.  *
  320.  * Set the drawing colors as either normal or inverted based on the
  321.  * fInvert flag.
  322.  */
  323. private VOID SetInvert(PED ped, BOOL fInvert)
  324. {
  325.     if (ped->fInvert != fInvert) {
  326.         GpiSetBackColor(ped->hps, fInvert ? SYSCLR_WINDOWTEXT : SYSCLR_WINDOW);
  327.         GpiSetColor(ped->hps, fInvert ? SYSCLR_WINDOW : SYSCLR_WINDOWTEXT);
  328.         ped->fInvert = fInvert;
  329.     }
  330. }
  331.  
  332. /*
  333.  * VOID WipePix(PED ped, PIX pix, BOOL fInvert)
  334.  *
  335.  * From current position, wipe out pix pixels.  If pix <= 0, wipe to end of
  336.  * line.  Sets current position appropriately.  Colour is dependent on
  337.  * fInvert.
  338.  */
  339. private VOID WipePix(PED ped, PIX pix, BOOL fInvert)
  340. {
  341.         RECTL  rclWipe;
  342.         POINTL ptlEnd;
  343.  
  344.         GpiQueryCurrentPosition(ped->hps, &ptlEnd);
  345.         rclWipe.yBottom = ptlEnd.y-ped->yDescPels;
  346.         rclWipe.yTop = rclWipe.yBottom + ped->yCharPels;
  347.         rclWipe.xLeft = ptlEnd.x;
  348.         rclWipe.xRight = (pix<0)
  349.                                 ? (ped->rclText.xRight)
  350.                                 : (ptlEnd.x+pix);
  351.         WinFillRect(ped->hps, (PRECTL)&rclWipe,
  352.                     fInvert ? SYSCLR_WINDOWTEXT : SYSCLR_WINDOW);
  353.         ptlEnd.x = rclWipe.xRight;
  354.         GpiMove(ped->hps, &ptlEnd);
  355. }
  356.  
  357. #define OBUFFSIZE 100
  358.  
  359. /*
  360.  * VOID OutputBuff(PED ped, PCHAR pch, POFFSET poff)
  361.  *
  362.  * Outputs the contents of the output buffer.
  363.  */
  364. private VOID OutputBuff(PED ped, PCHAR pch, POFFSET poff)
  365. {
  366.         if  (*poff > 0) {
  367.                 GpiCharString(ped->hps, (LONG)*poff, pch);
  368.                 *poff = 0;
  369.         }
  370. }
  371.  
  372.  
  373. /*
  374.  * VOID AppendBuff(PED ped, PCHAR pch, POFFSET poff, CHAR ch)
  375.  *
  376.  * Appends ch to the end of the output buffer, flushing if the output
  377.  * buffer is full.
  378.  */
  379. private VOID AppendBuff(PED ped, PCHAR pch, POFFSET poff, CHAR ch)
  380. {
  381.         *(pch+((*poff)++)) = ch;
  382.         if (*poff > OBUFFSIZE)
  383.                 OutputBuff(ped, pch, poff);
  384. }
  385.  
  386.  
  387. /*
  388.  * VOID ExtRedrawSel(PED ped, PMR pmr, IPT ipt, PIPT piptMin, PIPT piptMax,
  389.  *                   PBOOL pfMatched)
  390.  *
  391.  * Given a range of characters that is known to need redrawing, extend
  392.  * that range (if needed) to include changes to the selection.
  393.  * Changes *piptMin and *piptMax accordingly; if *piptMin is changed,
  394.  * *pfMatched is set to FALSE (otherwise left alone).  pmr is the
  395.  * line break record for the line in question; ipt is the starting ipt
  396.  * of that line.
  397.  */
  398. private VOID ExtRedrawSel(PED ped, PMR pmr, IPT ipt, PIPT piptMin,
  399.                           PIPT piptMax, PBOOL pfMatched)
  400. {
  401. #define exch(a, b, t) {t=a; a=b; b=t;}
  402.     IPT a0, a1, a2, a3, t;
  403.  
  404.     // find out what's been drawn, and what should be, and save for next time
  405.     a0 = pmr->cchBegin;
  406.     a1 = pmr->cchEnd;
  407.     pmr->cchBegin = a2 = max((IPT)0,min(pmr->cchLine,TxtQueryMinSel(ped)-ipt));
  408.     pmr->cchEnd = a3 = max((IPT)0,min(pmr->cchLine,TxtQueryMaxSel(ped)-ipt));
  409.  
  410.     // sort a0..a3 ascending
  411.     if (a1>a2)
  412.         exch(a1,a2,t);
  413.     if (a0>a1)
  414.         exch(a0,a1,t);
  415.     if (a2>a3)
  416.         exch(a2,a3,t);
  417.     if (a1>a2)
  418.         exch(a1,a2,t);
  419. #undef exch
  420.  
  421.     // extend the range as needed on the low end
  422.     if  (a0 != a1) {
  423.         if (a0 < *piptMin) {
  424.             *pfMatched = FALSE;
  425.             *piptMin = a0;
  426.         }
  427.         if (a1 > *piptMax) {
  428.             *piptMax = a1;
  429.         }
  430.     }
  431.  
  432.     // extend the range as needed on the high end
  433.     if (a2 != a3) {
  434.         if (a2 < *piptMin) {
  435.             *pfMatched = FALSE;
  436.             *piptMin = a2;
  437.         }
  438.         if (a3 > *piptMax) {
  439.             *piptMax = a3;
  440.         }
  441.     }
  442. }
  443.  
  444. /*
  445.  * VOID ExtRedrawRcl(PED ped, RECTL rcl, PPIX ppixMin, PPIX ppixMax,
  446.  *                   PBOOL pfMatched)
  447.  *
  448.  * Given a range of pixels that is known to need redrawing, extend
  449.  * that range (if needed) to include the redraw rectangle.
  450.  * Changes *ppixMin and *ppixMax accordingly; if *ppixMin is changed,
  451.  * *pfMatched is set to FALSE (otherwise left alone).
  452.  * Rectangle is in window coordinates.
  453.  */
  454. private VOID ExtRedrawRcl(PED ped, RECTL rcl, PPIX ppixMin,
  455.                           PPIX ppixMax, PBOOL pfMatched)
  456. {
  457.     PIX xL, xR;
  458.  
  459.     xL = (PIX)rcl.xLeft - (PIX)ped->rclText.xLeft + ped->hScroll;
  460.     xR = (PIX)rcl.xRight - (PIX)ped->rclText.xLeft + ped->hScroll;
  461.  
  462.     // extend the range as needed on the low end
  463.     if  (xL != xR) {
  464.         if (xL < *ppixMin) {
  465.             *pfMatched = FALSE;
  466.             *ppixMin = xL;
  467.         }
  468.         if (xR > *ppixMax) {
  469.             *ppixMax = xR;
  470.         }
  471.     }
  472. }
  473.  
  474.  
  475. /***********************************************************************
  476. *  General drawing drivers
  477. ***********************************************************************/
  478.  
  479. /*
  480.  * PMR DrawLine(PED ped, PMR pmr, LINE ln, IPT ipt, RECTL rcl, BOOL fForce)
  481.  *
  482.  * Draw one line, given pointer to line-break record and the screen
  483.  * position.  Returns pointer to marker record for next line.
  484.  * If fForce is FALSE, only the invalid part of the line will be redrawn;
  485.  * otherwise, the entire line will be redrawn.
  486.  */
  487. private PMR DrawLine(PED ped,PMR pmr,LINE ln,IPT ipt,RECTL rcl,BOOL fForce)
  488. {
  489.     POINTL ptl;
  490.     PIX pix;
  491.     PPR ppr, ppr2;
  492.     OFFSET off;
  493.     IPT iptMin, iptMax, iptCur, iptIMin, iptIMax;
  494.     PIX pixMin, pixMax;
  495.     IPT iptOff;
  496.     static CHAR obuff[OBUFFSIZE];
  497.     OFFSET obuffptr;
  498.     register CHAR ch;
  499.     register PIX pixCh;
  500.     BOOL fMatched;
  501.     BOOL fLastLine;
  502.  
  503.     fLastLine = TxtLength(ped) == ipt + pmr->cchLine;
  504.  
  505.     // keep track of where in line the cursor falls
  506.     iptCur = TxtQueryCursor(ped)-ipt;
  507.     iptIMin = TxtQueryMinSel(ped);
  508.     iptIMax = TxtQueryMaxSel(ped);
  509.  
  510.     // find out where this line should be drawn, vertically
  511.     ptl.y = ped->rclText.yTop-(ped->yCharPels*(ln+1))+ped->yDescPels;
  512.  
  513.     // set the range to be redrawn; take redraw rectangle and selection
  514.     // into account
  515.     fMatched = TRUE;
  516.     pixMin = fForce ? (PIX)0 : pmr->pixValid;
  517.     pixMax = pmr->pixLine;
  518.     iptMin = fForce ? (IPT)0 : pmr->cchValid;
  519.     iptMax = pmr->cchLine;
  520.     if (!fForce) {
  521.         ExtRedrawSel(ped, pmr, ipt, &iptMin, &iptMax, &fMatched);
  522.         ExtRedrawRcl(ped, rcl, &pixMin, &pixMax, &fMatched);
  523.     }
  524.  
  525.     // get ppr/off to the first text character in the line (if any)
  526.     if (pmr->cchLine == 0) {
  527.             ppr = pmr->pr.pprTextNext;
  528.     } else {
  529.             ppr = SkipMarkers((PPR)pmr, MARKER_ANY);
  530.     }
  531.     off = 0;
  532.  
  533.     // only do the drawing if there's something that needs to be
  534.     // redrawn:
  535.     if ((iptMin == pmr->cchLine) && (pixMin == pmr->pixLine)
  536.          && (pixMax == pmr->pixLine)) {
  537.         ptl.x = ped->rclText.xLeft - ped->hScroll + pmr->pixLine;
  538.         GpiMove(ped->hps, &ptl);
  539.     } else {
  540.         // if we still have a matched pixMin/iptMin, advance ppr/off that far.
  541.         if (fMatched) {
  542.             pix = pixMin;
  543.             ipt += iptMin;
  544.             if (iptMin == pmr->cchLine) {
  545.                 ppr = SkipText(ppr, MARKER_LINE);
  546.                 off = 0;
  547.             } else {
  548.                 iptOff = iptMin;
  549.                 ppr = PprOfOffset(ppr, &iptOff);
  550.                 off = (OFFSET)iptOff;
  551.             }
  552.         } else {
  553.                 pix = 0;
  554.         }
  555.  
  556.         // Search for the beginning of the text that should be drawn
  557.         ch = CharOfPointer(ppr,off);
  558.         pixCh = PixOfChar(ped, pix, ch);
  559.         while ((!(fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE))) &&
  560.                (pix+pixCh < pixMin) && (ipt < iptMin)) {
  561.             if (!fIsMarker(ppr)) {
  562.                 pix += pixCh;
  563.                 ipt++;
  564.             }
  565.             ch = AdvancePointer(&ppr,&off);
  566.             pixCh = PixOfChar(ped, pix, ch);
  567.         }
  568.  
  569.         // set up for drawing loop
  570.         ptl.x = ped->rclText.xLeft - ped->hScroll + pix;
  571.         GpiMove(ped->hps, &ptl);
  572.         obuffptr = 0;
  573.  
  574.         // buffer up and output chunks of text until a line marker
  575.         // is hit.  Handle tabs specially.
  576.         while ((!(fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE))) &&
  577.                 ((pix <= pixMax) || (ipt <= iptMax))) {
  578.             if  (!fIsMarker(ppr)) {
  579.                 if (((ipt >= iptIMin) && (ipt < iptIMax)) != ped->fInvert) {
  580.                     OutputBuff(ped, obuff, &obuffptr);
  581.                     SetInvert(ped,!ped->fInvert);
  582.                 }
  583.                 pixCh = PixOfChar(ped, pix, ch);
  584.                 if (ch == '\t') {
  585.                     OutputBuff(ped, obuff, &obuffptr);
  586.                     WipePix(ped, pixCh, ped->fInvert);
  587.                 } else if (ch == '\n') {
  588.                     AppendBuff(ped, obuff, &obuffptr, DISPLAY_NEWLINECHAR);
  589.                 } else {
  590.                     AppendBuff(ped, obuff, &obuffptr, ch);
  591.                 }
  592.                 pix += pixCh;
  593.                 ipt++;
  594.             } // else skip past marker
  595.             ch = AdvancePointer(&ppr, &off);
  596.         }
  597.          // flush the character buffer and clear to end-of-line
  598.         OutputBuff(ped, obuff, &obuffptr);
  599.  
  600.         // always wipe out the whitespace on the right when hit EOL
  601.         if (fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE)) {
  602.             WipePix(ped, (PIX)(-1), FALSE);
  603.         }
  604.     }
  605.  
  606.     // validate the line
  607.     pmr->sScreenLine = ln;
  608.     ped->papmrValid[ln] = pmr;
  609.     pmr->cchValid = pmr->cchLine;
  610.     pmr->pixValid = pmr->pixLine;
  611.  
  612.     // Set cursor within the line (if applicable)
  613.     if (((iptCur>=0) && (iptCur<pmr->cchLine)) ||
  614.            (iptCur == pmr->cchLine && fLastLine)) {
  615.         ppr2 = (PPR)pmr;
  616.         PositionCursor(ped,
  617.                        (SHORT)ptl.y-ped->yDescPels,
  618.                        PixFromText(ped,&ppr2,&off,(OFFSET)iptCur,0L),
  619.                        fForce);
  620.     }
  621.  
  622.     // return the next line break record
  623.     ppr = SkipText(ppr,MARKER_LINE);
  624.     return((PMR)ppr);
  625. }
  626. #undef OBUFFSIZE
  627.  
  628. /*
  629.  * VOID WhiteOutBottom(PED ped, LINE ln)
  630.  *
  631.  * White out the bottom region of the screen, starting  with line ln
  632.  * and including all screen area below.
  633.  */
  634. private VOID WhiteOutBottom(PED ped, LINE ln)
  635. {
  636.         RECTL rcl;
  637.  
  638.         rcl = ped->rclWnd;
  639.         rcl.yTop = ped->rclText.yTop-(ped->yCharPels*ln);
  640.         WinFillRect(ped->hps, (PRECTL)&rcl, SYSCLR_WINDOW);
  641. }
  642.  
  643. /***********************************************************************
  644. *  Scroll Handling
  645. ***********************************************************************/
  646.  
  647. /*
  648.  * struct _SCROLLCTX  -- a scrolling context
  649.  */
  650. struct _SCROLLCTX {
  651.         RECTL rcl;
  652.         PIX dy;
  653.         PMR pmrBegin;
  654.         LINE cln;
  655. };
  656. typedef struct _SCROLLCTX SCROLLCTX;
  657. typedef SCROLLCTX FAR *PSCROLLCTX;
  658.  
  659. /*
  660.  * VOID FlushScroll(PED ped, PSCROLLCTX psc)
  661.  *
  662.  * Given a rectangle that has been determined to require scrolling,
  663.  * do so.  When done, clear the scrolling information and record where
  664.  * lines are now.
  665.  */
  666. private VOID FlushScroll(PED ped, PSCROLLCTX psc)
  667. {
  668.     PMR pmr;
  669.     RECTL rclUpdate;
  670.     SHORT sRet;
  671.     LINE ln, lnMax;
  672.  
  673.     if (psc->cln > 0) {
  674.         // scroll the text
  675.         sRet = WinScrollWindow(ped->hwnd, 0, (SHORT)(psc->dy),
  676.                 (PRECTL)&(psc->rcl), (PRECTL)&(ped->rclText),
  677.                 NULL, (PRECTL)&rclUpdate, 0);
  678.  
  679.         // mark lines as valid if they're scrolled
  680.         pmr = psc->pmrBegin;
  681.         while (psc->cln) {
  682.             ped->papmrValid[pmr->sScreenLine]=pmr;
  683.             pmr = (PMR)SkipText(pmr->pr.pprTextNext,MARKER_LINE);
  684.             psc->cln--;
  685.         }
  686.  
  687.         // invalidate lines that need redrawing
  688.         lnMax = min(ped->lyWndSize, LineFromY(ped,(PIX)rclUpdate.yBottom));
  689.         for (ln = max(0,LineFromY(ped,(PIX)rclUpdate.yTop)); ln<lnMax; ln++) {
  690.             if (LineIntersectsRect(ped, ln, rclUpdate)) {
  691.                 ped->papmrValid[ln] = NULL;
  692.             }
  693.         }
  694.  
  695.         // clean up the scroll context
  696.         psc->dy = 0;
  697.     }
  698. }
  699.  
  700.  
  701. /*
  702.  * VOID ExtendScroll(PED ped, PSCROLLCTX psc)
  703.  *
  704.  * Given a scrolling rectangle and amount, add one line to the bottom of
  705.  * the rectangle.
  706.  */
  707. private VOID ExtendScroll(PED ped, PSCROLLCTX psc)
  708. {
  709.         psc->rcl.yBottom -= ped->yCharPels;
  710.         psc->cln++;
  711. }
  712.  
  713.  
  714. /*
  715.  * VOID SetScroll(PED ped, PSCROLLCTX psc, PIX dy, PMR pmr, LINE ln)
  716.  *
  717.  * Given that the text bits for line ln can be found by scrolling  them
  718.  * dy pixels, set up the appropriate scrolling context.
  719.  */
  720. private VOID SetScroll(PED ped, PSCROLLCTX psc, PIX dy, PMR pmr, LINE ln)
  721. {
  722.         psc->dy = dy;
  723.         psc->rcl = ped->rclText;
  724.         psc->pmrBegin = pmr;
  725.         psc->cln = 1;
  726.  
  727.         psc->rcl.yBottom = YFromLine(ped, ln) - ped->yDescPels - dy;
  728.         psc->rcl.yTop = psc->rcl.yBottom + ped->yCharPels;
  729. }
  730.  
  731.  
  732. /*
  733.  * VOID InitScroll(PED ped, PSCROLLCTX psc)
  734.  *
  735.  *  Initialize a scrolling context.
  736.  */
  737. private VOID InitScroll(PED ped, PSCROLLCTX psc)
  738. {
  739.         if (ped) psc->cln = 0;
  740. }
  741.  
  742.  
  743. /*
  744.  * VOID ScrollMovedLines(PED ped, PMR pmr, IPT ipt)
  745.  *
  746.  * Scroll lines that are valid (or partly so) but have moved on-screen.
  747.  * The first line on the screen is defined by pmr.  Ipt is the ipt
  748.  * immediately preceding the first character on that line.
  749.  */
  750. private VOID ScrollMovedLines(PED ped, PMR pmr, IPT ipt)
  751. {
  752.         LINE ln;
  753.         PMR pmr2;
  754.         PIX dyCur, dyLine;
  755.         SCROLLCTX sc;
  756.  
  757.         ln = 0;
  758.         InitScroll(ped,(PSCROLLCTX)&sc);
  759.         dyCur = 0;
  760.  
  761.         while ((ln<ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
  762.                 pmr2 = (PMR)SkipText(pmr->pr.pprTextNext, MARKER_LINE);
  763.  
  764.                 // try to scroll this line if it contains valid text
  765.                 if ((pmr->cchValid == pmr->cchLine) &&
  766.                     (pmr->sScreenLine != ln) &&
  767.                     (pmr->sScreenLine >= 0) &&
  768.                     (pmr->sScreenLine <= ped->lyWndSize) &&
  769.                     (pmr == ped->papmrValid[pmr->sScreenLine])) {
  770.                         dyLine = (pmr->sScreenLine - ln) * ped->yCharPels;
  771.                         if (dyLine != dyCur) {
  772.                                 FlushScroll(ped, &sc);
  773.                                 SetScroll(ped, &sc, dyLine, pmr, ln);
  774.                                 dyCur = dyLine;
  775.                         } else {
  776.                                 ExtendScroll(ped, &sc);
  777.                         }
  778.                         pmr->sScreenLine = ln;
  779.                 } else {
  780.                         dyCur = 0;
  781.                         FlushScroll(ped, &sc);
  782.                 }
  783.  
  784.                 // set up next line
  785.                 ln++;
  786.                 ipt += pmr->cchLine;
  787.                 pmr = pmr2;
  788.         }
  789.         FlushScroll(ped, &sc);
  790. }
  791.  
  792. /***********************************************************************
  793. * General redraw driver functions
  794. ***********************************************************************/
  795.  
  796. /*
  797.  * VOID DrawOnlyDirty(PED ped, RECTL rcl)
  798.  *
  799.  * Draw dirty or mismatched lines onto the screen
  800.  */
  801. private VOID DrawOnlyDirty(PED ped, RECTL rcl)
  802. {
  803.     LINE ln;
  804.     PMR pmr, pmr2;
  805.     IPT ipt;
  806.     BOOL fForce;
  807.  
  808.     // get marker for first line on screen
  809.     pmr = (PMR)SkipBackText((PPR)(ped->pmrFVL), MARKER_LINE);
  810.     ipt = ped->iptFVL;
  811.  
  812.     // Handle scroll possibilities before attempting to draw...
  813.     ScrollMovedLines(ped, pmr, ipt);
  814.  
  815.     // possibly draw each line...
  816.     ln = 0;
  817.     if (ped->fSelDelta)
  818.         HideCursor(ped);
  819.     while ((ln < ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
  820.         fIsMarker((PPR)pmr);
  821.         fForce = ((pmr->sScreenLine!=ln) ||
  822.                   (ped->papmrValid[ln]!=pmr));
  823.         if ((pmr->cchValid != pmr->cchLine) ||
  824.             (pmr->cchValid == 0) ||
  825.             fForce ||
  826.             (ped->fScrollDelta) ||
  827.             (ped->fSelDelta)) {
  828.             pmr2 = DrawLine(ped, pmr, ln, ipt, rcl, fForce);
  829.         } else {
  830.             pmr2 = (PMR)SkipText(pmr->pr.pprTextNext, MARKER_LINE);
  831.         }
  832.         ln++;
  833.         ipt += pmr->cchLine;
  834.         pmr = pmr2;
  835.     }
  836.     WhiteOutBottom(ped, ln);
  837. }
  838.  
  839.  
  840. /*
  841.  * VOID DrawEverything(PED ped)
  842.  *
  843.  * Draw all lines onto the screen
  844.  */
  845. private VOID DrawEverything(PED ped)
  846. {
  847.         LINE ln;
  848.         PMR pmr, pmr2;
  849.         IPT ipt;
  850.         HRGN hrgn, hrgnOld;
  851.  
  852.         HideCursor(ped);
  853.  
  854.         hrgn = GpiCreateRegion(ped->hps, 1L, (PRECTL)&(ped->rclWnd));
  855.         GpiSetClipRegion(ped->hps, hrgn, &hrgnOld);
  856.         GpiDestroyRegion(ped->hps, hrgnOld);
  857.  
  858.         WinFillRect(ped->hps, (PRECTL)&(ped->rclWnd), SYSCLR_APPWORKSPACE);
  859.  
  860.         hrgn = GpiCreateRegion(ped->hps, 1L, (PRECTL)&(ped->rclText));
  861.         GpiSetClipRegion(ped->hps, hrgn, &hrgnOld);
  862.         GpiDestroyRegion(ped->hps, hrgnOld);
  863.  
  864.         pmr = (PMR)SkipBackText((PPR)(ped->pmrFVL), MARKER_LINE);
  865.         ipt = ped->iptFVL;
  866.  
  867.         ln = 0;
  868.         while ((ln<ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
  869.                 pmr2 = DrawLine(ped, pmr, ln, ipt, ped->rclWnd, TRUE);
  870.                 ln++;
  871.                 ipt += pmr->cchLine;
  872.                 pmr = pmr2;
  873.         }
  874.         WhiteOutBottom(ped, ln);
  875. }
  876.  
  877.  
  878. /*
  879.  * VOID DispRefresh(PED ped, BOOL fForce, RECTL rcl)
  880.  *
  881.  * Refresh the display.   Only the area defined by the rectangle rcl needs
  882.  * to be considered.  Unless fForce is specified, only the minimum necessary
  883.  * redraw, based on change flags in the ped, will be drawn.
  884.  */
  885. public VOID DispRefresh(PED ped, BOOL fForce, RECTL rcl)
  886. {
  887.  
  888.     if  (ped->fFattrDelta || ped->fHSizeDelta) {
  889.         TxtReformat(ped, ped->pprHead, TRUE);
  890.         DrawEverything(ped);
  891.     } else if (fForce || ped->fVSizeDelta) {
  892.         DrawEverything(ped);
  893.     } else {
  894.         DrawOnlyDirty(ped,rcl);
  895.     }
  896.     ped->fFattrDelta = ped->fVSizeDelta = ped->fScrollDelta =
  897.         ped->fHSizeDelta = ped->fSelDelta = FALSE;
  898. }
  899.  
  900. /***********************************************************************
  901. * Miscellaneous queries
  902. ***********************************************************************/
  903.  
  904. /*
  905.  * PIX DispWidth(PED ped)
  906.  *
  907.  * Returns the width, in pixels, of the text-display area
  908.  */
  909. public PIX DispWidth(PED ped)
  910. {
  911.     return((SHORT)ped->rclText.xRight - (SHORT)ped->rclText.xLeft);
  912. }
  913.  
  914. /*
  915.  * LINE DispHeight(PED ped)
  916.  *
  917.  * Returns the height, in lines, of the text-display area
  918.  */
  919. public LINE DispHeight(PED ped)
  920. {
  921.     return(ped->lyWndSize);
  922. }
  923.  
  924. /*
  925.  * PIX YFromLine(PED ped, LINE ln)
  926.  *
  927.  * Returns the y-coordinate corresponding to the baseline of characters
  928.  * drawn on line ln.  Note that this function works properly even if
  929.  * ln does not correspond to a line visible on screen.
  930.  */
  931. private PIX YFromLine(PED ped, LINE ln)
  932. {
  933.     return((PIX)(ped->rclText.yTop)-(ped->yCharPels*(ln+1))+ped->yDescPels);
  934. }
  935.  
  936. /*
  937.  * BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl)
  938.  *
  939.  * returns whether the rectangle containing line ln, when intersected
  940.  * with the rectangle rcl, gives a non-null intersection.  Rcl is in
  941.  * window coordinates.
  942.  */
  943. private BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl)
  944. {
  945.     PIX y;
  946.  
  947.     y = YFromLine(ped, ln);
  948.     return((rcl.yBottom <= y-ped->yDescPels+ped->yCharPels) &&
  949.            (rcl.yTop >= y-ped->yDescPels));
  950. }
  951.  
  952.  
  953. /*
  954.  * LINE LineFromY(PED ped, PIX y)
  955.  *
  956.  * Returns the line containing the vertical coordinate y.
  957.  * Note that this function works properly even if
  958.  * y does not correspond to a line visible on screen.
  959.  */
  960. private LINE LineFromY(PED ped, PIX y)
  961. {
  962.     return((LINE)((ped->rclText.yTop - y) / ped->yCharPels));
  963. }
  964.  
  965. /***********************************************************************
  966. * Horizontal scroll setting
  967. ***********************************************************************/
  968.  
  969. /*
  970.  * VOID DispHScroll(PED ped, PIX dx, BOOL fNotify)
  971.  *
  972.  * changes the horizontal scrolling by dx (if possible).  If fNotify is
  973.  * true, a notification is sent to the owner window.
  974.  */
  975. public VOID DispHScroll(PED ped, PIX dx, BOOL fNotify)
  976. {
  977.         PIX hScrollOld;
  978.         RECTL rclScroll;
  979.         RECTL rclUpdate;
  980.         SHORT sRet;
  981.  
  982.         // normalise and update horizontal scrolling
  983.  
  984.         hScrollOld = ped->hScroll;
  985.         ped->hScroll = max(0,min(ped->pixCurMax-ped->AveCharWidth,
  986.                                  ped->hScroll+dx));
  987.         dx = ped->hScroll - hScrollOld;
  988.  
  989.         if (dx != 0) {
  990.             // find and scroll source rectangle
  991.             ped->fScrollDelta = TRUE;
  992.             rclScroll = ped->rclText;
  993.             if (dx > 0) { // increase scroll => shift display to left
  994.                 rclScroll.xLeft -= dx;
  995.             } else {
  996.                 rclScroll.xRight -= dx;
  997.             }
  998.             sRet = WinScrollWindow(ped->hwnd, (SHORT)(-dx), 0,
  999.                     (PRECTL)&rclScroll, (PRECTL)&(ped->rclText),
  1000.                     NULL, (PRECTL)&rclUpdate, 0);
  1001.             // paint exposed portion
  1002.             DispRefresh(ped, FALSE, rclUpdate);
  1003.     }
  1004.     if (fNotify) {
  1005.             NotifyScroll(ped, EN_HSCROLL, FALSE);
  1006.     }
  1007. }
  1008.  
  1009. /*
  1010.  * VOID DispHScrollTo(PED ped, PIX x, BOOL fNotify)
  1011.  *
  1012.  * changes the horizontal scrolling to x (or closest legal value).  If
  1013.  * fNotify is true, a notification is sent to the owner window.
  1014.  */
  1015. public VOID DispHScrollTo(PED ped, PIX x, BOOL fNotify)
  1016. {
  1017.     if (x < 0)
  1018.         x = 0;
  1019.  
  1020.     if (x > ped->pixCurMax - ped->AveCharWidth)
  1021.         x = ped->pixCurMax - ped->AveCharWidth;
  1022.  
  1023.     DispHScroll(ped, x-ped->hScroll, fNotify);
  1024. }
  1025.  
  1026.  
  1027. /***********************************************************************
  1028. * Vertical scroll setting
  1029. ***********************************************************************/
  1030.  
  1031. /*
  1032.  * VOID SetVScroll(PED ped, PMR pmrLine, IPT ipt, BOOL fNotify)
  1033.  *
  1034.  * Given a line and its ipt, set the FVL to that line.  Notify if needed,
  1035.  * and prod the display for refresh.
  1036.  */
  1037. private VOID SetVScroll(PED ped, PMR pmrLine, IPT ipt, BOOL fNotify)
  1038. {
  1039.     PMR pmrFVL;
  1040.     RECTL rcl;
  1041.  
  1042.     pmrFVL = ped->pmrFVL;
  1043.     LockMarker(pmrFVL, FALSE);
  1044.     DelMarker(ped, pmrFVL);
  1045.     pmrFVL = InsEndMarker(ped, (PPR)pmrLine, MARKER_DISPLAY);
  1046.     LockMarker(pmrFVL, TRUE);
  1047.     ped->pmrFVL = pmrFVL;
  1048.     ped->iptFVL = ipt;
  1049.     ZeroRect(rcl);
  1050.  
  1051.     DispRefresh(ped, FALSE, rcl);
  1052.  
  1053.     if (fNotify) {
  1054.         NotifyScroll(ped, EN_VSCROLL, TRUE);
  1055.     }
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  * VOID DispVScroll(PED ped, LINE dy, BOOL fNotify)
  1061.  *
  1062.  * changes the vertical scrolling by dy lines (if possible).  If fNotify is
  1063.  * true, a notification is sent to the owner window.
  1064.  */
  1065. public VOID DispVScroll(PED ped, LINE dy, BOOL fNotify)
  1066. {
  1067.     LINE cl;
  1068.     PMR pmrLine, pmrNext;
  1069.     IPT cchLine, iptLine;
  1070.  
  1071.     // get to the beginning of the FVL
  1072.     pmrLine = (PMR)SkipBackText((PPR)(ped->pmrFVL),MARKER_LINE);
  1073.     cchLine = TxtLengthToBOLPPR(ped,(PPR)(ped->pmrFVL),0);
  1074.     iptLine = ped->iptFVL - cchLine;
  1075.  
  1076.     // move dy lines forward or backward
  1077.     cl = dy;
  1078.     if (dy < 0) {
  1079.         while (cl && !(pmrLine->fFlags & MARKER_BOT)) {
  1080.             pmrLine = (PMR)SkipBackText(pmrLine->pr.pprTextPrev,MARKER_LINE);
  1081.             iptLine -= TxtLengthToEOLPPR(ped, (PPR)pmrLine, 0, TRUE);
  1082.             cl++;
  1083.         }
  1084.     } else if (dy > 0) {
  1085.         while (cl) {
  1086.             cchLine = TxtLengthToEOLPPR(ped, (PPR)pmrLine, 0, TRUE);
  1087.             pmrNext = (PMR)SkipText(pmrLine->pr.pprTextNext,MARKER_LINE);
  1088.             if (pmrNext->fFlags & MARKER_EOT)
  1089.                     break;
  1090.             iptLine += cchLine;
  1091.             pmrLine = pmrNext;
  1092.             cl--;
  1093.         }
  1094.     }
  1095.  
  1096.  
  1097.     // go with the line found
  1098.     SetVScroll(ped, pmrLine, iptLine, fNotify);
  1099. }
  1100.  
  1101. /*
  1102.  * VOID DispVScrollToIPT(PED ped, IPT ipt, BOOL fNotify)
  1103.  *
  1104.  * changes the vertical scrolling to line l (or closest legal value).  If
  1105.  * fNotify is true, a notification is sent to the owner window.
  1106.  */
  1107. public VOID DispVScrollToIPT(PED ped, IPT ipt, BOOL fNotify)
  1108. {
  1109.     PPR ppr;
  1110.     PMR pmrLine;
  1111.     IPT iptTemp;
  1112.  
  1113.     if (ipt == ped->iptMac)
  1114.             ipt--;
  1115.  
  1116.     iptTemp = ipt;
  1117.     ppr = TxtPPROfIpt(ped, &iptTemp);
  1118.     pmrLine = (PMR)SkipBackText(ppr, MARKER_LINE);
  1119.     ipt -= TxtLengthToBOLPPR(ped, ppr, (OFFSET)iptTemp);
  1120.     SetVScroll(ped, pmrLine, ipt, fNotify);
  1121. }
  1122.  
  1123.  
  1124. /***********************************************************************
  1125. * Cursor positioning, screen-relative
  1126. ***********************************************************************/
  1127.  
  1128.  
  1129. /*
  1130.  * VOID DispSetCursorXY(PED ped, PIX x, PIX y, BOOL fExtend, BOOL fWord)
  1131.  *
  1132.  * Sets the cursor position to the insertion point nearest the window
  1133.  * point (x,y).  If fExtend is true, only the cursor point is moved;
  1134.  * otherwise, both cursor and anchor are moved.  If fWord is true, the
  1135.  * anchor point (if any) is set to the beginning of the surrounding token;
  1136.  * the cursor point is set to the end of the surrounding token.  fWord
  1137.  * and fExtend can be combined.
  1138.  */
  1139. public VOID DispSetCursorXY(PED ped, PIX x, PIX y, BOOL fExtend, BOOL fWord)
  1140. {
  1141.     PMR pmr;
  1142.     PPR ppr;
  1143.     OFFSET off;
  1144.     IPT ipt, iptA, iptC;
  1145.     LINE ln;
  1146.     PIX xUsed;
  1147.     RECTL rcl;
  1148.  
  1149.     ln = LineFromY(ped,y);
  1150.  
  1151.     if (ln < 0) {
  1152.         DispVScroll(ped, -1, TRUE);
  1153.         ln = 0;
  1154.     } else if (ln > ped->lyWndSize) {
  1155.        DispVScroll(ped, 1, TRUE);
  1156.        ln = ped->lyWndSize;
  1157.     }
  1158.  
  1159.     if ((LONG)x < ped->rclText.xLeft) {
  1160.         DispHScroll(ped, -(DISPLAY_SCROLLACWS*ped->AveCharWidth), TRUE);
  1161.         x = (PIX)(ped->rclText.xLeft);
  1162.     } else if ((LONG)x > ped->rclText.xRight) {
  1163.         DispHScroll(ped, DISPLAY_SCROLLACWS*ped->AveCharWidth, TRUE);
  1164.         x = (PIX)(ped->rclText.xRight);
  1165.     }
  1166.  
  1167.     ipt = ped->iptFVL;
  1168.     pmr = (PMR)SkipBackText((PPR)ped->pmrFVL,MARKER_LINE);
  1169.     off = 0;
  1170.  
  1171.     while (ln && (!(pmr->fFlags & MARKER_EOT))) {
  1172.         ipt += pmr->cchLine;
  1173.         ln--;
  1174.         pmr = (PMR)SkipText(pmr->pr.pprTextNext,MARKER_LINE);
  1175.     }
  1176.  
  1177.     if (pmr->fFlags & MARKER_EOT) {
  1178.         iptA = iptC = TxtLength(ped);
  1179.     } else {
  1180.         ppr = pmr->pr.pprTextNext;
  1181.         xUsed = max(0,x-(PIX)(ped->rclText.xLeft)+ped->hScroll);
  1182.         if (xUsed > pmr->pixLine) {
  1183.             ipt += pmr->cchLine;
  1184.             if (fIsMarkerType(SkipText(ppr,MARKER_LINE),MARKER_LB)) {
  1185.                ipt--;
  1186.             }
  1187.         } else {
  1188.             ipt += TextFromPix(ped, &ppr, &off, &xUsed, ROUND_CLOSEST);
  1189.         }
  1190.         if (fWord) {
  1191.             iptA = ipt - TxtCchToBegOfToken(ppr,off);
  1192.             if (fExtend && (ipt < TxtQueryAnchor(ped))) {
  1193.                 iptC = iptA;
  1194.             } else {
  1195.                 iptC = ipt + TxtCchToEndOfToken(ppr,off);
  1196.             }
  1197.         } else {
  1198.             iptA = iptC = ipt;
  1199.         }
  1200.     }
  1201.  
  1202.     TxtSetAnchorCursor(ped, fExtend?(IPT)(-1):iptA, iptC);
  1203.     ZeroRect(rcl);
  1204.     DispRefresh(ped, FALSE, rcl);
  1205. }
  1206.