1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU Lesser 7 * General Public License Version 2.1 only ("LGPL") or the Common Development and 8 * Distribution License ("CDDL")(collectively, the "License"). You may not use this 9 * file except in compliance with the License. You can obtain a copy of the CDDL at 10 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at 11 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the 12 * specific language governing permissions and limitations under the License. When 13 * distributing the software, include this License Header Notice in each file and 14 * include the full text of the License in the License file as well as the 15 * following notice: 16 * 17 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 18 * (CDDL) 19 * For Covered Software in this distribution, this License shall be governed by the 20 * laws of the State of California (excluding conflict-of-law provisions). 21 * Any litigation relating to this License shall be subject to the jurisdiction of 22 * the Federal Courts of the Northern District of California and the state courts 23 * of the State of California, with venue lying in Santa Clara County, California. 24 * 25 * Contributor(s): 26 * 27 * If you wish your version of this file to be governed by only the CDDL or only 28 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to 29 * include this software in this distribution under the [CDDL or LGPL Version 2.1] 30 * license." If you don't indicate a single choice of license, a recipient has the 31 * option to distribute your version of this file under either the CDDL or the LGPL 32 * Version 2.1, or to extend the choice of license to its licensees as provided 33 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL 34 * Version 2 license, then the option applies only if the new code is made subject 35 * to such option by the copyright holder. 36 */ 37 38 #ifdef HAVE_CONFIG_H 39 #include <config.h> 40 #endif 41 42 #include <cstdio> 43 #include <cstdlib> 44 45 #include "ime.h" 46 47 #include "imi_data.h" 48 #include "imi_view.h" 49 #include "imi_clewin.h" 50 #include "imi_options.h" 51 #include "ic_history.h" 52 53 #ifndef CLE_SUNPINYIN_DATADIR 54 #define CLE_SUNPINYIN_DATADIR "/usr/lib/iiim/le/cle/input_methods/data" 55 #endif 56 57 static ImeResult sunpinyin_Initialize(ImeInfo ime_info); 58 static ImeResult sunpinyin_Destroy(ImeInfo ime_info); 59 static ImeResult sunpinyin_Process_Key_Event(ImeInputContext ic, ImeKey key_event); 60 static ImeResult sunpinyin_Process_Aux_Event(ImeInputContext ic, ImeEvent pEvent); 61 static ImeResult sunpinyin_Create_Session(ImeInputContext ic); 62 static ImeResult sunpinyin_Destroy_Session(ImeInputContext ic); 63 static ImeResult sunpinyin_focus_in(ImeInputContext ic); 64 static ImeResult sunpinyin_Attach_User(ImeInputContext ic); 65 static ImeResult sunpinyin_Dettach_User(ImeInputContext ic); 66 static ImeResult sunpinyin_Attach_Desktop(ImeInputContext ic); 67 static ImeResult sunpinyin_Dettach_Desktop(ImeInputContext ic); 68 69 ImeMethodsRec sunpinyin_methods = { 70 100, /* version */ 71 sunpinyin_Initialize, /* ImeInitialize */ 72 sunpinyin_Destroy, /* ImeDestroy */ 73 sunpinyin_Process_Key_Event, /* ImeProcessKeyEvent */ 74 sunpinyin_Process_Aux_Event, /* ImeProcessAuxEvent */ 75 sunpinyin_Create_Session, /* ImeAttachSession */ 76 sunpinyin_Destroy_Session, /* ImeDetachSession */ 77 sunpinyin_focus_in, /* ImeFocusIn */ 78 NULL, /* ImeFocusOut */ 79 sunpinyin_Attach_User, /* ImeAttachUser */ 80 sunpinyin_Dettach_User, /* ImeDetachUser */ 81 sunpinyin_Attach_Desktop, /* ImeAttachDesktop */ 82 sunpinyin_Dettach_Desktop, /* ImeDetachDesktop */ 83 NULL, /* ImeGetErrorMessage */ 84 }; 85 86 const char* InputStyleOptions[] = { 87 "", 88 "", 89 NULL 90 }; 91 92 const char* CharSetOptions[] = { 93 "GB2312", 94 "GBK", 95 NULL 96 }; 97 98 const char* LayoutOptions[] = { 99 "", 100 "", 101 NULL 102 }; 103 104 105 ImePropertyRec sunpinyin_options[] = { 106 { 1, ENCODE_UTF8, "/input_style", "", NULL, ImeProperty_Selection, {0, 0}, 0}, 107 { 2, ENCODE_UTF8, "/charset", "", NULL, ImeProperty_Selection, {0, 0}, 1}, 108 109 {30, ENCODE_UTF8, "/PageKey/Minus", "-/=", NULL, ImeProperty_Toggle, {0, 0}, 1}, 110 {31, ENCODE_UTF8, "/PageKey/Braket", "[/]", NULL, ImeProperty_Toggle, {0, 0}, 1}, 111 {32, ENCODE_UTF8, "/PageKey/Comma", ",/.", NULL, ImeProperty_Toggle, {0, 0}, 0} 112 113 #if 0 114 , 115 { 3, ENCODE_UTF8, "/history_memory", "(0,10)", NULL, ImeProperty_Int, {0, 10}, 5}, 116 { 4, ENCODE_UTF8, "/ranking_method", "", NULL, ImeProperty_Toggle, {0, 0}, 1}, 117 { 5, ENCODE_UTF8, "/layout", "", NULL, ImeProperty_Selection, {0, 0}, 0}, 118 119 { 7, ENCODE_UTF8, "/Fuzzy", "", NULL, ImeProperty_Toggle, {0, 0}, 0}, 120 { 8, ENCODE_UTF8, "/Fuzzy/zh", "zh = z", NULL, ImeProperty_Toggle, {0, 0}, 0}, 121 { 9, ENCODE_UTF8, "/Fuzzy/ch", "ch = c", NULL, ImeProperty_Toggle, {0, 0}, 0}, 122 { 10,ENCODE_UTF8, "/Fuzzy/sh", "sh = s", NULL, ImeProperty_Toggle, {0, 0}, 0}, 123 { 11,ENCODE_UTF8, "/Fuzzy/ln", "l = n", NULL, ImeProperty_Toggle, {0, 0}, 0}, 124 { 12,ENCODE_UTF8, "/Fuzzy/fh", "f = h", NULL, ImeProperty_Toggle, {0, 0}, 0}, 125 { 13,ENCODE_UTF8, "/Fuzzy/ang", "an = ang", NULL, ImeProperty_Toggle, {0, 0}, 0}, 126 { 14,ENCODE_UTF8, "/Fuzzy/eng", "en = eng", NULL, ImeProperty_Toggle, {0, 0}, 0}, 127 { 15,ENCODE_UTF8, "/Fuzzy/ing", "in = ing", NULL, ImeProperty_Toggle, {0, 0}, 0}, 128 { 50,ENCODE_UTF8, "/owner_aux", "", NULL, ImeProperty_Toggle, {0, 0}, 0}, 129 #endif 130 }; 131 132 ImePropertyListRec sunpinyin_optionlist = { 133 sizeof(sunpinyin_options)/sizeof(ImePropertyRec), 134 sunpinyin_options 135 }; 136 137 static void setOptionsFromPL(int count, ImePropertyRec* pl, CSunpinyinOptions* dst) 138 { 139 for (int i=0; i < count; ++i) { 140 switch (pl[i].id) { 141 case 1: 142 dst->m_ViewType = pl[i].value.int_value; break; 143 case 2: 144 dst->m_GBK = pl[i].value.int_value; break; 145 146 case 30: 147 dst->m_MinusAsPageUp = pl[i].value.int_value; break; 148 case 31: 149 dst->m_BracketAsPageUp = pl[i].value.int_value; break; 150 case 32: 151 dst->m_CommaAsPageUp = pl[i].value.int_value; break; 152 153 #if 0 154 case 3: 155 dst->m_MemoryPower = pl[i].value.int_value; break; 156 case 4: 157 dst->m_ContextRanking = pl[i].value.int_value; break; 158 case 5: 159 dst->m_LayoutVeritcal = pl[i].value.int_value; break; 160 case 7: 161 dst->m_Fuzzy = pl[i].value.int_value; break; 162 case 8: 163 dst->m_Fuzzy_zh = pl[i].value.int_value; break; 164 case 9: 165 dst->m_Fuzzy_ch = pl[i].value.int_value; break; 166 case 10: 167 dst->m_Fuzzy_sh = pl[i].value.int_value; break; 168 case 11: 169 dst->m_Fuzzy_ln = pl[i].value.int_value; break; 170 case 12: 171 dst->m_Fuzzy_fh = pl[i].value.int_value; break; 172 case 13: 173 dst->m_Fuzzy_ang = pl[i].value.int_value; break; 174 case 14: 175 dst->m_Fuzzy_eng = pl[i].value.int_value; break; 176 case 15: 177 dst->m_Fuzzy_ing = pl[i].value.int_value; break; 178 179 case 50: 180 #endif 181 182 default: 183 break; 184 } 185 } 186 } 187 188 static ImeInfoRec sunpinyin_inforec = { 189 100, //version 190 0, 191 ENCODE_UTF8, 192 "SunPinyin-ef1a0a92-9f0e-410b-b8a8-e6f296c1801c", //uuid 193 "Sun", //name 194 " <Phill.Zhang (at) sun.com>", 195 "Sun's intelligent Pinyin IM", 196 "Copyright (c) 2005 Sun Microsystems", 197 "sunpinyin_logo.xpm", 198 "zh_CN.GB18030,zh_CN.UTF-8,zh_CN.GBK,zh_CN.GB2312,zh", 199 &sunpinyin_optionlist, 200 NULL 201 }; 202 203 ImmServices imm_services; 204 205 class CSunpinyinUserData { 206 public: 207 CSunpinyinUserData() : m_pPref(NULL), m_pHistory(NULL) { } 208 209 CSunpinyinOptions* m_pPref; 210 CBigramHistory* m_pHistory; 211 }; 212 213 extern "C" { 214 215 ImeResult RegisterIME(ImmServices srvs, ImeInfo* ppinfo, ImeMethods* pmthds, int argc, char **argv) 216 { 217 #ifdef DEBUG 218 printf("Register Sun Pinyin IM...\n"); 219 fflush(stdout); 220 #endif 221 sunpinyin_options[0].range.multiString_range = InputStyleOptions; 222 sunpinyin_options[1].range.multiString_range = CharSetOptions; 223 sunpinyin_options[4].range.multiString_range = LayoutOptions; 224 225 CCLEWinHandler::sm_imm_srvs = imm_services = srvs; 226 227 *ppinfo = &sunpinyin_inforec; 228 *pmthds = &sunpinyin_methods; 229 230 return (IME_OK); 231 } 232 233 } 234 235 static const char* getLMPath(void) 236 { 237 return CLE_SUNPINYIN_DATADIR"/lm_sc.t3g"; 238 } 239 240 static const char* getPYTriePath(void) 241 { 242 return CLE_SUNPINYIN_DATADIR"/pydict_sc.bin"; 243 } 244 245 static CIMIData* s_pSunPinyinData = NULL; 246 247 static ImeResult sunpinyin_Initialize(ImeInfo sunpinyin_info) 248 { 249 bool suc=false; 250 251 #ifdef DEBUG 252 printf("sunpinyin_Initialize...\n"); 253 fflush(stdout); 254 #endif 255 256 s_pSunPinyinData = new CIMIData(); 257 if (s_pSunPinyinData && s_pSunPinyinData->loadResource(getLMPath(), getPYTriePath())) { 258 suc = true; 259 } 260 261 if (!suc) { 262 delete s_pSunPinyinData; 263 s_pSunPinyinData = NULL; 264 return IME_FAIL; 265 } 266 267 return IME_OK; 268 } 269 270 static ImeResult sunpinyin_Attach_User(ImeInputContext ic) 271 { 272 CSunpinyinUserData* pud = NULL; 273 if ((pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER)) == NULL) { 274 pud = new CSunpinyinUserData(); 275 pud->m_pPref = new CSunpinyinOptions(); 276 ImePropertyListRec* pl = imm_services->ImmGetPropertyList(ic); 277 if (pl != NULL) { // It must be non-zero 278 setOptionsFromPL(pl->count, pl->properties, pud->m_pPref); 279 } 280 pud->m_pHistory = new CBigramHistory(); 281 int sz = 0; 282 unsigned *psaved = (unsigned*)imm_services->ImmLoadUserProfile(ic, "history", &sz); 283 if (psaved != NULL) { 284 pud->m_pHistory->loadFromBuffer(psaved, sz); 285 imm_services->ImmFreeUserProfile(psaved); 286 } 287 imm_services->ImmSetData(ic, IME_SCOPE_USER, pud); 288 } 289 return IME_OK; 290 } 291 292 static ImeResult sunpinyin_Dettach_User(ImeInputContext ic) 293 { 294 CSunpinyinUserData* pud = NULL; 295 if ((pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER)) != NULL) { 296 size_t sz = 0; 297 void* pbuf = NULL; 298 if (pud->m_pHistory) { 299 pud->m_pHistory->bufferize(&pbuf, &sz); 300 if (pbuf) { 301 imm_services->ImmSaveUserProfile(ic, "history", pbuf, int(sz)); 302 free(pbuf); 303 } 304 } 305 delete pud; 306 } 307 imm_services->ImmSetData(ic, IME_SCOPE_USER, NULL); 308 return IME_OK; 309 } 310 311 static ImeResult sunpinyin_Attach_Desktop(ImeInputContext ic) 312 { 313 return IME_OK; 314 } 315 316 static ImeResult sunpinyin_Dettach_Desktop(ImeInputContext ic) 317 { 318 return IME_OK; 319 } 320 321 static ImeResult sunpinyin_Destroy(ImeInfo sunpinyin_info) 322 { 323 delete s_pSunPinyinData; 324 s_pSunPinyinData = NULL; 325 CCLEWinHandler::sm_imm_srvs = NULL; 326 327 return (IME_OK); 328 } 329 330 static ImeResult 331 _sunpinyin_Create_Session(ImeInputContext ic, int viewType) 332 { 333 #ifdef DEBUG 334 printf(">>>>>>>> SunPinyin alloc private session data...\n"); 335 fflush(stdout); 336 #endif 337 338 CSunpinyinUserData* pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER); 339 if (pud == NULL) { //should not happen, but now, CLE make it happen 340 sunpinyin_Attach_User(ic); 341 pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER); 342 } 343 344 CIMIData* pdata = s_pSunPinyinData; 345 346 CIMIContext *pic = NULL; 347 CIMIView *pv = NULL; 348 CCLEWinHandler *pwh = NULL; 349 350 #ifdef DEBUG 351 printf(" -------->>>>Creating searching related core structure...\n"); 352 fflush(stdout); 353 #endif 354 pic = new CIMIContext(); 355 pic->setCoreData(pdata); 356 pic->setHistoryMemory(pud->m_pHistory); 357 pic->setNonCompleteSyllable(true); 358 pic->clear(); 359 360 #ifdef DEBUG 361 printf(" -------->>>>Creating view...\n"); 362 fflush(stdout); 363 #endif 364 pv = CIMIViewFactory::createView(viewType); 365 #ifdef DEBUG 366 printf(" -------->>>>Setting view options...\n"); 367 fflush(stdout); 368 #endif 369 370 bool enable_gbk = pud->m_pPref->m_GBK; 371 if (imm_services->ImmGetSessionEncoding(ic) == ENCODE_GB2312) 372 pud->m_pPref->m_GBK = false; 373 pv->setPreference(pud->m_pPref); 374 pud->m_pPref->m_GBK = enable_gbk; 375 376 /* FIXME: we should add the candidate window size to preferences */ 377 pv->s_CandiWindowSize = 10; 378 379 #ifdef DEBUG 380 printf(" -------->>>>Bundle searching structure with view...\n"); 381 fflush(stdout); 382 #endif 383 pv->attachIC(pic); 384 385 #ifdef DEBUG 386 printf(" -------->>>>Creating GUI_Handler...\n"); 387 fflush(stdout); 388 #endif 389 pwh = new CCLEWinHandler(); 390 pwh->setOptions(pv->getPreference()); 391 pv->attachWinHandler(pwh); 392 pwh->setMainWindow(ic); 393 394 imm_services->ImmSetData(ic, IME_SCOPE_SESSION, pv); 395 #ifdef DEBUG 396 printf(" -------->>>>Done\n"); 397 fflush(stdout); 398 #endif 399 return (IME_OK); 400 } 401 402 static ImeResult 403 sunpinyin_Create_Session(ImeInputContext ic) 404 { 405 CSunpinyinUserData* pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER); 406 if (pud == NULL) { //should not happen, but now, CLE make it happen 407 sunpinyin_Attach_User(ic); 408 pud = (CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER); 409 } 410 411 int viewType = (pud->m_pPref->m_ViewType==0)?(CIMIViewFactory::SVT_CLASSIC):(CIMIViewFactory::SVT_MODERN); 412 return _sunpinyin_Create_Session(ic, viewType); 413 } 414 415 static ImeResult sunpinyin_Destroy_Session(ImeInputContext ic) 416 { 417 CIMIView *pv = (CIMIView*)imm_services->ImmGetData(ic, IME_SCOPE_SESSION); 418 imm_services->ImmSetData(ic, IME_SCOPE_SESSION, NULL); 419 420 if (pv) { 421 CIMIContext *pic = pv->getIC(); 422 CIMIWinHandler *pwh = pv->getWinHandler(); 423 424 delete pic; 425 delete pwh; 426 delete pv; 427 } 428 429 return (IME_OK); 430 } 431 432 static ImeResult sunpinyin_Process_Aux_Event(ImeInputContext ic, ImeEvent pEvent) 433 { 434 CIMIView *pv = (CIMIView*)imm_services->ImmGetData(ic, IME_SCOPE_SESSION); 435 436 int pgno; 437 bool relative; 438 439 if (pv && pEvent) { 440 switch (pEvent->type) { 441 case IME_EVENT_IMM_NOTIFY: 442 switch (pEvent->notify_event.trigger) { 443 case IMM_TRIGGER_FULL_HALF_PUNC: 444 pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, pEvent->notify_event.value); 445 break; 446 case IMM_TRIGGER_FULL_HALF_SIMBOL: 447 pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSIMBOL, pEvent->notify_event.value); 448 break; 449 } 450 return IME_OK; 451 452 case IME_EVENT_PROPERTY: 453 return sunpinyin_focus_in(ic); 454 455 case IME_EVENT_CANDI_SELECT: 456 pv->onCandidateSelectRequest(pEvent->candidate_select_event.param); 457 return IME_OK; 458 459 case IME_EVENT_CANDI_PAGE: 460 switch (pEvent->candidate_page_event.param) { 461 case ImeCandidatesPageFirst: 462 pgno = 0; relative = false; break; 463 case ImeCandidatesPagePrevious: 464 pgno = -1; relative = true; break; 465 case ImeCandidatesPageNext: 466 pgno = 1; relative = true; break; 467 case ImeCandidatesPageLast: 468 pgno = -1; relative = false; break; 469 default: 470 return IME_UNPROCESSED_EVENT; 471 } 472 pv->onCandidatePageRequest(pgno, relative); 473 return IME_OK; 474 } 475 } 476 477 return IME_UNPROCESSED_EVENT; 478 } 479 480 ImeResult sunpinyin_focus_in(ImeInputContext ic) 481 { 482 CIMIView *pv = (CIMIView*)imm_services->ImmGetData(ic, IME_SCOPE_SESSION); 483 if (pv) { 484 CSunpinyinOptions* pup = ((CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER))->m_pPref; 485 ImePropertyListRec* pl = imm_services->ImmGetPropertyList(ic); 486 setOptionsFromPL(pl->count, pl->properties, pup); 487 488 CSunpinyinOptions* pvp = (CSunpinyinOptions*)pv->getPreference(); 489 490 if ((pup->m_GBK != pvp->m_GBK && imm_services->ImmGetSessionEncoding(ic) != ENCODE_GB2312) || 491 (pup->m_ViewType != pvp->m_ViewType)) { 492 sunpinyin_Destroy_Session(ic); 493 imm_services->ImmHidePreedit(ic); 494 imm_services->ImmHideCandidates(ic); 495 sunpinyin_Create_Session(ic); 496 } else { 497 pvp->m_MinusAsPageUp = pup->m_MinusAsPageUp; 498 pvp->m_CommaAsPageUp = pup->m_CommaAsPageUp; 499 pvp->m_BracketAsPageUp = pup->m_BracketAsPageUp; 500 } 501 } 502 return IME_OK; 503 } 504 505 static ImeResult sunpinyin_Process_Key_Event(ImeInputContext ic, ImeKey key_event) 506 { 507 int ret = IME_UNUSED_KEY; 508 509 /* sunpinyin_focus_in(ic); */ 510 511 CIMIView *pv = (CIMIView*)imm_services->ImmGetData(ic, IME_SCOPE_SESSION); 512 513 #ifdef DEBUG 514 printf(">>>>>>>>SunPinyin Process a key [0x%X, 0x%X, 0x%X] on view @%X", 515 key_event->keycode, key_event->keychar, key_event->modifier,pv); 516 fflush(stdout); 517 #endif 518 519 //switch input GUI style between classic/modern style 520 if ((key_event->modifier & (IME_CTRL_MASK|IME_SHIFT_MASK|IME_ALT_MASK)) == IME_CTRL_MASK && key_event->keycode == IME_VK_BACK_QUOTE) { 521 522 if (pv) { 523 CSunpinyinOptions* pup = ((CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER))->m_pPref; 524 pup->m_ViewType = (pup->m_ViewType)?0:1; 525 sunpinyin_focus_in(ic); 526 } 527 ret = IME_OK; 528 529 } else if ((key_event->modifier & (IME_CTRL_MASK|IME_SHIFT_MASK|IME_ALT_MASK)) == IME_CTRL_MASK && key_event->keycode == IME_VK_K) { 530 if (pv) { 531 if (imm_services->ImmGetSessionEncoding(ic) != ENCODE_GB2312) { 532 CSunpinyinOptions* pup = ((CSunpinyinUserData*)imm_services->ImmGetData(ic, IME_SCOPE_USER))->m_pPref; 533 pup->m_GBK = (pup->m_GBK)?0:1; 534 sunpinyin_focus_in(ic); 535 } 536 ret = IME_OK; 537 } 538 } else if (pv) { 539 ((CCLEWinHandler*)pv->getWinHandler())->setMainWindow(ic); 540 if (pv->onKeyEvent(key_event->keycode, key_event->keychar, key_event->modifier)) 541 ret = IME_OK; 542 } 543 544 #ifdef DEBUG 545 printf((ret == IME_OK)?" ==>used!":"==>not used!"); 546 fflush(stdout); 547 #endif 548 549 return ImeResult(ret); 550 } 551