1 # vim:set et sts=4 sw=4:
5 # Copyright(c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
6 # Copyright(c) 2007-2010 Red Hat, Inc.
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
33 from ibus import interface
34 from languagebar import LanguageBar
35 from candidatepanel import CandidatePanel
36 from engineabout import EngineAbout
38 from i18n import _, N_
40 ICON_KEYBOARD = "ibus-keyboard"
41 ICON_ENGINE = "ibus-engine"
43 def show_uri(screen, link):
45 gtk.show_uri(screen, link, 0)
47 print >> sys.stderr, "pygtk do not support show_uri"
49 def url_hook(about, link, user_data):
50 show_uri(about.get_screen(), link)
52 def email_hook(about, email, user_data):
53 show_uri(about.get_screen(), "mailto:%s" % email)
55 gtk.about_dialog_set_url_hook(url_hook, None)
56 gtk.about_dialog_set_email_hook(email_hook, None)
58 class Panel(ibus.PanelBase):
59 __gtype_name__ = "IBusPanel"
60 def __init__(self, bus):
61 super(Panel, self).__init__(bus)
63 self.__config = self.__bus.get_config()
64 self.__focus_ic = None
65 self.__setup_pid = None
66 self.__prefix = os.getenv("IBUS_PREFIX")
67 self.__data_dir = path.join(self.__prefix, "share", "ibus")
68 # self.__icons_dir = path.join(self.__data_dir, "icons")
69 self.__setup_cmd = path.join(self.__prefix, "bin", "ibus-setup")
72 self.__config.connect("value-changed", self.__config_value_changed_cb)
73 self.__config.connect("reloaded", self.__config_reloaded_cb)
74 # self.__bus.config_add_watch("panel")
76 # add icon search path
77 # icon_theme = gtk.icon_theme_get_default()
78 # icon_theme.prepend_search_path(self.__icons_dir)
80 self.__language_bar = LanguageBar()
81 self.__language_bar.connect("property-activate",
82 lambda widget, prop_name, prop_state: self.property_activate(prop_name, prop_state))
83 self.__language_bar.connect("get-im-menu",
84 self.__get_im_menu_cb)
85 self.__language_bar.connect("show-engine-about",
86 self.__show_engine_about_cb)
87 self.__language_bar.connect("position-changed",
88 self.__position_changed_cb)
89 self.__language_bar.focus_out()
90 self.__language_bar.show_all()
92 self.__candidate_panel = CandidatePanel()
93 self.__candidate_panel.connect("cursor-up",
94 lambda widget: self.cursor_up())
95 self.__candidate_panel.connect("cursor-down",
96 lambda widget: self.cursor_down())
97 self.__candidate_panel.connect("page-up",
98 lambda widget: self.page_up())
99 self.__candidate_panel.connect("page-down",
100 lambda widget: self.page_down())
101 self.__candidate_panel.connect("candidate-clicked",
102 lambda widget, index, button, state: self.candidate_clicked(index, button, state))
105 self.__status_icon = gtk.StatusIcon()
106 # gnome-shell checks XClassHint.res_class with ShellTrayIcon.
107 # gtk_status_icon_set_name() can set XClassHint.res_class .
108 # However gtk_status_icon_new() also calls gtk_window_realize() so
109 # gtk_status_icon_set_visible() needs to be called to set WM_CLASS
110 # so that gtk_window_realize() is called later again.
111 # set_title is for gnome-shell notificationDaemon in bottom right.
112 self.__status_icon.set_visible(False)
113 self.__status_icon.set_name('ibus-ui-gtk')
114 self.__status_icon.set_title(_("IBus Panel"))
115 self.__status_icon.set_visible(True)
116 self.__status_icon.connect("popup-menu", self.__status_icon_popup_menu_cb)
117 self.__status_icon.connect("activate", self.__status_icon_activate_cb)
118 self.__status_icon.set_from_icon_name(ICON_KEYBOARD)
119 self.__status_icon.set_tooltip(_("IBus input method framework"))
120 self.__status_icon.set_visible(True)
122 self.__config_load_lookup_table_orientation()
123 self.__config_load_show()
124 self.__config_load_position()
125 self.__config_load_custom_font()
126 self.__config_load_show_icon_on_systray()
127 self.__config_load_show_im_name()
128 # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)
130 def set_cursor_location(self, x, y, w, h):
131 self.__candidate_panel.set_cursor_location(x, y, w, h)
133 def update_preedit_text(self, text, cursor_pos, visible):
134 self.__candidate_panel.update_preedit_text(text, cursor_pos, visible)
136 def show_preedit_text(self):
137 self.__candidate_panel.show_preedit_text()
139 def hide_preedit_text(self):
140 self.__candidate_panel.hide_preedit_text()
142 def update_auxiliary_text(self, text, visible):
143 self.__candidate_panel.update_auxiliary_text(text, visible)
145 def show_auxiliary_text(self):
146 self.__candidate_panel.show_auxiliary_text()
148 def hide_auxiliary_text(self):
149 self.__candidate_panel.hide_auxiliary_text()
151 def update_lookup_table(self, lookup_table, visible):
152 self.__candidate_panel.update_lookup_table(lookup_table, visible)
154 def show_lookup_table(self):
155 self.__candidate_panel.show_lookup_table()
157 def hide_lookup_table(self):
158 self.__candidate_panel.hide_lookup_table()
160 def page_up_lookup_table(self):
161 self.__candidate_panel.page_up_lookup_table()
163 def page_down_lookup_table(self):
164 self.__candidate_panel.page_down_lookup_table()
166 def cursor_up_lookup_table(self):
167 self.__candidate_panel.cursor_up_lookup_table()
169 def cursor_down_lookup_table(self):
170 self.__candidate_panel.cursor_down_lookup_table()
172 def show_candidate_window(self):
173 self.__candidate_panel.show_all()
175 def hide_candidate_window(self):
176 self.__candidate_panel.hide_all()
178 def show_language_bar(self):
179 self.__language_bar.show_all()
181 def hide_language_bar(self):
182 self.__language_bar.hide_all()
184 def register_properties(self, props):
185 self.__language_bar.register_properties(props)
187 def update_property(self, prop):
188 self.__language_bar.update_property(prop)
190 def get_status_icon(self):
191 return self.__status_icon
193 def __set_im_icon(self, icon_name):
195 icon_name = ICON_ENGINE
196 self.__language_bar.set_im_icon(icon_name)
197 if icon_name.startswith("/"):
198 self.__status_icon.set_from_file(icon_name)
200 self.__status_icon.set_from_icon_name(icon_name)
202 def __set_im_name(self, name):
203 self.__language_bar.set_im_name(name)
205 def focus_in(self, ic):
207 self.__focus_ic = ibus.InputContext(self.__bus, ic)
208 enabled = self.__focus_ic.is_enabled()
209 self.__language_bar.set_enabled(enabled)
212 self.__set_im_icon(ICON_KEYBOARD)
213 self.__set_im_name(None)
215 engine = self.__focus_ic.get_engine()
217 self.__set_im_icon(engine.icon)
218 self.__set_im_name(engine.longname)
220 self.__set_im_icon(ICON_KEYBOARD)
221 self.__set_im_name(None)
222 self.__language_bar.focus_in()
224 def focus_out(self, ic):
226 self.__focus_ic = None
227 self.__language_bar.set_enabled(False)
228 self.__language_bar.focus_out()
229 self.__set_im_icon(ICON_KEYBOARD)
230 self.__set_im_name(None)
232 def state_changed(self):
233 if not self.__focus_ic:
236 enabled = self.__focus_ic.is_enabled()
237 self.__language_bar.set_enabled(enabled)
241 self.__set_im_icon(ICON_KEYBOARD)
242 self.__set_im_name(None)
244 engine = self.__focus_ic.get_engine()
246 self.__set_im_icon(engine.icon)
247 self.__set_im_name(engine.longname)
249 self.__set_im_icon(ICON_KEYBOARD)
250 self.__set_im_name(None)
254 self.__candidate_panel.reset()
255 self.__language_bar.reset()
257 def start_setup(self):
260 def do_destroy(self):
263 def __config_load_lookup_table_orientation(self):
264 value = self.__config.get_value("panel", "lookup_table_orientation", 0)
265 if value in (ibus.ORIENTATION_HORIZONTAL, ibus.ORIENTATION_VERTICAL):
268 orientation = ibus.ORIENTATION_HORIZONTAL
269 self.__candidate_panel.set_orientation(orientation)
271 def __config_load_show(self):
272 show = self.__config.get_value("panel", "show", 0)
273 self.__language_bar.set_show(show)
275 def __config_load_position(self):
276 x = self.__config.get_value("panel", "x", -1)
277 y = self.__config.get_value("panel", "y", -1)
278 self.__language_bar.set_position(x, y)
280 def __config_load_custom_font(self):
281 use_custom_font = self.__config.get_value("panel", "use_custom_font", False)
282 font_name = gtk.settings_get_default().get_property("gtk-font-name")
283 font_name = unicode(font_name, "utf-8")
284 custom_font = self.__config.get_value("panel", "custom_font", font_name)
285 style_string = 'style "custom-font" { font_name="%s" }\n' \
286 'class "IBusCandidateLabel" style "custom-font"\n'
288 style_string = style_string % custom_font
289 gtk.rc_parse_string(style_string)
291 style_string = style_string % ""
292 gtk.rc_parse_string(style_string)
294 settings = gtk.settings_get_default()
295 gtk.rc_reset_styles(settings)
297 def __config_load_show_icon_on_systray(self):
298 value = self.__config.get_value("panel", "show_icon_on_systray", True)
299 self.__status_icon.set_visible(True if value else False)
301 def __config_load_show_im_name(self):
302 value = self.__config.get_value("panel", "show_im_name", False)
303 self.__language_bar.set_show_im_name(value)
305 def __config_value_changed_cb(self, bus, section, name, value):
306 if section != "panel":
308 if name == "lookup_table_orientation":
309 self.__config_load_lookup_table_orientation()
311 self.__config_load_show()
312 elif name == "use_custom_font" or name == "custom_font":
313 self.__config_load_custom_font()
314 elif name == "show_icon_on_systray":
315 self.__config_load_show_icon_on_systray()
316 elif name == "show_im_name":
317 self.__config_load_show_im_name()
318 elif name == "x" or name == "y":
321 print >> sys.stderr, "Unknown config item [%s]" % name
323 def __config_reloaded_cb(self, bus):
326 def __create_sys_menu(self):
328 item = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
329 item.connect("activate",
330 self.__sys_menu_item_activate_cb, gtk.STOCK_PREFERENCES)
332 item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
333 item.connect("activate",
334 self.__sys_menu_item_activate_cb, gtk.STOCK_ABOUT)
336 menu.add(gtk.SeparatorMenuItem())
337 item = gtk.MenuItem(_("Restart"))
338 item.connect("activate",
339 self.__sys_menu_item_activate_cb, "Restart")
341 item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
342 item.connect("activate",
343 self.__sys_menu_item_activate_cb, gtk.STOCK_QUIT)
347 menu.set_take_focus(False)
350 # def __create_im_menu(self):
352 # engines = self.__bus.list_active_engines()
355 # for engine in engines:
356 # lang = ibus.get_language_name(engine.language)
357 # if lang not in tmp:
359 # tmp[lang].append(engine)
362 # other = tmp.get(_("Other"), [])
363 # if _("Other") in tmp:
364 # langs.remove(_("Other"))
365 # langs.append(_("Other"))
367 # size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
369 # if len(tmp[lang]) == 1:
370 # engine = tmp[lang][0]
371 # item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
373 # item.set_image(_icon.IconWidget(engine.icon, size[0]))
375 # item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
376 # item.connect("activate", self.__im_menu_item_activate_cb, engine)
379 # item = gtk.MenuItem(lang)
381 # submenu = gtk.Menu()
382 # item.set_submenu(submenu)
383 # for engine in tmp[lang]:
384 # item = gtk.ImageMenuItem(engine.longname)
386 # item.set_image(_icon.IconWidget(engine.icon, size[0]))
388 # item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
389 # item.connect("activate", self.__im_menu_item_activate_cb, engine)
392 # item = gtk.ImageMenuItem(_("Turn off input method"))
393 # item.set_image(_icon.IconWidget("gtk-close", size[0]))
394 # item.connect("activate", self.__im_menu_item_activate_cb, None)
398 # menu.set_take_focus(False)
401 def __create_im_menu(self):
402 engines = self.__bus.list_active_engines()
404 (self.__focus_ic != None and self.__focus_ic.get_engine()) or \
405 (engines and engines[0]) or \
408 size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
410 for i, engine in enumerate(engines):
411 lang = ibus.get_language_name(engine.language)
412 item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
413 if current_engine and current_engine.name == engine.name:
414 for widget in item.get_children():
415 if isinstance(widget, gtk.Label):
416 widget.set_markup("<b>%s</b>" % widget.get_text())
418 item.set_image(_icon.IconWidget(engine.icon, size[0]))
420 item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
421 item.connect("activate", self.__im_menu_item_activate_cb, engine)
424 item = gtk.ImageMenuItem(_("Turn off input method"))
425 item.set_image(_icon.IconWidget("gtk-close", size[0]))
426 item.connect("activate", self.__im_menu_item_activate_cb, None)
427 if self.__focus_ic == None or not self.__focus_ic.is_enabled():
428 item.set_sensitive(False)
432 menu.set_take_focus(False)
435 def __get_im_menu_cb(self, languagebar):
436 menu = self.__create_im_menu()
439 def __show_engine_about_cb(self, langagebar):
441 engine = self.__focus_ic.get_engine()
442 dlg = EngineAbout(engine)
448 def __position_changed_cb(self, langagebar, x, y):
449 self.__config.set_value("panel", "x", x)
450 self.__config.set_value("panel", "y", y)
452 def __status_icon_popup_menu_cb(self, status_icon, button, active_time):
453 menu = self.__create_sys_menu()
454 menu.popup(None, None,
455 gtk.status_icon_position_menu,
460 def __status_icon_activate_cb(self, status_icon):
461 if not self.__focus_ic:
463 item = gtk.ImageMenuItem(_("No input window"))
464 size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
465 item.set_image(_icon.IconWidget("gtk-info", size[0]))
469 menu = self.__create_im_menu()
470 self.__language_bar.create_im_menu(menu)
471 menu.popup(None, None,
472 gtk.status_icon_position_menu,
474 gtk.get_current_event_time(),
477 def __im_menu_item_activate_cb(self, item, engine):
478 if not self.__focus_ic:
481 self.__focus_ic.set_engine(engine)
483 self.__focus_ic.disable()
485 def __sys_menu_item_activate_cb(self, item, command):
486 if command == gtk.STOCK_PREFERENCES:
488 elif command == gtk.STOCK_ABOUT:
489 about_dialog = gtk.AboutDialog()
490 about_dialog.set_program_name("IBus")
491 about_dialog.set_version(ibus.get_version())
492 about_dialog.set_copyright(ibus.get_copyright())
493 about_dialog.set_license(ibus.get_license())
494 about_dialog.set_comments(_("IBus is an intelligent input bus for Linux/Unix."))
495 about_dialog.set_website("http://code.google.com/p/ibus")
496 about_dialog.set_authors(["Peng Huang <shawn.p.huang@gmail.com>"])
497 about_dialog.set_documenters(["Peng Huang <shawn.p.huang@gmail.com>"])
498 about_dialog.set_translator_credits(_("translator-credits"))
499 about_dialog.set_logo_icon_name("ibus")
500 about_dialog.set_icon_name("ibus")
502 about_dialog.destroy()
503 elif command == gtk.STOCK_QUIT:
504 self.__bus.exit(False)
505 elif command == "Restart":
506 self.__bus.exit(True)
508 print >> sys.stderr, "Unknown command %s" % command
510 def __child_watch_cb(self, pid, status):
511 if self.__setup_pid == pid:
512 self.__setup_pid.close()
513 self.__setup_pid = None
515 def __start_setup(self):
516 if self.__setup_pid != None:
518 # if setup dialog is running, bring the dialog to front by SIGUSR1
519 os.kill(self.__setup_pid, signal.SIGUSR1)
522 # seems the setup dialog is not running anymore
523 self.__setup_pid.close()
524 self.__setup_pid = None
526 pid = glib.spawn_async(argv=[self.__setup_cmd, "ibus-setup"],
527 flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0]
528 self.__setup_pid = pid
529 glib.child_watch_add(self.__setup_pid, self.__child_watch_cb)