diff options
author | Huang Peng <shawn.p.huang@gmail.com> | 2008-10-05 22:16:30 +0800 |
---|---|---|
committer | Huang Peng <shawn.p.huang@gmail.com> | 2008-10-05 22:16:30 +0800 |
commit | 3d36b0eb76eafcda76082baecf34a48d6cbb0319 (patch) | |
tree | eef6a7f236cc373ac99d7aae29aa5affa472e920 /ui | |
parent | 249722e4aa36837b6ff5fd1436387bc66b482228 (diff) | |
download | ibus-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.am | 24 | ||||
-rw-r--r-- | ui/gtk/.gitignore | 1 | ||||
-rw-r--r-- | ui/gtk/Makefile.am | 49 | ||||
-rw-r--r-- | ui/gtk/candidatepanel.py | 484 | ||||
-rw-r--r-- | ui/gtk/handle.py | 109 | ||||
-rw-r--r-- | ui/gtk/ibus-ui-gtk.in | 23 | ||||
-rw-r--r-- | ui/gtk/icon.py | 51 | ||||
-rw-r--r-- | ui/gtk/languagebar.py | 197 | ||||
-rw-r--r-- | ui/gtk/main.py | 80 | ||||
-rw-r--r-- | ui/gtk/menu.py | 246 | ||||
-rw-r--r-- | ui/gtk/panel.py | 371 | ||||
-rw-r--r-- | ui/gtk/propitem.py | 62 | ||||
-rw-r--r-- | ui/gtk/toolitem.py | 172 |
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") + |