# vim:set et sts=4 sw=4:
#
# ibus - The Input Bus
#
# Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
# Copyright (c) 2014-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2007-2018 Red Hat, Inc.
#
# 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.1 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 library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import IBus
from gi.repository import Pango
import i18n
from icon import load_icon
from i18n import _, N_
class EngineTreeView(Gtk.TreeView):
__gtype_name__ = 'EngineTreeView'
__gproperties__ = {
'active-engine' : (
object,
'selected engine',
'selected engine',
GObject.ParamFlags.READABLE),
'engines' : (
object,
'engines',
'engines',
GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE)
}
def __init__(self):
super(EngineTreeView, self).__init__()
self.__engines = []
self.__changed = False
# self.set_headers_visible(True)
self.set_reorderable(True)
self.__model = Gtk.ListStore(GObject.TYPE_PYOBJECT, GObject.TYPE_STRING)
self.set_model(self.__model)
self.__model.connect("row-changed", self.__emit_changed_delay_cb, "row-changed")
self.__model.connect("row-deleted", self.__emit_changed_delay_cb, "row-deleted")
self.__model.connect("row-inserted", self.__emit_changed_delay_cb, "row-inserted")
self.__model.connect("rows-reordered", self.__emit_changed_delay_cb, "rows-reordered")
self.__model.set_default_sort_func(self.__sort_engines, None)
self.__model.set_sort_column_id(-1, Gtk.SortType.ASCENDING)
# create im name & icon column
column = Gtk.TreeViewColumn(_("Input Method"))
column.set_min_width(220)
renderer = Gtk.CellRendererPixbuf()
renderer.set_property("xalign", 0)
column.pack_start(renderer, False)
column.set_cell_data_func(renderer, self.__icon_cell_data_cb)
renderer = Gtk.CellRendererText()
renderer.set_property("xalign", 0)
renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
column.pack_start(renderer, True)
column.set_cell_data_func(renderer, self.__name_cell_data_cb)
self.append_column(column)
# create im keyboard layout column
renderer = Gtk.CellRendererCombo()
model = Gtk.ListStore(GObject.TYPE_STRING)
model.append(("us",))
model.append(("jp",))
model.append(("xkb",))
renderer.set_property("xalign", 0)
renderer.set_property("model", model)
renderer.set_property("text-column", 0)
renderer.set_property("has-entry", False)
renderer.set_property("editable", True)
column = Gtk.TreeViewColumn(_("Kbd"))
column.set_expand(False)
column.set_fixed_width(32)
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
column.pack_start(renderer, False)
column.set_cell_data_func(renderer, self.__layout_cell_data_cb)
# self.append_column(column)
self.get_selection().connect("changed", self.__selection_changed_cb)
def __sort_engines(self, model, a, b, data):
engine_a = model[a][0]
engine_b = model[b][0]
language_a = IBus.get_language_name(engine_a.get_language())
language_b = IBus.get_language_name(engine_b.get_language())
longname_a = i18n.gettext_engine_longname(engine_a)
longname_b = i18n.gettext_engine_longname(engine_b)
label_a = "%s - %s" % (language_a, longname_a)
label_b = "%s - %s" % (language_b, longname_b)
# http://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
return (label_a > label_b) - (label_a < label_b)
def __selection_changed_cb(self, *args):
self.notify("active-engine");
def __emit_changed(self, *args):
if self.__changed:
self.__changed = False
self.notify("engines")
def __emit_changed_delay_cb(self, *args):
if not self.__changed:
self.__changed = True
GLib.idle_add(self.__emit_changed)
def __icon_cell_data_cb(self, celllayout, renderer, model, it, data):
engine = self.__model.get_value(it, 0)
# When append_engine() is called, self.__model.append(None)
# is called internally and engine == None could happen in
# a slow system.
if engine == None:
return
icon_size = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)[0]
pixbuf = load_icon(engine.get_icon(), Gtk.IconSize.LARGE_TOOLBAR)
renderer.set_property("pixbuf", pixbuf)
def __name_cell_data_cb(self, celllayout, renderer, model, it, data):
engine = self.__model.get_value(it, 0)
# When append_engine() is called, self.__model.append(None)
# is called internally and engine == None could happen in
# a slow system.
if engine == None:
return
renderer.set_property("sensitive", True)
language = IBus.get_language_name(engine.get_language())
longname = i18n.gettext_engine_longname(engine)
renderer.set_property("text",
"%s - %s" % (language, longname))
renderer.set_property("weight", Pango.Weight.NORMAL)
def __layout_cell_data_cb(self, celllayout, renderer, model, it, data):
engine = self.__model.get_value(it, 0)
layout = self.__model.get_value(it, 1)
renderer.set_property("sensitive", True)
if not layout:
layout = engine.layout
renderer.set_property("text", layout)
renderer.set_property("weight", Pango.Weight.NORMAL)
def do_get_property(self, prop):
if prop.name == "active-engine":
it = self.get_selected_iter()
if it == None:
return None
row = self.__model.get(it, 0)
return row[0]
elif prop.name == "engines":
engines = [ r[0] for r in self.__model if r[0] != None]
return engines
else:
raise AttributeError('unknown property %s' % prop.name)
def do_set_property(self, prop, value):
if prop.name == "active-engine":
raise AttributeError("active-engine is readonly")
elif prop.name == "engines":
set_engines(value)
else:
raise AttributeError('unknown property %s' % prop.name)
def set_engines(self, engines):
self.__model.clear()
self.__engines = []
for e in engines:
if e in self.__engines:
continue
it = self.__model.append(None)
i18n.init_textdomain(e.get_textdomain())
self.__model.set(it, 0, e)
self.__engines.append(e)
self.__emit_changed()
def get_selected_iter(self):
selection = self.get_selection()
if selection:
return selection.get_selected()[1]
def get_engines(self):
return self.__engines
def get_sorted_engines(self):
return self.get_property("engines")
def get_active_engine(self):
return self.get_property("active-engine")
def prepend_engine(self, engine):
if engine == None or engine in self.__engines:
return
it = self.__model.prepend(None)
self.__model.set(it, 0, engine)
self.__engines = [engine] + self.__engines
def append_engine(self, engine):
if engine == None or engine in self.__engines:
return
it = self.__model.append(None)
self.__model.set(it, 0, engine)
self.__engines.append(engine)
def remove_engine(self):
it = self.get_selected_iter()
if it == None:
return
row = self.__model[it]
engine = row[0]
self.__engines.remove(engine)
index = row.path.get_indices()[0]
self.__model.remove(it)
try:
row = self.__model[index]
selection = self.get_selection()
selection.select_path(row.path)
except:
pass
def move_up_engine(self):
it = self.get_selected_iter()
if it == None:
return
row = self.__model[it]
index = row.path.get_indices()[0]
if index == 0:
return
self.__model.swap(it, self.__model[index - 1].iter)
self.scroll_to_cell(row.path, None)
def move_down_engine(self):
it = self.get_selected_iter()
if it == None:
return
row = self.__model[it]
index = row.path.get_indices()[0]
last_row = self.__model[-1]
last_index = last_row.path.get_indices()[0]
if index == last_index:
return
self.__model.swap(it, self.__model[index + 1].iter)
self.scroll_to_cell(row.path, None)
GObject.type_register(EngineTreeView)
if __name__ == "__main__":
tree = EngineTreeView()
tree.set_engines([IBus.EngineDesc(language="zh")])
w = Gtk.Window()
w.add(tree)
w.show_all()
Gtk.main()