6a1b2bc165ff6165a07984ce6f2583706380d07b
[platform/core/uifw/ise-engine-unikey.git] / ukengine / ukengine.cpp
1 // -*- mode:c++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-
2 /* Unikey Vietnamese Input Method
3  * Copyright (C) 2000-2005 Pham Kim Long
4  * Contact:
5  *   unikey@gmail.com
6  *   UniKey project: http://unikey.org
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <iostream>
27 #include "keycons.h"
28
29 /*
30 #if defined(_WIN32)
31 #include "keyhook.h"
32 #endif
33 */
34
35 #include "vnlexi.h"
36 #include "ukengine.h"
37
38 #include "charset.h"
39
40 using namespace std;
41
42 #define ENTER_CHAR 13
43 #define IS_ODD(x) (x & 1)
44 #define IS_EVEN(x) (!(x & 1))
45
46 #define IS_STD_VN_LOWER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_ODD(x))
47 #define IS_STD_VN_UPPER(x) ((x) >= VnStdCharOffset && (x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && IS_EVEN(x))
48
49 bool IsVnVowel[vnl_lastChar];
50
51 extern VnLexiName AZLexiUpper[]; //defined in inputproc.cpp
52 extern VnLexiName AZLexiLower[];
53
54 //see vnconv/data.cpp for explanation of these characters
55 unsigned char SpecialWesternChars[] = {
56   0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
57   0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93,
58   0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
59   0x9C, 0x9E, 0x9F, 0x00};
60
61 StdVnChar IsoStdVnCharMap[256];
62
63 inline StdVnChar IsoToStdVnChar(int keyCode)
64 {
65     return (keyCode < 256)? IsoStdVnCharMap[keyCode] : keyCode;
66 }
67
68 struct VowelSeqInfo {
69     int len;
70     int complete;
71     int conSuffix; //allow consonnant suffix
72     VnLexiName v[3];
73     VowelSeq sub[3];
74
75     int roofPos;
76     VowelSeq withRoof;
77
78     int hookPos;
79     VowelSeq withHook; //hook & bowl
80 };
81
82 VowelSeqInfo VSeqList[] = {
83     {1, 1, 1, {vnl_a, vnl_nonVnChar, vnl_nonVnChar}, {vs_a, vs_nil, vs_nil}, -1, vs_ar, -1, vs_ab},
84     {1, 1, 1, {vnl_ar, vnl_nonVnChar, vnl_nonVnChar}, {vs_ar, vs_nil, vs_nil}, 0, vs_nil, -1, vs_ab},
85     {1, 1, 1, {vnl_ab, vnl_nonVnChar, vnl_nonVnChar}, {vs_ab, vs_nil, vs_nil}, -1, vs_ar, 0, vs_nil},
86     {1, 1, 1, {vnl_e, vnl_nonVnChar, vnl_nonVnChar}, {vs_e, vs_nil, vs_nil}, -1, vs_er, -1, vs_nil},
87     {1, 1, 1, {vnl_er, vnl_nonVnChar, vnl_nonVnChar}, {vs_er, vs_nil, vs_nil}, 0, vs_nil, -1, vs_nil},
88     {1, 1, 1, {vnl_i, vnl_nonVnChar, vnl_nonVnChar}, {vs_i, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil},
89     {1, 1, 1, {vnl_o, vnl_nonVnChar, vnl_nonVnChar}, {vs_o, vs_nil, vs_nil}, -1, vs_or, -1, vs_oh},
90     {1, 1, 1, {vnl_or, vnl_nonVnChar, vnl_nonVnChar}, {vs_or, vs_nil, vs_nil}, 0, vs_nil, -1, vs_oh},
91     {1, 1, 1, {vnl_oh, vnl_nonVnChar, vnl_nonVnChar}, {vs_oh, vs_nil, vs_nil}, -1, vs_or, 0, vs_nil},
92     {1, 1, 1, {vnl_u, vnl_nonVnChar, vnl_nonVnChar}, {vs_u, vs_nil, vs_nil}, -1, vs_nil, -1, vs_uh},
93     {1, 1, 1, {vnl_uh, vnl_nonVnChar, vnl_nonVnChar}, {vs_uh, vs_nil, vs_nil}, -1, vs_nil, 0, vs_nil},
94     {1, 1, 1, {vnl_y, vnl_nonVnChar, vnl_nonVnChar}, {vs_y, vs_nil, vs_nil}, -1, vs_nil, -1, vs_nil},
95     {2, 1, 0, {vnl_a, vnl_i, vnl_nonVnChar}, {vs_a, vs_ai, vs_nil}, -1, vs_nil, -1, vs_nil},
96     {2, 1, 0, {vnl_a, vnl_o, vnl_nonVnChar}, {vs_a, vs_ao, vs_nil}, -1, vs_nil, -1, vs_nil},
97     {2, 1, 0, {vnl_a, vnl_u, vnl_nonVnChar}, {vs_a, vs_au, vs_nil}, -1, vs_aru, -1, vs_nil},
98     {2, 1, 0, {vnl_a, vnl_y, vnl_nonVnChar}, {vs_a, vs_ay, vs_nil}, -1, vs_ary, -1, vs_nil},
99     {2, 1, 0, {vnl_ar, vnl_u, vnl_nonVnChar}, {vs_ar, vs_aru, vs_nil}, 0, vs_nil, -1, vs_nil},
100     {2, 1, 0, {vnl_ar, vnl_y, vnl_nonVnChar}, {vs_ar, vs_ary, vs_nil}, 0, vs_nil, -1, vs_nil},
101     {2, 1, 0, {vnl_e, vnl_o, vnl_nonVnChar}, {vs_e, vs_eo, vs_nil}, -1, vs_nil, -1, vs_nil},
102     {2, 0, 0, {vnl_e, vnl_u, vnl_nonVnChar}, {vs_e, vs_eu, vs_nil}, -1, vs_eru, -1, vs_nil},
103     {2, 1, 0, {vnl_er, vnl_u, vnl_nonVnChar}, {vs_er, vs_eru, vs_nil}, 0, vs_nil, -1, vs_nil},
104     {2, 1, 0, {vnl_i, vnl_a, vnl_nonVnChar}, {vs_i, vs_ia, vs_nil}, -1, vs_nil, -1, vs_nil},
105     {2, 0, 1, {vnl_i, vnl_e, vnl_nonVnChar}, {vs_i, vs_ie, vs_nil}, -1, vs_ier, -1, vs_nil},
106     {2, 1, 1, {vnl_i, vnl_er, vnl_nonVnChar}, {vs_i, vs_ier, vs_nil}, 1, vs_nil, -1, vs_nil},
107     {2, 1, 0, {vnl_i, vnl_u, vnl_nonVnChar}, {vs_i, vs_iu, vs_nil}, -1, vs_nil, -1, vs_nil},
108     {2, 1, 1, {vnl_o, vnl_a, vnl_nonVnChar}, {vs_o, vs_oa, vs_nil}, -1, vs_nil, -1, vs_oab},
109     {2, 1, 1, {vnl_o, vnl_ab, vnl_nonVnChar}, {vs_o, vs_oab, vs_nil}, -1, vs_nil, 1, vs_nil},
110     {2, 1, 1, {vnl_o, vnl_e, vnl_nonVnChar}, {vs_o, vs_oe, vs_nil}, -1, vs_nil, -1, vs_nil},
111     {2, 1, 0, {vnl_o, vnl_i, vnl_nonVnChar}, {vs_o, vs_oi, vs_nil}, -1, vs_ori, -1, vs_ohi},
112     {2, 1, 0, {vnl_or, vnl_i, vnl_nonVnChar}, {vs_or, vs_ori, vs_nil}, 0, vs_nil, -1, vs_ohi},
113     {2, 1, 0, {vnl_oh, vnl_i, vnl_nonVnChar}, {vs_oh, vs_ohi, vs_nil}, -1, vs_ori, 0, vs_nil},
114     {2, 1, 1, {vnl_u, vnl_a, vnl_nonVnChar}, {vs_u, vs_ua, vs_nil}, -1, vs_uar, -1, vs_uha},
115     {2, 1, 1, {vnl_u, vnl_ar, vnl_nonVnChar}, {vs_u, vs_uar, vs_nil}, 1, vs_nil, -1, vs_nil},
116     {2, 0, 1, {vnl_u, vnl_e, vnl_nonVnChar}, {vs_u, vs_ue, vs_nil}, -1, vs_uer, -1, vs_nil},
117     {2, 1, 1, {vnl_u, vnl_er, vnl_nonVnChar}, {vs_u, vs_uer, vs_nil}, 1, vs_nil, -1, vs_nil},
118     {2, 1, 0, {vnl_u, vnl_i, vnl_nonVnChar}, {vs_u, vs_ui, vs_nil}, -1, vs_nil, -1, vs_uhi},
119     {2, 0, 1, {vnl_u, vnl_o, vnl_nonVnChar}, {vs_u, vs_uo, vs_nil}, -1, vs_uor, -1, vs_uho},
120     {2, 1, 1, {vnl_u, vnl_or, vnl_nonVnChar}, {vs_u, vs_uor, vs_nil}, 1, vs_nil, -1, vs_uoh},
121     {2, 1, 1, {vnl_u, vnl_oh, vnl_nonVnChar}, {vs_u, vs_uoh, vs_nil}, -1, vs_uor, 1, vs_uhoh},
122     {2, 0, 0, {vnl_u, vnl_u, vnl_nonVnChar}, {vs_u, vs_uu, vs_nil}, -1, vs_nil, -1, vs_uhu},
123     {2, 1, 1, {vnl_u, vnl_y, vnl_nonVnChar}, {vs_u, vs_uy, vs_nil}, -1, vs_nil, -1, vs_nil},
124     {2, 1, 0, {vnl_uh, vnl_a, vnl_nonVnChar}, {vs_uh, vs_uha, vs_nil}, -1, vs_nil, 0, vs_nil},
125     {2, 1, 0, {vnl_uh, vnl_i, vnl_nonVnChar}, {vs_uh, vs_uhi, vs_nil}, -1, vs_nil, 0, vs_nil},
126     {2, 0, 1, {vnl_uh, vnl_o, vnl_nonVnChar}, {vs_uh, vs_uho, vs_nil}, -1, vs_nil, 0, vs_uhoh},
127     {2, 1, 1, {vnl_uh, vnl_oh, vnl_nonVnChar}, {vs_uh, vs_uhoh, vs_nil}, -1, vs_nil, 0, vs_nil},
128     {2, 1, 0, {vnl_uh, vnl_u, vnl_nonVnChar}, {vs_uh, vs_uhu, vs_nil}, -1, vs_nil, 0, vs_nil},
129     {2, 0, 1, {vnl_y, vnl_e, vnl_nonVnChar}, {vs_y, vs_ye, vs_nil}, -1, vs_yer, -1, vs_nil},
130     {2, 1, 1, {vnl_y, vnl_er, vnl_nonVnChar}, {vs_y, vs_yer, vs_nil}, 1, vs_nil, -1, vs_nil},
131     {3, 0, 0, {vnl_i, vnl_e, vnl_u}, {vs_i, vs_ie, vs_ieu}, -1, vs_ieru, -1, vs_nil},
132     {3, 1, 0, {vnl_i, vnl_er, vnl_u}, {vs_i, vs_ier, vs_ieru}, 1, vs_nil, -1, vs_nil},
133     {3, 1, 0, {vnl_o, vnl_a, vnl_i}, {vs_o, vs_oa, vs_oai}, -1, vs_nil, -1, vs_nil},
134     {3, 1, 0, {vnl_o, vnl_a, vnl_y}, {vs_o, vs_oa, vs_oay}, -1, vs_nil, -1, vs_nil},  
135     {3, 1, 0, {vnl_o, vnl_e, vnl_o}, {vs_o, vs_oe, vs_oeo}, -1, vs_nil, -1, vs_nil},
136     {3, 0, 0, {vnl_u, vnl_a, vnl_y}, {vs_u, vs_ua, vs_uay}, -1, vs_uary, -1, vs_nil},
137     {3, 1, 0, {vnl_u, vnl_ar, vnl_y}, {vs_u, vs_uar, vs_uary}, 1, vs_nil, -1, vs_nil},
138     {3, 0, 0, {vnl_u, vnl_o, vnl_i}, {vs_u, vs_uo, vs_uoi}, -1, vs_uori, -1, vs_uhoi},
139     {3, 0, 0, {vnl_u, vnl_o, vnl_u}, {vs_u, vs_uo, vs_uou}, -1, vs_nil, -1, vs_uhou},
140     {3, 1, 0, {vnl_u, vnl_or, vnl_i}, {vs_u, vs_uor, vs_uori}, 1, vs_nil, -1, vs_uohi},
141     {3, 0, 0, {vnl_u, vnl_oh, vnl_i}, {vs_u, vs_uoh, vs_uohi}, -1, vs_uori, 1, vs_uhohi},
142     {3, 0, 0, {vnl_u, vnl_oh, vnl_u}, {vs_u, vs_uoh, vs_uohu}, -1, vs_nil, 1, vs_uhohu},
143     {3, 1, 0, {vnl_u, vnl_y, vnl_a}, {vs_u, vs_uy, vs_uya}, -1, vs_nil, -1, vs_nil},
144     {3, 0, 1, {vnl_u, vnl_y, vnl_e}, {vs_u, vs_uy, vs_uye}, -1, vs_uyer, -1, vs_nil},
145     {3, 1, 1, {vnl_u, vnl_y, vnl_er}, {vs_u, vs_uy, vs_uyer}, 2, vs_nil, -1, vs_nil},
146     {3, 1, 0, {vnl_u, vnl_y, vnl_u}, {vs_u, vs_uy, vs_uyu}, -1, vs_nil, -1, vs_nil},
147     {3, 0, 0, {vnl_uh, vnl_o, vnl_i}, {vs_uh, vs_uho, vs_uhoi}, -1, vs_nil, 0, vs_uhohi},
148     {3, 0, 0, {vnl_uh, vnl_o, vnl_u}, {vs_uh, vs_uho, vs_uhou}, -1, vs_nil, 0, vs_uhohu},
149     {3, 1, 0, {vnl_uh, vnl_oh, vnl_i}, {vs_uh, vs_uhoh, vs_uhohi}, -1, vs_nil, 0, vs_nil},
150     {3, 1, 0, {vnl_uh, vnl_oh, vnl_u}, {vs_uh, vs_uhoh, vs_uhohu}, -1, vs_nil, 0, vs_nil},
151     {3, 0, 0, {vnl_y, vnl_e, vnl_u}, {vs_y, vs_ye, vs_yeu}, -1, vs_yeru, -1, vs_nil},
152     {3, 1, 0, {vnl_y, vnl_er, vnl_u}, {vs_y, vs_yer, vs_yeru}, 1, vs_nil, -1, vs_nil}
153 };
154
155 struct ConSeqInfo {
156     int len;
157     VnLexiName c[3];
158     bool suffix;
159 };
160
161 ConSeqInfo CSeqList[] = {
162     {1, {vnl_b, vnl_nonVnChar, vnl_nonVnChar}, false},
163     {1, {vnl_c, vnl_nonVnChar, vnl_nonVnChar}, true},
164     {2, {vnl_c, vnl_h, vnl_nonVnChar}, true},
165     {1, {vnl_d, vnl_nonVnChar, vnl_nonVnChar}, false},
166     {1, {vnl_dd, vnl_nonVnChar, vnl_nonVnChar}, false},
167     {2, {vnl_d, vnl_z, vnl_nonVnChar}, false},
168     {1, {vnl_g, vnl_nonVnChar, vnl_nonVnChar}, false},
169     {2, {vnl_g, vnl_h, vnl_nonVnChar}, false},
170     {2, {vnl_g, vnl_i, vnl_nonVnChar}, false},
171     {3, {vnl_g, vnl_i, vnl_n}, false},
172     {1, {vnl_h, vnl_nonVnChar, vnl_nonVnChar}, false},
173     {1, {vnl_k, vnl_nonVnChar, vnl_nonVnChar}, false},
174     {2, {vnl_k, vnl_h, vnl_nonVnChar}, false},
175     {1, {vnl_l, vnl_nonVnChar, vnl_nonVnChar}, false},
176     {1, {vnl_m, vnl_nonVnChar, vnl_nonVnChar}, true},
177     {1, {vnl_n, vnl_nonVnChar, vnl_nonVnChar}, true},
178     {2, {vnl_n, vnl_g, vnl_nonVnChar}, true},
179     {3, {vnl_n, vnl_g, vnl_h}, false},
180     {2, {vnl_n, vnl_h, vnl_nonVnChar}, true},
181     {1, {vnl_p, vnl_nonVnChar, vnl_nonVnChar}, true},
182     {2, {vnl_p, vnl_h, vnl_nonVnChar}, false},
183     {1, {vnl_q, vnl_nonVnChar, vnl_nonVnChar}, false},
184     {2, {vnl_q, vnl_u, vnl_nonVnChar}, false},
185     {1, {vnl_r, vnl_nonVnChar, vnl_nonVnChar}, false},
186     {1, {vnl_s, vnl_nonVnChar, vnl_nonVnChar}, false},
187     {1, {vnl_t, vnl_nonVnChar, vnl_nonVnChar}, true},
188     {2, {vnl_t, vnl_h, vnl_nonVnChar}, false},
189     {2, {vnl_t, vnl_r, vnl_nonVnChar}, false},
190     {1, {vnl_v, vnl_nonVnChar, vnl_nonVnChar}, false},
191     {1, {vnl_x, vnl_nonVnChar, vnl_nonVnChar}, false}
192 };
193
194 const int VSeqCount = sizeof(VSeqList)/sizeof(VowelSeqInfo);
195 struct VSeqPair {
196     VnLexiName v[3];
197     VowelSeq vs;
198 };
199 VSeqPair SortedVSeqList[VSeqCount];
200
201 const int CSeqCount = sizeof(CSeqList)/sizeof(ConSeqInfo);
202 struct CSeqPair {
203     VnLexiName c[3];
204     ConSeq cs;
205 };
206 CSeqPair SortedCSeqList[CSeqCount];
207
208 struct VCPair {
209     VowelSeq v;
210     ConSeq c;
211 };
212
213 VCPair VCPairList [] = {
214   {vs_a, cs_c}, {vs_a, cs_ch}, {vs_a, cs_m}, {vs_a, cs_n}, {vs_a, cs_ng},
215                 {vs_a, cs_nh}, {vs_a, cs_p}, {vs_a, cs_t},
216   {vs_ar, cs_c}, {vs_ar, cs_m}, {vs_ar, cs_n}, {vs_ar, cs_ng}, {vs_ar, cs_p}, {vs_ar, cs_t},
217   {vs_ab, cs_c}, {vs_ab, cs_m}, {vs_ab, cs_n}, {vs_ab, cs_ng}, {vs_ab, cs_p}, {vs_ab, cs_t},
218
219   {vs_e, cs_c}, {vs_e, cs_ch}, {vs_e, cs_m}, {vs_e, cs_n}, {vs_e, cs_ng},
220                 {vs_e, cs_nh}, {vs_e, cs_p}, {vs_e, cs_t},
221   {vs_er, cs_c}, {vs_er, cs_ch}, {vs_er, cs_m}, {vs_er, cs_n}, {vs_er, cs_nh},
222                 {vs_er, cs_p}, {vs_er, cs_t},
223
224   {vs_i, cs_c}, {vs_i, cs_ch}, {vs_i, cs_m}, {vs_i, cs_n}, {vs_i, cs_nh}, {vs_i, cs_p}, {vs_i, cs_t},
225
226   {vs_o, cs_c}, {vs_o, cs_m}, {vs_o, cs_n}, {vs_o, cs_ng}, {vs_o, cs_p}, {vs_o, cs_t},
227   {vs_or, cs_c}, {vs_or, cs_m}, {vs_or, cs_n}, {vs_or, cs_ng}, {vs_or, cs_p}, {vs_or, cs_t},
228   {vs_oh, cs_m}, {vs_oh, cs_n}, {vs_oh, cs_p}, {vs_oh, cs_t},
229
230   {vs_u, cs_c}, {vs_u, cs_m}, {vs_u, cs_n}, {vs_u, cs_ng}, {vs_u, cs_p}, {vs_u, cs_t},
231   {vs_uh, cs_c}, {vs_uh, cs_m}, {vs_uh, cs_n}, {vs_uh, cs_ng}, {vs_uh, cs_t},
232
233   {vs_y, cs_t},
234   {vs_ie, cs_c}, {vs_ie, cs_m}, {vs_ie, cs_n}, {vs_ie, cs_ng}, {vs_ie, cs_p}, {vs_ie, cs_t},
235   {vs_ier, cs_c}, {vs_ier, cs_m}, {vs_ier, cs_n}, {vs_ier, cs_ng}, {vs_ier, cs_p}, {vs_ier, cs_t},
236
237   {vs_oa, cs_c}, {vs_oa, cs_ch}, {vs_oa, cs_m}, {vs_oa, cs_n}, {vs_oa, cs_ng},
238                  {vs_oa, cs_nh}, {vs_oa, cs_p}, {vs_oa, cs_t},
239   {vs_oab, cs_c}, {vs_oab, cs_m}, {vs_oab, cs_n}, {vs_oab, cs_ng}, {vs_oab, cs_t},
240
241   {vs_oe, cs_n}, {vs_oe, cs_t},
242
243   {vs_ua, cs_n}, {vs_ua, cs_ng}, {vs_ua, cs_t},
244   {vs_uar, cs_n}, {vs_uar, cs_ng}, {vs_uar, cs_t},
245
246   {vs_ue, cs_c}, {vs_ue, cs_ch}, {vs_ue, cs_n}, {vs_ue, cs_nh},
247   {vs_uer, cs_c}, {vs_uer, cs_ch}, {vs_uer, cs_n}, {vs_uer, cs_nh},
248
249   {vs_uo, cs_c}, {vs_uo, cs_m}, {vs_uo, cs_n}, {vs_uo, cs_ng}, {vs_uo, cs_p}, {vs_uo, cs_t},
250   {vs_uor, cs_c}, {vs_uor, cs_m}, {vs_uor, cs_n}, {vs_uor, cs_ng}, {vs_uor, cs_t},
251   {vs_uho, cs_c}, {vs_uho, cs_m}, {vs_uho, cs_n}, {vs_uho, cs_ng}, {vs_uho, cs_p}, {vs_uho, cs_t},
252   {vs_uhoh, cs_c}, {vs_uhoh, cs_m}, {vs_uhoh, cs_n}, {vs_uhoh, cs_ng}, {vs_uhoh, cs_p}, {vs_uhoh, cs_t},
253
254   {vs_uy, cs_c}, {vs_uy, cs_ch}, {vs_uy, cs_n}, {vs_uy, cs_nh}, {vs_uy, cs_p}, {vs_uy, cs_t},
255
256   {vs_ye, cs_m}, {vs_ye, cs_n}, {vs_ye, cs_ng}, {vs_ye, cs_p}, {vs_ye, cs_t},
257   {vs_yer, cs_m}, {vs_yer, cs_n}, {vs_yer, cs_ng}, {vs_yer, cs_t},
258
259   {vs_uye, cs_n}, {vs_uye, cs_t},
260   {vs_uyer, cs_n}, {vs_uyer, cs_t}
261
262 };
263
264 const int VCPairCount = sizeof(VCPairList)/sizeof(VCPair);
265
266 //TODO: auto-complete: e.g. luan -> lua^n
267
268 typedef int (UkEngine::* UkKeyProc)(UkKeyEvent & ev);
269
270 UkKeyProc UkKeyProcList[vneCount] = {
271     &UkEngine::processRoof,    //vneRoofAll
272     &UkEngine::processRoof,    //vneRoof_a
273     &UkEngine::processRoof,    //vneRoof_e
274     &UkEngine::processRoof,    //vneRoof_o
275     &UkEngine::processHook,    //vneHookAll
276     &UkEngine::processHook,    //vneHook_uo
277     &UkEngine::processHook,    //vneHook_u
278     &UkEngine::processHook,    //vneHook_o
279     &UkEngine::processHook,    //vneBowl
280     &UkEngine::processDd,      //vneDd
281     &UkEngine::processTone,    //vneTone0
282     &UkEngine::processTone,    //vneTone1
283     &UkEngine::processTone,    //vneTone2
284     &UkEngine::processTone,    //vneTone3
285     &UkEngine::processTone,    //vneTone4
286     &UkEngine::processTone,    //vneTone5
287     &UkEngine::processTelexW,  //vne_telex_w
288     &UkEngine::processMapChar, //vneMapChar
289     &UkEngine::processEscChar, //vneEscChar
290     &UkEngine::processAppend   //vneNormal
291 };
292
293
294 VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2 = vnl_nonVnChar, VnLexiName v3 = vnl_nonVnChar);
295 ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2 = vnl_nonVnChar, VnLexiName c3 = vnl_nonVnChar);
296
297 bool UkEngine::m_classInit = false;
298
299 //------------------------------------------------
300 int tripleVowelCompare(const void *p1, const void *p2)
301 {
302     VSeqPair *t1 = (VSeqPair *)p1;
303     VSeqPair *t2 = (VSeqPair *)p2;
304
305     for (int i=0; i<3; i++) {
306         if (t1->v[i] < t2->v[i])
307             return -1;
308         if (t1->v[i] > t2->v[i])
309             return 1;
310     }
311     return 0;
312 }
313
314 //------------------------------------------------
315 int tripleConCompare(const void *p1, const void *p2)
316 {
317     CSeqPair *t1 = (CSeqPair *)p1;
318     CSeqPair *t2 = (CSeqPair *)p2;
319
320     for (int i=0; i<3; i++) {
321         if (t1->c[i] < t2->c[i])
322             return -1;
323         if (t1->c[i] > t2->c[i])
324             return 1;
325     }
326     return 0;
327 }
328
329 //------------------------------------------------
330 int VCPairCompare(const void *p1, const void *p2)
331 {
332     VCPair *t1 = (VCPair *)p1;
333     VCPair *t2 = (VCPair *)p2;
334
335     if (t1->v < t2->v)
336         return -1;
337     if (t1->v > t2->v)
338       return 1;
339   
340     if (t1->c < t2->c)
341         return -1;
342     if (t1->c > t2->c)
343         return 1;
344     return 0;
345 }
346
347 //----------------------------------------------------------
348 bool isValidCV(ConSeq c, VowelSeq v)
349 {
350     if (c == cs_nil || v == vs_nil)
351         return true;
352
353     VowelSeqInfo & vInfo = VSeqList[v];
354
355     if ((c == cs_gi && vInfo.v[0] == vnl_i) ||
356         (c == cs_qu && vInfo.v[0] == vnl_u))
357         return false; // gi doesn't go with i, qu doesn't go with u
358   
359     if (c == cs_k) {
360         // k can only go with the following vowel sequences
361         static VowelSeq kVseq[] = {vs_e, vs_i, vs_y, vs_er, vs_eo, vs_eu, 
362                                    vs_eru, vs_ia, vs_ie, vs_ier, vs_ieu, vs_ieru, vs_nil};
363         int i;
364         for (i=0; kVseq[i] != vs_nil && kVseq[i] != v; i++);
365         return (kVseq[i] != vs_nil);
366     }
367
368     //More checks
369     return true;
370 }
371
372 //----------------------------------------------------------
373 bool isValidVC(VowelSeq v, ConSeq c)
374 {
375     if (v == vs_nil || c == cs_nil)
376         return true;
377
378     VowelSeqInfo & vInfo = VSeqList[v];
379     if (!vInfo.conSuffix)
380         return false;
381
382     ConSeqInfo & cInfo = CSeqList[c];
383     if (!cInfo.suffix)
384         return false;
385
386     VCPair p;
387     p.v = v;
388     p.c = c;
389     if (bsearch(&p, VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare))
390         return true;
391
392     return false;
393 }
394
395 //----------------------------------------------------------
396 bool isValidCVC(ConSeq c1, VowelSeq v, ConSeq c2)
397 {
398     if (v == vs_nil)
399         return (c1 == cs_nil || c2 != cs_nil);
400
401     if (c1 == cs_nil)
402         return isValidVC(v, c2);
403
404     if (c2 == cs_nil)
405         return isValidCV(c1, v);
406
407     bool okCV = isValidCV(c1, v);
408     bool okVC = isValidVC(v, c2);
409
410     if (okCV && okVC)
411         return true;
412
413     if (!okVC) {
414         //check some exceptions: vc fails but cvc passes
415
416         // quyn, quynh
417         if (c1 == cs_qu && v == vs_y && (c2 == cs_n || c2 == cs_nh))
418             return true;
419
420         // gieng, gie^ng
421         if (c1 == cs_gi && (v == vs_e || v == vs_er) && (c2 == cs_n || c2 == cs_ng))
422             return true;
423     }
424     return false;
425 }
426
427 //------------------------------------------------
428 void engineClassInit()
429 {
430     int i, j;
431
432     for (i=0; i < VSeqCount; i++) {
433         for (j=0; j<3; j++)
434             SortedVSeqList[i].v[j] = VSeqList[i].v[j];
435         SortedVSeqList[i].vs = (VowelSeq)i;
436     }
437
438     for (i=0; i < CSeqCount; i++) {
439         for (j=0; j<3; j++)
440             SortedCSeqList[i].c[j] = CSeqList[i].c[j];
441         SortedCSeqList[i].cs = (ConSeq)i;
442     }
443
444     qsort(SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare);
445     qsort(SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare);
446     qsort(VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare);
447
448     for (i=0; i<vnl_lastChar; i++)
449         IsVnVowel[i] = true;
450
451     unsigned char ch;
452     for (ch='a'; ch <= 'z'; ch++) {
453         if (ch != 'a' && ch != 'e' && ch != 'i' &&
454             ch != 'o' && ch != 'u' && ch != 'y') {
455             IsVnVowel[AZLexiLower[ch-'a']] = false;
456             IsVnVowel[AZLexiUpper[ch-'a']] = false;
457         }
458     }
459     IsVnVowel[vnl_dd] = false;
460     IsVnVowel[vnl_DD] = false;
461 }
462
463 //------------------------------------------------
464 VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2, VnLexiName v3)
465 {
466     VSeqPair key;
467     key.v[0] = v1;
468     key.v[1] = v2;
469     key.v[2] = v3;
470
471     VSeqPair *pInfo = (VSeqPair *)bsearch(&key, SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare);
472     if (pInfo == 0)
473         return vs_nil;
474     return pInfo->vs;
475 }
476
477 //------------------------------------------------
478 ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2, VnLexiName c3)
479 {
480     CSeqPair key;
481     key.c[0] = c1;
482     key.c[1] = c2;
483     key.c[2] = c3;
484
485     CSeqPair *pInfo = (CSeqPair *)bsearch(&key, SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare);
486     if (pInfo == 0)
487         return cs_nil;
488     return pInfo->cs;
489 }
490
491 //------------------------------------------------------------------
492 int UkEngine::processRoof(UkKeyEvent & ev)
493 {
494     if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0)
495         return processAppend(ev);
496
497     VnLexiName target;
498     switch (ev.evType) {
499     case vneRoof_a:
500         target = vnl_ar;
501         break;
502     case vneRoof_e:
503         target = vnl_er;
504         break;
505     case vneRoof_o:
506         target = vnl_or;
507         break;
508     default:
509         target = vnl_nonVnChar;
510     }
511
512
513     VowelSeq vs, newVs;
514     int i, vStart, vEnd;
515     int curTonePos, newTonePos, tone;
516     int changePos;
517     bool roofRemoved = false;
518
519     vEnd = m_current - m_buffer[m_current].vOffset;
520     vs = m_buffer[vEnd].vseq;
521     vStart = vEnd - (VSeqList[vs].len - 1);
522     curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
523     tone = m_buffer[curTonePos].tone;
524
525     bool doubleChangeUO = false;
526     if (vs == vs_uho || vs == vs_uhoh || vs == vs_uhoi || vs == vs_uhohi) {
527         //special cases: u+o+ -> uo^, u+o -> uo^, u+o+i -> uo^i, u+oi -> uo^i
528         newVs = lookupVSeq(vnl_u, vnl_or, VSeqList[vs].v[2]);
529         doubleChangeUO = true;
530     }
531     else {
532         newVs = VSeqList[vs].withRoof;
533     }
534
535     VowelSeqInfo *pInfo;
536
537     if (newVs == vs_nil) {
538         if (VSeqList[vs].roofPos == -1)
539             return processAppend(ev); //roof is not applicable
540     
541         //a roof already exists -> undo roof
542         VnLexiName curCh = m_buffer[vStart + VSeqList[vs].roofPos].vnSym;
543         if (target != vnl_nonVnChar && curCh != target)
544             return processAppend(ev); //specific roof and the roof character don't match
545
546         VnLexiName newCh = (curCh == vnl_ar)? vnl_a : ((curCh == vnl_er)? vnl_e : vnl_o);
547         changePos = vStart + VSeqList[vs].roofPos;
548
549         if (!m_pCtrl->options.freeMarking && changePos != m_current)
550             return processAppend(ev);
551
552         markChange(changePos);
553         m_buffer[changePos].vnSym = newCh;
554
555         if (VSeqList[vs].len == 3)
556             newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart+1].vnSym, m_buffer[vStart+2].vnSym);
557         else if (VSeqList[vs].len == 2)
558             newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart+1].vnSym);
559         else
560             newVs = lookupVSeq(m_buffer[vStart].vnSym);
561
562         pInfo = &VSeqList[newVs];
563         roofRemoved = true;
564     }
565     else {
566         pInfo = &VSeqList[newVs];
567         if (target != vnl_nonVnChar &&  pInfo->v[pInfo->roofPos] != target)
568             return processAppend(ev);
569
570         //check validity of new VC and CV
571         bool valid = true;
572         ConSeq c1 = cs_nil;
573         ConSeq c2 = cs_nil;
574         if (m_buffer[m_current].c1Offset != -1)
575             c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
576         
577         if (m_buffer[m_current].c2Offset != -1)
578             c2 = m_buffer[m_current-m_buffer[m_current].c2Offset].cseq;
579
580         valid = isValidCVC(c1, newVs, c2);
581         if (!valid)
582             return processAppend(ev);
583
584         if (doubleChangeUO) {
585             changePos = vStart;
586         }
587         else {
588             changePos = vStart + pInfo->roofPos;
589         }
590         if (!m_pCtrl->options.freeMarking && changePos != m_current)
591             return processAppend(ev);
592         markChange(changePos);
593         if (doubleChangeUO) {
594             m_buffer[vStart].vnSym = vnl_u;
595             m_buffer[vStart+1].vnSym = vnl_or;
596         }
597         else {
598             m_buffer[changePos].vnSym = pInfo->v[pInfo->roofPos];
599         }
600     }
601
602     for (i=0; i < pInfo->len; i++) { //update sub-sequences
603         m_buffer[vStart+i].vseq = pInfo->sub[i];
604     }
605
606     //check if tone re-position is needed
607     newTonePos = vStart + getTonePosition(newVs, vEnd == m_current);
608     /* //For now, users don't seem to like the following processing, thus commented out
609     if (roofRemoved && tone != 0 &&
610         (!pInfo->complete || changePos == curTonePos)) {
611         //remove tone if the vowel sequence becomes incomplete as a result of roof removal OR
612         //if removed roof is at the same position as the current tone
613         markChange(curTonePos);
614         m_buffer[curTonePos].tone = 0;
615     } else
616     */
617     if (curTonePos != newTonePos && tone != 0) {
618         markChange(newTonePos);
619         m_buffer[newTonePos].tone = tone;
620         markChange(curTonePos);
621         m_buffer[curTonePos].tone = 0;
622     }
623
624     if (roofRemoved) {
625         m_singleMode = false;
626         processAppend(ev);
627         m_reverted = true;
628     }
629
630     return 1;
631 }
632
633 //------------------------------------------------------------------
634 // can only be called from processHook
635 //------------------------------------------------------------------
636 int UkEngine::processHookWithUO(UkKeyEvent & ev)
637 {
638     VowelSeq vs, newVs;
639     int i, vStart, vEnd;
640     int curTonePos, newTonePos, tone;
641     bool hookRemoved = false;
642     bool removeWithUndo = true;
643     bool toneRemoved = false;
644
645     VnLexiName *v;
646
647     if (!m_pCtrl->options.freeMarking && m_buffer[m_current].vOffset != 0)
648         return processAppend(ev);    
649
650     vEnd = m_current - m_buffer[m_current].vOffset;
651     vs = m_buffer[vEnd].vseq;
652     vStart = vEnd - (VSeqList[vs].len - 1);
653     v = VSeqList[vs].v;
654     curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
655     tone = m_buffer[curTonePos].tone;
656
657     switch (ev.evType) {
658     case vneHook_u:
659         if (v[0] == vnl_u) {
660             newVs = VSeqList[vs].withHook;
661             markChange(vStart);
662             m_buffer[vStart].vnSym = vnl_uh;
663         }
664         else {// v[0] = vnl_uh, -> uo
665             newVs = lookupVSeq(vnl_u, vnl_o, v[2]);
666             markChange(vStart);
667             m_buffer[vStart].vnSym = vnl_u;
668             m_buffer[vStart+1].vnSym = vnl_o;
669             hookRemoved = true;
670             toneRemoved =  (m_buffer[vStart].tone != 0);
671         }
672         break;
673     case vneHook_o:
674         if (v[1] == vnl_o || v[1] == vnl_or) {
675             if (vEnd == m_current && VSeqList[vs].len == 2 && 
676                 m_buffer[m_current].form == vnw_cv && m_buffer[m_current-2].cseq == cs_th)
677             {
678                 // o|o^ -> o+
679                 newVs = VSeqList[vs].withHook;
680                 markChange(vStart+1);
681                 m_buffer[vStart+1].vnSym = vnl_oh;
682             }
683             else {
684                 newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]);
685                 if (v[0] == vnl_u) {
686                     markChange(vStart);
687                     m_buffer[vStart].vnSym = vnl_uh;
688                     m_buffer[vStart+1].vnSym = vnl_oh;
689                 }
690                 else {
691                     markChange(vStart+1);
692                     m_buffer[vStart+1].vnSym = vnl_oh;
693                 }
694             }
695         }
696         else {// v[1] = vnl_oh, -> uo
697             newVs = lookupVSeq(vnl_u, vnl_o, v[2]);
698             if (v[0] == vnl_uh) {
699                 markChange(vStart);
700                 m_buffer[vStart].vnSym = vnl_u;
701                 m_buffer[vStart+1].vnSym = vnl_o;
702             }
703             else {
704                 markChange(vStart+1);
705                 m_buffer[vStart+1].vnSym = vnl_o;
706             }
707             hookRemoved = true;
708             toneRemoved = (m_buffer[vStart+1].tone != 0);
709         }
710         break;
711     default:  //vneHookAll, vneHookUO:
712         if (v[0] == vnl_u) {
713             if (v[1] == vnl_o || v[1] == vnl_or) { 
714                 //uo -> uo+ if prefixed by "th"
715                 if ((vs == vs_uo || vs == vs_uor) && vEnd == m_current && 
716                     m_buffer[m_current].form == vnw_cv && m_buffer[m_current-2].cseq == cs_th) 
717                 {
718                     newVs = vs_uoh;
719                     markChange(vStart+1);
720                     m_buffer[vStart+1].vnSym = vnl_oh;
721                 }
722                 else {
723                     //uo -> u+o+
724                     newVs = VSeqList[vs].withHook;
725                     markChange(vStart);
726                     m_buffer[vStart].vnSym = vnl_uh;
727                     newVs = VSeqList[newVs].withHook;
728                     m_buffer[vStart+1].vnSym = vnl_oh;
729                 }
730             }
731             else {//uo+ -> u+o+
732                 newVs = VSeqList[vs].withHook;
733                 markChange(vStart);
734                 m_buffer[vStart].vnSym = vnl_uh;
735             }
736         }
737         else {//v[0] == vnl_uh
738             if (v[1] == vnl_o) { // u+o -> u+o+
739                 newVs = VSeqList[vs].withHook;
740                 markChange(vStart+1);
741                 m_buffer[vStart+1].vnSym = vnl_oh;
742             }
743             else { //v[1] == vnl_oh, u+o+ -> uo
744                 newVs = lookupVSeq(vnl_u, vnl_o, v[2]); //vs_uo;
745                 markChange(vStart);
746                 m_buffer[vStart].vnSym = vnl_u;
747                 m_buffer[vStart+1].vnSym = vnl_o;
748                 hookRemoved = true;
749                 toneRemoved = (m_buffer[vStart].tone != 0 || m_buffer[vStart+1].tone != 0);
750             }
751         }
752         break;
753     }
754
755     VowelSeqInfo *p = &VSeqList[newVs];
756     for (i=0; i < p->len; i++) { //update sub-sequences
757         m_buffer[vStart+i].vseq = p->sub[i];
758     }
759
760     //check if tone re-position is needed
761     newTonePos = vStart + getTonePosition(newVs, vEnd == m_current);
762     /* //For now, users don't seem to like the following processing, thus commented out
763     if (hookRemoved && tone != 0 && (!p->complete || toneRemoved)) {
764         //remove tone if the vowel sequence becomes incomplete as a result of hook removal
765         //OR if a removed hook is at the same position as the current tone
766         markChange(curTonePos);
767         m_buffer[curTonePos].tone = 0;
768     }
769     else 
770     */
771     if (curTonePos != newTonePos && tone != 0) {
772         markChange(newTonePos);
773         m_buffer[newTonePos].tone = tone;
774         markChange(curTonePos);
775         m_buffer[curTonePos].tone = 0;
776     }
777
778     if (hookRemoved && removeWithUndo) {
779         m_singleMode = false;
780         processAppend(ev);
781         m_reverted = true;
782     }
783
784     return 1;
785 }
786
787 //------------------------------------------------------------------
788 int UkEngine::processHook(UkKeyEvent & ev)
789 {
790     if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0)
791         return processAppend(ev);
792
793     VowelSeq vs, newVs;
794     int i, vStart, vEnd;
795     int curTonePos, newTonePos, tone;
796     int changePos;
797     bool hookRemoved = false;
798     VowelSeqInfo *pInfo;
799     VnLexiName *v;
800
801     vEnd = m_current - m_buffer[m_current].vOffset;
802     vs = m_buffer[vEnd].vseq;
803
804     v = VSeqList[vs].v;
805   
806     if (VSeqList[vs].len > 1 && 
807         ev.evType != vneBowl &&
808         (v[0] == vnl_u || v[0] == vnl_uh) &&
809         (v[1] == vnl_o || v[1] == vnl_oh || v[1] == vnl_or))
810         return processHookWithUO(ev);
811
812     vStart = vEnd - (VSeqList[vs].len - 1);
813     curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
814     tone = m_buffer[curTonePos].tone;
815
816     newVs = VSeqList[vs].withHook;
817     if (newVs == vs_nil) {
818         if (VSeqList[vs].hookPos == -1)
819             return processAppend(ev); //hook is not applicable
820
821         //a hook already exists -> undo hook
822         VnLexiName curCh = m_buffer[vStart + VSeqList[vs].hookPos].vnSym;
823         VnLexiName newCh = (curCh == vnl_ab)? vnl_a : ((curCh == vnl_uh)? vnl_u : vnl_o);
824         changePos = vStart + VSeqList[vs].hookPos;
825         if (!m_pCtrl->options.freeMarking && changePos != m_current)
826             return processAppend(ev);
827
828         switch (ev.evType) {
829         case vneHook_u:
830             if (curCh != vnl_uh)
831                 return processAppend(ev);
832             break;
833         case vneHook_o:
834             if (curCh != vnl_oh)
835                 return processAppend(ev);
836             break;
837         case vneBowl:
838             if (curCh != vnl_ab)
839                 return processAppend(ev);
840             break;
841         default:
842             if (ev.evType == vneHook_uo && curCh == vnl_ab)
843                 return processAppend(ev);
844         }
845
846         markChange(changePos);
847         m_buffer[changePos].vnSym = newCh;
848
849         if (VSeqList[vs].len == 3)
850             newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart+1].vnSym, m_buffer[vStart+2].vnSym);
851         else if (VSeqList[vs].len == 2)
852             newVs = lookupVSeq(m_buffer[vStart].vnSym, m_buffer[vStart+1].vnSym);
853         else
854             newVs = lookupVSeq(m_buffer[vStart].vnSym);
855
856         pInfo = &VSeqList[newVs];
857         hookRemoved = true;
858     }
859     else {
860         pInfo = &VSeqList[newVs];
861
862         switch (ev.evType) {
863         case vneHook_u:
864             if (pInfo->v[pInfo->hookPos] != vnl_uh)
865                 return processAppend(ev);
866             break;
867         case vneHook_o:
868             if (pInfo->v[pInfo->hookPos] != vnl_oh)
869                 return processAppend(ev);
870             break;
871         case vneBowl:
872             if (pInfo->v[pInfo->hookPos] != vnl_ab)
873                 return processAppend(ev);
874             break;
875         default: //vneHook_uo, vneHookAll
876             if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab)
877                 return processAppend(ev);
878         }
879
880         //check validity of new VC and CV
881         bool valid = true;
882         ConSeq c1 = cs_nil;
883         ConSeq c2 = cs_nil;
884         if (m_buffer[m_current].c1Offset != -1)
885             c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
886         
887         if (m_buffer[m_current].c2Offset != -1)
888             c2 = m_buffer[m_current-m_buffer[m_current].c2Offset].cseq;
889
890         valid = isValidCVC(c1, newVs, c2);
891
892         if (!valid)
893             return processAppend(ev);
894
895         changePos = vStart + pInfo->hookPos;
896         if (!m_pCtrl->options.freeMarking && changePos != m_current)
897             return processAppend(ev);
898
899         markChange(changePos);
900         m_buffer[changePos].vnSym = pInfo->v[pInfo->hookPos];
901     }
902    
903     for (i=0; i < pInfo->len; i++) { //update sub-sequences
904         m_buffer[vStart+i].vseq = pInfo->sub[i];
905     }
906
907     //check if tone re-position is needed
908     newTonePos = vStart + getTonePosition(newVs, vEnd == m_current);
909     /* //For now, users don't seem to like the following processing, thus commented out
910     if (hookRemoved && tone != 0 && 
911         (!pInfo->complete || (hookRemoved && curTonePos == changePos))) {
912         //remove tone if the vowel sequence becomes incomplete as a result of hook removal
913         //OR if a removed hook was at the same position as the current tone
914         markChange(curTonePos);
915         m_buffer[curTonePos].tone = 0;
916     }
917     else */
918     if (curTonePos != newTonePos && tone != 0) {
919         markChange(newTonePos);
920         m_buffer[newTonePos].tone = tone;
921         markChange(curTonePos);
922         m_buffer[curTonePos].tone = 0;
923     }
924
925     if (hookRemoved) {
926         m_singleMode = false;
927         processAppend(ev);
928         m_reverted = true;
929     }
930
931     return 1;
932 }
933
934 //----------------------------------------------------------
935 int UkEngine::getTonePosition(VowelSeq vs, bool terminated)
936 {
937     VowelSeqInfo & info = VSeqList[vs];
938     if (info.len == 1)
939         return 0;
940
941     if (info.roofPos != -1)
942         return info.roofPos;
943     if (info.hookPos != -1) {
944         if (vs == vs_uhoh || vs == vs_uhohi || vs == vs_uhohu) //u+o+, u+o+u, u+o+i
945             return 1;
946         return info.hookPos;
947     }
948   
949     if (info.len == 3)
950         return 1;
951
952     if (m_pCtrl->options.modernStyle &&
953         (vs == vs_oa || vs == vs_oe ||vs == vs_uy))
954         return 1;
955
956     return terminated ? 0 : 1;
957 }
958
959 //----------------------------------------------------------
960 int UkEngine::processTone(UkKeyEvent & ev)
961 {
962     if (m_current < 0 || !m_pCtrl->vietKey)
963         return processAppend(ev);
964
965     if (m_buffer[m_current].form == vnw_c && 
966         (m_buffer[m_current].cseq == cs_gi || m_buffer[m_current].cseq == cs_gin)) {
967         int p = (m_buffer[m_current].cseq == cs_gi)? m_current : m_current - 1;
968         if (m_buffer[p].tone == 0 && ev.tone == 0)
969             return processAppend(ev);
970         markChange(p);
971         if (m_buffer[p].tone == ev.tone) {
972             m_buffer[p].tone = 0;
973             m_singleMode = false;
974             processAppend(ev);
975             m_reverted = true;
976             return 1;
977         }
978         m_buffer[p].tone = ev.tone;
979         return 1;
980     }
981
982     if (m_buffer[m_current].vOffset < 0)
983         return processAppend(ev);
984
985     int vEnd;
986     VowelSeq vs;
987
988     vEnd = m_current - m_buffer[m_current].vOffset;
989     vs = m_buffer[vEnd].vseq;
990     VowelSeqInfo & info = VSeqList[vs];
991     if (m_pCtrl->options.spellCheckEnabled && !m_pCtrl->options.freeMarking && !info.complete)
992         return processAppend(ev);
993
994     if (m_buffer[m_current].form == vnw_vc || m_buffer[m_current].form == vnw_cvc) {
995         ConSeq cs = m_buffer[m_current].cseq;
996         if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) &&
997             (ev.tone == 2 || ev.tone == 3 || ev.tone == 4))
998             return processAppend(ev); // c, ch, p, t suffixes don't allow ` ? ~
999     }
1000       
1001     int toneOffset = getTonePosition(vs, vEnd == m_current);
1002     int tonePos = vEnd - (info.len -1 ) + toneOffset;
1003     if (m_buffer[tonePos].tone == 0 && ev.tone == 0)
1004         return processAppend(ev);
1005
1006     if (m_buffer[tonePos].tone == ev.tone) {
1007         markChange(tonePos);
1008         m_buffer[tonePos].tone = 0;
1009         m_singleMode = false;
1010         processAppend(ev);
1011         m_reverted = true;
1012         return 1;
1013     }
1014
1015     markChange(tonePos);
1016     m_buffer[tonePos].tone = ev.tone;
1017     return 1;
1018 }
1019
1020 //----------------------------------------------------------
1021 int UkEngine::processDd(UkKeyEvent & ev)
1022 {
1023     if (!m_pCtrl->vietKey || m_current < 0)
1024         return processAppend(ev);
1025     
1026     int pos;
1027
1028     // we want to allow dd even in non-vn sequence, because dd is used a lot in abbreviation
1029     // we allow dd only if preceding character is not a vowel
1030     if (m_buffer[m_current].form == vnw_nonVn && 
1031         m_buffer[m_current].vnSym == vnl_d &&
1032         (m_buffer[m_current-1].vnSym == vnl_nonVnChar ||!IsVnVowel[m_buffer[m_current-1].vnSym]))
1033     {
1034         m_singleMode = true;
1035         pos = m_current;
1036         markChange(pos);
1037         m_buffer[pos].cseq = cs_dd;
1038         m_buffer[pos].vnSym = vnl_dd;
1039         m_buffer[pos].form = vnw_c;
1040         m_buffer[pos].c1Offset = 0;
1041         m_buffer[pos].c2Offset = -1;
1042         m_buffer[pos].vOffset = -1;
1043         return 1;
1044     }
1045
1046     if (m_buffer[m_current].c1Offset < 0) {
1047         return processAppend(ev);
1048     }
1049
1050     pos = m_current - m_buffer[m_current].c1Offset;
1051     if (!m_pCtrl->options.freeMarking && pos != m_current)
1052         return processAppend(ev);
1053
1054     if (m_buffer[pos].cseq == cs_d) {
1055         markChange(pos);
1056         m_buffer[pos].cseq = cs_dd;
1057         m_buffer[pos].vnSym = vnl_dd;
1058         //never spellcheck a word which starts with dd, because it's used alot in abbreviation
1059         m_singleMode = true;
1060         return 1;
1061     }
1062
1063     if (m_buffer[pos].cseq == cs_dd) {
1064         //undo dd
1065         markChange(pos);
1066         m_buffer[pos].cseq = cs_d;
1067         m_buffer[pos].vnSym = vnl_d;
1068         m_singleMode = false;
1069         processAppend(ev);
1070         m_reverted = true;
1071         return 1;
1072     }
1073   
1074     return processAppend(ev);
1075 }
1076
1077 //----------------------------------------------------------
1078 VnLexiName changeCase(VnLexiName x)
1079 {
1080     if (x == vnl_nonVnChar)
1081         return x;
1082     if (!(x & 0x01))
1083         return (VnLexiName)(x+1);
1084     return (VnLexiName)(x-1);
1085 }
1086
1087 //----------------------------------------------------------
1088 inline VnLexiName vnToLower(VnLexiName x)
1089 {
1090     if (x == vnl_nonVnChar)
1091         return x;
1092     if (!(x & 0x01)) //even
1093         return (VnLexiName)(x+1);
1094     return x;
1095 }
1096
1097 //----------------------------------------------------------
1098 int UkEngine::processMapChar(UkKeyEvent & ev)
1099 {
1100     int capsLockOn = 0;
1101     int shiftPressed = 0;
1102     if (m_keyCheckFunc)
1103         m_keyCheckFunc(&shiftPressed, &capsLockOn);
1104
1105     if (capsLockOn)
1106         ev.vnSym = changeCase(ev.vnSym);
1107
1108     int ret = processAppend(ev);
1109     if (!m_pCtrl->vietKey)
1110         return ret;
1111
1112     if (m_current >= 0 && m_buffer[m_current].form != vnw_empty &&
1113         m_buffer[m_current].form != vnw_nonVn) {
1114         return 1;
1115     }
1116
1117     if (m_current < 0)
1118         return 0;
1119
1120     // mapChar doesn't apply
1121     m_current--;
1122     WordInfo & entry = m_buffer[m_current];
1123
1124     bool undo = false;
1125     // test if undo is needed
1126     if (entry.form != vnw_empty && entry.form != vnw_nonVn) {
1127         VnLexiName prevSym = entry.vnSym;
1128         if (entry.caps) {
1129             prevSym = (VnLexiName)(prevSym - 1);
1130         }
1131         if (prevSym == ev.vnSym) {
1132             if (entry.form != vnw_c) {
1133                 int vStart, vEnd, curTonePos, newTonePos, tone;
1134                 VowelSeq vs, newVs;
1135
1136                 vEnd = m_current - entry.vOffset;
1137                 vs = m_buffer[vEnd].vseq;
1138                 vStart = vEnd - VSeqList[vs].len +1;
1139                 curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
1140                 tone = m_buffer[curTonePos].tone;
1141                 markChange(m_current);
1142                 m_current--;
1143
1144                 //check if tone position is needed
1145                 if (tone != 0 && m_current >= 0 &&
1146                     (m_buffer[m_current].form == vnw_v || m_buffer[m_current].form == vnw_cv)) {
1147                     newVs = m_buffer[m_current].vseq;
1148                     newTonePos = vStart + getTonePosition(newVs, true);
1149                     if (newTonePos != curTonePos) {
1150                         markChange(newTonePos);
1151                         m_buffer[newTonePos].tone = tone;
1152                         markChange(curTonePos);
1153                         m_buffer[curTonePos].tone = 0;
1154                     }
1155                 }
1156             }
1157             else {
1158                 markChange(m_current);
1159                 m_current--;
1160             }
1161             undo = true;
1162         }
1163     }
1164
1165     ev.evType = vneNormal;
1166     ev.chType = m_pCtrl->input.getCharType(ev.keyCode);
1167     ev.vnSym = IsoToVnLexi(ev.keyCode);
1168     ret = processAppend(ev);
1169     if (undo) {
1170         m_singleMode = false;
1171         m_reverted = true;
1172         return 1;
1173     }
1174     return ret;
1175 }
1176
1177 //----------------------------------------------------------
1178 int UkEngine::processTelexW(UkKeyEvent & ev)
1179 {
1180     if (!m_pCtrl->vietKey)
1181         return processAppend(ev);
1182
1183     int ret;
1184     static bool usedAsMapChar = false;
1185     int capsLockOn = 0;
1186     int shiftPressed = 0;
1187     if (m_keyCheckFunc)
1188         m_keyCheckFunc(&shiftPressed, &capsLockOn);
1189
1190     if (usedAsMapChar) {
1191         ev.evType = vneMapChar;
1192         ev.vnSym = isupper(ev.keyCode)? vnl_Uh : vnl_uh;
1193         if (capsLockOn)
1194             ev.vnSym = changeCase(ev.vnSym);
1195         ev.chType = ukcVn;
1196         ret = processMapChar(ev);
1197         if (ret == 0) {
1198             if (m_current >= 0)
1199                 m_current--;
1200             usedAsMapChar = false;
1201             ev.evType = vneHookAll;
1202             return processHook(ev);
1203         }
1204         return ret;
1205     }
1206
1207     ev.evType = vneHookAll;
1208     usedAsMapChar = false;
1209     ret = processHook(ev);
1210     if (ret == 0) {
1211         if (m_current >= 0)
1212             m_current--;
1213         ev.evType = vneMapChar;
1214         ev.vnSym = isupper(ev.keyCode)? vnl_Uh : vnl_uh;
1215         if (capsLockOn)
1216             ev.vnSym = changeCase(ev.vnSym);
1217         ev.chType = ukcVn;
1218         usedAsMapChar = true;
1219         return processMapChar(ev);
1220     }
1221     return ret;
1222 }
1223
1224 //----------------------------------------------------------
1225 int UkEngine::checkEscapeVIQR(UkKeyEvent & ev)
1226 {
1227     if (m_current < 0)
1228         return 0;
1229     WordInfo & entry = m_buffer[m_current];
1230     int escape = 0;
1231     if (entry.form == vnw_v || entry.form == vnw_cv) {
1232         switch (ev.keyCode) {
1233         case '^':
1234             escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || entry.vnSym == vnl_e);
1235             break;
1236         case '(':
1237             escape = (entry.vnSym == vnl_a);
1238             break;
1239         case '+':
1240             escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u);
1241             break;
1242         case '\'':
1243         case '`':
1244         case '?':
1245         case '~':
1246         case '.':
1247             escape = (entry.tone == 0);
1248             break;
1249         }
1250     }
1251     else if (entry.form == vnw_nonVn) {
1252         unsigned char ch = toupper(entry.keyCode);
1253         switch (ev.keyCode) {
1254         case '^':
1255             escape = (ch == 'A' || ch == 'O' || ch == 'E');
1256             break;
1257         case '(':
1258             escape = (ch == 'A');
1259             break;
1260         case '+':
1261             escape = (ch == 'O' || ch == 'U');
1262             break;
1263         case '\'':
1264         case '`':
1265         case '?':
1266         case '~':
1267         case '.':
1268             escape = (ch == 'A' || ch == 'E' || ch == 'I' ||
1269                       ch == 'O' || ch == 'U' || ch == 'Y');
1270             break;
1271         }
1272     }
1273   
1274     if (escape) {
1275         m_current++;
1276         WordInfo *p = &m_buffer[m_current];
1277         p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn;
1278         p->c1Offset = p->c2Offset = p->vOffset = -1;
1279         p->keyCode = '?';
1280         p->vnSym = vnl_nonVnChar;
1281
1282         m_current++;
1283         p++;
1284         p->form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn;
1285         p->c1Offset = p->c2Offset = p->vOffset = -1;
1286         p->keyCode = ev.keyCode;
1287         p->vnSym = vnl_nonVnChar;
1288
1289         //write output
1290         m_pOutBuf[0] = '\\';
1291         m_pOutBuf[1] = ev.keyCode;
1292         *m_pOutSize = 2;
1293         m_outputWritten = true;
1294     }
1295     return escape;
1296 }
1297
1298 //----------------------------------------------------------
1299 int UkEngine::processAppend(UkKeyEvent & ev)
1300 {
1301     int ret = 0;
1302     switch (ev.chType) {
1303     case ukcReset:
1304 #if defined(_WIN32)
1305         if (ev.keyCode == ENTER_CHAR) {
1306             if (m_pCtrl->options.macroEnabled && macroMatch(ev))
1307                 return 1;
1308         }
1309 #endif
1310         reset();
1311         return 0;
1312     case ukcWordBreak:
1313         m_singleMode = false;
1314         return processWordEnd(ev);
1315     case ukcNonVn:
1316         {
1317             if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && checkEscapeVIQR(ev))
1318                 return 1;
1319
1320             m_current++;
1321             WordInfo & entry = m_buffer[m_current];
1322             entry.form = (ev.chType == ukcWordBreak) ? vnw_empty : vnw_nonVn;
1323             entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1324             entry.keyCode = ev.keyCode;
1325             entry.vnSym = vnToLower(ev.vnSym);
1326             entry.tone = 0;
1327             entry.caps = (entry.vnSym != ev.vnSym);
1328             if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1329                 return 0;
1330             markChange(m_current);
1331             return 1;
1332         }
1333     case ukcVn:
1334         {
1335             if (IsVnVowel[ev.vnSym]) {
1336                 VnLexiName v = (VnLexiName)StdVnNoTone[vnToLower(ev.vnSym)];
1337                 if (m_current >= 0 && m_buffer[m_current].form == vnw_c &&
1338                       ((m_buffer[m_current].cseq == cs_q && v == vnl_u) ||
1339                       (m_buffer[m_current].cseq == cs_g && v == vnl_i))) {
1340                     return appendConsonnant(ev); //process u after q, i after g as consonnants
1341                 }
1342                 return appendVowel(ev);
1343             }
1344             return appendConsonnant(ev);
1345         }
1346         break;
1347     }
1348
1349     return ret;
1350 }
1351
1352 //----------------------------------------------------------
1353 int UkEngine::appendVowel(UkKeyEvent & ev)
1354 {
1355     bool autoCompleted = false;
1356
1357     m_current++;
1358     WordInfo & entry = m_buffer[m_current];
1359
1360     VnLexiName lowerSym = vnToLower(ev.vnSym);
1361     VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym];
1362
1363     entry.vnSym = canSym;
1364     entry.caps = (lowerSym != ev.vnSym);
1365     entry.tone = (lowerSym - canSym)/2;
1366     entry.keyCode = ev.keyCode;
1367
1368     if (m_current == 0 || !m_pCtrl->vietKey) {
1369         entry.form = vnw_v;
1370         entry.c1Offset = entry.c2Offset = -1;
1371         entry.vOffset = 0;
1372         entry.vseq = lookupVSeq(canSym);
1373
1374         if (!m_pCtrl->vietKey || 
1375             ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode)) ) {
1376             return 0;
1377         }
1378         markChange(m_current);
1379         return 1;
1380     }
1381   
1382     WordInfo & prev = m_buffer[m_current-1];
1383     VowelSeq vs, newVs;
1384     ConSeq cs;
1385     int prevTonePos;
1386     int tone, newTone, tonePos, newTonePos;
1387
1388     switch (prev.form) {
1389
1390     case vnw_empty:
1391         entry.form = vnw_v;
1392         entry.c1Offset = entry.c2Offset = -1;
1393         entry.vOffset = 0;
1394         entry.vseq = newVs = lookupVSeq(canSym);
1395         break;
1396
1397     case vnw_nonVn:
1398     case vnw_cvc:
1399     case vnw_vc:
1400         entry.form = vnw_nonVn;
1401         entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1402         break;
1403
1404     case vnw_v:
1405     case vnw_cv:
1406         vs = prev.vseq;
1407         prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true);
1408         tone = m_buffer[prevTonePos].tone;
1409     
1410         if (lowerSym != canSym && tone != 0) //new sym has a tone, but there's is already a preceeding tone
1411             newVs = vs_nil;
1412         else {
1413             if (VSeqList[vs].len == 3)
1414                 newVs = vs_nil;
1415             else if (VSeqList[vs].len == 2)
1416                 newVs = lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym);
1417             else
1418                 newVs = lookupVSeq(VSeqList[vs].v[0], canSym);
1419         }
1420
1421         if (newVs != vs_nil && prev.form == vnw_cv) {
1422             cs = m_buffer[m_current - 1 - prev.c1Offset].cseq;
1423             if (!isValidCV(cs, newVs))
1424                 newVs = vs_nil;
1425         }
1426
1427         if (newVs == vs_nil) {
1428             entry.form = vnw_nonVn;
1429             entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1430             break;
1431         }
1432     
1433         entry.form = prev.form;
1434         if (prev.form == vnw_cv)
1435             entry.c1Offset = prev.c1Offset + 1;
1436         else
1437             entry.c1Offset = -1;
1438         entry.c2Offset = -1;
1439         entry.vOffset = 0;
1440         entry.vseq = newVs;
1441         entry.tone = 0;
1442         
1443         newTone = (lowerSym - canSym)/2;
1444         if (tone == 0) {
1445             if (newTone != 0) {
1446                 tone = newTone;
1447                 tonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1);
1448                 markChange(tonePos);
1449                 m_buffer[tonePos].tone = tone;
1450                 return 1;
1451             }
1452         }
1453         else {
1454             newTonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1);
1455             if (newTonePos != prevTonePos) {
1456                 markChange(prevTonePos);
1457                 m_buffer[prevTonePos].tone = 0;
1458                 markChange(newTonePos);
1459                 if (newTone != 0)
1460                     tone = newTone;
1461                 m_buffer[newTonePos].tone = tone;
1462                 return 1;
1463             }
1464             if (newTone != 0 && newTone != tone) {
1465                 tone = newTone;
1466                 markChange(prevTonePos);
1467                 m_buffer[prevTonePos].tone = tone;
1468                 return 1;
1469             }
1470
1471         }
1472
1473         break;
1474     case vnw_c:
1475         newVs = lookupVSeq(canSym);
1476         cs = prev.cseq;
1477         if (!isValidCV(cs, newVs)) {
1478             entry.form = vnw_nonVn;
1479             entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1480             break;
1481         }
1482
1483         entry.form = vnw_cv;
1484         entry.c1Offset = 1;
1485         entry.c2Offset = -1;
1486         entry.vOffset = 0;
1487         entry.vseq = newVs;
1488
1489         if (cs == cs_gi && prev.tone != 0) {
1490             if (entry.tone == 0)
1491                 entry.tone = prev.tone;
1492             markChange(m_current - 1);
1493             prev.tone = 0;
1494             return 1;
1495         }
1496     
1497         break;
1498   }
1499
1500     if (!autoCompleted &&
1501         (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && 
1502         isalpha(entry.keyCode)) {
1503         return 0;
1504     }
1505
1506     markChange(m_current);
1507     return 1;
1508 }
1509
1510 //----------------------------------------------------------
1511 int UkEngine::appendConsonnant(UkKeyEvent & ev)
1512 {
1513     bool complexEvent = false;
1514     m_current++;
1515     WordInfo & entry = m_buffer[m_current];
1516
1517     VnLexiName lowerSym = vnToLower(ev.vnSym);
1518
1519     entry.vnSym = lowerSym;
1520     entry.caps = (lowerSym != ev.vnSym);
1521     entry.keyCode = ev.keyCode;
1522     entry.tone = 0;
1523
1524     if (m_current == 0 || !m_pCtrl->vietKey) {
1525         entry.form = vnw_c;
1526         entry.c1Offset = 0;
1527         entry.c2Offset = -1;
1528         entry.vOffset = -1;
1529         entry.cseq = lookupCSeq(lowerSym);
1530         if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1531             return 0;
1532         markChange(m_current);
1533         return 1;
1534     }
1535
1536     ConSeq cs, newCs, c1;
1537     VowelSeq vs, newVs;
1538     bool isValid;
1539
1540     WordInfo & prev = m_buffer[m_current-1];
1541
1542     switch (prev.form) {
1543     case vnw_nonVn:
1544         entry.form = vnw_nonVn;
1545         entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1546         if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1547             return 0;
1548         markChange(m_current);
1549         return 1;
1550     case vnw_empty:
1551         entry.form = vnw_c;
1552         entry.c1Offset = 0;
1553         entry.c2Offset = -1;
1554         entry.vOffset = -1;
1555         entry.cseq = lookupCSeq(lowerSym);
1556         if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1557             return 0;
1558         markChange(m_current);
1559         return 1;
1560     case vnw_v:
1561     case vnw_cv:
1562         vs = prev.vseq;
1563         newVs = vs;
1564         if (vs == vs_uoh || vs == vs_uho) {
1565             newVs = vs_uhoh;
1566         }
1567
1568         c1 = cs_nil;
1569         if (prev.c1Offset != -1)
1570             c1 = m_buffer[m_current-1-prev.c1Offset].cseq;
1571
1572         newCs = lookupCSeq(lowerSym);
1573         isValid = isValidCVC(c1, newVs, newCs);
1574
1575         if (isValid) {
1576             //check u+o -> u+o+
1577             if (vs == vs_uho) {
1578                 markChange(m_current-1);
1579                 prev.vnSym = vnl_oh;
1580                 prev.vseq = vs_uhoh;
1581                 complexEvent = true;
1582             }
1583             else if (vs == vs_uoh) {
1584                 markChange(m_current-2);
1585                 m_buffer[m_current-2].vnSym = vnl_uh;
1586                 m_buffer[m_current-2].vseq = vs_uh;
1587                 prev.vseq = vs_uhoh;
1588                 complexEvent = true;
1589             }
1590
1591             if (prev.form == vnw_v) {
1592                 entry.form = vnw_vc;
1593                 entry.c1Offset = -1;
1594                 entry.c2Offset = 0;
1595                 entry.vOffset = 1;
1596             }
1597             else { //prev == vnw_cv
1598                 entry.form = vnw_cvc;
1599                 entry.c1Offset = prev.c1Offset + 1;
1600                 entry.c2Offset = 0;
1601                 entry.vOffset = 1;
1602             }
1603             entry.cseq = newCs;
1604
1605             //reposition tone if needed
1606             int oldIdx = (m_current-1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true);
1607             if (m_buffer[oldIdx].tone != 0) {
1608                 int newIdx = (m_current-1) - (VSeqList[newVs].len - 1) + getTonePosition(newVs, false);
1609                 if (newIdx != oldIdx) {
1610                     markChange(newIdx);
1611                     m_buffer[newIdx].tone = m_buffer[oldIdx].tone;
1612                     markChange(oldIdx);
1613                     m_buffer[oldIdx].tone = 0;
1614                     return 1;
1615                 }
1616             }
1617         }
1618         else {
1619             entry.form = vnw_nonVn;
1620             entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1621         }
1622
1623         if (complexEvent) {
1624             return 1;
1625         }
1626
1627         if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1628             return 0;
1629         markChange(m_current);
1630         return 1;
1631     case vnw_c:
1632     case vnw_vc:
1633     case vnw_cvc:
1634         cs = prev.cseq;
1635         if (CSeqList[cs].len == 3)
1636             newCs = cs_nil;
1637         else if (CSeqList[cs].len == 2)
1638             newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym);
1639         else
1640             newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym);
1641         
1642         if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) {
1643             // Check CVC combination
1644             c1 = cs_nil;
1645             if (prev.c1Offset != -1)
1646                 c1 = m_buffer[m_current-1-prev.c1Offset].cseq;
1647
1648             int vIdx = (m_current - 1) - prev.vOffset;
1649             vs = m_buffer[vIdx].vseq;
1650             isValid = isValidCVC(c1, vs, newCs);
1651
1652             if (!isValid)
1653                 newCs = cs_nil;
1654         }
1655
1656         if (newCs == cs_nil) {
1657             entry.form = vnw_nonVn;
1658             entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1659         }
1660         else {
1661             if (prev.form == vnw_c) {
1662                 entry.form = vnw_c;
1663                 entry.c1Offset = 0;
1664                 entry.c2Offset = -1;
1665                 entry.vOffset = -1;
1666             }
1667             else if (prev.form == vnw_vc) {
1668                 entry.form = vnw_vc;
1669                 entry.c1Offset = -1;
1670                 entry.c2Offset = 0;
1671                 entry.vOffset = prev.vOffset + 1;
1672             }
1673             else { //vnw_cvc
1674                 entry.form = vnw_cvc;
1675                 entry.c1Offset = prev.c1Offset + 1;
1676                 entry.c2Offset = 0;
1677                 entry.vOffset = prev.vOffset + 1;
1678             }
1679             entry.cseq = newCs;
1680         }
1681         if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1682             return 0;
1683         markChange(m_current);
1684         return 1;
1685     }
1686
1687     if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1688         return 0;
1689     markChange(m_current);
1690     return 1;
1691 }
1692
1693 //----------------------------------------------------------
1694 int UkEngine::processEscChar(UkKeyEvent & ev)
1695 {
1696     if (m_pCtrl->vietKey && 
1697         m_current >=0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) {
1698         m_toEscape = true;
1699     }
1700     return processAppend(ev);
1701 }
1702
1703 //----------------------------------------------------------
1704 void UkEngine::pass(int keyCode)
1705 {
1706     UkKeyEvent ev;
1707     m_pCtrl->input.keyCodeToEvent(keyCode, ev);
1708     processAppend(ev);
1709 }
1710
1711 //---------------------------------------------
1712 // This can be called only after other processing have been done.
1713 // The new event is supposed to be put into m_buffer already
1714 //---------------------------------------------
1715 int UkEngine::processNoSpellCheck(UkKeyEvent & ev)
1716 {
1717     WordInfo & entry = m_buffer[m_current];
1718     if (IsVnVowel[entry.vnSym]) {
1719         entry.form = vnw_v;
1720         entry.vOffset = 0;
1721         entry.vseq = lookupVSeq(entry.vnSym);
1722         entry.c1Offset = entry.c2Offset = -1;
1723     }
1724     else {
1725         entry.form = vnw_c;
1726         entry.c1Offset = 0;
1727         entry.c2Offset = -1;
1728         entry.vOffset = -1;
1729         entry.cseq = lookupCSeq(entry.vnSym);
1730     }
1731
1732     if (ev.evType == vneNormal &&
1733         ((entry.keyCode >= 'a' && entry.keyCode <= 'z') || 
1734          (entry.keyCode >= 'A' && entry.keyCode <= 'Z') ) )
1735         return 0;
1736     markChange(m_current);
1737     return 1;
1738 }
1739 //----------------------------------------------------------
1740 int UkEngine::process(unsigned int keyCode, int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
1741 {
1742     UkKeyEvent ev;
1743     prepareBuffer();
1744     m_backs = 0;
1745     m_changePos = m_current+1;
1746     m_pOutBuf = outBuf;
1747     m_pOutSize = &outSize;
1748     m_outputWritten = false;
1749     m_reverted = false;
1750     m_keyRestored = false;
1751     m_keyRestoring = false;
1752     m_outType = UkCharOutput;
1753
1754     m_pCtrl->input.keyCodeToEvent(keyCode, ev);
1755
1756     int ret;
1757     if (!m_toEscape) {
1758         ret = (this->*UkKeyProcList[ev.evType])(ev);
1759     }
1760     else {
1761         m_toEscape = false;
1762         if (m_current < 0 || ev.evType == vneNormal || ev.evType == vneEscChar) {
1763             ret = processAppend(ev);
1764         }
1765         else {
1766             m_current--;
1767             processAppend(ev);
1768             markChange(m_current); //this will assign m_backs to 1 and mark the character for output
1769             ret = 1;
1770         }
1771     }
1772
1773     if ( m_pCtrl->vietKey &&
1774          m_current >= 0 && m_buffer[m_current].form == vnw_nonVn &&
1775          ev.chType == ukcVn &&
1776          (!m_pCtrl->options.spellCheckEnabled || m_singleMode) )
1777     {
1778
1779         //The spell check has failed, but because we are in non-spellcheck mode,
1780         //we consider the new character as the beginning of a new word
1781         ret = processNoSpellCheck(ev);
1782         /*
1783         if ((!m_pCtrl->options.spellCheckEnabled || m_singleMode) || 
1784             ( !m_reverted && 
1785               (m_current < 1 || m_buffer[m_current-1].form != vnw_nonVn)) ) {
1786
1787             ret = processNoSpellCheck(ev);
1788         }
1789         */
1790     }
1791
1792     //we add key to key buffer only if that key has not caused a reset
1793     if (m_current >= 0) {
1794         ev.chType = m_pCtrl->input.getCharType(ev.keyCode);
1795         m_keyCurrent++;
1796         m_keyStrokes[m_keyCurrent].ev = ev;
1797         m_keyStrokes[m_keyCurrent].converted = (ret && !m_keyRestored);
1798     }
1799
1800     if (ret == 0) {
1801         backs = 0;
1802         outSize = 0;
1803         outType = m_outType;
1804         return 0;
1805     }
1806
1807     backs = m_backs;
1808     if (!m_outputWritten) {
1809         writeOutput(outBuf, outSize);
1810     }
1811     outType = m_outType;
1812
1813     return ret;
1814 }
1815
1816
1817 //----------------------------------------------------------
1818 // Returns 0 on success
1819 //         error code otherwise
1820 //  outBuf: buffer to write
1821 //  outSize: [in] size of buffer in bytes
1822 //           [out] bytes written to buffer
1823 //----------------------------------------------------------
1824 int UkEngine::writeOutput(unsigned char *outBuf, int & outSize)
1825 {
1826     StdVnChar stdChar;
1827     int i, bytesWritten;
1828     int ret = 1;
1829     StringBOStream os(outBuf, outSize);
1830     VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId);
1831     pCharset->startOutput();
1832
1833     for (i = m_changePos; i <= m_current; i++) {
1834         if (m_buffer[i].vnSym != vnl_nonVnChar) {
1835             //process vn symbol
1836             stdChar = m_buffer[i].vnSym + VnStdCharOffset;
1837             if (m_buffer[i].caps)
1838                 stdChar--;
1839             if (m_buffer[i].tone != 0)
1840                 stdChar += m_buffer[i].tone * 2;
1841         }
1842         else {
1843             stdChar = IsoToStdVnChar(m_buffer[i].keyCode);
1844         }
1845     
1846         if (stdChar != INVALID_STD_CHAR)
1847             ret = pCharset->putChar(os, stdChar, bytesWritten);
1848     }
1849
1850     outSize = os.getOutBytes();
1851     return (ret? 0 : VNCONV_OUT_OF_MEMORY);
1852 }
1853
1854 //---------------------------------------------
1855 // Returns the number of backspaces needed to
1856 // go back from last to first
1857 //---------------------------------------------
1858 int UkEngine::getSeqSteps(int first, int last)
1859 {
1860     StdVnChar stdChar;
1861
1862     if (last < first)
1863         return 0;
1864
1865     if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 || 
1866         m_pCtrl->charsetId == CONV_CHARSET_UNICODE)
1867         return (last - first +  1);
1868
1869     StringBOStream os(0, 0);
1870     int i, bytesWritten;
1871
1872     VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId);
1873     pCharset->startOutput();
1874
1875     for (i = first; i <= last; i++) {
1876         if (m_buffer[i].vnSym != vnl_nonVnChar) {
1877             //process vn symbol
1878             stdChar = m_buffer[i].vnSym + VnStdCharOffset;
1879             if (m_buffer[i].caps)
1880                 stdChar--;
1881             if (m_buffer[i].tone != 0)
1882                 stdChar += m_buffer[i].tone*2;
1883         }
1884         else {
1885             stdChar = m_buffer[i].keyCode;
1886         }
1887     
1888         if (stdChar != INVALID_STD_CHAR)
1889             pCharset->putChar(os, stdChar, bytesWritten);
1890     }
1891   
1892     int len = os.getOutBytes();
1893     if (m_pCtrl->charsetId == CONV_CHARSET_UNIDECOMPOSED)
1894         len = len / 2;
1895     return len;
1896 }
1897
1898 //---------------------------------------------
1899 void UkEngine::markChange(int pos)
1900 {
1901     if (pos < m_changePos) {
1902         m_backs += getSeqSteps(pos, m_changePos-1);
1903         m_changePos = pos;
1904     }
1905 }
1906
1907 //----------------------------------------------------------------
1908 // Called from processBackspace to keep
1909 // character buffer (m_buffer) and key stroke buffer in synch
1910 //----------------------------------------------------------------
1911 void UkEngine::synchKeyStrokeBuffer()
1912 {
1913     //synchronize with key-stroke buffer
1914     if (m_keyCurrent >= 0)
1915         m_keyCurrent--;
1916     if (m_current >= 0 && m_buffer[m_current].form == vnw_empty) {
1917         //in character buffer, we have reached a word break,
1918         //so we also need to move key stroke pointer backward to corresponding word break
1919         while (m_keyCurrent >= 0 && m_keyStrokes[m_keyCurrent].ev.chType != ukcWordBreak)
1920         {
1921             m_keyCurrent--;
1922         }
1923     }
1924 }
1925
1926 //---------------------------------------------
1927 int UkEngine::processBackspace(int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
1928 {
1929     outType = UkCharOutput;
1930     if (!m_pCtrl->vietKey || m_current < 0) {
1931         backs = 0;
1932         outSize = 0;
1933         return 0;
1934     }
1935
1936     m_backs = 0;
1937     m_changePos = m_current + 1;
1938     markChange(m_current);
1939
1940     if (m_current == 0 || 
1941         m_buffer[m_current].form == vnw_empty ||
1942         m_buffer[m_current].form == vnw_nonVn ||
1943         m_buffer[m_current].form == vnw_c ||
1944         m_buffer[m_current-1].form == vnw_c ||
1945         m_buffer[m_current-1].form == vnw_cvc ||
1946         m_buffer[m_current-1].form == vnw_vc) {
1947
1948         m_current--;
1949         backs = m_backs;
1950         outSize = 0;
1951         synchKeyStrokeBuffer();
1952         return (backs > 1);
1953     }
1954   
1955     VowelSeq vs, newVs;
1956     int curTonePos, newTonePos, tone, vStart, vEnd;
1957
1958     vEnd = m_current - m_buffer[m_current].vOffset;
1959     vs = m_buffer[vEnd].vseq;
1960     vStart = vEnd - VSeqList[vs].len + 1;
1961     newVs = m_buffer[m_current-1].vseq;
1962     curTonePos = vStart +  getTonePosition(vs, vEnd == m_current);
1963     newTonePos = vStart + getTonePosition(newVs, true);
1964     tone = m_buffer[curTonePos].tone;
1965
1966     if (tone == 0 || curTonePos == newTonePos || 
1967         (curTonePos == m_current && m_buffer[m_current].tone != 0)) {
1968         m_current--;
1969         backs = m_backs;
1970         outSize = 0;
1971         synchKeyStrokeBuffer();
1972         return (backs > 1);
1973     }
1974
1975     markChange(newTonePos);
1976     m_buffer[newTonePos].tone = tone;
1977     markChange(curTonePos);
1978     m_buffer[curTonePos].tone = 0;
1979     m_current--;
1980     synchKeyStrokeBuffer();
1981     backs = m_backs;
1982     writeOutput(outBuf, outSize);
1983     return 1;
1984 }
1985
1986 //------------------------------------------------
1987 void UkEngine::reset()
1988 {
1989     m_current = -1;
1990     m_keyCurrent = -1;
1991     m_singleMode = false;
1992     m_toEscape = false;
1993 }
1994
1995 //------------------------------------------------
1996 void UkEngine::resetKeyBuf()
1997 {
1998     m_keyCurrent = -1;
1999 }
2000
2001 //------------------------------------------------
2002 UkEngine::UkEngine()
2003 {
2004     if (!m_classInit) {
2005         engineClassInit();
2006         m_classInit = true;
2007     }
2008     m_pCtrl = 0;
2009     m_bufSize = MAX_UK_ENGINE;
2010     m_keyBufSize = MAX_UK_ENGINE;
2011     m_current = -1;
2012     m_keyCurrent = -1;
2013     m_singleMode = false;
2014     m_keyCheckFunc = 0;
2015     m_reverted = false;
2016     m_toEscape = false;
2017     m_keyRestored = false;
2018 }
2019
2020 //----------------------------------------------------
2021 // make sure there are at least 10 entries available
2022 //----------------------------------------------------
2023 void UkEngine::prepareBuffer()
2024 {
2025     int rid;
2026     //prepare symbol buffer
2027     if (m_current >= 0 && m_current + 10 >= m_bufSize) {
2028         // Get rid of at least half of the current entries
2029         // don't get rid from the middle of a word.
2030         for (rid = m_current/2; m_buffer[rid].form != vnw_empty && rid < m_current; rid++);
2031         if (rid == m_current) {
2032             m_current = -1;
2033         }
2034         else {
2035             rid++;
2036             memmove(m_buffer, m_buffer+rid, (m_current-rid+1)*sizeof(WordInfo));
2037             m_current -= rid;
2038         }
2039     }
2040
2041     //prepare key stroke buffer
2042     if (m_keyCurrent > 0 && m_keyCurrent + 1 >= m_keyBufSize) {
2043         // Get rid of at least half of the current entries
2044         rid = m_keyCurrent/2;
2045         memmove(m_keyStrokes, m_keyStrokes + rid, (m_keyCurrent-rid+1)*sizeof(m_keyStrokes[0]));
2046         m_keyCurrent -= rid;
2047     }
2048
2049 }
2050
2051 #define ENTER_CHAR 13
2052 enum VnCaseType {VnCaseNoChange, VnCaseAllCapital, VnCaseAllSmall};
2053
2054 //----------------------------------------------------
2055 int UkEngine::macroMatch(UkKeyEvent & ev)
2056 {
2057     int capsLockOn = 0;
2058     int shiftPressed = 0;
2059     if (m_keyCheckFunc)
2060         m_keyCheckFunc(&shiftPressed, &capsLockOn);
2061
2062     if (shiftPressed && (ev.keyCode ==' ' || ev.keyCode == ENTER_CHAR))
2063         return 0;
2064
2065     const StdVnChar *pMacText = NULL;
2066     StdVnChar key[MAX_MACRO_KEY_LEN+1];
2067     StdVnChar *pKeyStart;
2068
2069     // Use static macro text so we can gain a bit of performance
2070     // by avoiding memory allocation each time this function is called
2071     static StdVnChar macroText[MAX_MACRO_TEXT_LEN+1];
2072
2073     int i, j;
2074
2075     i = m_current;
2076     while (i >= 0 && (m_current-i + 1) < MAX_MACRO_KEY_LEN) {
2077         while (i>=0 && m_buffer[i].form != vnw_empty && (m_current-i + 1) < MAX_MACRO_KEY_LEN)
2078             i--;
2079         if (i>=0 && m_buffer[i].form != vnw_empty)
2080             return 0;
2081
2082         if (i>=0) {
2083             if (m_buffer[i].vnSym != vnl_nonVnChar) {
2084                 key[0] = m_buffer[i].vnSym + VnStdCharOffset;
2085                 if (m_buffer[i].caps)
2086                     key[0]--;
2087                 key[0] += m_buffer[i].tone*2;
2088             }
2089             else
2090                 key[0] = m_buffer[i].keyCode;
2091         }
2092
2093         for (j=i+1; j<=m_current; j++) {
2094             if (m_buffer[j].vnSym != vnl_nonVnChar) {
2095                 key[j-i] = m_buffer[j].vnSym + VnStdCharOffset;
2096                 if (m_buffer[j].caps)
2097                     key[j-i]--;
2098                 key[j-i] += m_buffer[j].tone*2;
2099             }
2100             else
2101                 key[j-i] = m_buffer[j].keyCode;
2102         }
2103         key[m_current-i+1] = 0;
2104         //search macro table
2105         pMacText = m_pCtrl->macStore.lookup(key+1);
2106         if (pMacText) {
2107             i++; //mark the position where change is needed
2108             pKeyStart = key + 1;
2109             break;
2110         }
2111         if (i>=0) {
2112             pMacText = m_pCtrl->macStore.lookup(key);
2113             if (pMacText) {
2114                 pKeyStart = key;
2115                 break;
2116             }
2117         }
2118         i--;
2119     }
2120
2121     if (!pMacText) {
2122         return 0;
2123     }
2124
2125     markChange(i);
2126
2127     // determine the form of macro replacements: ALL CAPITALS, First Character Capital, or no change
2128     VnCaseType macroCase;
2129     if (IS_STD_VN_LOWER(*pKeyStart)) {
2130         macroCase = VnCaseAllSmall;
2131     }
2132     else if (IS_STD_VN_UPPER(*pKeyStart)) {
2133         macroCase = VnCaseAllCapital;
2134         for (i=1; pKeyStart[i]; i++) {
2135             if (IS_STD_VN_LOWER(pKeyStart[i])) {
2136                 macroCase = VnCaseNoChange;
2137             }
2138         }
2139     }
2140     else macroCase = VnCaseNoChange;
2141
2142     // Convert case of macro text according to macroCase
2143     int charCount = 0;
2144     while (pMacText[charCount] != 0)
2145         charCount++;
2146
2147     for (i = 0; i < charCount; i++)
2148     {
2149         if (macroCase == VnCaseAllCapital)
2150             macroText[i] = StdVnToUpper(pMacText[i]);
2151         else if (macroCase == VnCaseAllSmall)
2152             macroText[i] = StdVnToLower(pMacText[i]);
2153         else
2154             macroText[i] = pMacText[i];
2155     }
2156
2157     // Convert to target output charset
2158     int outSize;
2159     int maxOutSize = *m_pOutSize;
2160     int inLen = charCount * sizeof(StdVnChar);
2161     VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId,
2162                 (UKBYTE *) macroText, (UKBYTE *)m_pOutBuf,
2163                 &inLen, &maxOutSize);
2164     outSize = maxOutSize;
2165
2166     //write the last input character
2167     StdVnChar vnChar;
2168     if (outSize < *m_pOutSize) {
2169         maxOutSize = *m_pOutSize - outSize;
2170         if (ev.vnSym != vnl_nonVnChar)
2171             vnChar = ev.vnSym + VnStdCharOffset;
2172         else
2173             vnChar = ev.keyCode;
2174         inLen = sizeof(StdVnChar);
2175         VnConvert(CONV_CHARSET_VNSTANDARD, m_pCtrl->charsetId,
2176                     (UKBYTE *) &vnChar, ((UKBYTE *)m_pOutBuf) + outSize,
2177                     &inLen, &maxOutSize);
2178         outSize += maxOutSize;
2179     }
2180     int backs = m_backs; //store m_backs before calling reset
2181     reset();
2182     m_outputWritten = true;
2183     m_backs = backs;
2184     *m_pOutSize = outSize;
2185     return 1;
2186 }
2187
2188 //----------------------------------------------------
2189 int UkEngine::restoreKeyStrokes(int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
2190 {
2191     outType = UkKeyOutput;
2192     if (!lastWordHasVnMark()) {
2193         backs = 0;
2194         outSize = 0;
2195         return 0;
2196     }
2197
2198     m_backs = 0;
2199     m_changePos = m_current+1;
2200
2201     int keyStart;
2202     bool converted = false;
2203     for (keyStart = m_keyCurrent; keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; keyStart--) {
2204         if (m_keyStrokes[keyStart].converted) {
2205             converted = true;
2206         }
2207     }
2208     keyStart++;
2209
2210     if (!converted) {
2211         //no key stroke has been converted, so it doesn't make sense to restore key strokes
2212         backs = 0;
2213         outSize = 0;
2214         return 0;
2215     }
2216
2217     //int i = m_current;
2218     while (m_current >=0 && m_buffer[m_current].form != vnw_empty)
2219         m_current--;
2220     markChange(m_current+1);
2221     backs = m_backs;
2222
2223     int count;
2224     int i;
2225     UkKeyEvent ev;
2226     m_keyRestoring = true;
2227     for (i=keyStart, count = 0; i <= m_keyCurrent; i++) {
2228         if (count<outSize) {
2229             outBuf[count++] = (unsigned char)m_keyStrokes[i].ev.keyCode;
2230         }
2231         m_pCtrl->input.keyCodeToSymbol(m_keyStrokes[i].ev.keyCode, ev);
2232         m_keyStrokes[i].converted = false;
2233         processAppend(ev);
2234     }
2235     outSize = count;
2236     m_keyRestoring = false;
2237
2238     return 1;
2239 }
2240
2241 //--------------------------------------------------
2242 void UkEngine::setSingleMode()
2243 {
2244     m_singleMode = true;
2245 }
2246
2247 //--------------------------------------------------
2248 void SetupUnikeyEngine()
2249 {
2250     SetupInputClassifierTable();
2251     int i;
2252     VnLexiName lexi;
2253
2254     //Calculate IsoStdVnCharMap
2255     for (i=0; i < 256; i++) {
2256         IsoStdVnCharMap[i] = i;
2257     }
2258
2259     for (i=0; SpecialWesternChars[i]; i++) {
2260         IsoStdVnCharMap[SpecialWesternChars[i]] = (vnl_lastChar + i) + VnStdCharOffset;
2261     }
2262
2263     for (i=0; i < 256; i++) {
2264         if ((lexi = IsoToVnLexi(i)) != vnl_nonVnChar) {
2265             IsoStdVnCharMap[i] = lexi + VnStdCharOffset;
2266         }
2267     }
2268 }
2269
2270 //--------------------------------------------------
2271 bool UkEngine::atWordBeginning()
2272 {
2273     return (m_current < 0 || m_buffer[m_current].form == vnw_empty);
2274 }
2275
2276 //--------------------------------------------------
2277 // Check for macro first, if there's a match, expand macro. If not:
2278 // Spell-check, if is valid Vietnamese, return normally, if not:
2279 // restore key strokes if auto-restore is enabled
2280 //--------------------------------------------------
2281 int UkEngine::processWordEnd(UkKeyEvent & ev)
2282 {
2283     if (m_pCtrl->options.macroEnabled && macroMatch(ev))
2284         return 1;
2285
2286     if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || m_keyRestoring) {
2287         m_current++;
2288         WordInfo & entry = m_buffer[m_current];
2289         entry.form = vnw_empty;
2290         entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
2291         entry.keyCode = ev.keyCode;
2292         entry.vnSym = vnToLower(ev.vnSym);
2293         entry.caps = (entry.vnSym != ev.vnSym);
2294         return 0;
2295     }
2296
2297     int outSize = 0;
2298     if (m_pCtrl->options.autoNonVnRestore && lastWordIsNonVn()) {
2299         outSize = *m_pOutSize;
2300         if (restoreKeyStrokes(m_backs, m_pOutBuf, outSize, m_outType)) {
2301             m_keyRestored = true;
2302             m_outputWritten = true;
2303         }
2304     }
2305
2306     m_current++;
2307     WordInfo & entry = m_buffer[m_current];
2308     entry.form = vnw_empty;
2309     entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
2310     entry.keyCode = ev.keyCode;
2311     entry.vnSym = vnToLower(ev.vnSym);
2312     entry.caps = (entry.vnSym != ev.vnSym);
2313     
2314     if (m_keyRestored && outSize < *m_pOutSize) {
2315         m_pOutBuf[outSize] = ev.keyCode;
2316         outSize++;
2317         *m_pOutSize = outSize;
2318         return 1;
2319     }
2320  
2321     return 0;
2322 }
2323
2324 //---------------------------------------------------------------------------
2325 // Test if last word is a non-Vietnamese word, so that
2326 // the engine can restore key strokes if it is indeed not a Vietnamese word
2327 //---------------------------------------------------------------------------
2328 bool UkEngine::lastWordIsNonVn()
2329 {
2330     if (m_current < 0)
2331         return false;
2332
2333     switch (m_buffer[m_current].form) {
2334         case vnw_nonVn:
2335             return true;
2336         case vnw_empty:
2337         case vnw_c:
2338             return false;
2339         case vnw_v:
2340         case vnw_cv:
2341             return !VSeqList[m_buffer[m_current].vseq].complete;
2342         case vnw_vc:
2343         case vnw_cvc: {
2344             int vIndex = m_current - m_buffer[m_current].vOffset;
2345             VowelSeq vs = m_buffer[vIndex].vseq;
2346             if (!VSeqList[vs].complete)
2347                 return true;
2348             ConSeq cs = m_buffer[m_current].cseq;
2349             ConSeq c1 = cs_nil;
2350             if (m_buffer[m_current].c1Offset != -1)
2351                 c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
2352
2353             if (!isValidCVC(c1, vs, cs)) {
2354                 return true;
2355             }
2356
2357             int tonePos = (vIndex - VSeqList[vs].len + 1) + getTonePosition(vs, false);
2358             int tone = m_buffer[tonePos].tone;
2359             if ((cs == cs_c || cs == cs_ch || cs == cs_p || cs == cs_t) &&
2360                 (tone == 2 || tone == 3 || tone == 4))
2361             {
2362                 return true;
2363             }
2364         }
2365     }
2366     return false;
2367 }
2368
2369 //---------------------------------------------------------------------------
2370 // Test if last word has a Vietnamese mark, that is tones, decorators
2371 //---------------------------------------------------------------------------
2372 bool UkEngine::lastWordHasVnMark()
2373 {
2374     for (int i=m_current; i>=0 && m_buffer[i].form != vnw_empty; i--) {
2375         VnLexiName sym = m_buffer[i].vnSym;
2376         if (sym != vnl_nonVnChar) {
2377             if (IsVnVowel[sym]) {
2378                 if (m_buffer[i].tone)
2379                     return true;
2380             }
2381             if (sym != StdVnRootChar[sym] )
2382                 return true;
2383         }
2384     }
2385     return false;
2386 }