diff options
author | fujiwarat <takao.fujiwara1@gmail.com> | 2015-02-19 19:37:55 +0900 |
---|---|---|
committer | fujiwarat <takao.fujiwara1@gmail.com> | 2015-02-19 19:37:55 +0900 |
commit | aab56c25356e3d036db8d3ffcef272a2b69c17d3 (patch) | |
tree | c3eb84214aada76b4d7cd0bcd0fdfc277f4cbf58 /ui | |
parent | e6c883269e00e2432109c639d696491034dde5d5 (diff) | |
download | ibus-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
Diffstat (limited to 'ui')
-rw-r--r-- | ui/gtk3/Makefile.am | 24 | ||||
-rw-r--r-- | ui/gtk3/indicator.vala | 422 | ||||
-rw-r--r-- | ui/gtk3/notification-item.xml | 63 | ||||
-rw-r--r-- | ui/gtk3/notification-watcher.xml | 30 | ||||
-rw-r--r-- | ui/gtk3/panel.vala | 191 |
5 files changed, 693 insertions, 37 deletions
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", ""); + } + } } } |