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 # gtk_status_icon_set_name() is not available in pygtk2 2.17
114 if hasattr(self.__status_icon, 'set_name'):
115 self.__status_icon.set_name('ibus-ui-gtk')
116 self.__status_icon.set_title(_("IBus Panel"))
117 self.__status_icon.set_visible(True)
118 self.__status_icon.connect("popup-menu", self.__status_icon_popup_menu_cb)
119 self.__status_icon.connect("activate", self.__status_icon_activate_cb)
120 self.__status_icon.set_from_icon_name(ICON_KEYBOARD)
121 self.__status_icon.set_tooltip(_("IBus input method framework"))
122 self.__status_icon.set_visible(True)
124 self.__config_load_lookup_table_orientation()
125 self.__config_load_show()
126 self.__config_load_position()
127 self.__config_load_custom_font()
128 self.__config_load_show_icon_on_systray()
129 self.__config_load_show_im_name()
130 # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0)
132 def set_cursor_location(self, x, y, w, h):
133 self.__candidate_panel.set_cursor_location(x, y, w, h)
135 def update_preedit_text(self, text, cursor_pos, visible):
136 self.__candidate_panel.update_preedit_text(text, cursor_pos, visible)
138 def show_preedit_text(self):
139 self.__candidate_panel.show_preedit_text()
141 def hide_preedit_text(self):
142 self.__candidate_panel.hide_preedit_text()
144 def update_auxiliary_text(self, text, visible):
145 self.__candidate_panel.update_auxiliary_text(text, visible)
147 def show_auxiliary_text(self):
148 self.__candidate_panel.show_auxiliary_text()
150 def hide_auxiliary_text(self):
151 self.__candidate_panel.hide_auxiliary_text()
153 def update_lookup_table(self, lookup_table, visible):
154 self.__candidate_panel.update_lookup_table(lookup_table, visible)
156 def show_lookup_table(self):
157 self.__candidate_panel.show_lookup_table()
159 def hide_lookup_table(self):
160 self.__candidate_panel.hide_lookup_table()
162 def page_up_lookup_table(self):
163 self.__candidate_panel.page_up_lookup_table()
165 def page_down_lookup_table(self):
166 self.__candidate_panel.page_down_lookup_table()
168 def cursor_up_lookup_table(self):
169 self.__candidate_panel.cursor_up_lookup_table()
171 def cursor_down_lookup_table(self):
172 self.__candidate_panel.cursor_down_lookup_table()
174 def show_candidate_window(self):
175 self.__candidate_panel.show_all()
177 def hide_candidate_window(self):
178 self.__candidate_panel.hide_all()
180 def show_language_bar(self):
181 self.__language_bar.show_all()
183 def hide_language_bar(self):
184 self.__language_bar.hide_all()
186 def register_properties(self, props):
187 self.__language_bar.register_properties(props)
189 def update_property(self, prop):
190 self.__language_bar.update_property(prop)
192 def get_status_icon(self):
193 return self.__status_icon
195 def __set_im_icon(self, icon_name):
197 icon_name = ICON_ENGINE
198 self.__language_bar.set_im_icon(icon_name)
199 if icon_name.startswith("/"):
200 self.__status_icon.set_from_file(icon_name)
202 self.__status_icon.set_from_icon_name(icon_name)
204 def __set_im_name(self, name):
205 self.__language_bar.set_im_name(name)
207 def focus_in(self, ic):
209 self.__focus_ic = ibus.InputContext(self.__bus, ic)
210 enabled = self.__focus_ic.is_enabled()
211 self.__language_bar.set_enabled(enabled)
214 self.__set_im_icon(ICON_KEYBOARD)
215 self.__set_im_name(None)
217 engine = self.__focus_ic.get_engine()
219 self.__set_im_icon(engine.icon)
220 self.__set_im_name(engine.longname)
222 self.__set_im_icon(ICON_KEYBOARD)
223 self.__set_im_name(None)
224 self.__language_bar.focus_in()
226 def focus_out(self, ic):
228 self.__focus_ic = None
229 self.__language_bar.set_enabled(False)
230 self.__language_bar.focus_out()
231 self.__set_im_icon(ICON_KEYBOARD)
232 self.__set_im_name(None)
234 def state_changed(self):
235 if not self.__focus_ic:
238 enabled = self.__focus_ic.is_enabled()
239 self.__language_bar.set_enabled(enabled)
243 self.__set_im_icon(ICON_KEYBOARD)
244 self.__set_im_name(None)
246 engine = self.__focus_ic.get_engine()
248 self.__set_im_icon(engine.icon)
249 self.__set_im_name(engine.longname)
251 self.__set_im_icon(ICON_KEYBOARD)
252 self.__set_im_name(None)
256 self.__candidate_panel.reset()
257 self.__language_bar.reset()
259 def start_setup(self):
262 def do_destroy(self):
265 def __config_load_lookup_table_orientation(self):
266 value = self.__config.get_value("panel", "lookup_table_orientation", 0)
267 if value in (ibus.ORIENTATION_HORIZONTAL, ibus.ORIENTATION_VERTICAL):
270 orientation = ibus.ORIENTATION_HORIZONTAL
271 self.__candidate_panel.set_orientation(orientation)
273 def __config_load_show(self):
274 show = self.__config.get_value("panel", "show", 0)
275 self.__language_bar.set_show(show)
277 def __config_load_position(self):
278 x = self.__config.get_value("panel", "x", -1)
279 y = self.__config.get_value("panel", "y", -1)
280 self.__language_bar.set_position(x, y)
282 def __config_load_custom_font(self):
283 use_custom_font = self.__config.get_value("panel", "use_custom_font", False)
284 font_name = gtk.settings_get_default().get_property("gtk-font-name")
285 font_name = unicode(font_name, "utf-8")
286 custom_font = self.__config.get_value("panel", "custom_font", font_name)
287 style_string = 'style "custom-font" { font_name="%s" }\n' \
288 'class "IBusCandidateLabel" style "custom-font"\n'
290 style_string = style_string % custom_font
291 gtk.rc_parse_string(style_string)
293 style_string = style_string % ""
294 gtk.rc_parse_string(style_string)
296 settings = gtk.settings_get_default()
297 gtk.rc_reset_styles(settings)
299 def __config_load_show_icon_on_systray(self):
300 value = self.__config.get_value("panel", "show_icon_on_systray", True)
301 self.__status_icon.set_visible(True if value else False)
303 def __config_load_show_im_name(self):
304 value = self.__config.get_value("panel", "show_im_name", False)
305 self.__language_bar.set_show_im_name(value)
307 def __config_value_changed_cb(self, bus, section, name, value):
308 if section != "panel":
310 if name == "lookup_table_orientation":
311 self.__config_load_lookup_table_orientation()
313 self.__config_load_show()
314 elif name == "use_custom_font" or name == "custom_font":
315 self.__config_load_custom_font()
316 elif name == "show_icon_on_systray":
317 self.__config_load_show_icon_on_systray()
318 elif name == "show_im_name":
319 self.__config_load_show_im_name()
320 elif name == "x" or name == "y":
323 print >> sys.stderr, "Unknown config item [%s]" % name
325 def __config_reloaded_cb(self, bus):
328 def __create_sys_menu(self):
330 item = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
331 item.connect("activate",
332 self.__sys_menu_item_activate_cb, gtk.STOCK_PREFERENCES)
334 item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
335 item.connect("activate",
336 self.__sys_menu_item_activate_cb, gtk.STOCK_ABOUT)
338 menu.add(gtk.SeparatorMenuItem())
339 item = gtk.MenuItem(_("Restart"))
340 item.connect("activate",
341 self.__sys_menu_item_activate_cb, "Restart")
343 item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
344 item.connect("activate",
345 self.__sys_menu_item_activate_cb, gtk.STOCK_QUIT)
349 menu.set_take_focus(False)
352 # def __create_im_menu(self):
354 # engines = self.__bus.list_active_engines()
357 # for engine in engines:
358 # lang = ibus.get_language_name(engine.language)
359 # if lang not in tmp:
361 # tmp[lang].append(engine)
364 # other = tmp.get(_("Other"), [])
365 # if _("Other") in tmp:
366 # langs.remove(_("Other"))
367 # langs.append(_("Other"))
369 # size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
371 # if len(tmp[lang]) == 1:
372 # engine = tmp[lang][0]
373 # item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
375 # item.set_image(_icon.IconWidget(engine.icon, size[0]))
377 # item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
378 # item.connect("activate", self.__im_menu_item_activate_cb, engine)
381 # item = gtk.MenuItem(lang)
383 # submenu = gtk.Menu()
384 # item.set_submenu(submenu)
385 # for engine in tmp[lang]:
386 # item = gtk.ImageMenuItem(engine.longname)
388 # item.set_image(_icon.IconWidget(engine.icon, size[0]))
390 # item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
391 # item.connect("activate", self.__im_menu_item_activate_cb, engine)
394 # item = gtk.ImageMenuItem(_("Turn off input method"))
395 # item.set_image(_icon.IconWidget("gtk-close", size[0]))
396 # item.connect("activate", self.__im_menu_item_activate_cb, None)
400 # menu.set_take_focus(False)
403 def __create_im_menu(self):
404 engines = self.__bus.list_active_engines()
406 (self.__focus_ic != None and self.__focus_ic.get_engine()) or \
407 (engines and engines[0]) or \
410 size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
412 for i, engine in enumerate(engines):
413 lang = ibus.get_language_name(engine.language)
414 item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname))
415 if current_engine and current_engine.name == engine.name:
416 for widget in item.get_children():
417 if isinstance(widget, gtk.Label):
418 widget.set_markup("<b>%s</b>" % widget.get_text())
420 item.set_image(_icon.IconWidget(engine.icon, size[0]))
422 item.set_image(_icon.IconWidget(ICON_ENGINE, size[0]))
423 item.connect("activate", self.__im_menu_item_activate_cb, engine)
426 item = gtk.ImageMenuItem(_("Turn off input method"))
427 item.set_image(_icon.IconWidget("gtk-close", size[0]))
428 item.connect("activate", self.__im_menu_item_activate_cb, None)
429 if self.__focus_ic == None or not self.__focus_ic.is_enabled():
430 item.set_sensitive(False)
434 menu.set_take_focus(False)
437 def __get_im_menu_cb(self, languagebar):
438 menu = self.__create_im_menu()
441 def __show_engine_about_cb(self, langagebar):
443 engine = self.__focus_ic.get_engine()
444 dlg = EngineAbout(engine)
450 def __position_changed_cb(self, langagebar, x, y):
451 self.__config.set_value("panel", "x", x)
452 self.__config.set_value("panel", "y", y)
454 def __status_icon_popup_menu_cb(self, status_icon, button, active_time):
455 menu = self.__create_sys_menu()
456 menu.popup(None, None,
457 gtk.status_icon_position_menu,
462 def __status_icon_activate_cb(self, status_icon):
463 if not self.__focus_ic:
465 item = gtk.ImageMenuItem(_("No input window"))
466 size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
467 item.set_image(_icon.IconWidget("gtk-info", size[0]))
471 menu = self.__create_im_menu()
472 self.__language_bar.create_im_menu(menu)
473 menu.popup(None, None,
474 gtk.status_icon_position_menu,
476 gtk.get_current_event_time(),
479 def __im_menu_item_activate_cb(self, item, engine):
480 if not self.__focus_ic:
483 self.__focus_ic.set_engine(engine)
485 self.__focus_ic.disable()
487 def __sys_menu_item_activate_cb(self, item, command):
488 if command == gtk.STOCK_PREFERENCES:
490 elif command == gtk.STOCK_ABOUT:
491 about_dialog = gtk.AboutDialog()
492 about_dialog.set_program_name("IBus")
493 about_dialog.set_version(ibus.get_version())
494 about_dialog.set_copyright(ibus.get_copyright())
495 about_dialog.set_license(ibus.get_license())
496 about_dialog.set_comments(_("IBus is an intelligent input bus for Linux/Unix."))
497 about_dialog.set_website("http://code.google.com/p/ibus")
498 about_dialog.set_authors(["Peng Huang <shawn.p.huang@gmail.com>"])
499 about_dialog.set_documenters(["Peng Huang <shawn.p.huang@gmail.com>"])
500 about_dialog.set_translator_credits(_("translator-credits"))
501 about_dialog.set_logo_icon_name("ibus")
502 about_dialog.set_icon_name("ibus")
504 about_dialog.destroy()
505 elif command == gtk.STOCK_QUIT:
506 self.__bus.exit(False)
507 elif command == "Restart":
508 self.__bus.exit(True)
510 print >> sys.stderr, "Unknown command %s" % command
512 def __child_watch_cb(self, pid, status):
513 if self.__setup_pid == pid:
514 self.__setup_pid.close()
515 self.__setup_pid = None
517 def __start_setup(self):
518 if self.__setup_pid != None:
520 # if setup dialog is running, bring the dialog to front by SIGUSR1
521 os.kill(self.__setup_pid, signal.SIGUSR1)
524 # seems the setup dialog is not running anymore
525 self.__setup_pid.close()
526 self.__setup_pid = None
528 pid = glib.spawn_async(argv=[self.__setup_cmd, "ibus-setup"],
529 flags=glib.SPAWN_DO_NOT_REAP_CHILD)[0]
530 self.__setup_pid = pid
531 glib.child_watch_add(self.__setup_pid, self.__child_watch_cb)