a0b4beec929ac700b3b4063e1d8e95e57c94e363
[platform/upstream/ibus.git] / setup / main.py
1 # vim:set et sts=4 sw=4:
2 #
3 # ibus - The Input Bus
4 #
5 # Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
6 #
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2 of the License, or (at your option) any later version.
11 #
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this program; if not, write to the
19 # Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 # Boston, MA  02111-1307  USA
21
22 import os
23 import sys
24 from os import path
25 import time
26 from xdg import BaseDirectory
27 import gtk
28 import gobject
29 import ibus
30 from gtk import gdk, glade
31 import keyboardshortcut
32
33 from gettext import dgettext
34 _  = lambda a : dgettext("ibus", a)
35 N_ = lambda a : a
36
37 (
38     COLUMN_NAME,
39     COLUMN_ENABLE,
40     COLUMN_PRELOAD,
41     COLUMN_VISIBLE,
42     COLUMN_ICON,
43     COLUMN_DATA,
44 ) = range(6)
45
46 (
47     DATA_NAME,
48     DATA_LOCAL_NAME,
49     DATA_LANG,
50     DATA_ICON,
51     DATA_AUTHOR,
52     DATA_CREDITS,
53     DATA_EXEC,
54     DATA_STARTED,
55     DATA_PRELOAD
56 ) = range(9)
57
58 CONFIG_GENERAL_SHORTCUT = "/general/keyboard_shortcut_%s"
59 CONFIG_PRELOAD_ENGINES = "/general/preload_engines"
60 CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION = "/panel/lookup_table_orientation"
61 CONFIG_PANEL_AUTO_HIDE = "/panel/auto_hide"
62 CONFIG_PANEL_USE_CUSTOM_FONT = "/panel/use_custom_font"
63 CONFIG_PANEL_CUSTOM_FONT = "/panel/custom_font"
64
65 class Setup(object):
66     def __flush_gtk_events(self):
67         while gtk.events_pending():
68             gtk.main_iteration()
69
70     def __init__(self):
71         super(Setup, self).__init__()
72         glade.textdomain("ibus")
73         glade_file = path.join(path.dirname(__file__), "./setup.glade")
74         self.__xml = glade.XML(glade_file)
75         self.__bus = None
76         self.__init_bus()
77         self.__init_ui()
78
79     def __init_ui(self):
80         # add icon search path
81         icon_theme = gtk.icon_theme_get_default()
82         dir = path.dirname(__file__)
83         icondir = path.join(dir, "..", "icons")
84         icon_theme.prepend_search_path(icondir)
85
86
87         self.__dialog = self.__xml.get_widget("dialog_setup")
88
89         # auto start ibus
90         self.__checkbutton_auto_start = self.__xml.get_widget("checkbutton_auto_start")
91         self.__checkbutton_auto_start.set_active(self.__is_auto_start())
92         self.__checkbutton_auto_start.connect("toggled", self.__checkbutton_auto_start_toggled_cb)
93
94         # keyboard shortcut
95         for name in (N_("trigger"), N_("next_engine"), N_("prev_engine")):
96             shortcuts = self.__bus.config_get_value(CONFIG_GENERAL_SHORTCUT % name, [])
97             button = self.__xml.get_widget("button_%s" % name)
98             entry = self.__xml.get_widget("entry_%s" % name)
99             entry.set_text(";".join(shortcuts))
100             button.connect("clicked", self.__shortcut_button_clicked_cb, name, entry)
101
102         # lookup table orientation
103         self.__combobox_lookup_table_orientation = self.__xml.get_widget("combobox_lookup_table_orientation")
104         self.__combobox_lookup_table_orientation.set_active(
105             self.__bus.config_get_value(CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION, 0))
106         self.__combobox_lookup_table_orientation.connect("changed",
107             self.__combobox_lookup_table_orientation_changed_cb)
108
109         # auto hide
110         self.__checkbutton_auto_hide = self.__xml.get_widget("checkbutton_auto_hide")
111         self.__checkbutton_auto_hide.set_active(
112             self.__bus.config_get_value(CONFIG_PANEL_AUTO_HIDE, False))
113         self.__checkbutton_auto_hide.connect("toggled", self.__checkbutton_auto_hide_toggled_cb)
114
115         # custom font
116         self.__checkbutton_custom_font = self.__xml.get_widget("checkbutton_custom_font")
117         self.__checkbutton_custom_font.set_active(
118             self.__bus.config_get_value(CONFIG_PANEL_USE_CUSTOM_FONT, False))
119         self.__checkbutton_custom_font.connect("toggled", self.__checkbutton_custom_font_toggled_cb)
120
121         self.__fontbutton_custom_font = self.__xml.get_widget("fontbutton_custom_font")
122         if self.__bus.config_get_value(CONFIG_PANEL_USE_CUSTOM_FONT, False):
123             self.__fontbutton_custom_font.set_sensitive(True)
124         else:
125             self.__fontbutton_custom_font.set_sensitive(False)
126         font_name = gtk.settings_get_default().get_property("gtk-font-name")
127         font_name = unicode(font_name, "utf-8")
128         font_name = self.__bus.config_get_value(CONFIG_PANEL_CUSTOM_FONT, font_name)
129         self.__fontbutton_custom_font.connect("notify::font-name", self.__fontbutton_custom_font_notify_cb)
130         self.__fontbutton_custom_font.set_font_name(font_name)
131
132         self.__init_engine_view()
133
134     def __init_bus(self):
135         try:
136             self.__bus = ibus.Bus()
137             self.__bus.connect("config-value-changed", self.__config_value_changed_cb)
138             self.__bus.connect("config-reloaded", self.__config_reloaded_cb)
139             self.__bus.config_add_watch("/general")
140             self.__bus.config_add_watch("/panel")
141         except:
142             while self.__bus == None:
143                 message = _("IBus daemon is not started. Do you want to start it now?")
144                 print >> sys.stderr, message
145                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION,
146                         buttons = gtk.BUTTONS_YES_NO,
147                         message_format = message)
148                 id = dlg.run()
149                 dlg.destroy()
150                 self.__flush_gtk_events()
151                 if id != gtk.RESPONSE_YES:
152                     sys.exit(0)
153                 pid = os.spawnlp(os.P_NOWAIT, "ibus", "ibus")
154                 time.sleep(1)
155                 try:
156                     self.__bus = ibus.Bus()
157                 except:
158                     continue
159                 message = _("IBus has been started! " + \
160                     "If you can not use IBus, please add below lines in $HOME/.bashrc, and relogin your desktop.\n" + \
161                     "  export GTK_IM_MODULE=ibus\n" + \
162                     "  export XMODIFIERS=@im=ibus\n"
163                     "  export QT_IM_MODULE=ibus"
164                     )
165                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
166                                         buttons = gtk.BUTTONS_OK,
167                                         message_format = message)
168                 id = dlg.run()
169                 dlg.destroy()
170                 self.__flush_gtk_events()
171
172     def __init_engine_view(self):
173         # engines tree
174         self.__tree = self.__xml.get_widget("treeview_engines")
175         self.__preload_engines = set(self.__bus.config_get_value(CONFIG_PRELOAD_ENGINES, []))
176         model = self.__create_model()
177         self.__tree.set_model(model)
178
179         # column for engine
180         column = gtk.TreeViewColumn()
181         column.set_title(_("Engine"))
182
183         renderer = gtk.CellRendererPixbuf()
184         renderer.set_property("xalign", 0.5)
185
186         column.pack_start(renderer)
187         column.set_attributes(renderer, pixbuf = COLUMN_ICON, visible = COLUMN_VISIBLE)
188
189         renderer = gtk.CellRendererText()
190         renderer.set_property("xalign", 0.0)
191
192         # column.set_clickable(True)
193         column.pack_start(renderer)
194         column.set_attributes(renderer, text = COLUMN_NAME)
195
196         self.__tree.append_column(column)
197
198         # column for started
199         renderer = gtk.CellRendererToggle()
200         renderer.set_data('column', COLUMN_ENABLE)
201         renderer.set_property("xalign", 0.5)
202         renderer.connect("toggled", self.__item_started_column_toggled_cb, model)
203
204         #col_offset = gtk.TreeViewColumn("Holiday", renderer, text=HOLIDAY_NAME)
205         column = gtk.TreeViewColumn(_("Started"), renderer, active = COLUMN_ENABLE, visible = COLUMN_VISIBLE)
206         self.__tree.append_column(column)
207
208         # column for preload
209         renderer = gtk.CellRendererToggle()
210         renderer.set_data('column', COLUMN_PRELOAD)
211         renderer.set_property("xalign", 0.5)
212         renderer.connect("toggled", self.__item_preload_column_toggled_cb, model)
213
214         column = gtk.TreeViewColumn(_("Preload"), renderer, active = COLUMN_PRELOAD, visible = COLUMN_VISIBLE)
215         self.__tree.append_column(column)
216
217         renderer = gtk.CellRendererText()
218         column = gtk.TreeViewColumn("", renderer)
219         self.__tree.append_column(column)
220
221     def __shortcut_button_clicked_cb(self, button, name, entry):
222         buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)
223         title = _("Select keyboard shortcut for %s") %  _(name)
224         dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title)
225         text = entry.get_text()
226         if text:
227             shortcuts = text.split(';')
228         else:
229             shortcuts = None
230         dialog.set_shortcuts(shortcuts)
231         id = dialog.run()
232         shortcuts = list(set(dialog.get_shortcuts()))
233         dialog.destroy()
234         if id != gtk.RESPONSE_OK:
235             return
236         self.__bus.config_set_value(CONFIG_GENERAL_SHORTCUT % name, shortcuts)
237         entry.set_text(";".join(shortcuts))
238
239
240     def __item_started_column_toggled_cb(self, cell, path_str, model):
241
242         # get toggled iter
243         iter = model.get_iter_from_string(path_str)
244         data = model.get_value(iter, COLUMN_DATA)
245
246         # do something with the value
247         if data[DATA_STARTED] == False:
248             try:
249                 self.__bus.register_start_engine(data[DATA_LANG], data[DATA_NAME])
250             except Exception, e:
251                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
252                         buttons = gtk.BUTTONS_CLOSE,
253                         message_format = str(e))
254                 dlg.run()
255                 dlg.destroy()
256                 self.__flush_gtk_events()
257                 return
258         else:
259             try:
260                 self.__bus.register_stop_engine(data[DATA_LANG], data[DATA_NAME])
261             except Exception, e:
262                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
263                         buttons = gtk.BUTTONS_CLOSE,
264                         message_format = str(e))
265                 dlg.run()
266                 dlg.destroy()
267                 self.__flush_gtk_events()
268                 return
269         data[DATA_STARTED] = not data[DATA_STARTED]
270
271         # set new value
272         model.set(iter, COLUMN_ENABLE, data[DATA_STARTED])
273
274     def __item_preload_column_toggled_cb(self, cell, path_str, model):
275
276         # get toggled iter
277         iter = model.get_iter_from_string(path_str)
278         data = model.get_value(iter, COLUMN_DATA)
279
280         data[DATA_PRELOAD] = not data[DATA_PRELOAD]
281         engine = "%s:%s" % (data[DATA_LANG], data[DATA_NAME])
282
283         if data[DATA_PRELOAD]:
284             if engine not in self.__preload_engines:
285                 self.__preload_engines.add(engine)
286                 self.__bus.config_set_list(CONFIG_PRELOAD_ENGINES, list(self.__preload_engines), "s")
287         else:
288             if engine in self.__preload_engines:
289                 self.__preload_engines.remove(engine)
290                 self.__bus.config_set_list(CONFIG_PRELOAD_ENGINES, list(self.__preload_engines), "s")
291
292
293         # set new value
294         model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
295
296     def __load_icon(self, icon, icon_size):
297         pixbuf = None
298         try:
299             pixbuf = gdk.pixbuf_new_from_file(icon)
300             w, h = pixbuf.get_width(), pixbuf.get_height()
301             rate = max(w, h) / float(icon_size)
302             w = int(w / rate)
303             h = int(h / rate)
304             pixbuf = pixbuf.scale_simple(w, h, gdk.INTERP_BILINEAR)
305         except:
306             pass
307         if pixbuf == None:
308             try:
309                 theme = gtk.icon_theme_get_default()
310                 pixbuf = theme.load_icon(icon, icon_size, 0)
311             except:
312                 pass
313         return pixbuf
314
315     def __create_model(self):
316         # create tree store
317         model = gtk.TreeStore(
318             gobject.TYPE_STRING,
319             gobject.TYPE_BOOLEAN,
320             gobject.TYPE_BOOLEAN,
321             gobject.TYPE_BOOLEAN,
322             gdk.Pixbuf,
323             gobject.TYPE_PYOBJECT)
324
325         langs = dict()
326
327         self.__bus.register_reload_engines()
328         for name, local_name, lang, icon, author, credits, _exec, started in self.__bus.register_list_engines():
329             _lang = ibus.LANGUAGES.get(lang, "other")
330             _lang = _(_lang)
331             if _lang not in langs:
332                 langs[_lang] = list()
333             langs[_lang].append([name, local_name, lang, icon, author, credits, _exec, started])
334
335         keys = langs.keys()
336         keys.sort()
337
338         icon_size = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR)[0]
339         pixbuf_missing = self.__load_icon("engine-default", icon_size)
340         if pixbuf_missing == None:
341             pixbuf_missing = self.__load_icon("gtk-missing-image", icon_size)
342
343         for key in keys:
344             iter = model.append(None)
345             model.set(iter,
346                 COLUMN_NAME, key,
347                 COLUMN_ENABLE, False,
348                 COLUMN_PRELOAD, False,
349                 COLUMN_VISIBLE, False,
350                 COLUMN_ICON, None,
351                 COLUMN_DATA, None)
352             langs[key].sort()
353
354             for name, local_name, lang, icon, author, credits, _exec, started in langs[key]:
355                 child_iter = model.append(iter)
356                 is_preload = "%s:%s" % (lang, name) in self.__preload_engines
357
358                 pixbuf = self.__load_icon(icon, icon_size)
359                 if pixbuf == None:
360                     pixbuf = pixbuf_missing
361
362                 model.set(child_iter,
363                     COLUMN_NAME, local_name,
364                     COLUMN_ENABLE, started,
365                     COLUMN_PRELOAD, is_preload,
366                     COLUMN_VISIBLE, True,
367                     COLUMN_ICON, pixbuf,
368                     COLUMN_DATA,
369                     [name, local_name, lang, icon, author, credits, _exec, started, is_preload])
370
371         return model
372
373     def __is_auto_start(self):
374         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
375         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
376
377         if not path.exists(link_file):
378             return False
379         if not path.islink(link_file):
380             return False
381         if path.realpath(link_file) != ibus_desktop:
382             return False
383         return True
384
385     def __checkbutton_auto_start_toggled_cb(self, button):
386         auto_start_dir = path.join(BaseDirectory.xdg_config_home, "autostart")
387         if not path.isdir(auto_start_dir):
388             os.makedirs(auto_start_dir)
389
390         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
391         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
392         # unlink file
393         try:
394             os.unlink(link_file)
395         except:
396             pass
397         if self.__checkbutton_auto_start.get_active():
398             os.symlink(ibus_desktop, link_file)
399
400     def __combobox_lookup_table_orientation_changed_cb(self, combobox):
401         self.__bus.config_set_value(
402             CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION,
403             self.__combobox_lookup_table_orientation.get_active())
404
405     def __checkbutton_auto_hide_toggled_cb(self, button):
406         self.__bus.config_set_value(
407             CONFIG_PANEL_AUTO_HIDE,
408             self.__checkbutton_auto_hide.get_active())
409
410     def __checkbutton_custom_font_toggled_cb(self, button):
411         if self.__checkbutton_custom_font.get_active():
412             self.__fontbutton_custom_font.set_sensitive(True)
413             self.__bus.config_set_value(CONFIG_PANEL_USE_CUSTOM_FONT, True)
414         else:
415             self.__fontbutton_custom_font.set_sensitive(False)
416             self.__bus.config_set_value(CONFIG_PANEL_USE_CUSTOM_FONT, False)
417
418     def __fontbutton_custom_font_notify_cb(self, button, arg):
419         font_name = self.__fontbutton_custom_font.get_font_name()
420         font_name = unicode(font_name, "utf-8")
421         self.__bus.config_set_value(CONFIG_PANEL_CUSTOM_FONT, font_name)
422
423     def __config_value_changed_cb(self, bus, key, value):
424         if key == CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION:
425             item = self.__bus.config_get_value(CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION, 0)
426             if item != 0 and item != 1:
427                 item = 0
428             self.__combobox_lookup_table_orientation.set_active(item)
429
430     def __config_reloaded_cb(self, bus):
431         pass
432
433     def run(self):
434         return self.__dialog.run()
435
436 if __name__ == "__main__":
437     Setup().run()