home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / textview.c < prev    next >
Text File  |  2002-08-11  |  140KB  |  3,874 lines

  1.  
  2. /*
  3.  *@@sourcefile textview.c:
  4.  *      all-new XTextView control as well as device-independent
  5.  *      text formatting and printing. Whoa.
  6.  *
  7.  *      <B>Text view control</B>
  8.  *
  9.  *      This is a read-only control to display any given text
  10.  *      (PSZ) in any given font. As opposed to a multi-line entry
  11.  *      field (MLE), this can handle multiple fonts and character
  12.  *      and paragraph formatting. Also, this thing sets its scroll
  13.  *      bars right, which is one of the most annoying bugs in the
  14.  *      MLE control.
  15.  *
  16.  *      This is currently in the process of turning into a full-fledged
  17.  *      "rich text" control. For example, the WarpIN "Readme" pages
  18.  *      use this control.
  19.  *
  20.  *      This is all new with V0.9.1. Great changes have been made
  21.  *      with V0.9.3.
  22.  *
  23.  *      To use the text view control, you must call txvRegisterTextView
  24.  *      in your application first. This registers the WC_XTEXTVIEW
  25.  *      window class with PM.
  26.  *
  27.  *      Then create your XTextView using WinCreateWindow. The
  28.  *      XTextView control has an XTEXTVIEWCDATA control data
  29.  *      structure which optionally can be passed to WinCreateWindow
  30.  *      like this:
  31.  *
  32.  +          XTEXTVIEWCDATA xtxvCData;
  33.  +          memset(&xtxvCData, 0, sizeof(xtxvCData));
  34.  +          xtxvCData.cbData = sizeof(xtxvCData);
  35.  +          xtxvCData.flStyle  = XTXF_VSCROLL;
  36.  +          xtxvCData.ulXBorder = 20;
  37.  +          xtxvCData.ulYBorder = 20;
  38.  +          G_hwndProcView = WinCreateWindow(hwndClient,        // parent
  39.  +                                           WC_XTEXTVIEW,      // class
  40.  +                                           "",                // title, always ignored
  41.  +                                           WS_VISIBLE,        // style flags
  42.  +                                           0, 0, 100, 100,    // pos and size
  43.  +                                           hwndClient,        // owner
  44.  +                                           HWND_TOP,          // z-order
  45.  +                                           ID_PROCINFO,       // win ID
  46.  +                                           &xtxvCData,        // control data
  47.  +                                           0);                // presparams
  48.  +
  49.  *      <B>Setting the text to be displayed</B>
  50.  *
  51.  *      The text to be displayed must be passed to the control using
  52.  *      the standard WinSetWindowText function (upon which PM sends a
  53.  *      WM_SETWINDOWPARMS message to the control), which is then
  54.  *      automatically formatted and painted.
  55.  *
  56.  *      However, since the XTextView control is extremely capable,
  57.  *      a few things need to be noted:
  58.  *
  59.  *      -- The text view assumes that lines terminate with \n ONLY.
  60.  *         The \r char is used for soft line breaks (start new line
  61.  *         in the same paragraph, similar to the HTML BR tag). If
  62.  *         you give the control the usual OS/2 \r\n sequence, you
  63.  *         get large spacings. Use txvStripLinefeeds to strip the
  64.  *         \r characters before setting the text.
  65.  *
  66.  *         In short, to give the control any text, do this:
  67.  +
  68.  +              PSZ psz = ...  // whatever, load string
  69.  +              txvStripLinefeeds(&psz);        // reallocates
  70.  +              WinSetWindowText(hwndTextView, psz);
  71.  *
  72.  *      -- The control uses the \xFF (255) character internally as
  73.  *         an escape code for formatting commands. See "Escape codes"
  74.  *         below. If your text contains this character, you should
  75.  *         overwrite all occurences with spaces, or they will be
  76.  *         considered an escape code, which will cause problems.
  77.  *
  78.  *      -- If you don't care about learning all the escape codes,
  79.  *         you can automatically have HTML code converted to the
  80.  *         XTextView format using txvConvertFromHTML, which will
  81.  *         automatically insert all the codes right from plain
  82.  *         HTML. In the above code, use txvConvertFromHTML instead
  83.  *         of txvStripLinefeeds.
  84.  *
  85.  *      <B>Code page support</B>
  86.  *
  87.  *      The XTextView control assumes that the text given to it uses
  88.  *      the same codepage as the message queue (thread) on which
  89.  *      the control is running. So if you need codepage support,
  90.  *      issue WinSetCp before creating the text view control.
  91.  *
  92.  *      <B>Text formatting</B>
  93.  *
  94.  *      The XTextView control has a default paragraph format which
  95.  *      determines how text is formatted. If you don't change this
  96.  *      format, the control performs no word-wrapping and displays
  97.  *      all text "as is", that is, practically no formatting is
  98.  *      performed.
  99.  *
  100.  *      You can change the default paragraph format by sending
  101.  *      TXM_SETPARFORMAT to the control. This takes a XFMTPARAGRAPH
  102.  *      structure for input.
  103.  *
  104.  *      To quickly enable word-wrapping only, we have the extra
  105.  *      TXM_SETWORDWRAP message. This changes the word-wrapping flag
  106.  *      in the default paragraph format only so you don't have to
  107.  *      mess with all the rest.
  108.  *
  109.  *      The XTextView control is extremely fast in formatting. It
  110.  *      does pre-calculations once so that resizing the text
  111.  *      window does not perform a full reformat, but a quick
  112.  *      format based on the pre-calculations.
  113.  *
  114.  *      Presently, formatting is done synchronously. It is planned
  115.  *      to put formatting into a separate thread. Performance is
  116.  *      acceptable already now unless very large texts (> 200 KB)
  117.  *      are formatted (tested on a PII-400 machine).
  118.  *
  119.  *      <B>Presentation Parameters</B>
  120.  *
  121.  *      The XTextView control recognizes the following presentation
  122.  *      parameters:
  123.  *
  124.  *      -- PP_BACKGROUNDCOLOR; if not set, SYSCLR_DIALOGBACKGROUND
  125.  *         (per default gray) is used.
  126.  *
  127.  *      -- PP_FOREGROUNDCOLOR: if not set, SYSCLR_WINDOWSTATICTEXT
  128.  *         (per default blue) is used to signify that the text
  129.  *         cannot be worked on.
  130.  *
  131.  *      -- PP_FONTNAMESIZE: default font. This is the system font,
  132.  *         if not set.
  133.  *
  134.  *      This implies that fonts and colors can be dropped on the
  135.  *      control in the normal way. Font changes will cause a reformat.
  136.  *
  137.  *      Changing those presentation parameters is equivalent to
  138.  *      changing the corresponding fields in the default paragraph
  139.  *      format using TXM_SETPARFORMAT.
  140.  *
  141.  *      <B>Escape codes</B>
  142.  *
  143.  *      All XTextView escape codes start with a \xFF (255) character,
  144.  *      followed by at least one more character. The escape sequences
  145.  *      are variable in length and can have parameters. For details,
  146.  *      see textview.h where all these are listed.
  147.  *
  148.  *      Escape codes are evaluated by txvFormatText during formatting.
  149.  *
  150.  *      If you choose to give the text view control a text which
  151.  *      contains escape codes, you better make sure that you get the
  152.  *      exact codes right, or the text view control can crash. The
  153.  *      control has been optimized for speed, so no checking is done
  154.  *      on escape sequences.
  155.  *
  156.  *      <B>Device-independent text formatting</B>
  157.  *
  158.  *      If the features of the XTextView control satisfy your needs,
  159.  *      there's not much to worry about. However, if you're interested
  160.  *      in formatting the text yourself, here's more:
  161.  *
  162.  *      This file has the txvFormatText function, which is capable
  163.  *      of formatting an input string into any HPS. This works for
  164.  *      windows (used by the text view control) and printers (used
  165.  *      by txvPrint). Word-wrapping is supported. This is used by
  166.  *      the XTextView control internally whenever (re)formatting is
  167.  *      needed: either when the text is set or the formatting parameters
  168.  *      (fonts, margins, etc.) have changed.
  169.  *
  170.  *      These functions are designed to be used in a two-step process:
  171.  *      first format the text (using txvFormatText), then paint it
  172.  *      (using txvPaintText) for viewing or printing.
  173.  *      This speeds up painting dramatically, because formatting
  174.  *      might take some time.
  175.  *
  176.  *      Note: Version numbering in this file relates to XWorkplace version
  177.  *            numbering.
  178.  *
  179.  *@@header "helpers\textview.h"
  180.  *
  181.  *@@added V0.9.1 (2000-02-13) [umoeller]
  182.  */
  183.  
  184. /*
  185.  *      Copyright (C) 2000 Ulrich Möller.
  186.  *      This program is part of the XWorkplace package.
  187.  *      This program is free software; you can redistribute it and/or modify
  188.  *      it under the terms of the GNU General Public License as published by
  189.  *      the Free Software Foundation, in version 2 as it comes in the COPYING
  190.  *      file of the XWorkplace main distribution.
  191.  *      This program is distributed in the hope that it will be useful,
  192.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  193.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  194.  *      GNU General Public License for more details.
  195.  */
  196.  
  197. #define OS2EMX_PLAIN_CHAR
  198.     // this is needed for "os2emx.h"; if this is defined,
  199.     // emx will define PSZ as _signed_ char, otherwise
  200.     // as unsigned char
  201.  
  202. #define OS2EMX_PLAIN_CHAR
  203.     // this is needed for "os2emx.h"; if this is defined,
  204.     // emx will define PSZ as _signed_ char, otherwise
  205.     // as unsigned char
  206.  
  207. #define INCL_WINWINDOWMGR
  208. #define INCL_WINFRAMEMGR
  209. #define INCL_WINMESSAGEMGR
  210. #define INCL_WININPUT
  211. #define INCL_WINRECTANGLES
  212. #define INCL_WINPOINTERS
  213. #define INCL_WINSYS
  214. #define INCL_WINSCROLLBARS
  215. #define INCL_WINSTDFONT
  216. #define INCL_WINCOUNTRY
  217.  
  218. #define INCL_DEV
  219. #define INCL_SPL
  220. #define INCL_SPLDOSPRINT
  221.  
  222. #define INCL_GPIPRIMITIVES
  223. #define INCL_GPILCIDS
  224. #define INCL_GPILOGCOLORTABLE
  225. #define INCL_GPITRANSFORMS
  226. #define INCL_GPIREGIONS
  227.  
  228. #define INCL_ERRORS
  229. #include <os2.h>
  230.  
  231. #include <stdlib.h>
  232. #include <stdio.h>
  233. #include <string.h>
  234.  
  235. #include "setup.h"                      // code generation and debugging options
  236.  
  237. #include "helpers\comctl.h"
  238. #include "helpers\gpih.h"
  239. #include "helpers\linklist.h"
  240. #include "helpers\stringh.h"
  241. #include "helpers\winh.h"
  242. #include "helpers\xstring.h"            // extended string helpers
  243.  
  244. #include "helpers\textview.h"
  245. #include "helpers\textv_html.h"
  246.  
  247. #pragma hdrstop
  248.  
  249. /*
  250.  *@@category: Helpers\PM helpers\Window classes\XTextView control
  251.  *      See textview.c.
  252.  */
  253.  
  254. /* ******************************************************************
  255.  *
  256.  *   Device-independent functions
  257.  *
  258.  ********************************************************************/
  259.  
  260. /*
  261.  *@@ txvInitFormat:
  262.  *
  263.  */
  264.  
  265. VOID txvInitFormat(PXFORMATDATA pxfd)
  266. {
  267.     memset(pxfd, 0, sizeof(XFORMATDATA));
  268.     lstInit(&pxfd->llRectangles,
  269.             TRUE);      // auto-free items
  270.     lstInit(&pxfd->llWords,
  271.             TRUE);      // auto-free items
  272.     xstrInit(&pxfd->strViewText, 0);
  273. }
  274.  
  275. /*
  276.  *@@ SetSubFont:
  277.  *
  278.  *@@added V0.9.3 (2000-05-06) [umoeller]
  279.  */
  280.  
  281. static VOID SetSubFont(HPS hps,
  282.                        PXFMTFONT pFont,
  283.                        ULONG ulPointSize,
  284.                        PSZ pszFaceName,
  285.                        ULONG flFormat)
  286. {
  287.     CHAR    ac[256];
  288.     ULONG   ul;
  289.     POINTL  ptlStart = {0, 0},
  290.             aptl[257];
  291.  
  292.     if (pFont->lcid)
  293.     {
  294.         // font already loaded:
  295.         if (GpiQueryCharSet(hps) == pFont->lcid)
  296.             // font currently selected:
  297.             GpiSetCharSet(hps, LCID_DEFAULT);
  298.         GpiDeleteSetId(hps, pFont->lcid);
  299.  
  300.     }
  301.  
  302.     if (pszFaceName)
  303.         pFont->lcid = gpihFindFont(hps,
  304.                                    ulPointSize,
  305.                                    TRUE,        // family, not face name
  306.                                    pszFaceName,
  307.                                    flFormat,
  308.                                    &pFont->FontMetrics);
  309.     else
  310.         pFont->lcid = LCID_DEFAULT; // 0
  311.  
  312.     GpiSetCharSet(hps, pFont->lcid);
  313.     if (pFont->FontMetrics.fsDefn & FM_DEFN_OUTLINE)
  314.         // is outline font:
  315.         gpihSetPointSize(hps, ulPointSize);
  316.  
  317.     for (ul = 0;
  318.          ul < 256;
  319.          ul++)
  320.         ac[ul] = ul;
  321.  
  322.     GpiQueryCharStringPosAt(hps,
  323.                             &ptlStart,
  324.                             0,
  325.                             254,        // starting at one
  326.                             ac + 1,     // starting at one
  327.                             NULL,
  328.                             aptl);
  329.     // now compute width of every char
  330.     for (ul = 1;
  331.          ul < 256;
  332.          ul++)
  333.     {
  334.         pFont->alCX[ul] = aptl[ul+1].x - aptl[ul].x;
  335.     }
  336. }
  337.  
  338. /*
  339.  *@@ SetFormatFont:
  340.  *      creates logical fonts from the specified
  341.  *      font information.
  342.  *
  343.  *@@added V0.9.3 (2000-05-06) [umoeller]
  344.  */
  345.  
  346. static VOID SetFormatFont(HPS hps,           // in: HPS to select default font into
  347.                           PXFMTCHARACTER pxfmtc, // in/out: format data
  348.                           ULONG ulPointSize, // in: font point size (e.g. 12) or 0
  349.                           PSZ pszFaceName)   // in: font face name (e.g. "Courier") or NULL
  350. {
  351.     pxfmtc->lPointSize = ulPointSize;
  352.  
  353.     // regular
  354.     SetSubFont(hps,
  355.                &pxfmtc->fntRegular,
  356.                ulPointSize,
  357.                pszFaceName,
  358.                0);
  359.  
  360.     // bold
  361.     SetSubFont(hps,
  362.                &pxfmtc->fntBold,
  363.                ulPointSize,
  364.                pszFaceName,
  365.                FATTR_SEL_BOLD);
  366.  
  367.     // italics
  368.     SetSubFont(hps,
  369.                &pxfmtc->fntItalics,
  370.                ulPointSize,
  371.                pszFaceName,
  372.                FATTR_SEL_ITALIC);
  373.  
  374.     // bold italics
  375.     SetSubFont(hps,
  376.                &pxfmtc->fntBoldItalics,
  377.                ulPointSize,
  378.                pszFaceName,
  379.                FATTR_SEL_BOLD | FATTR_SEL_ITALIC);
  380. }
  381.  
  382. /*
  383.  *@@ AppendCharNoCheck:
  384.  *
  385.  *@@added V0.9.3 (2000-05-07) [umoeller]
  386.  */
  387.  
  388. static VOID AppendCharNoCheck(char **ppszNew,
  389.                               PULONG pcbNew,
  390.                               char **ppTarget,
  391.                               char c)
  392. {
  393.     ULONG   cbSizeThis = *ppTarget - *ppszNew;
  394.     if (cbSizeThis >= *pcbNew)
  395.     {
  396.         // more mem needed:
  397.         *pcbNew += 10000;
  398.         *ppszNew = (PSZ)realloc(*ppszNew, *pcbNew);
  399.                     // if first call, pszNew is NULL, and realloc
  400.                     // behaves just like malloc
  401.         // adjust target, because ptr might have changed
  402.         *ppTarget = *ppszNew + cbSizeThis;
  403.     }
  404.  
  405.     **ppTarget = c;
  406.     (*ppTarget)++;
  407. }
  408.  
  409. /*
  410.  *@@ txvStripLinefeeds:
  411.  *      this removes all linefeeds (\r) from
  412.  *      the specified string to prepare it
  413.  *      for display with the text view control.
  414.  *
  415.  *      This also replaces tabs (\t) with ulTabSize spaces.
  416.  *
  417.  *      The buffer gets reallocated by this function, so it
  418.  *      must be free()'able.
  419.  *
  420.  *@@added V0.9.3 (2000-05-07) [umoeller]
  421.  *@@changed V0.9.20 (2002-08-10) [umoeller]: now stripping \xFF too
  422.  */
  423.  
  424. VOID txvStripLinefeeds(char **ppszText,
  425.                        ULONG ulTabSize)
  426. {
  427.     PSZ     pSource = *ppszText;
  428.     ULONG   cbNew = 1000;
  429.     PSZ     pszNew = (PSZ)malloc(cbNew);
  430.     PSZ     pTarget = pszNew;
  431.     ULONG   ul;
  432.  
  433.     while (*pSource)
  434.     {
  435.         switch (*pSource)
  436.         {
  437.             case '\r':
  438.                 pSource++;
  439.             break;
  440.  
  441.             case '\t':
  442.                 for (ul = 0;
  443.                      ul < ulTabSize;
  444.                      ul++)
  445.                     AppendCharNoCheck(&pszNew,
  446.                                       &cbNew,
  447.                                       &pTarget,
  448.                                       ' ');
  449.  
  450.                 // skip the tab
  451.                 pSource++;
  452.             break;
  453.  
  454.             case '\xFF':        // V0.9.20 (2002-08-10) [umoeller]
  455.                 AppendCharNoCheck(&pszNew,
  456.                                   &cbNew,
  457.                                   &pTarget,
  458.                                   ' ');
  459.                 pSource++;
  460.             break;
  461.  
  462.             default:
  463.                 AppendCharNoCheck(&pszNew,
  464.                                   &cbNew,
  465.                                   &pTarget,
  466.                                   *pSource++);
  467.         }
  468.     }
  469.  
  470.     AppendCharNoCheck(&pszNew,
  471.                       &cbNew,
  472.                       &pTarget,
  473.                       '\n');
  474.     AppendCharNoCheck(&pszNew,
  475.                       &cbNew,
  476.                       &pTarget,
  477.                       0);
  478.  
  479.     free(*ppszText);
  480.     *ppszText = pszNew;
  481. }
  482.  
  483. /* ******************************************************************
  484.  *
  485.  *   Device-independent text formatting
  486.  *
  487.  ********************************************************************/
  488.  
  489. /*
  490.  *@@ strhFindEOL2:
  491.  *      finds the end of a line.
  492.  *
  493.  *      An "end of line" means the next \r, \n, or \0 character
  494.  *      after *ppszSearchIn.
  495.  *
  496.  *      This returns the pointer to that exact character, which
  497.  *      can be equal or higher than *ppszSearchIn.
  498.  *      This should never return NULL because at some point,
  499.  *      there will be a null byte in your string (unless you have
  500.  *      a heap problem).
  501.  *
  502.  *      If the EOL character is not null (\0), *ppszSearchIN is
  503.  *      advanced to the first character of the _next_ line. This
  504.  *      can be the EOL pointer plus one if you have a UNIX-style
  505.  *      string (\n only at the end of each line) or EOL + 2 for
  506.  *      DOS and OS/2-style EOLs (which have \r\n at the end of
  507.  *      each line).
  508.  *
  509.  *@added V0.9.3 (2000-05-06) [umoeller]
  510.  */
  511.  
  512. static PSZ strhFindEOL2(PSZ *ppszSearchIn,        // in: where to search
  513.                         PULONG pulOffset)       // out: offset (ptr can be NULL)
  514. {
  515.     PSZ     pThis = *ppszSearchIn,
  516.             prc = NULL;
  517.     while (TRUE)
  518.     {
  519.         if ( (*pThis == '\r') || (*pThis == '\n') || (*pThis == 0) )
  520.         {
  521.             prc = pThis;
  522.             break;
  523.         }
  524.         pThis++;
  525.     }
  526.  
  527.     // before modifying pointer, store offset
  528.     if (pulOffset)
  529.         *pulOffset = prc - *ppszSearchIn;
  530.  
  531.     if (*prc == 0)
  532.     {
  533.         // null byte (end of string):
  534.         *ppszSearchIn = prc;
  535.     }
  536.     else
  537.     {
  538.         // not null byte (end of string):
  539.         // skip following newline characters
  540.         if (*prc == '\r')
  541.         {
  542.             if ( *(prc+1) == '\n')
  543.                 // we have a \r char next,
  544.                 // that's the DOS and OS/2 format (\r\n):
  545.                 // skip that too
  546.                 *ppszSearchIn = prc + 2;
  547.             else
  548.                 *ppszSearchIn = prc + 1;
  549.         }
  550.         else if (*prc == '\n')
  551.             // UNIX format (used by HTML formatter also):
  552.             *ppszSearchIn = prc + 1;
  553.     }
  554.  
  555.     // now:
  556.     // 1)   prc points to the \r, \n, or \0 character (EOL)
  557.     // 2)   *ppszSearchIn has been advanced to the first character
  558.     //      of the next line or points to the \0 character
  559.  
  560.     return prc;
  561. }
  562.  
  563. /* #define TXVFRECTF_EMPTY                  0x0001
  564. #define TXVFRECTF_PARAGRAPHDONE          0x0002
  565. #define TXVFRECTF_WORDSLEFT              0x0004
  566. #define TXVFRECTF_STOPPEDONESCAPE        0x0008
  567. #define TXVFRECTF_ENDOFTEXT              0x0010
  568.    */
  569.  
  570. /*
  571.  *@@ FORMATLINEBUF:
  572.  *      worker structure to store various data
  573.  *      in txvFormatText in between CreateWord
  574.  *      calls. This has been created for speed
  575.  *      so we don't have to pass all these on
  576.  *      the stack all the time.
  577.  *
  578.  *@@added V0.9.3 (2000-05-06) [umoeller]
  579.  */
  580.  
  581. typedef struct _FORMATLINEBUF
  582. {
  583.     PSZ             pLastChar;  // ptr to null terminator in text
  584.  
  585.     // formatting data; this is set by txvFormatText according
  586.     // to escape characters and read by txvCreateRectangle
  587.     XFMTPARAGRAPH   fmtp;
  588.     PXFMTCHARACTER  pfmtc;      // pointer to character formatting data
  589.     PXFMTFONT       pfmtf;      // pointer to font to use
  590.  
  591.     BOOL            fPre,
  592.                     fBold,
  593.                     fItalics;
  594.  
  595.     // current anchor
  596.     PSZ             pszCurrentLink;
  597.                         // this is != NULL if we're currently in a link block
  598.                         // and points to an item in XFORMATDATA.llLinks
  599.                         // (simply copied to the word structs that are created)
  600.  
  601.     // data copied to TXVWORD
  602.     LONG            lcid;
  603.     LONG            lPointSize;
  604.     ULONG           flChar;     // any combination of CHS_UNDERSCORE and CHS_STRIKEOUT
  605.  
  606.     // counters, ...
  607.     LONG            lXCurrent;  // current X position while adding words to rectangle
  608. } FORMATLINEBUF, *PFORMATLINEBUF;
  609.  
  610. /*
  611.  *@@ CreateWord:
  612.  *
  613.  *      --  If the word ends with one or several spaces,
  614.  *          ppStartOfWord is set to the beginning of the
  615.  *          next word (non-space character).
  616.  *          pWord->ulFlags is set to 0.
  617.  *
  618.  *      --  If the word ends with an escape character,
  619.  *          ppStartOfWord is set to point to the escape,
  620.  *          which must be handled by the caller.
  621.  *          pWord->ulFlags is set to TXVWORDF_GLUEWITHNEXT.
  622.  *
  623.  *      --  If the word ends with a \n or \r,
  624.  *          ppStartOfWord is set to the beginning of the
  625.  *          next line (first char after \n or \r). This
  626.  *          may be another \n or \r, but the first one
  627.  *          is skipped.
  628.  *          pWord->ulFlags is set to TXVWORDF_LINEBREAK or
  629.  *          TXVWORDF_LINEFEED.
  630.  *
  631.  *@@added V0.9.3 (2000-05-14) [umoeller]
  632.  *@@changed V0.9.20 (2002-08-10) [umoeller]: rewrote link implementation
  633.  */
  634.  
  635. static PTXVWORD CreateWord(HPS hps,
  636.                            PSZ *ppStartOfWord,
  637.                            PFORMATLINEBUF pflbuf)
  638. {
  639.     PTXVWORD   pWord = NULL;
  640.  
  641.     // find next word:
  642.     if (**ppStartOfWord)
  643.     {
  644.         PSZ         pWordStart = *ppStartOfWord,
  645.                     pWordEnd = NULL;
  646.         PSZ         pCheck = *ppStartOfWord;
  647.         ULONG       cChars = 0;
  648.                     // cCheck = 0;
  649.  
  650.         pWord = (PTXVWORD)malloc(sizeof(TXVWORD));
  651.         memset(pWord, 0, sizeof(TXVWORD));
  652.                 // this includes fIsEscapeSequence = FALSE;
  653.         pWord->pStart = pWordStart;
  654.  
  655.         // initially, this has pWordStart pointing
  656.         // to *ppStartOfWord. If a word is found,
  657.         // pWordStart is set to the first char of
  658.         // the word and pWordEnd receives the
  659.         // pointer to the first character after the word (probably space)
  660.         if (strhGetWord(&pWordStart, // in/out
  661.                         pflbuf->pLastChar,
  662.                         " ",
  663.                         "\x0d\x0a \xFF", // in: end chars; includes our escape!
  664.                         &pWordEnd))  // out: first char after word
  665.         {
  666.             // whoa, found a word:
  667.             while (*pWordEnd == ' ')
  668.                 pWordEnd++;
  669.  
  670.             cChars = (pWordEnd - *ppStartOfWord);
  671.         }
  672.  
  673.         if (cChars)
  674.         {
  675.             POINTL      aptlText[TXTBOX_COUNT];
  676.             // cChars is != 0 if strhGetWord succeeded AND the
  677.             // line is not empty, so go on
  678.             // cCheck = cChars;
  679.  
  680.             // advance input pointer
  681.             *ppStartOfWord = pWordEnd;
  682.  
  683.             GpiQueryTextBox(hps,
  684.                             // no. of chars since start of word:
  685.                             cChars,
  686.                             // first char:
  687.                             pCheck,
  688.                             TXTBOX_COUNT,
  689.                             (PPOINTL)&aptlText);
  690.  
  691.             pWord->cChars = cChars;
  692.             pWord->ulFlags = 0;
  693.  
  694.             if (cChars)
  695.                 pWord->ulCXWithSpaces = aptlText[TXTBOX_TOPRIGHT].x;
  696.             else
  697.                 pWord->ulCXWithSpaces = 0;
  698.  
  699.             pWord->ulCY = aptlText[TXTBOX_TOPRIGHT].y
  700.                           - aptlText[TXTBOX_BOTTOMRIGHT].y;
  701.             // store base line ofs; aptlText[TXTBOX_BOTTOMRIGHT].y is negative
  702.             // if the string has any characters drawn below the base line, e.g.
  703.             // for the "g" and "y" characters
  704.             pWord->ulBaseLineOfs = -aptlText[TXTBOX_BOTTOMRIGHT].y;
  705.         }
  706.         else
  707.         {
  708.             // no word found or empty line:
  709.             pWord->ulCY = pflbuf->pfmtf->FontMetrics.lMaxBaselineExt;
  710.         }
  711.  
  712.         switch (**ppStartOfWord)
  713.         {
  714.             case TXVESC_CHAR: // '\xFF':
  715.                 pWord->ulFlags = TXVWORDF_GLUEWITHNEXT;
  716.             break;
  717.  
  718.             case '\n':
  719.                 pWord->ulFlags = TXVWORDF_LINEBREAK;
  720.                 (*ppStartOfWord)++;     // skip \n
  721.             break;
  722.  
  723.             case '\r':
  724.                 pWord->ulFlags = TXVWORDF_LINEFEED;
  725.                 (*ppStartOfWord)++;     // skip \r
  726.             break;
  727.         }
  728.  
  729.         pWord->lcid = pflbuf->pfmtf->lcid;
  730.         pWord->lPointSize = pflbuf->lPointSize;
  731.         pWord->flChar = pflbuf->flChar;
  732.  
  733.         pWord->pszLinkTarget = pflbuf->pszCurrentLink; // 0 if none
  734.     }
  735.  
  736.     return pWord;
  737. }
  738.  
  739. /*
  740.  *@@ ProcessEscapes:
  741.  *      gets called when txvFormatText stops on an
  742.  *      escape character (\xFF). This evaluates the
  743.  *      escape sequence, reacts accordingly, and
  744.  *      advances *ppCurrent to after the escape
  745.  *      sequence so that regular processing can
  746.  *      continue.
  747.  *
  748.  *      There are two types of escape sequences:
  749.  *
  750.  *      -- Those which are only relevant during word processing,
  751.  *         such as character formatting attributes (bold, italics,
  752.  *         font, size, ...). Those affect the TXVWORD structure
  753.  *         directly and are thus never evaluated in step 2,
  754.  *         rectangles correlation.
  755.  *
  756.  *      -- Those which affect spacings, margins, etc. (paragraph
  757.  *         formatting). These need to be re-evaluated even during
  758.  *         "quick" format, without words being recalculated, because
  759.  *         those spacings affect the output rectangles.
  760.  *
  761.  *         If one of those sequences is encountered, this function
  762.  *         appends a special TXVWORD structure to XFORMATDATA.llWords.
  763.  *
  764.  *@@added V0.9.3 (2000-05-07) [umoeller]
  765.  */
  766.  
  767. static PTXVWORD ProcessEscapes(char **ppCurrent,          // in/out: current position; initially points to esc char
  768.                                PXFORMATDATA pxfd,         // in/out: formatting data
  769.                                PFORMATLINEBUF pflbuf,     // in/out: formatting buffer
  770.                                BOOL fWordsProcessed)      // FALSE during step 1 (words processing),
  771.                                                           // TRUE during step 2 (rectangles correlation)
  772. {
  773.     PTXVWORD pEscapeWord = NULL;
  774.  
  775.     // this is set to TRUE either above or by txvCreateRectangle if
  776.     // an escape character was found; txvCreateRectangle
  777.     // then sets pCurrent to the escape character (\xFF)
  778.     CHAR    cCode1 = *((*ppCurrent) + 1);
  779.     CHAR    cCode2 = *((*ppCurrent) + 2);
  780.     ULONG   ulSkip = 3; // per default, skip \xFF plus two
  781.     CHAR    szDecimal[10];
  782.     LONG    lDecimal;
  783.  
  784.     BOOL    fCreateWord = FALSE,
  785.             fPaintEscapeWord = FALSE;
  786.  
  787.     switch (cCode1)
  788.     {
  789.         case 1:     // change font:
  790.             // three decimals follow specifying the font
  791.             memcpy(szDecimal, (*ppCurrent) + 2, 3);
  792.             szDecimal[3] = 0;
  793.             lDecimal = atoi(szDecimal);
  794.             if (lDecimal == 0)
  795.                 pflbuf->pfmtc = &pxfd->fmtcStandard;
  796.             else if (lDecimal == 1)
  797.                 pflbuf->pfmtc = &pxfd->fmtcCode;
  798.             ulSkip = 5;
  799.         break;
  800.  
  801.         case 2:     // B or /B
  802.             if (cCode2 == 1)
  803.                 pflbuf->fBold = TRUE;
  804.             else
  805.                 pflbuf->fBold = FALSE;
  806.         break;
  807.  
  808.         case 3:     // I or /I
  809.             if (cCode2 == 1)
  810.                 pflbuf->fItalics = TRUE;
  811.             else
  812.                 pflbuf->fItalics = FALSE;
  813.         break;
  814.  
  815.         case 4:     // U or /U
  816.             if (cCode2 == 1)
  817.                 pflbuf->flChar |= CHS_UNDERSCORE;
  818.             else
  819.                 pflbuf->flChar &= ~CHS_UNDERSCORE;
  820.         break;
  821.  
  822.         case 5:     // STRIKE or /STRIKE
  823.             if (cCode2 == 1)
  824.                 pflbuf->flChar |= CHS_STRIKEOUT;
  825.             else
  826.                 pflbuf->flChar &= ~CHS_STRIKEOUT;
  827.         break;
  828.  
  829.         case 6:     // A HREF= (link)
  830.                     // changed implementation V0.9.20 (2002-08-10) [umoeller]
  831.         {
  832.             // this is variable in length and terminated with
  833.             // another 0xFF char; what's in between is the
  834.             // link target name and gets appended to
  835.             // XFORMATDATA.llLinks
  836.             PSZ pEnd;
  837.             if (pEnd = strchr((*ppCurrent) + 2, 0xFF))
  838.             {
  839.                 pflbuf->pszCurrentLink = strhSubstr((*ppCurrent) + 2, pEnd);
  840.                 ulSkip = pEnd - *ppCurrent + 1;
  841.  
  842.                 lstAppendItem(&pxfd->llLinks,
  843.                               pflbuf->pszCurrentLink);
  844.             }
  845.         }
  846.         break;
  847.  
  848.         case 7:     // /A HREF (end of link)
  849.             pflbuf->pszCurrentLink = NULL;
  850.             ulSkip = 2;
  851.         break;
  852.  
  853.         case 8:     // A NAME= (anchor name)
  854.         {
  855.             // this is variable in length and terminated with
  856.             // another 0xFF char; we completely ignore this
  857.             // here and just skip the anchor name, this is
  858.             // only used with TXM_JUMPTOANCHORNAME, which then
  859.             // searches the buffer
  860.             PSZ pEnd;
  861.             if (pEnd = strchr((*ppCurrent) + 2, 0xFF))
  862.             {
  863.                 ulSkip = pEnd - *ppCurrent + 1;
  864.                 // store this with the other words so we can
  865.                 // find this word later
  866.                 fCreateWord = TRUE;
  867.                 // and store this with the rectangles
  868.                 fPaintEscapeWord = TRUE;
  869.             }
  870.         }
  871.         break;
  872.  
  873.         case 0x10:  // relative point size in percent
  874.             // three characters follow specifying the
  875.             // percentage
  876.             memcpy(szDecimal, (*ppCurrent) + 2, 3);
  877.             szDecimal[3] = 0;
  878.             lDecimal = atoi(szDecimal);
  879.  
  880.             pflbuf->lPointSize = pflbuf->pfmtc->lPointSize * lDecimal / 100;
  881.             ulSkip = 5;
  882.         break;
  883.  
  884.         case 0x20: // left margin changed:
  885.             memcpy(szDecimal, (*ppCurrent) + 2, 4);   // four decimals xxxx
  886.             szDecimal[4] = 0;
  887.             lDecimal = atoi(szDecimal);
  888.  
  889.             // this is based on the current average font width, so
  890.             // find this:
  891.             pflbuf->fmtp.lLeftMargin = (lDecimal
  892.                                         * pflbuf->lPointSize);
  893.             ulSkip = 6;
  894.             fCreateWord = TRUE;     // for rectangle correlation
  895.         break;
  896.  
  897.         case 0x21: // first line margin changed:
  898.             memcpy(szDecimal, (*ppCurrent) + 2, 4);   // +xxx, -xxx
  899.             szDecimal[4] = 0;
  900.             lDecimal = atoi(szDecimal);
  901.  
  902.             // this is based on the current average font width, so
  903.             // find this:
  904.             pflbuf->fmtp.lFirstLineMargin = (lDecimal
  905.                                              * pflbuf->lPointSize);
  906.             ulSkip = 6;
  907.             fCreateWord = TRUE;     // for rectangle correlation
  908.         break;
  909.  
  910.         case 0x22: // tab: forward current X to left margin
  911.             pflbuf->lXCurrent = pflbuf->fmtp.lLeftMargin;
  912.  
  913.             ulSkip = 2;
  914.             fCreateWord = TRUE;     // for rectangle correlation
  915.         break;
  916.  
  917.         case 0x23: // marker: store this in output, this needs
  918.                    // to be painted
  919.             fCreateWord = TRUE;
  920.             fPaintEscapeWord = TRUE;
  921.             ulSkip = 3;
  922.         break;
  923.  
  924.         case 0x30:  // spacing before paragraph:
  925.             // four chars follow with either "####" or decimal spacing
  926.             memcpy(szDecimal, (*ppCurrent) + 2, 4);
  927.             szDecimal[4] = 0;
  928.             if (memcmp(szDecimal, "####", 4) == 0)
  929.                 // reset to default:
  930.                 pflbuf->fmtp.lSpaceBefore = pxfd->fmtpStandard.lSpaceBefore;
  931.             else
  932.             {
  933.                 lDecimal = atoi(szDecimal);
  934.                 pflbuf->fmtp.lSpaceBefore = lDecimal;
  935.             }
  936.             ulSkip = 6;
  937.             fCreateWord = TRUE;     // for rectangle correlation
  938.         break;
  939.  
  940.         case 0x31:  // spacing before paragraph:
  941.             // four chars follow with either "####" or decimal spacing
  942.             memcpy(szDecimal, (*ppCurrent) + 2, 4);
  943.             szDecimal[4] = 0;
  944.             if (memcmp(szDecimal, "####", 4) == 0)
  945.                 // reset to default:
  946.                 pflbuf->fmtp.lSpaceAfter = pxfd->fmtpStandard.lSpaceAfter;
  947.             else
  948.             {
  949.                 lDecimal = atoi(szDecimal);
  950.                 pflbuf->fmtp.lSpaceAfter = lDecimal;
  951.             }
  952.             ulSkip = 6;
  953.             fCreateWord = TRUE;     // for rectangle correlation
  954.         break;
  955.  
  956.         case 0x32:  // word-wrapping:
  957.             // here follows a single char being "0" or "1"
  958.             if (    *((*ppCurrent) + 2) == '0')
  959.                 pflbuf->fmtp.fWordWrap = FALSE;
  960.             else
  961.                 pflbuf->fmtp.fWordWrap = TRUE;
  962.             fCreateWord = TRUE;     // for rectangle correlation
  963.     }
  964.  
  965.     if (fCreateWord)        // append for rectangle correlation?
  966.         if (!fWordsProcessed)    // are we processing words still (step 1)?
  967.         {
  968.             // yes: append to list for rectangle correlation later
  969.             pEscapeWord = (PTXVWORD)malloc(sizeof(TXVWORD));
  970.             memset(pEscapeWord, 0, sizeof(TXVWORD));
  971.             // mark as escape sequence
  972.             pEscapeWord->pStart = *ppCurrent;
  973.             pEscapeWord->cChars = ulSkip;
  974.             pEscapeWord->cEscapeCode = *(*ppCurrent + 1);
  975.             pEscapeWord->fPaintEscapeWord = fPaintEscapeWord;
  976.             pEscapeWord->pszLinkTarget = pflbuf->pszCurrentLink;
  977.                     // V0.9.20 (2002-08-10) [umoeller]
  978.                     // NULL if none
  979.             if (fPaintEscapeWord)
  980.             {
  981.                 pEscapeWord->lX = pflbuf->lXCurrent;
  982.                 pEscapeWord->lcid = pflbuf->pfmtf->lcid;
  983.                 pEscapeWord->lPointSize = pflbuf->lPointSize;
  984.                 pEscapeWord->flChar = pflbuf->flChar;
  985.             }
  986.             lstAppendItem(&pxfd->llWords, pEscapeWord);
  987.         }
  988.  
  989.     if (!fWordsProcessed)
  990.         // if we're still processing words, advance
  991.         // current pointer by the escape length
  992.         *ppCurrent += ulSkip;
  993.  
  994.     return pEscapeWord;
  995. }
  996.  
  997. /*
  998.  *@@ txvFormatText:
  999.  *      this is the core function to text formatting, which
  1000.  *      must be done before the text can be painted into an
  1001.  *      HPS. See the top of textview.c for details.
  1002.  *
  1003.  *      Even though this function does not seem to have a
  1004.  *      lot of parameters, it is extremely powerful. This
  1005.  *      function handles paragraph and character formatting
  1006.  *      automatically. See XFMTPARAGRAPH and XFMTCHARACTER
  1007.  *      for possible formatting attributes, which are part
  1008.  *      of the XFORMATDATA structure passed to this function.
  1009.  *
  1010.  *      "Formatting" means splitting up any zero-terminated
  1011.  *      string (XFORMATDATA.pszViewText) into a possibly
  1012.  *      large list of TXVRECTANGLE structures, which each
  1013.  *      hold a rectangle to be painted. This allows for
  1014.  *      extremely fast painting.
  1015.  *
  1016.  *      Each TXVRECTANGLE in turn holds several "words" to
  1017.  *      be painted. A word consists of a TXVWORD structure
  1018.  *      and is normally a sequence of characters between
  1019.  *      spaces, \n and \r characters. As an exception, if
  1020.  *      escape sequences come up, such a "word" is split up
  1021.  *      into several words because character formatting
  1022.  *      (font, size, ...) is done on a per-word basis when painting.
  1023.  *
  1024.  *      This approach allows for quicker word-wrapping when only
  1025.  *      the output (paint) rectangle is changed because we don't
  1026.  *      have to re-calculate all the character widths (TXVWORD) once we
  1027.  *      got the words. Instead, when re-formatting, we just recompose
  1028.  *      the rectangles based on the words we calculated already.
  1029.  *      Of course, when character widths change (e.g. because
  1030.  *      fonts are changed), everything has to be redone.
  1031.  *
  1032.  *      Processing depends on the current formatting settings
  1033.  *      of the XFORMATDATA structure passed to this func and
  1034.  *      can become quite complicated:
  1035.  *
  1036.  *      --  In the simplest possible formatting mode, that is, if
  1037.  *          word wrapping is disabled, each such TXVRECTANGLE
  1038.  *          structure will hold one paragraph from the text
  1039.  *          (that is, the text between two \n chars).
  1040.  *
  1041.  *      --  If word wrapping is enabled, each paragraph in the text
  1042.  *          can consist of several such rectangles if the paragraph
  1043.  *          does not fit into one line. In that case, we create
  1044.  *          one XFMTRECTANGLE for each line which is needed to
  1045.  *          display the paragraph word-wrapped.
  1046.  *
  1047.  *      This uses an XFORMATDATA structure for input and output
  1048.  *      (besides the other parameters).
  1049.  *
  1050.  *      On input, specify the following:
  1051.  *
  1052.  *      -- hps: window or printer HPS. This is used for
  1053.  *         formatting only, but nothing is painted.
  1054.  *
  1055.  *      -- XFORMATDATA.pszViewText: the text to be formatted.
  1056.  *         This must follow certain conventions; the \xFF,
  1057.  *         \r, and \n characters have a special meaning.
  1058.  *         See the top of textview.c for details.
  1059.  *
  1060.  *      -- XFORMATDATA.fmtpStandard, fmtcStandard, fmtcCode:
  1061.  *         paragraph and character formatting attributes.
  1062.  *         For the simplest possible formatting, memset
  1063.  *         all these to 0. Word-wrapping depends on
  1064.  *         the paragraph formats.
  1065.  *
  1066.  *      -- prclView: rectangle for which formatting should take
  1067.  *         place. When this is called for a screen window,
  1068.  *         this should be the visible area of the window
  1069.  *         (WinQueryWindowRect).
  1070.  *         When this is called with a printer PS, this should
  1071.  *         be the size of a printer page.
  1072.  *
  1073.  *      This function updates the following:
  1074.  *
  1075.  *      -- XFORMATDATA.llWords: list of TXVWORD structures,
  1076.  *         holding all the "words" in the text as described
  1077.  *         above. This list can grow very long, but only needs
  1078.  *         to be recalculated when fonts change.
  1079.  *
  1080.  *      -- XFORMATDATA.llRectangles: list of TXVRECTANGLE
  1081.  *         structures, correlating the words on the words list
  1082.  *         to paint rectangles.
  1083.  *
  1084.  *      -- XFORMATDATA.szlWorkspace: total width
  1085.  *         and height of the "workspace", i.e. the total space
  1086.  *         needed to display the text (in pels). This might
  1087.  *         be smaller, the same, or larger than prclView,
  1088.  *         depending on whether the text fits into prclView.
  1089.  *
  1090.  *         When displaying text, you should display scroll bars
  1091.  *         if the workspace is larger than the window (prclView).
  1092.  *
  1093.  *         When printing, if the workspace is larger than the
  1094.  *         printer page (prclView), you will need to call
  1095.  *         txvPaintText several times for each page.
  1096.  *
  1097.  *      All coordinates are in world space (PU_PELS).
  1098.  *
  1099.  *@@changed V0.9.3 (2000-05-06) [umoeller]: largely rewritten; now handling paragraph and character formats
  1100.  *@@todo TXVWORDF_GLUEWITHNEXT
  1101.  */
  1102.  
  1103. VOID txvFormatText(HPS hps,             // in: HPS whose font is used for
  1104.                                         // calculating text dimensions
  1105.                    PXFORMATDATA pxfd,   // in: formatting data
  1106.                    PRECTL prclView,     // in: rectangle to format for (window or printer page)
  1107.                    BOOL fFullRecalc)    // in: re-calculate word list too? (must be TRUE on the first call)
  1108. {
  1109.     /* ULONG   ulWinCX = (prclView->xRight - prclView->xLeft),
  1110.             ulWinCY = (prclView->yTop - prclView->yBottom); */
  1111.  
  1112.     lstClear(&pxfd->llRectangles);
  1113.     if (fFullRecalc)
  1114.         lstClear(&pxfd->llWords);
  1115.  
  1116.     pxfd->szlWorkspace.cx = 0;
  1117.     pxfd->szlWorkspace.cy = 0;
  1118.  
  1119.     if (pxfd->strViewText.cbAllocated)
  1120.     {
  1121.         ULONG   ulTextLen = pxfd->strViewText.ulLength;
  1122.  
  1123.         FORMATLINEBUF   flbuf;
  1124.         LONG            lcidLast = -99,
  1125.                         lPointSizeLast = -99;
  1126.  
  1127.         memset(&flbuf, 0, sizeof(flbuf));
  1128.         // copy default paragraph formatting
  1129.         memcpy(&flbuf.fmtp, &pxfd->fmtpStandard, sizeof(flbuf.fmtp));
  1130.         // set font
  1131.         flbuf.pfmtc = &pxfd->fmtcStandard;
  1132.         flbuf.lPointSize = pxfd->fmtcStandard.lPointSize;
  1133.         flbuf.pLastChar = pxfd->strViewText.psz + ulTextLen;
  1134.  
  1135.         if (ulTextLen)
  1136.         {
  1137.             ULONG   cWords = 0;
  1138.  
  1139.             if (fFullRecalc)
  1140.             {
  1141.                 /*
  1142.                  * step 1: create words
  1143.                  *
  1144.                  */
  1145.  
  1146.                 PSZ     pCurrent = pxfd->strViewText.psz;
  1147.  
  1148.                 // loop until null terminator
  1149.                 while (*pCurrent)
  1150.                 {
  1151.                     PTXVWORD pWord;
  1152.  
  1153.                     if (flbuf.fBold)
  1154.                     {
  1155.                         if (flbuf.fItalics)
  1156.                             flbuf.pfmtf = &flbuf.pfmtc->fntBoldItalics;
  1157.                         else
  1158.                             flbuf.pfmtf = &flbuf.pfmtc->fntBold;
  1159.                     }
  1160.                     else
  1161.                         if (flbuf.fItalics)
  1162.                             flbuf.pfmtf = &flbuf.pfmtc->fntItalics;
  1163.                         else
  1164.                             flbuf.pfmtf = &flbuf.pfmtc->fntRegular;
  1165.  
  1166.                     // set font for subsequent calculations,
  1167.                     // if changed (this includes the first call)
  1168.                     if (lcidLast != flbuf.pfmtf->lcid)
  1169.                     {
  1170.                         GpiSetCharSet(hps, flbuf.pfmtf->lcid);
  1171.                         lcidLast = flbuf.pfmtf->lcid;
  1172.                         // force recalc of point size
  1173.                         lPointSizeLast = -99;
  1174.                     }
  1175.  
  1176.                     if (lPointSizeLast != flbuf.lPointSize)
  1177.                     {
  1178.                         if (flbuf.pfmtf->FontMetrics.fsDefn & FM_DEFN_OUTLINE)
  1179.                             // is outline font:
  1180.                             gpihSetPointSize(hps, flbuf.lPointSize);
  1181.                         lPointSizeLast = flbuf.lPointSize;
  1182.                     }
  1183.  
  1184.                     if (pWord = CreateWord(hps,
  1185.                                            &pCurrent, // advanced to next word
  1186.                                            &flbuf))
  1187.                     {
  1188.                         lstAppendItem(&pxfd->llWords, pWord);
  1189.  
  1190.                         /* {
  1191.                             CHAR szWord[3000];
  1192.                             strhncpy0(szWord, pWord->pStart, min(pWord->cChars, sizeof(szWord)));
  1193.                             _Pmpf(("Found word '%s'", szWord));
  1194.                         } */
  1195.  
  1196.                         cWords++;
  1197.  
  1198.                         while (*pCurrent == TXVESC_CHAR) // '\xFF')
  1199.                         {
  1200.                             // handle escapes;
  1201.                             // this advances pCurrent depending on the
  1202.                             // escape sequence length and might append
  1203.                             // another "word" for the escape sequence
  1204.                             // if it's relevant for rectangle correlation
  1205.                             ProcessEscapes(&pCurrent,
  1206.                                            pxfd,
  1207.                                            &flbuf,
  1208.                                            FALSE); // fWordsProcessed
  1209.                         }
  1210.                     }
  1211.                     else
  1212.                         break;
  1213.                 }
  1214.             } // end if (fFullRecalc)
  1215.             else
  1216.                 cWords = lstCountItems(&pxfd->llWords);
  1217.  
  1218.             /*
  1219.              * step 2: create rectangles
  1220.              *
  1221.              */
  1222.  
  1223.             if (cWords)
  1224.             {
  1225.                 PLISTNODE pWordNode = lstQueryFirstNode(&pxfd->llWords);
  1226.  
  1227.                 LONG        lCurrentYTop = prclView->yTop,
  1228.                             lOrigYTop = lCurrentYTop;
  1229.  
  1230.                 BOOL        fRects2Go = TRUE;
  1231.  
  1232.                 // space before paragraph; this is reset
  1233.                 // to 0 if we start a new rectangle for
  1234.                 // the same paragraph
  1235.                 ULONG       ulYPre = flbuf.fmtp.lSpaceBefore;
  1236.  
  1237.                 // rectangles loop
  1238.                 while (fRects2Go)
  1239.                 {
  1240.                     BOOL    fWords2Go = TRUE;
  1241.                     ULONG   ulWordsInThisRect = 0;
  1242.  
  1243.                     // maximum height of words in this rect
  1244.                     ULONG   lWordsMaxCY = 0;
  1245.  
  1246.                     // start a new rectangle:
  1247.                     PTXVRECTANGLE pRect = (PTXVRECTANGLE)malloc(sizeof(TXVRECTANGLE));
  1248.                     lstInit(&pRect->llWords,
  1249.                             FALSE);     // no auto-free; the words are stored in the main
  1250.                                         // list also, which is freed
  1251.                     // rectangle's xLeft;
  1252.                     // xRight will be set when we're done with this rectangle
  1253.                     pRect->rcl.xLeft = prclView->xLeft + flbuf.fmtp.lLeftMargin;
  1254.                     if (ulYPre)
  1255.                         // starting new paragraph:
  1256.                         // add first-line offset also
  1257.                         pRect->rcl.xLeft += flbuf.fmtp.lFirstLineMargin;
  1258.  
  1259.                     // current X pos: start with left of rectangle
  1260.                     flbuf.lXCurrent = pRect->rcl.xLeft;
  1261.  
  1262.                     // max baseline ofs: set to 0, this will be raised
  1263.                     pRect->ulMaxBaseLineOfs = 0;
  1264.  
  1265.                     // words-per-rectangle loop;
  1266.                     // we keep adding words to the rectangle until
  1267.                     // a) words no longer fit and word-wrapping is on;
  1268.                     // b) a newline or line feed is found;
  1269.                     // c) the last word has been reached;
  1270.                     while (fWords2Go)
  1271.                     {
  1272.                         PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
  1273. /*
  1274.     #define TXVWORDF_GLUEWITHNEXT       1       // escape
  1275.     #define TXVWORDF_LINEBREAK          2       // \n
  1276.     #define TXVWORDF_LINEFEED           4       // \r
  1277. */
  1278.                         BOOL    fNextWord = FALSE;
  1279.  
  1280.                         if (pWordThis->cEscapeCode)
  1281.                         {
  1282.                             // pseudo-word for escape sequence:
  1283.                             // process...
  1284.                             ProcessEscapes((PSZ*)&pWordThis->pStart,
  1285.                                            pxfd,
  1286.                                            &flbuf,
  1287.                                            TRUE);
  1288.  
  1289.                             // append this sequence only if it's needed
  1290.                             // for painting (list markers etc.)
  1291.                             if (pWordThis->fPaintEscapeWord)
  1292.                             {
  1293.                                 pWordThis->lX = flbuf.lXCurrent;
  1294.                                 pWordThis->pRectangle = pRect;
  1295.                                 lstAppendItem(&pRect->llWords, pWordThis);
  1296.                                 ulWordsInThisRect++;
  1297.                             }
  1298.  
  1299.                             fNextWord = TRUE;
  1300.                         }
  1301.                         else
  1302.                         {
  1303.                             BOOL    fWordWrapped = FALSE;
  1304.  
  1305.                             // not escape sequence, but real word: format...
  1306.                             // is word-wrapping on?
  1307.                             if (flbuf.fmtp.fWordWrap)
  1308.                             {
  1309.                                 // yes: check if the word still fits
  1310.                                 if (    (flbuf.lXCurrent + pWordThis->ulCXWithSpaces // ###
  1311.                                              > prclView->xRight)
  1312.                                              // > ulWinCX)
  1313.                                         // but always add the first word in the rectangle,
  1314.                                         // because otherwise we get infinite loops
  1315.                                      && (ulWordsInThisRect > 0)
  1316.                                    )
  1317.                                     // no:
  1318.                                     fWordWrapped = TRUE;
  1319.                             }
  1320.  
  1321.                             if (fWordWrapped)
  1322.                                 // start a new rectangle with the current word:
  1323.                                 fWords2Go = FALSE;
  1324.                                         // and do _not_ advance to the next word,
  1325.                                         // but start with this word for the next
  1326.                                         // rectangle...
  1327.                             else
  1328.                             {
  1329.                                 // add this word to the rectangle:
  1330.  
  1331.                                 // store current X pos in word
  1332.                                 pWordThis->lX = flbuf.lXCurrent;
  1333.  
  1334.                                 // increase current X pos by word width
  1335.                                 flbuf.lXCurrent += pWordThis->ulCXWithSpaces;
  1336.  
  1337.                                 // store word in rectangle
  1338.                                 pWordThis->pRectangle = pRect;
  1339.                                 lstAppendItem(&pRect->llWords, pWordThis);
  1340.                                             // @@todo memory leak right here!!!
  1341.                                 ulWordsInThisRect++;
  1342.  
  1343.                                 // store highest word width found for this rect
  1344.                                 if (pWordThis->ulCY > lWordsMaxCY)
  1345.                                     lWordsMaxCY = pWordThis->ulCY;
  1346.  
  1347.                                 // store highest base line ofs found for this rect
  1348.                                 if (pWordThis->ulBaseLineOfs > pRect->ulMaxBaseLineOfs)
  1349.                                     pRect->ulMaxBaseLineOfs = pWordThis->ulBaseLineOfs;
  1350.  
  1351.                                 // go for next word in any case
  1352.                                 fNextWord = TRUE;
  1353.                             } // end if (!fBreakThisWord)
  1354.  
  1355.                             // now check: add more words to this rectangle?
  1356.                             if (    (pWordThis->ulFlags == TXVWORDF_LINEBREAK)
  1357.                                         // no if linebreak found
  1358.                                  || (pWordThis->ulFlags == TXVWORDF_LINEFEED)
  1359.                                         // no if linefeed found
  1360.                                  || (!fWords2Go)
  1361.                                         // no if we're out of words or
  1362.                                         // word-break was forced
  1363.                                )
  1364.                             {
  1365.                                 // no: finish up this rectangle...
  1366.  
  1367.                                 // xLeft has been set on top
  1368.                                 pRect->rcl.xRight = flbuf.lXCurrent;
  1369.                                 pRect->rcl.yTop = lCurrentYTop - ulYPre;
  1370.                                 pRect->rcl.yBottom = pRect->rcl.yTop - lWordsMaxCY;
  1371.  
  1372.                                 // decrease current y top for next line
  1373.                                 lCurrentYTop = pRect->rcl.yBottom;
  1374.                                 if (!fRects2Go)
  1375.                                     // we're done completely:
  1376.                                     // add another one
  1377.                                     lCurrentYTop -= lWordsMaxCY;
  1378.  
  1379.                                 if (fWordWrapped)
  1380.                                     // starting with wrapped word in next line:
  1381.                                     ulYPre = 0;
  1382.                                 else
  1383.                                     if (pWordThis->ulFlags == TXVWORDF_LINEFEED)
  1384.                                         ulYPre = 0;
  1385.                                     else if (pWordThis->ulFlags == TXVWORDF_LINEBREAK)
  1386.                                     {
  1387.                                         // line break:
  1388.                                         // set y-pre for next loop
  1389.                                         ulYPre = flbuf.fmtp.lSpaceBefore;
  1390.                                         // and add paragraph post-y
  1391.                                         lCurrentYTop -= flbuf.fmtp.lSpaceAfter;
  1392.                                     }
  1393.  
  1394.                                 // update x extents
  1395.                                 if (pRect->rcl.xRight > pxfd->szlWorkspace.cx)
  1396.                                     pxfd->szlWorkspace.cx = pRect->rcl.xRight;
  1397.  
  1398.                                 // and quit the inner loop
  1399.                                 fWords2Go = FALSE;
  1400.                             } // end finish up rectangle
  1401.                         } // end else if (pWordThis->fIsEscapeSequence)
  1402.  
  1403.                         if (fNextWord)
  1404.                         {
  1405.                             pWordNode = pWordNode->pNext;
  1406.                             if (!pWordNode)
  1407.                             {
  1408.                                 // no more to go:
  1409.                                 // quit
  1410.                                 fWords2Go = FALSE;
  1411.                                 fRects2Go = FALSE;
  1412.                             }
  1413.                         }
  1414.                     } // end while (fWords2Go)
  1415.  
  1416.                     // store rectangle
  1417.                     lstAppendItem(&pxfd->llRectangles, pRect);
  1418.                 }
  1419.  
  1420.                 // lCurrentYTop now has the bottommost point we've used;
  1421.                 // store this as workspace (this might be negative)
  1422.                 pxfd->szlWorkspace.cy = lOrigYTop - lCurrentYTop;
  1423.             }
  1424.         }
  1425.     }
  1426. }
  1427.  
  1428. /* ******************************************************************
  1429.  *
  1430.  *   Device-independent text painting
  1431.  *
  1432.  ********************************************************************/
  1433.  
  1434. /*
  1435.  *@@ DrawListMarker:
  1436.  *
  1437.  *@@added V0.9.3 (2000-05-17) [umoeller]
  1438.  */
  1439.  
  1440. static VOID DrawListMarker(HPS hps,
  1441.                            PRECTL prclLine,        // current line rectangle
  1442.                            PTXVWORD pWordThis,    // current word
  1443.                            LONG lViewXOfs)         // in: x offset to paint; 0 means rightmost
  1444. {
  1445.     POINTL ptl;
  1446.  
  1447.     ULONG ulBulletSize = pWordThis->lPointSize * 2 / 3; // 2/3 of point size
  1448.  
  1449.     ARCPARAMS   arcp = {1, 1, 0, 0};
  1450.  
  1451.     // pWordThis->pStart points to the \xFF character;
  1452.     // next is the "marker" escape (\x23),
  1453.     // next is the marker type
  1454.     CHAR cBulletType =  *((pWordThis->pStart) + 2) ;
  1455.  
  1456.     switch (cBulletType)
  1457.     {
  1458.         case 2:     // square (filled box)
  1459.             ptl.x = pWordThis->lX - lViewXOfs;
  1460.             // center bullet vertically
  1461.             ptl.y = prclLine->yBottom
  1462.                     +   (   (prclLine->yTop - prclLine->yBottom) // height
  1463.                              - ulBulletSize
  1464.                         ) / 2;
  1465.  
  1466.             GpiMove(hps, &ptl);
  1467.             ptl.x += ulBulletSize;
  1468.             ptl.y += ulBulletSize;
  1469.             GpiBox(hps, DRO_FILL, &ptl, 0, 0);
  1470.         break;
  1471.  
  1472.         default: // case 1:     // disc (filled circle)
  1473.             ptl.x = pWordThis->lX - lViewXOfs;
  1474.             // center bullet vertically;
  1475.             // the arc is drawn with the current position in its center
  1476.             ptl.y = prclLine->yBottom
  1477.                     +   (   (prclLine->yTop - prclLine->yBottom) // height
  1478.                             / 2
  1479.                         );
  1480.  
  1481.             GpiSetArcParams(hps, &arcp);
  1482.             GpiMove(hps, &ptl);
  1483.             GpiFullArc(hps,
  1484.                        (cBulletType == 3)
  1485.                             ? DRO_OUTLINE
  1486.                             : DRO_FILL,
  1487.                        MAKEFIXED(ulBulletSize / 2,      // radius!
  1488.                                  0));
  1489.         break;
  1490.  
  1491.     }
  1492. }
  1493.  
  1494. /*
  1495.  *@@ txvPaintText:
  1496.  *      device-independent function for painting.
  1497.  *      This can only be called after the text has
  1498.  *      been formatted (using txvFormatText).
  1499.  *
  1500.  *      This only paints rectangles which are within
  1501.  *      prcl2Paint.
  1502.  *
  1503.  *      -- For WM_PAINT, set this to the
  1504.  *         update rectangle, and set fPaintHalfLines
  1505.  *         to TRUE.
  1506.  *
  1507.  *      -- For printing, set this to the page rectangle,
  1508.  *         and set fPaintHalfLines to FALSE.
  1509.  *
  1510.  *      All coordinates are in world space (PU_PELS).
  1511.  *
  1512.  *@@changed V0.9.3 (2000-05-05) [umoeller]: fixed wrong visible lines calculations; great speedup painting!
  1513.  *@@changed V0.9.3 (2000-05-06) [umoeller]: now using gpihCharStringPosAt
  1514.  */
  1515.  
  1516. BOOL txvPaintText(HAB hab,
  1517.                   HPS hps,             // in: window or printer PS
  1518.                   PXFORMATDATA pxfd,
  1519.                   PRECTL prcl2Paint,   // in: invalid rectangle to be drawn,
  1520.                                        // can be NULL to paint all
  1521.                   LONG lViewXOfs,      // in: x offset to paint; 0 means rightmost
  1522.                   PULONG pulViewYOfs,  // in: y offset to paint; 0 means _top_most;
  1523.                                        // out: y offset which should be passed to next call
  1524.                                        // (if TRUE is returned and fPaintHalfLines == FALSE)
  1525.                   BOOL fPaintHalfLines, // in: if FALSE, lines which do not fully fit on
  1526.                                        // the page are dropped (useful for printing)
  1527.                   PULONG pulLineIndex) // in: line to start painting with;
  1528.                                        // out: next line to paint, if any
  1529.                                        // (if TRUE is returned and fPaintHalfLines == FALSE)
  1530. {
  1531.     BOOL    brc = FALSE,
  1532.             fAnyLinesPainted = FALSE;
  1533.     ULONG   ulCurrentLineIndex = *pulLineIndex;
  1534.     // LONG    lViewYOfsSaved = *pulViewYOfs;
  1535.     PLISTNODE pRectNode = lstNodeFromIndex(&pxfd->llRectangles,
  1536.                                            ulCurrentLineIndex);
  1537.  
  1538.     LONG    lcidLast = -99;
  1539.     LONG    lPointSizeLast = -99;
  1540.  
  1541.     while (pRectNode)
  1542.     {
  1543.         PTXVRECTANGLE   pLineRcl = (PTXVRECTANGLE)pRectNode->pItemData;
  1544.         BOOL            fPaintThis = FALSE;
  1545.  
  1546.         // compose rectangle to draw for this line
  1547.         RECTL           rclLine;
  1548.         rclLine.xLeft = pLineRcl->rcl.xLeft - lViewXOfs;
  1549.         rclLine.xRight = pLineRcl->rcl.xRight - lViewXOfs;
  1550.         rclLine.yBottom = pLineRcl->rcl.yBottom + *pulViewYOfs;
  1551.         rclLine.yTop = pLineRcl->rcl.yTop + *pulViewYOfs;
  1552.  
  1553.         /* if (pmpf)
  1554.         {
  1555.             CHAR szTemp[100];
  1556.             ULONG cb = min(pLineRcl->cLineChars, 99);
  1557.             strhncpy0(szTemp, pLineRcl->pStartOfLine, cb);
  1558.  
  1559.             _Pmpf(("Checking line %d: '%s'",
  1560.                     ulCurrentLineIndex,
  1561.                     szTemp));
  1562.  
  1563.             _Pmpf(("    (yB stored %d -> in HPS %d against win yB %d)",
  1564.                     pLineRcl->rcl.yBottom,
  1565.                     rclLine.yBottom,
  1566.                     prcl2Paint->yBottom));
  1567.         } */
  1568.  
  1569.         if (prcl2Paint == NULL)
  1570.             // draw all:
  1571.             fPaintThis = TRUE;
  1572.         else
  1573.         {
  1574.             BOOL fBottomInPaint = (    (rclLine.yBottom >= prcl2Paint->yBottom)
  1575.                                     && (rclLine.yBottom <= prcl2Paint->yTop)
  1576.                                   );
  1577.             BOOL fTopInPaint =    (    (rclLine.yTop >= prcl2Paint->yBottom)
  1578.                                     && (rclLine.yTop <= prcl2Paint->yTop)
  1579.                                   );
  1580.  
  1581.             if ((fBottomInPaint) && (fTopInPaint))
  1582.                 // both in update rect:
  1583.                 fPaintThis = TRUE;
  1584.             else
  1585.                 if (fPaintHalfLines)
  1586.                 {
  1587.                     if ((fBottomInPaint) || (fTopInPaint))
  1588.                     // only one in update rect:
  1589.                         fPaintThis = TRUE;
  1590.                     else
  1591.                         // now, for very small update rectangles,
  1592.                         // especially with slow scrolling,
  1593.                         // we can have the case that the paint rectangle
  1594.                         // is only a few pixels high so that the top of
  1595.                         // the line is above the repaint, and the bottom
  1596.                         // of the line is below it!
  1597.                         if (    (rclLine.yTop >= prcl2Paint->yTop)
  1598.                              && (rclLine.yBottom <= prcl2Paint->yBottom)
  1599.                            )
  1600.                             fPaintThis = TRUE;
  1601.                 }
  1602.         }
  1603.  
  1604.         if (fPaintThis)
  1605.         {
  1606.             // rectangle invalid: paint this rectangle
  1607.             // by going thru the member words
  1608.             PLISTNODE   pWordNode = lstQueryFirstNode(&pLineRcl->llWords);
  1609.  
  1610.             POINTL ptlStart;
  1611.  
  1612.             while (pWordNode)
  1613.             {
  1614.                 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
  1615.                 ULONG flChar = pWordThis->flChar;
  1616.  
  1617.                 if (pWordThis->pszLinkTarget)       // V0.9.20 (2002-08-10) [umoeller]
  1618.                     flChar |= CHS_UNDERSCORE;
  1619.  
  1620.                 // x start: this word's X coordinate
  1621.                 ptlStart.x = pWordThis->lX - lViewXOfs;
  1622.                 // y start: bottom line of rectangle plus highest
  1623.                 // base line offset found in all words (format step 2)
  1624.                 ptlStart.y = rclLine.yBottom + pLineRcl->ulMaxBaseLineOfs;
  1625.                          // pWordThis->ulBaseLineOfs;
  1626.  
  1627.                 // set font for subsequent calculations,
  1628.                 // if changed (this includes the first call)
  1629.                 if (lcidLast != pWordThis->lcid)
  1630.                 {
  1631.                     GpiSetCharSet(hps, pWordThis->lcid);
  1632.                     lcidLast = pWordThis->lcid;
  1633.                     // force recalc of point size
  1634.                     lPointSizeLast = -99;
  1635.                 }
  1636.  
  1637.                 if (lPointSizeLast != pWordThis->lPointSize)
  1638.                 {
  1639.                     if (pWordThis->lPointSize)
  1640.                         // is outline font:
  1641.                         gpihSetPointSize(hps, pWordThis->lPointSize);
  1642.                     lPointSizeLast = pWordThis->lPointSize;
  1643.                 }
  1644.  
  1645.                 if (!pWordThis->cEscapeCode)
  1646.                     // regular word:
  1647.                     gpihCharStringPosAt(hps,
  1648.                                         &ptlStart,
  1649.                                         &rclLine,
  1650.                                         flChar,
  1651.                                         pWordThis->cChars,
  1652.                                         (PSZ)pWordThis->pStart);
  1653.                 else
  1654.                 {
  1655.                     // check escape code
  1656.                     switch (pWordThis->cEscapeCode)
  1657.                     {
  1658.                         case 0x23:
  1659.                             // escape to be painted:
  1660.                             DrawListMarker(hps,
  1661.                                            &rclLine,
  1662.                                            pWordThis,
  1663.                                            lViewXOfs);
  1664.                         break;
  1665.                     }
  1666.                 }
  1667.  
  1668.                 // ptlStart.x += pWordThis->ulCXWithSpaces;
  1669.  
  1670.                 fAnyLinesPainted = TRUE;
  1671.                 pWordNode = pWordNode->pNext;
  1672.             }
  1673.  
  1674.             /* {
  1675.                 LONG lColor = GpiQueryColor(hps);
  1676.                 POINTL ptl2;
  1677.                 GpiSetColor(hps, RGBCOL_RED);
  1678.                 ptl2.x = rclLine.xLeft;
  1679.                 ptl2.y = rclLine.yBottom;
  1680.                 GpiMove(hps, &ptl2);
  1681.                 ptl2.x = rclLine.xRight;
  1682.                 ptl2.y = rclLine.yTop;
  1683.                 GpiBox(hps,
  1684.                        DRO_OUTLINE,
  1685.                        &ptl2,
  1686.                        0, 0);
  1687.                 GpiSetColor(hps, lColor);
  1688.             } */
  1689.  
  1690.         }
  1691.         else
  1692.         {
  1693.             // this line is no longer fully visible:
  1694.  
  1695.             if (fAnyLinesPainted)
  1696.             {
  1697.                 // we had painted lines already:
  1698.                 // this means that all the following lines are
  1699.                 // too far below the window, so quit
  1700.                 /* if (pmpf)
  1701.                     _Pmpf(("Quitting with line %d (xL = %d yB = %d)",
  1702.                             ulCurrentLineIndex, rclLine.xLeft, rclLine.yBottom)); */
  1703.  
  1704.                 *pulLineIndex = ulCurrentLineIndex;
  1705.                 if (pRectNode->pNext)
  1706.                 {
  1707.                     // another line to paint:
  1708.                     PTXVRECTANGLE   pLineRcl2 = (PTXVRECTANGLE)pRectNode->pNext->pItemData;
  1709.                     // return TRUE
  1710.                     brc = TRUE;
  1711.                     // and set *pulViewYOfs to the top of
  1712.                     // the next line, which wasn't visible
  1713.                     // on the page any more
  1714.                     *pulViewYOfs = pLineRcl2->rcl.yTop + *pulViewYOfs;
  1715.                 }
  1716.                 break;
  1717.             }
  1718.             // else no lines painted yet:
  1719.             // go for next node, because we're still above the visible window
  1720.         }
  1721.  
  1722.         // next line
  1723.         pRectNode = pRectNode->pNext;
  1724.         // raise index to return
  1725.         ulCurrentLineIndex++;
  1726.     }
  1727.  
  1728.     if (!fAnyLinesPainted)
  1729.         brc = FALSE;
  1730.  
  1731.     return brc;
  1732. }
  1733.  
  1734. /*
  1735.  *@@ txvFindWordFromPoint:
  1736.  *      returns the list node of the word under the
  1737.  *      given point. The list node is from the global
  1738.  *      words list in pxfd.
  1739.  *
  1740.  *@@added V0.9.3 (2000-05-18) [umoeller]
  1741.  */
  1742.  
  1743. PLISTNODE txvFindWordFromPoint(PXFORMATDATA pxfd,
  1744.                                PPOINTL pptl)
  1745. {
  1746.     PLISTNODE pWordNodeFound = NULL;
  1747.  
  1748.     PLISTNODE pRectangleNode = lstQueryFirstNode(&pxfd->llRectangles);
  1749.     while ((pRectangleNode) && (!pWordNodeFound))
  1750.     {
  1751.         PTXVRECTANGLE prclThis = (PTXVRECTANGLE)pRectangleNode->pItemData;
  1752.         if (    (pptl->x >= prclThis->rcl.xLeft)
  1753.              && (pptl->x <= prclThis->rcl.xRight)
  1754.              && (pptl->y >= prclThis->rcl.yBottom)
  1755.              && (pptl->y <= prclThis->rcl.yTop)
  1756.            )
  1757.         {
  1758.             // cool, we found the rectangle:
  1759.             // now go thru the words in this rectangle
  1760.             PLISTNODE pWordNode = lstQueryFirstNode(&prclThis->llWords);
  1761.             while (pWordNode)
  1762.             {
  1763.                 PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
  1764.  
  1765.                 if (    (pptl->x >= pWordThis->lX)
  1766.                      && (pptl->x <= pWordThis->lX + pWordThis->ulCXWithSpaces)
  1767.                    )
  1768.                 {
  1769.                     pWordNodeFound = pWordNode;
  1770.                     break;
  1771.                 }
  1772.                 pWordNode = pWordNode->pNext;
  1773.             }
  1774.         }
  1775.         pRectangleNode = pRectangleNode->pNext;
  1776.     }
  1777.  
  1778.     return pWordNodeFound;
  1779. }
  1780.  
  1781. /*
  1782.  *@@ txvFindWordFromAnchor:
  1783.  *      returns the list node from the global words list
  1784.  *      BEFORE the word which represents the escape sequence
  1785.  *      containing the specified anchor name.
  1786.  *
  1787.  *@@added V0.9.4 (2000-06-12) [umoeller]
  1788.  */
  1789.  
  1790. PLISTNODE txvFindWordFromAnchor(PXFORMATDATA pxfd,
  1791.                                 const char *pszAnchorName)
  1792. {
  1793.     PLISTNODE pNodeFound = NULL;
  1794.  
  1795.     ULONG cbAnchorName = strlen(pszAnchorName);
  1796.  
  1797.     PLISTNODE pWordNode = lstQueryFirstNode(&pxfd->llWords);
  1798.     while ((pWordNode) && (!pNodeFound))
  1799.     {
  1800.         PTXVWORD pWordThis = (PTXVWORD)pWordNode->pItemData;
  1801.         if (pWordThis->cEscapeCode == 7)
  1802.         {
  1803.             // this word is an anchor escape sequence:
  1804.             if (strnicmp(pszAnchorName, (pWordThis->pStart + 2), cbAnchorName) == 0)
  1805.             {
  1806.                 // matches: check length
  1807.                 if (*(pWordThis->pStart + 2 + cbAnchorName) == (char)0xFF)
  1808.                     // OK:
  1809.                     pNodeFound = pWordNode;
  1810.             }
  1811.         }
  1812.  
  1813.         pWordNode = pWordNode ->pNext;
  1814.     }
  1815.  
  1816.     if (pNodeFound)
  1817.     {
  1818.         // anchor found:
  1819.         // go backwords in word list until we find a "real" word
  1820.         // which is no escape sequence
  1821.         while (pNodeFound)
  1822.         {
  1823.             PTXVWORD pWordThis = (PTXVWORD)pNodeFound->pItemData;
  1824.             if (pWordThis->cEscapeCode)
  1825.                 pNodeFound = pNodeFound->pPrevious;
  1826.             else
  1827.                 break;
  1828.         }
  1829.     }
  1830.  
  1831.     return pNodeFound;
  1832. }
  1833.  
  1834. /* ******************************************************************
  1835.  *
  1836.  *   Window-dependent functions
  1837.  *
  1838.  ********************************************************************/
  1839.  
  1840. #define QWL_PRIVATE     4               // V0.9.20 (2002-08-10) [umoeller]
  1841.  
  1842. /*
  1843.  *@@ TEXTVIEWWINDATA:
  1844.  *      view control-internal structure, stored in
  1845.  *      QWL_PRIVATE at fnwpTextView.
  1846.  *      This is device-dependent on the text view
  1847.  *      window.
  1848.  */
  1849.  
  1850. typedef struct _TEXTVIEWWINDATA
  1851. {
  1852.     HAB     hab;                // anchor block (for speed)
  1853.  
  1854.     HDC     hdc;
  1855.     HPS     hps;
  1856.  
  1857.     ULONG   flStyle;            // window style flags copied on WM_CREATE
  1858.                                 // V0.9.20 (2002-08-10) [umoeller]
  1859.  
  1860.     LONG    lBackColor,
  1861.             lForeColor;
  1862.  
  1863.     XTEXTVIEWCDATA cdata;       // control data, as passed to WM_CREATE
  1864.  
  1865.     XFORMATDATA xfd;
  1866.  
  1867.     HWND    hwndVScroll,        // vertical scroll bar
  1868.             hwndHScroll;        // horizontal scroll bar
  1869.  
  1870.     BOOL    fVScrollVisible,    // TRUE if vscroll is currently used
  1871.             fHScrollVisible;    // TRUE if hscroll is currently used
  1872.  
  1873.     RECTL   rclViewReal,        // window rect as returned by WinQueryWindowRect
  1874.                                 // (top right point is inclusive!)
  1875.             rclViewPaint,       // same as rclViewReal, but excluding scroll bars
  1876.             rclViewText;        // same as rclViewPaint, but excluding cdata borders
  1877.  
  1878.     ULONG   ulViewXOfs,         // pixels that we have scrolled to the RIGHT; 0 means very left
  1879.             ulViewYOfs;         // pixels that we have scrolled to the BOTTOM; 0 means very top
  1880.  
  1881.     BOOL    fAcceptsPresParamsNow; // TRUE after first WM_PAINT
  1882.  
  1883.     // anchor clicking
  1884.     PLISTNODE   pWordNodeFirstInAnchor;  // points to first word which belongs to anchor
  1885.     // USHORT      usLastAnchorClicked;    // last anchor which was clicked (1-0xFFFF)
  1886.     PSZ         pszLastLinkClicked;     // last link that was clicked (points into llLinks)
  1887.  
  1888. } TEXTVIEWWINDATA, *PTEXTVIEWWINDATA;
  1889.  
  1890. #define ID_VSCROLL      100
  1891. #define ID_HSCROLL      101
  1892.  
  1893. /*
  1894.  *@@ UpdateTextViewPresData:
  1895.  *      called from WM_CREATE and WM_PRESPARAMCHANGED
  1896.  *      in fnwpTextView to update the TEXTVIEWWINDATA
  1897.  *      from the window's presparams. This calls
  1898.  *      txvSetDefaultFormat in turn.
  1899.  */
  1900.  
  1901. static VOID UpdateTextViewPresData(HWND hwndTextView,
  1902.                                    PTEXTVIEWWINDATA ptxvd)
  1903. {
  1904.     PSZ pszFont;
  1905.     ptxvd->lBackColor = winhQueryPresColor(hwndTextView,
  1906.                                            PP_BACKGROUNDCOLOR,
  1907.                                            FALSE,             // no inherit
  1908.                                            SYSCLR_DIALOGBACKGROUND);
  1909.     ptxvd->lForeColor = winhQueryPresColor(hwndTextView,
  1910.                                            PP_FOREGROUNDCOLOR,
  1911.                                            FALSE,             // no inherit
  1912.                                            SYSCLR_WINDOWSTATICTEXT);
  1913.  
  1914.     if ((pszFont = winhQueryWindowFont(hwndTextView)))
  1915.     {
  1916.         ULONG ulSize;
  1917.         PSZ pszFaceName;
  1918.         // _Pmpf(("font: %s", pszFont));
  1919.         if (gpihSplitPresFont(pszFont,
  1920.                               &ulSize,
  1921.                               &pszFaceName))
  1922.         {
  1923.             SetFormatFont(ptxvd->hps,
  1924.                              &ptxvd->xfd.fmtcStandard,
  1925.                              ulSize,
  1926.                              pszFaceName);
  1927.         }
  1928.         free(pszFont);
  1929.     }
  1930. }
  1931.  
  1932. /*
  1933.  *@@ AdjustViewRects:
  1934.  *      updates the internal size-dependent structures
  1935.  *      and positions the scroll bars.
  1936.  *
  1937.  *      This is device-dependent for the text view
  1938.  *      control and must be called before FormatText2Screen
  1939.  *      so that the view rectangles get calculated right.
  1940.  *
  1941.  *      Required input in TEXTVIEWWINDATA:
  1942.  *
  1943.  *      -- rclViewReal: the actual window dimensions.
  1944.  *
  1945.  *      -- cdata: control data.
  1946.  *
  1947.  *      Output from this function in TEXTVIEWWINDATA:
  1948.  *
  1949.  *      -- rclViewPaint: the paint subrectangle (which
  1950.  *         is rclViewReal minus scrollbars, if any).
  1951.  *
  1952.  *      -- rclViewText: the text subrectangle (which
  1953.  *         is rclViewPaint minus borders).
  1954.  */
  1955.  
  1956. static VOID AdjustViewRects(HWND hwndTextView,
  1957.                             PTEXTVIEWWINDATA ptxvd)
  1958. {
  1959.     ULONG ulScrollCX = WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL),
  1960.           ulScrollCY = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL),
  1961.           ulOfs;
  1962.  
  1963.     // calculate rclViewPaint:
  1964.     // 1) left
  1965.     ptxvd->rclViewPaint.xLeft = ptxvd->rclViewReal.xLeft;
  1966.     // 2) bottom
  1967.     ptxvd->rclViewPaint.yBottom = ptxvd->rclViewReal.yBottom;
  1968.     if (ptxvd->fHScrollVisible)
  1969.         // if we have a horizontal scroll bar at the bottom,
  1970.         // raise bottom by its height
  1971.         ptxvd->rclViewPaint.yBottom += ulScrollCY;
  1972.     // 3) right
  1973.     ptxvd->rclViewPaint.xRight = ptxvd->rclViewReal.xRight;
  1974.     if (ptxvd->fVScrollVisible)
  1975.         // if we have a vertical scroll bar at the right,
  1976.         // subtract its width from the right
  1977.         ptxvd->rclViewPaint.xRight -= ulScrollCX;
  1978.     ptxvd->rclViewPaint.yTop = ptxvd->rclViewReal.yTop;
  1979.  
  1980.     // calculate rclViewText from that
  1981.     ptxvd->rclViewText.xLeft = ptxvd->rclViewPaint.xLeft + ptxvd->cdata.ulXBorder;
  1982.     ptxvd->rclViewText.yBottom = ptxvd->rclViewPaint.yBottom + ptxvd->cdata.ulYBorder;
  1983.     ptxvd->rclViewText.xRight = ptxvd->rclViewPaint.xRight - ptxvd->cdata.ulXBorder;
  1984.     ptxvd->rclViewText.yTop = ptxvd->rclViewPaint.yTop - ptxvd->cdata.ulXBorder;
  1985.  
  1986.     // now reposition scroll bars; their sizes may change
  1987.     // if either the vertical or horizontal scroll bar has
  1988.     // popped up or been hidden
  1989.     if (ptxvd->flStyle & XS_VSCROLL)
  1990.     {
  1991.         // vertical scroll bar enabled:
  1992.         ulOfs = 0;
  1993.         if (ptxvd->fHScrollVisible)
  1994.             ulOfs = ulScrollCX;
  1995.         WinSetWindowPos(ptxvd->hwndVScroll,
  1996.                         HWND_TOP,
  1997.                         ptxvd->rclViewReal.xRight - ulScrollCX,
  1998.                         ulOfs,          // y
  1999.                         ulScrollCX,     // cx
  2000.                         ptxvd->rclViewReal.yTop - ulOfs,     // cy
  2001.                         SWP_MOVE | SWP_SIZE);
  2002.     }
  2003.  
  2004.     if (ptxvd->flStyle & XS_HSCROLL)
  2005.     {
  2006.         ulOfs = 0;
  2007.         if (ptxvd->fVScrollVisible)
  2008.             ulOfs = ulScrollCX;
  2009.         WinSetWindowPos(ptxvd->hwndHScroll,
  2010.                         HWND_TOP,
  2011.                         0,
  2012.                         0,
  2013.                         ptxvd->rclViewReal.xRight - ulOfs,     // cx
  2014.                         ulScrollCY, // cy
  2015.                         SWP_MOVE | SWP_SIZE);
  2016.     }
  2017. }
  2018.  
  2019. /*
  2020.  *@@ FormatText2Screen:
  2021.  *      device-dependent version of text formatting
  2022.  *      for the text view window. This calls txvFormatText
  2023.  *      in turn and updates the view's scroll bars.
  2024.  *
  2025.  *@@changed V0.9.3 (2000-05-05) [umoeller]: fixed buggy vertical scroll bars
  2026.  */
  2027.  
  2028. static VOID FormatText2Screen(HWND hwndTextView,
  2029.                               PTEXTVIEWWINDATA ptxvd,
  2030.                               BOOL fAlreadyRecursing,  // in: set this to FALSE when calling
  2031.                               BOOL fFullRecalc)
  2032. {
  2033.     ULONG   ulWinCX,
  2034.             ulWinCY;
  2035.  
  2036.     // call device-independent formatter with the
  2037.     // window presentation space
  2038.     txvFormatText(ptxvd->hps,
  2039.                   &ptxvd->xfd,
  2040.                   &ptxvd->rclViewText,
  2041.                   fFullRecalc);
  2042.  
  2043.     ulWinCY = (ptxvd->rclViewText.yTop - ptxvd->rclViewText.yBottom);
  2044.  
  2045.     if (ptxvd->ulViewYOfs < 0)
  2046.         ptxvd->ulViewYOfs = 0;
  2047.     if (ptxvd->ulViewYOfs > ((LONG)ptxvd->xfd.szlWorkspace.cy - ulWinCY))
  2048.         ptxvd->ulViewYOfs = (LONG)ptxvd->xfd.szlWorkspace.cy - ulWinCY;
  2049.  
  2050.     // vertical scroll bar enabled at all?
  2051.     if (ptxvd->flStyle & XS_VSCROLL)
  2052.     {
  2053.         BOOL fEnabled = winhUpdateScrollBar(ptxvd->hwndVScroll,
  2054.                                             ulWinCY,
  2055.                                             ptxvd->xfd.szlWorkspace.cy,
  2056.                                             ptxvd->ulViewYOfs,
  2057.                                             (ptxvd->flStyle & XS_AUTOVHIDE));
  2058.         // is auto-hide on?
  2059.         if (ptxvd->flStyle & XS_AUTOVHIDE)
  2060.         {
  2061.             // yes, auto-hide on: did visibility change?
  2062.             if (fEnabled != ptxvd->fVScrollVisible)
  2063.                 // visibility changed:
  2064.                 // if we're not already recursing,
  2065.                 // force calling ourselves again
  2066.                 if (!fAlreadyRecursing)
  2067.                 {
  2068.                     ptxvd->fVScrollVisible = fEnabled;
  2069.                     AdjustViewRects(hwndTextView,
  2070.                                     ptxvd);
  2071.                     FormatText2Screen(hwndTextView,
  2072.                                       ptxvd,
  2073.                                       TRUE,   // fAlreadyRecursing
  2074.                                       FALSE);   // quick format
  2075.                 }
  2076.         }
  2077.     }
  2078.  
  2079.     ulWinCX = (ptxvd->rclViewText.xRight - ptxvd->rclViewText.xLeft);
  2080.  
  2081.     // horizontal scroll bar enabled at all?
  2082.     if (ptxvd->flStyle & XS_HSCROLL)
  2083.     {
  2084.         BOOL fEnabled = winhUpdateScrollBar(ptxvd->hwndHScroll,
  2085.                                             ulWinCX,
  2086.                                             ptxvd->xfd.szlWorkspace.cx,
  2087.                                             ptxvd->ulViewXOfs,
  2088.                                             (ptxvd->flStyle & XS_AUTOHHIDE));
  2089.         // is auto-hide on?
  2090.         if (ptxvd->flStyle & XS_AUTOHHIDE)
  2091.         {
  2092.             // yes, auto-hide on: did visibility change?
  2093.             if (fEnabled != ptxvd->fHScrollVisible)
  2094.                 // visibility changed:
  2095.                 // if we're not already recursing,
  2096.                 // force calling ourselves again (at the bottom)
  2097.                 if (!fAlreadyRecursing)
  2098.                 {
  2099.                     ptxvd->fHScrollVisible = fEnabled;
  2100.                     AdjustViewRects(hwndTextView,
  2101.                                     ptxvd);
  2102.                 }
  2103.         }
  2104.     }
  2105.  
  2106.     WinInvalidateRect(hwndTextView, NULL, FALSE);
  2107. }
  2108.  
  2109. /*
  2110.  *@@ SetWindowText:
  2111.  *      implementation for WM_SETWINDOWPARAMS and
  2112.  *      also WM_CREATE to set the window text.
  2113.  *
  2114.  *@@added V0.9.20 (2002-08-10) [umoeller]
  2115.  */
  2116.  
  2117. VOID SetWindowText(HWND hwndTextView,
  2118.                    PTEXTVIEWWINDATA ptxvd,
  2119.                    PCSZ pcszText)
  2120. {
  2121.     if (pcszText && *pcszText)
  2122.     {
  2123.         PXSTRING pstr = &ptxvd->xfd.strViewText;
  2124.         PSZ p;
  2125.  
  2126.         switch (ptxvd->flStyle & XS_FORMAT_MASK)
  2127.         {
  2128.             case XS_PLAINTEXT:          // 0x0100
  2129.                 xstrcpy(pstr,
  2130.                         pcszText,
  2131.                         0);
  2132.                 xstrConvertLineFormat(pstr,
  2133.                                       CRLF2LF);
  2134.                 p = pstr->psz;
  2135.                 while (p = strchr(p, '\xFF'))
  2136.                     *p = ' ';
  2137.             break;
  2138.  
  2139.             case XS_HTML:               // 0x0200
  2140.                 if (p = strdup(pcszText))
  2141.                 {
  2142.                     PSZ p2 = p;
  2143.                     while (p2 = strchr(p2, '\xFF'))
  2144.                         *p2 = ' ';
  2145.                     txvConvertFromHTML(&p, NULL, NULL, NULL);
  2146.                     xstrset(pstr, p);
  2147.                     xstrConvertLineFormat(pstr,
  2148.                                           CRLF2LF);
  2149.                 }
  2150.             break;
  2151.  
  2152.             default: // case XS_PREFORMATTED:       // 0x0000
  2153.                 // no conversion (default)
  2154.                 xstrcpy(pstr,
  2155.                         pcszText,
  2156.                         0);
  2157.             break;
  2158.         }
  2159.  
  2160.         // if the last character of the window text is not "\n",
  2161.         // add it explicitly here, or our lines processing
  2162.         // is being funny
  2163.         // V0.9.20 (2002-08-10) [umoeller]
  2164.         if (pstr->psz[pstr->ulLength - 1] != '\n')
  2165.             xstrcatc(pstr, '\n');
  2166.  
  2167.         ptxvd->ulViewXOfs = 0;
  2168.         ptxvd->ulViewYOfs = 0;
  2169.         AdjustViewRects(hwndTextView,
  2170.                         ptxvd);
  2171.         FormatText2Screen(hwndTextView,
  2172.                           ptxvd,
  2173.                           FALSE,
  2174.                           TRUE);        // full format
  2175.     }
  2176. }
  2177.  
  2178. /*
  2179.  *@@ PaintViewText2Screen:
  2180.  *      device-dependent version of text painting
  2181.  *      for the text view window. This calls txvPaintText
  2182.  *      in turn and updates the view's scroll bars.
  2183.  */
  2184.  
  2185. static VOID PaintViewText2Screen(PTEXTVIEWWINDATA ptxvd,
  2186.                                  PRECTL prcl2Paint)  // in: invalid rectangle, can be NULL == paint all
  2187. {
  2188.     ULONG   ulLineIndex = 0;
  2189.     ULONG   ulYOfs = ptxvd->ulViewYOfs;
  2190.     txvPaintText(ptxvd->hab,
  2191.                  ptxvd->hps,        // paint PS: screen
  2192.                  &ptxvd->xfd,       // formatting data
  2193.                  prcl2Paint,        // update rectangle given to us
  2194.                  ptxvd->ulViewXOfs,  // current X scrolling offset
  2195.                  &ulYOfs,            // current Y scrolling offset
  2196.                  TRUE,              // draw even partly visible lines
  2197.                  &ulLineIndex);
  2198. }
  2199.  
  2200. /*
  2201.  *@@ PaintViewFocus:
  2202.  *      paint a focus rectangle.
  2203.  */
  2204.  
  2205. static VOID PaintViewFocus(HPS hps,
  2206.                            PTEXTVIEWWINDATA ptxvd,
  2207.                            BOOL fFocus)
  2208. {
  2209.     POINTL  ptl;
  2210.     HRGN    hrgn;
  2211.     GpiSetClipRegion(hps,
  2212.                      NULLHANDLE,
  2213.                      &hrgn);
  2214.     GpiSetColor(hps,
  2215.                 (fFocus)
  2216.                     ? WinQuerySysColor(HWND_DESKTOP, SYSCLR_HILITEBACKGROUND, 0)
  2217.                     : ptxvd->lBackColor);
  2218.     GpiSetLineType(hps, LINETYPE_DOT);
  2219.     ptl.x = ptxvd->rclViewPaint.xLeft;
  2220.     ptl.y = ptxvd->rclViewPaint.yBottom;
  2221.     GpiMove(hps, &ptl);
  2222.     ptl.x = ptxvd->rclViewPaint.xRight - 1;
  2223.     ptl.y = ptxvd->rclViewPaint.yTop - 1;
  2224.     GpiBox(hps,
  2225.            DRO_OUTLINE,
  2226.            &ptl,
  2227.            0, 0);
  2228. }
  2229.  
  2230. /*
  2231.  *@@ RepaintWord:
  2232.  *
  2233.  *@@added V0.9.3 (2000-05-18) [umoeller]
  2234.  */
  2235.  
  2236. static VOID RepaintWord(PTEXTVIEWWINDATA ptxvd,
  2237.                         PTXVWORD pWordThis,
  2238.                         LONG lColor)
  2239. {
  2240.     POINTL ptlStart;
  2241.     ULONG flChar = pWordThis->flChar;
  2242.     PTXVRECTANGLE pLineRcl = pWordThis->pRectangle;
  2243.  
  2244.     RECTL           rclLine;
  2245.     rclLine.xLeft = pLineRcl->rcl.xLeft - ptxvd->ulViewXOfs;
  2246.     rclLine.xRight = pLineRcl->rcl.xRight - ptxvd->ulViewXOfs;
  2247.     rclLine.yBottom = pLineRcl->rcl.yBottom + ptxvd->ulViewYOfs;
  2248.     rclLine.yTop = pLineRcl->rcl.yTop + ptxvd->ulViewYOfs;
  2249.  
  2250.     if (pWordThis->pszLinkTarget)
  2251.         flChar |= CHS_UNDERSCORE;
  2252.  
  2253.     // x start: this word's X coordinate
  2254.     ptlStart.x = pWordThis->lX - ptxvd->ulViewXOfs;
  2255.     // y start: bottom line of rectangle plus highest
  2256.     // base line offset found in all words (format step 2)
  2257.     ptlStart.y = rclLine.yBottom + pLineRcl->ulMaxBaseLineOfs;
  2258.              // pWordThis->ulBaseLineOfs;
  2259.  
  2260.     GpiSetCharSet(ptxvd->hps, pWordThis->lcid);
  2261.     if (pWordThis->lPointSize)
  2262.         // is outline font:
  2263.         gpihSetPointSize(ptxvd->hps, pWordThis->lPointSize);
  2264.  
  2265.     GpiSetColor(ptxvd->hps,
  2266.                 lColor);
  2267.  
  2268.     if (!pWordThis->cEscapeCode)
  2269.     {
  2270.         gpihCharStringPosAt(ptxvd->hps,
  2271.                             &ptlStart,
  2272.                             &rclLine,
  2273.                             flChar,
  2274.                             pWordThis->cChars,
  2275.                             (PSZ)pWordThis->pStart);
  2276.     }
  2277.     else
  2278.         // escape to be painted:
  2279.         DrawListMarker(ptxvd->hps,
  2280.                        &rclLine,
  2281.                        pWordThis,
  2282.                        ptxvd->ulViewXOfs);
  2283. }
  2284.  
  2285. /*
  2286.  *@@ RepaintAnchor:
  2287.  *
  2288.  *@@added V0.9.3 (2000-05-18) [umoeller]
  2289.  */
  2290.  
  2291. static VOID RepaintAnchor(PTEXTVIEWWINDATA ptxvd,
  2292.                           LONG lColor)
  2293. {
  2294.     PLISTNODE pNode = ptxvd->pWordNodeFirstInAnchor;
  2295.     PSZ     pszLinkTarget = NULL;
  2296.     while (pNode)
  2297.     {
  2298.         PTXVWORD pWordThis = (PTXVWORD)pNode->pItemData;
  2299.         if (!pszLinkTarget)
  2300.             // first loop:
  2301.             pszLinkTarget = pWordThis->pszLinkTarget;
  2302.         else
  2303.             if (pWordThis->pszLinkTarget != pszLinkTarget)
  2304.                 // first word with different anchor:
  2305.                 break;
  2306.  
  2307.         RepaintWord(ptxvd,
  2308.                     pWordThis,
  2309.                     lColor);
  2310.         pNode = pNode->pNext;
  2311.     }
  2312. }
  2313.  
  2314. /*
  2315.  *@@ fnwpTextView:
  2316.  *      window procedure for the text view control. This is
  2317.  *      registered with the WC_XTEXTVIEW class in txvRegisterTextView.
  2318.  *
  2319.  *      The text view control is not a subclassed whatever control,
  2320.  *      but a control implemented from scratch. As a result, we
  2321.  *      had to implement all messages which are usually recognized
  2322.  *      by a control. In detail, we have:
  2323.  *
  2324.  *      -- WM_WINDOWPOSCHANGED: if the control is resized, the
  2325.  *         text is reformatted and the scroll bars are readjusted.
  2326.  *         See AdjustViewRects and txvFormatText.
  2327.  *
  2328.  *      -- WM_PRESPARAMCHANGED: if fonts or colors are dropped
  2329.  *         on the control, we reformat the text also.
  2330.  *
  2331.  *      -- WM_HSCROLL and WM_VSCROLL: this calls winhHandleScrollMsg
  2332.  *         to scroll the window contents.
  2333.  *
  2334.  *      -- WM_BUTTON1DOWN: this sets the focus to the control.
  2335.  *
  2336.  *      -- WM_SETFOCUS: if we receive the focus, we draw a fine
  2337.  *         dotted line in the "selection" color around the text
  2338.  *         window.
  2339.  *
  2340.  *      -- WM_CHAR: if we have the focus, the user can move the
  2341.  *         visible part within the workspace using the usual
  2342.  *         cursor and HOME/END keys.
  2343.  *
  2344.  *      -- WM_MOUSEMOVE: this sends WM_CONTROLPOINTER to the
  2345.  *         owner so the owner can change the mouse pointer.
  2346.  *
  2347.  *      <B>Painting</B>
  2348.  *
  2349.  *      The text view control creates a micro presentation space
  2350.  *      from the window's device context upon WM_CREATE, which is
  2351.  *      stored in TEXTVIEWWINDATA. We do not use WinBeginPaint in
  2352.  *      WM_PAINT, but only the PS we created ourselves. This saves
  2353.  *      us from resetting and researching all the fonts etc., which
  2354.  *      should be speedier.
  2355.  *
  2356.  *      The text view control uses a private window word for storing
  2357.  *      its own data. The client is free to use QWL_USER of the
  2358.  *      text view control.
  2359.  *
  2360.  *@@changed V0.9.3 (2000-05-05) [umoeller]: removed TXM_NEWTEXT; now supporting WinSetWindowText
  2361.  *@@changed V0.9.3 (2000-05-07) [umoeller]: crashed if create param was NULL; fixed
  2362.  *@@changed V0.9.20 (2002-08-10) [umoeller]: no longer using QWL_USER, which is free now
  2363.  *@@changed V0.9.20 (2002-08-10) [umoeller]: setting text on window creation never worked, fixed
  2364.  *@@changed V0.9.20 (2002-08-10) [umoeller]: added TXN_ANCHORCLICKED owner notify for anchors
  2365.  *@@changed V0.9.20 (2002-08-10) [umoeller]: converted private style flags to XS_* window style flags
  2366.  *@@changed V0.9.20 (2002-08-10) [umoeller]: added support for formatting HTML and plain text automatically
  2367.  */
  2368.  
  2369. static MRESULT EXPENTRY fnwpTextView(HWND hwndTextView, ULONG msg, MPARAM mp1, MPARAM mp2)
  2370. {
  2371.     MRESULT mrc = 0;
  2372.  
  2373.     PTEXTVIEWWINDATA ptxvd = (PTEXTVIEWWINDATA)WinQueryWindowPtr(hwndTextView, QWL_PRIVATE);
  2374.  
  2375.     switch (msg)
  2376.     {
  2377.         /*
  2378.          * WM_CREATE:
  2379.          *
  2380.          */
  2381.  
  2382.         case WM_CREATE:
  2383.         {
  2384.             PXTEXTVIEWCDATA pcd = (PXTEXTVIEWCDATA)mp1;
  2385.                         // can be NULL
  2386.             PCREATESTRUCT pcs = (PCREATESTRUCT)mp2;
  2387.             SBCDATA sbcd;
  2388.  
  2389.             mrc = (MPARAM)TRUE;     // error
  2390.  
  2391.             // allocate TEXTVIEWWINDATA for QWL_PRIVATE
  2392.             if (ptxvd = (PTEXTVIEWWINDATA)malloc(sizeof(TEXTVIEWWINDATA)))
  2393.             {
  2394.                 SIZEL   szlPage = {0, 0};
  2395.                 BOOL    fShow = FALSE;
  2396.  
  2397.                 // query message queue
  2398.                 HMQ hmq = WinQueryWindowULong(hwndTextView, QWL_HMQ);
  2399.                 // get codepage of message queue
  2400.                 ULONG ulCodepage = WinQueryCp(hmq);
  2401.  
  2402.                 memset(ptxvd, 0, sizeof(TEXTVIEWWINDATA));
  2403.                 WinSetWindowPtr(hwndTextView, QWL_PRIVATE, ptxvd);
  2404.  
  2405.                 ptxvd->hab = WinQueryAnchorBlock(hwndTextView);
  2406.  
  2407.                 ptxvd->hdc = WinOpenWindowDC(hwndTextView);
  2408.                 ptxvd->hps = GpiCreatePS(ptxvd->hab,
  2409.                                          ptxvd->hdc,
  2410.                                          &szlPage, // use same page size as device
  2411.                                          PU_PELS | GPIT_MICRO | GPIA_ASSOC);
  2412.  
  2413.                 // copy window style flags V0.9.20 (2002-08-10) [umoeller]
  2414.                 ptxvd->flStyle = pcs->flStyle;
  2415.  
  2416.                 gpihSwitchToRGB(ptxvd->hps);
  2417.  
  2418.                 // set codepage; GPI defaults this to
  2419.                 // the process codepage
  2420.                 GpiSetCp(ptxvd->hps, ulCodepage);
  2421.  
  2422.                 txvInitFormat(&ptxvd->xfd);
  2423.  
  2424.                 // copy control data, if present
  2425.                 if (pcd)
  2426.                     memcpy(&ptxvd->cdata, pcd, pcd->cbData);
  2427.  
  2428.                 // check values which might cause null divisions
  2429.                 if (ptxvd->cdata.ulVScrollLineUnit == 0)
  2430.                     ptxvd->cdata.ulVScrollLineUnit = 15;
  2431.                 if (ptxvd->cdata.ulHScrollLineUnit == 0)
  2432.                     ptxvd->cdata.ulHScrollLineUnit = 15;
  2433.  
  2434.                 ptxvd->fAcceptsPresParamsNow = FALSE;
  2435.  
  2436.                 // copy window dimensions from CREATESTRUCT
  2437.                 ptxvd->rclViewReal.xLeft = 0;
  2438.                 ptxvd->rclViewReal.yBottom = 0;
  2439.                 ptxvd->rclViewReal.xRight = pcs->cx;
  2440.                 ptxvd->rclViewReal.yTop = pcs->cy;
  2441.  
  2442.                 sbcd.cb = sizeof(SBCDATA);
  2443.                 sbcd.sHilite = 0;
  2444.                 sbcd.posFirst = 0;
  2445.                 sbcd.posLast = 100;
  2446.                 sbcd.posThumb = 30;
  2447.                 sbcd.cVisible = 50;
  2448.                 sbcd.cTotal = 50;
  2449.  
  2450.                 ptxvd->hwndVScroll = WinCreateWindow(hwndTextView,
  2451.                                                      WC_SCROLLBAR,
  2452.                                                      "",
  2453.                                                      SBS_VERT | SBS_THUMBSIZE | WS_VISIBLE,
  2454.                                                      10, 10,
  2455.                                                      20, 100,
  2456.                                                      hwndTextView,     // owner
  2457.                                                      HWND_TOP,
  2458.                                                      ID_VSCROLL,
  2459.                                                      &sbcd,
  2460.                                                      0);
  2461.                 fShow = ((ptxvd->flStyle & XS_VSCROLL) != 0);
  2462.                 WinShowWindow(ptxvd->hwndVScroll, fShow);
  2463.                 ptxvd->fVScrollVisible = fShow;
  2464.  
  2465.                 ptxvd->hwndHScroll = WinCreateWindow(hwndTextView,
  2466.                                                      WC_SCROLLBAR,
  2467.                                                      "",
  2468.                                                      SBS_THUMBSIZE | WS_VISIBLE,
  2469.                                                      10, 10,
  2470.                                                      20, 100,
  2471.                                                      hwndTextView,     // owner
  2472.                                                      HWND_TOP,
  2473.                                                      ID_HSCROLL,
  2474.                                                      &sbcd,
  2475.                                                      0);
  2476.                 fShow = ((ptxvd->flStyle & XS_HSCROLL) != 0);
  2477.                 WinShowWindow(ptxvd->hwndHScroll, fShow);
  2478.                 ptxvd->fHScrollVisible = fShow;
  2479.  
  2480.                 if (ptxvd->flStyle & XS_WORDWRAP)
  2481.                     // word-wrapping should be enabled from the start:
  2482.                     // V0.9.20 (2002-08-10) [umoeller]
  2483.                     ptxvd->xfd.fmtpStandard.fWordWrap = TRUE;
  2484.  
  2485.                 // set "code" format
  2486.                 SetFormatFont(ptxvd->hps,
  2487.                               &ptxvd->xfd.fmtcCode,
  2488.                               6,
  2489.                               "System VIO");
  2490.  
  2491.                 // get colors from presparams/syscolors
  2492.                 UpdateTextViewPresData(hwndTextView, ptxvd);
  2493.  
  2494.                 AdjustViewRects(hwndTextView,
  2495.                                 ptxvd);
  2496.  
  2497.                 if (ptxvd->flStyle & XS_HTML)
  2498.                 {
  2499.                     // if we're operating in HTML mode, set a
  2500.                     // different default paragraph format to
  2501.                     // make things prettier
  2502.                     // V0.9.20 (2002-08-10) [umoeller]
  2503.                     ptxvd->xfd.fmtpStandard.lSpaceBefore = 5;
  2504.                     ptxvd->xfd.fmtpStandard.lSpaceAfter = 5;
  2505.                 }
  2506.  
  2507.                 // setting the window text on window creation never
  2508.                 // worked V0.9.20 (2002-08-10) [umoeller]
  2509.                 if (pcs->pszText)
  2510.                     SetWindowText(hwndTextView,
  2511.                                   ptxvd,
  2512.                                   pcs->pszText);
  2513.  
  2514.                 mrc = (MPARAM)FALSE;        // OK
  2515.             }
  2516.         }
  2517.         break;
  2518.  
  2519.         /*
  2520.          * WM_SETWINDOWPARAMS:
  2521.          *      this message sets the window parameters,
  2522.          *      most importantly, the window text.
  2523.          *
  2524.          *      This updates the control.
  2525.          */
  2526.  
  2527.         case WM_SETWINDOWPARAMS:
  2528.         {
  2529.             WNDPARAMS *pwndParams;
  2530.             if (    (pwndParams = (WNDPARAMS *)mp1)
  2531.                  && (pwndParams->fsStatus & WPM_TEXT)
  2532.                )
  2533.             {
  2534.                 SetWindowText(hwndTextView,
  2535.                               ptxvd,
  2536.                               pwndParams->pszText);
  2537.                 mrc = (MRESULT)TRUE;     // was missing V0.9.20 (2002-08-10) [umoeller]
  2538.             }
  2539.         }
  2540.         break;
  2541.  
  2542.         /*
  2543.          * WM_WINDOWPOSCHANGED:
  2544.          *
  2545.          */
  2546.  
  2547.         case WM_WINDOWPOSCHANGED:
  2548.         {
  2549.             // this msg is passed two SWP structs:
  2550.             // one for the old, one for the new data
  2551.             // (from PM docs)
  2552.             PSWP pswpNew = (PSWP)(mp1);
  2553.             // PSWP pswpOld = pswpNew + 1;
  2554.  
  2555.             // resizing?
  2556.             if (pswpNew->fl & SWP_SIZE)
  2557.             {
  2558.                 if (ptxvd)
  2559.                 {
  2560.                     WinQueryWindowRect(hwndTextView,
  2561.                                        &ptxvd->rclViewReal);
  2562.                     AdjustViewRects(hwndTextView,
  2563.                                     ptxvd);
  2564.                     FormatText2Screen(hwndTextView,
  2565.                                       ptxvd,
  2566.                                       FALSE,
  2567.                                       FALSE);   // quick format
  2568.                 }
  2569.             }
  2570.         }
  2571.         break;
  2572.  
  2573.         /*
  2574.          * WM_PAINT:
  2575.          *
  2576.          */
  2577.  
  2578.         case WM_PAINT:
  2579.         {
  2580.             HRGN    hrgnOldClip;
  2581.  
  2582.             /* HPS hps = WinBeginPaint(hwndTextView,
  2583.                                     ptxvd->hps,
  2584.                                     &rcl2Paint);  // store invalid rectangle here
  2585.                */
  2586.  
  2587.             if (ptxvd)
  2588.             {
  2589.                 RECTL   rclClip;
  2590.                 RECTL   rcl2Update;
  2591.  
  2592.                 // get update rectangle
  2593.                 WinQueryUpdateRect(hwndTextView,
  2594.                                    &rcl2Update);
  2595.                 // since we're not using WinBeginPaint,
  2596.                 // we must validate the update region,
  2597.                 // or we'll get bombed with WM_PAINT msgs
  2598.                 WinValidateRect(hwndTextView,
  2599.                                 NULL,
  2600.                                 FALSE);
  2601.  
  2602.                 // reset clip region to "all"
  2603.                 GpiSetClipRegion(ptxvd->hps,
  2604.                                  NULLHANDLE,
  2605.                                  &hrgnOldClip);        // out: old clip region
  2606.                 // reduce clip region to update rectangle
  2607.                 GpiIntersectClipRectangle(ptxvd->hps,
  2608.                                           &rcl2Update);     // exclusive
  2609.  
  2610.                 // draw little box at the bottom right
  2611.                 // (in between scroll bars) if we have
  2612.                 // both vertical and horizontal scroll bars
  2613.                 if (    (ptxvd->flStyle & (XS_VSCROLL | XS_HSCROLL))
  2614.                         == (XS_VSCROLL | XS_HSCROLL)
  2615.                      && (ptxvd->fVScrollVisible)
  2616.                      && (ptxvd->fHScrollVisible)
  2617.                    )
  2618.                 {
  2619.                     RECTL   rclBox;
  2620.                     rclBox.xLeft = ptxvd->rclViewPaint.xRight;
  2621.                     rclBox.yBottom = 0;
  2622.                     rclBox.xRight = rclBox.xLeft + WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL);
  2623.                     rclBox.yTop = WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
  2624.                     WinFillRect(ptxvd->hps,
  2625.                                 &rclBox,
  2626.                                 WinQuerySysColor(HWND_DESKTOP,
  2627.                                                  SYSCLR_DIALOGBACKGROUND,
  2628.                                                  0));
  2629.                 }
  2630.  
  2631.                 // paint "view paint" rectangle white;
  2632.                 // this can be larger than "view text"
  2633.                 WinFillRect(ptxvd->hps,
  2634.                             &ptxvd->rclViewPaint,       // exclusive
  2635.                             ptxvd->lBackColor);
  2636.  
  2637.                 // now reduce clipping rectangle to "view text" rectangle
  2638.                 rclClip.xLeft = ptxvd->rclViewText.xLeft;
  2639.                 rclClip.xRight = ptxvd->rclViewText.xRight - 1;
  2640.                 rclClip.yBottom = ptxvd->rclViewText.yBottom;
  2641.                 rclClip.yTop = ptxvd->rclViewText.yTop - 1;
  2642.                 GpiIntersectClipRectangle(ptxvd->hps,
  2643.                                           &rclClip);    // exclusive
  2644.                 // finally, draw text lines in invalid rectangle;
  2645.                 // this subfunction is smart enough to redraw only
  2646.                 // the lines which intersect with rcl2Update
  2647.                 GpiSetColor(ptxvd->hps, ptxvd->lForeColor);
  2648.                 PaintViewText2Screen(ptxvd,
  2649.                                      &rcl2Update);
  2650.  
  2651.                 if (    (!(ptxvd->flStyle & XS_STATIC))
  2652.                                 // V0.9.20 (2002-08-10) [umoeller]
  2653.                      && (WinQueryFocus(HWND_DESKTOP) == hwndTextView)
  2654.                    )
  2655.                 {
  2656.                     // we have the focus:
  2657.                     // reset clip region to "all"
  2658.                     GpiSetClipRegion(ptxvd->hps,
  2659.                                      NULLHANDLE,
  2660.                                      &hrgnOldClip);        // out: old clip region
  2661.                     PaintViewFocus(ptxvd->hps,
  2662.                                    ptxvd,
  2663.                                    TRUE);
  2664.                 }
  2665.  
  2666.                 ptxvd->fAcceptsPresParamsNow = TRUE;
  2667.             }
  2668.  
  2669.             // WinEndPaint(hps);
  2670.         }
  2671.         break;
  2672.  
  2673.         /*
  2674.          * WM_PRESPARAMCHANGED:
  2675.          *
  2676.          *      Changing the color or font settings
  2677.          *      is equivalent to changing the default
  2678.          *      paragraph format. See TXM_SETFORMAT.
  2679.          */
  2680.  
  2681.         case WM_PRESPARAMCHANGED:
  2682.             mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
  2683.             if (ptxvd)
  2684.             {
  2685.                 LONG    lPPIndex = (LONG)mp1;
  2686.                 switch (lPPIndex)
  2687.                 {
  2688.                     case 0:     // layout palette thing dropped
  2689.                     case PP_BACKGROUNDCOLOR:
  2690.                     case PP_FOREGROUNDCOLOR:
  2691.                     case PP_FONTNAMESIZE:
  2692.                         // re-query our presparams
  2693.                         UpdateTextViewPresData(hwndTextView, ptxvd);
  2694.                 }
  2695.  
  2696.                 if (ptxvd->fAcceptsPresParamsNow)
  2697.                     FormatText2Screen(hwndTextView,
  2698.                                       ptxvd,
  2699.                                       FALSE,
  2700.                                       TRUE);    // full reformat
  2701.             }
  2702.         break;
  2703.  
  2704.         /*
  2705.          * WM_VSCROLL:
  2706.          *
  2707.          */
  2708.  
  2709.         case WM_VSCROLL:
  2710.             if (ptxvd->fVScrollVisible)
  2711.             {
  2712.                 winhHandleScrollMsg(hwndTextView,
  2713.                                     ptxvd->hwndVScroll,
  2714.                                     &ptxvd->ulViewYOfs,
  2715.                                     &ptxvd->rclViewText,
  2716.                                     ptxvd->xfd.szlWorkspace.cy,
  2717.                                     ptxvd->cdata.ulVScrollLineUnit,
  2718.                                     msg,
  2719.                                     mp2);
  2720.             }
  2721.         break;
  2722.  
  2723.         /*
  2724.          * WM_HSCROLL:
  2725.          *
  2726.          */
  2727.  
  2728.         case WM_HSCROLL:
  2729.             if (ptxvd->fHScrollVisible)
  2730.             {
  2731.                 winhHandleScrollMsg(hwndTextView,
  2732.                                     ptxvd->hwndHScroll,
  2733.                                     &ptxvd->ulViewXOfs,
  2734.                                     &ptxvd->rclViewText,
  2735.                                     ptxvd->xfd.szlWorkspace.cx,
  2736.                                     ptxvd->cdata.ulHScrollLineUnit,
  2737.                                     msg,
  2738.                                     mp2);
  2739.             }
  2740.         break;
  2741.  
  2742.         /*
  2743.          * WM_SETFOCUS:
  2744.          *
  2745.          */
  2746.  
  2747.         case WM_SETFOCUS:
  2748.         {
  2749.             if (ptxvd->flStyle & XS_STATIC)
  2750.             {
  2751.                 if (mp2)
  2752.                 {
  2753.                     // we're receiving the focus, but shouldn't have it:
  2754.                     // then behave like the static control does, that is,
  2755.                     // give focus to the next window in the dialog
  2756.                     HWND    hwnd = hwndTextView,
  2757.                             hwndStart = hwnd;
  2758.  
  2759.                     while (TRUE)
  2760.                     {
  2761.                         ULONG flStyle;
  2762.  
  2763.                         if (!(hwnd = WinQueryWindow(hwnd, QW_NEXT)))
  2764.                             hwnd = WinQueryWindow(WinQueryWindow(hwndStart, QW_PARENT), QW_TOP);
  2765.  
  2766.                         // avoid endless looping
  2767.                         if (hwnd == hwndStart)
  2768.                         {
  2769.                             if (    (hwnd = WinQueryWindow(hwnd, QW_OWNER))
  2770.                                  && (hwnd == hwndStart)
  2771.                                )
  2772.                                 hwnd = NULLHANDLE;
  2773.  
  2774.                             break;
  2775.                         }
  2776.  
  2777.                         if (    (flStyle = WinQueryWindowULong(hwnd, QWL_STYLE))
  2778.                              && (flStyle & (WS_DISABLED | WS_TABSTOP | WS_VISIBLE)
  2779.                                     == (WS_TABSTOP | WS_VISIBLE))
  2780.                            )
  2781.                         {
  2782.                             WinSetFocus(HWND_DESKTOP, hwnd);
  2783.                             break;
  2784.                         }
  2785.                     };
  2786.                 }
  2787.             }
  2788.             else
  2789.             {
  2790.                 HPS hps = WinGetPS(hwndTextView);
  2791.                 gpihSwitchToRGB(hps);
  2792.                 PaintViewFocus(hps,
  2793.                                ptxvd,
  2794.                                (mp2 != 0));
  2795.                 WinReleasePS(hps);
  2796.             }
  2797.         }
  2798.         break;
  2799.  
  2800.         /*
  2801.          * WM_MOUSEMOVE:
  2802.          *      send WM_CONTROLPOINTER to owner.
  2803.          */
  2804.  
  2805.         case WM_MOUSEMOVE:
  2806.         {
  2807.             HWND    hwndOwner;
  2808.             if (hwndOwner = WinQueryWindow(hwndTextView, QW_OWNER))
  2809.             {
  2810.                 HPOINTER hptrSet
  2811.                     = (HPOINTER)WinSendMsg(hwndOwner,
  2812.                                            WM_CONTROLPOINTER,
  2813.                                            (MPARAM)(LONG)WinQueryWindowUShort(hwndTextView,
  2814.                                                                               QWS_ID),
  2815.                                            (MPARAM)WinQuerySysPointer(HWND_DESKTOP,
  2816.                                                                       SPTR_ARROW,
  2817.                                                                       FALSE));
  2818.                 WinSetPointer(HWND_DESKTOP, hptrSet);
  2819.             }
  2820.         }
  2821.         break;
  2822.  
  2823.         /*
  2824.          * WM_BUTTON1DOWN:
  2825.          *
  2826.          */
  2827.  
  2828.         case WM_BUTTON1DOWN:
  2829.         {
  2830.             POINTL ptlPos;
  2831.             PLISTNODE pWordNodeClicked;
  2832.  
  2833.             ptlPos.x = SHORT1FROMMP(mp1) + ptxvd->ulViewXOfs;
  2834.             ptlPos.y = SHORT2FROMMP(mp1) - ptxvd->ulViewYOfs;
  2835.  
  2836.             if (    (!(ptxvd->flStyle & XS_STATIC))
  2837.                             // V0.9.20 (2002-08-10) [umoeller]
  2838.                  && (hwndTextView != WinQueryFocus(HWND_DESKTOP))
  2839.                )
  2840.                 WinSetFocus(HWND_DESKTOP, hwndTextView);
  2841.  
  2842.             ptxvd->pszLastLinkClicked = NULL;
  2843.  
  2844.             if (pWordNodeClicked = txvFindWordFromPoint(&ptxvd->xfd,
  2845.                                                         &ptlPos))
  2846.             {
  2847.                 PTXVWORD pWordClicked = (PTXVWORD)pWordNodeClicked->pItemData;
  2848.  
  2849.                 // store link target (can be NULL)
  2850.                 if (ptxvd->pszLastLinkClicked = pWordClicked->pszLinkTarget)
  2851.                 {
  2852.                     // word has a link target:
  2853.                     PLISTNODE   pNode = pWordNodeClicked;
  2854.  
  2855.                     // reset first word of anchor
  2856.                     ptxvd->pWordNodeFirstInAnchor = NULL;
  2857.  
  2858.                     // go back to find the first word which has this anchor,
  2859.                     // because we need to repaint them all
  2860.                     while (pNode)
  2861.                     {
  2862.                         PTXVWORD pWordThis = (PTXVWORD)pNode->pItemData;
  2863.                         if (pWordThis->pszLinkTarget == pWordClicked->pszLinkTarget)
  2864.                         {
  2865.                             // still has same anchor:
  2866.                             // go for previous
  2867.                             ptxvd->pWordNodeFirstInAnchor = pNode;
  2868.                             pNode = pNode->pPrevious;
  2869.                         }
  2870.                         else
  2871.                             // different anchor:
  2872.                             // pNodeFirst points to first node with same anchor now
  2873.                             break;
  2874.                     }
  2875.  
  2876.                     RepaintAnchor(ptxvd,
  2877.                                   RGBCOL_RED);
  2878.                 }
  2879.             }
  2880.  
  2881.             WinSetCapture(HWND_DESKTOP, hwndTextView);
  2882.             mrc = (MPARAM)TRUE;
  2883.         }
  2884.         break;
  2885.  
  2886.         /*
  2887.          * WM_BUTTON1UP:
  2888.          *
  2889.          */
  2890.  
  2891.         case WM_BUTTON1UP:
  2892.         {
  2893.             POINTL      ptlPos;
  2894.             // PTXVWORD    pWordClicked = NULL;
  2895.             HWND        hwndOwner = NULLHANDLE;
  2896.  
  2897.             ptlPos.x = SHORT1FROMMP(mp1) + ptxvd->ulViewXOfs;
  2898.             ptlPos.y = SHORT2FROMMP(mp1) - ptxvd->ulViewYOfs;
  2899.             WinSetCapture(HWND_DESKTOP, NULLHANDLE);
  2900.  
  2901.             if (ptxvd->pszLastLinkClicked)
  2902.             {
  2903.                 RepaintAnchor(ptxvd,
  2904.                               ptxvd->lForeColor);
  2905.  
  2906.                 // nofify owner
  2907.                 if (hwndOwner = WinQueryWindow(hwndTextView, QW_OWNER))
  2908.                     WinPostMsg(hwndOwner,
  2909.                                WM_CONTROL,
  2910.                                MPFROM2SHORT(WinQueryWindowUShort(hwndTextView,
  2911.                                                                  QWS_ID),
  2912.                                             TXVN_LINK),
  2913.                                (MPARAM)(ULONG)(ptxvd->pszLastLinkClicked));
  2914.             }
  2915.  
  2916.             mrc = (MPARAM)TRUE;
  2917.         }
  2918.         break;
  2919.  
  2920.         /*
  2921.          * WM_CHAR:
  2922.          *
  2923.          */
  2924.  
  2925.         case WM_CHAR:
  2926.         {
  2927.             BOOL    fDefProc = TRUE;
  2928.             USHORT usFlags    = SHORT1FROMMP(mp1);
  2929.             // USHORT usch       = SHORT1FROMMP(mp2);
  2930.             USHORT usvk       = SHORT2FROMMP(mp2);
  2931.  
  2932.             if (usFlags & KC_VIRTUALKEY)
  2933.             {
  2934.                 ULONG   ulMsg = 0;
  2935.                 USHORT  usID = ID_VSCROLL;
  2936.                 SHORT   sPos = 0;
  2937.                 SHORT   usCmd = 0;
  2938.                 fDefProc = FALSE;
  2939.  
  2940.                 switch (usvk)
  2941.                 {
  2942.                     case VK_UP:
  2943.                         ulMsg = WM_VSCROLL;
  2944.                         usCmd = SB_LINEUP;
  2945.                     break;
  2946.  
  2947.                     case VK_DOWN:
  2948.                         ulMsg = WM_VSCROLL;
  2949.                         usCmd = SB_LINEDOWN;
  2950.                     break;
  2951.  
  2952.                     case VK_RIGHT:
  2953.                         ulMsg = WM_HSCROLL;
  2954.                         usCmd = SB_LINERIGHT;
  2955.                     break;
  2956.  
  2957.                     case VK_LEFT:
  2958.                         ulMsg = WM_HSCROLL;
  2959.                         usCmd = SB_LINELEFT;
  2960.                     break;
  2961.  
  2962.                     case VK_PAGEUP:
  2963.                         ulMsg = WM_VSCROLL;
  2964.                         if (usFlags & KC_CTRL)
  2965.                         {
  2966.                             sPos = 0;
  2967.                             usCmd = SB_SLIDERPOSITION;
  2968.                         }
  2969.                         else
  2970.                             usCmd = SB_PAGEUP;
  2971.                     break;
  2972.  
  2973.                     case VK_PAGEDOWN:
  2974.                         ulMsg = WM_VSCROLL;
  2975.                         if (usFlags & KC_CTRL)
  2976.                         {
  2977.                             sPos = ptxvd->xfd.szlWorkspace.cy;
  2978.                             usCmd = SB_SLIDERPOSITION;
  2979.                         }
  2980.                         else
  2981.                             usCmd = SB_PAGEDOWN;
  2982.                     break;
  2983.  
  2984.                     case VK_HOME:
  2985.                         if (usFlags & KC_CTRL)
  2986.                             // vertical:
  2987.                             ulMsg = WM_VSCROLL;
  2988.                         else
  2989.                             ulMsg = WM_HSCROLL;
  2990.  
  2991.                         sPos = 0;
  2992.                         usCmd = SB_SLIDERPOSITION;
  2993.                     break;
  2994.  
  2995.                     case VK_END:
  2996.                         if (usFlags & KC_CTRL)
  2997.                         {
  2998.                             // vertical:
  2999.                             ulMsg = WM_VSCROLL;
  3000.                             sPos = ptxvd->xfd.szlWorkspace.cy;
  3001.                         }
  3002.                         else
  3003.                         {
  3004.                             ulMsg = WM_HSCROLL;
  3005.                             sPos = ptxvd->xfd.szlWorkspace.cx;
  3006.                         }
  3007.  
  3008.                         usCmd = SB_SLIDERPOSITION;
  3009.                     break;
  3010.  
  3011.                     default:
  3012.                         // other:
  3013.                         fDefProc = TRUE;
  3014.                 }
  3015.  
  3016.                 if (    ((usFlags & KC_KEYUP) == 0)
  3017.                      && (ulMsg)
  3018.                    )
  3019.                     WinSendMsg(hwndTextView,
  3020.                                ulMsg,
  3021.                                MPFROMSHORT(usID),
  3022.                                MPFROM2SHORT(sPos,
  3023.                                             usCmd));
  3024.             }
  3025.  
  3026.             if (fDefProc)
  3027.                 mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
  3028.                         // sends to owner
  3029.             else
  3030.                 mrc = (MPARAM)TRUE;
  3031.         }
  3032.         break;
  3033.  
  3034.         /*
  3035.          *@@ TXM_QUERYPARFORMAT:
  3036.          *      this msg can be sent to the text view control
  3037.          *      to retrieve the paragraph format with the
  3038.          *      index specified in mp1.
  3039.          *
  3040.          *      This must be sent, not posted, to the control.
  3041.          *
  3042.          *      Parameters:
  3043.          *
  3044.          *      -- ULONG mp1: index of format to query.
  3045.          *              Must be 0 currently for the standard
  3046.          *              paragraph format.
  3047.          *
  3048.          *      -- PXFMTPARAGRAPH mp2: pointer to buffer
  3049.          *              which is to receive the formatting
  3050.          *              data.
  3051.          *
  3052.          *      Returns TRUE if copying was successful.
  3053.          *
  3054.          *@@added V0.9.3 (2000-05-06) [umoeller]
  3055.          */
  3056.  
  3057.         case TXM_QUERYPARFORMAT:
  3058.         {
  3059.             PXFMTPARAGRAPH pFmt = NULL; // source
  3060.  
  3061.             mrc = (MPARAM)FALSE;
  3062.  
  3063.             if (mp1 == 0)
  3064.                 pFmt = &ptxvd->xfd.fmtpStandard;
  3065.             /* else if ((ULONG)mp1 == 1)
  3066.                 pFmt = &ptxvd->xfd.fmtpCode; */
  3067.  
  3068.             if ((pFmt) && (mp2))
  3069.             {
  3070.                 memcpy(mp2, pFmt, sizeof(XFMTPARAGRAPH));
  3071.                 mrc = (MPARAM)TRUE;
  3072.             }
  3073.         }
  3074.         break;
  3075.  
  3076.         /*
  3077.          *@@ TXM_SETPARFORMAT:
  3078.          *      reverse to TXM_QUERYPARFORMAT, this sets a
  3079.          *      paragraph format (line spacings, margins
  3080.          *      and such).
  3081.          *
  3082.          *      This must be sent, not posted, to the control.
  3083.          *
  3084.          *      Parameters:
  3085.          *
  3086.          *      -- ULONG mp1: index of format to set.
  3087.          *              Must be 0 currently for the standard
  3088.          *              paragraph format.
  3089.          *
  3090.          *      -- PXFMTPARAGRAPH mp2: pointer to buffer
  3091.          *              from which to copy formatting data.
  3092.          *              If this pointer is NULL, the format
  3093.          *              is reset to the default.
  3094.          *
  3095.          *      This reformats the control.
  3096.          *
  3097.          *@@added V0.9.3 (2000-05-06) [umoeller]
  3098.          */
  3099.  
  3100.         case TXM_SETPARFORMAT:
  3101.         {
  3102.             PXFMTPARAGRAPH pFmt = NULL;     // target
  3103.  
  3104.             mrc = (MPARAM)FALSE;
  3105.  
  3106.             if (mp1 == 0)
  3107.                 pFmt = &ptxvd->xfd.fmtpStandard;
  3108.             /* else if ((ULONG)mp1 == 1)
  3109.                 pFmt = &ptxvd->xfd.fmtpCode; */
  3110.  
  3111.             if (pFmt)
  3112.             {
  3113.                 if (mp2)
  3114.                     // copy
  3115.                     memcpy(pFmt, mp2, sizeof(XFMTPARAGRAPH));
  3116.                 else
  3117.                     // default:
  3118.                     memset(pFmt, 0, sizeof(XFMTPARAGRAPH));
  3119.  
  3120.                 FormatText2Screen(hwndTextView,
  3121.                                   ptxvd,
  3122.                                   FALSE,
  3123.                                   TRUE);    // full reformat
  3124.  
  3125.                 mrc = (MPARAM)TRUE;
  3126.             }
  3127.         }
  3128.         break;
  3129.  
  3130.         /*
  3131.          *@@ TXM_SETWORDWRAP:
  3132.          *      this text view control msg quickly changes
  3133.          *      the word-wrapping style of the default
  3134.          *      paragraph formatting.
  3135.          *
  3136.          *      This may be sent or posted.
  3137.          *
  3138.          *      (BOOL)mp1 determines whether word wrapping
  3139.          *      should be turned on or off.
  3140.          */
  3141.  
  3142.         case TXM_SETWORDWRAP:
  3143.         {
  3144.             BOOL    ulOldFlFormat = ptxvd->xfd.fmtpStandard.fWordWrap;
  3145.             ptxvd->xfd.fmtpStandard.fWordWrap = (BOOL)mp1;
  3146.             if (ptxvd->xfd.fmtpStandard.fWordWrap != ulOldFlFormat)
  3147.                 FormatText2Screen(hwndTextView,
  3148.                                   ptxvd,
  3149.                                   FALSE,
  3150.                                   FALSE);       // quick format
  3151.         }
  3152.         break;
  3153.  
  3154.         /*
  3155.          *@@ TXM_QUERYCDATA:
  3156.          *      copies the current XTEXTVIEWCDATA
  3157.          *      into the specified buffer.
  3158.          *
  3159.          *      This must be sent, not posted, to the control.
  3160.          *
  3161.          *      Parameters:
  3162.          *
  3163.          *      --  PXTEXTVIEWCDATA mp1: target buffer.
  3164.          *          Before calling this, you MUST specify
  3165.          *          XTEXTVIEWCDATA.cbData.
  3166.          *
  3167.          *      Returns: the bytes that were copied as
  3168.          *      a ULONG.
  3169.          */
  3170.  
  3171.         case TXM_QUERYCDATA:
  3172.             if (mp1)
  3173.             {
  3174.                 PXTEXTVIEWCDATA pTarget = (PXTEXTVIEWCDATA)mp1;
  3175.                 mrc = (MRESULT)min(pTarget->cbData, sizeof(XTEXTVIEWCDATA));
  3176.                 memcpy(pTarget,
  3177.                        &ptxvd->cdata,
  3178.                        (ULONG)mrc);
  3179.             }
  3180.         break;
  3181.  
  3182.         /*
  3183.          *@@ TXM_SETCDATA:
  3184.          *      updates the current XTEXTVIEWCDATA
  3185.          *      with the data from the specified buffer.
  3186.          *
  3187.          *      This must be sent, not posted, to the control.
  3188.          *
  3189.          *      Parameters:
  3190.          *      -- PXTEXTVIEWCDATA mp1: source buffer.
  3191.          *         Before calling this, you MUST specify
  3192.          *         XTEXTVIEWCDATA.cbData.
  3193.          */
  3194.  
  3195.         case TXM_SETCDATA:
  3196.             if (mp1)
  3197.             {
  3198.                 PXTEXTVIEWCDATA pSource = (PXTEXTVIEWCDATA)mp1;
  3199.                 memcpy(&ptxvd->cdata, pSource, pSource->cbData);
  3200.             }
  3201.         break;
  3202.  
  3203.         /*
  3204.          *@@ TXM_JUMPTOANCHORNAME:
  3205.          *      scrolls the XTextView control contents so that
  3206.          *      the text marked with the specified anchor name
  3207.          *      (TXVESC_ANCHORNAME escape) appears at the top
  3208.          *      of the control.
  3209.          *
  3210.          *      This must be sent, not posted, to the control.
  3211.          *
  3212.          *      Parameters:
  3213.          *      -- PSZ mp1: anchor name (e.g. "anchor1").
  3214.          *
  3215.          *@@added V0.9.4 (2000-06-12) [umoeller]
  3216.          */
  3217.  
  3218.         case TXM_JUMPTOANCHORNAME:
  3219.             if (mp1)
  3220.             {
  3221.                 PLISTNODE pWordNode = txvFindWordFromAnchor(&ptxvd->xfd,
  3222.                                                             (const char*)mp1);
  3223.                 if (pWordNode)
  3224.                 {
  3225.                     // found:
  3226.                     PTXVWORD pWord = (PTXVWORD)pWordNode->pItemData;
  3227.                     if (pWord)
  3228.                     {
  3229.                         PTXVRECTANGLE pRect = pWord->pRectangle;
  3230.                         ULONG ulWinCY = (ptxvd->rclViewText.yTop - ptxvd->rclViewText.yBottom);
  3231.  
  3232.                         // now we need to scroll the window so that this rectangle is on top.
  3233.                         // Since rectangles start out with the height of the window (e.g. +768)
  3234.                         // and then have lower y coordinates down to way in the negatives,
  3235.                         // to get the y offset, we must...
  3236.                         ptxvd->ulViewYOfs = (-pRect->rcl.yTop) - ulWinCY;
  3237.  
  3238.                         if (ptxvd->ulViewYOfs < 0)
  3239.                             ptxvd->ulViewYOfs = 0;
  3240.                         if (ptxvd->ulViewYOfs > ((LONG)ptxvd->xfd.szlWorkspace.cy - ulWinCY))
  3241.                             ptxvd->ulViewYOfs = (LONG)ptxvd->xfd.szlWorkspace.cy - ulWinCY;
  3242.  
  3243.                         // vertical scroll bar enabled at all?
  3244.                         if (ptxvd->flStyle & XS_VSCROLL)
  3245.                         {
  3246.                             /* BOOL fEnabled = */ winhUpdateScrollBar(ptxvd->hwndVScroll,
  3247.                                                                 ulWinCY,
  3248.                                                                 ptxvd->xfd.szlWorkspace.cy,
  3249.                                                                 ptxvd->ulViewYOfs,
  3250.                                                                 (ptxvd->flStyle & XS_AUTOVHIDE));
  3251.                             WinInvalidateRect(hwndTextView, NULL, FALSE);
  3252.                         }
  3253.                     }
  3254.                 }
  3255.             }
  3256.         break;
  3257.  
  3258.         /*
  3259.          *@@ TXM_QUERYTEXTEXTENT:
  3260.          *      returns the extents of the currently set text,
  3261.          *      that is, the width and height of the internal
  3262.          *      work area, of which the current view rectangle
  3263.          *      displays a subrectangle.
  3264.          *
  3265.          *      This must be sent, not posted, to the control.
  3266.          *
  3267.          *      Parameters:
  3268.          *
  3269.          *      --  PSIZEL mp1: pointer to a SIZEL buffer,
  3270.          *          which receives the extent in the cx and
  3271.          *          cy members. These will be set to null
  3272.          *          values if the control currently has no
  3273.          *          text.
  3274.          *
  3275.          *      Returns TRUE on success.
  3276.          *
  3277.          *@@added V0.9.20 (2002-08-10) [umoeller]
  3278.          */
  3279.  
  3280.         case TXM_QUERYTEXTEXTENT:
  3281.             if (mp1)
  3282.             {
  3283.                 memcpy((PSIZEL)mp1,
  3284.                        &ptxvd->xfd.szlWorkspace,
  3285.                        sizeof(SIZEL));
  3286.                 mrc = (MRESULT)TRUE;
  3287.             }
  3288.         break;
  3289.  
  3290.         /*
  3291.          * WM_DESTROY:
  3292.          *      clean up.
  3293.          */
  3294.  
  3295.         case WM_DESTROY:
  3296.             xstrClear(&ptxvd->xfd.strViewText);
  3297.             lstClear(&ptxvd->xfd.llRectangles);
  3298.             lstClear(&ptxvd->xfd.llWords);
  3299.             GpiDestroyPS(ptxvd->hps);
  3300.             free(ptxvd);
  3301.             mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
  3302.         break;
  3303.  
  3304.         default:
  3305.             mrc = WinDefWindowProc(hwndTextView, msg, mp1, mp2);
  3306.     }
  3307.  
  3308.     return mrc;
  3309. }
  3310.  
  3311. /*
  3312.  *@@ txvRegisterTextView:
  3313.  *      registers the Text View class with PM. Required
  3314.  *      before the text view control can be used.
  3315.  */
  3316.  
  3317. BOOL txvRegisterTextView(HAB hab)
  3318. {
  3319.     return WinRegisterClass(hab,
  3320.                             WC_XTEXTVIEW,
  3321.                             fnwpTextView,
  3322.                             0,
  3323.                             2 * sizeof(PVOID));     // QWL_USER and QWL_PRIVATE
  3324. }
  3325.  
  3326. /*
  3327.  *@@ txvReplaceWithTextView:
  3328.  *      replaces any window with a text view control.
  3329.  *      You must call txvRegisterTextView beforehand.
  3330.  *
  3331.  *@@added V0.9.1 (2000-02-13) [umoeller]
  3332.  */
  3333.  
  3334. HWND txvReplaceWithTextView(HWND hwndParentAndOwner,
  3335.                             USHORT usID,
  3336.                             ULONG flWinStyle,
  3337.                             USHORT usBorder)
  3338. {
  3339.     HWND hwndMLE = WinWindowFromID(hwndParentAndOwner, usID),
  3340.          hwndTextView = NULLHANDLE;
  3341.     if (hwndMLE)
  3342.     {
  3343.         ULONG           ul,
  3344.                         // attrFound,
  3345.                         abValue[32];
  3346.         SWP             swpMLE;
  3347.         XTEXTVIEWCDATA  xtxCData;
  3348.         PSZ             pszFont = winhQueryWindowFont(hwndMLE);
  3349.         LONG            lBackClr = -1,
  3350.                         lForeClr = -1;
  3351.  
  3352.         if ((ul = WinQueryPresParam(hwndMLE,
  3353.                                     PP_BACKGROUNDCOLOR,
  3354.                                     0,
  3355.                                     NULL,
  3356.                                     (ULONG)sizeof(abValue),
  3357.                                     (PVOID)&abValue,
  3358.                                     QPF_NOINHERIT)))
  3359.             lBackClr = abValue[0];
  3360.  
  3361.         if ((ul = WinQueryPresParam(hwndMLE,
  3362.                                     PP_FOREGROUNDCOLOR,
  3363.                                     0,
  3364.                                     NULL,
  3365.                                     (ULONG)sizeof(abValue),
  3366.                                     (PVOID)&abValue,
  3367.                                     QPF_NOINHERIT)))
  3368.             lForeClr = abValue[0];
  3369.  
  3370.         WinQueryWindowPos(hwndMLE, &swpMLE);
  3371.  
  3372.         WinDestroyWindow(hwndMLE);
  3373.         memset(&xtxCData, 0, sizeof(xtxCData));
  3374.         xtxCData.cbData = sizeof(xtxCData);
  3375.         xtxCData.ulXBorder = usBorder;
  3376.         xtxCData.ulYBorder = usBorder;
  3377.         hwndTextView = WinCreateWindow(hwndParentAndOwner,
  3378.                                        WC_XTEXTVIEW,
  3379.                                        "",
  3380.                                        flWinStyle,
  3381.                                        swpMLE.x,
  3382.                                        swpMLE.y,
  3383.                                        swpMLE.cx,
  3384.                                        swpMLE.cy,
  3385.                                        hwndParentAndOwner,
  3386.                                        HWND_TOP,
  3387.                                        usID,
  3388.                                        &xtxCData,
  3389.                                        0);
  3390.         if (pszFont)
  3391.         {
  3392.             winhSetWindowFont(hwndTextView, pszFont);
  3393.             free(pszFont);
  3394.         }
  3395.  
  3396.         if (lBackClr != -1)
  3397.             WinSetPresParam(hwndTextView,
  3398.                             PP_BACKGROUNDCOLOR,
  3399.                             sizeof(ULONG),
  3400.                             &lBackClr);
  3401.         if (lForeClr != -1)
  3402.             WinSetPresParam(hwndTextView,
  3403.                             PP_FOREGROUNDCOLOR,
  3404.                             sizeof(ULONG),
  3405.                             &lForeClr);
  3406.     }
  3407.     return hwndTextView;
  3408. }
  3409.  
  3410. /* ******************************************************************
  3411.  *
  3412.  *   Printer-dependent functions
  3413.  *
  3414.  ********************************************************************/
  3415.  
  3416. /*
  3417.  *@@ prthQueryQueues:
  3418.  *      returns a buffer containing all print queues
  3419.  *      on the system.
  3420.  *
  3421.  *      This is usually the first step before printing.
  3422.  *      After calling this function, show a dlg to the
  3423.  *      user, allow him to select the printer queue
  3424.  *      to be used. This can then be passed to
  3425.  *      prthCreatePrinterDC.
  3426.  *
  3427.  *      Use prthFreeBuf to free the returned buffer.
  3428.  */
  3429.  
  3430. static PRQINFO3* prthEnumQueues(PULONG pulReturned)    // out: no. of queues found
  3431. {
  3432.     SPLERR  rc;
  3433.     ULONG   cTotal;
  3434.     ULONG   cbNeeded = 0;
  3435.     PRQINFO3 *pprq3 = NULL;
  3436.  
  3437.     // count queues & get number of bytes needed for buffer
  3438.     rc = SplEnumQueue(NULL,     // default computer
  3439.                       3,        // detail level
  3440.                       NULL,     // pbuf
  3441.                       0L,       // cbBuf
  3442.                       pulReturned, // out: entries returned
  3443.                       &cTotal,  // out: total entries available
  3444.                       &cbNeeded,
  3445.                       NULL);    // reserved
  3446.  
  3447.     if (!rc && cbNeeded)
  3448.     {
  3449.         pprq3 = (PRQINFO3*)malloc(cbNeeded);
  3450.         if (pprq3)
  3451.         {
  3452.             // enum the queues
  3453.             rc = SplEnumQueue(NULL,
  3454.                               3,
  3455.                               pprq3,
  3456.                               cbNeeded,
  3457.                               pulReturned,
  3458.                               &cTotal,
  3459.                               &cbNeeded,
  3460.                               NULL);
  3461.         }
  3462.     }
  3463.  
  3464.     return pprq3;
  3465. }
  3466.  
  3467. /*
  3468.  *@@ prthFreeBuf:
  3469.  *
  3470.  */
  3471.  
  3472. static VOID prthFreeBuf(PVOID pprq3)
  3473. {
  3474.     if (pprq3)
  3475.         free(pprq3);
  3476. }
  3477.  
  3478. /*
  3479.  *@@ prthCreatePrinterDC:
  3480.  *      creates a device context for the printer
  3481.  *      specified by the given printer queue.
  3482.  *
  3483.  *      As a nifty feature, this returns printer
  3484.  *      device resolution automatically in the
  3485.  *      specified buffer.
  3486.  *
  3487.  *      Returns NULLHANDLE (== DEV_ERROR) on errors.
  3488.  *
  3489.  *      Use DevCloseDC to destroy the DC.
  3490.  *
  3491.  *      Based on print sample by Peter Fitzsimmons, Fri  95-09-29 02:47:16am.
  3492.  */
  3493.  
  3494. static HDC prthCreatePrinterDC(HAB hab,
  3495.                                PRQINFO3 *pprq3,
  3496.                                PLONG palRes)  // out: 2 longs holding horizontal and vertical
  3497.                                               // printer resolution in pels per inch
  3498. {
  3499.     HDC     hdc = NULLHANDLE;
  3500.     DEVOPENSTRUC dos;
  3501.     PSZ     p;
  3502.  
  3503.     memset(&dos, 0, sizeof(dos));
  3504.     p = strrchr(pprq3->pszDriverName, '.');
  3505.     if (p)
  3506.         *p = 0;                         // del everything after '.'
  3507.  
  3508.     dos.pszLogAddress = pprq3->pszName;
  3509.     dos.pszDriverName = pprq3->pszDriverName;
  3510.     dos.pdriv = pprq3->pDriverData;
  3511.     dos.pszDataType = "PM_Q_STD";
  3512.     hdc = DevOpenDC(hab,
  3513.                     OD_QUEUED,
  3514.                     "*",
  3515.                     4L,     // count of items in next param
  3516.                     (PDEVOPENDATA)&dos,
  3517.                     0);     // compatible DC
  3518.  
  3519.     if (hdc)
  3520.         DevQueryCaps(hdc,
  3521.                      CAPS_HORIZONTAL_FONT_RES,
  3522.                      2,
  3523.                      palRes);   // buffer
  3524.  
  3525.     return hdc;
  3526. }
  3527.  
  3528. /*
  3529.  *@@ prthQueryForms:
  3530.  *      returns a buffer containing all forms
  3531.  *      supported by the specified printer DC.
  3532.  *
  3533.  *      Use prthFreeBuf to free the returned
  3534.  *      buffer.
  3535.  *
  3536.  *      HCINFO uses different model spaces for
  3537.  *      the returned info. See PMREF for details.
  3538.  */
  3539.  
  3540. static HCINFO* prthQueryForms(HDC hdc,
  3541.                               PULONG pulCount)
  3542. {
  3543.     HCINFO  *pahci = NULL;
  3544.  
  3545.     LONG    cForms;
  3546.  
  3547.     // get form count
  3548.     cForms = DevQueryHardcopyCaps(hdc, 0L, 0L, NULL); // phci);
  3549.     if (cForms)
  3550.     {
  3551.         pahci = (HCINFO*)malloc(cForms * sizeof(HCINFO));
  3552.         if (pahci)
  3553.         {
  3554.             *pulCount = DevQueryHardcopyCaps(hdc, 0, cForms, pahci);
  3555.         }
  3556.     }
  3557.  
  3558.     return pahci;
  3559. }
  3560.  
  3561. /*
  3562.  *@@ prthCreatePS:
  3563.  *      creates a "normal" presentation space from the specified
  3564.  *      printer device context (which can be opened thru
  3565.  *      prthCreatePrinterDC).
  3566.  *
  3567.  *      Returns NULLHANDLE on errors.
  3568.  *
  3569.  *      Based on print sample by Peter Fitzsimmons, Fri  95-09-29 02:47:16am.
  3570.  */
  3571.  
  3572. static HPS prthCreatePS(HAB hab,       // in: anchor block
  3573.                         HDC hdc,       // in: printer device context
  3574.                         ULONG ulUnits) // in: one of:
  3575.                                        // -- PU_PELS
  3576.                                        // -- PU_LOMETRIC
  3577.                                        // -- PU_HIMETRIC
  3578.                                        // -- PU_LOENGLISH
  3579.                                        // -- PU_HIENGLISH
  3580.                                        // -- PU_TWIPS
  3581. {
  3582.     SIZEL   sizel;
  3583.  
  3584.     sizel.cx = 0;
  3585.     sizel.cy = 0;
  3586.     return GpiCreatePS(hab,
  3587.                        hdc,
  3588.                        &sizel,
  3589.                        ulUnits | GPIA_ASSOC | GPIT_NORMAL);
  3590. }
  3591.  
  3592. /*
  3593.  *@@ prthStartDoc:
  3594.  *      calls DevEscape with DEVESC_STARTDOC.
  3595.  *      This must be called before any painting
  3596.  *      into the HDC's HPS. Any GPI calls made
  3597.  *      before this are ignored.
  3598.  *
  3599.  *      pszDocTitle appears in the spooler.
  3600.  */
  3601.  
  3602. static VOID prthStartDoc(HDC hdc,
  3603.                          PSZ pszDocTitle)
  3604. {
  3605.     DevEscape(hdc,
  3606.               DEVESC_STARTDOC,
  3607.               strlen(pszDocTitle),
  3608.               pszDocTitle,
  3609.               0L,
  3610.               0L);
  3611. }
  3612.  
  3613. /*
  3614.  *@@ prthNextPage:
  3615.  *      calls DevEscape with DEVESC_NEWFRAME.
  3616.  *      Signals when an application has finished writing to a page and wants to
  3617.  *      start a new page. It is similar to GpiErase processing for a screen device
  3618.  *      context, and causes a reset of the attributes. This escape is used with a
  3619.  *      printer device to advance to a new page.
  3620.  */
  3621.  
  3622. static VOID prthNextPage(HDC hdc)
  3623. {
  3624.     DevEscape(hdc,
  3625.               DEVESC_NEWFRAME,
  3626.               0,
  3627.               0,
  3628.               0,
  3629.               0);
  3630. }
  3631.  
  3632. /*
  3633.  *@@ prthEndDoc:
  3634.  *      calls DevEscape with DEVESC_ENDDOC
  3635.  *      and disassociates the HPS from the HDC.
  3636.  *      Call this right before doing
  3637.  +          GpiDestroyPS(hps);
  3638.  +          DevCloseDC(hdc);
  3639.  */
  3640.  
  3641. static VOID prthEndDoc(HDC hdc,
  3642.                        HPS hps)
  3643. {
  3644.     DevEscape(hdc, DEVESC_ENDDOC, 0L, 0L, 0, NULL);
  3645.     GpiAssociate(hps, NULLHANDLE);
  3646. }
  3647.  
  3648. /*
  3649.  *@@ txvPrint:
  3650.  *      this does the actual printing.
  3651.  */
  3652.  
  3653. BOOL txvPrint(HAB hab,
  3654.               HDC hdc,          // in: printer device context
  3655.               HPS hps,          // in: printer presentation space (using PU_PELS)
  3656.               PSZ pszViewText,  // in: text to print
  3657.               ULONG ulSize,     // in: default font point size
  3658.               PSZ pszFaceName,  // in: default font face name
  3659.               HCINFO  *phci,    // in: hardcopy form to use
  3660.               PSZ pszDocTitle,  // in: document title (appears in spooler)
  3661.               FNPRINTCALLBACK *pfnCallback)
  3662. {
  3663.     RECTL       rclPageDevice,
  3664.                 rclPageWorld;
  3665.     XFORMATDATA xfd;
  3666.     BOOL        fAnotherPage = FALSE;
  3667.     ULONG       ulCurrentLineIndex = 0,
  3668.                 ulCurrentPage = 1;
  3669.     ULONG       ulCurrentYOfs = 0;
  3670.  
  3671.     /* MATRIXLF    matlf;
  3672.     POINTL      ptlCenter;
  3673.     FIXED       scalars[2]; */
  3674.  
  3675.     // important: we must do a STARTDOC before we use the printer HPS.
  3676.     prthStartDoc(hdc,
  3677.                  pszDocTitle);
  3678.  
  3679.     // the PS is in TWIPS, but our world coordinate
  3680.     // space is in pels, so we need to transform
  3681.     /* GpiQueryViewingTransformMatrix(hps,
  3682.                                    1L,
  3683.                                    &matlf);
  3684.     ptlCenter.x = 0;
  3685.     ptlCenter.y = 0;
  3686.     scalars[0] =  MAKEFIXED(2,0);
  3687.     scalars[1] =  MAKEFIXED(3,0);
  3688.  
  3689.     GpiScale (hps,
  3690.               &matlf,
  3691.               TRANSFORM_REPLACE,
  3692.               scalars,
  3693.               &ptlCenter); */
  3694.  
  3695.     // initialize format with font from window
  3696.     txvInitFormat(&xfd);
  3697.  
  3698.     /* SetFormatFont(hps,
  3699.                      &xfd,
  3700.                      ulSize,
  3701.                      pszFaceName); */
  3702.  
  3703.     // use text from window
  3704.     xstrcpy(&xfd.strViewText, pszViewText, 0);
  3705.  
  3706.     // setup page
  3707.     GpiQueryPageViewport(hps,
  3708.                          &rclPageDevice);
  3709.     // this is in device units; convert this
  3710.     // to the world coordinate space of the printer PS
  3711.     memcpy(&rclPageWorld, &rclPageDevice, sizeof(RECTL));
  3712.     GpiConvert(hps,
  3713.                CVTC_DEVICE,     // source
  3714.                CVTC_WORLD,
  3715.                2,               // 2 points, it's a rectangle
  3716.                (PPOINTL)&rclPageWorld);
  3717.  
  3718.     // left and bottom margins are in millimeters...
  3719.     /* rclPage.xLeft = 100;        // ###
  3720.     rclPage.yBottom = 100;
  3721.     rclPage.xRight = rclPage.xLeft + phci->xPels;
  3722.     rclPage.yTop = rclPage.yBottom + phci->yPels; */
  3723.  
  3724.     txvFormatText(hps,
  3725.                   &xfd,          // in: ptxvd->rclViewText
  3726.                   &rclPageWorld,
  3727.                   TRUE);
  3728.  
  3729.     do
  3730.     {
  3731.         _Pmpf(("---- printing page %d",
  3732.                 ulCurrentPage));
  3733.  
  3734.         fAnotherPage = txvPaintText(hab,
  3735.                                     hps,
  3736.                                     &xfd,
  3737.                                     &rclPageWorld,
  3738.                                     0,
  3739.                                     &ulCurrentYOfs,
  3740.                                     FALSE,      // draw only fully visible lines
  3741.                                     &ulCurrentLineIndex); // in/out: line to start with
  3742.         if (fAnotherPage)
  3743.         {
  3744.             prthNextPage(hdc);
  3745.  
  3746.             if (pfnCallback(ulCurrentPage++, 0) == FALSE)
  3747.                 fAnotherPage = FALSE;
  3748.         }
  3749.     } while (fAnotherPage);
  3750.  
  3751.     prthEndDoc(hdc, hps);
  3752.  
  3753.     return TRUE;
  3754. }
  3755.  
  3756. /*
  3757.  *@@ txvPrintWindow:
  3758.  *      one-shot function which prints the contents
  3759.  *      of the specified XTextView control to the
  3760.  *      default printer, using the default form.
  3761.  *
  3762.  *      Returns a nonzero value upon errors.
  3763.  *
  3764.  *      Based on print sample by Peter Fitzsimmons, Fri  95-09-29 02:47:16am.
  3765.  */
  3766.  
  3767. int txvPrintWindow(HWND hwndTextView,
  3768.                    PSZ pszDocTitle,  // in: document title (appears in spooler)
  3769.                    FNPRINTCALLBACK *pfnCallback)
  3770. {
  3771.     int     irc = 0;
  3772.  
  3773.     PTEXTVIEWWINDATA ptxvd = (PTEXTVIEWWINDATA)WinQueryWindowPtr(hwndTextView, QWL_PRIVATE);
  3774.  
  3775.     if (!ptxvd)
  3776.         irc = 1;
  3777.     else
  3778.     {
  3779.         ULONG       cReturned = 0;
  3780.         PRQINFO3    *pprq3 = prthEnumQueues(&cReturned);
  3781.         HDC         hdc = NULLHANDLE;
  3782.         LONG        caps[2];
  3783.  
  3784.         // find default queue
  3785.         if (pprq3)
  3786.         {
  3787.             ULONG   i;
  3788.             // search for default queue;
  3789.             for (i = 0; i < cReturned; i++)
  3790.                 if (pprq3[i].fsType & PRQ3_TYPE_APPDEFAULT)
  3791.                 {
  3792.                     hdc = prthCreatePrinterDC(ptxvd->hab,
  3793.                                               &pprq3[i],
  3794.                                               caps);
  3795.  
  3796.                     break;
  3797.                 }
  3798.             prthFreeBuf(pprq3);
  3799.         }
  3800.  
  3801.         if (!hdc)
  3802.             irc = 2;
  3803.         else
  3804.         {
  3805.             // OK, we got a printer DC:
  3806.             HPS     hps;
  3807.             ULONG   cForms = 0;
  3808.             HCINFO  *pahci,
  3809.                     *phciSelected = 0;
  3810.  
  3811.             // find default form
  3812.             pahci = prthQueryForms(hdc,
  3813.                                    &cForms);
  3814.             if (pahci)
  3815.             {
  3816.                 HCINFO  *phciThis = pahci;
  3817.                 ULONG   i;
  3818.                 for (i = 0;
  3819.                      i < cForms;
  3820.                      i++, phciThis++)
  3821.                 {
  3822.                     if (phciThis->flAttributes & HCAPS_CURRENT)
  3823.                     {
  3824.                         phciSelected = phciThis;
  3825.                     }
  3826.                 }
  3827.             }
  3828.  
  3829.             if (!phciSelected)
  3830.                 irc = 3;
  3831.             else
  3832.             {
  3833.                 // create printer PS
  3834.                 hps = prthCreatePS(ptxvd->hab,
  3835.                                    hdc,
  3836.                                    PU_PELS);
  3837.  
  3838.                 if (hps == GPI_ERROR)
  3839.                     irc = 4;
  3840.                 else
  3841.                 {
  3842.                     PSZ pszFont;
  3843.                     ULONG ulSize = 0;
  3844.                     PSZ pszFaceName = 0;
  3845.  
  3846.                     if ((pszFont = winhQueryWindowFont(hwndTextView)))
  3847.                         gpihSplitPresFont(pszFont,
  3848.                                           &ulSize,
  3849.                                           &pszFaceName);
  3850.                     txvPrint(ptxvd->hab,
  3851.                              hdc,
  3852.                              hps,
  3853.                              ptxvd->xfd.strViewText.psz,
  3854.                              ulSize,
  3855.                              pszFaceName,
  3856.                              phciSelected,
  3857.                              pszDocTitle,
  3858.                              pfnCallback);
  3859.  
  3860.                     if (pszFont)
  3861.                         free(pszFont);
  3862.  
  3863.                     GpiDestroyPS(hps);
  3864.                 }
  3865.             }
  3866.             DevCloseDC(hdc);
  3867.         }
  3868.     }
  3869.  
  3870.     return irc;
  3871. }
  3872.  
  3873.  
  3874.