summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfujiwarat <takao.fujiwara1@gmail.com>2015-02-19 19:37:55 +0900
committerfujiwarat <takao.fujiwara1@gmail.com>2015-02-19 19:37:55 +0900
commitaab56c25356e3d036db8d3ffcef272a2b69c17d3 (patch)
treec3eb84214aada76b4d7cd0bcd0fdfc277f4cbf58
parente6c883269e00e2432109c639d696491034dde5d5 (diff)
downloadibus-aab56c25356e3d036db8d3ffcef272a2b69c17d3.tar.gz
Add ibus panel icon for plasma-desktop in KDE5
plasma-desktop does not provide notification area for GtkStatusIcon (QSystemTrayIcon too). KDE5 provides AppIndicator or KStatusNotifierItem. KStatusNotifierItem uses QT5 code and C++. AppIndicator is an instance using dbus methods of KStatusNotifierItem and it supports GTK3. This implements the dbus method names directly so that activate menu is supported. http://blog.martin-graesslin.com/blog/2014/06/where-are-my-systray-icons/ https://lists.fedoraproject.org/pipermail/devel/2014-June/199782.html http://comments.gmane.org/gmane.comp.kde.devel.core/81971 BUG=https://code.google.com/p/ibus/issues/detail?id=1749 TEST=ui/appindicator/ibus-ui-appindicator Review URL: https://codereview.appspot.com/189680044
-rw-r--r--configure.ac76
-rw-r--r--src/ibusservice.c5
-rw-r--r--ui/gtk3/Makefile.am24
-rw-r--r--ui/gtk3/indicator.vala422
-rw-r--r--ui/gtk3/notification-item.xml63
-rw-r--r--ui/gtk3/notification-watcher.xml30
-rw-r--r--ui/gtk3/panel.vala191
7 files changed, 740 insertions, 71 deletions
diff --git a/configure.ac b/configure.ac
index 14afa574..61fbb77f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,8 +2,8 @@
#
# ibus - The Input Bus
#
-# Copyright (c) 2007-2014 Peng Huang <shawn.p.huang@gmail.com>
-# Copyright (c) 2007-2014 Red Hat, Inc.
+# Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2007-2015 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
@@ -206,6 +206,15 @@ AC_ARG_ENABLE(wayland,
)
AM_CONDITIONAL([ENABLE_WAYLAND], [test x"$enable_wayland" = x"yes"])
+# --enable-appindicator option.
+AC_ARG_ENABLE(appindicator,
+ AS_HELP_STRING([--enable-appindicator],
+ [Build appindicator support]),
+ [enable_appindicator=$enableval],
+ [enable_appindicator=yes]
+)
+AM_CONDITIONAL([ENABLE_APPINDICATOR], [test x"$enable_appindicator" = x"yes"])
+
if test x"$enable_gtk2" = x"yes" -o x"$enable_xim" = x"yes" ; then
# check for gtk2
PKG_CHECK_MODULES(GTK2, [
@@ -261,6 +270,10 @@ else
enable_wayland="no (disabled, use --enable-wayland to enable)"
fi
+if test x"$enable_appindicator" = x"yes"; then
+ enable_appindicator="yes (enabled, use --disable-appindicator to disable)"
+fi
+
# GObject introspection
GOBJECT_INTROSPECTION_CHECK([0.6.8])
@@ -612,34 +625,35 @@ tools/Makefile
AC_OUTPUT
AC_MSG_RESULT([
Build options:
- Version $IBUS_VERSION
- Install prefix $prefix
- Build shared libs $enable_shared
- Build static libs $enable_static
- CFLAGS $CFLAGS
- PYTHON $PYTHON
- PYTHON2 $PYTHON2
- Gtk2 immodule dir $GTK2_IM_MODULEDIR
- Gtk3 immodule dir $GTK3_IM_MODULEDIR
- Build gtk2 immodule $enable_gtk2
- Build gtk3 immodule $enable_gtk3
- Build XIM agent server $enable_xim
- Build wayland support $enable_wayland
- Build python library $enable_python_library
- Build gconf modules $enable_gconf
- Build memconf modules $enable_memconf
- Build dconf modules $enable_dconf
- Build introspection $found_introspection
- IBus-1.0.gir scannerflags "$IBUS_GIR_SCANNERFLAGS"
- Build vala binding $enable_vala
- Build document $enable_gtk_doc
- Build UI $enable_ui
- Build engine $enable_engine
- Enable key snooper $enable_key_snooper
- No snooper regexes "$NO_SNOOPER_APPS"
- Panel icon "$IBUS_ICON_KEYBOARD"
- Enable surrounding-text $enable_surrounding_text
- Enable libnotify $enable_libnotify
- Run test cases $enable_tests
+ Version $IBUS_VERSION
+ Install prefix $prefix
+ Build shared libs $enable_shared
+ Build static libs $enable_static
+ CFLAGS $CFLAGS
+ PYTHON $PYTHON
+ PYTHON2 $PYTHON2
+ Gtk2 immodule dir $GTK2_IM_MODULEDIR
+ Gtk3 immodule dir $GTK3_IM_MODULEDIR
+ Build gtk2 immodule $enable_gtk2
+ Build gtk3 immodule $enable_gtk3
+ Build XIM agent server $enable_xim
+ Build wayland support $enable_wayland
+ Build appindicator support $enable_appindicator
+ Build python library $enable_python_library
+ Build gconf modules $enable_gconf
+ Build memconf modules $enable_memconf
+ Build dconf modules $enable_dconf
+ Build introspection $found_introspection
+ IBus-1.0.gir scannerflags "$IBUS_GIR_SCANNERFLAGS"
+ Build vala binding $enable_vala
+ Build document $enable_gtk_doc
+ Build UI $enable_ui
+ Build engine $enable_engine
+ Enable key snooper $enable_key_snooper
+ No snooper regexes "$NO_SNOOPER_APPS"
+ Panel icon "$IBUS_ICON_KEYBOARD"
+ Enable surrounding-text $enable_surrounding_text
+ Enable libnotify $enable_libnotify
+ Run test cases $enable_tests
])
diff --git a/src/ibusservice.c b/src/ibusservice.c
index d0ef4e04..2199d61d 100644
--- a/src/ibusservice.c
+++ b/src/ibusservice.c
@@ -1,8 +1,8 @@
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
/* vim:set et sts=4: */
/* ibus - The Input Bus
- * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
- * Copyright (C) 2008-2010 Red Hat, Inc.
+ * Copyright (C) 2008-2015 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2008-2015 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
@@ -502,7 +502,6 @@ ibus_service_register (IBusService *service,
{
g_return_val_if_fail (IBUS_IS_SERVICE (service), FALSE);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
- g_return_val_if_fail (connection != service->priv->connection || service->priv->constructed == FALSE, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
GArray *array = NULL;
diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
index 2a4da5e9..40cce117 100644
--- a/ui/gtk3/Makefile.am
+++ b/ui/gtk3/Makefile.am
@@ -2,8 +2,8 @@
#
# ibus - The Input Bus
#
-# Copyright (c) 2007-2013 Peng Huang <shawn.p.huang@gmail.com>
-# Copyright (c) 2007-2013 Red Hat, Inc.
+# Copyright (c) 2007-2015 Peng Huang <shawn.p.huang@gmail.com>
+# Copyright (c) 2007-2015 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
@@ -94,6 +94,10 @@ AM_VALAFLAGS += \
$(NULL)
endif
+if ENABLE_APPINDICATOR
+AM_VALAFLAGS += --define=INDICATOR
+endif
+
libexec_PROGRAMS = ibus-ui-gtk3
ibus_ui_gtk3_SOURCES = \
@@ -102,6 +106,7 @@ ibus_ui_gtk3_SOURCES = \
candidatepanel.vala \
handle.vala \
iconwidget.vala \
+ indicator.vala \
keybindingmanager.vala \
panel.vala \
pango.vala \
@@ -110,18 +115,33 @@ ibus_ui_gtk3_SOURCES = \
separator.vala \
switcher.vala \
xkblayout.vala \
+ gen-notification-item.xml.c \
+ gen-notification-watcher.xml.c \
$(NULL)
ibus_ui_gtk3_LDADD = \
$(AM_LDADD) \
$(NULL)
+gen-%.xml.c: %.xml
+ echo "Building $@ from $<"
+ echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@
+ sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@
+ echo ";" >> $@
+
CLEANFILES = \
gtkpanel.xml \
$(NULL)
+# References:
+# libappindicator/src/notification-item.xml
+# libappindicator/src/notification-watcher.xml
+# knotifications/src/org.kde.StatusNotifierItem.xml
+# knotifications/src/org.kde.StatusNotifierWatcher.xml
EXTRA_DIST = \
gtkpanel.xml.in \
+ notification-item.xml \
+ notification-watcher.xml \
$(NULL)
diff --git a/ui/gtk3/indicator.vala b/ui/gtk3/indicator.vala
new file mode 100644
index 00000000..013b1c48
--- /dev/null
+++ b/ui/gtk3/indicator.vala
@@ -0,0 +1,422 @@
+/* vim:set et sts=4 sw=4:
+ *
+ * ibus - The Input Bus
+ *
+ * Copyright(c) 2015 Takao Fujiwara <takao.fujiwara1@gmail.com>
+ * Copyright(c) 2015 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
+ */
+
+/* This class extends AppIndicator because
+ * AppIndicator misses "Activate" dbus method in the definition
+ * for left click on the indicator.
+ */
+
+public extern string _notification_item;
+public extern string _notification_watcher;
+
+class Indicator : IBus.Service
+{
+ public string id { get; construct; }
+ public string category_s { get; construct; }
+ public string status_s { get; set; }
+ public string icon_name { get; set; }
+ public string icon_desc { get; set; }
+ public string attention_icon_name { get; set; }
+ public string attention_icon_desc { get; set; }
+ public string title { get; set; }
+ public string icon_theme_path { get; set; }
+ public bool connected { get; set; }
+ public string label_s { get; set; }
+ public string label_guide_s { get; set; }
+ public uint32 ordering_index { get; set; }
+
+ public enum Category {
+ APPLICATION_STATUS,
+ COMMUNICATIONS,
+ SYSTEM_SERVICES,
+ HARDWARE,
+ OTHER;
+
+ public string to_nick() {
+ switch(this) {
+ case APPLICATION_STATUS: return "ApplicationStatus";
+ case COMMUNICATIONS: return "Communications";
+ case SYSTEM_SERVICES: return "SystemServices";
+ case HARDWARE: return "Hardware";
+ case OTHER: return "Other";
+ default: assert_not_reached();
+ }
+ }
+ }
+
+ public enum Status {
+ PASSIVE,
+ ACTIVE,
+ ATTENTION;
+
+ public string to_nick() {
+ switch(this) {
+ case PASSIVE: return "Passive";
+ case ACTIVE: return "Active";
+ case ATTENTION: return "NeedsAttention";
+ default: assert_not_reached();
+ }
+ }
+ }
+
+ private const string DEFAULT_ITEM_PATH = "/org/ayatana/NotificationItem";
+ private const string NOTIFICATION_ITEM_DBUS_IFACE =
+ "org.kde.StatusNotifierItem";
+ private const string NOTIFICATION_WATCHER_DBUS_IFACE =
+ "org.kde.StatusNotifierWatcher";
+ private const string NOTIFICATION_WATCHER_DBUS_ADDR =
+ "org.kde.StatusNotifierWatcher";
+ private const string NOTIFICATION_WATCHER_DBUS_OBJ =
+ "/StatusNotifierWatcher";
+
+ private GLib.DBusNodeInfo m_watcher_node_info;
+ private unowned GLib.DBusInterfaceInfo m_watcher_interface_info;
+ private GLib.DBusProxy m_proxy;
+ private int m_context_menu_x;
+ private int m_context_menu_y;
+ private int m_activate_menu_x;
+ private int m_activate_menu_y;
+
+ public Indicator(string id,
+ GLib.DBusConnection connection,
+ Category category = Category.OTHER) {
+ string path = DEFAULT_ITEM_PATH + "/" + id;
+ path = path.delimit("-", '_');
+
+ // AppIndicator.set_category() converts enum value to string internally.
+ GLib.Object(object_path: path,
+ id: id,
+ connection: connection,
+ category_s: category.to_nick());
+ this.status_s = Status.PASSIVE.to_nick();
+ this.icon_name = "";
+ this.icon_desc = "";
+ this.title = "";
+ this.icon_theme_path = "";
+ this.attention_icon_name = "";
+ this.attention_icon_desc = "";
+ this.label_s = "";
+ this.label_guide_s = "";
+ unregister(connection);
+ add_interfaces(_notification_item);
+ try {
+ if (!register(connection))
+ return;
+ } catch (GLib.Error e) {
+ warning("Failed to register the application indicator xml: " +
+ e.message);
+ return;
+ }
+
+ try {
+ m_watcher_node_info =
+ new GLib.DBusNodeInfo.for_xml(_notification_watcher);
+ } catch (GLib.Error e) {
+ warning("Failed to create dbus node info: " + e.message);
+ return;
+ }
+ m_watcher_interface_info =
+ m_watcher_node_info.lookup_interface(
+ NOTIFICATION_WATCHER_DBUS_IFACE);
+ check_connect();
+ }
+
+ private void check_connect() {
+ if (m_proxy == null) {
+ GLib.DBusProxy.new.begin(
+ connection,
+ GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
+ GLib.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
+ m_watcher_interface_info,
+ NOTIFICATION_WATCHER_DBUS_ADDR,
+ NOTIFICATION_WATCHER_DBUS_OBJ,
+ NOTIFICATION_WATCHER_DBUS_IFACE,
+ null,
+ (obj, res) => {
+ bus_watcher_ready(obj, res);
+ });
+ } else {
+ bus_watcher_ready(null, null);
+ }
+ }
+
+ private void bus_watcher_ready(GLib.Object? obj, GLib.AsyncResult? res) {
+ if (res != null) {
+ try {
+ m_proxy = GLib.DBusProxy.new.end(res);
+ } catch (GLib.IOError e) {
+ warning("Failed to call dbus proxy: " + e.message);
+ return;
+ }
+
+ m_proxy.notify["g-name-owner"].connect((obj, pspec) => {
+ var name = m_proxy.get_name_owner();
+ if (name != null)
+ check_connect();
+ });
+ }
+
+ var name = m_proxy.get_name_owner();
+ // KDE panel does not run yet if name == null
+ if (name == null)
+ return;
+
+ m_proxy.call.begin("RegisterStatusNotifierItem",
+ new GLib.Variant("(s)", this.object_path),
+ GLib.DBusCallFlags.NONE,
+ -1,
+ null,
+ (p_obj, p_res) => {
+ try {
+ m_proxy.call.end(p_res);
+ registered_status_notifier_item();
+ } catch (GLib.Error e) {
+ warning("Failed to call " +
+ "RegisterStatusNotifierItem: " +
+ e.message);
+ }
+ });
+ }
+
+ private void _context_menu_cb(GLib.DBusConnection connection,
+ GLib.Variant parameters,
+ GLib.DBusMethodInvocation invocation) {
+ GLib.Variant var_x = parameters.get_child_value(0);
+ GLib.Variant var_y = parameters.get_child_value(1);
+ m_context_menu_x = var_x.get_int32();
+ m_context_menu_y = var_y.get_int32();
+ context_menu(2, 0);
+ }
+
+ private void _activate_menu_cb(GLib.DBusConnection connection,
+ GLib.Variant parameters,
+ GLib.DBusMethodInvocation invocation) {
+ GLib.Variant var_x = parameters.get_child_value(0);
+ GLib.Variant var_y = parameters.get_child_value(1);
+ m_activate_menu_x = var_x.get_int32();
+ m_activate_menu_y = var_y.get_int32();
+ activate();
+ }
+
+ private GLib.Variant _get_id(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.id);
+ }
+
+ private GLib.Variant _get_category(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.category_s);
+ }
+
+ private GLib.Variant _get_status(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.status_s);
+ }
+
+ private GLib.Variant _get_icon_name(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.icon_name);
+ }
+
+ private GLib.Variant _get_icon_desc(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.icon_desc);
+ }
+
+ private GLib.Variant _get_attention_icon_name(GLib.DBusConnection
+ connection) {
+ return new GLib.Variant.string(this.attention_icon_name);
+ }
+
+ private GLib.Variant _get_attention_icon_desc(GLib.DBusConnection
+ connection) {
+ return new GLib.Variant.string(this.attention_icon_desc);
+ }
+
+ private GLib.Variant _get_title(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.title);
+ }
+
+ private GLib.Variant _get_icon_theme_path(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.icon_theme_path);
+ }
+
+ private GLib.Variant? _get_menu(GLib.DBusConnection connection) {
+ return null;
+ }
+
+ private GLib.Variant _get_xayatana_label(GLib.DBusConnection connection) {
+ return new GLib.Variant.string(this.label_s);
+ }
+
+ private GLib.Variant _get_xayatana_label_guide(GLib.DBusConnection
+ connection) {
+ return new GLib.Variant.string(this.label_guide_s);
+ }
+
+ private GLib.Variant _get_xayatana_ordering_index(GLib.DBusConnection
+ connection) {
+ return new GLib.Variant.uint32(this.ordering_index);
+ }
+
+ public override void service_method_call(GLib.DBusConnection
+ connection,
+ string sender,
+ string object_path,
+ string interface_name,
+ string method_name,
+ GLib.Variant parameters,
+ GLib.DBusMethodInvocation
+ invocation) {
+ GLib.return_if_fail (object_path == this.object_path);
+ GLib.return_if_fail (interface_name == NOTIFICATION_ITEM_DBUS_IFACE);
+
+ if (method_name == "Activate") {
+ _activate_menu_cb(connection, parameters, invocation);
+ return;
+ }
+ if (method_name == "ContextMenu") {
+ _context_menu_cb(connection, parameters, invocation);
+ return;
+ }
+
+ warning("service_method_call() does not handle the method: " +
+ method_name);
+ }
+
+ public override GLib.Variant service_get_property(GLib.DBusConnection
+ connection,
+ string sender,
+ string object_path,
+ string interface_name,
+ string property_name) {
+ GLib.return_val_if_fail (object_path == this.object_path, null);
+ GLib.return_val_if_fail (
+ interface_name == NOTIFICATION_ITEM_DBUS_IFACE,
+ null);
+
+ if (property_name == "Id")
+ return _get_id(connection);
+ if (property_name == "Category")
+ return _get_category(connection);
+ if (property_name == "Status")
+ return _get_status(connection);
+ if (property_name == "IconName")
+ return _get_icon_name(connection);
+ if (property_name == "IconAccessibleDesc")
+ return _get_icon_desc(connection);
+ if (property_name == "AttentionIconName")
+ return _get_attention_icon_name(connection);
+ if (property_name == "AttentionAccessibleDesc")
+ return _get_attention_icon_desc(connection);
+ if (property_name == "Title")
+ return _get_title(connection);
+ if (property_name == "IconThemePath")
+ return _get_icon_theme_path(connection);
+ if (property_name == "Menu")
+ return _get_menu(connection);
+ if (property_name == "XAyatanaLabel")
+ return _get_xayatana_label(connection);
+ if (property_name == "XAyatanaLabelGuide")
+ return _get_xayatana_label_guide(connection);
+ if (property_name == "XAyatanaOrderingIndex")
+ return _get_xayatana_ordering_index(connection);
+
+ warning("service_get_property() does not handle the property: " +
+ property_name);
+
+ return null;
+ }
+
+ public override bool service_set_property(GLib.DBusConnection
+ connection,
+ string sender,
+ string object_path,
+ string interface_name,
+ string property_name,
+ GLib.Variant value) {
+ return false;
+ }
+
+ // AppIndicator.set_status() converts enum value to string internally.
+ public void set_status(Status status) {
+ string status_s = status.to_nick();
+ if (this.status_s == status_s)
+ return;
+ this.status_s = status_s;
+ if (this.connection == null)
+ return;
+ try {
+ this.connection.emit_signal(null,
+ this.object_path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewStatus",
+ new GLib.Variant("(s)", status_s));
+ } catch(GLib.Error e) {
+ warning("Unable to send signal for NewIcon: %s", e.message);
+ }
+ }
+
+ // AppIndicator.set_icon() is deprecated.
+ public void set_icon_full(string icon_name, string? icon_desc) {
+ bool changed = false;
+ if (this.icon_name != icon_name) {
+ this.icon_name = icon_name;
+ changed = true;
+ }
+ if (this.icon_desc != icon_desc) {
+ this.icon_desc = icon_desc;
+ changed = true;
+ }
+ if (!changed)
+ return;
+ if (this.connection == null)
+ return;
+ try {
+ this.connection.emit_signal(null,
+ this.object_path,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ "NewIcon",
+ null);
+ } catch(GLib.Error e) {
+ warning("Unable to send signal for NewIcon: %s", e.message);
+ }
+ }
+
+ public void position_context_menu(Gtk.Menu menu,
+ out int x,
+ out int y,
+ out bool push_in) {
+ x = m_context_menu_x;
+ y = m_context_menu_y;
+ push_in = false;
+ }
+
+ public void position_activate_menu(Gtk.Menu menu,
+ out int x,
+ out int y,
+ out bool push_in) {
+ x = m_activate_menu_x;
+ y = m_activate_menu_y;
+ push_in = false;
+ }
+
+ public signal void context_menu(uint button, uint activate_time);
+ public signal void activate();
+ public signal void registered_status_notifier_item();
+}
diff --git a/ui/gtk3/notification-item.xml b/ui/gtk3/notification-item.xml
new file mode 100644
index 00000000..aecb8d9b
--- /dev/null
+++ b/ui/gtk3/notification-item.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/StatusNotifierItem">
+ <interface name="org.kde.StatusNotifierItem">
+
+<!-- Properties -->
+ <property name="Id" type="s" access="read" />
+ <property name="Category" type="s" access="read" />
+ <property name="Status" type="s" access="read" />
+ <property name="IconName" type="s" access="read" />
+ <property name="IconAccessibleDesc" type="s" access="read" />
+ <property name="AttentionIconName" type="s" access="read" />
+ <property name="AttentionAccessibleDesc" type="s" access="read" />
+ <property name="Title" type="s" access="read" />
+ <!-- An additional path to add to the theme search path
+ to find the icons specified above. -->
+ <property name="IconThemePath" type="s" access="read" />
+ <property name="Menu" type="o" access="read" />
+ <property name="XAyatanaLabel" type="s" access="read" />
+ <property name="XAyatanaLabelGuide" type="s" access="read" />
+ <property name="XAyatanaOrderingIndex" type="u" access="read" />
+
+<!-- Methods -->
+ <method name="Scroll">
+ <arg type="i" name="delta" direction="in" />
+ <arg type="s" name="orientation" direction="in" />
+ </method>
+ <method name="SecondaryActivate">
+ <arg type="i" name="x" direction="in" />
+ <arg type="i" name="y" direction="in" />
+ </method>
+ <method name="XAyatanaSecondaryActivate">
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+ <!-- Activate is missed in AppIndicator -->
+ <method name="Activate">
+ <arg type="i" name="x" direction="in" />
+ <arg type="i" name="y" direction="in" />
+ </method>
+ <method name="ContextMenu">
+ <arg type="i" name="x" direction="in"/>
+ <arg type="i" name="y" direction="in"/>
+ </method>
+
+<!-- Signals -->
+ <signal name="NewIcon">
+ </signal>
+ <signal name="NewIconThemePath">
+ <arg type="s" name="icon_theme_path" direction="out" />
+ </signal>
+ <signal name="NewAttentionIcon">
+ </signal>
+ <signal name="NewStatus">
+ <arg type="s" name="status" direction="out" />
+ </signal>
+ <signal name="XAyatanaNewLabel">
+ <arg type="s" name="label" direction="out" />
+ <arg type="s" name="guide" direction="out" />
+ </signal>
+ <signal name="NewTitle">
+ </signal>
+
+ </interface>
+</node>
diff --git a/ui/gtk3/notification-watcher.xml b/ui/gtk3/notification-watcher.xml
new file mode 100644
index 00000000..0c28a8f8
--- /dev/null
+++ b/ui/gtk3/notification-watcher.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/StatusNotifierWatcher">
+ <interface name="org.kde.StatusNotifierWatcher">
+
+<!-- Properties -->
+ <property name="ProtocolVersion" type="i" access="read" />
+ <property name="IsStatusNotifierHostRegistered" type="b" access="read" />
+ <property name="RegisteredStatusNotifierItems" type="as" access="read" />
+
+<!-- Methods -->
+ <method name="RegisterStatusNotifierItem">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true" />
+ <arg type="s" name="service" direction="in" />
+ </method>
+ <method name="RegisterStatusNotifierHost">
+ <arg type="s" name="service" direction="in" />
+ </method>
+
+<!-- Signals -->
+ <signal name="StatusNotifierItemRegistered">
+ <arg type="s" name="service" direction="out" />
+ </signal>
+ <signal name="StatusNotifierItemUnregistered">
+ <arg type="s" name="service" direction="out" />
+ </signal>
+ <signal name="StatusNotifierHostRegistered">
+ </signal>
+
+ </interface>
+</node>
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index b9b8e690..13798608 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -3,6 +3,7 @@
* ibus - The Input Bus
*
* Copyright(c) 2011-2014 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright(c) 2015 Takao Fujwiara <takao.fujiwara1@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -35,10 +36,20 @@ class Panel : IBus.PanelService {
public bool reverse { get; set; }
}
+ private enum IconType {
+ STATUS_ICON,
+ INDICATOR,
+ }
+
private IBus.Bus m_bus;
private GLib.Settings m_settings_general = null;
private GLib.Settings m_settings_hotkey = null;
private GLib.Settings m_settings_panel = null;
+ private IconType m_icon_type = IconType.STATUS_ICON;
+ private Indicator m_indicator;
+#if INDICATOR
+ private GLib.DBusConnection m_session_bus_connection;
+#endif
private Gtk.StatusIcon m_status_icon;
private Gtk.Menu m_ime_menu;
private Gtk.Menu m_sys_menu;
@@ -81,12 +92,15 @@ class Panel : IBus.PanelService {
init_settings();
// init ui
- m_status_icon = new Gtk.StatusIcon();
- m_status_icon.set_name("ibus-ui-gtk");
- m_status_icon.set_title("IBus Panel");
- m_status_icon.popup_menu.connect(status_icon_popup_menu_cb);
- m_status_icon.activate.connect(status_icon_activate_cb);
- m_status_icon.set_from_icon_name("ibus-keyboard");
+#if INDICATOR
+ if (is_kde()) {
+ init_indicator();
+ } else {
+ init_status_icon();
+ }
+#else
+ init_status_icon();
+#endif
m_candidate_panel = new CandidatePanel();
m_candidate_panel.page_up.connect((w) => this.page_up());
@@ -190,6 +204,75 @@ class Panel : IBus.PanelService {
});
}
+#if INDICATOR
+ private bool is_kde() {
+ if (Environment.get_variable("XDG_CURRENT_DESKTOP") == "KDE")
+ return true;
+ warning ("If you launch KDE5 on xterm, " +
+ "export XDG_CURRENT_DESKTOP=KDE before launch KDE5.");
+ return false;
+ }
+
+ private void init_indicator() {
+ m_icon_type = IconType.INDICATOR;
+ GLib.Bus.get.begin(GLib.BusType.SESSION, null, (obj, res) => {
+ try {
+ m_session_bus_connection = GLib.Bus.get.end(res);
+ m_indicator =
+ new Indicator("ibus-ui-gtk3",
+ m_session_bus_connection,
+ Indicator.Category.APPLICATION_STATUS);
+ m_indicator.title = _("IBus Panel");
+ m_indicator.registered_status_notifier_item.connect(() => {
+ m_indicator.set_status(Indicator.Status.ACTIVE);
+ state_changed();
+ });
+ m_indicator.context_menu.connect((b, t) => {
+ Gtk.Menu menu = create_context_menu();
+ menu.popup(null,
+ null,
+ m_indicator.position_context_menu,
+ 0,
+ Gtk.get_current_event_time());
+ });
+ m_indicator.activate.connect(() => {
+ Gtk.Menu menu = create_activate_menu();
+ menu.popup(null,
+ null,
+ m_indicator.position_activate_menu,
+ 0,
+ Gtk.get_current_event_time());
+ });
+ } catch (GLib.IOError e) {
+ warning("Failed to get the session bus: %s", e.message);
+ }
+ });
+ }
+#endif
+
+ private void init_status_icon() {
+ m_status_icon = new Gtk.StatusIcon();
+ m_status_icon.set_name("ibus-ui-gtk");
+ m_status_icon.set_title(_("IBus Panel"));
+ m_status_icon.popup_menu.connect((b, t) => {
+ Gtk.Menu menu = create_context_menu();
+ menu.popup(null,
+ null,
+ m_status_icon.position_menu,
+ 0,
+ Gtk.get_current_event_time());
+ });
+ m_status_icon.activate.connect(() => {
+ Gtk.Menu menu = create_activate_menu();
+ menu.popup(null,
+ null,
+ m_status_icon.position_menu,
+ 0,
+ Gtk.get_current_event_time());
+ });
+ m_status_icon.set_from_icon_name("ibus-keyboard");
+ }
+
private void keybinding_manager_bind(KeybindingManager keybinding_manager,
string? accelerator) {
uint switch_keysym = 0;
@@ -503,11 +586,22 @@ class Panel : IBus.PanelService {
}
private void set_show_icon_on_systray() {
- if (m_status_icon == null)
- return;
+ if (m_icon_type == IconType.STATUS_ICON) {
+ if (m_status_icon == null)
+ return;
- m_status_icon.set_visible(
- m_settings_panel.get_boolean("show-icon-on-systray"));
+ m_status_icon.set_visible(
+ m_settings_panel.get_boolean("show-icon-on-systray"));
+ } else if (m_icon_type == IconType.INDICATOR) {
+ if (m_indicator == null)
+ return;
+
+ if (m_settings_panel.get_boolean("show-icon-on-systray")) {
+ m_indicator.set_status(Indicator.Status.ACTIVE);
+ } else {
+ m_indicator.set_status(Indicator.Status.PASSIVE);
+ }
+ }
}
private void set_lookup_table_orientation() {
@@ -558,8 +652,13 @@ class Panel : IBus.PanelService {
if (m_xkb_icon_pixbufs.size() > 0) {
m_xkb_icon_pixbufs.remove_all();
- if (m_status_icon != null && m_switcher != null)
- state_changed();
+ if (m_icon_type == IconType.STATUS_ICON) {
+ if (m_status_icon != null && m_switcher != null)
+ state_changed();
+ } else if (m_icon_type == IconType.INDICATOR) {
+ if (m_indicator != null && m_switcher != null)
+ state_changed();
+ }
}
}
@@ -941,9 +1040,7 @@ class Panel : IBus.PanelService {
}
}
- private void status_icon_popup_menu_cb(Gtk.StatusIcon status_icon,
- uint button,
- uint activate_time) {
+ private Gtk.Menu create_context_menu() {
// Show system menu
if (m_sys_menu == null) {
Gtk.MenuItem item;
@@ -970,14 +1067,10 @@ class Panel : IBus.PanelService {
m_sys_menu.show_all();
}
- m_sys_menu.popup(null,
- null,
- m_status_icon.position_menu,
- 0,
- Gtk.get_current_event_time());
+ return m_sys_menu;
}
- private void status_icon_activate_cb(Gtk.StatusIcon status_icon) {
+ private Gtk.Menu create_activate_menu() {
m_ime_menu = new Gtk.Menu();
// Show properties and IME switching menu
@@ -1009,11 +1102,8 @@ class Panel : IBus.PanelService {
// Do not take focuse to avoid some focus related issues.
m_ime_menu.set_take_focus(false);
- m_ime_menu.popup(null,
- null,
- m_status_icon.position_menu,
- 0,
- Gtk.get_current_event_time());
+
+ return m_ime_menu;
}
/* override virtual functions */
@@ -1141,15 +1231,28 @@ class Panel : IBus.PanelService {
if (m_switcher_is_running)
return;
+ if (m_icon_type == IconType.INDICATOR) {
+ // Wait for the callback of the session bus.
+ if (m_indicator == null)
+ return;
+ }
+
var icon_name = "ibus-keyboard";
var engine = m_bus.get_global_engine();
if (engine != null)
icon_name = engine.get_icon();
- if (icon_name[0] == '/')
- m_status_icon.set_from_file(icon_name);
- else {
+ if (icon_name[0] == '/') {
+ if (m_icon_type == IconType.STATUS_ICON) {
+ m_status_icon.set_from_file(icon_name);
+ }
+ else if (m_icon_type == IconType.INDICATOR) {
+ warning("appindicator requires an icon name in a theme " +
+ "path instead of the full path: %s", icon_name);
+ m_indicator.set_icon_full("ibus-engine", "");
+ }
+ } else {
string language = null;
if (engine != null) {
@@ -1159,14 +1262,32 @@ class Panel : IBus.PanelService {
}
if (language != null) {
- Gdk.Pixbuf pixbuf = create_icon_pixbuf_with_string(language);
- m_status_icon.set_from_pixbuf(pixbuf);
+ if (m_icon_type == IconType.STATUS_ICON) {
+ Gdk.Pixbuf pixbuf =
+ create_icon_pixbuf_with_string(language);
+ m_status_icon.set_from_pixbuf(pixbuf);
+ }
+ else if (m_icon_type == IconType.INDICATOR) {
+ /* Appindicator does not support pixbuf. */
+ m_indicator.set_icon_full(icon_name, "");
+ }
} else {
var theme = Gtk.IconTheme.get_default();
- if (theme.lookup_icon(icon_name, 48, 0) != null)
- m_status_icon.set_from_icon_name(icon_name);
- else
- m_status_icon.set_from_icon_name("ibus-engine");
+ if (theme.lookup_icon(icon_name, 48, 0) != null) {
+ if (m_icon_type == IconType.STATUS_ICON) {
+ m_status_icon.set_from_icon_name(icon_name);
+ }
+ else if (m_icon_type == IconType.INDICATOR) {
+ m_indicator.set_icon_full(icon_name, "");
+ }
+ } else {
+ if (m_icon_type == IconType.STATUS_ICON) {
+ m_status_icon.set_from_icon_name("ibus-engine");
+ }
+ else if (m_icon_type == IconType.INDICATOR) {
+ m_indicator.set_icon_full("ibus-engine", "");
+ }
+ }
}
}