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