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
6 * UniKey project: http://unikey.org
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.
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.
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.
43 #define IS_ODD(x) (x & 1)
44 #define IS_EVEN(x) (!(x & 1))
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))
49 bool IsVnVowel[vnl_lastChar];
51 extern VnLexiName AZLexiUpper[]; //defined in inputproc.cpp
52 extern VnLexiName AZLexiLower[];
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};
61 StdVnChar IsoStdVnCharMap[256];
63 inline StdVnChar IsoToStdVnChar(int keyCode)
65 return (keyCode < 256)? IsoStdVnCharMap[keyCode] : keyCode;
71 int conSuffix; //allow consonnant suffix
79 VowelSeq withHook; //hook & bowl
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}
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}
194 const int VSeqCount = sizeof(VSeqList)/sizeof(VowelSeqInfo);
199 VSeqPair SortedVSeqList[VSeqCount];
201 const int CSeqCount = sizeof(CSeqList)/sizeof(ConSeqInfo);
206 CSeqPair SortedCSeqList[CSeqCount];
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},
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},
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},
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},
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},
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},
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},
241 {vs_oe, cs_n}, {vs_oe, cs_t},
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},
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},
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},
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},
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},
259 {vs_uye, cs_n}, {vs_uye, cs_t},
260 {vs_uyer, cs_n}, {vs_uyer, cs_t}
264 const int VCPairCount = sizeof(VCPairList)/sizeof(VCPair);
266 //TODO: auto-complete: e.g. luan -> lua^n
268 typedef int (UkEngine::* UkKeyProc)(UkKeyEvent & ev);
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
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);
297 bool UkEngine::m_classInit = false;
299 //------------------------------------------------
300 int tripleVowelCompare(const void *p1, const void *p2)
302 VSeqPair *t1 = (VSeqPair *)p1;
303 VSeqPair *t2 = (VSeqPair *)p2;
305 for (int i=0; i<3; i++) {
306 if (t1->v[i] < t2->v[i])
308 if (t1->v[i] > t2->v[i])
314 //------------------------------------------------
315 int tripleConCompare(const void *p1, const void *p2)
317 CSeqPair *t1 = (CSeqPair *)p1;
318 CSeqPair *t2 = (CSeqPair *)p2;
320 for (int i=0; i<3; i++) {
321 if (t1->c[i] < t2->c[i])
323 if (t1->c[i] > t2->c[i])
329 //------------------------------------------------
330 int VCPairCompare(const void *p1, const void *p2)
332 VCPair *t1 = (VCPair *)p1;
333 VCPair *t2 = (VCPair *)p2;
347 //----------------------------------------------------------
348 bool isValidCV(ConSeq c, VowelSeq v)
350 if (c == cs_nil || v == vs_nil)
353 VowelSeqInfo & vInfo = VSeqList[v];
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
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};
364 for (i=0; kVseq[i] != vs_nil && kVseq[i] != v; i++);
365 return (kVseq[i] != vs_nil);
372 //----------------------------------------------------------
373 bool isValidVC(VowelSeq v, ConSeq c)
375 if (v == vs_nil || c == cs_nil)
378 VowelSeqInfo & vInfo = VSeqList[v];
379 if (!vInfo.conSuffix)
382 ConSeqInfo & cInfo = CSeqList[c];
389 if (bsearch(&p, VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare))
395 //----------------------------------------------------------
396 bool isValidCVC(ConSeq c1, VowelSeq v, ConSeq c2)
399 return (c1 == cs_nil || c2 != cs_nil);
402 return isValidVC(v, c2);
405 return isValidCV(c1, v);
407 bool okCV = isValidCV(c1, v);
408 bool okVC = isValidVC(v, c2);
414 //check some exceptions: vc fails but cvc passes
417 if (c1 == cs_qu && v == vs_y && (c2 == cs_n || c2 == cs_nh))
421 if (c1 == cs_gi && (v == vs_e || v == vs_er) && (c2 == cs_n || c2 == cs_ng))
427 //------------------------------------------------
428 void engineClassInit()
432 for (i=0; i < VSeqCount; i++) {
434 SortedVSeqList[i].v[j] = VSeqList[i].v[j];
435 SortedVSeqList[i].vs = (VowelSeq)i;
438 for (i=0; i < CSeqCount; i++) {
440 SortedCSeqList[i].c[j] = CSeqList[i].c[j];
441 SortedCSeqList[i].cs = (ConSeq)i;
444 qsort(SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare);
445 qsort(SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare);
446 qsort(VCPairList, VCPairCount, sizeof(VCPair), VCPairCompare);
448 for (i=0; i<vnl_lastChar; i++)
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;
459 IsVnVowel[vnl_dd] = false;
460 IsVnVowel[vnl_DD] = false;
463 //------------------------------------------------
464 VowelSeq lookupVSeq(VnLexiName v1, VnLexiName v2, VnLexiName v3)
471 VSeqPair *pInfo = (VSeqPair *)bsearch(&key, SortedVSeqList, VSeqCount, sizeof(VSeqPair), tripleVowelCompare);
477 //------------------------------------------------
478 ConSeq lookupCSeq(VnLexiName c1, VnLexiName c2, VnLexiName c3)
485 CSeqPair *pInfo = (CSeqPair *)bsearch(&key, SortedCSeqList, CSeqCount, sizeof(CSeqPair), tripleConCompare);
491 //------------------------------------------------------------------
492 int UkEngine::processRoof(UkKeyEvent & ev)
494 if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0)
495 return processAppend(ev);
509 target = vnl_nonVnChar;
515 int curTonePos, newTonePos, tone;
517 bool roofRemoved = false;
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;
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;
532 newVs = VSeqList[vs].withRoof;
537 if (newVs == vs_nil) {
538 if (VSeqList[vs].roofPos == -1)
539 return processAppend(ev); //roof is not applicable
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
546 VnLexiName newCh = (curCh == vnl_ar)? vnl_a : ((curCh == vnl_er)? vnl_e : vnl_o);
547 changePos = vStart + VSeqList[vs].roofPos;
549 if (!m_pCtrl->options.freeMarking && changePos != m_current)
550 return processAppend(ev);
552 markChange(changePos);
553 m_buffer[changePos].vnSym = newCh;
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);
560 newVs = lookupVSeq(m_buffer[vStart].vnSym);
562 pInfo = &VSeqList[newVs];
566 pInfo = &VSeqList[newVs];
567 if (target != vnl_nonVnChar && pInfo->v[pInfo->roofPos] != target)
568 return processAppend(ev);
570 //check validity of new VC and CV
574 if (m_buffer[m_current].c1Offset != -1)
575 c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
577 if (m_buffer[m_current].c2Offset != -1)
578 c2 = m_buffer[m_current-m_buffer[m_current].c2Offset].cseq;
580 valid = isValidCVC(c1, newVs, c2);
582 return processAppend(ev);
584 if (doubleChangeUO) {
588 changePos = vStart + pInfo->roofPos;
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;
598 m_buffer[changePos].vnSym = pInfo->v[pInfo->roofPos];
602 for (i=0; i < pInfo->len; i++) { //update sub-sequences
603 m_buffer[vStart+i].vseq = pInfo->sub[i];
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;
617 if (curTonePos != newTonePos && tone != 0) {
618 markChange(newTonePos);
619 m_buffer[newTonePos].tone = tone;
620 markChange(curTonePos);
621 m_buffer[curTonePos].tone = 0;
625 m_singleMode = false;
633 //------------------------------------------------------------------
634 // can only be called from processHook
635 //------------------------------------------------------------------
636 int UkEngine::processHookWithUO(UkKeyEvent & ev)
640 int curTonePos, newTonePos, tone;
641 bool hookRemoved = false;
642 bool removeWithUndo = true;
643 bool toneRemoved = false;
647 if (!m_pCtrl->options.freeMarking && m_buffer[m_current].vOffset != 0)
648 return processAppend(ev);
650 vEnd = m_current - m_buffer[m_current].vOffset;
651 vs = m_buffer[vEnd].vseq;
652 vStart = vEnd - (VSeqList[vs].len - 1);
654 curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
655 tone = m_buffer[curTonePos].tone;
660 newVs = VSeqList[vs].withHook;
662 m_buffer[vStart].vnSym = vnl_uh;
664 else {// v[0] = vnl_uh, -> uo
665 newVs = lookupVSeq(vnl_u, vnl_o, v[2]);
667 m_buffer[vStart].vnSym = vnl_u;
668 m_buffer[vStart+1].vnSym = vnl_o;
670 toneRemoved = (m_buffer[vStart].tone != 0);
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)
679 newVs = VSeqList[vs].withHook;
680 markChange(vStart+1);
681 m_buffer[vStart+1].vnSym = vnl_oh;
684 newVs = lookupVSeq(vnl_uh, vnl_oh, v[2]);
687 m_buffer[vStart].vnSym = vnl_uh;
688 m_buffer[vStart+1].vnSym = vnl_oh;
691 markChange(vStart+1);
692 m_buffer[vStart+1].vnSym = vnl_oh;
696 else {// v[1] = vnl_oh, -> uo
697 newVs = lookupVSeq(vnl_u, vnl_o, v[2]);
698 if (v[0] == vnl_uh) {
700 m_buffer[vStart].vnSym = vnl_u;
701 m_buffer[vStart+1].vnSym = vnl_o;
704 markChange(vStart+1);
705 m_buffer[vStart+1].vnSym = vnl_o;
708 toneRemoved = (m_buffer[vStart+1].tone != 0);
711 default: //vneHookAll, vneHookUO:
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)
719 markChange(vStart+1);
720 m_buffer[vStart+1].vnSym = vnl_oh;
724 newVs = VSeqList[vs].withHook;
726 m_buffer[vStart].vnSym = vnl_uh;
727 newVs = VSeqList[newVs].withHook;
728 m_buffer[vStart+1].vnSym = vnl_oh;
732 newVs = VSeqList[vs].withHook;
734 m_buffer[vStart].vnSym = vnl_uh;
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;
743 else { //v[1] == vnl_oh, u+o+ -> uo
744 newVs = lookupVSeq(vnl_u, vnl_o, v[2]); //vs_uo;
746 m_buffer[vStart].vnSym = vnl_u;
747 m_buffer[vStart+1].vnSym = vnl_o;
749 toneRemoved = (m_buffer[vStart].tone != 0 || m_buffer[vStart+1].tone != 0);
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];
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;
771 if (curTonePos != newTonePos && tone != 0) {
772 markChange(newTonePos);
773 m_buffer[newTonePos].tone = tone;
774 markChange(curTonePos);
775 m_buffer[curTonePos].tone = 0;
778 if (hookRemoved && removeWithUndo) {
779 m_singleMode = false;
787 //------------------------------------------------------------------
788 int UkEngine::processHook(UkKeyEvent & ev)
790 if (!m_pCtrl->vietKey || m_current < 0 || m_buffer[m_current].vOffset < 0)
791 return processAppend(ev);
795 int curTonePos, newTonePos, tone;
797 bool hookRemoved = false;
801 vEnd = m_current - m_buffer[m_current].vOffset;
802 vs = m_buffer[vEnd].vseq;
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);
812 vStart = vEnd - (VSeqList[vs].len - 1);
813 curTonePos = vStart + getTonePosition(vs, vEnd == m_current);
814 tone = m_buffer[curTonePos].tone;
816 newVs = VSeqList[vs].withHook;
817 if (newVs == vs_nil) {
818 if (VSeqList[vs].hookPos == -1)
819 return processAppend(ev); //hook is not applicable
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);
831 return processAppend(ev);
835 return processAppend(ev);
839 return processAppend(ev);
842 if (ev.evType == vneHook_uo && curCh == vnl_ab)
843 return processAppend(ev);
846 markChange(changePos);
847 m_buffer[changePos].vnSym = newCh;
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);
854 newVs = lookupVSeq(m_buffer[vStart].vnSym);
856 pInfo = &VSeqList[newVs];
860 pInfo = &VSeqList[newVs];
864 if (pInfo->v[pInfo->hookPos] != vnl_uh)
865 return processAppend(ev);
868 if (pInfo->v[pInfo->hookPos] != vnl_oh)
869 return processAppend(ev);
872 if (pInfo->v[pInfo->hookPos] != vnl_ab)
873 return processAppend(ev);
875 default: //vneHook_uo, vneHookAll
876 if (ev.evType == vneHook_uo && pInfo->v[pInfo->hookPos] == vnl_ab)
877 return processAppend(ev);
880 //check validity of new VC and CV
884 if (m_buffer[m_current].c1Offset != -1)
885 c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
887 if (m_buffer[m_current].c2Offset != -1)
888 c2 = m_buffer[m_current-m_buffer[m_current].c2Offset].cseq;
890 valid = isValidCVC(c1, newVs, c2);
893 return processAppend(ev);
895 changePos = vStart + pInfo->hookPos;
896 if (!m_pCtrl->options.freeMarking && changePos != m_current)
897 return processAppend(ev);
899 markChange(changePos);
900 m_buffer[changePos].vnSym = pInfo->v[pInfo->hookPos];
903 for (i=0; i < pInfo->len; i++) { //update sub-sequences
904 m_buffer[vStart+i].vseq = pInfo->sub[i];
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;
918 if (curTonePos != newTonePos && tone != 0) {
919 markChange(newTonePos);
920 m_buffer[newTonePos].tone = tone;
921 markChange(curTonePos);
922 m_buffer[curTonePos].tone = 0;
926 m_singleMode = false;
934 //----------------------------------------------------------
935 int UkEngine::getTonePosition(VowelSeq vs, bool terminated)
937 VowelSeqInfo & info = VSeqList[vs];
941 if (info.roofPos != -1)
943 if (info.hookPos != -1) {
944 if (vs == vs_uhoh || vs == vs_uhohi || vs == vs_uhohu) //u+o+, u+o+u, u+o+i
952 if (m_pCtrl->options.modernStyle &&
953 (vs == vs_oa || vs == vs_oe ||vs == vs_uy))
956 return terminated ? 0 : 1;
959 //----------------------------------------------------------
960 int UkEngine::processTone(UkKeyEvent & ev)
962 if (m_current < 0 || !m_pCtrl->vietKey)
963 return processAppend(ev);
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);
971 if (m_buffer[p].tone == ev.tone) {
972 m_buffer[p].tone = 0;
973 m_singleMode = false;
978 m_buffer[p].tone = ev.tone;
982 if (m_buffer[m_current].vOffset < 0)
983 return processAppend(ev);
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);
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 ` ? ~
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);
1006 if (m_buffer[tonePos].tone == ev.tone) {
1007 markChange(tonePos);
1008 m_buffer[tonePos].tone = 0;
1009 m_singleMode = false;
1015 markChange(tonePos);
1016 m_buffer[tonePos].tone = ev.tone;
1020 //----------------------------------------------------------
1021 int UkEngine::processDd(UkKeyEvent & ev)
1023 if (!m_pCtrl->vietKey || m_current < 0)
1024 return processAppend(ev);
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]))
1034 m_singleMode = true;
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;
1046 if (m_buffer[m_current].c1Offset < 0) {
1047 return processAppend(ev);
1050 pos = m_current - m_buffer[m_current].c1Offset;
1051 if (!m_pCtrl->options.freeMarking && pos != m_current)
1052 return processAppend(ev);
1054 if (m_buffer[pos].cseq == cs_d) {
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;
1063 if (m_buffer[pos].cseq == cs_dd) {
1066 m_buffer[pos].cseq = cs_d;
1067 m_buffer[pos].vnSym = vnl_d;
1068 m_singleMode = false;
1074 return processAppend(ev);
1077 //----------------------------------------------------------
1078 VnLexiName changeCase(VnLexiName x)
1080 if (x == vnl_nonVnChar)
1083 return (VnLexiName)(x+1);
1084 return (VnLexiName)(x-1);
1087 //----------------------------------------------------------
1088 inline VnLexiName vnToLower(VnLexiName x)
1090 if (x == vnl_nonVnChar)
1092 if (!(x & 0x01)) //even
1093 return (VnLexiName)(x+1);
1097 //----------------------------------------------------------
1098 int UkEngine::processMapChar(UkKeyEvent & ev)
1101 int shiftPressed = 0;
1103 m_keyCheckFunc(&shiftPressed, &capsLockOn);
1106 ev.vnSym = changeCase(ev.vnSym);
1108 int ret = processAppend(ev);
1109 if (!m_pCtrl->vietKey)
1112 if (m_current >= 0 && m_buffer[m_current].form != vnw_empty &&
1113 m_buffer[m_current].form != vnw_nonVn) {
1120 // mapChar doesn't apply
1122 WordInfo & entry = m_buffer[m_current];
1125 // test if undo is needed
1126 if (entry.form != vnw_empty && entry.form != vnw_nonVn) {
1127 VnLexiName prevSym = entry.vnSym;
1129 prevSym = (VnLexiName)(prevSym - 1);
1131 if (prevSym == ev.vnSym) {
1132 if (entry.form != vnw_c) {
1133 int vStart, vEnd, curTonePos, newTonePos, tone;
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);
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;
1158 markChange(m_current);
1165 ev.evType = vneNormal;
1166 ev.chType = m_pCtrl->input.getCharType(ev.keyCode);
1167 ev.vnSym = IsoToVnLexi(ev.keyCode);
1168 ret = processAppend(ev);
1170 m_singleMode = false;
1177 //----------------------------------------------------------
1178 int UkEngine::processTelexW(UkKeyEvent & ev)
1180 if (!m_pCtrl->vietKey)
1181 return processAppend(ev);
1184 static bool usedAsMapChar = false;
1186 int shiftPressed = 0;
1188 m_keyCheckFunc(&shiftPressed, &capsLockOn);
1190 if (usedAsMapChar) {
1191 ev.evType = vneMapChar;
1192 ev.vnSym = isupper(ev.keyCode)? vnl_Uh : vnl_uh;
1194 ev.vnSym = changeCase(ev.vnSym);
1196 ret = processMapChar(ev);
1200 usedAsMapChar = false;
1201 ev.evType = vneHookAll;
1202 return processHook(ev);
1207 ev.evType = vneHookAll;
1208 usedAsMapChar = false;
1209 ret = processHook(ev);
1213 ev.evType = vneMapChar;
1214 ev.vnSym = isupper(ev.keyCode)? vnl_Uh : vnl_uh;
1216 ev.vnSym = changeCase(ev.vnSym);
1218 usedAsMapChar = true;
1219 return processMapChar(ev);
1224 //----------------------------------------------------------
1225 int UkEngine::checkEscapeVIQR(UkKeyEvent & ev)
1229 WordInfo & entry = m_buffer[m_current];
1231 if (entry.form == vnw_v || entry.form == vnw_cv) {
1232 switch (ev.keyCode) {
1234 escape = (entry.vnSym == vnl_a || entry.vnSym == vnl_o || entry.vnSym == vnl_e);
1237 escape = (entry.vnSym == vnl_a);
1240 escape = (entry.vnSym == vnl_o || entry.vnSym == vnl_u);
1247 escape = (entry.tone == 0);
1251 else if (entry.form == vnw_nonVn) {
1252 unsigned char ch = toupper(entry.keyCode);
1253 switch (ev.keyCode) {
1255 escape = (ch == 'A' || ch == 'O' || ch == 'E');
1258 escape = (ch == 'A');
1261 escape = (ch == 'O' || ch == 'U');
1268 escape = (ch == 'A' || ch == 'E' || ch == 'I' ||
1269 ch == 'O' || ch == 'U' || ch == 'Y');
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;
1280 p->vnSym = vnl_nonVnChar;
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;
1290 m_pOutBuf[0] = '\\';
1291 m_pOutBuf[1] = ev.keyCode;
1293 m_outputWritten = true;
1298 //----------------------------------------------------------
1299 int UkEngine::processAppend(UkKeyEvent & ev)
1302 switch (ev.chType) {
1305 if (ev.keyCode == ENTER_CHAR) {
1306 if (m_pCtrl->options.macroEnabled && macroMatch(ev))
1313 m_singleMode = false;
1314 return processWordEnd(ev);
1317 if (m_pCtrl->vietKey && m_pCtrl->charsetId == CONV_CHARSET_VIQR && checkEscapeVIQR(ev))
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);
1327 entry.caps = (entry.vnSym != ev.vnSym);
1328 if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1330 markChange(m_current);
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
1342 return appendVowel(ev);
1344 return appendConsonnant(ev);
1352 //----------------------------------------------------------
1353 int UkEngine::appendVowel(UkKeyEvent & ev)
1355 bool autoCompleted = false;
1358 WordInfo & entry = m_buffer[m_current];
1360 VnLexiName lowerSym = vnToLower(ev.vnSym);
1361 VnLexiName canSym = (VnLexiName)StdVnNoTone[lowerSym];
1363 entry.vnSym = canSym;
1364 entry.caps = (lowerSym != ev.vnSym);
1365 entry.tone = (lowerSym - canSym)/2;
1366 entry.keyCode = ev.keyCode;
1368 if (m_current == 0 || !m_pCtrl->vietKey) {
1370 entry.c1Offset = entry.c2Offset = -1;
1372 entry.vseq = lookupVSeq(canSym);
1374 if (!m_pCtrl->vietKey ||
1375 ((m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) && isalpha(entry.keyCode)) ) {
1378 markChange(m_current);
1382 WordInfo & prev = m_buffer[m_current-1];
1386 int tone, newTone, tonePos, newTonePos;
1388 switch (prev.form) {
1392 entry.c1Offset = entry.c2Offset = -1;
1394 entry.vseq = newVs = lookupVSeq(canSym);
1400 entry.form = vnw_nonVn;
1401 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1407 prevTonePos = (m_current - 1) - (VSeqList[vs].len - 1) + getTonePosition(vs, true);
1408 tone = m_buffer[prevTonePos].tone;
1410 if (lowerSym != canSym && tone != 0) //new sym has a tone, but there's is already a preceeding tone
1413 if (VSeqList[vs].len == 3)
1415 else if (VSeqList[vs].len == 2)
1416 newVs = lookupVSeq(VSeqList[vs].v[0], VSeqList[vs].v[1], canSym);
1418 newVs = lookupVSeq(VSeqList[vs].v[0], canSym);
1421 if (newVs != vs_nil && prev.form == vnw_cv) {
1422 cs = m_buffer[m_current - 1 - prev.c1Offset].cseq;
1423 if (!isValidCV(cs, newVs))
1427 if (newVs == vs_nil) {
1428 entry.form = vnw_nonVn;
1429 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1433 entry.form = prev.form;
1434 if (prev.form == vnw_cv)
1435 entry.c1Offset = prev.c1Offset + 1;
1437 entry.c1Offset = -1;
1438 entry.c2Offset = -1;
1443 newTone = (lowerSym - canSym)/2;
1447 tonePos = getTonePosition(newVs, true) + ((m_current - 1) - VSeqList[vs].len + 1);
1448 markChange(tonePos);
1449 m_buffer[tonePos].tone = tone;
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);
1461 m_buffer[newTonePos].tone = tone;
1464 if (newTone != 0 && newTone != tone) {
1466 markChange(prevTonePos);
1467 m_buffer[prevTonePos].tone = tone;
1475 newVs = lookupVSeq(canSym);
1477 if (!isValidCV(cs, newVs)) {
1478 entry.form = vnw_nonVn;
1479 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1483 entry.form = vnw_cv;
1485 entry.c2Offset = -1;
1489 if (cs == cs_gi && prev.tone != 0) {
1490 if (entry.tone == 0)
1491 entry.tone = prev.tone;
1492 markChange(m_current - 1);
1500 if (!autoCompleted &&
1501 (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING) &&
1502 isalpha(entry.keyCode)) {
1506 markChange(m_current);
1510 //----------------------------------------------------------
1511 int UkEngine::appendConsonnant(UkKeyEvent & ev)
1513 bool complexEvent = false;
1515 WordInfo & entry = m_buffer[m_current];
1517 VnLexiName lowerSym = vnToLower(ev.vnSym);
1519 entry.vnSym = lowerSym;
1520 entry.caps = (lowerSym != ev.vnSym);
1521 entry.keyCode = ev.keyCode;
1524 if (m_current == 0 || !m_pCtrl->vietKey) {
1527 entry.c2Offset = -1;
1529 entry.cseq = lookupCSeq(lowerSym);
1530 if (!m_pCtrl->vietKey || m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1532 markChange(m_current);
1536 ConSeq cs, newCs, c1;
1540 WordInfo & prev = m_buffer[m_current-1];
1542 switch (prev.form) {
1544 entry.form = vnw_nonVn;
1545 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1546 if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1548 markChange(m_current);
1553 entry.c2Offset = -1;
1555 entry.cseq = lookupCSeq(lowerSym);
1556 if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1558 markChange(m_current);
1564 if (vs == vs_uoh || vs == vs_uho) {
1569 if (prev.c1Offset != -1)
1570 c1 = m_buffer[m_current-1-prev.c1Offset].cseq;
1572 newCs = lookupCSeq(lowerSym);
1573 isValid = isValidCVC(c1, newVs, newCs);
1578 markChange(m_current-1);
1579 prev.vnSym = vnl_oh;
1580 prev.vseq = vs_uhoh;
1581 complexEvent = true;
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;
1591 if (prev.form == vnw_v) {
1592 entry.form = vnw_vc;
1593 entry.c1Offset = -1;
1597 else { //prev == vnw_cv
1598 entry.form = vnw_cvc;
1599 entry.c1Offset = prev.c1Offset + 1;
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) {
1611 m_buffer[newIdx].tone = m_buffer[oldIdx].tone;
1613 m_buffer[oldIdx].tone = 0;
1619 entry.form = vnw_nonVn;
1620 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1627 if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1629 markChange(m_current);
1635 if (CSeqList[cs].len == 3)
1637 else if (CSeqList[cs].len == 2)
1638 newCs = lookupCSeq(CSeqList[cs].c[0], CSeqList[cs].c[1], lowerSym);
1640 newCs = lookupCSeq(CSeqList[cs].c[0], lowerSym);
1642 if (newCs != cs_nil && (prev.form == vnw_vc || prev.form == vnw_cvc)) {
1643 // Check CVC combination
1645 if (prev.c1Offset != -1)
1646 c1 = m_buffer[m_current-1-prev.c1Offset].cseq;
1648 int vIdx = (m_current - 1) - prev.vOffset;
1649 vs = m_buffer[vIdx].vseq;
1650 isValid = isValidCVC(c1, vs, newCs);
1656 if (newCs == cs_nil) {
1657 entry.form = vnw_nonVn;
1658 entry.c1Offset = entry.c2Offset = entry.vOffset = -1;
1661 if (prev.form == vnw_c) {
1664 entry.c2Offset = -1;
1667 else if (prev.form == vnw_vc) {
1668 entry.form = vnw_vc;
1669 entry.c1Offset = -1;
1671 entry.vOffset = prev.vOffset + 1;
1674 entry.form = vnw_cvc;
1675 entry.c1Offset = prev.c1Offset + 1;
1677 entry.vOffset = prev.vOffset + 1;
1681 if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1683 markChange(m_current);
1687 if (m_pCtrl->charsetId != CONV_CHARSET_UNI_CSTRING)
1689 markChange(m_current);
1693 //----------------------------------------------------------
1694 int UkEngine::processEscChar(UkKeyEvent & ev)
1696 if (m_pCtrl->vietKey &&
1697 m_current >=0 && m_buffer[m_current].form != vnw_empty && m_buffer[m_current].form != vnw_nonVn) {
1700 return processAppend(ev);
1703 //----------------------------------------------------------
1704 void UkEngine::pass(int keyCode)
1707 m_pCtrl->input.keyCodeToEvent(keyCode, ev);
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)
1717 WordInfo & entry = m_buffer[m_current];
1718 if (IsVnVowel[entry.vnSym]) {
1721 entry.vseq = lookupVSeq(entry.vnSym);
1722 entry.c1Offset = entry.c2Offset = -1;
1727 entry.c2Offset = -1;
1729 entry.cseq = lookupCSeq(entry.vnSym);
1732 if (ev.evType == vneNormal &&
1733 ((entry.keyCode >= 'a' && entry.keyCode <= 'z') ||
1734 (entry.keyCode >= 'A' && entry.keyCode <= 'Z') ) )
1736 markChange(m_current);
1739 //----------------------------------------------------------
1740 int UkEngine::process(unsigned int keyCode, int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
1745 m_changePos = m_current+1;
1747 m_pOutSize = &outSize;
1748 m_outputWritten = false;
1750 m_keyRestored = false;
1751 m_keyRestoring = false;
1752 m_outType = UkCharOutput;
1754 m_pCtrl->input.keyCodeToEvent(keyCode, ev);
1758 ret = (this->*UkKeyProcList[ev.evType])(ev);
1762 if (m_current < 0 || ev.evType == vneNormal || ev.evType == vneEscChar) {
1763 ret = processAppend(ev);
1768 markChange(m_current); //this will assign m_backs to 1 and mark the character for output
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) )
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);
1783 if ((!m_pCtrl->options.spellCheckEnabled || m_singleMode) ||
1785 (m_current < 1 || m_buffer[m_current-1].form != vnw_nonVn)) ) {
1787 ret = processNoSpellCheck(ev);
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);
1796 m_keyStrokes[m_keyCurrent].ev = ev;
1797 m_keyStrokes[m_keyCurrent].converted = (ret && !m_keyRestored);
1803 outType = m_outType;
1808 if (!m_outputWritten) {
1809 writeOutput(outBuf, outSize);
1811 outType = m_outType;
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)
1827 int i, bytesWritten;
1829 StringBOStream os(outBuf, outSize);
1830 VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId);
1831 pCharset->startOutput();
1833 for (i = m_changePos; i <= m_current; i++) {
1834 if (m_buffer[i].vnSym != vnl_nonVnChar) {
1836 stdChar = m_buffer[i].vnSym + VnStdCharOffset;
1837 if (m_buffer[i].caps)
1839 if (m_buffer[i].tone != 0)
1840 stdChar += m_buffer[i].tone * 2;
1843 stdChar = IsoToStdVnChar(m_buffer[i].keyCode);
1846 if (stdChar != INVALID_STD_CHAR)
1847 ret = pCharset->putChar(os, stdChar, bytesWritten);
1850 outSize = os.getOutBytes();
1851 return (ret? 0 : VNCONV_OUT_OF_MEMORY);
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)
1865 if (m_pCtrl->charsetId == CONV_CHARSET_XUTF8 ||
1866 m_pCtrl->charsetId == CONV_CHARSET_UNICODE)
1867 return (last - first + 1);
1869 StringBOStream os(0, 0);
1870 int i, bytesWritten;
1872 VnCharset *pCharset = VnCharsetLibObj.getVnCharset(m_pCtrl->charsetId);
1873 pCharset->startOutput();
1875 for (i = first; i <= last; i++) {
1876 if (m_buffer[i].vnSym != vnl_nonVnChar) {
1878 stdChar = m_buffer[i].vnSym + VnStdCharOffset;
1879 if (m_buffer[i].caps)
1881 if (m_buffer[i].tone != 0)
1882 stdChar += m_buffer[i].tone*2;
1885 stdChar = m_buffer[i].keyCode;
1888 if (stdChar != INVALID_STD_CHAR)
1889 pCharset->putChar(os, stdChar, bytesWritten);
1892 int len = os.getOutBytes();
1893 if (m_pCtrl->charsetId == CONV_CHARSET_UNIDECOMPOSED)
1898 //---------------------------------------------
1899 void UkEngine::markChange(int pos)
1901 if (pos < m_changePos) {
1902 m_backs += getSeqSteps(pos, m_changePos-1);
1907 //----------------------------------------------------------------
1908 // Called from processBackspace to keep
1909 // character buffer (m_buffer) and key stroke buffer in synch
1910 //----------------------------------------------------------------
1911 void UkEngine::synchKeyStrokeBuffer()
1913 //synchronize with key-stroke buffer
1914 if (m_keyCurrent >= 0)
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)
1926 //---------------------------------------------
1927 int UkEngine::processBackspace(int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
1929 outType = UkCharOutput;
1930 if (!m_pCtrl->vietKey || m_current < 0) {
1937 m_changePos = m_current + 1;
1938 markChange(m_current);
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) {
1951 synchKeyStrokeBuffer();
1956 int curTonePos, newTonePos, tone, vStart, vEnd;
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;
1966 if (tone == 0 || curTonePos == newTonePos ||
1967 (curTonePos == m_current && m_buffer[m_current].tone != 0)) {
1971 synchKeyStrokeBuffer();
1975 markChange(newTonePos);
1976 m_buffer[newTonePos].tone = tone;
1977 markChange(curTonePos);
1978 m_buffer[curTonePos].tone = 0;
1980 synchKeyStrokeBuffer();
1982 writeOutput(outBuf, outSize);
1986 //------------------------------------------------
1987 void UkEngine::reset()
1991 m_singleMode = false;
1995 //------------------------------------------------
1996 void UkEngine::resetKeyBuf()
2001 //------------------------------------------------
2002 UkEngine::UkEngine()
2009 m_bufSize = MAX_UK_ENGINE;
2010 m_keyBufSize = MAX_UK_ENGINE;
2013 m_singleMode = false;
2017 m_keyRestored = false;
2020 //----------------------------------------------------
2021 // make sure there are at least 10 entries available
2022 //----------------------------------------------------
2023 void UkEngine::prepareBuffer()
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) {
2036 memmove(m_buffer, m_buffer+rid, (m_current-rid+1)*sizeof(WordInfo));
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;
2051 #define ENTER_CHAR 13
2052 enum VnCaseType {VnCaseNoChange, VnCaseAllCapital, VnCaseAllSmall};
2054 //----------------------------------------------------
2055 int UkEngine::macroMatch(UkKeyEvent & ev)
2058 int shiftPressed = 0;
2060 m_keyCheckFunc(&shiftPressed, &capsLockOn);
2062 if (shiftPressed && (ev.keyCode ==' ' || ev.keyCode == ENTER_CHAR))
2065 const StdVnChar *pMacText = NULL;
2066 StdVnChar key[MAX_MACRO_KEY_LEN+1];
2067 StdVnChar *pKeyStart;
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];
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)
2079 if (i>=0 && m_buffer[i].form != vnw_empty)
2083 if (m_buffer[i].vnSym != vnl_nonVnChar) {
2084 key[0] = m_buffer[i].vnSym + VnStdCharOffset;
2085 if (m_buffer[i].caps)
2087 key[0] += m_buffer[i].tone*2;
2090 key[0] = m_buffer[i].keyCode;
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)
2098 key[j-i] += m_buffer[j].tone*2;
2101 key[j-i] = m_buffer[j].keyCode;
2103 key[m_current-i+1] = 0;
2104 //search macro table
2105 pMacText = m_pCtrl->macStore.lookup(key+1);
2107 i++; //mark the position where change is needed
2108 pKeyStart = key + 1;
2112 pMacText = m_pCtrl->macStore.lookup(key);
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;
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;
2140 else macroCase = VnCaseNoChange;
2142 // Convert case of macro text according to macroCase
2144 while (pMacText[charCount] != 0)
2147 for (i = 0; i < charCount; i++)
2149 if (macroCase == VnCaseAllCapital)
2150 macroText[i] = StdVnToUpper(pMacText[i]);
2151 else if (macroCase == VnCaseAllSmall)
2152 macroText[i] = StdVnToLower(pMacText[i]);
2154 macroText[i] = pMacText[i];
2157 // Convert to target output charset
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;
2166 //write the last input character
2168 if (outSize < *m_pOutSize) {
2169 maxOutSize = *m_pOutSize - outSize;
2170 if (ev.vnSym != vnl_nonVnChar)
2171 vnChar = ev.vnSym + VnStdCharOffset;
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;
2180 int backs = m_backs; //store m_backs before calling reset
2182 m_outputWritten = true;
2184 *m_pOutSize = outSize;
2188 //----------------------------------------------------
2189 int UkEngine::restoreKeyStrokes(int & backs, unsigned char *outBuf, int & outSize, UkOutputType & outType)
2191 outType = UkKeyOutput;
2192 if (!lastWordHasVnMark()) {
2199 m_changePos = m_current+1;
2202 bool converted = false;
2203 for (keyStart = m_keyCurrent; keyStart >= 0 && m_keyStrokes[keyStart].ev.chType != ukcWordBreak; keyStart--) {
2204 if (m_keyStrokes[keyStart].converted) {
2211 //no key stroke has been converted, so it doesn't make sense to restore key strokes
2217 //int i = m_current;
2218 while (m_current >=0 && m_buffer[m_current].form != vnw_empty)
2220 markChange(m_current+1);
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;
2231 m_pCtrl->input.keyCodeToSymbol(m_keyStrokes[i].ev.keyCode, ev);
2232 m_keyStrokes[i].converted = false;
2236 m_keyRestoring = false;
2241 //--------------------------------------------------
2242 void UkEngine::setSingleMode()
2244 m_singleMode = true;
2247 //--------------------------------------------------
2248 void SetupUnikeyEngine()
2250 SetupInputClassifierTable();
2254 //Calculate IsoStdVnCharMap
2255 for (i=0; i < 256; i++) {
2256 IsoStdVnCharMap[i] = i;
2259 for (i=0; SpecialWesternChars[i]; i++) {
2260 IsoStdVnCharMap[SpecialWesternChars[i]] = (vnl_lastChar + i) + VnStdCharOffset;
2263 for (i=0; i < 256; i++) {
2264 if ((lexi = IsoToVnLexi(i)) != vnl_nonVnChar) {
2265 IsoStdVnCharMap[i] = lexi + VnStdCharOffset;
2270 //--------------------------------------------------
2271 bool UkEngine::atWordBeginning()
2273 return (m_current < 0 || m_buffer[m_current].form == vnw_empty);
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)
2283 if (m_pCtrl->options.macroEnabled && macroMatch(ev))
2286 if (!m_pCtrl->options.spellCheckEnabled || m_singleMode || m_current < 0 || m_keyRestoring) {
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);
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;
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);
2314 if (m_keyRestored && outSize < *m_pOutSize) {
2315 m_pOutBuf[outSize] = ev.keyCode;
2317 *m_pOutSize = outSize;
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()
2333 switch (m_buffer[m_current].form) {
2341 return !VSeqList[m_buffer[m_current].vseq].complete;
2344 int vIndex = m_current - m_buffer[m_current].vOffset;
2345 VowelSeq vs = m_buffer[vIndex].vseq;
2346 if (!VSeqList[vs].complete)
2348 ConSeq cs = m_buffer[m_current].cseq;
2350 if (m_buffer[m_current].c1Offset != -1)
2351 c1 = m_buffer[m_current-m_buffer[m_current].c1Offset].cseq;
2353 if (!isValidCVC(c1, vs, cs)) {
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))
2369 //---------------------------------------------------------------------------
2370 // Test if last word has a Vietnamese mark, that is tones, decorators
2371 //---------------------------------------------------------------------------
2372 bool UkEngine::lastWordHasVnMark()
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)
2381 if (sym != StdVnRootChar[sym] )