summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorHuang Peng <shawn.p.huang@gmail.com>2008-10-05 22:16:30 +0800
committerHuang Peng <shawn.p.huang@gmail.com>2008-10-05 22:16:30 +0800
commit3d36b0eb76eafcda76082baecf34a48d6cbb0319 (patch)
treeeef6a7f236cc373ac99d7aae29aa5affa472e920 /ui
parent249722e4aa36837b6ff5fd1436387bc66b482228 (diff)
downloadibus-3d36b0eb76eafcda76082baecf34a48d6cbb0319.tar.gz
ibus-3d36b0eb76eafcda76082baecf34a48d6cbb0319.tar.xz
ibus-3d36b0eb76eafcda76082baecf34a48d6cbb0319.zip
Move panel into ui folder, and rename panel to gtk.
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.am24
-rw-r--r--ui/gtk/.gitignore1
-rw-r--r--ui/gtk/Makefile.am49
-rw-r--r--ui/gtk/candidatepanel.py484
-rw-r--r--ui/gtk/handle.py109
-rw-r--r--ui/gtk/ibus-ui-gtk.in23
-rw-r--r--ui/gtk/icon.py51
-rw-r--r--ui/gtk/languagebar.py197
-rw-r--r--ui/gtk/main.py80
-rw-r--r--ui/gtk/menu.py246
-rw-r--r--ui/gtk/panel.py371
-rw-r--r--ui/gtk/propitem.py62
-rw-r--r--ui/gtk/toolitem.py172
13 files changed, 1869 insertions, 0 deletions
diff --git a/ui/Makefile.am b/ui/Makefile.am
new file mode 100644
index 0000000..851c80e
--- /dev/null
+++ b/ui/Makefile.am
@@ -0,0 +1,24 @@
+# vim:set noet ts=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+SUBDIRS = \
+ gtk \
+ $(NULL)
diff --git a/ui/gtk/.gitignore b/ui/gtk/.gitignore
new file mode 100644
index 0000000..a278abf
--- /dev/null
+++ b/ui/gtk/.gitignore
@@ -0,0 +1 @@
+ibus-panel
diff --git a/ui/gtk/Makefile.am b/ui/gtk/Makefile.am
new file mode 100644
index 0000000..610b6a5
--- /dev/null
+++ b/ui/gtk/Makefile.am
@@ -0,0 +1,49 @@
+# vim:set noet ts=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+ui_gtk_PYTHON = \
+ candidatepanel.py \
+ handle.py \
+ icon.py \
+ languagebar.py \
+ main.py \
+ menu.py \
+ panel.py \
+ propitem.py \
+ toolitem.py \
+ $(NULL)
+
+ui_gtkdir = $(pkgdatadir)/ui/gtk
+
+bin_SCRIPTS = ibus-ui-gtk
+
+CLEANFILES = \
+ *.pyc \
+ $(NULL)
+
+EXTRA_DIST = \
+ ibus-ui-gtk.in \
+ $(NULL)
+
+test:
+ $(ENV) DBUS_DEBUG=true \
+ PYTHONPATH=$(top_srcdir) \
+ $(PYTHON) $(srcdir)/main.py
diff --git a/ui/gtk/candidatepanel.py b/ui/gtk/candidatepanel.py
new file mode 100644
index 0000000..528c7e7
--- /dev/null
+++ b/ui/gtk/candidatepanel.py
@@ -0,0 +1,484 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or(at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import pango
+import ibus
+from ibus.gtk import PangoAttrList
+
+class Label(gtk.Label): pass
+gobject.type_register(Label, "IBusPanelLabel")
+
+class HSeparator(gtk.HBox):
+ def __init__ (self):
+ gtk.HBox.__init__ (self)
+ self.pack_start(gtk.HSeparator(), True, True, 4)
+
+class VSeparator(gtk.VBox):
+ def __init__ (self):
+ gtk.VBox.__init__ (self)
+ self.pack_start(gtk.VSeparator(), True, True, 4)
+
+class CandidateArea(gtk.HBox):
+ def __init__ (self, orientation):
+ gtk.HBox.__init__ (self)
+ self.__orientation = orientation
+ self.__labels = []
+ self.__create_ui()
+
+ def __create_ui(self):
+ if self.__orientation == gtk.ORIENTATION_VERTICAL:
+ self.__vbox1 = gtk.VBox()
+ self.__vbox1.set_homogeneous(True)
+ self.__vbox2 = gtk.VBox()
+ self.__vbox2.set_homogeneous(True)
+ self.pack_start(self.__vbox1, False, False, 4)
+ self.pack_start(VSeparator(), False, False, 0)
+ self.pack_start(self.__vbox2, True, True, 4)
+
+ for i in xrange(1, 11):
+ label1 = Label("%d." % (i % 10))
+ label1.set_alignment(0.0, 0.5)
+ label1.set_no_show_all(True)
+
+ label2 = Label()
+ label2.set_alignment(0.0, 0.5)
+ label2.set_no_show_all(True)
+
+ if self.__orientation == gtk.ORIENTATION_VERTICAL:
+ label1.set_property("xpad", 8)
+ label2.set_property("xpad", 8)
+ self.__vbox1.pack_start(label1, False, False, 2)
+ self.__vbox2.pack_start(label2, False, False, 2)
+ else:
+ hbox = gtk.HBox()
+ hbox.pack_start(label1, False, False, 1)
+ hbox.pack_start(label2, False, False, 1)
+ self.pack_start(hbox, False, False, 4)
+
+ self.__labels.append((label1, label2))
+
+ self.__labels[0][0].show()
+ self.__labels[0][1].show()
+
+ def set_candidates(self, candidates, focus_candidate = 0, show_cursor = True):
+ assert len(candidates) <= len(self.__labels)
+ i = 0
+ for text, attrs in candidates:
+ if i == focus_candidate and show_cursor:
+ if attrs == None:
+ attrs = pango.AttrList()
+ color = self.__labels[i][1].style.base[gtk.STATE_SELECTED]
+ end_index = len(text.encode("utf8"))
+ attr = pango.AttrBackground(color.red, color.green, color.blue, 0, end_index)
+ attrs.change(attr)
+ color = self.__labels[i][1].style.text[gtk.STATE_SELECTED]
+ attr = pango.AttrForeground(color.red, color.green, color.blue, 0, end_index)
+ attrs.insert(attr)
+
+ self.__labels[i][1].set_text(text)
+ self.__labels[i][1].set_property("attributes", attrs)
+ self.__labels[i][0].show()
+ self.__labels[i][1].show()
+
+ i += 1
+
+ for label1, label2 in self.__labels[max(1, len(candidates)):]:
+ label1.hide()
+ label2.hide()
+
+ if len(candidates) == 0:
+ self.__labels[0][0].set_text("")
+ self.__labels[0][1].set_text("")
+ else:
+ self.__labels[0][0].set_text("1.")
+
+class CandidatePanel(gtk.VBox):
+ __gproperties__ = {
+ 'orientation' : (gtk.Orientation, # type
+ 'orientation of candidates', # nick name
+ 'the orientation of candidates list', # description
+ 0,
+ gobject.PARAM_READWRITE) # flags
+ }
+
+ __gsignals__ = {
+ "cursor-up" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ()),
+ "cursor-down" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ()),
+ }
+
+ def __init__ (self):
+ gtk.VBox.__init__ (self)
+ self.__tooltips = gtk.Tooltips()
+
+ self.__toplevel = gtk.Window(gtk.WINDOW_POPUP)
+ self.__viewport = gtk.Viewport()
+ self.__viewport.set_shadow_type(gtk.SHADOW_IN)
+ self.__toplevel.add(self.__viewport)
+ self.__viewport.add(self)
+ self.__toplevel.add_events(
+ gdk.BUTTON_PRESS_MASK | \
+ gdk.BUTTON_RELEASE_MASK | \
+ gdk.BUTTON1_MOTION_MASK)
+ self.__begin_move = False
+ # self.__toplevel.connect("button-press-event", self.__button_press_event_cb)
+ # self.__toplevel.connect("button-release-event", self.__button_release_event_cb)
+ # self.__toplevel.connect("motion-notify-event", self.__motion_notify_event_cb)
+ self.__toplevel.connect("size-allocate", lambda w, a: self.__check_position())
+
+ self.__orientation = gtk.ORIENTATION_HORIZONTAL
+ self.__orientation = gtk.ORIENTATION_VERTICAL
+ self.__preedit_visible = False
+ self.__aux_string_visible = False
+ self.__lookup_table_visible = False
+ self.__preedit_string = ""
+ self.__preedit_attrs = pango.AttrList()
+ self.__aux_string = ""
+ self.__aux_attrs = pango.AttrList()
+ self.__lookup_table = None
+
+ self.__cursor_location = (0, 0)
+
+ self.__recreate_ui()
+
+ def __recreate_ui(self):
+ for w in self:
+ self.remove(w)
+ w.destroy()
+ # create preedit label
+ self.__preedit_label = Label(self.__preedit_string)
+ self.__preedit_label.set_attributes(self.__preedit_attrs)
+ self.__preedit_label.set_alignment(0.0, 0.5)
+ self.__preedit_label.set_padding(8, 0)
+ self.__preedit_label.set_no_show_all(True)
+ if self.__preedit_visible:
+ self.__preedit_label.show()
+
+ # create aux label
+ self.__aux_label = Label(self.__aux_string)
+ self.__aux_label.set_attributes(self.__aux_attrs)
+ self.__aux_label.set_alignment(0.0, 0.5)
+ self.__aux_label.set_padding(8, 0)
+ self.__tooltips.set_tip(self.__aux_label, "Aux string")
+ self.__aux_label.set_no_show_all(True)
+ if self.__aux_string_visible:
+ self.__aux_label.show()
+
+ # create candidates area
+ self.__candidate_area = CandidateArea(self.__orientation)
+ self.__candidate_area.set_no_show_all(True)
+ self.update_lookup_table(self.__lookup_table, self.__lookup_table_visible)
+
+ # create state label
+ self.__state_label = Label()
+ self.__state_label.set_size_request(20, -1)
+
+ # create buttons
+ self.__prev_button = gtk.Button()
+ self.__prev_button.connect("clicked", lambda x: self.emit("cursor-up"))
+ self.__prev_button.set_relief(gtk.RELIEF_NONE)
+ self.__tooltips.set_tip(self.__prev_button, "Previous candidate")
+
+ self.__next_button = gtk.Button()
+ self.__next_button.connect("clicked", lambda x: self.emit("cursor-down"))
+ self.__next_button.set_relief(gtk.RELIEF_NONE)
+ self.__tooltips.set_tip(self.__next_button, "Next candidate")
+
+ self.__pack_all_widgets()
+
+ def __pack_all_widgets(self):
+ if self.__orientation == gtk.ORIENTATION_VERTICAL:
+ # package all widgets in vertical mode
+ image = gtk.Image()
+ image.set_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_MENU)
+ self.__prev_button.set_image(image)
+
+ image = gtk.Image()
+ image.set_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_MENU)
+ self.__next_button.set_image(image)
+ vbox = gtk.VBox()
+ vbox.pack_start(self.__preedit_label, False, False, 0)
+ vbox.pack_start(self.__aux_label, False, False, 0)
+ self.pack_start(vbox, False, False, 5)
+ self.pack_start(HSeparator(), False, False)
+ self.pack_start(self.__candidate_area, False, False, 2)
+ self.pack_start(HSeparator(), False, False)
+ hbox= gtk.HBox()
+ hbox.pack_start(self.__state_label, True, True)
+ hbox.pack_start(VSeparator(), False, False)
+ hbox.pack_start(self.__prev_button, False, False, 2)
+ hbox.pack_start(self.__next_button, False, False, 2)
+ self.pack_start(hbox, False, False)
+ else:
+ # package all widgets in HORIZONTAL mode
+ image = gtk.Image()
+ image.set_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_MENU)
+ self.__prev_button.set_image(image)
+
+ image = gtk.Image()
+ image.set_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_MENU)
+ self.__next_button.set_image(image)
+
+ vbox = gtk.VBox()
+ vbox.pack_start(self.__preedit_label, False, False, 0)
+ vbox.pack_start(self.__aux_label, False, False, 0)
+ self.pack_start(vbox, False, False, 5)
+ self.pack_start(HSeparator(), False, False)
+ hbox= gtk.HBox()
+ hbox.pack_start(self.__candidate_area, True, True, 2)
+ hbox.pack_start(VSeparator(), False, False)
+ hbox.pack_start(self.__prev_button, False, False, 2)
+ hbox.pack_start(self.__next_button, False, False, 2)
+ self.pack_start(hbox, False, False)
+
+ # self.hide_all()
+ # self.show_all()
+
+ def show_preedit(self):
+ self.__preedit_visible = True
+ self.__preedit_label.show()
+ self.__check_show_states()
+
+ def hide_preedit(self):
+ self.__preedit_visible = False
+ self.__preedit_label.hide()
+ self.__check_show_states()
+
+ def update_preedit(self, text, attrs, cursor_pos, visible):
+ if attrs:
+ attrs = PangoAttrList(attrs, text)
+ if visible:
+ self.show_preedit()
+ else:
+ self.hide_preedit()
+ self.__preedit_string = text
+ self.__preedit_label.set_text(text)
+ if attrs == None:
+ attrs = pango.AttrList()
+ self.__preedit_attrs = attrs
+ self.__preedit_label.set_attributes(attrs)
+
+ def show_aux_string(self):
+ self.__aux_string_visible = True
+ self.__aux_label.show()
+ self.__check_show_states()
+
+ def hide_aux_string(self):
+ self.__aux_string_visible = False
+ self.__aux_label.hide()
+ self.__check_show_states()
+
+ def update_aux_string(self, text, attrs, show):
+ attrs = PangoAttrList(attrs, text)
+
+ if show:
+ self.show_aux_string()
+ else:
+ self.hide_aux_string()
+
+ self.__aux_string = text
+ self.__aux_label.set_text(text)
+ if attrs == None:
+ attrs = pango.AttrList()
+ self.__aux_attrs = attrs
+ self.__aux_label.set_attributes(attrs)
+
+ def __refresh_candidates(self):
+ candidates = self.__lookup_table.get_canidates_in_current_page()
+ candidates = map(lambda x: (x[0], PangoAttrList(x[1], x[0]) if x[1] else None), candidates)
+ self.__candidate_area.set_candidates(candidates,
+ self.__lookup_table.get_cursor_pos_in_current_page(),
+ self.__lookup_table.is_cursor_visible()
+ )
+
+ def update_lookup_table(self, lookup_table, visible):
+ if lookup_table == None:
+ lookup_table = ibus.LookupTable()
+
+ if visible:
+ self.show_lookup_table()
+ else:
+ self.hide_lookup_table()
+
+ self.__lookup_table = lookup_table
+ self.__refresh_candidates()
+
+ def show_lookup_table(self):
+ self.__lookup_table_visible = True
+ self.__candidate_area.set_no_show_all(False)
+ self.__candidate_area.show_all()
+ self.__check_show_states()
+
+ def hide_lookup_table(self):
+ self.__lookup_table_visible = False
+ self.__candidate_area.hide_all()
+ self.__candidate_area.set_no_show_all(True)
+ self.__check_show_states()
+
+ def page_up_lookup_table(self):
+ self.__lookup_table.page_up()
+ self.__refresh_candidates()
+
+ def page_down_lookup_table(self):
+ self.__lookup_table.page_down()
+ self.__refresh_candidates()
+
+ def cursor_up_lookup_table(self):
+ self.__lookup_table.cursor_up()
+ self.__refresh_candidates()
+
+ def cursor_down_lookup_table(self):
+ self.__lookup_table.cursor_down()
+ self.__refresh_candidates()
+
+ def set_cursor_location(self, x, y):
+ self.__cursor_location = (x, y)
+ self.__check_position()
+
+ def __check_show_states(self):
+ if self.__preedit_visible or \
+ self.__aux_string_visible or \
+ self.__lookup_table_visible:
+ self.show_all()
+ self.emit("show")
+ else:
+ self.hide_all()
+ self.emit("hide")
+
+ def reset(self):
+ self.update_preedit("", None, 0, False)
+ self.update_aux_string("", None, False)
+ self.update_lookup_table(None, False)
+ self.hide()
+
+ def set_orientation(self, orientation):
+ if self.__orientation == orientation:
+ return
+ self.__orientation = orientation
+ self.__recreate_ui()
+ if self.__toplevel.flags() & gtk.VISIBLE:
+ self.show_all()
+
+ def get_orientation(self):
+ return self.__orientation
+
+ def do_set_property(self, property, value):
+ if property == 'orientation':
+ self.set_orientation(value)
+ else:
+ return gtk.DrawingArea.do_set_property(property, value)
+
+ def do_get_property(self, property):
+ if property == 'orientation':
+ return self.__orientation
+ else:
+ return gtk.DrawingArea.do_get_property(property)
+
+ # def do_expose_event(self, event):
+ # self.style.paint_box(self.window,
+ # gtk.STATE_NORMAL,
+ # gtk.SHADOW_IN,
+ # event.area,
+ # self,
+ # "panel",
+ # self.allocation.x, self.allocation.y,
+ # self.allocation.width, self.allocation.height)
+
+ # gtk.VBox.do_expose_event(self, event)
+
+ def do_size_request(self, requisition):
+ gtk.VBox.do_size_request(self, requisition)
+ self.__toplevel.resize(1, 1)
+
+ def __check_position(self):
+ bx = self.__cursor_location[0] + self.__toplevel.allocation.width
+ by = self.__cursor_location[1] + self.__toplevel.allocation.height
+
+ root_window = gdk.get_default_root_window()
+ sx, sy = root_window.get_size()
+
+ if bx > sx:
+ x = sx - self.__toplevel.allocation.width
+ else:
+ x = self.__cursor_location[0]
+
+ if by > sy:
+ y = sy - self.__toplevel.allocation.height
+ else:
+ y = self.__cursor_location[1]
+
+ self.move(x, y)
+
+ def __button_press_event_cb(self, widget, event):
+ if event.button == 1:
+ self.__begin_move = True
+ self.__press_pos = event.x_root, event.y_root
+ self.__toplevel.window.set_cursor(gdk.Cursor(gdk.FLEUR))
+ return True
+
+ if event.button == 3:
+ if self.get_orientation() == gtk.ORIENTATION_HORIZONTAL:
+ self.set_orientation(gtk.ORIENTATION_VERTICAL)
+ else:
+
+ self.set_orientation(gtk.ORIENTATION_HORIZONTAL)
+ return True
+ return False
+
+ def __button_release_event_cb(self, widget, event):
+ if event.button == 1:
+ del self.__press_pos
+ self.__begin_move = False
+ self.__toplevel.window.set_cursor(gdk.Cursor(gdk.LEFT_PTR))
+ return True
+ return False
+
+ def __motion_notify_event_cb(self, widget, event):
+ if self.__begin_move != True:
+ return False
+ x, y = self.__toplevel.get_position()
+ x = int(x + event.x_root - self.__press_pos[0])
+ y = int(y + event.y_root - self.__press_pos[1])
+ self.move(x, y)
+ self.__press_pos = event.x_root, event.y_root
+ return True
+
+ def show_all(self):
+ gtk.VBox.show_all(self)
+ self.__toplevel.show_all()
+
+ def hide_all(self):
+ gtk.VBox.hide_all(self)
+ self.__toplevel.hide_all()
+
+ def move(self, x, y):
+ self.__toplevel.move(x, y)
+
+gobject.type_register(CandidatePanel, "IBusCandidate")
+
diff --git a/ui/gtk/handle.py b/ui/gtk/handle.py
new file mode 100644
index 0000000..327a018
--- /dev/null
+++ b/ui/gtk/handle.py
@@ -0,0 +1,109 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or(at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+
+class Handle(gtk.EventBox):
+ def __init__ (self):
+ super(Handle, self).__init__()
+ self.set_visible_window(False)
+ self.set_size_request(10, -1)
+ self.set_events(
+ gdk.EXPOSURE_MASK | \
+ gdk.BUTTON_PRESS_MASK | \
+ gdk.BUTTON_RELEASE_MASK | \
+ gdk.BUTTON1_MOTION_MASK)
+
+ self.__move_begined = False
+
+ root = gdk.get_default_root_window()
+
+ def do_button_press_event(self, event):
+ if event.button == 1:
+ root = gdk.get_default_root_window()
+ try:
+ desktop = root.property_get("_NET_CURRENT_DESKTOP")[2][0]
+ self.__workarea = root.property_get("_NET_WORKAREA")[2][desktop * 4: (desktop + 1) * 4]
+ except:
+ self.__workarea = None
+ self.__move_begined = True
+ toplevel = self.get_toplevel()
+ x, y = toplevel.get_position()
+ self.__press_pos = event.x_root - x, event.y_root - y
+ self.window.set_cursor(gdk.Cursor(gdk.FLEUR))
+ return True
+ return False
+
+ def do_button_release_event(self, event):
+ if event.button == 1:
+ self.__move_begined = False
+ del self.__press_pos
+ del self.__workarea
+ self.window.set_cursor(gdk.Cursor(gdk.LEFT_PTR))
+ return True
+
+ return False
+
+ def do_motion_notify_event(self, event):
+ if not self.__move_begined:
+ return
+ toplevel = self.get_toplevel()
+ x, y = toplevel.get_position()
+ x = int(event.x_root - self.__press_pos[0])
+ y = int(event.y_root - self.__press_pos[1])
+
+ if self.__workarea == None:
+ toplevel.move(x, y)
+ return
+
+ if x < self.__workarea[0] and x > self.__workarea[0] - 16:
+ x = self.__workarea[0]
+ if y < self.__workarea[1] and y > self.__workarea[1] - 16:
+ y = self.__workarea[1]
+
+ w, h = toplevel.get_size()
+ if x + w > self.__workarea[0] + self.__workarea[2] and \
+ x + w < self.__workarea[0] + self.__workarea[2] + 16:
+ x = self.__workarea[0] + self.__workarea[2] - w
+ if y + h > self.__workarea[1] + self.__workarea[3] and \
+ y + h < self.__workarea[1] + self.__workarea[3] + 16:
+ y = self.__workarea[1] + self.__workarea[3] - h
+
+ toplevel.move(x, y)
+
+ def do_expose_event(self, event):
+ self.style.paint_handle(
+ self.window,
+ gtk.STATE_NORMAL,
+ gtk.SHADOW_OUT,
+ event.area,
+ self,
+ "",
+ self.allocation.x, self.allocation.y,
+ 10, self.allocation.height,
+ gtk.ORIENTATION_VERTICAL)
+ return True
+
+gobject.type_register(Handle, "IBusHandle")
+
+
diff --git a/ui/gtk/ibus-ui-gtk.in b/ui/gtk/ibus-ui-gtk.in
new file mode 100644
index 0000000..1f2331b
--- /dev/null
+++ b/ui/gtk/ibus-ui-gtk.in
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+export IBUS_PREFIX=@prefix@
+exec python @prefix@/share/ibus/ui/gtk/main.py $@
+
diff --git a/ui/gtk/icon.py b/ui/gtk/icon.py
new file mode 100644
index 0000000..4a373a9
--- /dev/null
+++ b/ui/gtk/icon.py
@@ -0,0 +1,51 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+
+class IconWidget(gtk.Image):
+ def __init__(self, icon, size):
+ super(IconWidget, self).__init__()
+ pixbuf = None
+ try:
+ if icon.startswith("/"):
+ pixbuf = gdk.pixbuf_new_from_file(icon)
+ else:
+ theme = gtk.icon_theme_get_default()
+ pixbuf = theme.load_icon(icon, size, 0)
+ except:
+ pass
+
+ if pixbuf == None:
+ theme = gtk.icon_theme_get_default()
+ pixbuf = theme.load_icon(gtk.STOCK_MISSING_IMAGE, size, 0)
+
+ width = pixbuf.get_width()
+ height = pixbuf.get_height()
+ scale = float(size) / float(max(width, height))
+ width = int(scale * width)
+ height = int(scale * height)
+ pixbuf = pixbuf.scale_simple(width, height, gdk.INTERP_BILINEAR)
+
+ self.set_from_pixbuf(pixbuf)
+ self.show()
+
diff --git a/ui/gtk/languagebar.py b/ui/gtk/languagebar.py
new file mode 100644
index 0000000..65eb2e5
--- /dev/null
+++ b/ui/gtk/languagebar.py
@@ -0,0 +1,197 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or(at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import ibus
+from handle import Handle
+from menu import menu_position
+from toolitem import ToolButton,\
+ ToggleToolButton, \
+ SeparatorToolItem, \
+ MenuToolButton
+
+from gettext import dgettext
+_ = lambda a : dgettext("ibus", a)
+N_ = lambda a : a
+
+ICON_SIZE = gtk.ICON_SIZE_MENU
+
+class LanguageBar(gtk.Toolbar):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ "get-im-menu" : (
+ gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_PYOBJECT,
+ ()),
+ }
+
+ def __init__ (self):
+ super(LanguageBar, self).__init__()
+ self.__auto_hide = False
+ self.__enabled = False
+ self.__has_focus = False
+ self.set_style(gtk.TOOLBAR_ICONS)
+ self.set_show_arrow(False)
+ self.set_property("icon-size", ICON_SIZE)
+ self.__create_ui()
+
+ self.__properties = []
+ self.__toplevel = gtk.Window(gtk.WINDOW_POPUP)
+ self.__toplevel.add(self)
+
+ root = gdk.get_default_root_window()
+ try:
+ workarea = root.property_get("_NET_WORKAREA")[2]
+ right, bottom = workarea[2], workarea[3]
+ except:
+ right, bottom = root.get_size()
+ self.__position = right - 200, bottom - 40
+ self.__toplevel.move(*self.__position)
+
+ def __create_ui(self):
+ # create move handle
+ self.__handle = gtk.ToolItem()
+ self.__handle.add(Handle())
+ self.insert(self.__handle, -1)
+
+ # create input methods menu
+ prop = ibus.Property(name = "", type = ibus.PROP_TYPE_TOGGLE, icon = "ibus", tooltip = _("Switch engine"))
+ self.__im_menu = ToggleToolButton(prop)
+ self.__im_menu.set_homogeneous(False)
+ self.__im_menu.connect("toggled", self.__im_menu_toggled_cb)
+ self.insert(self.__im_menu, -1)
+
+ def __im_menu_toggled_cb(self, widget):
+ if self.__im_menu.get_active():
+ menu = self.emit("get-im-menu")
+ menu.connect("deactivate", self.__im_menu_deactivate_cb)
+ menu.popup(None, None,
+ menu_position,
+ 0,
+ gtk.get_current_event_time(),
+ widget)
+ def __im_menu_deactivate_cb(self, menu):
+ self.__im_menu.set_active(False)
+
+ def __remove_properties(self):
+ # reset all properties
+
+ map(lambda i: i.destroy(), self.__properties)
+ self.__properties = []
+
+ def do_show(self):
+ gtk.Toolbar.do_show(self)
+
+ def do_size_request(self, requisition):
+ gtk.Toolbar.do_size_request(self, requisition)
+ self.__toplevel.resize(1, 1)
+
+ def set_im_icon(self, icon_name):
+ self.__im_menu.set_icon_name(icon_name)
+
+ def reset(self):
+ self.__remove_properties()
+
+ def set_enabled(self, enabled):
+ self.__enabled = enabled
+ if self.__enabled and self.__has_focus:
+ self.show_all()
+ if not self.__enabled and self.__auto_hide:
+ self.hide_all()
+
+ def is_enabled(self):
+ return self.__enabled
+
+ def set_auto_hide(self, auto_hide):
+ self.__auto_hide = auto_hide
+ if self.__has_focus:
+ self.focus_in()
+ else:
+ self.focus_out()
+
+ def get_auto_hide(self):
+ return self.__auto_hide
+
+ def register_properties(self, props):
+ self.__remove_properties()
+ # create new properties
+ for prop in props:
+ if prop.type == ibus.PROP_TYPE_NORMAL:
+ item = ToolButton(prop = prop)
+ elif prop.type == ibus.PROP_TYPE_TOGGLE:
+ item = ToggleToolButton(prop = prop)
+ elif prop.type == ibus.PROP_TYPE_MENU:
+ item = MenuToolButton(prop = prop)
+ elif prop.type == PROP_TYPE_SEPARATOR:
+ item = SeparatorToolItem()
+ else:
+ raise IBusException("Unknown property type = %d" % prop.type)
+
+ item.connect("property-activate",
+ lambda w, n, s: self.emit("property-activate", n, s))
+
+ item.set_sensitive(prop.sensitive)
+
+ item.set_no_show_all(True)
+
+ if prop.visible:
+ item.show()
+ else:
+ item.hide()
+
+ self.__properties.append(item)
+ self.insert(item, -1)
+
+ def update_property(self, prop):
+ map(lambda x: x.update_property(prop), self.__properties)
+
+ def show_all(self):
+ self.__toplevel.show_all()
+ self.__toplevel.window.raise_()
+ gtk.Toolbar.show_all(self)
+
+ def hide_all(self):
+ x, y = self.__toplevel.get_position()
+ self.__toplevel.hide_all()
+ gtk.Toolbar.hide_all(self)
+
+ # save bar position
+ self.__toplevel.move(x, y)
+
+ def focus_in(self):
+ self.__has_focus = True
+ self.__im_menu.set_sensitive(True)
+ if self.__enabled:
+ self.show_all()
+
+ def focus_out(self):
+ self.__has_focus = False
+ self.__im_menu.set_sensitive(False)
+ if self.__auto_hide:
+ self.hide_all()
+
+gobject.type_register(LanguageBar, "IBusLanguageBar")
+
diff --git a/ui/gtk/main.py b/ui/gtk/main.py
new file mode 100644
index 0000000..07ea3c0
--- /dev/null
+++ b/ui/gtk/main.py
@@ -0,0 +1,80 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or(at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import os
+import sys
+import getopt
+import ibus
+import gtk
+import locale
+import panel
+
+class PanelApplication:
+ def __init__ (self):
+ self.__bus = ibus.Bus()
+ self.__bus.connect("destroy", self.__bus_destroy_cb)
+
+ self.__panel = panel.Panel(self.__bus)
+
+ def run(self):
+ gtk.main()
+
+ def __bus_destroy_cb(self, _ibus):
+ gtk.main_quit()
+
+
+def launch_panel():
+ # gtk.settings_get_default().props.gtk_theme_name = "/home/phuang/.themes/aud-Default/gtk-2.0/gtkrc"
+ # gtk.rc_parse("./themes/default/gtkrc")
+ PanelApplication().run()
+
+def print_help(out, v = 0):
+ print >> out, "-h, --help show this message."
+ print >> out, "-d, --daemonize daemonize ibus"
+ sys.exit(v)
+
+def main():
+ daemonize = False
+ shortopt = "hd"
+ longopt = ["help", "daemonize"]
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], shortopt, longopt)
+ except getopt.GetoptError, err:
+ print_help(sys.stderr, 1)
+
+ for o, a in opts:
+ if o in("-h", "--help"):
+ print_help(sys.stdout)
+ elif o in("-d", "--daemonize"):
+ daemonize = True
+ else:
+ print >> sys.stderr, "Unknown argument: %s" % o
+ print_help(sys.stderr, 1)
+
+ if daemonize:
+ if os.fork():
+ sys.exit()
+
+ launch_panel()
+
+if __name__ == "__main__":
+ locale.bind_textdomain_codeset("ibus", "UTF-8")
+ main()
diff --git a/ui/gtk/menu.py b/ui/gtk/menu.py
new file mode 100644
index 0000000..19911e8
--- /dev/null
+++ b/ui/gtk/menu.py
@@ -0,0 +1,246 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gobject
+import ibus
+import icon
+from propitem import PropItem
+
+class Menu (gtk.Menu, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__ (self, prop):
+ gtk.Menu.__init__ (self)
+ PropItem.__init__ (self, prop)
+
+ self.set_take_focus (False)
+ self.__create_items (self._prop.sub_props)
+ self.show_all ()
+ self.set_sensitive (prop.sensitive)
+
+ def __create_items (self, props):
+ radio_group = None
+
+ for prop in props:
+ if prop.type == ibus.PROP_TYPE_NORMAL:
+ item = gtk.ImageMenuItem (prop)
+ elif prop.type == ibus.PROP_TYPE_TOGGLE:
+ item = CheckMenuItem (prop)
+ elif prop.type == ibus.PROP_TYPE_RADIO:
+ item = RadioMenuItem (radio_group, prop)
+ radio_group = item
+ elif prop.type == ibus.PROP_TYPE_SEPARATOR:
+ item = SeparatorMenuItem ()
+ radio_group = None
+ elif prop.type == ibus.PROP_TYPE_MENU:
+ item = gtk.ImageMenuItem (prop)
+ item.set_submenu (Menu (prop))
+ else:
+ assert Fasle
+
+ item.set_sensitive (prop.sensitive)
+ if prop.visible:
+ item.set_no_show_all (False)
+ item.show ()
+ else:
+ item.set_no_show_all (True)
+ item.hide ()
+
+ item.connect ("property-activate", lambda w,n,s: self.emit ("property-activate", n, s))
+
+ self.append (item)
+ self._sub_items.append (item)
+
+ def popup (self, button, active_time, widget):
+ gtk.Menu.popup (self, None, None, menu_position,
+ button, active_time, widget)
+
+ def _property_clicked (self, item, prop):
+ pass
+
+
+class ImageMenuItem (gtk.ImageMenuItem, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__ (self, prop):
+ gtk.ImageMenuItem.__init__ (self, label = prop.label)
+ PropItem.__init__ (self, prop)
+
+ if self._prop.icon:
+ size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+ self.set_image (icon.IconWidget(prop.icon, size[0]))
+
+ if self._prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+ def do_activate (self):
+ self.emit ("property-activate", self._prop.name, self._prop.state)
+
+ def property_changed (self):
+ self.set_sensitive (self._prop.sensitive)
+ if self._prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+
+class CheckMenuItem (gtk.CheckMenuItem, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__ (self, prop):
+ gtk.CheckMenuItem.__init__ (self, label = prop.label)
+ PropItem.__init__ (self, prop)
+
+ self.set_active (self._prop._state == ibus.PROP_STATE_CHECKED)
+
+ if prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+ def do_toggled (self):
+ if self.get_active ():
+ self._prop.state = ibus.PROP_STATE_CHECKED
+ else:
+ self._prop.state = ibus.PROP_STATE_UNCHECKED
+ self.emit ("property-activate", self._prop.name, self._prop.state)
+
+ def property_changed (self):
+ self.set_active (self._prop.state == ibus.PROP_STATE_CHECKED)
+ self.set_sensitive (self._prop.sensitive)
+ if self._prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+
+class RadioMenuItem (gtk.RadioMenuItem, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__ (self, group, prop):
+ gtk.RadioMenuItem.__init__ (self, group, label = prop.label)
+ PropItem.__init__ (self, prop)
+
+ self.set_active (self._prop.state == ibus.PROP_STATE_CHECKED)
+
+ if prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+ def property_changed (self):
+ self.set_active (self._prop.state == ibus.PROP_STATE_CHECKED)
+ self.set_sensitive (self._prop.sensitive)
+ if self._prop.visible:
+ self.set_no_show_all (False)
+ self.show_all ()
+ else:
+ self.set_no_show_all (True)
+ self.hide_all ()
+
+ def do_toggled (self):
+ if self.get_active ():
+ self._prop.state = ibus.PROP_STATE_CHECKED
+ else:
+ self._prop.state = ibus.PROP_STATE_UNCHECKED
+ self.emit ("property-activate", self._prop.name, self._prop.state)
+
+class SeparatorMenuItem (gtk.SeparatorMenuItem, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+
+gobject.type_register (Menu, "IBusMenu")
+gobject.type_register (ImageMenuItem, "IBusImageMenuItem")
+gobject.type_register (CheckMenuItem, "IBusCheckMenuItem")
+gobject.type_register (RadioMenuItem, "IBusRadioMenuItem")
+gobject.type_register (SeparatorMenuItem, "IBusSeparatorMenuItem")
+
+def menu_position (menu, button):
+ screen = button.get_screen ()
+ monitor = screen.get_monitor_at_window (button.window)
+ monitor_allocation = screen.get_monitor_geometry (monitor)
+
+ x, y = button.window.get_origin ()
+ x += button.allocation.x
+ y += button.allocation.y
+
+ menu_width, menu_height = menu.size_request ()
+
+ if x + menu_width >= monitor_allocation.width:
+ x -= menu_width - button.allocation.width
+ elif x - menu_width <= 0:
+ pass
+ else:
+ if x <= monitor_allocation.width * 3 / 4:
+ pass
+ else:
+ x -= menu_width - button.allocation.width
+
+ if y + button.allocation.height + menu_height >= monitor_allocation.height:
+ y -= menu_height
+ elif y - menu_height <= 0:
+ y += button.allocation.height
+ else:
+ if y <= monitor_allocation.height * 3 / 4:
+ y += button.allocation.height
+ else:
+ y -= menu_height
+
+ return (x, y, False)
+
diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py
new file mode 100644
index 0000000..de54464
--- /dev/null
+++ b/ui/gtk/panel.py
@@ -0,0 +1,371 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or(at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import ibus
+import icon as _icon
+import os
+import sys
+from os import path
+from ibus import interface
+from languagebar import LanguageBar
+from candidatepanel import CandidatePanel
+
+from gettext import dgettext
+_ = lambda a : dgettext("ibus", a)
+N_ = lambda a : a
+
+def show_uri(screen, link):
+ try:
+ gtk.show_uri(screen, link, 0)
+ except:
+ print >> sys.stderr, "pygtk do not support show_uri"
+
+def url_hook(about, link, user_data):
+ show_uri(about.get_screen(), link)
+
+def email_hook(about, email, user_data):
+ show_uri(about.get_screen(), "mailto:%s" % email)
+
+gtk.about_dialog_set_url_hook(url_hook, None)
+gtk.about_dialog_set_email_hook(email_hook, None)
+
+class Panel(ibus.PanelBase):
+ def __init__ (self, bus):
+ super(Panel, self).__init__(bus)
+ self.__bus = bus
+ self.__focus_ic = None
+ self.__setup_pid = 0
+ self.__prefix = os.getenv("IBUS_PREFIX")
+ self.__data_dir = path.join(self.__prefix, "share", "ibus")
+ self.__icons_dir = path.join(self.__data_dir, "icons")
+ self.__ibus_icon = path.join(self.__icons_dir, "ibus.svg")
+ self.__setup_cmd = path.join(self.__prefix, "bin", "ibus-setup")
+
+ # connect bus signal
+ self.__bus.connect("config-value-changed", self.__config_value_changed_cb)
+ self.__bus.connect("config-reloaded", self.__config_reloaded_cb)
+ self.__bus.config_add_watch("panel")
+
+ # add icon search path
+ icon_theme = gtk.icon_theme_get_default()
+ dir = path.dirname(__file__)
+ icondir = path.join(dir, "..", "icons")
+ icon_theme.prepend_search_path(icondir)
+
+ self.__language_bar = LanguageBar()
+ self.__language_bar.connect("property-activate",
+ lambda widget, prop_name, prop_state: self.property_activate(prop_name, prop_state))
+ self.__language_bar.connect("get-im-menu",
+ self.__get_im_menu_cb)
+ self.__language_bar.focus_out()
+ self.__language_bar.show_all()
+
+ self.__candidate_panel = CandidatePanel()
+ self.__candidate_panel.connect("cursor-up",
+ lambda widget: self.cursor_up())
+ self.__candidate_panel.connect("cursor-down",
+ lambda widget: self.cursor_down())
+
+ self.__status_icon = gtk.StatusIcon()
+ self.__status_icon.connect("popup-menu", self.__status_icon_popup_menu_cb)
+ self.__status_icon.connect("activate", self.__status_icon_activate_cb)
+ self.__status_icon.set_from_file(self.__ibus_icon)
+ self.__status_icon.set_tooltip(_("iBus - Running"))
+ self.__status_icon.set_visible(True)
+
+ self.__config_load_lookup_table_orientation()
+ self.__config_load_auto_hide()
+ self.__config_load_custom_font()
+ self.__bus.request_name(ibus.panel.IBUS_PANEL_NAME, 0)
+
+ def set_cursor_location(self, x, y, w, h):
+ self.__candidate_panel.set_cursor_location(x + w, y + h)
+
+ def update_preedit(self, text, attrs, cursor_pos, visible):
+ self.__candidate_panel.update_preedit(text, attrs, cursor_pos, visible)
+
+ def show_preedit(self):
+ self.__candidate_panel.show_preedit()
+
+ def hide_preedit(self):
+ self.__candidate_panel.hide_preedit()
+
+ def update_aux_string(self, text, attrs, visible):
+ self.__candidate_panel.update_aux_string(text, attrs, visible)
+
+ def show_aux_string(self):
+ self.__candidate_panel.show_aux_string()
+
+ def hide_aux_string(self):
+ self.__candidate_panel.hide_aux_string()
+
+ def update_lookup_table(self, lookup_table, visible):
+ self.__candidate_panel.update_lookup_table(lookup_table, visible)
+
+ def show_lookup_table(self):
+ self.__candidate_panel.show_lookup_table()
+
+ def hide_lookup_table(self):
+ self.__candidate_panel.hide_lookup_table()
+
+ def page_up_lookup_table(self):
+ self.__candidate_panel.page_up_lookup_table()
+
+ def page_down_lookup_table(self):
+ self.__candidate_panel.page_down_lookup_table()
+
+ def cursor_up_lookup_table(self):
+ self.__candidate_panel.cursor_up_lookup_table()
+
+ def cursor_down_lookup_table(self):
+ self.__candidate_panel.cursor_down_lookup_table()
+
+ def show_candidate_window(self):
+ self.__candidate_panel.show_all()
+
+ def hide_candidate_window(self):
+ self.__candidate_panel.hide_all()
+
+ def show_language_bar(self):
+ self.__language_bar.show_all()
+
+ def hide_language_bar(self):
+ self.__language_bar.hide_all()
+
+ def register_properties(self, props):
+ self.__language_bar.register_properties(props)
+
+ def update_property(self, prop):
+ self.__language_bar.update_property(prop)
+
+ def __set_im_icon(self, icon_name):
+ self.__language_bar.set_im_icon(icon_name)
+ if icon_name.startswith("/"):
+ self.__status_icon.set_from_file(icon_name)
+ else:
+ self.__status_icon.set_from_icon_name(icon_name)
+
+ def focus_in(self, ic):
+ self.reset()
+ self.__focus_ic = ic
+
+ factory, enabled = self.__bus.get_input_context_states(ic)
+ self.__language_bar.set_enabled(enabled)
+
+ if factory == "" or not enabled:
+ self.__set_im_icon(self.__ibus_icon)
+ else:
+ name, lang, icon, authors, credits = self.__bus.get_factory_info(factory)
+ self.__set_im_icon(icon)
+ self.__language_bar.focus_in()
+
+ def focus_out(self, ic):
+ self.reset()
+ if self.__focus_ic == ic:
+ self.__focus_ic = None
+ self.__language_bar.focus_out()
+ self.__set_im_icon(self.__ibus_icon)
+
+ def states_changed(self):
+ if not self.__focus_ic:
+ return
+ factory, enabled = self.__bus.get_input_context_states(self.__focus_ic)
+ self.__language_bar.set_enabled(enabled)
+ if enabled == False or not factory:
+ self.__set_im_icon(self.__ibus_icon)
+ else:
+ name, lang, icon, authors, credits = self.__bus.get_factory_info(factory)
+ self.__set_im_icon(icon)
+
+ def reset(self):
+ self.__candidate_panel.reset()
+ self.__language_bar.reset()
+
+ def do_destroy(self):
+ gtk.main_quit()
+
+ def __config_load_lookup_table_orientation(self):
+ value = self.__bus.config_get_value("panel", "lookup_table_orientation", 0)
+ if value != 0 and value != 1:
+ value = 0
+ if value == 0:
+ self.__candidate_panel.set_orientation(gtk.ORIENTATION_HORIZONTAL)
+ else:
+ self.__candidate_panel.set_orientation(gtk.ORIENTATION_VERTICAL)
+
+ def __config_load_auto_hide(self):
+ auto_hide = self.__bus.config_get_value("panel", "auto_hide", False)
+ self.__language_bar.set_auto_hide(auto_hide)
+
+ def __config_load_custom_font(self):
+ use_custom_font = self.__bus.config_get_value("panel", "use_custom_font", False)
+ font_name = gtk.settings_get_default().get_property("gtk-font-name")
+ font_name = unicode(font_name, "utf-8")
+ custom_font = self.__bus.config_get_value("panel", "custom_font", font_name)
+ style_string = 'style "custom-font" { font_name="%s" }\n' \
+ 'class "IBusPanelLabel" style "custom-font"\n'
+ if use_custom_font:
+ style_string = style_string % custom_font
+ gtk.rc_parse_string(style_string)
+ else:
+ style_string = style_string % ""
+ gtk.rc_parse_string(style_string)
+
+ settings = gtk.settings_get_default()
+ gtk.rc_reset_styles(settings)
+
+ def __config_value_changed_cb(self, bus, section, name, value):
+ if name == "lookup_table_orientation":
+ self.__config_load_lookup_table_orientation()
+ elif name == "auto_hide":
+ self.__config_load_auto_hide()
+ elif name == "use_custom_font" or name == "custom_font":
+ self.__config_load_custom_font()
+
+ def __config_reloaded_cb(self, bus):
+ pass
+
+ def __create_sys_menu(self):
+ menu = gtk.Menu()
+ item = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
+ item.connect("activate",
+ self.__sys_menu_item_activate_cb, gtk.STOCK_PREFERENCES)
+ menu.add(item)
+ item = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
+ item.connect("activate",
+ self.__sys_menu_item_activate_cb, gtk.STOCK_ABOUT)
+ menu.add(item)
+ menu.add(gtk.SeparatorMenuItem())
+ item = gtk.ImageMenuItem(gtk.STOCK_QUIT)
+ item.connect("activate",
+ self.__sys_menu_item_activate_cb, gtk.STOCK_QUIT)
+ menu.add(item)
+
+ menu.show_all()
+ menu.set_take_focus(False)
+ return menu
+
+ def __create_im_menu(self):
+ menu = gtk.Menu()
+ factories = self.__bus.get_factories()
+
+ if not factories:
+ item = gtk.MenuItem(label = "no engine")
+ item.set_sensitive(False)
+ menu.add(item)
+ else:
+ tmp = {}
+ for factory in factories:
+ name, lang, icon, authors, credits = self.__bus.get_factory_info(factory)
+ lang = ibus.get_language_name(lang)
+ if not icon:
+ icon = "engine-default"
+ if lang not in tmp:
+ tmp[lang] = []
+ tmp[lang].append((name, lang, icon, authors, credits, factory))
+ langs = tmp.keys()
+ other = tmp.get(_("Other"), [])
+ if _("Other") in tmp:
+ langs.remove(_("Other"))
+ langs.append(_("Other"))
+ for lang in langs:
+ if len(tmp[lang]) == 1:
+ name, lang, icon, authors, credits, factory = tmp[lang][0]
+ item = gtk.ImageMenuItem("%s - %s" % (lang, name))
+ size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+ item.set_image (_icon.IconWidget(icon, size[0]))
+ item.connect("activate", self.__im_menu_item_activate_cb, factory)
+ menu.add(item)
+ else:
+ item = gtk.MenuItem(lang)
+ menu.add(item)
+ submenu = gtk.Menu()
+ item.set_submenu(submenu)
+ for name, __lang, icon, authors, credits, factory in tmp[lang]:
+ item = gtk.ImageMenuItem(name)
+ size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+ item.set_image (_icon.IconWidget(icon, size[0]))
+ item.connect("activate", self.__im_menu_item_activate_cb, factory)
+ submenu.add(item)
+
+ menu.show_all()
+ menu.set_take_focus(False)
+ return menu
+
+ def __get_im_menu_cb(self, languagebar):
+ menu = self.__create_im_menu()
+ return menu
+
+ def __status_icon_popup_menu_cb(self, status_icon, button, active_time):
+ menu = self.__create_sys_menu()
+ menu.popup(None, None,
+ gtk.status_icon_position_menu,
+ button,
+ active_time,
+ self.__status_icon)
+
+ def __status_icon_activate_cb(self, status_icon):
+ if not self.__focus_ic:
+ return
+ menu = self.__create_im_menu()
+ menu.popup(None, None,
+ gtk.status_icon_position_menu,
+ 0,
+ gtk.get_current_event_time(),
+ self.__status_icon)
+
+ def __im_menu_item_activate_cb(self, item, factory):
+ self.__bus.set_factory(factory)
+
+ def __sys_menu_item_activate_cb(self, item, command):
+ if command == gtk.STOCK_PREFERENCES:
+ self.__start_setup()
+ elif command == gtk.STOCK_ABOUT:
+ about_dialog = gtk.AboutDialog()
+ about_dialog.set_program_name("iBus")
+ about_dialog.set_version(ibus.get_version())
+ about_dialog.set_copyright(ibus.get_copyright())
+ about_dialog.set_license(ibus.get_license())
+ about_dialog.set_comments(_("IBus is an intelligent input bus for Linux/Unix."))
+ about_dialog.set_website("http://code.google.com/p/ibus")
+ about_dialog.set_authors(["Huang Peng <shawn.p.huang@gmail.com>"])
+ about_dialog.set_documenters(["Huang Peng <shawn.p.huang@gmail.com>"])
+ about_dialog.set_translator_credits(_("translator-credits"))
+ about_dialog.set_logo_icon_name("ibus")
+ about_dialog.run()
+ about_dialog.destroy()
+ elif command == gtk.STOCK_QUIT:
+ self.__bus.kill()
+ else:
+ print >> sys.stderr, "Unknown command %s" % command
+
+ def __start_setup(self):
+ if self.__setup_pid != 0:
+ pid, state = os.waitpid(self.__setup_pid, os.P_NOWAIT)
+ if pid != self.__setup_pid:
+ return
+ self.__setup_pid = 0
+ self.__setup_pid = os.spawnl(os.P_NOWAIT, self.__setup_cmd, "ibus-setup")
+
+gobject.type_register(Panel, "IBusPanel")
diff --git a/ui/gtk/propitem.py b/ui/gtk/propitem.py
new file mode 100644
index 0000000..3d363ed
--- /dev/null
+++ b/ui/gtk/propitem.py
@@ -0,0 +1,62 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+class PropItem:
+ def __init__ (self, prop):
+ self._prop = prop
+ self._sub_items = []
+
+ def update_property (self, prop):
+ if self._prop == None:
+ return False
+
+ retval = False
+
+ if self._prop.name == prop.name and self._prop.type == prop.type:
+ self._prop = prop
+ self.property_changed ()
+ retval = True
+
+ if any (map (lambda i: i.update_property (prop), self._sub_items)):
+ retval = True
+
+ return retval
+
+ def set_prop_label (self, label):
+ self._prop.label = label
+ self.property_changed ()
+
+ def set_icon (self, icon):
+ self._prop.icon = icon
+ self.property_changed ()
+
+ def set_tooltip (self, tooltip):
+ self._prop.tooltip = tooltip
+ self.property_changed ()
+
+ def set_state (self, state):
+ self._prop.state = state
+ self.property_changed ()
+
+ def property_changed (self):
+ pass
+
+
diff --git a/ui/gtk/toolitem.py b/ui/gtk/toolitem.py
new file mode 100644
index 0000000..48147a7
--- /dev/null
+++ b/ui/gtk/toolitem.py
@@ -0,0 +1,172 @@
+# vim:set et sts=4 sw=4:
+#
+# ibus - The Input Bus
+#
+# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import ibus
+from propitem import PropItem
+import icon
+from menu import *
+
+class ToolButton(gtk.ToolButton, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__(self, prop):
+ gtk.ToolButton.__init__ (self, label = prop.label)
+ self.set_homogeneous(False)
+ PropItem.__init__ (self, prop)
+ self.property_changed()
+
+ def set_icon_name(self, icon_name):
+ if icon_name:
+ widget = icon.IconWidget(icon_name, 18)
+ gtk.ToolButton.set_icon_widget(self, widget)
+ self.set_is_important(False)
+ elif self._prop.label:
+ gtk.ToolButton.set_icon_widget(self, None)
+ self.set_is_important(True)
+ else:
+ widget = icon.IconWidget("ibus", 18)
+ gtk.ToolButton.set_icon_widget(self, widget)
+ self.set_is_important(False)
+
+ self._prop.icon = icon_name
+
+ def set_tooltip_text(self, text):
+ if text:
+ gtk.ToolButton.set_tooltip_text(self, text)
+ else:
+ gtk.ToolButton.set_tooltip_text(self, None)
+
+ self._prop.tooltip = text
+
+ def property_changed(self):
+ self.set_label(self._prop.label)
+ self.set_tooltip_text(self._prop.tooltip)
+ self.set_sensitive(self._prop.sensitive)
+ self.set_icon_name(self._prop.icon)
+
+ if self._prop.visible:
+ self.set_no_show_all(False)
+ self.show_all()
+ else:
+ self.set_no_show_all(True)
+ self.hide_all()
+
+ def do_clicked(self):
+ self.emit("property-activate", self._prop.name, self._prop.state)
+
+
+class ToggleToolButton(gtk.ToggleToolButton, PropItem):
+ __gsignals__ = {
+ "property-activate" : (
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ }
+
+ def __init__ (self, prop):
+ gtk.ToggleToolButton.__init__ (self)
+ self.set_homogeneous(False)
+ PropItem.__init__ (self, prop)
+ self.property_changed()
+
+ def set_icon_name(self, icon_name):
+ if icon_name:
+ widget = icon.IconWidget(icon_name, 18)
+ gtk.ToggleToolButton.set_icon_widget(self, widget)
+ self.set_is_important(False)
+ elif self._prop.label:
+ gtk.ToggleToolButton.set_icon_widget(self, None)
+ self.set_is_important(True)
+ else:
+ widget = icon.IconWidget("ibus", 18)
+ gtk.ToggleToolButton.set_icon_widget(self, widget)
+ self.set_is_important(False)
+
+ self._prop.icon = icon_name
+
+ def set_tooltip_text(self, text):
+ if text:
+ gtk.ToggleToolButton.set_tooltip_text(self, text)
+ else:
+ gtk.ToggleToolButton.set_tooltip_text(self, None)
+
+ self._prop.tooltip = text
+
+ def property_changed(self):
+ self.set_tooltip_text(self._prop.tooltip)
+ self.set_label(self._prop.label)
+ self.set_icon_name(self._prop.icon)
+ self.set_active(self._prop.state == ibus.PROP_STATE_CHECKED)
+ self.set_sensitive(self._prop.sensitive)
+ if self._prop.visible:
+ self.set_no_show_all(False)
+ self.show_all()
+ else:
+ self.set_no_show_all(True)
+ self.hide_all()
+
+ def do_toggled(self):
+ if self.get_active():
+ self._prop.state = ibus.PROP_STATE_CHECKED
+ else:
+ self._prop.state = ibus.PROP_STATE_UNCHECKED
+ self.emit("property-activate", self._prop.name, self._prop.state)
+
+class SeparatorToolItem(gtk.SeparatorToolItem, PropItem):
+ def __init__ (self, prop):
+ gtk.SeparatorToolItem.__init__(self)
+ self.set_homogeneous(False)
+ PropItem.__init__(self, prop)
+
+class MenuToolButton(ToggleToolButton):
+ # __gsignals__ = {
+ # "property-activate" : (
+ # gobject.SIGNAL_RUN_FIRST,
+ # gobject.TYPE_NONE,
+ # (gobject.TYPE_STRING, gobject.TYPE_INT)),
+ # }
+
+ def __init__ (self, prop):
+ super(MenuToolButton, self).__init__(prop)
+ self._menu = Menu(prop)
+ self._menu.connect("deactivate", lambda m: self.set_active(False))
+ self._menu.connect("property-activate", lambda w,n,s: self.emit("property-activate", n, s))
+
+ def update_property(self, prop):
+ PropItem.update_property(self, prop)
+ self._menu.update_property(prop)
+
+ def do_toggled(self):
+ if self.get_active():
+ self._menu.popup(0, gtk.get_current_event_time(), self)
+
+gobject.type_register(ToolButton, "ToolButton")
+gobject.type_register(ToggleToolButton, "IBusToggleToolButton")
+gobject.type_register(MenuToolButton, "MenuToolButton")
+