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.1 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 GNU
16 # 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 library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
28 from gi.repository import Gio
29 from gi.repository import GLib
30 from gi.repository import Gtk
31 from gi.repository import IBus
34 import keyboardshortcut
36 from enginecombobox import EngineComboBox
37 from enginetreeview import EngineTreeView
38 from engineabout import EngineAbout
39 from i18n import DOMAINNAME, _, N_, init as i18n_init
63 def __flush_gtk_events(self):
64 while Gtk.events_pending():
68 super(Setup, self).__init__()
70 self.__settings_general = Gio.Settings("org.freedesktop.ibus.general");
71 self.__settings_hotkey = Gio.Settings(
72 "org.freedesktop.ibus.general.hotkey");
73 self.__settings_panel = Gio.Settings("org.freedesktop.ibus.panel");
75 # IBus.Bus() calls ibus_bus_new().
76 # Gtk.Builder().add_from_file() also calls ibus_bus_new_async()
77 # via ibus_im_context_new().
78 # Then if IBus.Bus() is called after Gtk.Builder().add_from_file(),
79 # the connection delay would be happened without an async
84 gtk_builder_file = path.join(path.dirname(__file__), "./setup.ui")
85 self.__builder = Gtk.Builder()
86 self.__builder.set_translation_domain(DOMAINNAME)
87 self.__builder.add_from_file(gtk_builder_file);
90 def __init_hotkey(self):
92 label = 'switch_engine'
93 shortcuts = self.__settings_hotkey.get_strv(name)
94 button = self.__builder.get_object("button_%s" % label)
95 entry = self.__builder.get_object("entry_%s" % label)
96 entry.set_text("; ".join(shortcuts))
97 tooltip = "\n".join(shortcuts)
99 _("Use shortcut with shift to switch to the previous input method")
100 entry.set_tooltip_text(tooltip)
101 button.connect("clicked", self.__shortcut_button_clicked_cb,
102 name, "general/hotkey", label, entry)
104 def __init_panel(self):
105 # lookup table orientation
106 self.__combobox_lookup_table_orientation = self.__builder.get_object(
107 "combobox_lookup_table_orientation")
108 self.__settings_panel.bind('lookup-table-orientation',
109 self.__combobox_lookup_table_orientation,
111 Gio.SettingsBindFlags.DEFAULT)
114 self.__combobox_panel_show = self.__builder.get_object(
115 "combobox_panel_show")
116 self.__settings_panel.bind('show',
117 self.__combobox_panel_show,
119 Gio.SettingsBindFlags.DEFAULT)
122 self.__combobox_panel_position = self.__builder.get_object(
123 "combobox_panel_position")
124 self.__combobox_panel_position.set_active(3)
125 #self.__settings_panel.bind('position',
126 # self.__combobox_panel_position,
128 # Gio.SettingsBindFlags.DEFAULT)
131 self.__checkbutton_custom_font = self.__builder.get_object(
132 "checkbutton_custom_font")
133 self.__settings_panel.bind('use-custom-font',
134 self.__checkbutton_custom_font,
136 Gio.SettingsBindFlags.DEFAULT)
138 self.__fontbutton_custom_font = self.__builder.get_object(
139 "fontbutton_custom_font")
140 self.__settings_panel.bind('custom-font',
141 self.__fontbutton_custom_font,
143 Gio.SettingsBindFlags.DEFAULT)
144 self.__settings_panel.bind('use-custom-font',
145 self.__fontbutton_custom_font,
147 Gio.SettingsBindFlags.GET)
149 # show icon on system tray
150 self.__checkbutton_show_icon_on_systray = self.__builder.get_object(
151 "checkbutton_show_icon_on_systray")
152 self.__settings_panel.bind('show-icon-on-systray',
153 self.__checkbutton_show_icon_on_systray,
155 Gio.SettingsBindFlags.DEFAULT)
158 self.__checkbutton_show_im_name = self.__builder.get_object(
159 "checkbutton_show_im_name")
160 self.__settings_panel.bind('show-im-name',
161 self.__checkbutton_show_im_name,
163 Gio.SettingsBindFlags.DEFAULT)
165 def __init_general(self):
167 self.__checkbutton_embed_preedit_text = self.__builder.get_object(
168 "checkbutton_embed_preedit_text")
169 self.__settings_general.bind('embed-preedit-text',
170 self.__checkbutton_embed_preedit_text,
172 Gio.SettingsBindFlags.DEFAULT)
174 # use system keyboard layout setting
175 self.__checkbutton_use_sys_layout = self.__builder.get_object(
176 "checkbutton_use_sys_layout")
177 self.__settings_general.bind('use-system-keyboard-layout',
178 self.__checkbutton_use_sys_layout,
180 Gio.SettingsBindFlags.DEFAULT)
182 # use global ime setting
183 self.__checkbutton_use_global_engine = self.__builder.get_object(
184 "checkbutton_use_global_engine")
185 self.__settings_general.bind('use-global-engine',
186 self.__checkbutton_use_global_engine,
188 Gio.SettingsBindFlags.DEFAULT)
191 self.__engines = self.__bus.list_engines()
192 self.__combobox = self.__builder.get_object("combobox_engines")
193 self.__combobox.set_engines(self.__engines)
196 for e in self.__engines:
197 tmp_dict[e.get_name()] = e
198 engine_names = self.__settings_general.get_strv('preload-engines')
199 engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
201 self.__treeview = self.__builder.get_object("treeview_engines")
202 self.__treeview.set_engines(engines)
204 button = self.__builder.get_object("button_engine_add")
205 button.connect("clicked", self.__button_engine_add_cb)
206 button = self.__builder.get_object("button_engine_remove")
207 button.connect("clicked", lambda *args:self.__treeview.remove_engine())
208 button = self.__builder.get_object("button_engine_up")
209 button.connect("clicked", lambda *args:self.__treeview.move_up_engine())
211 button = self.__builder.get_object("button_engine_down")
212 button.connect("clicked",
213 lambda *args:self.__treeview.move_down_engine())
215 button = self.__builder.get_object("button_engine_about")
216 button.connect("clicked", self.__button_engine_about_cb)
218 self.__engine_setup_exec_list = {}
219 button = self.__builder.get_object("button_engine_preferences")
220 button.connect("clicked", self.__button_engine_preferences_cb)
222 self.__combobox.connect("notify::active-engine",
223 self.__combobox_notify_active_engine_cb)
224 self.__treeview.connect("notify::active-engine", self.__treeview_notify_cb)
225 self.__treeview.connect("notify::engines", self.__treeview_notify_cb)
228 # add icon search path
229 self.__window = self.__builder.get_object("window_preferences")
230 self.__window.connect("delete-event", Gtk.main_quit)
232 self.__button_close = self.__builder.get_object("button_close")
233 self.__button_close.connect("clicked", Gtk.main_quit)
236 self.__checkbutton_auto_start = self.__builder.get_object(
237 "checkbutton_auto_start")
238 self.__checkbutton_auto_start.set_active(self.__is_auto_start())
239 self.__checkbutton_auto_start.connect("toggled",
240 self.__checkbutton_auto_start_toggled_cb)
244 self.__init_general()
246 def __combobox_notify_active_engine_cb(self, combobox, property):
247 engine = self.__combobox.get_active_engine()
248 button = self.__builder.get_object("button_engine_add")
249 button.set_sensitive(
250 engine != None and engine not in self.__treeview.get_engines())
252 def __get_engine_setup_exec_args(self, engine):
256 setup = str(engine.get_setup())
259 args.insert(1, path.basename(args[0]))
261 name = str(engine.get_name())
262 libexecdir = os.environ['IBUS_LIBEXECDIR']
263 setup_path = (libexecdir + '/' + 'ibus-setup-' if libexecdir != None \
264 else 'ibus-setup-') + name.split(':')[0]
265 if path.exists(setup_path):
266 args.append(setup_path)
267 args.append(path.basename(setup_path))
270 def __treeview_notify_cb(self, treeview, prop):
271 if prop.name not in ("active-engine", "engines"):
274 engines = self.__treeview.get_engines()
275 engine = self.__treeview.get_active_engine()
277 self.__builder.get_object("button_engine_remove").set_sensitive(engine != None)
278 self.__builder.get_object("button_engine_about").set_sensitive(engine != None)
279 self.__builder.get_object("button_engine_up").set_sensitive(engine not in engines[:1])
280 self.__builder.get_object("button_engine_down").set_sensitive(engine not in engines[-1:])
282 obj = self.__builder.get_object("button_engine_preferences")
283 if len(self.__get_engine_setup_exec_args(engine)) != 0:
284 obj.set_sensitive(True)
286 obj.set_sensitive(False)
288 if prop.name == "engines":
289 engine_names = map(lambda e: e.get_name(), engines)
290 self.__settings_general.set_strv('preload-engines', engine_names)
292 def __button_engine_add_cb(self, button):
293 engine = self.__combobox.get_active_engine()
294 self.__treeview.append_engine(engine)
296 def __button_engine_about_cb(self, button):
297 engine = self.__treeview.get_active_engine()
299 about = EngineAbout(engine)
303 def __button_engine_preferences_cb(self, button):
304 engine = self.__treeview.get_active_engine()
305 args = self.__get_engine_setup_exec_args(engine)
308 name = engine.get_name()
309 if name in self.__engine_setup_exec_list.keys():
311 wpid, sts = os.waitpid(self.__engine_setup_exec_list[name],
313 # the setup is still running.
318 del self.__engine_setup_exec_list[name]
319 self.__engine_setup_exec_list[name] = os.spawnl(os.P_NOWAIT, *args)
321 def __init_bus(self):
322 self.__bus = IBus.Bus()
323 if self.__bus.is_connected():
326 message = _("The IBus daemon is not running. Do you wish to start it?")
327 dlg = Gtk.MessageDialog(type = Gtk.MessageType.QUESTION,
328 buttons = Gtk.ButtonsType.YES_NO,
332 self.__flush_gtk_events()
333 if id != Gtk.ResponseType.YES:
336 main_loop = GLib.MainLoop()
339 GLib.timeout_add_seconds(timeout, lambda *args: main_loop.quit())
340 self.__bus.connect("connected", lambda *args: main_loop.quit())
342 os.spawnlp(os.P_NOWAIT, "ibus-daemon", "ibus-daemon", "--xim")
346 if self.__bus.is_connected():
347 message = _("IBus has been started! "
348 "If you cannot use IBus, add the following lines to your $HOME/.bashrc; then relog into your desktop.\n"
349 " export GTK_IM_MODULE=ibus\n"
350 " export XMODIFIERS=@im=ibus\n"
351 " export QT_IM_MODULE=ibus"
353 dlg = Gtk.MessageDialog(type = Gtk.MessageType.INFO,
354 buttons = Gtk.ButtonsType.OK,
358 self.__flush_gtk_events()
360 # Translators: %d == 5 currently
361 message = _("IBus daemon could not be started in %d seconds")
362 dlg = Gtk.MessageDialog(type = Gtk.MessageType.INFO,
363 buttons = Gtk.ButtonsType.OK,
364 text = message % timeout)
367 self.__flush_gtk_events()
370 def __shortcut_button_clicked_cb(self, button, name, section, _name, entry):
371 buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
372 Gtk.STOCK_OK, Gtk.ResponseType.OK)
373 title = _("Select keyboard shortcut for %s") % \
374 _("switching input methods")
375 dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title)
376 text = entry.get_text()
378 shortcuts = text.split("; ")
381 dialog.set_shortcuts(shortcuts)
383 shortcuts = dialog.get_shortcuts()
385 if id != Gtk.ResponseType.OK:
387 self.__settings_hotkey.set_strv(name, shortcuts)
388 text = "; ".join(shortcuts)
390 tooltip = "\n".join(shortcuts)
392 _("Use shortcut with shift to switch to the previous input method")
393 entry.set_tooltip_text(tooltip)
395 def __item_started_column_toggled_cb(self, cell, path_str, model):
398 iter = model.get_iter_from_string(path_str)
399 data = model.get_value(iter, COLUMN_DATA)
401 # do something with the value
402 if data[DATA_STARTED] == False:
404 self.__bus.register_start_engine(data[DATA_LANG], data[DATA_NAME])
406 dlg = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
407 buttons = Gtk.ButtonsType.CLOSE,
408 message_format = str(e))
411 self.__flush_gtk_events()
415 self.__bus.register_stop_engine(data[DATA_LANG], data[DATA_NAME])
417 dlg = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
418 buttons = Gtk.ButtonsType.CLOSE,
419 message_format = str(e))
422 self.__flush_gtk_events()
424 data[DATA_STARTED] = not data[DATA_STARTED]
427 model.set(iter, COLUMN_ENABLE, data[DATA_STARTED])
429 def __item_preload_column_toggled_cb(self, cell, path_str, model):
432 iter = model.get_iter_from_string(path_str)
433 data = model.get_value(iter, COLUMN_DATA)
435 data[DATA_PRELOAD] = not data[DATA_PRELOAD]
436 engine = "%s:%s" % (data[DATA_LANG], data[DATA_NAME])
438 if data[DATA_PRELOAD]:
439 if engine not in self.__preload_engines:
440 self.__preload_engines.add(engine)
441 self.__settings_general.set_strv('preload-engines',
442 list(self.__preload_engines))
444 if engine in self.__preload_engines:
445 self.__preload_engines.remove(engine)
446 self.__settings_general.set_strv('preload-engines',
447 list(self.__preload_engines))
450 model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
452 def __is_auto_start(self):
453 link_file = path.join(GLib.get_user_config_dir(),
454 "autostart/ibus.desktop")
455 ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
456 "share/applications/ibus.desktop")
458 if not path.exists(link_file):
460 if not path.islink(link_file):
462 if path.realpath(link_file) != ibus_desktop:
466 def __checkbutton_auto_start_toggled_cb(self, button):
467 auto_start_dir = path.join(GLib.get_user_config_dir(), "autostart")
468 if not path.isdir(auto_start_dir):
469 os.makedirs(auto_start_dir)
471 link_file = path.join(GLib.get_user_config_dir(),
472 "autostart/ibus.desktop")
473 ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
474 "share/applications/ibus.desktop")
480 if self.__checkbutton_auto_start.get_active():
481 os.symlink(ibus_desktop, link_file)
483 def __sigusr1_cb(self, *args):
484 self.__window.present()
487 self.__window.show_all()
488 signal.signal(signal.SIGUSR1, self.__sigusr1_cb)
491 if __name__ == "__main__":
493 locale.setlocale(locale.LC_ALL, '')
495 print >> sys.stderr, "Using the fallback 'C' locale"
496 locale.setlocale(locale.LC_ALL, 'C')