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