1 # vim:set et sts=4 sw=4:
2 # -*- coding: utf-8 -*-
4 # ibus-anthy - The Anthy engine for IBus
6 # Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2, or (at your option)
13 # This program 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
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 from ibus import keysyms
30 MODE_HALF_WIDTH_KATAKANA, \
32 MODE_WIDE_LATIN = range(0, 5)
36 class Engine(ibus.EngineBase):
37 def __init__(self, bus, object_path):
38 super(Engine, self).__init__(bus, object_path)
40 # create anthy context
41 self.__context = anthy.anthy_context()
42 self.__context._set_encoding(anthy.ANTHY_UTF8_ENCODING)
45 self.__input_mode = MODE_HIRAGANA
48 self.__lookup_table = ibus.LookupTable()
49 self.__prop_list = self.__init_props()
51 # use reset to init values
54 def __init_props(self):
55 props = ibus.PropList()
57 # init input mode properties
58 mode_prop = ibus.Property(name = "InputMode",
59 type = ibus.PROP_TYPE_MENU,
61 tooltip = "Switch input mode")
62 self.__prop_dict["InputMode"] = mode_prop
64 mode_props = ibus.PropList()
65 mode_props.append(ibus.Property(name = "InputMode.Hiragana",
66 type = ibus.PROP_TYPE_RADIO,
68 mode_props.append(ibus.Property(name = "InputMode.Katakana",
69 type = ibus.PROP_TYPE_RADIO,
71 mode_props.append(ibus.Property(name = "InputMode.HalfWidthKatakana",
72 type = ibus.PROP_TYPE_RADIO,
73 label = "Half width katakana"))
74 mode_props.append(ibus.Property(name = "InputMode.Latin",
75 type = ibus.PROP_TYPE_RADIO,
77 mode_props.append(ibus.Property(name = "InputMode.WideLatin",
78 type = ibus.PROP_TYPE_RADIO,
79 label = "Wide Latin"))
81 mode_props[self.__input_mode].set_state(ibus.PROP_STATE_CHECKED)
83 for prop in mode_props:
84 self.__prop_dict[prop.get_name()] = prop
86 mode_prop.set_sub_props(mode_props)
87 props.append(mode_prop)
91 test_prop = ibus.Property(name = "TestProp",
92 type = ibus.PROP_TYPE_TOGGLE,
94 tooltip = "test property")
95 self.__prop_dict["TestProp"] = test_prop
96 props.append(test_prop)
101 # reset values of engine
103 self.__input_chars = u""
104 self.__convert_chars = u""
105 self.__cursor_pos = 0
106 self.__need_update = False
107 self.__convert_begined = False
108 self.__segments = list()
109 self.__lookup_table.clean()
110 self.__lookup_table_visible = False
113 # only process cursor down in convert mode
114 if not self.__convert_begined:
117 if not self.__lookup_table.page_up():
120 candidate = self.__lookup_table.get_current_candidate()[0]
121 index = self.__lookup_table.get_cursor_pos()
122 self.__segments[self.__cursor_pos] = index, candidate
127 # only process cursor down in convert mode
128 if not self.__convert_begined:
131 if not self.__lookup_table.page_down():
134 candidate = self.__lookup_table.get_current_candidate()[0]
135 index = self.__lookup_table.get_cursor_pos()
136 self.__segments[self.__cursor_pos] = index, candidate
141 # only process cursor down in convert mode
142 if not self.__convert_begined:
145 if not self.__lookup_table.cursor_up():
148 candidate = self.__lookup_table.get_current_candidate()[0]
149 index = self.__lookup_table.get_cursor_pos()
150 self.__segments[self.__cursor_pos] = index, candidate
154 def cursor_down(self):
155 # only process cursor down in convert mode
156 if not self.__convert_begined:
159 if not self.__lookup_table.cursor_down():
162 candidate = self.__lookup_table.get_current_candidate()[0]
163 index = self.__lookup_table.get_cursor_pos()
164 self.__segments[self.__cursor_pos] = index, candidate
168 def __commit_string(self, text):
170 self.commit_string(text)
173 def process_key_event(self, keyval, is_press, state):
174 # ignore key release events
178 if keyval == keysyms.Return:
179 return self.__on_key_return()
180 elif keyval == keysyms.Escape:
181 return self.__on_key_escape()
182 elif keyval == keysyms.BackSpace:
183 return self.__on_key_back_space()
184 elif keyval == keysyms.Delete or keyval == keysyms.KP_Delete:
185 return self.__on_key_delete()
186 elif keyval == keysyms.space:
187 return self.__on_key_space()
188 elif keyval >= keysyms._1 and keyval <= keysyms._9:
189 index = keyval - keysyms._1
190 return self.__on_key_number(index)
191 elif keyval == keysyms.Page_Up or keyval == keysyms.KP_Page_Up:
192 return self.__on_key_page_up()
193 elif keyval == keysyms.Page_Down or keyval == keysyms.KP_Page_Down:
194 return self.__on_key_page_down()
195 elif keyval == keysyms.Up:
196 return self.__on_key_up()
197 elif keyval == keysyms.Down:
198 return self.__on_key_down()
199 elif keyval == keysyms.Left:
200 return self.__on_key_left()
201 elif keyval == keysyms.Right:
202 return self.__on_key_right()
203 elif keyval in xrange(keysyms.a, keysyms.z + 1) or \
204 keyval in xrange(keysyms.A, keysyms.Z + 1):
205 return self.__on_key_common(keyval)
211 def property_activate(self, prop_name, state):
212 prop = self.__prop_dict[prop_name]
213 prop.set_state(state)
215 if state == ibus.PROP_STATE_CHECKED:
216 if prop_name == "InputMode.Hiragana":
217 prop = self.__prop_dict["InputMode"]
218 prop.set_label(_(u"あ"))
219 self.__input_mode = MODE_HIRAGANA
220 self.update_property(prop)
221 elif prop_name == "InputMode.Katakana":
222 prop = self.__prop_dict["InputMode"]
223 prop.set_label(_(u"ア"))
224 self.__input_mode = MODE_KATAKANA
225 self.update_property(prop)
226 elif prop_name == "InputMode.HalfWidthKatakana":
227 prop = self.__prop_dict["InputMode"]
228 prop.set_label(_(u"ア"))
229 self.__input_mode = MODE_HALF_WIDTH_KATAKANA
230 self.update_property(prop)
231 elif prop_name == "InputMode.Latin":
232 prop = self.__prop_dict["InputMode"]
233 self.__input_mode = MODE_LATIN
234 prop.set_label(_(u"A"))
235 self.update_property(prop)
236 elif prop_name == "InputMode.WideLatin":
237 prop = self.__prop_dict["InputMode"]
238 prop.set_label(_(u"A"))
239 self.__input_mode = MODE_WIDE_LATIN
240 self.update_property(prop)
243 self.register_properties(self.__prop_list)
249 def __begin_convert(self):
250 if self.__convert_begined:
252 self.__convert_begined = True
254 self.__context.set_string(self.__input_chars.encode("utf-8"))
255 conv_stat = anthy.anthy_conv_stat()
256 self.__context.get_stat(conv_stat)
258 for i in xrange(0, conv_stat.nr_segment):
259 buf = self.__context.get_segment(i, 0)
260 text = unicode(buf, "utf-8")
261 self.__segments.append((0, text))
263 self.__cursor_pos = 0
264 self.__fill_lookup_table()
265 self.__lookup_table_visible = False
267 def __fill_lookup_table(self):
269 seg_stat = anthy.anthy_segment_stat()
270 self.__context.get_segment_stat(self.__cursor_pos, seg_stat)
273 self.__lookup_table.clean()
274 for i in xrange(0, seg_stat.nr_candidate):
275 buf = self.__context.get_segment(self.__cursor_pos, i)
276 candidate = unicode(buf, "utf-8")
277 self.__lookup_table.append_candidate(candidate)
280 def __invalidate(self):
281 if self.__need_update:
283 self.__need_update = True
284 gobject.idle_add(self.__update, priority = gobject.PRIORITY_LOW)
287 def __update_input_chars(self):
288 begin, end = max(self.__cursor_pos - 4, 0), self.__cursor_pos
290 for i in range(begin, end):
291 text = self.__input_chars[i:end]
292 romja = romaji_typing_rule.get(text, None)
294 self.__input_chars = u"".join((self.__input_chars[:i], romja, self.__input_chars[end:]))
295 self.__cursor_pos -= len(text)
296 self.__cursor_pos += len(romja)
298 attrs = ibus.AttrList()
299 attrs.append(ibus.AttributeUnderline(ibus.ATTR_UNDERLINE_SINGLE, 0, len(self.__input_chars.encode("utf-8"))))
301 self.update_preedit(self.__input_chars, attrs, self.__cursor_pos, len(self.__input_chars) > 0)
302 self.update_aux_string(u"", ibus.AttrList(), False)
303 self.update_lookup_table(self.__lookup_table, self.__lookup_table_visible)
305 def __update_convert_chars(self):
306 self.__convert_chars = u""
309 for seg_index, text in self.__segments:
310 self.__convert_chars += text
311 if i <= self.__cursor_pos:
315 attrs = ibus.AttrList()
316 attrs.append(ibus.AttributeUnderline(ibus.ATTR_UNDERLINE_SINGLE, 0, len(self.__convert_chars)))
317 attrs.append(ibus.AttributeBackground(ibus.RGB(200, 200, 240),
318 pos - len(self.__segments[self.__cursor_pos][1]),
320 self.update_preedit(self.__convert_chars, attrs, pos, True)
321 aux_string = u"( %d / %d )" % (self.__lookup_table.get_cursor_pos() + 1, self.__lookup_table.get_number_of_candidates())
322 self.update_aux_string(aux_string, ibus.AttrList(), self.__lookup_table_visible)
323 self.update_lookup_table(self.__lookup_table, self.__lookup_table_visible)
326 self.__need_update = False
327 if self.__convert_begined == False:
328 self.__update_input_chars()
330 self.__update_convert_chars()
332 def __on_key_return(self):
333 if not self.__input_chars:
335 if self.__convert_begined == False:
336 self.__commit_string(self.__input_chars)
339 for seg_index, text in self.__segments:
340 self.__context.commit_segment(i, seg_index)
341 self.__commit_string(self.__convert_chars)
344 def __on_key_escape(self):
345 if not self.__input_chars:
351 def __on_key_back_space(self):
352 if not self.__input_chars:
355 if self.__convert_begined:
356 self.__convert_begined = False
357 self.__cursor_pos = len(self.__input_chars)
358 self.__lookup_table.clean()
359 self.__lookup_table_visible = False
360 elif self.__cursor_pos > 0:
361 self.__input_chars = self.__input_chars[:self.__cursor_pos - 1] + self.__input_chars [self.__cursor_pos:]
362 self.__cursor_pos -= 1
367 def __on_key_delete(self):
368 if not self.__input_chars:
371 if self.__convert_begined:
372 self.__convert_begined = False
373 self.__cursor_pos = len(self.__input_chars)
374 self.__lookup_table.clean()
375 self.__lookup_table_visible = False
376 elif self.__cursor_pos < len(self.__input_chars):
377 self.__input_chars = self.__input_chars[:self.__cursor_pos] + self.__input_chars [self.__cursor_pos + 1:]
382 def __on_key_space(self):
383 if not self.__input_chars:
385 if self.__convert_begined == False:
386 self.__begin_convert()
389 self.__lookup_table_visible = True
393 def __on_key_up(self):
394 if not self.__input_chars:
396 self.__lookup_table_visible = True
400 def __on_key_down(self):
401 if not self.__input_chars:
403 self.__lookup_table_visible = True
407 def __on_key_page_up(self):
408 if not self.__input_chars:
410 if self.__lookup_table_visible == True:
414 def __on_key_page_down(self):
415 if not self.__input_chars:
417 if self.__lookup_table_visible == True:
421 def __on_key_left(self):
422 if not self.__input_chars:
424 if self.__cursor_pos == 0:
426 self.__cursor_pos -= 1
427 self.__lookup_table_visible = False
428 self.__fill_lookup_table()
432 def __on_key_right(self):
433 if not self.__input_chars:
436 if self.__convert_begined:
437 max_pos = len(self.__segments) - 1
439 max_pos = len(self.__input_chars)
440 if self.__cursor_pos == max_pos:
442 self.__cursor_pos += 1
443 self.__lookup_table_visible = False
444 self.__fill_lookup_table()
449 def __on_key_number(self, index):
450 if not self.__input_chars:
453 if self.__convert_begined and self.__lookup_table_visible:
454 candidates = self.__lookup_table.get_canidates_in_current_page()
455 if self.__lookup_table.set_cursor_pos_in_current_page(index):
456 index = self.__lookup_table.get_cursor_pos()
457 candidate = self.__lookup_table.get_current_candidate()[0]
458 self.__segments[self.__cursor_pos] = index, candidate
459 self.__lookup_table_visible = False
460 self.__on_key_right()
465 def __on_key_common(self, keyval):
466 if self.__convert_begined:
468 for seg_index, text in self.__segments:
469 self.__context.commit_segment(i, seg_index)
470 self.__commit_string(self.__convert_chars)
471 self.__input_chars += unichr(keyval)
472 self.__cursor_pos += 1