home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / S12547.ZIP / TYPER.C < prev    next >
C/C++ Source or Header  |  1990-03-19  |  12KB  |  452 lines

  1. /*
  2.     TYPER.C -- based on the sample of the same name.
  3.  
  4.     This program correctly handles DBCS, and also demonstrates word
  5.     wrapping.  It uses a circular buffer to store lines.
  6.  
  7.     Limitations:  This program does not recompute word breaks on
  8.     WM_SIZE messages.
  9. */
  10. #define    INCL_DOSNLS
  11. #define INCL_PM
  12. #define    INCL_NLS
  13. #include <os2.h>
  14. #include <string.h>
  15. #include "typer.h"
  16.  
  17. //  Variable declarations
  18. //
  19. UCHAR    vaszBuffer[MAX_LINES][MAX_LINE_LENGTH + 1];
  20.                     // Circular queue of lines
  21. USHORT    vcchLine = 0;            // Number of characters in current line
  22. SHORT    vcpelyChar = 0;         // Character height
  23. BOOL    vfIsLeadByte[256];        // Is Byte i a DBCS lead byte?
  24. HAB     vhab;                // Handle to the Anchor Block
  25. HWND    vhwndTyper;            // Handle to the Client Area
  26. HWND    vhwndTyperFrame;        // Handle to the Frame Window
  27. USHORT    vusCurrent = 0;            // Current vaszBuffer line referenced
  28.  
  29. //  Useful macros
  30. //
  31. #define    LOADSTRING(id, sz) \
  32.     WinLoadString(vhab, NULL, id, sizeof(sz), (PCH) sz)
  33.  
  34. //  Internal declarations
  35. //
  36. VOID    main(VOID);            // Main routine
  37. VOID    InitLeadByteTable(VOID);    // Initialize the lead byte table
  38. VOID    TyperChar(HWND, MPARAM, MPARAM);// WM_CHAR processing subroutine
  39. VOID    TyperCreate(HWND);         // WM_CREATE processing subroutine
  40. VOID    TyperPaint(HWND, HPS, PRECTL);    // WM_PAINT processing subroutine
  41.  
  42. VOID main(VOID) {
  43.     HMQ        hmqTyper;
  44.     QMSG    qmsg;
  45.     UCHAR    szClassName[MAX_STRING];
  46.     UCHAR    szWindowTitle[MAX_STRING];
  47.     ULONG    ctlData;
  48.  
  49.     // Standard initialization for a PM application
  50.     //
  51.     vhab     = WinInitialize(NULL);
  52.     hmqTyper = WinCreateMsgQueue(vhab, 0);
  53.  
  54.     // Register the Typer window class -- if not successful, exit.
  55.     //
  56.     LOADSTRING(IDS_NAME, szClassName);
  57.     if (!WinRegisterClass(vhab, (PCH) szClassName, (PFNWP)TyperWndProc,
  58.                            CS_SIZEREDRAW, 0))
  59.     DosExit(EXIT_PROCESS, 0);
  60.  
  61.     // Get the title of the application, and create the main window.
  62.     // Notice that FCF_DBE_APPSTAT is included because the client area
  63.     // will allow Kanji characters to be input.
  64.     //
  65.     LOADSTRING(IDS_TITLE, szWindowTitle);
  66.     ctlData = FCF_STANDARD & ~(FCF_ICON | FCF_ACCELTABLE) | FCF_DBE_APPSTAT;
  67.     vhwndTyperFrame = WinCreateStdWindow(HWND_DESKTOP, WS_VISIBLE,
  68.             &ctlData, (PCH)szClassName, (PCH)szWindowTitle,
  69.             0L, NULL, IDR_TYPER, (PHWND)&vhwndTyper);
  70.  
  71.     //    Initialize the lead byte table
  72.     //
  73.     InitLeadByteTable();
  74.  
  75.     // Poll messages from event queue
  76.     //
  77.     while (WinGetMsg(vhab, &qmsg, NULL, 0, 0))
  78.         WinDispatchMsg(vhab, &qmsg);
  79.  
  80.     // Standard cleanup code for PM applications
  81.     //
  82.     WinDestroyWindow(vhwndTyperFrame);
  83.     WinDestroyMsgQueue(hmqTyper);
  84.     WinTerminate(vhab);
  85.     DosExit(EXIT_PROCESS, 1);
  86. }
  87.  
  88. MRESULT EXPENTRY TyperWndProc(HWND hwnd, USHORT usMsg, MPARAM mp1, MPARAM mp2) {
  89. /*
  90.     Note:  This scrolling behavior may not be extensible if the characters
  91.        are to be output from top to bottom, right to left, as can be
  92.        seen in traditional Chinese and Japanese scripts.
  93. */
  94.     HPS        hps;
  95.     RECTL    rclPaint;
  96.     RECTL    rclWindow;
  97.  
  98.     switch (usMsg) {
  99.     case WM_CREATE:
  100.         TyperCreate(hwnd);
  101.         break;
  102.  
  103.     case WM_CLOSE:
  104.         WinPostMsg(hwnd, WM_QUIT, 0L, 0L);
  105.         break;
  106.  
  107.     case WM_ERASEBACKGROUND:
  108.  
  109.         // Erase the background if this message is received
  110.         //
  111.         return TRUE;
  112.         break;
  113.  
  114.     case WM_CHAR:
  115.  
  116.         if (!(SHORT1FROMMP(mp1) & KC_KEYUP)) {
  117.  
  118.         // Process the downstrokes entered.
  119.         //
  120.         TyperChar(hwnd, mp1, mp2);
  121.  
  122.         // Invalidate the updated line.
  123.         //
  124.         WinQueryWindowRect(hwnd, &rclWindow);
  125.         rclWindow.yTop = vcpelyChar - 1;
  126.         WinInvalidateRect(hwnd, &rclWindow, TRUE);
  127.         WinPostMsg(hwnd, WM_PAINT, 0L, 0L);
  128.         }
  129.         break;
  130.  
  131.     case WM_QUERYCONVERTPOS:
  132.         WinQueryWindowRect(hwnd, &rclWindow);
  133.  
  134.         hps = WinGetPS(hwnd);
  135.         WinDrawText(hps, 0xFFFF, vaszBuffer[vusCurrent],
  136.         &rclWindow, 0L, 0L, DT_LEFT | DT_BOTTOM | DT_QUERYEXTENT);
  137.         WinReleasePS(hps);
  138.  
  139.         ((PRECTL) mp1)->xLeft = rclWindow.xRight;
  140.         ((PRECTL) mp1)->yBottom = rclWindow.yBottom;
  141.  
  142.         return QCP_CONVERT;
  143.         break;
  144.  
  145.     case WM_PAINT:
  146.  
  147.         // Repaint the invalid region
  148.         // Side effect:  May scroll up one line as needed.
  149.         //
  150.         hps = WinBeginPaint(hwnd, NULL, &rclPaint);
  151.         TyperPaint(hwnd, hps, &rclPaint);
  152.         WinEndPaint(hps);
  153.             break;
  154.  
  155.     case WM_COMMAND:
  156.  
  157.         switch (COMMANDMSG(&usMsg)->cmd) {
  158.  
  159.         // Trap the About... menu item, and put up the dialog box
  160.         //
  161.         case IDM_ABOUT:
  162.             WinDlgBox(HWND_DESKTOP, hwnd, AboutDlgProc,
  163.               NULL, IDD_ABOUT, NULL);
  164.             break;
  165.  
  166.         default: break;
  167.         }
  168.         break;
  169.  
  170.     default:
  171.         return WinDefWindowProc(hwnd, usMsg, mp1, mp2);
  172.         break;
  173.     }
  174.     return 0L;
  175. }
  176.  
  177. MRESULT EXPENTRY AboutDlgProc(HWND hwndDlg,USHORT usMsg,MPARAM mp1,MPARAM mp2) {
  178. /*
  179.     About... dialog procedure
  180. */
  181.     switch(usMsg) {
  182.     case WM_COMMAND:
  183.         switch(COMMANDMSG(&usMsg)->cmd) {
  184.         case DID_OK:    WinDismissDlg(hwndDlg, TRUE);
  185.         default:    break;
  186.         }
  187.     default: return WinDefDlgProc(hwndDlg, usMsg, mp1, mp2);
  188.     }
  189.     return FALSE;
  190. }
  191.  
  192. VOID TyperChar(HWND hwnd, MPARAM mp1, MPARAM mp2) {
  193. /*
  194.     This routine does simple input processing.
  195.  
  196.     Assumptions:  That there are no DBCS deadkeys, or invalid composites.
  197.     This also implies that a deadkey followed by a DBCS space results in
  198.     a DBCS space (without WinAlarm() being called).
  199. */
  200.     BOOL    fDone;
  201.     PUCHAR    pszLast;
  202.     UCHAR    ch;
  203.     USHORT    fs;
  204.     USHORT    vkey;
  205.  
  206.     ch = (UCHAR) CHAR1FROMMP(mp2);
  207.     fs = (USHORT) SHORT1FROMMP(mp1);
  208.     vkey = (USHORT) SHORT2FROMMP(mp2);
  209.     fDone = FALSE;
  210.  
  211.     if (fs & KC_VIRTUALKEY) {
  212.  
  213.     // Receiving a backspace...
  214.     //
  215.     if ((fDone = (vkey == VK_BACKSPACE || vkey == VK_DELETE))
  216.         && vcchLine > 0) {
  217.  
  218.         pszLast = WinPrevChar(vhab, 0, 0,
  219.             &vaszBuffer[vusCurrent][0], &vaszBuffer[vusCurrent][vcchLine]);
  220.  
  221.         // If DBCS, zero out the trailing byte.
  222.         //
  223.         if (vfIsLeadByte[*pszLast]) {
  224.         vaszBuffer[vusCurrent][--vcchLine] = '\0';
  225.         }
  226.  
  227.         // Zero out one character (SBCS, or DBCS lead byte)
  228.         //
  229.         vaszBuffer[vusCurrent][--vcchLine] = '\0';
  230.  
  231.     // Receiving a carriage return (CR)...
  232.     //
  233.     } else if (vkey == VK_NEWLINE || vkey == VK_ENTER) {
  234.         fDone = TRUE;
  235.  
  236.         // Save the line in the vaszBuffer
  237.         //
  238.         vusCurrent = (vusCurrent + 1) % MAX_LINES;
  239.  
  240.         // Initialize the next text line
  241.         //
  242.         vcchLine = 0; vaszBuffer[vusCurrent][0] = '\0';
  243.  
  244.         // Scroll window upwards, for efficient updating.
  245.         //
  246.         WinScrollWindow(hwnd,0,vcpelyChar,NULL,NULL,NULL,NULL,SW_INVALIDATERGN);
  247.     }
  248.     }
  249.  
  250.     // If we haven't encountered VK_BACKSPACE, VK_DELETE, VK_NEWLINE, or
  251.     // VK_ENTER, process KC_CHAR values.  We do this because some valid
  252.     // characters are also valid vkeys (like Space).
  253.     //
  254.     if (!fDone) {
  255.     // If DBCS character fits, add it.
  256.     //
  257.     if (fs & KC_CHAR) {
  258.         if (vfIsLeadByte[ch] && (vcchLine + 2 < MAX_LINE_LENGTH)) {
  259.  
  260.         // Add DBCS to line array
  261.         //
  262.         vaszBuffer[vusCurrent][vcchLine++]    = ch;
  263.         vaszBuffer[vusCurrent][vcchLine++]    = CHAR2FROMMP(mp2);
  264.         vaszBuffer[vusCurrent][vcchLine]    = '\0';
  265.  
  266.         // If SBCS character fits, add it.
  267.         //
  268.         } else if (vcchLine + 1 < MAX_LINE_LENGTH) {
  269.  
  270.         if (fs & KC_INVALIDCOMP) {
  271.  
  272.             // If we have a space, advance over the current character.
  273.             //
  274.             if (ch == ' ')
  275.             vcchLine++;
  276.  
  277.             // Otherwise, complain audibly.
  278.             //
  279.             else
  280.             WinAlarm(HWND_DESKTOP, WA_WARNING);
  281.  
  282.         } else {
  283.  
  284.             // Add character to line array
  285.             //
  286.             vaszBuffer[vusCurrent][vcchLine++]    = ch;
  287.         }
  288.         vaszBuffer[vusCurrent][vcchLine]    = '\0';
  289.  
  290.         // If it's a deadkey, reposition so that we'll overwrite
  291.         // the deadkey on KC_INVALIDCOMP or KC_COMPOSITE.
  292.         //
  293.         if (fs & KC_DEADKEY)
  294.             vcchLine--;
  295.         }
  296.     }
  297.     }
  298. }
  299.  
  300. VOID TyperCreate(HWND hwnd) {
  301.     HPS        hps;
  302.     FONTMETRICS fmTyper;
  303.     USHORT    i;
  304.  
  305.     // Initialize text buffer
  306.     //
  307.     for (i = 0; i < MAX_LINES; i++)
  308.     vaszBuffer[i][0] = '\0';
  309.  
  310.     // Get the character height
  311.     //
  312.     hps = WinGetPS(hwnd);
  313.     GpiQueryFontMetrics(hps, (LONG) sizeof(FONTMETRICS), &fmTyper);
  314.     vcpelyChar = (SHORT) fmTyper.lMaxBaselineExt + 1;
  315.     WinReleasePS(hps);
  316. }
  317.  
  318. VOID TyperPaint(HWND hwnd, HPS hps, PRECTL prclUpdate) {
  319.     USHORT  usUpdateTop;
  320.     USHORT  usUpdateBottom;
  321.     RECTL   rclArea;
  322.     RECTL   rclWindow;
  323.     UCHAR   *pszTmp;
  324.     USHORT  cchDrawn;
  325.     USHORT  cchTmp;
  326.     USHORT  usWhich;
  327.     USHORT  usUpdate;
  328.     SHORT   sNew = -1;
  329.  
  330.     // Compute the lines to be updated.
  331.     // NOTE:  This assumes that screen coordinates start with y = 0.
  332.     //
  333.     usUpdateTop = (USHORT) (prclUpdate->yTop / vcpelyChar);
  334.     usUpdateBottom = (USHORT) (prclUpdate->yBottom / vcpelyChar);
  335.  
  336.     // The following code is functionally equivalent to
  337.     //     usWhich = (vusCurrent - usUpdateBottom) % MAX_LINES;
  338.     //
  339.     sNew = (vusCurrent - usUpdateBottom);
  340.     usWhich = ((sNew >= 0) ?
  341.         sNew % MAX_LINES : 
  342.         MAX_LINES - (-sNew % MAX_LINES));
  343.  
  344.     // Initialize the width of the text line
  345.     //
  346.     WinQueryWindowRect(hwnd, &rclWindow);
  347.     rclArea.xLeft  = rclWindow.xLeft;
  348.     rclArea.xRight = rclWindow.xRight;
  349.  
  350.     // For all the relevant lines...
  351.     //
  352.     for (usUpdate = usUpdateBottom;
  353.         usUpdate <= usUpdateTop;
  354.         usUpdate++) {
  355.  
  356.     // Initialize the height of the text line
  357.     //
  358.     rclArea.yBottom     = vcpelyChar * usUpdate;
  359.     rclArea.yTop        = rclArea.yBottom + (vcpelyChar - 1);
  360.  
  361.     // Load the line to be printed
  362.     //
  363.     pszTmp = vaszBuffer[usWhich];
  364.     cchTmp = strlen(pszTmp);
  365.  
  366.     // Always draw in a word wrapped fashion
  367.     //
  368.     if (cchTmp) {
  369.         cchDrawn = WinDrawText(hps, cchTmp, pszTmp, &rclArea,
  370.             CLR_BLACK, CLR_BACKGROUND,
  371.             DT_ERASERECT | DT_LEFT | DT_TOP | DT_WORDBREAK);
  372.         if (usWhich == vusCurrent) {
  373.         while (cchDrawn && cchDrawn < cchTmp) {
  374.  
  375.             // Fix up code, line's longer than expected!
  376.             // We must perform additional word wrapping.
  377.             //
  378.             // Add a new line to the line buffer (this
  379.             // code assumes that we must be on the last
  380.             // line to wrap.
  381.             //
  382.             sNew = (vusCurrent + 1) % MAX_LINES;
  383.             vaszBuffer[sNew][0] = '\0';
  384.             strcpy(vaszBuffer[sNew], vaszBuffer[vusCurrent] + cchDrawn);
  385.  
  386.             // Adjust old line to reflect character deletion
  387.             //
  388.             vaszBuffer[vusCurrent][cchDrawn] = '\0';
  389.  
  390.             // Set up new parameters for TyperChar()
  391.             //
  392.             vcchLine -= cchDrawn;
  393.             vusCurrent = sNew;
  394.  
  395.             // Scroll the window, then fill in the invalid region
  396.             //
  397.             WinScrollWindow(hwnd, 0, vcpelyChar,
  398.                     NULL, NULL, NULL, &rclArea, 0);
  399.  
  400.             pszTmp = vaszBuffer[vusCurrent];
  401.             cchTmp -= cchDrawn;
  402.             cchDrawn = WinDrawText(hps, cchTmp, pszTmp, &rclArea,
  403.                 CLR_BLACK, CLR_BACKGROUND,
  404.                 DT_ERASERECT | DT_LEFT | DT_TOP | DT_WORDBREAK);
  405.  
  406.             // We increment the update pointer because there's one
  407.             // fewer line to update.
  408.             usUpdate++;
  409.         }
  410.         }
  411.     } else {
  412.         // Clear the rectangle
  413.         //
  414.         WinFillRect(hps, &rclArea, CLR_BACKGROUND);
  415.     }
  416.     // Point to the next line to be drawn
  417.     // This is functionally equivalent to
  418.     //    usWhich = (usWhich - 1) % MAX_LINES;
  419.     //
  420.     usWhich = (usWhich ? (usWhich - 1) : MAX_LINES - 1);
  421.     }
  422. }
  423.  
  424. VOID InitLeadByteTable(VOID) {
  425. /*
  426.    This routine initializes the array which tells if index "i" is
  427.    a valid leading byte in the current codepage.
  428. */
  429.     COUNTRYCODE ctryc;                // Used with DosGetDBCSEv() call
  430.     UCHAR    vachDBCSEv[TABLE_SIZE];     // Lead Byte range table
  431.     SHORT    i, j;                // Temporary variables
  432.  
  433.     // Initialize the array
  434.     //
  435.     for (i = 0; i <= 0xFF; i++)
  436.     vfIsLeadByte[i] = FALSE;
  437.  
  438.     // Get the valid lead byte ranges
  439.     // Use (country, codepage) = (0, 0) for current process settings
  440.     //
  441.     ctryc.country = ctryc.codepage = 0;
  442.     DosGetDBCSEv(TABLE_SIZE, &ctryc, vachDBCSEv);
  443.  
  444.     // Fill in the array, "blacking out" all the returned ranges
  445.     //
  446.     while (vachDBCSEv[j] && vachDBCSEv[j + 1]) {
  447.     for (i = vachDBCSEv[j]; i <= vachDBCSEv[j + 1]; i++)
  448.         vfIsLeadByte[i] = TRUE;
  449.     j += 2;
  450.     }
  451. }
  452.