re-implement ibus in c language.
[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 locale
23 import gettext
24 import os
25 import sys
26 import time
27 import gtk
28 import gobject
29 import pango
30 import ibus
31 import keyboardshortcut
32 from os import path
33 from xdg import BaseDirectory
34 from gtk import gdk
35 from gtk import glade
36 from enginecombobox import EngineComboBox
37 from enginetreeview import EngineTreeView
38 from icon import load_icon
39
40 _  = lambda a : gettext.dgettext("ibus", a)
41 N_ = lambda a : a
42
43 (
44     COLUMN_NAME,
45     COLUMN_ENABLE,
46     COLUMN_PRELOAD,
47     COLUMN_VISIBLE,
48     COLUMN_ICON,
49     COLUMN_DATA,
50 ) = range(6)
51
52 (
53     DATA_NAME,
54     DATA_LOCAL_NAME,
55     DATA_LANG,
56     DATA_ICON,
57     DATA_AUTHOR,
58     DATA_CREDITS,
59     DATA_EXEC,
60     DATA_STARTED,
61     DATA_PRELOAD
62 ) = range(9)
63
64 class Setup(object):
65     def __flush_gtk_events(self):
66         while gtk.events_pending():
67             gtk.main_iteration()
68
69     def __init__(self):
70         super(Setup, self).__init__()
71         locale.bind_textdomain_codeset("ibus", "UTF-8")
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         self.__dialog = self.__xml.get_widget("dialog_setup")
82
83         # auto start ibus
84         self.__checkbutton_auto_start = self.__xml.get_widget("checkbutton_auto_start")
85         self.__checkbutton_auto_start.set_active(self.__is_auto_start())
86         self.__checkbutton_auto_start.connect("toggled", self.__checkbutton_auto_start_toggled_cb)
87
88         # keyboard shortcut
89         # trigger
90         self.__config = self.__bus.get_config()
91         shortcuts = self.__config.get_value(
92                         "general/hotkey", "trigger",
93                         ibus.CONFIG_GENERAL_SHORTCUT_TRIGGER_DEFAULT)
94
95         button = self.__xml.get_widget("button_trigger")
96         entry = self.__xml.get_widget("entry_trigger")
97         entry.set_text("; ".join(shortcuts))
98         button.connect("clicked", self.__shortcut_button_clicked_cb,
99                     N_("trigger"), "general/hotkey", "trigger", entry)
100
101         # next engine
102         shortcuts = self.__config.get_value(
103                         "general/hotkey", "next_engine",
104                         ibus.CONFIG_GENERAL_SHORTCUT_NEXT_ENGINE_DEFAULT)
105         button = self.__xml.get_widget("button_next_engine")
106         entry = self.__xml.get_widget("entry_next_engine")
107         entry.set_text("; ".join(shortcuts))
108         button.connect("clicked", self.__shortcut_button_clicked_cb,
109                     N_("next engine"), "general/hotkey", "next_engine", entry)
110
111         # prev engine
112         shortcuts = self.__config.get_value(
113                         "general/hotkey", "prev_engine",
114                         ibus.CONFIG_GENERAL_SHORTCUT_PREV_ENGINE_DEFAULT)
115         button = self.__xml.get_widget("button_prev_engine")
116         entry = self.__xml.get_widget("entry_prev_engine")
117         entry.set_text("; ".join(shortcuts))
118         button.connect("clicked", self.__shortcut_button_clicked_cb,
119                     N_("prev engine"), "general/hotkey", "prev_engine", entry)
120
121         # lookup table orientation
122         self.__combobox_lookup_table_orientation = self.__xml.get_widget("combobox_lookup_table_orientation")
123         self.__combobox_lookup_table_orientation.set_active(
124             self.__config.get_value("panel", "lookup_table_orientation", 0))
125         self.__combobox_lookup_table_orientation.connect("changed",
126             self.__combobox_lookup_table_orientation_changed_cb)
127
128         # auto hide
129         self.__checkbutton_auto_hide = self.__xml.get_widget("checkbutton_auto_hide")
130         self.__checkbutton_auto_hide.set_active(
131             self.__config.get_value("panel", "auto_hide", False))
132         self.__checkbutton_auto_hide.connect("toggled", self.__checkbutton_auto_hide_toggled_cb)
133
134         # custom font
135         self.__checkbutton_custom_font = self.__xml.get_widget("checkbutton_custom_font")
136         self.__checkbutton_custom_font.set_active(
137             self.__config.get_value("panel", "use_custom_font", False))
138         self.__checkbutton_custom_font.connect("toggled", self.__checkbutton_custom_font_toggled_cb)
139
140         self.__fontbutton_custom_font = self.__xml.get_widget("fontbutton_custom_font")
141         if self.__config.get_value("panel", "use_custom_font", False):
142             self.__fontbutton_custom_font.set_sensitive(True)
143         else:
144             self.__fontbutton_custom_font.set_sensitive(False)
145         font_name = gtk.settings_get_default().get_property("gtk-font-name")
146         font_name = unicode(font_name, "utf-8")
147         font_name = self.__config.get_value("panel", "custom_font", font_name)
148         self.__fontbutton_custom_font.connect("notify::font-name", self.__fontbutton_custom_font_notify_cb)
149         self.__fontbutton_custom_font.set_font_name(font_name)
150
151         # init engine page
152         self.__engines = self.__bus.list_engines()
153         self.__combobox = EngineComboBox(self.__engines)
154         self.__combobox.show()
155         self.__xml.get_widget("alignment_engine_combobox").add(self.__combobox)
156
157         tmp_dict = {}
158         for e in self.__engines:
159             tmp_dict[e.name] = e
160         engine_names = self.__config.get_value("general", "preload_engines", [])
161         engines = []
162         for n in engine_names:
163             if n in tmp_dict:
164                 engines.append(tmp_dict[n])
165         self.__treeview = EngineTreeView(engines)
166         self.__treeview.show()
167         self.__xml.get_widget("scrolledwindow_engine_treeview").add(self.__treeview)
168
169         self.__treeview.connect("changed", self.__treeview_changed_cb)
170
171         button = self.__xml.get_widget("button_engine_add")
172         button.connect("clicked",
173                        lambda *args:self.__treeview.prepend_engine(self.__combobox.get_active_engine()))
174         button = self.__xml.get_widget("button_engine_remove")
175         button.connect("clicked", lambda *args:self.__treeview.remove_engine())
176         button = self.__xml.get_widget("button_engine_up")
177         button.connect("clicked", lambda *args:self.__treeview.move_up_engine())
178
179         button = self.__xml.get_widget("button_engine_down")
180         button.connect("clicked", lambda *args:self.__treeview.move_down_engine())
181
182     def __treeview_changed_cb(self, treeview):
183         engines = self.__treeview.get_engines()
184         engine_names = map(lambda e: e.name, engines)
185         self.__config.set_list("general", "preload_engines", engine_names, "s")
186
187     def __init_bus(self):
188         try:
189             self.__bus = ibus.Bus()
190             # self.__bus.connect("config-value-changed", self.__config_value_changed_cb)
191             # self.__bus.connect("config-reloaded", self.__config_reloaded_cb)
192             # self.__bus.config_add_watch("/general")
193             # self.__bus.config_add_watch("/general/hotkey")
194             # self.__bus.config_add_watch("/panel")
195         except:
196             while self.__bus == None:
197                 message = _("IBus daemon is not started. Do you want to start it now?")
198                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_QUESTION,
199                         buttons = gtk.BUTTONS_YES_NO,
200                         message_format = message)
201                 id = dlg.run()
202                 dlg.destroy()
203                 self.__flush_gtk_events()
204                 if id != gtk.RESPONSE_YES:
205                     sys.exit(0)
206                 pid = os.spawnlp(os.P_NOWAIT, "ibus", "ibus")
207                 time.sleep(1)
208                 try:
209                     self.__bus = ibus.Bus()
210                 except:
211                     continue
212                 message = _("IBus has been started! "
213                     "If you can not use IBus, please add below lines in $HOME/.bashrc, and relogin your desktop.\n"
214                     "  export GTK_IM_MODULE=ibus\n"
215                     "  export XMODIFIERS=@im=ibus\n"
216                     "  export QT_IM_MODULE=ibus"
217                     )
218                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_INFO,
219                                         buttons = gtk.BUTTONS_OK,
220                                         message_format = message)
221                 id = dlg.run()
222                 dlg.destroy()
223                 self.__flush_gtk_events()
224
225     def __shortcut_button_clicked_cb(self, button, name, section, _name, entry):
226         buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)
227         title = _("Select keyboard shortcut for %s") %  _(name)
228         dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title)
229         text = entry.get_text()
230         if text:
231             shortcuts = text.split("; ")
232         else:
233             shortcuts = None
234         dialog.set_shortcuts(shortcuts)
235         id = dialog.run()
236         shortcuts = list(set(dialog.get_shortcuts()))
237         dialog.destroy()
238         if id != gtk.RESPONSE_OK:
239             return
240         self.__config.set_value(section, _name, shortcuts)
241         entry.set_text("; ".join(shortcuts))
242
243
244     def __item_started_column_toggled_cb(self, cell, path_str, model):
245
246         # get toggled iter
247         iter = model.get_iter_from_string(path_str)
248         data = model.get_value(iter, COLUMN_DATA)
249
250         # do something with the value
251         if data[DATA_STARTED] == False:
252             try:
253                 self.__bus.register_start_engine(data[DATA_LANG], data[DATA_NAME])
254             except Exception, e:
255                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
256                         buttons = gtk.BUTTONS_CLOSE,
257                         message_format = str(e))
258                 dlg.run()
259                 dlg.destroy()
260                 self.__flush_gtk_events()
261                 return
262         else:
263             try:
264                 self.__bus.register_stop_engine(data[DATA_LANG], data[DATA_NAME])
265             except Exception, e:
266                 dlg = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
267                         buttons = gtk.BUTTONS_CLOSE,
268                         message_format = str(e))
269                 dlg.run()
270                 dlg.destroy()
271                 self.__flush_gtk_events()
272                 return
273         data[DATA_STARTED] = not data[DATA_STARTED]
274
275         # set new value
276         model.set(iter, COLUMN_ENABLE, data[DATA_STARTED])
277
278     def __item_preload_column_toggled_cb(self, cell, path_str, model):
279
280         # get toggled iter
281         iter = model.get_iter_from_string(path_str)
282         data = model.get_value(iter, COLUMN_DATA)
283
284         data[DATA_PRELOAD] = not data[DATA_PRELOAD]
285         engine = "%s:%s" % (data[DATA_LANG], data[DATA_NAME])
286
287         if data[DATA_PRELOAD]:
288             if engine not in self.__preload_engines:
289                 self.__preload_engines.add(engine)
290                 self.__config.set_list("general", "preload_engines", list(self.__preload_engines), "s")
291         else:
292             if engine in self.__preload_engines:
293                 self.__preload_engines.remove(engine)
294                 self.__config.set_list("general", "preload_engines", list(self.__preload_engines), "s")
295
296         # set new value
297         model.set(iter, COLUMN_PRELOAD, data[DATA_PRELOAD])
298
299     def __is_auto_start(self):
300         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
301         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
302
303         if not path.exists(link_file):
304             return False
305         if not path.islink(link_file):
306             return False
307         if path.realpath(link_file) != ibus_desktop:
308             return False
309         return True
310
311     def __checkbutton_auto_start_toggled_cb(self, button):
312         auto_start_dir = path.join(BaseDirectory.xdg_config_home, "autostart")
313         if not path.isdir(auto_start_dir):
314             os.makedirs(auto_start_dir)
315
316         link_file = path.join(BaseDirectory.xdg_config_home, "autostart/ibus.desktop")
317         ibus_desktop = path.join(os.getenv("IBUS_PREFIX"), "share/applications/ibus.desktop")
318         # unlink file
319         try:
320             os.unlink(link_file)
321         except:
322             pass
323         if self.__checkbutton_auto_start.get_active():
324             os.symlink(ibus_desktop, link_file)
325
326     def __combobox_lookup_table_orientation_changed_cb(self, combobox):
327         self.__config.set_value(
328             "panel", "lookup_table_orientation",
329             self.__combobox_lookup_table_orientation.get_active())
330
331     def __checkbutton_auto_hide_toggled_cb(self, button):
332         self.__config.set_value(
333             "panel", "auto_hide",
334             self.__checkbutton_auto_hide.get_active())
335
336     def __checkbutton_custom_font_toggled_cb(self, button):
337         if self.__checkbutton_custom_font.get_active():
338             self.__fontbutton_custom_font.set_sensitive(True)
339             self.__config.set_value("panel", "use_custom_font", True)
340         else:
341             self.__fontbutton_custom_font.set_sensitive(False)
342             self.__config.set_value("panel", "use_custom_font", False)
343
344     def __fontbutton_custom_font_notify_cb(self, button, arg):
345         font_name = self.__fontbutton_custom_font.get_font_name()
346         font_name = unicode(font_name, "utf-8")
347         self.__config.set_value("panel", "custom_font", font_name)
348
349     def __config_value_changed_cb(self, bus, section, name, value):
350         pass
351
352     def __config_reloaded_cb(self, bus):
353         pass
354
355     def run(self):
356         return self.__dialog.run()
357
358 if __name__ == "__main__":
359     Setup().run()