1requires 2.0.0 2%alltop{ 3/* 4 * Copyright © 2009,2010 Red Hat, Inc. All rights reserved. 5 * Copyright © 2009,2010 Ding-Yi Chen <dchen at redhat.com> 6 * 7 * This file is part of the ibus-chewing Project. 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 2 12 * of the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that ill be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 */ 23#include <stdlib.h> 24#include <libintl.h> 25#include <ibus.h> 26#include <chewing.h> 27#include <string.h> 28#include <stdio.h> 29#define GETTEXT_PACKAGE "gtk20" 30#include <glib/gi18n.h> 31#include <X11/Xlib.h> 32#include <X11/XKBlib.h> 33#include <ctype.h> 34#include "IBusChewingUtil.h" 35#include "IBusChewingProperties.h" 36#include "IBusChewingPreEdit.h" 37#include "IBusChewingSystray.h" 38#ifdef USE_GSETTINGS 39#include "GSettingsBackend.h" 40#endif 41#ifdef USE_GCONF2 42#include "GConf2Backend.h" 43#endif 44%} 45 46enum CHEWING_INPUT_STYLE { 47 IN_APPLICATION, 48 IN_CANDIDATE, 49} Chewing:Input:Style; 50 51/** 52 * EngineFlag: 53 * @UNIT_TEST: Engine is in unit test mode (i.e. not connect to Bus) 54 * @INITIALIZED: Engine is initialized. 55 * @ENABLED: Engine is enabled. 56 * @FOCUS_IN: Engine gets focus. 57 * @SHOW_CANDIDATE: Whether the candidate selection should be shown. Important for Plain Zhuyin. 58 * @IS_PASSWORD: Current input is password. 59 * @PROPERTIES_REGISTERED: Engine registered the properties. 60 * 61 * 62 * Engine flags show the current states of engine, 63 * Thus those might change quite often. 64 * 65 * If SYSTEM_KEYBOARD_LAYOUT is enabled, IBus-Chewing uses whatever 66 * key_syms received. Otherwise it converts according to en-US 67 * keyboard layout. 68 * 69 * It does not make any difference for en-US keyboards (which are 70 * the default keyboards in Taiwan). However, for French AZERTY 71 * keyboard. Pressing the key below 1 returns 72 * 73 * SYSTEM_KEYBOARD_LAYOUT enabled (key a): ㄇ 74 * SYSTEM_KEYBOARD_LAYOUT disabled (key q): ㄆ 75 * 76 * libChewing does not have direct method to clean commit buffer. 77 * Thus NEED_COMMIT is needed to prevent unnecessary commit. 78 */ 79enum ENGINE_FLAG { 80 INITIALIZED = 0x1, 81 ENABLED = 0x2, 82 FOCUS_IN = 0x4, 83 IS_PASSWORD = 0x8, 84 PROPERTIES_REGISTERED = 0x10, 85} Engine:Flag; 86 87%h{ 88extern MkdgPropertySpec propSpecs[]; 89extern const gchar *page_labels[]; 90extern const gchar *button_labels[]; 91extern GtkResponseType button_responses[]; 92 93#define ibus_chewing_engine_has_status_flag(self,f) mkdg_has_flag(self->_priv->statusFlags,f) 94#define ibus_chewing_engine_set_status_flag(self,f) mkdg_set_flag(self->_priv->statusFlags,f) 95#define ibus_chewing_engine_clear_status_flag(self,f) mkdg_clear_flag(self->_priv->statusFlags,f) 96 97#define ibus_chewing_engine_has_capabilite(self,f) mkdg_has_flag(self->_priv->capabilite,f) 98#define ibus_chewing_engine_set_capabilite(self,f) mkdg_set_flag(self->_priv->capabilite,f) 99#define ibus_chewing_engine_clear_capabilite(self,f) mkdg_clear_flag(self->_priv->capabilite,f) 100 101 102#define is_password(self) ibus_chewing_engine_has_status_flag(self, ENGINE_FLAG_IS_PASSWORD) 103#define ibus_chewing_engine_is_chinese_mode(self) ibus_chewing_pre_edit_get_chi_eng_mode(self->icPreEdit) 104#define is_caps_lock(self) (is_caps_led_on(self->_priv->pDisplay)) 105 106#define ibus_text_is_empty(iText) ((iText==NULL) || STRING_IS_EMPTY(iText->text)) 107 108#define is_plain_chewing ibus_chewing_pre_edit_get_property_boolean(self->icPreEdit, "plain-zhuyin") 109 110#define cursor_current chewing_cursor_Current(self->icPreEdit->context) 111%} 112 113%privateheader{ 114#include "maker-dialog.h" 115void ibus_chewing_engine_handle_Default(IBusChewingEngine * self, 116 guint keyval, 117 gboolean shiftPressed); 118#define self_handle_Default ibus_chewing_engine_handle_Default 119 120void ibus_chewing_engine_candidate_clicked(IBusEngine * engine, 121 guint index, guint button, 122 guint state); 123void ibus_chewing_engine_property_activate(IBusEngine * engine, 124 const gchar * prop_name, 125 guint prop_state); 126void ibus_chewing_engine_set_content_type(IBusEngine * engine, 127 guint purpose, guint hints); 128 129void refresh_pre_edit_text(IBusChewingEngine * self); 130void update_pre_edit_text(IBusChewingEngine * self); 131void refresh_aux_text(IBusChewingEngine * self); 132void update_aux_text(IBusChewingEngine * self); 133void update_lookup_table(IBusChewingEngine * self); 134void refresh_outgoing_text(IBusChewingEngine * self); 135void commit_text(IBusChewingEngine * self); 136%} 137 138%{ 139extern gint ibus_chewing_verbose; 140 141#define IBUS_CHEWING_MAIN 142#include "IBusConfigBackend.h" 143%} 144 145class IBus:Chewing:Engine from IBus:Engine { 146 public IBusChewingPreEdit *icPreEdit= NULL 147 destroywith ibus_chewing_pre_edit_free; 148 149 public GtkWidget *sDialog=NULL; 150 151 /** 152 * preEditText: text in pre-edit buffer. 153 * 154 * When IN_APPLICATION: Shows pre-edit string. 155 * When IN_CANDIDATE: content of pre-edit is hidden, 156 * the content is visiable in auxiliary field. 157 * 158 * This variable is not update until next "update" event, 159 * so it can be used in integration test. 160 */ 161 public IBusText *preEditText=NULL; 162 163 /** 164 * auxText: text to be displayed in auxiliary candidate window. 165 * If PLAIN CHEWING 166 * When IN_APPLICATION: show chewing_aux_string 167 * When IN_CANDIDATE: show the preedit 168 * 169 * This variable is not update until next "update" event, 170 * so it can be used in integration test. 171 */ 172 public IBusText *auxText=NULL; 173 174 /** 175 * outgoingText: text to be committed to engine. 176 * 177 * This variable is not update until next "update" event, 178 * so it can be used in integration test. 179 */ 180 public IBusText *outgoingText=NULL; 181 182 protected IBusChewingSystrayIcon *iChiEngSystrayIcon=NULL 183 destroywith ibus_chewing_systray_icon_free; 184 185 /* Log file */ 186 protected FILE *logFile = NULL; 187 188 /* Indicate the string is outgoing to avoid update() set 189 * NEED_COMMIT status 190 * again when focus in event is trigger, 191 * because the last key is Return key 192 * and libchewing is still Commit state. */ 193 194 private EngineFlag statusFlags = 0; 195 196 private IBusCapabilite capabilite =0 ; 197 198 private ChewingInputStyle inputStyle; 199 200 public IBusProperty *chieng_prop = { 201 g_object_ref_sink(ibus_property_new 202 ("InputMode", PROP_TYPE_NORMAL, 203 SELF_GET_CLASS(self)->chieng_prop_label_chi, 204 NULL, 205 SELF_GET_CLASS(self)->chieng_prop_tooltip_chi, 206 TRUE, TRUE, 207 PROP_STATE_UNCHECKED, NULL)) 208 } 209 destroywith g_object_unref; 210 211 public IBusProperty *alnumSize_prop = { 212 g_object_ref_sink(ibus_property_new 213 ("chewing_alnumSize_prop", PROP_TYPE_NORMAL, 214 SELF_GET_CLASS(self)->alnumSize_prop_label_half, 215 NULL, NULL, TRUE, TRUE, 216 PROP_STATE_UNCHECKED, NULL)) 217 } 218 destroywith g_object_unref; 219 220 public IBusPropList *prop_list = 221 { g_object_ref_sink(ibus_prop_list_new()) } 222 destroywith g_object_unref; 223 224 protected IBusKeymap *keymap_us = { ibus_keymap_get("us") }; 225 226 private Display *pDisplay = { XOpenDisplay(NULL) } 227 destroywith XCloseDisplay; 228 229 classwide IBusText *chieng_prop_label_chi = { 230 g_object_ref_sink(ibus_text_new_from_static_string(_("Chi"))) 231 }; 232 classwide IBusText *chieng_prop_label_eng = { 233 g_object_ref_sink(ibus_text_new_from_static_string(_("Eng"))) 234 }; 235 classwide IBusText *chieng_prop_tooltip_chi = { 236 g_object_ref_sink(ibus_text_new_from_static_string 237 (_("Click to switch to English"))) 238 }; 239 classwide IBusText *chieng_prop_tooltip_eng = { 240 g_object_ref_sink(ibus_text_new_from_static_string 241 (_("Click to switch to Chinese"))) 242 }; 243 classwide IBusText *chieng_prop_symbol_chi = { 244 g_object_ref_sink(ibus_text_new_from_static_string("中")) 245 }; 246 classwide IBusText *chieng_prop_symbol_eng = { 247 g_object_ref_sink(ibus_text_new_from_static_string("英")) 248 }; 249 classwide IBusText *alnumSize_prop_label_full = { 250 g_object_ref_sink(ibus_text_new_from_static_string(_("Full"))) 251 }; 252 classwide IBusText *alnumSize_prop_label_half = { 253 g_object_ref_sink(ibus_text_new_from_static_string(_("Half"))) 254 }; 255 classwide IBusText *emptyText = { 256 g_object_ref_sink(ibus_text_new_from_static_string("")) 257 }; 258 259 init(self) { 260 /* initialize the object here */ 261 IBUS_CHEWING_LOG(INFO, "init() %sinitialized", 262 (self->_priv-> 263 statusFlags & ENGINE_FLAG_INITIALIZED) ? "" : 264 "un"); 265 if (self->_priv->statusFlags & ENGINE_FLAG_INITIALIZED) { 266 return; 267 } 268 269 IBUS_CHEWING_LOG(INFO, "init() CHEWING_DATADIR_REAL=%s", 270 QUOTE_ME(CHEWING_DATADIR_REAL)); 271 272 gchar *logFilename = getenv("IBUS_CHEWING_LOGFILE"); 273 if (logFilename != NULL) { 274 self->logFile = fopen(logFilename, "w+"); 275 if (self->logFile == NULL) { 276 IBUS_CHEWING_LOG(WARN, "init() Cannot write to logfile %s, ignored", 277 logFilename); 278 }else{ 279 mkdg_log_set_file(self->logFile); 280 } 281 } 282 283 if (self->icPreEdit == NULL){ 284#ifdef USE_GSETTINGS 285 MkdgBackend *backend = 286 mkdg_g_settings_backend_new(QUOTE_ME(PROJECT_SCHEMA_ID), 287 QUOTE_ME(PROJECT_SCHEMA_DIR), NULL); 288#elif defined USE_GCONF2 289 MkdgBackend *backend = 290 gconf2_backend_new(QUOTE_ME(PROJECT_SCHEMA_BASE), NULL); 291#else 292 MkdgBackend *backend = NULL; 293 g_error("Flag GSETTINGS_SUPPORT or GCONF2_SUPPORT are required!"); 294 return; 295#endif /* USE_GSETTINGS */ 296 self->icPreEdit = ibus_chewing_pre_edit_new(backend); 297 } 298 g_assert(self->icPreEdit); 299 300 self->icPreEdit->engine=IBUS_ENGINE(self); 301 302 /* init properties */ 303 ibus_prop_list_append(self->prop_list, self->chieng_prop); 304 ibus_prop_list_append(self->prop_list, self->alnumSize_prop); 305 306 ibus_chewing_engine_set_status_flag(self, 307 ENGINE_FLAG_INITIALIZED); 308 309 /* In case we cannot open X display */ 310 if (self->_priv->pDisplay==NULL){ 311 IBUS_CHEWING_LOG(WARN, "init() XOpenDisplay return NULL"); 312 } 313 IBUS_CHEWING_LOG(DEBUG, "init() Done"); 314 } 315 316 class_init(klass) { 317 ibus_engine_class->property_activate = 318 ibus_chewing_engine_property_activate; 319 ibus_engine_class->process_key_event = 320 ibus_chewing_engine_process_key_event; 321 ibus_engine_class->candidate_clicked = 322 ibus_chewing_engine_candidate_clicked; 323#if IBUS_CHECK_VERSION(1, 5, 4) 324 ibus_engine_class->set_content_type = 325 ibus_chewing_engine_set_content_type; 326#endif 327 } 328 329 constructor(self){ 330 /* GOB need this for generating constructor */ 331 } 332 333 protected void use_setting(self) { 334 IBUS_CHEWING_LOG(INFO, "use_setting()"); 335 336 ibus_chewing_pre_edit_use_all_configure(self->icPreEdit); 337 338 /* Input style */ 339 if(ibus_chewing_properties_read_boolean_general(self->icPreEdit->iProperties, 340 "ibus/general", "embed-preedit-text", NULL)){ 341 self->_priv->inputStyle=CHEWING_INPUT_STYLE_IN_APPLICATION; 342 }else{ 343 self->_priv->inputStyle=CHEWING_INPUT_STYLE_IN_CANDIDATE; 344 } 345 } 346 347 protected void restore_mode(self) { 348 IBUS_CHEWING_LOG(DEBUG, "restore_mode() statusFlags=%x", 349 self->_priv->statusFlags); 350 if (self->_priv->pDisplay!=NULL){ 351 /* Restore Led Mode only make sense if pDisplay is available */ 352 if (ibus_chewing_pre_edit_has_flag(self->icPreEdit, FLAG_SYNC_FROM_IM)){ 353 IBUS_CHEWING_LOG(DEBUG, "restore_mode() FLAG_SYNC_FROM_IM"); 354 if (ibus_chewing_engine_is_chinese_mode(self) == is_caps_lock(self)) { 355 /* ChiEng mode does not agree each other */ 356 set_caps_led(!ibus_chewing_engine_is_chinese_mode(self), self->_priv->pDisplay); 357 } 358 }else if (ibus_chewing_pre_edit_has_flag(self->icPreEdit, FLAG_SYNC_FROM_KEYBOARD)){ 359 IBUS_CHEWING_LOG(DEBUG, "restore_mode() FLAG_SYNC_FROM_KEYBOARD"); 360 chewing_set_ChiEngMode(self->icPreEdit->context, (is_caps_lock(self))? 0: CHINESE_MODE ); 361 } 362 self_refresh_property(self, "InputMode"); 363 } 364 } 365 366 protected void update(self) { 367 IBUS_CHEWING_LOG(DEBUG, "update() statusFlags=%x", 368 self->_priv->statusFlags); 369 update_pre_edit_text(self); 370 update_aux_text(self); 371 commit_text(self); 372 373 IBUS_CHEWING_LOG(DEBUG, 374 "update() nPhoneSeq=%d statusFlags=%x", 375 chewing_get_phoneSeqLen(self->icPreEdit->context), 376 self->_priv->statusFlags); 377 update_lookup_table(self); 378 self_refresh_property_list(self); 379 } 380 381 protected void refresh_property(self, const gchar * prop_name) 382 { 383#ifndef UNIT_TEST 384 IBUS_CHEWING_LOG(DEBUG, "refresh_property(%s) status=%x", 385 prop_name, self->_priv->statusFlags); 386 if (ibus_chewing_systray_chi_eng_icon_create_or_hide(self)){ 387 ibus_chewing_systray_chi_eng_icon_refresh_value(self); 388 } 389 390 if (STRING_EQUALS(prop_name, "InputMode")) { 391 if (ibus_chewing_pre_edit_get_chi_eng_mode(self->icPreEdit)) { 392 /* Chinese */ 393 ibus_property_set_label(self->chieng_prop, 394 SELF_GET_CLASS(self)-> 395 chieng_prop_label_chi); 396 ibus_property_set_tooltip(self->chieng_prop, 397 SELF_GET_CLASS(self)-> 398 chieng_prop_tooltip_chi); 399#if IBUS_CHECK_VERSION(1, 5, 0) 400 ibus_property_set_symbol(self->chieng_prop, 401 SELF_GET_CLASS(self)-> 402 chieng_prop_symbol_chi); 403#endif 404 } else { 405 /* English */ 406 ibus_property_set_label(self->chieng_prop, 407 SELF_GET_CLASS(self)-> 408 chieng_prop_label_eng); 409 ibus_property_set_tooltip(self->chieng_prop, 410 SELF_GET_CLASS(self)-> 411 chieng_prop_tooltip_eng); 412#if IBUS_CHECK_VERSION(1, 5, 0) 413 ibus_property_set_symbol(self->chieng_prop, 414 SELF_GET_CLASS(self)-> 415 chieng_prop_symbol_eng); 416#endif 417 } 418 ibus_engine_update_property(IBUS_ENGINE(self), 419 self->chieng_prop); 420 421 } else if (STRING_EQUALS(prop_name, "chewing_alnumSize_prop")) { 422 if (chewing_get_ShapeMode(self->icPreEdit->context)) { 423 /* Full-Sized Shape */ 424 ibus_property_set_label(self->alnumSize_prop, 425 SELF_GET_CLASS(self)-> 426 alnumSize_prop_label_full); 427 } else { 428 /* Half-Sized Shape */ 429 ibus_property_set_label(self->alnumSize_prop, 430 SELF_GET_CLASS(self)-> 431 alnumSize_prop_label_half); 432 } 433 if (self->_priv-> 434 statusFlags & ENGINE_FLAG_PROPERTIES_REGISTERED) 435 ibus_engine_update_property(IBUS_ENGINE(self), 436 self->alnumSize_prop); 437 } 438#endif 439 } 440 441 /** 442 * refresh_property_list: 443 * @self: this instances. 444 * 445 * Refresh the property list (language bar). 446 */ 447 public void refresh_property_list(self) { 448#ifndef UNIT_TEST 449 self_refresh_property(self, "InputMode"); 450 self_refresh_property(self, "chewing_alnumSize_prop"); 451 452#endif 453 } 454 455 /** 456 * hide_property_list: 457 * @self: this instances. 458 * 459 * Hide the property list (language bar). 460 */ 461 public void hide_property_list(self) { 462#ifndef UNIT_TEST 463 IBUS_ENGINE_GET_CLASS(self)->property_hide(IBUS_ENGINE(self), 464 "chewing_alnumSize_prop"); 465#endif 466 } 467 468 private IBusProperty *get_ibus_property_by_name(self, const gchar * prop_name) { 469 if (STRING_EQUALS(prop_name, "InputMode")) { 470 return self->chieng_prop; 471 } else if (STRING_EQUALS(prop_name, "chewing_alnumSize_prop")) { 472 return self->alnumSize_prop; 473 } 474 IBUS_CHEWING_LOG(MSG, "get_ibus_property_by_name(%s): NULL is returned", 475 prop_name); 476 return NULL; 477 } 478 479 /*============================================ 480 * Overridden Parent (IBusEngine) methods 481 */ 482 override(IBus:Engine) void 483 reset(IBus:Engine * engine) { 484 Self *self = SELF(engine); 485 ibus_chewing_engine_reset(self); 486 } 487 488 override(IBus:Engine) void 489 page_up(IBus:Engine * engine) { 490 Self *self = SELF(engine); 491 if (is_password(self)) 492 return; 493 ibus_chewing_pre_edit_process_key(self->icPreEdit, IBUS_KEY_Page_Up, 0); 494 self_update(self); 495 } 496 497 498 override(IBus:Engine) void 499 page_down(IBus:Engine * engine) { 500 Self *self = SELF(engine); 501 if (is_password(self)) 502 return; 503 ibus_chewing_pre_edit_process_key(self->icPreEdit, IBUS_KEY_Page_Down, 0); 504 self_update(self); 505 } 506 507 override(IBus:Engine) void 508 cursor_up(IBus:Engine * engine) { 509 Self *self = SELF(engine); 510 if (is_password(self)) 511 return; 512 ibus_chewing_pre_edit_process_key(self->icPreEdit, IBUS_KEY_Up, 0); 513 self_update(self); 514 } 515 516 override(IBus:Engine) void 517 cursor_down(IBus:Engine * engine) { 518 Self *self = SELF(engine); 519 if (is_password(self)) 520 return; 521 ibus_chewing_pre_edit_process_key(self->icPreEdit, IBUS_KEY_Down, 0); 522 self_update(self); 523 } 524 525 override(IBus:Engine) void 526 enable(IBus:Engine * engine) { 527 Self *self = SELF(engine); 528 ibus_chewing_engine_enable(self); 529 } 530 531 override(IBus:Engine) void 532 disable(IBus:Engine * engine) { 533 Self *self = SELF(engine); 534 ibus_chewing_engine_disable(self); 535 } 536 537 override(IBus:Engine) void 538 focus_in(IBus:Engine * engine) { 539 Self *self = SELF(engine); 540 ibus_chewing_engine_focus_in(self); 541 } 542 543 override(IBus:Engine) void 544 focus_out(IBus:Engine * engine) { 545 Self *self = SELF(engine); 546 ibus_chewing_engine_focus_out(self); 547 } 548 549 override(IBus:Engine) void 550 set_capabilities(IBus:Engine * engine, guint caps) { 551 Self *self = SELF(engine); 552 self->_priv->capabilite = caps; 553 IBUS_CHEWING_LOG(MSG, "***** set_capabilities(%x): statusFlags=%x", 554 caps, self->_priv->statusFlags); 555 } 556 557 override(IBus:Engine) void 558 property_show(IBus:Engine * engine, const gchar * prop_name) { 559 IBUS_CHEWING_LOG(INFO, "property_show(-, %s)", prop_name); 560 Self *self = SELF(engine); 561 IBusProperty *prop = self_get_ibus_property_by_name(self, prop_name); 562 ibus_property_set_visible(prop, TRUE); 563 } 564 565 override(IBus:Engine) void 566 property_hide(IBus:Engine * engine, const gchar * prop_name) { 567 IBUS_CHEWING_LOG(INFO, "property_hide(-, %s)", prop_name); 568 Self *self = SELF(engine); 569 IBusProperty *prop = self_get_ibus_property_by_name(self, prop_name); 570 ibus_property_set_visible(prop, FALSE); 571 } 572} 573 574%h{ 575void ibus_chewing_engine_reset(IBusChewingEngine *self); 576void ibus_chewing_engine_enable(IBusChewingEngine *self); 577void ibus_chewing_engine_disable(IBusChewingEngine *self); 578void ibus_chewing_engine_focus_in(IBusChewingEngine *self); 579void ibus_chewing_engine_focus_out(IBusChewingEngine *self); 580gboolean ibus_chewing_engine_process_key_event(IBusEngine *self, 581 guint key_sym, 582 guint keycode, 583 guint modifiers); 584%} 585 586%{ 587#include "IBusChewingEngine-signal.c" 588#include "IBusChewingEngine-input-events.c" 589%} 590