2 # -*- coding: utf-8 -*-
6 # Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
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
16 # GNU 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 program; if not, write to the
20 # Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21 # Boston, MA 02111-1307 USA
30 from ibus import keysyms
31 from ibus import interface
34 MODE_HALF_WIDTH_KATAKANA, \
36 MODE_WIDE_LATIN = range (0, 5)
40 class Engine (interface.IEngine):
41 def __init__ (self, dbusconn, object_path):
42 interface.IEngine.__init__ (self, dbusconn, object_path)
43 self._dbusconn = dbusconn
45 # create anthy context
46 self._context = anthy.anthy_context ()
47 self._context._set_encoding (anthy.ANTHY_UTF8_ENCODING)
50 self._input_mode = MODE_HIRAGANA
53 self._lookup_table = ibus.LookupTable ()
54 self._prop_list = self._init_props ()
56 # use reset to init values
59 def _init_props (self):
60 props = ibus.PropList ()
62 # init input mode properties
63 mode_prop = ibus.Property (name = "InputMode",
64 type = ibus.PROP_TYPE_MENU,
66 tooltip = "Switch input mode")
67 self._prop_dict["InputMode"] = mode_prop
69 mode_props = ibus.PropList ()
70 mode_props.append (ibus.Property (name = "InputMode.Hiragana",
71 type = ibus.PROP_TYPE_RADIO,
73 mode_props.append (ibus.Property (name = "InputMode.Katakana",
74 type = ibus.PROP_TYPE_RADIO,
76 mode_props.append (ibus.Property (name = "InputMode.HalfWidthKatakana",
77 type = ibus.PROP_TYPE_RADIO,
78 label = "Half width katakana"))
79 mode_props.append (ibus.Property (name = "InputMode.Latin",
80 type = ibus.PROP_TYPE_RADIO,
82 mode_props.append (ibus.Property (name = "InputMode.WideLatin",
83 type = ibus.PROP_TYPE_RADIO,
84 label = "Wide Latin"))
86 mode_props[self._input_mode].set_state (ibus.PROP_STATE_CHECKED)
88 for prop in mode_props:
89 self._prop_dict[prop.get_name ()] = prop
91 mode_prop.set_sub_props (mode_props)
92 props.append (mode_prop)
96 test_prop = ibus.Property (name = "TestProp",
97 type = ibus.PROP_TYPE_TOGGLE,
99 tooltip = "test property")
100 self._prop_dict["TestProp"] = test_prop
101 props.append (test_prop)
106 # reset values of engine
108 self._input_chars = u""
109 self._convert_chars = u""
111 self._need_update = False
112 self._convert_begined = False
114 self._lookup_table.clean ()
115 self._lookup_table_visible = False
118 def _begin_convert (self):
119 if self._convert_begined:
121 self._convert_begined = True
123 self._context.set_string (self._input_chars.encode ("utf-8"))
124 conv_stat = anthy.anthy_conv_stat ()
125 self._context.get_stat (conv_stat)
127 for i in xrange (0, conv_stat.nr_segment):
128 buf = self._context.get_segment (i, 0)
129 text = unicode (buf, "utf-8")
130 self._segments.append ((0, text))
133 self._fill_lookup_table ()
134 self._lookup_table_visible = False
136 def _fill_lookup_table (self):
138 seg_stat = anthy.anthy_segment_stat ()
139 self._context.get_segment_stat (self._cursor_pos, seg_stat)
142 self._lookup_table.clean ()
143 for i in xrange (0, seg_stat.nr_candidate):
144 buf = self._context.get_segment (self._cursor_pos, i)
145 candidate = unicode (buf, "utf-8")
146 self._lookup_table.append_candidate (candidate)
149 def _invalidate (self):
150 if self._need_update:
152 self._need_update = True
153 gobject.idle_add (self._update, priority = gobject.PRIORITY_LOW)
156 # only process cursor down in convert mode
157 if not self._convert_begined:
160 if not self._lookup_table.page_up ():
163 candidate = self._lookup_table.get_current_candidate ()[0]
164 index = self._lookup_table.get_cursor_pos ()
165 self._segments[self._cursor_pos] = index, candidate
169 def _page_down (self):
170 # only process cursor down in convert mode
171 if not self._convert_begined:
174 if not self._lookup_table.page_down ():
177 candidate = self._lookup_table.get_current_candidate ()[0]
178 index = self._lookup_table.get_cursor_pos ()
179 self._segments[self._cursor_pos] = index, candidate
183 def _cursor_up (self):
184 # only process cursor down in convert mode
185 if not self._convert_begined:
188 if not self._lookup_table.cursor_up ():
191 candidate = self._lookup_table.get_current_candidate ()[0]
192 index = self._lookup_table.get_cursor_pos ()
193 self._segments[self._cursor_pos] = index, candidate
197 def _cursor_down (self):
198 # only process cursor down in convert mode
199 if not self._convert_begined:
202 if not self._lookup_table.cursor_down ():
205 candidate = self._lookup_table.get_current_candidate ()[0]
206 index = self._lookup_table.get_cursor_pos ()
207 self._segments[self._cursor_pos] = index, candidate
211 def _commit_string (self, text):
213 self.CommitString (text)
216 def _update_input_chars (self):
217 begin, end = max (self._cursor_pos - 4, 0), self._cursor_pos
219 for i in range (begin, end):
220 text = self._input_chars[i:end]
221 romja = romaji_typing_rule.get (text, None)
223 self._input_chars = u"".join ((self._input_chars[:i], romja, self._input_chars[end:]))
224 self._cursor_pos -= len(text)
225 self._cursor_pos += len(romja)
227 attrs = ibus.AttrList ()
228 attrs.append (ibus.AttributeUnderline (pango.UNDERLINE_SINGLE, 0, len (self._input_chars.encode ("utf-8"))))
230 self.UpdatePreedit (dbus.String (self._input_chars),
231 attrs.to_dbus_value (),
232 dbus.Int32 (self._cursor_pos),
233 len (self._input_chars) > 0)
234 self.UpdateAuxString (u"", ibus.AttrList ().to_dbus_value (), False)
235 self.UpdateLookupTable (self._lookup_table.to_dbus_value (), self._lookup_table_visible)
237 def _update_convert_chars (self):
238 self._convert_chars = u""
241 for seg_index, text in self._segments:
242 self._convert_chars += text
243 if i <= self._cursor_pos:
247 attrs = ibus.AttrList ()
248 attrs.append (ibus.AttributeUnderline (pango.UNDERLINE_SINGLE, 0, len (self._convert_chars)))
249 attrs.append (ibus.AttributeBackground (ibus.RGB (200, 200, 240),
250 pos - len (self._segments[self._cursor_pos][1]),
252 self.UpdatePreedit (dbus.String (self._convert_chars),
253 attrs.to_dbus_value (),
256 aux_string = u"( %d / %d )" % (self._lookup_table.get_cursor_pos () + 1, self._lookup_table.get_number_of_candidates())
257 self.UpdateAuxString (aux_string, ibus.AttrList ().to_dbus_value (), self._lookup_table_visible)
258 self.UpdateLookupTable (self._lookup_table.to_dbus_value (), self._lookup_table_visible)
261 self._need_update = False
262 if self._convert_begined == False:
263 self._update_input_chars ()
265 self._update_convert_chars ()
267 def _on_key_return (self):
268 if not self._input_chars:
270 if self._convert_begined == False:
271 self._commit_string (self._input_chars)
274 for seg_index, text in self._segments:
275 self._context.commit_segment (i, seg_index)
276 self._commit_string (self._convert_chars)
279 def _on_key_escape (self):
280 if not self._input_chars:
286 def _on_key_back_space (self):
287 if not self._input_chars:
290 if self._convert_begined:
291 self._convert_begined = False
292 self._cursor_pos = len (self._input_chars)
293 self._lookup_table.clean ()
294 self._lookup_table_visible = False
295 elif self._cursor_pos > 0:
296 self._input_chars = self._input_chars[:self._cursor_pos - 1] + self._input_chars [self._cursor_pos:]
297 self._cursor_pos -= 1
302 def _on_key_delete (self):
303 if not self._input_chars:
306 if self._convert_begined:
307 self._convert_begined = False
308 self._cursor_pos = len (self._input_chars)
309 self._lookup_table.clean ()
310 self._lookup_table_visible = False
311 elif self._cursor_pos < len (self._input_chars):
312 self._input_chars = self._input_chars[:self._cursor_pos] + self._input_chars [self._cursor_pos + 1:]
317 def _on_key_space (self):
318 if not self._input_chars:
320 if self._convert_begined == False:
321 self._begin_convert ()
324 self._lookup_table_visible = True
328 def _on_key_up (self):
329 if not self._input_chars:
331 self._lookup_table_visible = True
335 def _on_key_down (self):
336 if not self._input_chars:
338 self._lookup_table_visible = True
342 def _on_key_page_up (self):
343 if not self._input_chars:
345 if self._lookup_table_visible == True:
349 def _on_key_page_down (self):
350 if not self._input_chars:
352 if self._lookup_table_visible == True:
356 def _on_key_left (self):
357 if not self._input_chars:
359 if self._cursor_pos == 0:
361 self._cursor_pos -= 1
362 self._lookup_table_visible = False
363 self._fill_lookup_table ()
367 def _on_key_right (self):
368 if not self._input_chars:
371 if self._convert_begined:
372 max_pos = len (self._segments) - 1
374 max_pos = len (self._input_chars)
375 if self._cursor_pos == max_pos:
377 self._cursor_pos += 1
378 self._lookup_table_visible = False
379 self._fill_lookup_table ()
384 def _on_key_number (self, index):
385 if not self._input_chars:
388 if self._convert_begined and self._lookup_table_visible:
389 candidates = self._lookup_table.get_canidates_in_current_page ()
390 if self._lookup_table.set_cursor_pos_in_current_page (index):
391 index = self._lookup_table.get_cursor_pos ()
392 candidate = self._lookup_table.get_current_candidate ()[0]
393 self._segments[self._cursor_pos] = index, candidate
394 self._lookup_table_visible = False
395 self._on_key_right ()
400 def _on_key_common (self, keyval):
401 if self._convert_begined:
403 for seg_index, text in self._segments:
404 self._context.commit_segment (i, seg_index)
405 self._commit_string (self._convert_chars)
406 self._input_chars += unichr (keyval)
407 self._cursor_pos += 1
411 def _process_key_event (self, keyval, is_press, state):
412 # ignore key release events
416 if keyval == keysyms.Return:
417 return self._on_key_return ()
418 elif keyval == keysyms.Escape:
419 return self._on_key_escape ()
420 elif keyval == keysyms.BackSpace:
421 return self._on_key_back_space ()
422 elif keyval == keysyms.Delete or keyval == keysyms.KP_Delete:
423 return self._on_key_delete ()
424 elif keyval == keysyms.space:
425 return self._on_key_space ()
426 elif keyval >= keysyms._1 and keyval <= keysyms._9:
427 index = keyval - keysyms._1
428 return self._on_key_number (index)
429 elif keyval == keysyms.Page_Up or keyval == keysyms.KP_Page_Up:
430 return self._on_key_page_up ()
431 elif keyval == keysyms.Page_Down or keyval == keysyms.KP_Page_Down:
432 return self._on_key_page_down ()
433 elif keyval == keysyms.Up:
434 return self._on_key_up ()
435 elif keyval == keysyms.Down:
436 return self._on_key_down ()
437 elif keyval == keysyms.Left:
438 return self._on_key_left ()
439 elif keyval == keysyms.Right:
440 return self._on_key_right ()
441 elif keyval in xrange (keysyms.a, keysyms.z + 1) or \
442 keyval in xrange (keysyms.A, keysyms.Z + 1):
443 return self._on_key_common (keyval)
449 def _property_activate (self, prop_name, state):
450 prop = self._prop_dict[prop_name]
451 prop.set_state (state)
453 if state == ibus.PROP_STATE_CHECKED:
454 if prop_name == "InputMode.Hiragana":
455 prop = self._prop_dict["InputMode"]
456 prop.set_label (_("あ"))
457 self._input_mode = MODE_HIRAGANA
458 self._update_property (prop)
459 elif prop_name == "InputMode.Katakana":
460 prop = self._prop_dict["InputMode"]
461 prop.set_label (_("ア"))
462 self._input_mode = MODE_KATAKANA
463 self._update_property (prop)
464 elif prop_name == "InputMode.HalfWidthKatakana":
465 prop = self._prop_dict["InputMode"]
466 prop.set_label (_("ア"))
467 self._input_mode = MODE_HALF_WIDTH_KATAKANA
468 self._update_property (prop)
469 elif prop_name == "InputMode.Latin":
470 prop = self._prop_dict["InputMode"]
471 self._input_mode = MODE_LATIN
472 prop.set_label (_("A"))
473 self._update_property (prop)
474 elif prop_name == "InputMode.WideLatin":
475 prop = self._prop_dict["InputMode"]
476 prop.set_label (_("A"))
477 self._input_mode = MODE_WIDE_LATIN
478 self._update_property (prop)
480 def _update_property (self, prop):
481 self.UpdateProperty (prop.to_dbus_value ())
483 # methods for dbus rpc
484 def ProcessKeyEvent (self, keyval, is_press, state):
486 return self._process_key_event (keyval, is_press, state)
492 self.RegisterProperties (self._prop_list.to_dbus_value ())
498 def SetCursorLocation (self, x, y, w, h):
513 def CursorDown (self):
516 def SetEnable (self, enable):
517 self._enable = enable
519 self.RegisterProperties (self._prop_list.to_dbus_value ())
521 def PropertyActivate (self, prop_name, prop_state):
522 self._property_activate (prop_name, prop_state)