1
# vim:set et sts=4 sw=4:
5
# Copyright (c) 2007-2014 Peng Huang <shawn.p.huang@gmail.com>
6
# Copyright (c) 2007-2014 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
24
from __future__ import print_function
31
from gi.repository import Gio
32
from gi.repository import GLib
33
from gi.repository import Gtk
34
from gi.repository import IBus
37
import keyboardshortcut
39
from enginecombobox import EngineComboBox
40
from enginetreeview import EngineTreeView
41
from engineabout import EngineAbout
42
from i18n import DOMAINNAME, _, N_, init as i18n_init
66
def __flush_gtk_events(self):
67
while Gtk.events_pending():
71
super(Setup, self).__init__()
73
self.__settings_general = Gio.Settings("org.freedesktop.ibus.general");
74
self.__settings_hotkey = Gio.Settings(
75
"org.freedesktop.ibus.general.hotkey");
76
self.__settings_panel = Gio.Settings("org.freedesktop.ibus.panel");
78
# IBus.Bus() calls ibus_bus_new().
79
# Gtk.Builder().add_from_file() also calls ibus_bus_new_async()
80
# via ibus_im_context_new().
81
# Then if IBus.Bus() is called after Gtk.Builder().add_from_file(),
82
# the connection delay would be happened without an async
87
gtk_builder_file = path.join(path.dirname(__file__), "./setup.ui")
88
self.__builder = Gtk.Builder()
89
self.__builder.set_translation_domain(DOMAINNAME)
90
self.__builder.add_from_file(gtk_builder_file);
93
def __init_hotkey(self):
95
label = 'switch_engine'
96
shortcuts = self.__settings_hotkey.get_strv(name)
97
button = self.__builder.get_object("button_%s" % label)
98
entry = self.__builder.get_object("entry_%s" % label)
99
entry.set_text("; ".join(shortcuts))
100
tooltip = "\n".join(shortcuts)
102
_("Use shortcut with shift to switch to the previous input method")
103
entry.set_tooltip_text(tooltip)
104
button.connect("clicked", self.__shortcut_button_clicked_cb,
105
name, "general/hotkey", label, entry)
107
def __init_panel(self):
108
# lookup table orientation
109
self.__combobox_lookup_table_orientation = self.__builder.get_object(
110
"combobox_lookup_table_orientation")
111
self.__settings_panel.bind('lookup-table-orientation',
112
self.__combobox_lookup_table_orientation,
114
Gio.SettingsBindFlags.DEFAULT)
117
self.__combobox_panel_show = self.__builder.get_object(
118
"combobox_panel_show")
119
self.__settings_panel.bind('show',
120
self.__combobox_panel_show,
122
Gio.SettingsBindFlags.DEFAULT)
125
self.__combobox_panel_position = self.__builder.get_object(
126
"combobox_panel_position")
127
self.__combobox_panel_position.set_active(3)
128
#self.__settings_panel.bind('position',
129
# self.__combobox_panel_position,
131
# Gio.SettingsBindFlags.DEFAULT)
134
self.__checkbutton_custom_font = self.__builder.get_object(
135
"checkbutton_custom_font")
136
self.__settings_panel.bind('use-custom-font',
137
self.__checkbutton_custom_font,
139
Gio.SettingsBindFlags.DEFAULT)
141
self.__fontbutton_custom_font = self.__builder.get_object(
142
"fontbutton_custom_font")
143
self.__settings_panel.bind('custom-font',
144
self.__fontbutton_custom_font,
146
Gio.SettingsBindFlags.DEFAULT)
147
self.__settings_panel.bind('use-custom-font',
148
self.__fontbutton_custom_font,
150
Gio.SettingsBindFlags.GET)
152
# show icon on system tray
153
self.__checkbutton_show_icon_on_systray = self.__builder.get_object(
154
"checkbutton_show_icon_on_systray")
155
self.__settings_panel.bind('show-icon-on-systray',
156
self.__checkbutton_show_icon_on_systray,
158
Gio.SettingsBindFlags.DEFAULT)
161
self.__checkbutton_show_im_name = self.__builder.get_object(
162
"checkbutton_show_im_name")
163
self.__settings_panel.bind('show-im-name',
164
self.__checkbutton_show_im_name,
166
Gio.SettingsBindFlags.DEFAULT)
168
def __init_general(self):
170
self.__checkbutton_embed_preedit_text = self.__builder.get_object(
171
"checkbutton_embed_preedit_text")
172
self.__settings_general.bind('embed-preedit-text',
173
self.__checkbutton_embed_preedit_text,
175
Gio.SettingsBindFlags.DEFAULT)
177
# use system keyboard layout setting
178
self.__checkbutton_use_sys_layout = self.__builder.get_object(
179
"checkbutton_use_sys_layout")
180
self.__settings_general.bind('use-system-keyboard-layout',
181
self.__checkbutton_use_sys_layout,
183
Gio.SettingsBindFlags.DEFAULT)
185
# use global ime setting
186
self.__checkbutton_use_global_engine = self.__builder.get_object(
187
"checkbutton_use_global_engine")
188
self.__settings_general.bind('use-global-engine',
189
self.__checkbutton_use_global_engine,
191
Gio.SettingsBindFlags.DEFAULT)
194
self.__engines = self.__bus.list_engines()
195
self.__combobox = self.__builder.get_object("combobox_engines")
196
self.__combobox.set_engines(self.__engines)
199
for e in self.__engines:
200
tmp_dict[e.get_name()] = e
201
engine_names = self.__settings_general.get_strv('preload-engines')
202
engines = [tmp_dict[name] for name in engine_names if name in tmp_dict]
204
self.__treeview = self.__builder.get_object("treeview_engines")
205
self.__treeview.set_engines(engines)
207
button = self.__builder.get_object("button_engine_add")
208
button.connect("clicked", self.__button_engine_add_cb)
209
button = self.__builder.get_object("button_engine_remove")
210
button.connect("clicked", lambda *args:self.__treeview.remove_engine())
211
button = self.__builder.get_object("button_engine_up")
212
button.connect("clicked", lambda *args:self.__treeview.move_up_engine())
214
button = self.__builder.get_object("button_engine_down")
215
button.connect("clicked",
216
lambda *args:self.__treeview.move_down_engine())
218
button = self.__builder.get_object("button_engine_about")
219
button.connect("clicked", self.__button_engine_about_cb)
221
self.__engine_setup_exec_list = {}
222
button = self.__builder.get_object("button_engine_preferences")
223
button.connect("clicked", self.__button_engine_preferences_cb)
225
self.__combobox.connect("notify::active-engine",
226
self.__combobox_notify_active_engine_cb)
227
self.__treeview.connect("notify::active-engine", self.__treeview_notify_cb)
228
self.__treeview.connect("notify::engines", self.__treeview_notify_cb)
231
# add icon search path
232
self.__window = self.__builder.get_object("window_preferences")
233
self.__window.connect("delete-event", Gtk.main_quit)
235
self.__button_close = self.__builder.get_object("button_close")
236
self.__button_close.connect("clicked", Gtk.main_quit)
239
self.__checkbutton_auto_start = self.__builder.get_object(
240
"checkbutton_auto_start")
241
self.__checkbutton_auto_start.set_active(self.__is_auto_start())
242
self.__checkbutton_auto_start.connect("toggled",
243
self.__checkbutton_auto_start_toggled_cb)
247
self.__init_general()
249
def __combobox_notify_active_engine_cb(self, combobox, property):
250
engine = self.__combobox.get_active_engine()
251
button = self.__builder.get_object("button_engine_add")
252
button.set_sensitive(
253
engine != None and engine not in self.__treeview.get_engines())
255
def __get_engine_setup_exec_args(self, engine):
259
setup = str(engine.get_setup())
262
args.insert(1, path.basename(args[0]))
264
name = str(engine.get_name())
265
libexecdir = os.environ['IBUS_LIBEXECDIR']
266
setup_path = (libexecdir + '/' + 'ibus-setup-' if libexecdir != None \
267
else 'ibus-setup-') + name.split(':')[0]
268
if path.exists(setup_path):
269
args.append(setup_path)
270
args.append(path.basename(setup_path))
273
def __treeview_notify_cb(self, treeview, prop):
274
if prop.name not in ("active-engine", "engines"):
277
engines = self.__treeview.get_engines()
278
engine = self.__treeview.get_active_engine()
280
self.__builder.get_object("button_engine_remove").set_sensitive(engine != None)
281
self.__builder.get_object("button_engine_about").set_sensitive(engine != None)
282
self.__builder.get_object("button_engine_up").set_sensitive(engine not in engines[:1])
283
self.__builder.get_object("button_engine_down").set_sensitive(engine not in engines[-1:])
285
obj = self.__builder.get_object("button_engine_preferences")
286
if len(self.__get_engine_setup_exec_args(engine)) != 0:
287
obj.set_sensitive(True)
289
obj.set_sensitive(False)
291
if prop.name == "engines":
292
engine_names = [e.get_name() for e in engines]
293
self.__settings_general.set_strv('preload-engines', engine_names)
295
def __button_engine_add_cb(self, button):
296
engine = self.__combobox.get_active_engine()
297
self.__treeview.append_engine(engine)
299
def __button_engine_about_cb(self, button):
300
engine = self.__treeview.get_active_engine()
302
about = EngineAbout(engine)
306
def __button_engine_preferences_cb(self, button):
307
engine = self.__treeview.get_active_engine()
308
args = self.__get_engine_setup_exec_args(engine)
311
name = engine.get_name()
312
if name in list(self.__engine_setup_exec_list.keys()):
314
wpid, sts = os.waitpid(self.__engine_setup_exec_list[name],
316
# the setup is still running.
321
del self.__engine_setup_exec_list[name]
322
self.__engine_setup_exec_list[name] = os.spawnl(os.P_NOWAIT, *args)
324
def __init_bus(self):
325
self.__bus = IBus.Bus()
326
if self.__bus.is_connected():
329
message = _("The IBus daemon is not running. Do you wish to start it?")
330
dlg = Gtk.MessageDialog(type = Gtk.MessageType.QUESTION,
331
buttons = Gtk.ButtonsType.YES_NO,
335
self.__flush_gtk_events()
336
if id != Gtk.ResponseType.YES:
339
main_loop = GLib.MainLoop()
342
GLib.timeout_add_seconds(timeout, lambda *args: main_loop.quit())
343
self.__bus.connect("connected", lambda *args: main_loop.quit())
345
os.spawnlp(os.P_NOWAIT, "ibus-daemon", "ibus-daemon", "--xim")
349
if self.__bus.is_connected():
350
message = _("IBus has been started! "
351
"If you cannot use IBus, add the following lines to your $HOME/.bashrc; then relog into your desktop.\n"
352
" export GTK_IM_MODULE=ibus\n"
353
" export XMODIFIERS=@im=ibus\n"
354
" export QT_IM_MODULE=ibus"
356
dlg = Gtk.MessageDialog(type = Gtk.MessageType.INFO,
357
buttons = Gtk.ButtonsType.OK,
361
self.__flush_gtk_events()
363
# Translators: %d == 5 currently
364
message = _("IBus daemon could not be started in %d seconds")
365
dlg = Gtk.MessageDialog(type = Gtk.MessageType.INFO,
366
buttons = Gtk.ButtonsType.OK,
367
text = message % timeout)
370
self.__flush_gtk_events()
373
def __shortcut_button_clicked_cb(self, button, name, section, _name, entry):
374
buttons = (_("_Cancel"), Gtk.ResponseType.CANCEL,
375
_("_OK"), Gtk.ResponseType.OK)
376
title1 = _("Select keyboard shortcut for %s")
377
# Translators: Title of the window
378
title2 = _("switching input methods")
379
title = title1 % title2
380
dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title)
381
text = entry.get_text()
383
shortcuts = text.split("; ")
386
dialog.set_shortcuts(shortcuts)
388
shortcuts = dialog.get_shortcuts()
390
if id != Gtk.ResponseType.OK:
392
self.__settings_hotkey.set_strv(name, shortcuts)
393
text = "; ".join(shortcuts)
395
tooltip = "\n".join(shortcuts)
397
_("Use shortcut with shift to switch to the previous input method")
398
entry.set_tooltip_text(tooltip)
400
def __item_started_column_toggled_cb(self, cell, path_str, model):
403
iter = model.get_iter_from_string(path_str)
404
data = model.get_value(iter, COLUMN_DATA)
406
# do something with the value
407
if data[DATA_STARTED] == False:
409
self.__bus.register_start_engine(data[DATA_LANG], data[DATA_NAME])
410
except Exception as e:
411
dlg = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
412
buttons = Gtk.ButtonsType.CLOSE,
413
message_format = str(e))
416
self.__flush_gtk_events()
420
self.__bus.register_stop_engine(data[DATA_LANG], data[DATA_NAME])
421
except Exception as e:
422
dlg = Gtk.MessageDialog(type = Gtk.MessageType.ERROR,
423
buttons = Gtk.ButtonsType.CLOSE,
424
message_format = str(e))
427
self.__flush_gtk_events()
429
data[DATA_STARTED] = not data[DATA_STARTED]
432
model.set(iter, COLUMN_ENABLE, data[DATA_STARTED])
434
def __item_preload_column_toggled_cb(self, cell, path_str, model):
437
iter = model.get_iter_from_string(path_str)
438
data = model.get_value(iter, COLUMN_DATA)
440
data[DATA_PRELOAD] = not data[DATA_PRELOAD]
441
engine = "%s:%s" % (data[DATA_LANG], data[DATA_NAME])
443
if data[DATA_PRELOAD]:
444
if engine not in self.__preload_engines:
445
self.__preload_engines.add(engine)
446
self.__settings_general.set_strv('preload-engines',
447
list(self.__preload_engines))
449
if engine in self.__preload_engines:
450
self.__preload_engines.remove(engine)
451
self.__settings_general.set_strv('preload-engines',
452
list(self.__preload_engines))
455
model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
457
def __is_auto_start(self):
458
link_file = path.join(GLib.get_user_config_dir(),
459
"autostart/ibus.desktop")
460
ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
461
"share/applications/ibus.desktop")
463
if not path.exists(link_file):
465
if not path.islink(link_file):
467
if path.realpath(link_file) != ibus_desktop:
471
def __checkbutton_auto_start_toggled_cb(self, button):
472
auto_start_dir = path.join(GLib.get_user_config_dir(), "autostart")
473
if not path.isdir(auto_start_dir):
474
os.makedirs(auto_start_dir)
476
link_file = path.join(GLib.get_user_config_dir(),
477
"autostart/ibus.desktop")
478
ibus_desktop = path.join(os.getenv("IBUS_PREFIX"),
479
"share/applications/ibus.desktop")
485
if self.__checkbutton_auto_start.get_active():
486
os.symlink(ibus_desktop, link_file)
488
def __sigusr1_cb(self, *args):
489
self.__window.present()
492
self.__window.show_all()
493
signal.signal(signal.SIGUSR1, self.__sigusr1_cb)
496
if __name__ == "__main__":
498
locale.setlocale(locale.LC_ALL, '')
500
print("Using the fallback 'C' locale", file=sys.stderr)
501
locale.setlocale(locale.LC_ALL, 'C')