internationalize engine's name
[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
62 class Setup(object):
63     def __flush_gtk_events(self):
64         while gtk.events_pending():
65             gtk.main_iteration()
66
67     def __init__(self):
68         super(Setup, self).__init__()
69         glade.textdomain("ibus")
70         glade_file = path.join(path.dirname(__file__), "./setup.glade")
71         self.__xml = glade.XML(glade_file)
72         self.__bus = None
73         self.__init_bus()
74         self.__init_ui()
75
76     def __init_ui(self):
77         # add icon search path
78         icon_theme = gtk.icon_theme_get_default()
79         dir = path.dirname(__file__)
80         icondir = path.join(dir, "..", "icons")
81         icon_theme.prepend_search_path(icondir)
82
83
84         self.__dialog = self.__xml.get_widget("dialog_setup")
85
86         # auto start ibus
87         self.__checkbutton_auto_start = self.__xml.get_widget("checkbutton_auto_start")
88         self.__checkbutton_auto_start.set_active(self.__is_auto_start())
89         self.__checkbutton_auto_start.connect("toggled", self.__checkbutton_auto_start_toggled_cb)
90
91         # keyboard shortcut
92         for name in (N_("trigger"), N_("next_engine"), N_("prev_engine")):
93             shortcuts = self.__bus.config_get_value(CONFIG_GENERAL_SHORTCUT % name, [])
94             button = self.__xml.get_widget("button_%s" % name)
95             entry = self.__xml.get_widget("entry_%s" % name)
96             entry.set_text(";".join(shortcuts))
97             button.connect("clicked", self.__shortcut_button_clicked_cb, name, entry)
98
99         # lookup table orientation
100         self.__combobox_lookup_table_orientation = self.__xml.get_widget("combobox_lookup_table_orientation")
101         self.__combobox_lookup_table_orientation.set_active(
102             self.__bus.config_get_value(CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION, 0))
103         self.__combobox_lookup_table_orientation.connect("changed",
104             self.__combobox_lookup_table_orientation_changed_cb)
105
106         self.__init_engine_view()
107
108     def __init_bus(self):
109         try:
110             self.__bus = ibus.Bus()
111             self.__bus.connect("config-value-changed", self.__config_value_changed_cb)
112             self.__bus.connect("config-reloaded", self.__config_reloaded_cb)
113             self.__bus.config_add_watch("/general")
114             self.__bus.config_add_watch("/panel")
115         except:
116             while self.__bus == None:
117                 message = _("IBus daemon is not started. Do you want to start it now?")
118                 print >> sys.stderr, message
119                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION,
120                         buttons = gtk.BUTTONS_YES_NO,
121                         message_format = message)
122                 id = dlg.run()
123                 dlg.destroy()
124                 self.__flush_gtk_events()
125                 if id != gtk.RESPONSE_YES:
126                     sys.exit(0)
127                 pid = os.spawnlp(os.P_NOWAIT, "ibus", "ibus")
128                 time.sleep(1)
129                 try:
130                     self.__bus = ibus.Bus()
131                 except:
132                     continue
133                 message = _("IBus has been started! " + \
134                     "If you can not use IBus, please add below lines in $HOME/.bashrc, and relogin your desktop.\n" + \
135                     "  export GTK_IM_MODULE=ibus\n" + \
136                     "  export XMODIFIERS=@im=ibus\n"
137                     "  export QT_IM_MODULE=ibus"
138                     )
139                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
140                                         buttons = gtk.BUTTONS_OK,
141                                         message_format = message)
142                 id = dlg.run()
143                 dlg.destroy()
144                 self.__flush_gtk_events()
145
146     def __init_engine_view(self):
147         # engines tree
148         self.__tree = self.__xml.get_widget("treeview_engines")
149         self.__preload_engines = set(self.__bus.config_get_value(CONFIG_PRELOAD_ENGINES, []))
150         model = self.__create_model()
151         self.__tree.set_model(model)
152
153         # column for engine
154         column = gtk.TreeViewColumn()
155         column.set_title(_("Engine"))
156
157         renderer = gtk.CellRendererPixbuf()
158         renderer.set_property("xalign", 0.5)
159
160         column.pack_start(renderer)
161         column.set_attributes(renderer, pixbuf = COLUMN_ICON, visible = COLUMN_VISIBLE)
162
163         renderer = gtk.CellRendererText()
164         renderer.set_property("xalign", 0.0)
165
166         # column.set_clickable(True)
167         column.pack_start(renderer)
168         column.set_attributes(renderer, text = COLUMN_NAME)
169
170         self.__tree.append_column(column)
171
172         # column for started
173         renderer = gtk.CellRendererToggle()
174         renderer.set_data('column', COLUMN_ENABLE)
175         renderer.set_property("xalign", 0.5)
176         renderer.connect("toggled", self.__item_started_column_toggled_cb, model)
177
178         #col_offset = gtk.TreeViewColumn("Holiday", renderer, text=HOLIDAY_NAME)
179         column = gtk.TreeViewColumn(_("Started"), renderer, active = COLUMN_ENABLE, visible = COLUMN_VISIBLE)
180         self.__tree.append_column(column)
181
182         # column for preload
183         renderer = gtk.CellRendererToggle()
184         renderer.set_data('column', COLUMN_PRELOAD)
185         renderer.set_property("xalign", 0.5)
186         renderer.connect("toggled", self.__item_preload_column_toggled_cb, model)
187
188         column = gtk.TreeViewColumn(_("Preload"), renderer, active = COLUMN_PRELOAD, visible = COLUMN_VISIBLE)
189         self.__tree.append_column(column)
190
191         renderer = gtk.CellRendererText()
192         column = gtk.TreeViewColumn("", renderer)
193         self.__tree.append_column(column)
194
195     def __shortcut_button_clicked_cb(self, button, name, entry):
196         buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)
197         title = _("Select keyboard shortcut for %s") %  _(name)
198         dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title)
199         text = entry.get_text()
200         if text:
201             shortcuts = text.split(';')
202         else:
203             shortcuts = None
204         dialog.set_shortcuts(shortcuts)
205         id = dialog.run()
206         shortcuts = list(set(dialog.get_shortcuts()))
207         dialog.destroy()
208         if id != gtk.RESPONSE_OK:
209             return
210         self.__bus.config_set_value(CONFIG_GENERAL_SHORTCUT % name, shortcuts)
211         entry.set_text(";".join(shortcuts))
212
213
214     def __item_started_column_toggled_cb(self, cell, path_str, model):
215
216         # get toggled iter
217         iter = model.get_iter_from_string(path_str)
218         data = model.get_value(iter, COLUMN_DATA)
219
220         # do something with the value
221         if data[DATA_STARTED] == False:
222             try:
223                 self.__bus.register_start_engine(data[DATA_LANG], data[DATA_NAME])
224             except Exception, e:
225                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
226                         buttons = gtk.BUTTONS_CLOSE,
227                         message_format = str(e))
228                 dlg.run()
229                 dlg.destroy()
230                 self.__flush_gtk_events()
231                 return
232         else:
233             try:
234                 self.__bus.register_stop_engine(data[DATA_LANG], data[DATA_NAME])
235             except Exception, e:
236                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
237                         buttons = gtk.BUTTONS_CLOSE,
238                         message_format = str(e))
239                 dlg.run()
240                 dlg.destroy()
241                 self.__flush_gtk_events()
242                 return
243         data[DATA_STARTED] = not data[DATA_STARTED]
244
245         # set new value
246         model.set(iter, COLUMN_ENABLE, data[DATA_STARTED])
247
248     def __item_preload_column_toggled_cb(self, cell, path_str, model):
249
250         # get toggled iter
251         iter = model.get_iter_from_string(path_str)
252         data = model.get_value(iter, COLUMN_DATA)
253
254         data[DATA_PRELOAD] = not data[DATA_PRELOAD]
255         engine = "%s:%s" % (data[DATA_LANG], data[DATA_NAME])
256
257         if data[DATA_PRELOAD]:
258             if engine not in self.__preload_engines:
259                 self.__preload_engines.add(engine)
260                 self.__bus.config_set_list(CONFIG_PRELOAD_ENGINES, list(self.__preload_engines), "s")
261         else:
262             if engine in self.__preload_engines:
263                 self.__preload_engines.remove(engine)
264                 self.__bus.config_set_list(CONFIG_PRELOAD_ENGINES, list(self.__preload_engines), "s")
265
266
267         # set new value
268         model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
269
270     def __load_icon(self, icon, icon_size):
271         pixbuf = None
272         try:
273             pixbuf = gdk.pixbuf_new_from_file(icon)
274             w, h = pixbuf.get_width(), pixbuf.get_height()
275             rate = max(w, h) / float(icon_size)
276             w = int(w / rate)
277             h = int(h / rate)
278             pixbuf = pixbuf.scale_simple(w, h, gdk.INTERP_BILINEAR)
279         except:
280             pass
281         if pixbuf == None:
282             try:
283                 theme = gtk.icon_theme_get_default()
284                 pixbuf = theme.load_icon(icon, icon_size, 0)
285             except:
286                 pass
287         return pixbuf
288
289     def __create_model(self):
290         # create tree store
291         model = gtk.TreeStore(
292             gobject.TYPE_STRING,
293             gobject.TYPE_BOOLEAN,
294             gobject.TYPE_BOOLEAN,
295             gobject.TYPE_BOOLEAN,
296             gdk.Pixbuf,
297             gobject.TYPE_PYOBJECT)
298
299         langs = dict()
300
301         for name, local_name, lang, icon, author, credits, _exec, started in self.__bus.register_list_engines():
302             _lang = ibus.LANGUAGES.get(lang, "other")
303             _lang = _(_lang)
304             if _lang not in langs:
305                 langs[_lang] = list()
306             langs[_lang].append([name, local_name, lang, icon, author, credits, _exec, started])
307
308         keys = langs.keys()
309         keys.sort()
310
311         icon_size = gtk.icon_size_lookup(gtk.ICON_SIZE_LARGE_TOOLBAR)[0]
312         pixbuf_missing = self.__load_icon("engine-default", icon_size)
313         if pixbuf_missing == None:
314             pixbuf_missing = self.__load_icon("gtk-missing-image", icon_size)
315
316         for key in keys:
317             iter = model.append(None)
318             model.set(iter,
319                 COLUMN_NAME, key,
320                 COLUMN_ENABLE, False,
321                 COLUMN_PRELOAD, False,
322                 COLUMN_VISIBLE, False,
323                 COLUMN_ICON, None,
324                 COLUMN_DATA, None)
325             langs[key].sort()
326
327             for name, local_name, lang, icon, author, credits, _exec, started in langs[key]:
328                 child_iter = model.append(iter)
329                 is_preload = "%s:%s" % (lang, name) in self.__preload_engines
330
331                 pixbuf = self.__load_icon(icon, icon_size)
332                 if pixbuf == None:
333                     pixbuf = pixbuf_missing
334
335                 model.set(child_iter,
336                     COLUMN_NAME, local_name,
337                     COLUMN_ENABLE, started,
338                     COLUMN_PRELOAD, is_preload,
339                     COLUMN_VISIBLE, True,
340                     COLUMN_ICON, pixbuf,
341                     COLUMN_DATA,
342                     [name, local_name, lang, icon, author, credits, _exec, started, is_preload])
343
344         return model
345
346     def __is_auto_start(self):
347         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
348         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
349
350         if not path.exists(link_file):
351             return False
352         if not path.islink(link_file):
353             return False
354         if path.realpath(link_file) != ibus_desktop:
355             return False
356         return True
357
358     def __checkbutton_auto_start_toggled_cb(self, button):
359         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
360         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
361         # unlink file
362         try:
363             os.unlink(link_file)
364         except:
365             pass
366         if self.__checkbutton_auto_start.get_active():
367             os.symlink(ibus_desktop, link_file)
368
369     def __combobox_lookup_table_orientation_changed_cb(self, combobox):
370         self.__bus.config_set_value(
371             CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION,
372             self.__combobox_lookup_table_orientation.get_active())
373
374     def __config_value_changed_cb(self, bus, key, value):
375         if key == CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION:
376             item = self.__bus.config_get_value(CONFIG_PANEL_LOOKUP_TABLE_ORIENTATION, 0)
377             if item != 0 and item != 1:
378                 item = 0
379             self.__combobox_lookup_table_orientation.set_active(item)
380
381     def __config_reloaded_cb(self, bus):
382         pass
383
384     def run(self):
385         return self.__dialog.run()
386
387 if __name__ == "__main__":
388     Setup().run()