home *** CD-ROM | disk | FTP | other *** search
/ Languages Around the World / LanguageWorld.iso / language / japanese / win_prog / win_jwp / convert.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-31  |  54.2 KB  |  1,897 lines

  1. /* Copyright (C) Stephen Chung, 1991-1993.  All rights reserved. */
  2.  
  3. /* This distribution of JWP makes use of the Kana-to-Kanji conversion  */
  4. /* dictionary of the Kyoto University "Wnn" project.  The original wnn */
  5. /* copyright follows...                                                */
  6.  
  7. /*
  8.  * Copyright Kyoto University Research Institute for Mathematical Sciences
  9.  *                 1987, 1988, 1989, 1990, 1991
  10.  * Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991
  11.  * Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991
  12.  *
  13.  * Permission to use, copy, modify, distribute, and sell this software
  14.  * and its documentation for any purpose is hereby granted without fee,
  15.  * provided that all of the following conditions are satisfied:
  16.  *
  17.  * 1) The above copyright notices appear in all copies
  18.  * 2) Both those copyright notices and this permission notice appear
  19.  *    in supporting documentation
  20.  * 3) The name of "Wnn" isn't changed unless substantial modifications
  21.  *    are made, or
  22.  * 3') Following words followed by the above copyright notices appear
  23.  *    in all supporting documentation of software based on "Wnn":
  24.  *
  25.  *   "This software is based on the original version of Wnn developed by
  26.  *    Kyoto University Research Institute for Mathematical Sciences (KURIMS),
  27.  *    OMRON Corporation and ASTEC Inc."
  28.  *
  29.  * 4) The names KURIMS, OMRON and ASTEC not be used in advertising or
  30.  *    publicity pertaining to distribution of the software without
  31.  *    specific, written prior permission
  32.  *
  33.  * KURIMS, OMRON and ASTEC make no representations about the suitability
  34.  * of this software for any purpose.  It is provided "as is" without
  35.  * express or implied warranty.
  36.  *
  37.  * Wnn consortium is one of distributors of the official Wnn source code
  38.  * release.  Wnn consortium also makes no representations about the
  39.  * suitability of this software for any purpose.  It is provided "as is"
  40.  * without express or implied warranty.
  41.  *
  42.  * KURIMS, OMRON, ASTEC AND WNN CONSORTIUM DISCLAIM ALL WARRANTIES WITH
  43.  * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  44.  * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL KURIMS, OMRON, ASTEC OR
  45.  * WNN CONSORTIUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  46.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  47.  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  48.  * TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  49.  * PERFORMANCE OF THIS SOFTWARE.
  50.  *
  51.  */
  52.  
  53. #include "jwp.h"
  54.  
  55.  
  56. #define INDEXLEVEL      3
  57. #define SEPARATION      (SYSFONT->width / 2)
  58. #define CACHESIZE       (5L * 1024L)
  59.  
  60. typedef struct {
  61.     BYTE key[INDEXLEVEL+1];
  62.     LONG int offset;
  63. } INDEX;
  64.  
  65. typedef struct {
  66.     BYTE keylen;
  67.     BYTE choicelen;
  68.     union {
  69.         BYTE string[4];
  70.         BYTE far *ptr;
  71.     } key;
  72.     union {
  73.         KANJI string[2];
  74.         KANJI far *ptr;
  75.     } choice;
  76. } CHOICE;
  77.  
  78.  
  79.  
  80. static OFSTRUCT dctof;
  81.  
  82. static BYTE far *index = NULL;
  83. static LONG int far *offset = NULL;
  84. static int nr_index = 0;
  85. static int words[BUFSIZE];
  86.  
  87. static CHOICE far *ConvCache = NULL;
  88. static int ConvCacheSize = 0;
  89. static long int ConvRequests = 0L, ConvHits = 0L, ConvUsage = 0L, ConvChoiceChanges = 0L;
  90.  
  91. static BOOL sizing = FALSE;
  92.  
  93. KANJI kanji_list[MAXLINELEN];
  94.  
  95. typedef struct KanjiListStruct {
  96.     KANJI far *kanji;
  97.     struct KanjiListStruct far *prev, far *next;
  98. } KANJILIST;
  99.  
  100. typedef struct UserConvListStruct {
  101.     BYTE far *kana;
  102.     KANJILIST far *list;
  103.     struct UserConvListStruct far *prev, far *next;
  104. } USERCONV;
  105.  
  106. static USERCONV far *UserConversions = NULL;
  107. static USERCONV far *CurrentConv = NULL;
  108. static KANJILIST far *CurrentKanji = NULL;
  109. static BOOL UserConvChanged = FALSE;
  110. static BOOL AfterHitTest = FALSE;
  111.  
  112.  
  113.  
  114. int bytelen (BYTE far *p)
  115. {
  116.     int i;
  117.  
  118.     for (i = 0; *p; i++, p++);
  119.  
  120.     return (i);
  121. }
  122.  
  123.  
  124.  
  125. int bytecmp (BYTE far *p1, BYTE far *p2)
  126. {
  127.     for (; *p1 == *p2; p1++, p2++) {
  128.         if (*p1 == 0) return (0);
  129.     }
  130.  
  131.     return (*p1 - *p2);
  132. }
  133.  
  134.  
  135. int bytencmp (BYTE far *p1, BYTE far *p2, int n)
  136. {
  137.     int i;
  138.  
  139.     for (i = 0; i < n; i++) {
  140.         if (p1[i] != p2[i]) return (p1[i] - p2[i]);
  141.         if (p1[i] == 0) return (0);
  142.     }
  143.  
  144.     return (0);
  145. }
  146.  
  147.  
  148. KANJI far *kanjicpy (KANJI far *p1, KANJI far *p2)
  149. {
  150.     while (*p1++ = *p2++);
  151.  
  152.     return (p1);
  153. }
  154.  
  155.  
  156.  
  157. int kanjicmp (KANJI far *p1, KANJI far *p2)
  158. {
  159.     for (; *p1 == *p2; p1++, p2++) {
  160.         if (*p1 == 0) return (0);
  161.     }
  162.  
  163.     return (*p1 - *p2);
  164. }
  165.  
  166.  
  167. int kanjincmp (KANJI far *p1, KANJI far *p2, int n)
  168. {
  169.     int i;
  170.  
  171.     for (i = 0; i < n; i++) {
  172.         if (p1[i] != p2[i]) return (p1[i] - p2[i]);
  173.         if (p1[i] == 0) return (0);
  174.     }
  175.  
  176.     return (0);
  177. }
  178.  
  179.  
  180. int kanjilen (KANJI far *s)
  181. {
  182.     int i;
  183.  
  184.     for (i = 0; *s; s++, i++);
  185.     return (i);
  186. }
  187.  
  188.  
  189. int unitlen (UNIT far *s)
  190. {
  191.     int i;
  192.  
  193.     for (i = 0; s->kanji; s++, i++);
  194.     return (i);
  195. }
  196.  
  197.  
  198. KANJI far *kanjicat (KANJI far *d, KANJI far *s)
  199. {
  200.     kanjicpy(d + kanjilen(d), s);
  201.     return (d);
  202. }
  203.  
  204.  
  205. static int ConvHash (BYTE far *key)
  206. {
  207.     int i;
  208.     long int sum;
  209.  
  210.     for (i = 0, sum = 0L; key[i]; i++) sum += key[i];
  211.  
  212.     return (sum % ConvCacheSize);
  213. }
  214.  
  215.  
  216.  
  217. static int indexcmp (BYTE far *p1, BYTE far *p2)
  218. {
  219.     int i;
  220.  
  221.     for (i = 0; i < INDEXLEVEL; i++) {
  222.         if (p1[i] != p2[i]) return (p1[i] - p2[i]);
  223.         if (p1[i] == '\0') return (0);
  224.     }
  225.  
  226.     return (0);
  227. }
  228.  
  229.  
  230.  
  231. static void ReadUserConversions (char *fname)
  232. {
  233.     int fd;
  234.     int i, j, nr_conv, nr_list, n;
  235.     OFSTRUCT of;
  236.     USERCONV far *up;
  237.     KANJILIST far *kp;
  238.  
  239.     fd = OpenFile(fname, &of, OF_READ);
  240.     if (fd < 0) return;
  241.  
  242.     lseek(fd, 0L, 0);
  243.  
  244.     read(fd, &nr_conv, sizeof(int));
  245.  
  246.     for (i = 0; i < nr_conv; i++) {
  247.         if (UserConversions == NULL) {
  248.             UserConversions = up = StructAlloc(USERCONV);
  249.             up->prev = up->next = NULL;
  250.         } else {
  251.             up->next = StructAlloc(USERCONV);
  252.             up->next->prev = up;
  253.             up = up->next;
  254.             up->next = NULL;
  255.         }
  256.         up->list = NULL;
  257.  
  258.         read (fd, &n, sizeof(int));
  259.         up->kana = BlockAlloc(n);
  260.         _lread(fd, up->kana, n);
  261.  
  262.         read (fd, &nr_list, sizeof(int));
  263.  
  264.         for (j = 0; j < nr_list; j++) {
  265.             if (up->list == NULL) {
  266.                 up->list = kp = StructAlloc(KANJILIST);
  267.                 kp->prev = kp->next = NULL;
  268.             } else {
  269.                 kp->next = StructAlloc(KANJILIST);
  270.                 kp->next->prev = kp;
  271.                 kp = kp->next;
  272.                 kp->next = NULL;
  273.             }
  274.  
  275.             read (fd, &n, sizeof(int));
  276.             kp->kanji = BlockAlloc(n * sizeof(KANJI));
  277.             _lread(fd, kp->kanji, n * sizeof(KANJI));
  278.         }
  279.     }
  280.  
  281.     close(fd);
  282. }
  283.  
  284.  
  285.  
  286. void WriteUserConversions (char *fname)
  287. {
  288.     int fd, n;
  289.     OFSTRUCT of;
  290.     USERCONV far *up;
  291.     KANJILIST far *kp;
  292.  
  293.     if (!UserConvChanged) return;
  294.  
  295.     fd = OpenFile(fname, &of, OF_CREATE | OF_WRITE);
  296.     if (fd < 0) {
  297.         ErrorMessage(global.hwnd, "WARNING: Cannot write User Conversion Dictionary '%s'!", fname);
  298.         return;
  299.     }
  300.  
  301.     lseek(fd, 0L, 0);
  302.  
  303.     /* How many conversions? */
  304.  
  305.     for (up = UserConversions, n = 0; up != NULL; up = up->next, n++);
  306.     write(fd, &n, sizeof(int));
  307.  
  308.     for (up = UserConversions; up != NULL; up = up->next) {
  309.         n = bytelen(up->kana) + 1;
  310.         write(fd, &n, sizeof(int));
  311.         _lwrite(fd, up->kana, n);
  312.  
  313.         /* How many kanji's? */
  314.  
  315.         for (kp = up->list, n = 0; kp != NULL; kp = kp->next, n++);
  316.         write(fd, &n, sizeof(int));
  317.  
  318.         for (kp = up->list; kp != NULL; kp = kp->next) {
  319.             n = kanjilen(kp->kanji) + 1;
  320.             write(fd, &n, sizeof(int));
  321.             _lwrite(fd, kp->kanji, n * sizeof(KANJI));
  322.         }
  323.     }
  324.  
  325.     close(fd);
  326. }
  327.  
  328.  
  329.  
  330. int InitConversion (void)
  331. {
  332.     int fd;
  333.     int bytes, n, blocks;
  334.     unsigned int i, j;
  335.     BYTE far *cp;
  336.     INDEX far *ip;
  337.     OFSTRUCT idxof;
  338.  
  339.  
  340.     kanji_list[0] = 0;
  341.  
  342.     fd = OpenFile (global.userdict, &idxof, OF_READ);
  343.     if (fd >= 0) {
  344.         close(fd);
  345.         ReadUserConversions(global.userdict);
  346.     }
  347.  
  348.     for (;;) {
  349.         fd = OpenFile (global.convdict, &dctof, OF_READ);
  350.         if (fd >= 0) break;
  351.         if (!RetryMessage ("Cannot open dictionary '%s'!", global.convdict)) {
  352.             ErrorMessage(global.hwnd, PROGNAME " cannot continue.");
  353.             exit (-1);
  354.         }
  355.     }
  356.     close(fd);
  357.  
  358.     for (;;) {
  359.         fd = OpenFile (global.convidx, &idxof, OF_READ);
  360.         if (fd >= 0) break;
  361.         if (!RetryMessage ("Cannt open index file '%s'!", global.convidx)) {
  362.             ErrorMessage(global.hwnd, PROGNAME " cannot continue.");
  363.             exit (-1);
  364.         }
  365.     }
  366.  
  367.     lseek(fd, 0L, 0);
  368.  
  369.     cp = index = (BYTE far *) BlockAlloc(C64K);
  370.     offset = (LONG int far *) BlockAlloc(C64K);
  371.  
  372.     nr_index = 0;
  373.  
  374.     /* Read the file in blocks of 32K */
  375.  
  376.     blocks = 32760 / sizeof(INDEX);     /* A little less than 32K */
  377.     bytes = blocks * sizeof(INDEX);
  378.     ip = (INDEX far *) BlockAlloc(bytes);
  379.  
  380.     for (;;) {
  381.         n = _lread(fd, (char far *) ip, bytes);
  382.         n /= sizeof(INDEX);
  383.         for (i = 0; i < n; i++) {
  384.             for (j = 0; j < INDEXLEVEL; j++) *cp++ = ip[i].key[j];
  385.             offset[nr_index++] = ip[i].offset;
  386.         }
  387.         if (n < blocks) break;
  388.     }
  389.  
  390.     FreeBlock(ip);
  391.  
  392.     close(fd);
  393.  
  394.     return (TRUE);
  395. }
  396.  
  397.  
  398.  
  399. BOOL MergeKanjiLists (KANJI *d, KANJI *s)
  400. {
  401.     int i, j, k, len;
  402.     BOOL diff;
  403.  
  404.     if (!d[0]) {
  405.         if (!s[0]) return (FALSE);
  406.  
  407.         kanjicpy(d, s);
  408.         return (TRUE);
  409.     }
  410.  
  411.     if (!s[0]) return (TRUE);
  412.  
  413.     len = kanjilen(s);
  414.     s[len++] = '/';
  415.     s[len] = 0;
  416.  
  417.     len = kanjilen(d);
  418.     d[len++] = '/';
  419.     d[len] = 0;
  420.  
  421.     for (i = 0; s[i]; i++) {
  422.         j = 0;
  423.  
  424.         for (; d[j]; j++) {
  425.             diff = TRUE;
  426.             for (k = 0; d[j+k] != '/' && d[j+k] == s[i+k]; k++);
  427.  
  428.             if (d[j+k] == s[i+k]) {
  429.                 diff = FALSE;
  430.                 break;
  431.             }
  432.             for (; d[j] != '/'; j++);
  433.         }
  434.  
  435.         if (diff) {
  436.             for (k = 0; s[i+k] && s[i+k] != '/'; k++) d[len++] = s[i+k];
  437.             d[len++] = '/';
  438.             d[len] = 0;
  439.         }
  440.  
  441.         for (; s[i] && s[i] != '/'; i++);
  442.  
  443.         if (!s[i]) break;
  444.     }
  445.  
  446.     len = kanjilen(d);
  447.     if (d[len-1] == '/') d[len-1] = 0;
  448.  
  449.     return (TRUE);
  450. }
  451.  
  452.  
  453.  
  454. static int BinarySearch (BYTE *key)
  455. {
  456.     unsigned int top, bottom, middle;
  457.     int diff;
  458.  
  459.     top = 0;
  460.     bottom = nr_index - 1;
  461.  
  462.     for (;;) {
  463.         middle = (top + bottom) / 2;
  464.  
  465.         diff = indexcmp(key, &(index[middle * (unsigned int) INDEXLEVEL]));
  466.  
  467.         if (diff == 0) return (middle);
  468.  
  469.         if (top >= bottom - 1) {
  470.             diff = indexcmp(key, &(index[bottom * (unsigned int) INDEXLEVEL]));
  471.             if (diff == 0) return (bottom);
  472.             break;
  473.         }
  474.  
  475.         if (diff > 0) top = middle;
  476.         else bottom = middle;
  477.     }
  478.  
  479.     return (top);
  480.  
  481. }
  482.  
  483.  
  484. static int SearchUserDictionary (USERCONV far *UserConv, BYTE *target, KANJI endkana, KANJI *out)
  485. {
  486.     int i, tlen, klen, olen;
  487.     char *cp, ending = '*', kending;
  488.     USERCONV far *up;
  489.     KANJILIST far *kp;
  490.     BOOL exact = FALSE, hope = FALSE;
  491.  
  492.     if (endkana != 0) {
  493.         cp = ReverseKana(endkana);
  494.         if (*cp == '-') cp++;
  495.         ending = *cp;
  496.     }
  497.  
  498.     tlen = bytelen(target);
  499.     olen = kanjilen(out);
  500.  
  501.     for (up = UserConv; up != NULL; up = up->next) {
  502.         klen = bytelen(up->kana);
  503.         if (!(up->kana[klen - 1] & 0x80)) {
  504.             kending = up->kana[klen - 1];
  505.             klen--;
  506.         } else {
  507.             kending = '*';
  508.         }
  509.  
  510.         if (klen == tlen) {
  511.             if (!bytencmp(up->kana, target, tlen) && ending == kending) {
  512.                 exact = TRUE;
  513.                 /* Put in the kanji strings */
  514.                 for (kp = up->list; kp != NULL; kp = kp->next) {
  515.                     if (olen > 0) out[olen++] = '/';
  516.                     for (i = 0; kp->kanji[i]; i++) out[olen++] = kp->kanji[i];
  517.                     if (endkana != 0) out[olen++] = endkana;
  518.                 }
  519.                 out[olen] = 0;
  520.             }
  521.         } else if (tlen < klen && ending == '*') {
  522.             if (!bytencmp(up->kana, target, tlen)) hope = TRUE;
  523.         }
  524.  
  525.         if (exact && hope) break;
  526.     }
  527.  
  528.     if (!exact) return (hope ? 1 : 0);
  529.     else return (hope ? 3 : 2);
  530. }
  531.  
  532.  
  533.  
  534. static int SearchStandardDictionary (FILE *fp, BYTE *target, KANJI endkana, KANJI *out)
  535. {
  536.     int i, j, diff;
  537.     int ReturnVal;
  538.     char ending = '*';
  539.     char *cp;
  540.     BOOL exact = FALSE;
  541.     BOOL hope = FALSE;
  542.  
  543.     BYTE kana[MAXLINELEN];
  544.     char dct_ending[2];
  545.     BYTE conv[MAXLINELEN];
  546.     BYTE buffer[MAXLINELEN];
  547.  
  548.     out[0] = 0;
  549.  
  550.     if (endkana != 0) {
  551.         cp = ReverseKana(endkana);
  552.         if (*cp == '-') cp++;
  553.         ending = *cp;
  554.     }
  555.  
  556.     for (;;) {
  557.         if (fgets(buffer, MAXLINELEN, fp) == NULL) break;
  558.  
  559.         kana[0] = dct_ending[0] = conv[0] = '\0';
  560.  
  561.         sscanf(buffer, "%s %s %s", kana, dct_ending, conv);
  562.  
  563.         diff = bytecmp (kana, target);
  564.  
  565.         if (diff > 0 && ending == '*') {
  566.             if (bytencmp (kana, target, bytelen(target)) == 0) hope = TRUE;
  567.         }
  568.  
  569.         if (diff > 0) break;
  570.  
  571.         if (ending != dct_ending[0]) continue;
  572.  
  573.         if (diff == 0) {
  574.             exact = TRUE;
  575.             for (i = j = 0; conv[j]; i++, j++) {
  576.                 if (conv[j] & 0x0080) {
  577.                     out[i] = (conv[j] << 8) | conv[j+1];
  578.                     j++;
  579.                 } else {
  580.                     if (endkana != 0 && conv[j] == '/') out[i++] = endkana;
  581.                     out[i] = conv[j];
  582.                 }
  583.             }
  584.  
  585.             if (endkana != 0) out[i++] = endkana;
  586.             out[i] = 0;
  587.  
  588.             if (ending != '*') break;
  589.         }
  590.     }
  591.  
  592.     for (i = 0; out[i]; i++) out[i] &= 0x7f7f;
  593.  
  594.     if (!exact) ReturnVal = (hope ? 1 : 0);
  595.     else ReturnVal = (hope ? 3 : 2);
  596.  
  597.  
  598.     /* Anything in the user dictionary? */
  599.  
  600.     i = SearchUserDictionary (UserConversions, target, endkana, out);
  601.  
  602.     return (i | ReturnVal);
  603. }
  604.  
  605.  
  606.  
  607. int FindConversion (KANJI *line, KANJI *match)
  608. {
  609.     int i, r, len;
  610.     int fd;
  611.     FILE *fp;
  612.     BYTE key[MAXLINELEN];
  613.     KANJI conv[MAXLINELEN];
  614.     KANJI ending;
  615.  
  616.  
  617.     match[0] = conv[0] = 0;
  618.  
  619.  
  620.     for (i = 0; line[i] != 0; i++) {
  621.         if (ISKANJI(line[i])) {
  622.             key[i] = LOBYTE(line[i]) | 0x0080;
  623.         } else {
  624.             key[i] = LOBYTE(line[i]) & 0x007f;
  625.         }
  626.     }
  627.     key[i] = '\0';
  628.  
  629.  
  630.     /* Find the conversion without ending */
  631.  
  632.     i = BinarySearch(key);
  633.     if (i < 0) return (0);
  634.  
  635.     fd = OpenFile(NULL, &(dctof), OF_READ | OF_REOPEN);
  636.  
  637.     fp = fdopen(fd, "r");
  638.     if (fp == NULL) return (0);
  639.  
  640.     fseek(fp, offset[i], 0);
  641.  
  642.     kanji_list[0] = 0;
  643.  
  644.     r = SearchStandardDictionary(fp, key, 0, match);
  645.  
  646.     len = kanjilen(line);
  647.  
  648.     if (len <= 1) {
  649.         fclose(fp);
  650.         return (r);
  651.     }
  652.  
  653.  
  654.     /* Find the conversion with ending */
  655.  
  656.     ending = line[len-1];
  657.  
  658.     key[len-1] = '\0';
  659.  
  660.     i = BinarySearch(key);
  661.     if (i < 0) {
  662.         fclose(fp);
  663.         return (r);
  664.     }
  665.  
  666.     fseek(fp, offset[i], 0);
  667.  
  668.     if (SearchStandardDictionary(fp, key, ending, conv)) {
  669.         MergeKanjiLists(match, conv);
  670.         r |= 0x02;
  671.     }
  672.  
  673.     fclose(fp);
  674.  
  675.     return (r);
  676. }
  677.  
  678.  
  679.  
  680. void PutIntoConvCache (BYTE far *key, KANJI far *choice)
  681. {
  682.     long int i, j, k, n;
  683.     BYTE far *bp;
  684.     KANJI far *kp;
  685.  
  686.  
  687.     /* Cache there? */
  688.  
  689.     if (ConvCache == NULL) {
  690.         ConvCache = (CHOICE far *) BlockAlloc(CACHESIZE);
  691.         ConvCacheSize = CACHESIZE / sizeof(CHOICE);
  692.         ConvRequests = ConvHits = ConvUsage = 0L;
  693.         for (i = 0; i < ConvCacheSize; i++)
  694.             ConvCache[i].keylen = ConvCache[i].choicelen = 0;
  695.     }
  696.  
  697.     n = ConvHash(key);
  698.  
  699.     ConvChoiceChanges++;
  700.  
  701.     /* In the cache already? */
  702.  
  703.     for (i = n; ;) {
  704.         j = ConvCache[i].keylen;
  705.         k = ConvCache[i].choicelen;
  706.  
  707.         if (j <= 0 || k <= 0) break;
  708.  
  709.         if (j > 4) bp = ConvCache[i].key.ptr; else bp = ConvCache[i].key.string;
  710.         //if (ConvHash(bp) != n) break;
  711.  
  712.         if (!bytencmp(bp, key, j)) {                     /* Found it! */
  713.             if (k > 2) FreeBlock(ConvCache[i].choice.ptr);
  714.             k = kanjilen(choice);
  715.  
  716.             if (k > 2) kp = ConvCache[i].choice.ptr = (KANJI far *) BlockAlloc(k * sizeof(KANJI));
  717.             else kp = ConvCache[i].choice.string;
  718.  
  719.             _fmemcpy(kp, choice, k * sizeof(KANJI));
  720.             ConvCache[i].choicelen = k;
  721.             return;
  722.         }
  723.         if (++i >= ConvCacheSize) i = 0;                    /* Wrap around */
  724.         if (i == n) break;                                  /* Complete cycle */
  725.     }
  726.  
  727.     /* Is the cache too full? */
  728.  
  729.     if (ConvUsage > (ConvCacheSize * 2 / 3)) {
  730.         /* Bump off something by random */
  731.         i = n;
  732.     }
  733.  
  734.     /* Put it in the cache */
  735.  
  736.     if (j > 4) FreeBlock(ConvCache[i].key.ptr);
  737.     j = bytelen(key);
  738.  
  739.     if (j > 4) bp = ConvCache[i].key.ptr = (BYTE far *) BlockAlloc(j);
  740.     else bp = ConvCache[i].key.string;
  741.  
  742.     _fmemcpy(bp, key, j);
  743.     ConvCache[i].keylen = j;
  744.  
  745.     if (k > 2) FreeBlock(ConvCache[i].choice.ptr);
  746.     k = kanjilen(choice);
  747.  
  748.     if (k > 2) kp = ConvCache[i].choice.ptr = (KANJI far *) BlockAlloc(k * sizeof(KANJI));
  749.     else kp = ConvCache[i].choice.string;
  750.  
  751.     _fmemcpy(kp, choice, k * sizeof(KANJI));
  752.     ConvCache[i].choicelen = k; //((k > 2) ? -1 : k);
  753.  
  754.     ConvUsage++;
  755. }
  756.  
  757.  
  758. int ConvCacheLookup (BYTE far *key, KANJI far *list)
  759. {
  760.     long int i, j, k, n;
  761.     BYTE far *bp;
  762.     KANJI far *kp;
  763.  
  764.  
  765.     if (ConvCache == NULL) return (-1);
  766.  
  767.     ConvRequests++;
  768.  
  769.     n = ConvHash(key);
  770.  
  771.     for (i = n; ;) {
  772.         j = ConvCache[i].keylen;
  773.         k = ConvCache[i].choicelen;
  774.  
  775.         if (j <= 0 || k <= 0) break;
  776.  
  777.         /* Found it! */
  778.         if (j > 4) bp = ConvCache[i].key.ptr; else bp = ConvCache[i].key.string;
  779.  
  780.         if (!bytencmp(bp, key, j)) {                     /* Found it! */
  781.             ConvHits++;
  782.  
  783.             /* Look it up in the list */
  784.  
  785.             if (k > 2) kp = ConvCache[i].choice.ptr; else kp = ConvCache[i].choice.string;
  786.  
  787.             for (i = j = 0; ; ) {
  788.                 if (!kanjincmp(kp, list + j, k) && (list[j + k] == '/' || list[j + k] == 0))
  789.                     return (i);
  790.  
  791.                 /* Skip to the next word */
  792.  
  793.                 for (; list[j] && list[j] != '/'; j++);
  794.                 if (!list[j]) return (-1);
  795.  
  796.                 i++; j++;
  797.             }
  798.         }
  799.         if (++i >= ConvCacheSize) i = 0;                    /* Wrap around */
  800.         if (i == n) break;                                  /* Complete cycle */
  801.     }
  802.  
  803.     return (-1);
  804. }
  805.  
  806.  
  807.  
  808. void WriteConversionCache (char *fname)
  809. {
  810.     int i, j, k;
  811.     int fd;
  812.     OFSTRUCT of;
  813.     KANJI far *kp;
  814.     BYTE far *bp;
  815.  
  816.     if (ConvChoiceChanges <= 0L) return;
  817.  
  818.     fd = OpenFile(fname, &of, OF_CREATE | OF_WRITE);
  819.     if (fd < 0) {
  820.         ErrorMessage(global.hwnd, "WARNING: Cannot write conversion cache '%s'!", fname);
  821.         return;
  822.     }
  823.  
  824.     lseek(fd, 0L, 0);
  825.  
  826.     for (i = 0; i < ConvCacheSize; i++) {
  827.         j = ConvCache[i].keylen;
  828.         k = ConvCache[i].choicelen;
  829.  
  830.         if (j <= 0 || k <= 0) continue;
  831.  
  832.         write(fd, &j, sizeof(int));
  833.         if (j > 4) bp = ConvCache[i].key.ptr; else bp = ConvCache[i].key.string;
  834.         _lwrite(fd, bp, j);
  835.  
  836.         write(fd, &k, sizeof(int));
  837.         if (k > 2) kp = ConvCache[i].choice.ptr; else kp = ConvCache[i].choice.string;
  838.         _lwrite(fd, kp, k * sizeof(KANJI));
  839.     }
  840.  
  841.     close(fd);
  842. }
  843.  
  844.  
  845.  
  846. void ReadConversionCache (char *fname)
  847. {
  848.     int i, fd;
  849.     OFSTRUCT of;
  850.     BYTE keybuf[BUFSIZE];
  851.     KANJI choicebuf[BUFSIZE];
  852.  
  853.     fd = OpenFile(fname, &of, OF_READ);
  854.     if (fd < 0) return;
  855.  
  856.     lseek(fd, 0L, 0);
  857.  
  858.     for (;;) {
  859.         if (read(fd, &i, sizeof(int)) != sizeof(int)) break;
  860.         if (read(fd, keybuf, i) != i) break;
  861.         keybuf[i] = 0;
  862.  
  863.         if (read(fd, &i, sizeof(int)) != sizeof(int)) break;
  864.         if (read(fd, choicebuf, i * sizeof(KANJI)) != i * sizeof(KANJI)) break;
  865.         choicebuf[i] = 0;
  866.  
  867.         PutIntoConvCache(keybuf, choicebuf);
  868.     }
  869.  
  870.     close(fd);
  871. }
  872.  
  873.  
  874.  
  875. static void MoveChoice (HWND hwnd, int oldpos, int oldlen, int newpos, int length, int gap)
  876. {
  877.     HDC hdc;
  878.     RECT rect;
  879.     HRGN hrgn;
  880.  
  881.     hdc = GetDC(hwnd);
  882.  
  883.     GetClientRect(hwnd, &rect);
  884.     hrgn = CreateRectRgn (BORDERSPACE - gap, BORDERSPACE - gap,
  885.                             rect.right - BORDERSPACE + gap + 1,
  886.                             rect.bottom - BORDERSPACE + gap + 1);
  887.     SelectClipRgn(hdc, hrgn);
  888.     DeleteObject(hrgn);
  889.  
  890.     if (oldpos >= 0) {
  891.         PatBlt(hdc, oldpos- gap, BORDERSPACE - gap, oldlen - SYSFONT->leading + 2 * gap,
  892.                 SYSFONT->height + 2 * gap, DSTINVERT);
  893.     }
  894.  
  895.     if (newpos >= 0 && length > 0) {
  896.         PatBlt(hdc, newpos - gap, BORDERSPACE - gap, length - SYSFONT->leading + 2*gap,
  897.                 SYSFONT->height + 2*gap, DSTINVERT);
  898.     }
  899.  
  900.     ReleaseDC(hwnd, hdc);
  901. }
  902.  
  903.  
  904.  
  905. static void ChangeChoice(FILEOPTIONS *f, int wordchoice)
  906. {
  907.     int j, k, convlen;
  908.     int options = OP_REFORMAT | OP_UPDATE | OP_CHOOSEKANJI;
  909.     POSITION p;
  910.     ONELINE far *lp;
  911.     KANJI buffer[BUFSIZE];
  912.  
  913.     if (SELPARA1(f) == NULL || SELTYPE(f) != SEL_CONVERSION) return;
  914.  
  915.     for (k = 0, j = words[wordchoice]; j < words[wordchoice+1] - 1; j++, k++)
  916.         buffer[k] = kanji_list[j];
  917.     buffer[k] = 0;
  918.  
  919.     p = SEL1(f);
  920.  
  921.     convlen = SELPOS2(f) - SELPOS1(f) + 1,
  922.     SELPOS2(f) = SELPOS1(f) + kanjilen(buffer) - 1;
  923.     SELNOW(f) = FALSE;
  924.  
  925.     if (k == convlen) options |= OP_SAMELEN;
  926.  
  927.     /* Find the line */
  928.  
  929.     for (lp = PARAOF(p)->lines; lp != NULL; lp = lp->next)
  930.         if (lp->position > SELPOS1(f)) break;
  931.  
  932.     if (lp != NULL) lp = lp->prev;
  933.     else lp = PARAOF(p)->lastline;
  934.  
  935.     LINEOF(p) = lp;
  936.     POSOF(p) = SELPOS1(f) - LINEOF(p)->position;
  937.  
  938.     EnableFontCache(FALSE);
  939.     UndoFixConvert(f, p, convlen, kanjilen(buffer));
  940.     ReplaceString(f, p, convlen, buffer, options);
  941.     EnableFontCache(TRUE);
  942. }
  943.  
  944.  
  945. /* LB_RESETCONTENT = Initialize (lParam = FILEOPTIONS *) */
  946. /* LB_SETHORIZONTALEXTENT = Move selection to the middle     */
  947.  
  948. LONG FAR PASCAL ConvProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
  949. {
  950.     int         i, j, k, r;
  951.     int         leftgap, rightgap, gap;
  952.     HDC         hdc;
  953.     HRGN        hrgn;
  954.     PAINTSTRUCT ps;
  955.     RECT        rect;
  956.     BYTE        far *cbufp;
  957.     static FILEOPTIONS *selfile;
  958.     static int  scrollrange;
  959.     static int  scrollpos;
  960.     static int  choicepos;
  961.  
  962.  
  963.     switch (message) {
  964.  
  965.     case WM_CREATE:
  966.         sizing = FALSE;
  967.         global.convsel = -1;
  968.         scrollpos = 0;
  969.         scrollrange = choicepos = -1;
  970.         SetScrollRange(hwnd, SB_HORZ, 0, 100, TRUE);
  971.         SetScrollPos(hwnd, SB_HORZ, 0, TRUE);
  972.         kanji_list[0] = 0;
  973.  
  974.         if (global.listposition == 0) {
  975.             GetWindowRect(global.hwnd, &rect);
  976.  
  977.             i = /*GetSystemMetrics(SM_CYCAPTION) + */ SYSFONT->height +
  978.                     2 * BORDERSPACE + GetSystemMetrics(SM_CYHSCROLL) +
  979.                     2 * GetSystemMetrics(SM_CYBORDER);
  980.  
  981.             if (global.convbar.right < 0) global.convbar.right = rect.right;
  982.             global.convbar.bottom = i;
  983.  
  984.             MoveWindow(hwnd, rect.left + global.convbar.left, rect.top + global.convbar.top,
  985.                        global.convbar.right, global.convbar.bottom, TRUE);
  986.             SetActiveWindow(global.hwnd);
  987.         }
  988.  
  989.         return (0);
  990.  
  991.     case WM_GETMINMAXINFO: {
  992.         POINT far *lp;
  993.  
  994.         lp = (POINT far *) lParam;
  995.  
  996.         lp[3].y = lp[4].y = global.convbar.bottom;
  997.         if (lp[3].x < global.convbar.right) {
  998.             lp[3].x = 2 * SYSFONT->width + 2 * BORDERSPACE;
  999.         }
  1000.  
  1001.         return (0);
  1002.     }
  1003.  
  1004.     case WM_SIZE:
  1005.         if (global.listposition != 0 || global.convhwnd == NULL) break;
  1006.  
  1007.         GetWindowRect(hwnd, &rect);
  1008.         global.convbar.right = rect.right - rect.left;
  1009.         return (0);
  1010.  
  1011.     case WM_MOVE: {
  1012.         int x, y1, y2;
  1013.         RECT rect1, rect2;
  1014.  
  1015.         if (global.hwnd == NULL || global.clienthwnd == NULL) return (0);
  1016.         if (IsIconic(hwnd) || IsIconic(global.hwnd)) return (0);
  1017.  
  1018.         GetWindowRect(global.clienthwnd, &rect1);
  1019.         GetWindowRect(hwnd, &rect2);
  1020.  
  1021.         if (!AfterHitTest) {
  1022.             if (global.listposition == 0) {
  1023.                 GetWindowRect(global.hwnd, &rect1);
  1024.  
  1025.                 global.convbar.left = rect2.left - rect1.left;
  1026.                 global.convbar.top = rect2.top - rect1.top;
  1027.             }
  1028.  
  1029.             return (0);
  1030.         }
  1031.  
  1032.         AfterHitTest = FALSE;
  1033.  
  1034.         if (global.listposition != 0) {
  1035.             if (rect2.top < rect1.top) {
  1036.                 global.listposition = 1;
  1037.             } else if (rect2.bottom > rect1.bottom) {
  1038.                 global.listposition = 2;
  1039.             } else {
  1040.                 GetWindowRect(global.hwnd, &rect1);
  1041.                 global.convbar.left = rect2.left - rect1.left;
  1042.                 global.convbar.top = rect2.top - rect1.top;
  1043.                 global.listposition = 0;
  1044.             }
  1045.         } else {
  1046.             if (rect2.top < rect1.top) {
  1047.                 global.listposition = 1;
  1048.             } else if (rect2.bottom > rect1.bottom) {
  1049.                 global.listposition = 2;
  1050.             } else {
  1051.                 GetWindowRect(global.hwnd, &rect1);
  1052.  
  1053.                 global.convbar.left = rect2.left - rect1.left;
  1054.                 global.convbar.top = rect2.top - rect1.top;
  1055.  
  1056.                 return (0);
  1057.             }
  1058.         }
  1059.  
  1060.         GetClientRect(global.hwnd, &rect1);
  1061.         i = rect1.bottom - rect1.top;
  1062.         PostMessage(global.hwnd, WM_SIZE, 0, MAKELONG(rect1.right - rect1.left, i));
  1063.         OptionsChanged = TRUE;
  1064.         return (0);
  1065.     }
  1066.  
  1067.     case LB_RESETCONTENT:
  1068.         choicepos = scrollrange = -1;
  1069.         selfile = (FILEOPTIONS *) lParam;
  1070.  
  1071.         k = 0;
  1072.         words[k++] = 0;
  1073.         scrollrange = 0;
  1074.  
  1075.         for (i = 0; kanji_list[i] != 0; i++) {
  1076.             if ((kanji_list[i] & 0x7fff) == '/') {
  1077.                 words[k++] = i + 1;
  1078.                 scrollrange++;
  1079.             }
  1080.         }
  1081.         words[k++] = i + 1;
  1082.  
  1083.         SendMessage(hwnd, LB_SETHORIZONTALEXTENT, 0, 0L);   /* Move to middle */
  1084.  
  1085.         SetScrollRange(hwnd, SB_HORZ, 0, (scrollrange <= 0) ? 1 : scrollrange, FALSE);
  1086.         SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1087.  
  1088.         InvalidateRect(hwnd, NULL, FALSE);
  1089.         UpdateWindow(hwnd);
  1090.         SetActiveWindow(global.hwnd);
  1091.         return (0);
  1092.  
  1093.     case LB_SETHORIZONTALEXTENT:
  1094.         if (global.convsel < 0) {
  1095.             scrollpos = 0;
  1096.         } else {
  1097.             /* Put it into the middle */
  1098.  
  1099.             scrollpos = global.convsel;
  1100.             if (scrollpos > scrollrange) scrollpos = scrollrange;
  1101.  
  1102.             GetClientRect(hwnd, &rect);
  1103.             j = (rect.right - 2 * BORDERSPACE) / 2;
  1104.  
  1105.             for (; scrollpos > 0; scrollpos--) {
  1106.                 j -= (words[scrollpos] - words[scrollpos-1] - 1) * (SYSFONT->width + SYSFONT->leading) + SEPARATION;
  1107.                 if (j < 0) break;
  1108.             }
  1109.             if (scrollpos < 0) scrollpos = 0;
  1110.         }
  1111.         SetActiveWindow(global.hwnd);
  1112.         return (0);
  1113.  
  1114.     case WM_HSCROLL:
  1115.         SetActiveWindow(global.hwnd);
  1116.  
  1117.         gap = SYSFONT->leading;
  1118.  
  1119.         switch (wParam) {
  1120.  
  1121.         case SB_LINEUP:
  1122.             if (scrollpos <= 0) return (0);
  1123.  
  1124.             GetClientRect(hwnd, &rect);
  1125.             rect.top = rect.left = BORDERSPACE - gap;
  1126.             rect.bottom -= (BORDERSPACE - gap - 1);
  1127.             rect.right -= (BORDERSPACE - gap - 1);
  1128.  
  1129.             k = (words[scrollpos] - words[scrollpos-1] - 1) * (SYSFONT->width + SYSFONT->leading) + SEPARATION;
  1130.  
  1131.             ScrollWindow(hwnd, k, 0, NULL, &rect);
  1132.  
  1133.             if (choicepos >= 0) choicepos += k;
  1134.             if (choicepos >= (rect.right - BORDERSPACE) || choicepos < BORDERSPACE)
  1135.                 choicepos = -1;
  1136.  
  1137.             scrollpos--;
  1138.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1139.             break;
  1140.  
  1141.         case SB_LINEDOWN:
  1142.             if (scrollpos >= scrollrange) return (0);
  1143.  
  1144.             GetClientRect(hwnd, &rect);
  1145.             rect.top = rect.left = BORDERSPACE - gap;
  1146.             rect.bottom -= (BORDERSPACE - gap - 1);
  1147.             rect.right -= (BORDERSPACE - gap - 1);
  1148.  
  1149.             k = (words[scrollpos+1] - words[scrollpos] - 1) * (SYSFONT->width + SYSFONT->leading) + SEPARATION;
  1150.  
  1151.             ScrollWindow(hwnd, -k, 0, NULL, &rect);
  1152.  
  1153.             if (choicepos >= 0) choicepos -= k;
  1154.             if (choicepos >= (rect.right - BORDERSPACE) || choicepos < BORDERSPACE)
  1155.                 choicepos = -1;
  1156.  
  1157.             scrollpos++;
  1158.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1159.             break;
  1160.  
  1161.         case SB_PAGEUP:
  1162.             if (scrollpos <= 0) return (0);
  1163.  
  1164.             GetClientRect(hwnd, &rect);
  1165.  
  1166.             j = 0;
  1167.             for (i = scrollpos; i >= 0; i--) {
  1168.                 k = (words[i] - words[i-1] - 1) * (SYSFONT->width + SYSFONT->leading);
  1169.                 if (j + k + SEPARATION > rect.right - BORDERSPACE) break;
  1170.                 j += k + SEPARATION;
  1171.             }
  1172.             if (i + 1 != scrollpos) i++;
  1173.             scrollpos = i;
  1174.             choicepos = -1;
  1175.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1176.             InvalidateRect(hwnd, NULL, FALSE);
  1177.             break;
  1178.  
  1179.         case SB_PAGEDOWN:
  1180.             if (scrollpos >= scrollrange) return (0);
  1181.  
  1182.             GetClientRect(hwnd, &rect);
  1183.  
  1184.             j = 0;
  1185.             for (i = scrollpos; i <= scrollrange; i++) {
  1186.                 k = (words[i+1] - words[i] - 1) * (SYSFONT->width + SYSFONT->leading);
  1187.                 if (j + k + SEPARATION > rect.right - BORDERSPACE) break;
  1188.                 j += k + SEPARATION;
  1189.             }
  1190.             if (i - 1 != scrollpos) i--;
  1191.             scrollpos = i;
  1192.             choicepos = -1;
  1193.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1194.             InvalidateRect(hwnd, NULL, FALSE);
  1195.             break;
  1196.  
  1197.         case SB_THUMBPOSITION:
  1198.             scrollpos = LOWORD(lParam);
  1199.             choicepos = -1;
  1200.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1201.             InvalidateRect(hwnd, NULL, FALSE);
  1202.             break;
  1203.  
  1204.         default:
  1205.             return (0);
  1206.         }
  1207.  
  1208.         UpdateWindow(hwnd);
  1209.         return (0);
  1210.  
  1211.     case WM_KEYDOWN:
  1212.         SetActiveWindow(global.hwnd);
  1213.  
  1214.         if (global.convsel < 0) return (0);
  1215.  
  1216.         gap = SYSFONT->leading;
  1217.  
  1218.         switch (wParam) {
  1219.  
  1220.         case VK_HOME:
  1221.             scrollpos = 0;
  1222.             global.convsel = 0;
  1223.             choicepos = -1;
  1224.             SetScrollPos(hwnd, SB_HORZ, 0, TRUE);
  1225.             InvalidateRect(hwnd, NULL, FALSE);
  1226.             break;
  1227.  
  1228.         case VK_END:
  1229.             global.convsel = scrollrange;
  1230.             choicepos = -1;
  1231.  
  1232.             SendMessage(hwnd, LB_SETHORIZONTALEXTENT, 0, 0L);   /* Move to middle */
  1233.             SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1234.  
  1235.             InvalidateRect(hwnd, NULL, FALSE);
  1236.             return (0);
  1237.  
  1238.         case VK_LEFT:
  1239.             if (global.convsel <= 0) {
  1240.                 global.convsel = scrollrange;
  1241.                 scrollpos = scrollrange;
  1242.                 SendMessage(hwnd, LB_SETHORIZONTALEXTENT, 0, 0L);   /* Move to middle */
  1243.                 InvalidateRect(hwnd, NULL, FALSE);
  1244.                 UpdateWindow(hwnd);
  1245.                 SetScrollPos(hwnd, SB_HORZ, scrollpos, TRUE);
  1246.                 return (0);
  1247.             }
  1248.  
  1249.             i = (words[global.convsel+1] - words[global.convsel] - 1) * (SYSFONT->width + SYSFONT->leading);
  1250.             global.convsel--;
  1251.             j = (words[global.convsel+1] - words[global.convsel] - 1) * (SYSFONT->width + SYSFONT->leading);
  1252.  
  1253.             if (choicepos < 0) {
  1254.                 scrollpos = global.convsel;
  1255.                 InvalidateRect(hwnd, NULL, FALSE);
  1256.                 UpdateWindow(hwnd);
  1257.             } else {
  1258.                 MoveChoice(hwnd, choicepos, i, choicepos - j - SEPARATION, j, gap);
  1259.                 choicepos -= j + SEPARATION;
  1260.                 if (scrollpos == global.convsel + 1)
  1261.                     SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, 0L);
  1262.             }
  1263.             return (0);
  1264.  
  1265.         case VK_RIGHT:
  1266.             if (global.convsel >= scrollrange) {
  1267.                 global.convsel = 0;
  1268.                 scrollpos = 0;
  1269.                 InvalidateRect(hwnd, NULL, FALSE);
  1270.                 UpdateWindow(hwnd);
  1271.                 SetScrollPos(hwnd, SB_HORZ, 0, TRUE);
  1272.                 return (0);
  1273.             }
  1274.  
  1275.             GetClientRect(hwnd, &rect);
  1276.  
  1277.             i = (words[global.convsel+1] - words[global.convsel] - 1) * (SYSFONT->width + SYSFONT->leading);
  1278.             global.convsel++;
  1279.             j = (words[global.convsel+1] - words[global.convsel] - 1) * (SYSFONT->width + SYSFONT->leading);
  1280.  
  1281.             if (choicepos < 0) {
  1282.                 scrollpos = global.convsel;
  1283.                 InvalidateRect(hwnd, NULL, FALSE);
  1284.                 UpdateWindow(hwnd);
  1285.             } else {
  1286.                 MoveChoice(hwnd, choicepos, i, choicepos + i + SEPARATION, j, gap);
  1287.                 choicepos += i + SEPARATION;
  1288.                 while (choicepos + j >= (rect.right - BORDERSPACE) && scrollpos < scrollrange)
  1289.                     SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, 0L);
  1290.             }
  1291.             return (0);
  1292.  
  1293.         case VK_RETURN: {
  1294.             KANJI buffer[BUFSIZE];
  1295.  
  1296.             if (selfile == NULL) return (0);
  1297.  
  1298.             ChangeChoice(selfile, global.convsel);
  1299.  
  1300.             for (k = 0, j = words[global.convsel]; j < words[global.convsel + 1] - 1; j++, k++)
  1301.                 buffer[k] = kanji_list[j];
  1302.             buffer[k] = 0;
  1303.  
  1304.             PutIntoConvCache(LastConvKey, buffer);
  1305.             return (0);
  1306.         }
  1307.  
  1308.         default: return (0);
  1309.         }
  1310.         UpdateWindow(hwnd);
  1311.         return (0);
  1312.  
  1313.     case WM_NCHITTEST:
  1314.     case WM_LBUTTONDOWN: {
  1315.         int x, y, i;
  1316.         int oldlen;
  1317.  
  1318.         SetActiveWindow(global.hwnd);
  1319.  
  1320.         if (message == WM_NCHITTEST) {
  1321.             i = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
  1322.  
  1323.             if (i != HTCLIENT) return (i);
  1324.  
  1325.             GetWindowRect(hwnd, &rect);
  1326.  
  1327.             x = LOWORD(lParam) - rect.left;
  1328.             y = HIWORD(lParam) - rect.top;
  1329.  
  1330.             if (global.listposition == 0) {
  1331.                 x -= GetSystemMetrics(SM_CXFRAME);
  1332.                 y -= GetSystemMetrics(SM_CYFRAME);
  1333.             }
  1334.  
  1335.             if (x < BORDERSPACE || y < BORDERSPACE) {
  1336.                 AfterHitTest = TRUE;
  1337.                 return (HTCAPTION);
  1338.             }
  1339.             if (y >= BORDERSPACE + SYSFONT->height) {
  1340.                 AfterHitTest = TRUE;
  1341.                 return (HTCAPTION);
  1342.             }
  1343.         } else {
  1344.             x = LOWORD(lParam);
  1345.             y = HIWORD(lParam);
  1346.  
  1347.             if (x < BORDERSPACE || y < BORDERSPACE) return (0);
  1348.             if (y >= BORDERSPACE + SYSFONT->height) return (0);
  1349.         }
  1350.  
  1351.         gap = SYSFONT->leading;
  1352.         GetClientRect(hwnd, &rect);
  1353.  
  1354.         /* Find the old selected position */
  1355.  
  1356.         oldlen = (words[global.convsel+1] - words[global.convsel] - 1) * (SYSFONT->width + SYSFONT->leading);
  1357.  
  1358.         j = BORDERSPACE;
  1359.  
  1360.         for (i = scrollpos; i <= scrollrange; i++) {
  1361.             k = (words[i+1] - words[i] - 1) * (SYSFONT->width + SYSFONT->leading);
  1362.             if (j + k > x) break;
  1363.             if (message == WM_NCHITTEST && j + k + SEPARATION > x) {
  1364.                 AfterHitTest = TRUE;
  1365.                 return (HTCAPTION);
  1366.             }
  1367.             j += k + SEPARATION;
  1368.         }
  1369.  
  1370.         if (message == WM_NCHITTEST) {
  1371.             if (i > scrollrange) {
  1372.                 AfterHitTest = TRUE;
  1373.                 return (HTCAPTION);
  1374.             } else {
  1375.                 AfterHitTest = FALSE;
  1376.                 return (HTCLIENT);
  1377.             }
  1378.         } else {
  1379.             if (i > scrollrange) return (0);
  1380.         }
  1381.  
  1382.         if (i == global.convsel) return (0);
  1383.  
  1384.         MoveChoice(hwnd, choicepos, oldlen, j, k, gap);
  1385.         choicepos = j;
  1386.  
  1387.         global.convsel = i;
  1388.  
  1389.         /* Replace the kanji */
  1390.  
  1391.         SendMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0L);
  1392.  
  1393.         return (0);
  1394.     }
  1395.  
  1396.     case WM_PAINT:
  1397.         if (global.convsel < 0) choicepos = -1;
  1398.  
  1399.         gap = SYSFONT->leading;
  1400.  
  1401.         hdc = BeginPaint(hwnd, &ps);
  1402.  
  1403.         SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
  1404.         SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
  1405.  
  1406.         GetClientRect(hwnd, &rect);
  1407.         rect.top = rect.left = -1;
  1408.         rect.bottom++; rect.right++;
  1409.         Create3DEffect(hdc, &rect, 1, 0);
  1410.  
  1411.         if (scrollrange < 0) {
  1412.             EndPaint(hwnd, &ps);
  1413.             return (0);
  1414.         }
  1415.  
  1416.         hrgn = CreateRectRgn (BORDERSPACE - gap, BORDERSPACE - gap,
  1417.                                 rect.right - BORDERSPACE + gap,
  1418.                                 rect.bottom - BORDERSPACE + gap);
  1419.         SelectClipRgn(hdc, hrgn);
  1420.         DeleteObject(hrgn);
  1421.  
  1422.         j = BORDERSPACE;
  1423.  
  1424.         EnableFontCache(FALSE);
  1425.  
  1426.         for (i = words[scrollpos]; kanji_list[i]; i++) {
  1427.             if (j + SYSFONT->width + gap < ps.rcPaint.left) {
  1428.                 j += ((kanji_list[i] & 0x7fff) == '/') ?
  1429.                         SEPARATION : SYSFONT->width + SYSFONT->leading;
  1430.                 continue;
  1431.             }
  1432.             if (j > ps.rcPaint.right) break;
  1433.  
  1434.             if ((kanji_list[i] & 0x7fff) == '/') {
  1435.                 j += SEPARATION;
  1436.                 continue;
  1437.             }
  1438.             if (ISASCII(kanji_list[i])) continue;
  1439.  
  1440.             r = Jis2Index(kanji_list[i], SYSFONT->holes);
  1441.             if (r < 0) r = Jis2Index(BADKANJI, SYSFONT->holes);
  1442.             r = GetKanjiBitmap(SYSFONT, r, &cbufp);
  1443.  
  1444.             DisplayKanjiBitmap(hdc, j, BORDERSPACE + SYSFONT->height,
  1445.                                     SYSFONT->width, SYSFONT->height,
  1446.                                     r, SRCCOPY, cbufp);
  1447.  
  1448.             if (global.convsel >= 0 &&
  1449.                 words[global.convsel] <= i && i < words[global.convsel+1]) {
  1450.  
  1451.                 if (i > words[global.convsel]) {
  1452.                     PatBlt(hdc, j - SYSFONT->leading, BORDERSPACE - gap,
  1453.                                 SYSFONT->leading, SYSFONT->height + 2*gap, DSTINVERT);
  1454.                 } else {
  1455.                     choicepos = j;
  1456.                 }
  1457.  
  1458.                 leftgap = rightgap = 0;
  1459.  
  1460.                 if (i <= words[global.convsel]) {
  1461.                     leftgap = gap;
  1462.                 }
  1463.                 if (i >= words[global.convsel+1] - 2) {
  1464.                     rightgap = gap;
  1465.                 }
  1466.  
  1467.                 PatBlt(hdc, j - leftgap, BORDERSPACE - gap,
  1468.                         SYSFONT->width + leftgap + rightgap, SYSFONT->height + 2*gap,
  1469.                         DSTINVERT);
  1470.             }
  1471.  
  1472.             j += SYSFONT->width + SYSFONT->leading;
  1473.         }
  1474.  
  1475.         EnableFontCache(TRUE);
  1476.  
  1477.         EndPaint(hwnd, &ps);
  1478.  
  1479.         return (0);
  1480.     }
  1481.  
  1482.     return (DefWindowProc(hwnd, message, wParam, lParam));
  1483. }
  1484.  
  1485.  
  1486.  
  1487. void ConvCacheStatistics (long int *usage, long int *requests, long int *hits)
  1488. {
  1489.     *requests = ConvRequests;
  1490.     *hits = ConvHits;
  1491.     *usage = ConvUsage;
  1492. }
  1493.  
  1494.  
  1495.  
  1496. static BOOL IsValidReading (UNIT far *s)
  1497. {
  1498.     int i;
  1499.     KANJI ch;
  1500.  
  1501.     if (!s[0].kanji) return (FALSE);
  1502.  
  1503.     for (i = 0; s[i].kanji; i++) {
  1504.         ch = s[i].kanji;
  1505.         if (!ISKANJI(ch)) {
  1506.             if ('A' <= ch && ch <= 'Z') ch += 32;
  1507.             if (i <= 0) return (FALSE);
  1508.             if (strchr("ukgsztdnmhbpr", LOBYTE(ch)) == NULL) return (FALSE);
  1509.             return (!s[i+1].kanji);
  1510.         } else if (HIBYTE(ch) != 0x24) return (FALSE);
  1511.     }
  1512.  
  1513.     return (TRUE);
  1514. }
  1515.  
  1516.  
  1517.  
  1518. static KANJI far *ConvertUser (int id, LONG lParam, KANJI *buf)
  1519. {
  1520.     int i;
  1521.     USERCONV far *up;
  1522.     KANJILIST far *kp;
  1523.  
  1524.     if (lParam == NULL) {
  1525.         buf[0] = 0;
  1526.         return (buf);
  1527.     }
  1528.  
  1529.     if (id == 4201) {
  1530.         up = (USERCONV far *) lParam;
  1531.         for (i = 0; up->kana[i]; i++) {
  1532.             if (up->kana[i] & 0x80) buf[i] = 0x2400 | (up->kana[i] & 0x007f);
  1533.             else buf[i] = up->kana[i];
  1534.         }
  1535.         buf[i] = 0;
  1536.         return (buf);
  1537.     } else if (id == 4202) {
  1538.         kp = (KANJILIST far *) lParam;
  1539.         return (kp->kanji);
  1540.     } else {
  1541.         ErrorMessage(global.hwnd, "Bad id to ConvertUser! (%d)", id);
  1542.         return (buf);
  1543.     }
  1544. }
  1545.  
  1546.  
  1547.  
  1548. BOOL FAR PASCAL EditUserConversionProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
  1549. {
  1550.     switch (message) {
  1551.         case WM_INITDIALOG: {
  1552.             int i;
  1553.             FILEOPTIONS *f;
  1554.             KANJI buf[BUFSIZE];
  1555.  
  1556.             /* Set the type and mode-change icon */
  1557.             SendDlgItemMessage(hwnd, 4201, EM_SETMODIFY, FN_CONTROL | FN_NOKANJI, 0L);
  1558.             SendDlgItemMessage(hwnd, 4201, EM_SETRECT, GetDlgItem(hwnd, 4211), 0L);
  1559.  
  1560.             SendDlgItemMessage(hwnd, 4202, EM_SETMODIFY, FN_CONTROL, 0L);
  1561.             SendDlgItemMessage(hwnd, 4202, EM_SETRECT, GetDlgItem(hwnd, 4211), 0L);
  1562.  
  1563.             f = (FILEOPTIONS *) SendDlgItemMessage(hwnd, 4201, EM_GETHANDLE, 0, 0L);
  1564.             SendDlgItemMessage(hwnd, 4211, EM_SETHANDLE, f->hwnd, 0L);  /* mode-change icon */
  1565.  
  1566.             if (CurrentConv != NULL) {
  1567.                 for (i = 0; CurrentConv->kana[i]; i++) {
  1568.                     if (CurrentConv->kana[i] & 0x80) buf[i] = 0x2400 | (CurrentConv->kana[i] & 0x007f);
  1569.                     else buf[i] = CurrentConv->kana[i];
  1570.                 }
  1571.                 buf[i] = 0;
  1572.                 SendDlgItemMessage(hwnd, 4201, EM_REPLACESEL, 0, (LONG) buf);
  1573.                 SetFocus(GetDlgItem(hwnd, 4202));
  1574.             }
  1575.  
  1576.             if (CurrentKanji != NULL) {
  1577.                 /* Edit */
  1578.                 SendDlgItemMessage(hwnd, 4202, EM_REPLACESEL, 0, (LONG) CurrentKanji->kanji);
  1579.                 i = kanjilen(CurrentKanji->kanji);
  1580.                 SendDlgItemMessage(hwnd, 4202, EM_SETSEL, i, MAKELONG(0, i-1));
  1581.                 SetWindowText(GetDlgItem(hwnd, 1), "&Done!");
  1582.                 SetWindowText(hwnd, "Edit an Existing User Conversion");
  1583.             } else {
  1584.                 /* Add */
  1585.                 SetWindowText(GetDlgItem(hwnd, 1), "&Add!");
  1586.                 SetWindowText(hwnd, "Add a New User Conversion");
  1587.             }
  1588.  
  1589.             CenterDialogBox(hwnd);
  1590.             return (TRUE);
  1591.         }
  1592.  
  1593.         case WM_COMMAND:
  1594.             switch (wParam) {
  1595.                 case IDCANCEL:
  1596.                     CurrentConv = NULL;
  1597.                     CurrentKanji = NULL;
  1598.                     EndDialog(hwnd, FALSE);
  1599.                     return (TRUE);
  1600.  
  1601.                 case IDOK: {
  1602.                     int i;
  1603.                     UNIT far *up, far *up1;
  1604.                     KANJI ch;
  1605.  
  1606.                     up1 = (UNIT far *) SendDlgItemMessage(hwnd, 4202, EM_GETLINE, 0, 0L);
  1607.                     if (unitlen(up1) <= 0) {
  1608.                         ErrorMessage(hwnd, "Sorry, you must enter a KANJI for the conversion");
  1609.                         SetFocus(GetDlgItem(hwnd, 4202));
  1610.                         return (TRUE);
  1611.                     }
  1612.  
  1613.                     if (CurrentKanji == NULL) {
  1614.                         /* An Add */
  1615.                         up = (UNIT far *) SendDlgItemMessage(hwnd, 4201, EM_GETLINE, 0, 0L);
  1616.                         if (!IsValidReading(up)) {
  1617.                             ErrorMessage(hwnd, "Sorry, what you have entered is not "
  1618.                                                   "a proper kana reading.  A proper kana "
  1619.                                                   "reading is a string of hiragana, with "
  1620.                                                   "no spaces and other symbols.  It may "
  1621.                                                   "or may not be followed by ONE (and only "
  1622.                                                   "one) of the following letters:\n"
  1623.                                                   "\n"
  1624.                                                   "     u, k, g, s, z, t, d, n, m, h, b, p, r\n"
  1625.                                                   "\n"
  1626.                                                   "Consult the documentation for details.");
  1627.                             SetFocus(GetDlgItem(hwnd, 4201));
  1628.                             return (TRUE);
  1629.                         }
  1630.  
  1631.                         CurrentConv = StructAlloc(USERCONV);
  1632.                         CurrentKanji = StructAlloc(KANJILIST);
  1633.  
  1634.                         CurrentConv->list = CurrentKanji;
  1635.                         CurrentConv->next = CurrentConv->prev = NULL;
  1636.                         CurrentKanji->next = CurrentKanji->prev = NULL;
  1637.  
  1638.                         CurrentConv->kana = BlockAlloc(unitlen(up) + 5);
  1639.  
  1640.                         for (i = 0; ; i++) {
  1641.                             ch = up[i].kanji;
  1642.                             if (ch == 0) {
  1643.                                 CurrentConv->kana[i] = 0;
  1644.                                 break;
  1645.                             }
  1646.                             if (ISKANJI(ch)) CurrentConv->kana[i] = LOBYTE(ch) | 0x0080;
  1647.                             else {
  1648.                                 ch = LOBYTE(ch) & 0x7f;
  1649.                                 if ('A' <= ch && ch <= 'Z') ch += 32;
  1650.                                 CurrentConv->kana[i] = ch;
  1651.                             }
  1652.                         }
  1653.                     } else {
  1654.                         /* An Edit */
  1655.                         FreeBlock(CurrentKanji->kanji);
  1656.                     }
  1657.  
  1658.                     CurrentKanji->kanji = BlockAlloc((unitlen(up1) + 5) * sizeof(KANJI));
  1659.                     for (i = 0; ; i++) {
  1660.                         if (!(CurrentKanji->kanji[i] = up1[i].kanji)) break;
  1661.                     }
  1662.                     UserConvChanged = TRUE;
  1663.                     EndDialog(hwnd, FALSE);
  1664.                     return (TRUE);
  1665.                 }
  1666.             }
  1667.             return (TRUE);
  1668.  
  1669.         case WM_PAINT: {
  1670.             HDC hdc;
  1671.             PAINTSTRUCT ps;
  1672.  
  1673.             hdc = BeginPaint(hwnd, &ps);
  1674.  
  1675.             DrawBoundingBox(hwnd, hdc, 4201);
  1676.             DrawBoundingBox(hwnd, hdc, 4202);
  1677.  
  1678.             EndPaint(hwnd, &ps);
  1679.             return (TRUE);
  1680.         }
  1681.     }
  1682.     return (FALSE);
  1683. }
  1684.  
  1685.  
  1686.  
  1687. BOOL FAR PASCAL UserConversionProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
  1688. {
  1689.     int i, j;
  1690.     USERCONV far *up;
  1691.     KANJILIST far *kp;
  1692.     INPUTMODE oldmode;
  1693.  
  1694.     switch (message) {
  1695.         case WM_INITDIALOG:
  1696.             EnableWindow(GetDlgItem(hwnd, 4212), FALSE);    /* Edit */
  1697.             EnableWindow(GetDlgItem(hwnd, 4213), FALSE);    /* Delete */
  1698.  
  1699.             for (up = UserConversions; up != NULL; up = up->next) {
  1700.                 SendDlgItemMessage(hwnd, 4201, LB_ADDSTRING, 0, (LONG) up);
  1701.             }
  1702.             CenterDialogBox(hwnd);
  1703.             return (TRUE);
  1704.  
  1705.         case WM_KEYDOWN:
  1706.             switch (wParam) {
  1707.                 case VK_ESCAPE: SendMessage(hwnd, WM_COMMAND, IDCANCEL, 0L);
  1708.                                 return (TRUE);
  1709.             }
  1710.             break;
  1711.  
  1712.         case WM_COMMAND:
  1713.             switch (wParam) {
  1714.                 case 4201:      /* Kana list */
  1715.                     switch (HIWORD(lParam)) {
  1716.                         case LBN_SELCHANGE: {
  1717.                             i = SendDlgItemMessage(hwnd, 4201, LB_GETCURSEL, 0, 0L);
  1718.                             if (i == LB_ERR) {
  1719.                                 MessageBeep(0);
  1720.                                 return (TRUE);
  1721.                             }
  1722.  
  1723.                             SendDlgItemMessage(hwnd, 4202, LB_RESETCONTENT, 0, 0L);
  1724.                             SendDlgItemMessage(hwnd, 4202, WM_SETREDRAW, FALSE, 0L);
  1725.  
  1726.                             up = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1727.                             for (kp = up->list; kp != NULL; kp = kp->next) {
  1728.                                 SendDlgItemMessage(hwnd, 4202, LB_ADDSTRING, 0, (LONG) kp);
  1729.                             }
  1730.  
  1731.                             SendDlgItemMessage(hwnd, 4202, WM_SETREDRAW, TRUE, 0L);
  1732.                             InvalidateRect(GetDlgItem(hwnd, 4202), NULL, TRUE);
  1733.  
  1734.                             EnableWindow(GetDlgItem(hwnd, 4212), FALSE);    /* Edit */
  1735.                             EnableWindow(GetDlgItem(hwnd, 4213), FALSE);    /* Delete */
  1736.                             return (0);
  1737.                         }
  1738.                     }
  1739.                     return (TRUE);
  1740.  
  1741.                 case 4202:      /* Kanji list */
  1742.                     switch (HIWORD(lParam)) {
  1743.                         case LBN_SELCHANGE: {
  1744.                             i = SendDlgItemMessage(hwnd, 4202, LB_GETCURSEL, 0, 0L);
  1745.                             if (i == LB_ERR) {
  1746.                                 MessageBeep(0);
  1747.                                 return (TRUE);
  1748.                             }
  1749.  
  1750.                             EnableWindow(GetDlgItem(hwnd, 4212), TRUE);    /* Edit */
  1751.                             EnableWindow(GetDlgItem(hwnd, 4213), TRUE);    /* Delete */
  1752.  
  1753.                             return (0);
  1754.                         }
  1755.  
  1756.                         case LBN_DBLCLK:
  1757.                             SendMessage(hwnd, WM_COMMAND, 4212, 0L);    /* Edit */
  1758.                             return (TRUE);
  1759.                     }
  1760.                     return (TRUE);
  1761.  
  1762.                 case 4211:  /* Add button */
  1763.                     i = SendDlgItemMessage(hwnd, 4201, LB_GETCURSEL, 0, 0L);
  1764.                     if (i == LB_ERR) {
  1765.                         CurrentConv = NULL;
  1766.                     } else {
  1767.                         CurrentConv = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1768.                     }
  1769.                     CurrentKanji = NULL;
  1770.  
  1771.                     oldmode = global.mode;
  1772.                     DialogBox (hInstance, "EditUserDict", hwnd, EditUserConversionProc);
  1773.                     if (oldmode != global.mode) ToggleInputMode();
  1774.  
  1775.                     if (CurrentConv == NULL && CurrentKanji == NULL) return (TRUE);
  1776.  
  1777.                     /* Kana already in the list? */
  1778.  
  1779.                     j = SendDlgItemMessage(hwnd, 4201, LB_GETCOUNT, 0, 0L);
  1780.                     for (i = 0; i < j; i++) {
  1781.                         up = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1782.                         if (!bytecmp(up->kana, CurrentConv->kana)) break;
  1783.                     }
  1784.  
  1785.                     if (i >= j) {   /* Add to the list */
  1786.                         CurrentConv->next = UserConversions;
  1787.                         CurrentConv->prev = NULL;
  1788.                         if (UserConversions != NULL) UserConversions->prev = CurrentConv;
  1789.                         UserConversions = CurrentConv;
  1790.                         SendDlgItemMessage(hwnd, 4201, LB_ADDSTRING, 0, (LONG) CurrentConv);
  1791.  
  1792.                         /* Now determine which string is the one we added */
  1793.                         j = SendDlgItemMessage(hwnd, 4201, LB_GETCOUNT, 0, 0L);
  1794.                         for (i = 0; i < j; i++) {
  1795.                             up = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1796.                             if (!bytecmp(up->kana, CurrentConv->kana)) break;
  1797.                         }
  1798.  
  1799.                         if (i < j) {
  1800.                             SendDlgItemMessage(hwnd, 4201, LB_SETCURSEL, i, 0L);
  1801.                             SendMessage(hwnd, WM_COMMAND, 4201, MAKELONG(0, LBN_SELCHANGE));
  1802.                         } else {
  1803.                             SendDlgItemMessage(hwnd, 4202, LB_RESETCONTENT, 0, 0L);
  1804.                         }
  1805.  
  1806.                         EnableWindow(GetDlgItem(hwnd, 4212), TRUE);    /* Edit */
  1807.                         EnableWindow(GetDlgItem(hwnd, 4213), TRUE);    /* Delete */
  1808.                     } else {            /* Add to the kanji list */
  1809.                         CurrentKanji->next = up->list;
  1810.                         CurrentKanji->prev = NULL;
  1811.                         if (up->list != NULL) up->list->prev = CurrentKanji;
  1812.                         up->list = CurrentKanji;
  1813.                         FreeBlock(CurrentConv);
  1814.  
  1815.                         SendDlgItemMessage(hwnd, 4201, LB_SETCURSEL, i, 0L);
  1816.                         SendMessage(hwnd, WM_COMMAND, 4201, MAKELONG(0, LBN_SELCHANGE));
  1817.                         EnableWindow(GetDlgItem(hwnd, 4212), TRUE);    /* Edit */
  1818.                         EnableWindow(GetDlgItem(hwnd, 4213), FALSE);    /* Delete */
  1819.                     }
  1820.                     return (TRUE);
  1821.  
  1822.                 case 4212:      /* Edit button */
  1823.                     i = SendDlgItemMessage(hwnd, 4201, LB_GETCURSEL, 0, 0L);
  1824.                     j = SendDlgItemMessage(hwnd, 4202, LB_GETCURSEL, 0, 0L);
  1825.                     if (i == LB_ERR || j == LB_ERR) {
  1826.                         MessageBeep(0);
  1827.                         return (TRUE);
  1828.                     }
  1829.                     CurrentConv = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1830.                     CurrentKanji = (KANJILIST far *) SendDlgItemMessage(hwnd, 4202, LB_GETITEMDATA, j, 0L);
  1831.  
  1832.                     oldmode = global.mode;
  1833.                     DialogBox (hInstance, "EditUserDict", hwnd, EditUserConversionProc);
  1834.                     if (oldmode != global.mode) ToggleInputMode();
  1835.  
  1836.                     if (CurrentConv == NULL && CurrentKanji == NULL) return (TRUE);
  1837.  
  1838.                     FreeBlock(CurrentConv->kana);
  1839.                     FreeStruct(CurrentConv);
  1840.  
  1841.                     InvalidateRect(GetDlgItem(hwnd, 4202), NULL, TRUE);
  1842.                     return (TRUE);
  1843.  
  1844.                 case 4213:      /* Delete button */
  1845.                     i = SendDlgItemMessage(hwnd, 4201, LB_GETCURSEL, 0, 0L);
  1846.                     j = SendDlgItemMessage(hwnd, 4202, LB_GETCURSEL, 0, 0L);
  1847.                     if (i == LB_ERR || j == LB_ERR) {
  1848.                         MessageBeep(0);
  1849.                         return (TRUE);
  1850.                     }
  1851.  
  1852.                     if (YesNo(hwnd, "Do you REALLY want to delete this conversion?") != IDYES)
  1853.                         return (TRUE);
  1854.  
  1855.                     up = (USERCONV far *) SendDlgItemMessage(hwnd, 4201, LB_GETITEMDATA, i, 0L);
  1856.                     kp = (KANJILIST far *) SendDlgItemMessage(hwnd, 4202, LB_GETITEMDATA, j, 0L);
  1857.  
  1858.                     UserConvChanged = TRUE;
  1859.                     EnableWindow(GetDlgItem(hwnd, 4212), FALSE);    /* Edit */
  1860.                     EnableWindow(GetDlgItem(hwnd, 4213), FALSE);    /* Delete */
  1861.  
  1862.                     /* First delete the kanji */
  1863.                     if (kp->prev != NULL) kp->prev->next = kp->next;
  1864.                     if (kp->next != NULL) kp->next->prev = kp->prev;
  1865.                     if (up->list == kp) up->list = kp->next;
  1866.                     FreeBlock(kp->kanji);
  1867.                     FreeStruct(kp);
  1868.  
  1869.                     if (up->list != NULL) {
  1870.                         SendDlgItemMessage(hwnd, 4202, LB_DELETESTRING, j, 0L);
  1871.                         return (TRUE);
  1872.                     }
  1873.  
  1874.                     /* Delete the kana also */
  1875.  
  1876.                     if (up->prev != NULL) up->prev->next = up->next;
  1877.                     if (up->next != NULL) up->next->prev = up->prev;
  1878.                     if (UserConversions == up) UserConversions = up->next;
  1879.                     SendDlgItemMessage(hwnd, 4201, LB_DELETESTRING, i, 0L);
  1880.                     SendDlgItemMessage(hwnd, 4202, LB_RESETCONTENT, 0, 0L);
  1881.                     return (TRUE);
  1882.  
  1883.                 case IDOK:
  1884.                 case IDCANCEL:
  1885.                     EndDialog(hwnd, FALSE);
  1886.                     return (TRUE);
  1887.             }
  1888.  
  1889.         case WM_COMPAREITEM:
  1890.         case WM_DELETEITEM:
  1891.         case WM_DRAWITEM:
  1892.         case WM_MEASUREITEM:
  1893.             return (JlistProc(hwnd, message, wParam, lParam, TRUE, ConvertUser));
  1894.     }
  1895.     return (FALSE);
  1896. }
  1897.