Release 1.5.4
[platform/upstream/ibus.git] / ui / gtk2 / menu.py
1 # vim:set et sts=4 sw=4:
2 #
3 # ibus - The Input Bus
4 #
5 # Copyright(c) 2007-2010 Peng Huang <shawn.p.huang@gmail.com>
6 # Copyright(c) 2007-2010 Red Hat, Inc.
7 #
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.
12 #
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.
17 #
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
21 # USA
22
23 import gtk
24 import gobject
25 import ibus
26 import icon
27 from propitem import PropItem
28
29 class Menu(gtk.Menu, PropItem):
30     __gtype_name__ = "IBusMenu"
31     __gsignals__ = {
32     "property-activate" :(
33         gobject.SIGNAL_RUN_FIRST,
34         gobject.TYPE_NONE,
35        (gobject.TYPE_STRING, gobject.TYPE_INT)),
36     }
37
38     def __init__(self, prop):
39         gtk.Menu.__init__(self)
40         PropItem.__init__(self, prop)
41
42         self.set_take_focus(False)
43         self.__create_items(self._prop.sub_props)
44         self.show_all()
45         self.set_sensitive(prop.sensitive)
46
47     def __create_items(self, props):
48         radio_group = None
49
50         for prop in props:
51             if prop.type == ibus.PROP_TYPE_NORMAL:
52                 item = ImageMenuItem(prop)
53             elif prop.type == ibus.PROP_TYPE_TOGGLE:
54                 item = CheckMenuItem(prop)
55             elif prop.type == ibus.PROP_TYPE_RADIO:
56                 item = RadioMenuItem(radio_group, prop)
57                 radio_group = item
58             elif prop.type == ibus.PROP_TYPE_SEPARATOR:
59                 item = SeparatorMenuItem()
60                 radio_group = None
61             elif prop.type == ibus.PROP_TYPE_MENU:
62                 item = ImageMenuItem(prop)
63                 if prop.icon:
64                     size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
65                     item.set_image(icon.IconWidget(prop.icon, size[0]))
66                 if prop.label:
67                     item.set_label(prop.label.text)
68                 item.set_submenu(Menu(prop))
69             else:
70                 assert Fasle
71
72             if prop.tooltip:
73                 item.set_tooltip_text(prop.tooltip.text)
74             item.set_sensitive(prop.sensitive)
75             if prop.visible:
76                 item.set_no_show_all(False)
77                 item.show()
78             else:
79                 item.set_no_show_all(True)
80                 item.hide()
81
82             self.append(item)
83             self._sub_items.append(item)
84
85             if prop.type not in (ibus.PROP_TYPE_NORMAL, ibus.PROP_TYPE_TOGGLE, ibus.PROP_TYPE_RADIO):
86                 continue
87             item.connect("property-activate",
88                 lambda w,n,s: self.emit("property-activate", n, s))
89
90     def popup(self, button, active_time, widget):
91         gtk.Menu.popup(self, None, None, menu_position,
92                             button, active_time, widget)
93
94     def _property_clicked(self, item, prop):
95         pass
96
97
98 class ImageMenuItem(gtk.ImageMenuItem, PropItem):
99     __gtype_name__ = "IBusImageMenuItem"
100     __gsignals__ = {
101     "property-activate" :(
102         gobject.SIGNAL_RUN_FIRST,
103         gobject.TYPE_NONE,
104        (gobject.TYPE_STRING, gobject.TYPE_INT)),
105     }
106
107     def __init__(self, prop):
108         gtk.ImageMenuItem.__init__(self)
109         PropItem.__init__(self, prop)
110
111         if self._prop.icon:
112             size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
113             self.set_image(icon.IconWidget(prop.icon, size[0]))
114         if self._prop.label:
115             self.set_label(prop.label.text)
116
117         if self._prop.visible:
118             self.set_no_show_all(False)
119             self.show_all()
120         else:
121             self.set_no_show_all(True)
122             self.hide_all()
123
124     def do_activate(self):
125         self.emit("property-activate", self._prop.key, self._prop.state)
126
127     def property_changed(self):
128         self.set_sensitive(self._prop.sensitive)
129         if self._prop.visible:
130             self.set_no_show_all(False)
131             self.show_all()
132         else:
133             self.set_no_show_all(True)
134             self.hide_all()
135
136
137 class CheckMenuItem(gtk.CheckMenuItem, PropItem):
138     __gtype_name__ = "IBusCheckMenuItem"
139     __gsignals__ = {
140     "property-activate" :(
141         gobject.SIGNAL_RUN_FIRST,
142         gobject.TYPE_NONE,
143        (gobject.TYPE_STRING, gobject.TYPE_INT)),
144     }
145
146     def __init__(self, prop):
147         gtk.CheckMenuItem.__init__(self, label=prop.label.text)
148         PropItem.__init__(self, prop)
149
150         self.set_active(self._prop.state == ibus.PROP_STATE_CHECKED)
151
152         if self._prop.visible:
153             self.set_no_show_all(False)
154             self.show_all()
155         else:
156             self.set_no_show_all(True)
157             self.hide_all()
158
159     def do_toggled(self):
160         # Do not send property-activate to engine in case the event is
161         # sent from engine.
162         do_emit = False
163         if self.get_active():
164             if self._prop.state != ibus.PROP_STATE_CHECKED:
165                 do_emit = True
166             self._prop.state = ibus.PROP_STATE_CHECKED
167         else:
168             if self._prop.state != ibus.PROP_STATE_UNCHECKED:
169                 do_emit = True
170             self._prop.state = ibus.PROP_STATE_UNCHECKED
171         if do_emit:
172             self.emit("property-activate", self._prop.key, self._prop.state)
173
174     def property_changed(self):
175         self.set_active(self._prop.state == ibus.PROP_STATE_CHECKED)
176         self.set_sensitive(self._prop.sensitive)
177         if self._prop.visible:
178             self.set_no_show_all(False)
179             self.show_all()
180         else:
181             self.set_no_show_all(True)
182             self.hide_all()
183
184
185 class RadioMenuItem(gtk.RadioMenuItem, PropItem):
186     __gtype_name__ = "IBusRadioMenuItem"
187     __gsignals__ = {
188     "property-activate" :(
189         gobject.SIGNAL_RUN_FIRST,
190         gobject.TYPE_NONE,
191        (gobject.TYPE_STRING, gobject.TYPE_INT)),
192     }
193
194     def __init__(self, group, prop):
195         gtk.RadioMenuItem.__init__(self, group, label = prop.label.text)
196         PropItem.__init__(self, prop)
197
198         self.set_active(self._prop.state == ibus.PROP_STATE_CHECKED)
199
200         if prop.visible:
201             self.set_no_show_all(False)
202             self.show_all()
203         else:
204             self.set_no_show_all(True)
205             self.hide_all()
206
207     def property_changed(self):
208         self.set_active(self._prop.state == ibus.PROP_STATE_CHECKED)
209         self.set_sensitive(self._prop.sensitive)
210         if self._prop.visible:
211             self.set_no_show_all(False)
212             self.show_all()
213         else:
214             self.set_no_show_all(True)
215             self.hide_all()
216
217     def do_toggled(self):
218         # Do not send property-activate to engine in case the event is
219         # sent from engine.
220         do_emit = False
221         if self.get_active():
222             if self._prop.state != ibus.PROP_STATE_CHECKED:
223                 do_emit = True
224             self._prop.state = ibus.PROP_STATE_CHECKED
225         else:
226             if self._prop.state != ibus.PROP_STATE_UNCHECKED:
227                 do_emit = True
228             self._prop.state = ibus.PROP_STATE_UNCHECKED
229         if do_emit:
230             self.emit("property-activate", self._prop.key, self._prop.state)
231
232 class SeparatorMenuItem(gtk.SeparatorMenuItem, PropItem):
233     __gtype_name__ = "IBusSeparatorMenuItem"
234     __gsignals__ = {
235     "property-activate" :(
236         gobject.SIGNAL_RUN_FIRST,
237         gobject.TYPE_NONE,
238        (gobject.TYPE_STRING, gobject.TYPE_INT)),
239     }
240
241     def __init__(self):
242         gtk.SeparatorMenuItem.__init__(self)
243         PropItem.__init__(self, None)
244
245
246 def menu_position(menu, button):
247     screen = button.get_screen()
248     monitor = screen.get_monitor_at_window(button.window)
249     monitor_allocation = screen.get_monitor_geometry(monitor)
250
251     x, y = button.window.get_origin()
252     x += button.allocation.x
253     y += button.allocation.y
254
255     menu_width, menu_height = menu.size_request()
256
257     if x + menu_width >= monitor_allocation.width:
258         x -= menu_width - button.allocation.width
259     elif x - menu_width <= 0:
260         pass
261     else:
262         if x <= monitor_allocation.width * 3 / 4:
263             pass
264         else:
265             x -= menu_width - button.allocation.width
266
267     if y + button.allocation.height + menu_height >= monitor_allocation.height:
268         y -= menu_height
269     elif y - menu_height <= 0:
270         y += button.allocation.height
271     else:
272         if y <= monitor_allocation.height * 3 / 4:
273             y += button.allocation.height
274         else:
275             y -= menu_height
276
277     return (x, y, False)
278