summaryrefslogtreecommitdiff
path: root/applets/notification_area/system-tray
diff options
context:
space:
mode:
authorColomban Wendling <[email protected]>2017-01-19 18:46:10 +0100
committerlukefromdc <[email protected]>2017-01-23 13:49:34 -0500
commit7d39b2e82f46777efa67224f078c1cec9e827654 (patch)
tree7a415a153f4a3afb52358ed8a34ced08c25bdc5c /applets/notification_area/system-tray
parenta506150684ad2e71b1f70190ee70fe9eda7a4ba9 (diff)
downloadmate-panel-7d39b2e82f46777efa67224f078c1cec9e827654.tar.bz2
mate-panel-7d39b2e82f46777efa67224f078c1cec9e827654.tar.xz
Add StatusNotifier support to the Notification Area applet
The StatusNotifier part of the implementation is based off gnome-panel's status-notifier applet.
Diffstat (limited to 'applets/notification_area/system-tray')
-rw-r--r--applets/notification_area/system-tray/Makefile.am42
-rw-r--r--applets/notification_area/system-tray/fixedtip.c281
-rw-r--r--applets/notification_area/system-tray/fixedtip.h72
-rw-r--r--applets/notification_area/system-tray/na-marshal.list3
-rw-r--r--applets/notification_area/system-tray/na-tray-child.c715
-rw-r--r--applets/notification_area/system-tray/na-tray-child.h78
-rw-r--r--applets/notification_area/system-tray/na-tray-manager.c988
-rw-r--r--applets/notification_area/system-tray/na-tray-manager.h113
-rw-r--r--applets/notification_area/system-tray/na-tray.c730
-rw-r--r--applets/notification_area/system-tray/na-tray.h69
10 files changed, 3091 insertions, 0 deletions
diff --git a/applets/notification_area/system-tray/Makefile.am b/applets/notification_area/system-tray/Makefile.am
new file mode 100644
index 00000000..f3e8c360
--- /dev/null
+++ b/applets/notification_area/system-tray/Makefile.am
@@ -0,0 +1,42 @@
+
+noinst_LTLIBRARIES = libsystem-tray.la
+
+AM_CPPFLAGS = \
+ $(NOTIFICATION_AREA_CFLAGS) \
+ -I$(srcdir) \
+ -I$(srcdir)/.. \
+ -DMATELOCALEDIR=\""$(datadir)/locale"\" \
+ -DG_LOG_DOMAIN=\""notification-area-applet"\" \
+ $(DISABLE_DEPRECATED_CFLAGS)
+
+AM_CFLAGS = $(WARN_CFLAGS)
+
+libsystem_tray_la_SOURCES = \
+ fixedtip.h \
+ fixedtip.c \
+ na-marshal.c \
+ na-marshal.h \
+ na-tray.c \
+ na-tray.h \
+ na-tray-child.c \
+ na-tray-child.h \
+ na-tray-manager.c \
+ na-tray-manager.h
+
+libsystem_tray_la_LIBADD = \
+ $(X_LIBS) \
+ $(NOTIFICATION_AREA_LIBS)
+
+na-marshal.h: na-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) $< --header --prefix=_na_marshal > $@
+
+na-marshal.c: na-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)echo "#include \"na-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=_na_marshal >> $@
+
+BUILT_SOURCES = na-marshal.c na-marshal.h
+
+EXTRA_DIST = \
+ na-marshal.list
+
+-include $(top_srcdir)/git.mk
diff --git a/applets/notification_area/system-tray/fixedtip.c b/applets/notification_area/system-tray/fixedtip.c
new file mode 100644
index 00000000..6356de9b
--- /dev/null
+++ b/applets/notification_area/system-tray/fixedtip.c
@@ -0,0 +1,281 @@
+/* Marco fixed tooltip routine */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "fixedtip.h"
+
+/* Signals */
+enum
+{
+ CLICKED,
+ LAST_SIGNAL
+};
+
+static guint fixedtip_signals[LAST_SIGNAL] = { 0 };
+
+struct _NaFixedTipPrivate
+{
+ GtkWidget *parent;
+ GtkWidget *label;
+ GtkOrientation orientation;
+};
+
+G_DEFINE_TYPE (NaFixedTip, na_fixed_tip, GTK_TYPE_WINDOW)
+
+static gboolean
+button_press_handler (GtkWidget *fixedtip,
+ GdkEventButton *event,
+ gpointer data)
+{
+ if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+ g_signal_emit (fixedtip, fixedtip_signals[CLICKED], 0);
+
+ return FALSE;
+}
+
+static gboolean
+na_fixed_tip_draw (GtkWidget *widget, cairo_t *cr)
+{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ int width, height;
+
+ width = gtk_widget_get_allocated_width (widget);
+ height = gtk_widget_get_allocated_height (widget);
+
+ state = gtk_widget_get_state_flags (widget);
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLTIP);
+ gtk_style_context_set_state (context, state);
+
+ cairo_save (cr);
+ gtk_render_background (context, cr,
+ 0., 0.,
+ (gdouble)width,
+ (gdouble)height);
+ cairo_restore (cr);
+
+ gtk_style_context_restore (context);
+
+ return FALSE;
+}
+
+static void
+na_fixed_tip_class_init (NaFixedTipClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->draw = na_fixed_tip_draw;
+
+ fixedtip_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaFixedTipClass, clicked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (class, sizeof (NaFixedTipPrivate));
+}
+
+/* Did you already see this code? Yes, it's gtk_tooltips_ force_window() ;-) */
+static void
+na_fixed_tip_init (NaFixedTip *fixedtip)
+{
+ GtkWidget *label;
+
+ fixedtip->priv = G_TYPE_INSTANCE_GET_PRIVATE (fixedtip, NA_TYPE_FIXED_TIP,
+ NaFixedTipPrivate);
+
+ gtk_window_set_type_hint (GTK_WINDOW (fixedtip),
+ GDK_WINDOW_TYPE_HINT_TOOLTIP);
+
+ gtk_widget_set_app_paintable (GTK_WIDGET (fixedtip), TRUE);
+ gtk_window_set_resizable (GTK_WINDOW (fixedtip), FALSE);
+ gtk_widget_set_name (GTK_WIDGET (fixedtip), "gtk-tooltips");
+ gtk_container_set_border_width (GTK_CONTAINER (fixedtip), 4);
+
+ label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+#if GTK_CHECK_VERSION (3, 16, 0)
+ gtk_label_set_xalign (GTK_LABEL (label), 0.5);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.5);
+#else
+ gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+#endif
+ gtk_widget_show (label);
+ gtk_container_add (GTK_CONTAINER (fixedtip), label);
+ fixedtip->priv->label = label;
+
+ gtk_widget_add_events (GTK_WIDGET (fixedtip), GDK_BUTTON_PRESS_MASK);
+
+ g_signal_connect (fixedtip, "button_press_event",
+ G_CALLBACK (button_press_handler), NULL);
+
+ fixedtip->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+}
+
+static void
+na_fixed_tip_position (NaFixedTip *fixedtip)
+{
+ GdkScreen *screen;
+ GdkWindow *parent_window;
+ GtkRequisition req;
+ int root_x;
+ int root_y;
+ int parent_width;
+ int parent_height;
+ int screen_width;
+ int screen_height;
+
+ screen = gtk_widget_get_screen (fixedtip->priv->parent);
+ parent_window = gtk_widget_get_window (fixedtip->priv->parent);
+
+ gtk_window_set_screen (GTK_WINDOW (fixedtip), screen);
+
+ gtk_widget_get_preferred_size (GTK_WIDGET (fixedtip), &req, NULL);
+
+ gdk_window_get_origin (parent_window, &root_x, &root_y);
+ parent_width = gdk_window_get_width(parent_window);
+ parent_height = gdk_window_get_height(parent_window);
+
+ screen_width = gdk_screen_get_width (screen);
+ screen_height = gdk_screen_get_height (screen);
+
+ /* pad between panel and message window */
+#define PAD 5
+
+ if (fixedtip->priv->orientation == GTK_ORIENTATION_VERTICAL)
+ {
+ if (root_x <= screen_width / 2)
+ root_x += parent_width + PAD;
+ else
+ root_x -= req.width + PAD;
+ }
+ else
+ {
+ if (root_y <= screen_height / 2)
+ root_y += parent_height + PAD;
+ else
+ root_y -= req.height + PAD;
+ }
+
+ /* Push onscreen */
+ if ((root_x + req.width) > screen_width)
+ root_x = screen_width - req.width;
+
+ if ((root_y + req.height) > screen_height)
+ root_y = screen_height - req.height;
+
+ gtk_window_move (GTK_WINDOW (fixedtip), root_x, root_y);
+}
+
+static void
+na_fixed_tip_parent_size_allocated (GtkWidget *parent,
+ GtkAllocation *allocation,
+ NaFixedTip *fixedtip)
+{
+ na_fixed_tip_position (fixedtip);
+}
+
+static void
+na_fixed_tip_parent_screen_changed (GtkWidget *parent,
+ GdkScreen *new_screen,
+ NaFixedTip *fixedtip)
+{
+ na_fixed_tip_position (fixedtip);
+}
+
+GtkWidget *
+na_fixed_tip_new (GtkWidget *parent,
+ GtkOrientation orientation)
+{
+ NaFixedTip *fixedtip;
+
+ g_return_val_if_fail (parent != NULL, NULL);
+
+ fixedtip = g_object_new (NA_TYPE_FIXED_TIP,
+ "type", GTK_WINDOW_POPUP,
+ NULL);
+
+ fixedtip->priv->parent = parent;
+
+#if 0
+ //FIXME: would be nice to be able to get the toplevel for the tip, but this
+ //doesn't work
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (parent);
+ /*
+ if (toplevel && gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
+ gtk_window_set_transient_for (GTK_WINDOW (fixedtip), GTK_WINDOW (toplevel));
+ */
+#endif
+
+ fixedtip->priv->orientation = orientation;
+
+ //FIXME: would be nice to move the tip when the notification area moves
+ g_signal_connect_object (parent, "size-allocate",
+ G_CALLBACK (na_fixed_tip_parent_size_allocated),
+ fixedtip, 0);
+ g_signal_connect_object (parent, "screen-changed",
+ G_CALLBACK (na_fixed_tip_parent_screen_changed),
+ fixedtip, 0);
+
+ na_fixed_tip_position (fixedtip);
+
+ return GTK_WIDGET (fixedtip);
+}
+
+void
+na_fixed_tip_set_markup (GtkWidget *widget,
+ const char *markup_text)
+{
+ NaFixedTip *fixedtip;
+
+ g_return_if_fail (NA_IS_FIXED_TIP (widget));
+
+ fixedtip = NA_FIXED_TIP (widget);
+
+ gtk_label_set_markup (GTK_LABEL (fixedtip->priv->label),
+ markup_text);
+
+ na_fixed_tip_position (fixedtip);
+}
+
+void
+na_fixed_tip_set_orientation (GtkWidget *widget,
+ GtkOrientation orientation)
+{
+ NaFixedTip *fixedtip;
+
+ g_return_if_fail (NA_IS_FIXED_TIP (widget));
+
+ fixedtip = NA_FIXED_TIP (widget);
+
+ if (orientation == fixedtip->priv->orientation)
+ return;
+
+ fixedtip->priv->orientation = orientation;
+
+ na_fixed_tip_position (fixedtip);
+}
diff --git a/applets/notification_area/system-tray/fixedtip.h b/applets/notification_area/system-tray/fixedtip.h
new file mode 100644
index 00000000..c220f103
--- /dev/null
+++ b/applets/notification_area/system-tray/fixedtip.h
@@ -0,0 +1,72 @@
+/* Fixed tooltip routine */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc.
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef FIXED_TIP_H
+#define FIXED_TIP_H
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NA_TYPE_FIXED_TIP (na_fixed_tip_get_type ())
+#define NA_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_FIXED_TIP, NaFixedTip))
+#define NA_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_FIXED_TIP, NaFixedTipClass))
+#define NA_IS_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_FIXED_TIP))
+#define NA_IS_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_FIXED_TIP))
+#define NA_FIXED_TIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_FIXED_TIP, NaFixedTipClass))
+
+typedef struct _NaFixedTip NaFixedTip;
+typedef struct _NaFixedTipPrivate NaFixedTipPrivate;
+typedef struct _NaFixedTipClass NaFixedTipClass;
+
+struct _NaFixedTip
+{
+ GtkWindow parent_instance;
+
+ NaFixedTipPrivate *priv;
+};
+
+struct _NaFixedTipClass
+{
+ GtkWindowClass parent_class;
+
+ void (* clicked) (NaFixedTip *fixedtip);
+};
+
+GType na_fixed_tip_get_type (void);
+
+GtkWidget *na_fixed_tip_new (GtkWidget *parent,
+ GtkOrientation orientation);
+
+void na_fixed_tip_set_markup (GtkWidget *widget,
+ const char *markup_text);
+
+void na_fixed_tip_set_orientation (GtkWidget *widget,
+ GtkOrientation orientation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FIXED_TIP_H */
diff --git a/applets/notification_area/system-tray/na-marshal.list b/applets/notification_area/system-tray/na-marshal.list
new file mode 100644
index 00000000..e3fc3993
--- /dev/null
+++ b/applets/notification_area/system-tray/na-marshal.list
@@ -0,0 +1,3 @@
+VOID:OBJECT,OBJECT
+VOID:OBJECT,STRING,LONG,LONG
+VOID:OBJECT,LONG
diff --git a/applets/notification_area/system-tray/na-tray-child.c b/applets/notification_area/system-tray/na-tray-child.c
new file mode 100644
index 00000000..2a87f1e1
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray-child.c
@@ -0,0 +1,715 @@
+/* na-tray-child.c
+ * Copyright (C) 2002 Anders Carlsson <[email protected]>
+ * Copyright (C) 2003-2006 Vincent Untz
+ * Copyright (C) 2008 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 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 St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "na-tray-child.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#include "na-item.h"
+
+enum
+{
+ PROP_0,
+ PROP_ORIENTATION
+};
+
+static void na_item_init (NaItemInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
+ G_IMPLEMENT_INTERFACE (NA_TYPE_ITEM, na_item_init))
+
+static void
+na_tray_child_finalize (GObject *object)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (object);
+
+ g_clear_pointer (&child->id, g_free);
+
+ G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object);
+}
+
+static void
+na_tray_child_realize (GtkWidget *widget)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+ GdkVisual *visual = gtk_widget_get_visual (widget);
+ GdkWindow *window;
+
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
+
+ window = gtk_widget_get_window (widget);
+
+ if (child->has_alpha)
+ {
+ /* We have real transparency with an ARGB visual and the Composite
+ * extension. */
+
+ /* Set a transparent background */
+ cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0);
+ gdk_window_set_background_pattern (window, transparent);
+ gdk_window_set_composited (window, TRUE);
+ cairo_pattern_destroy (transparent);
+
+ child->parent_relative_bg = FALSE;
+ }
+ else if (visual == gdk_window_get_visual(gdk_window_get_parent(window)))
+ {
+ /* Otherwise, if the visual matches the visual of the parent window, we
+ * can use a parent-relative background and fake transparency. */
+ gdk_window_set_background_pattern (window, NULL);
+
+ child->parent_relative_bg = TRUE;
+ }
+ else
+ {
+ /* Nothing to do; the icon will sit on top of an ugly gray box */
+ child->parent_relative_bg = FALSE;
+ }
+
+ gdk_window_set_composited (window, child->composited);
+
+ gtk_widget_set_app_paintable (GTK_WIDGET (child),
+ child->parent_relative_bg || child->has_alpha);
+
+ /* Double-buffering will interfere with the parent-relative-background fake
+ * transparency, since the double-buffer code doesn't know how to fill in the
+ * background of the double-buffer correctly.
+ */
+ gtk_widget_set_double_buffered (GTK_WIDGET (child),
+ child->parent_relative_bg);
+}
+
+static void
+na_tray_child_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ /* The default handler resets the background according to the new style.
+ * We either use a transparent background or a parent-relative background
+ * and ignore the style background. So, just don't chain up.
+ */
+}
+
+static void
+na_tray_child_get_preferred_width (GtkWidget *widget,
+ gint *minimal_width,
+ gint *natural_width)
+{
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->get_preferred_width (widget,
+ minimal_width,
+ natural_width);
+
+ if (*minimal_width < 16)
+ *minimal_width = 16;
+
+ if (*natural_width < 16)
+ *natural_width = 16;
+}
+
+static void
+na_tray_child_get_preferred_height (GtkWidget *widget,
+ gint *minimal_height,
+ gint *natural_height)
+{
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->get_preferred_height (widget,
+ minimal_height,
+ natural_height);
+
+ if (*minimal_height < 16)
+ *minimal_height = 16;
+
+ if (*natural_height < 16)
+ *natural_height = 16;
+}
+
+static void
+na_tray_child_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+ GtkAllocation widget_allocation;
+ gboolean moved, resized;
+
+ gtk_widget_get_allocation (widget, &widget_allocation);
+
+ moved = (allocation->x != widget_allocation.x ||
+ allocation->y != widget_allocation.y);
+ resized = (allocation->width != widget_allocation.width ||
+ allocation->height != widget_allocation.height);
+
+ /* When we are allocating the widget while mapped we need special handling
+ * for both real and fake transparency.
+ *
+ * Real transparency: we need to invalidate and trigger a redraw of the old
+ * and new areas. (GDK really should handle this for us, but doesn't as of
+ * GTK+-2.14)
+ *
+ * Fake transparency: if the widget moved, we need to force the contents to
+ * be redrawn with the new offset for the parent-relative background.
+ */
+ if ((moved || resized) && gtk_widget_get_mapped (widget))
+ {
+ if (na_tray_child_has_alpha (child))
+ gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
+ &widget_allocation, FALSE);
+ }
+
+ GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget,
+ allocation);
+
+ if ((moved || resized) && gtk_widget_get_mapped (widget))
+ {
+ if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
+ gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
+ &widget_allocation, FALSE);
+ else if (moved && child->parent_relative_bg)
+ na_tray_child_force_redraw (child);
+ }
+}
+
+/* The plug window should completely occupy the area of the child, so we won't
+ * get an expose event. But in case we do (the plug unmaps itself, say), this
+ * expose handler draws with real or fake transparency.
+ */
+static gboolean
+na_tray_child_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (widget);
+
+ if (na_tray_child_has_alpha (child))
+ {
+ /* Clear to transparent */
+ cairo_set_source_rgba (cr, 0, 0, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+ else if (child->parent_relative_bg)
+ {
+ /* Clear to parent-relative pixmap */
+ GdkWindow *window;
+ cairo_surface_t *target;
+ GdkRectangle clip_rect;
+
+ window = gtk_widget_get_window (widget);
+ target = cairo_get_group_target (cr);
+
+ gdk_cairo_get_clip_rectangle (cr, &clip_rect);
+
+ /* Clear to parent-relative pixmap
+ * We need to use direct X access here because GDK doesn't know about
+ * the parent relative pixmap. */
+ cairo_surface_flush (target);
+
+ XClearArea (GDK_WINDOW_XDISPLAY (window),
+ GDK_WINDOW_XID (window),
+ clip_rect.x, clip_rect.y,
+ clip_rect.width, clip_rect.height,
+ False);
+ cairo_surface_mark_dirty_rectangle (target,
+ clip_rect.x, clip_rect.y,
+ clip_rect.width, clip_rect.height);
+ }
+
+ return FALSE;
+}
+
+/* Children with alpha channels have been set to be composited by calling
+ * gdk_window_set_composited(). We need to paint these children ourselves.
+ *
+ * FIXME: is that still needed on GTK3? Seems like it could be done in draw().
+ */
+static gboolean
+na_tray_child_draw_on_parent (NaItem *item,
+ GtkWidget *parent,
+ cairo_t *parent_cr)
+{
+ if (na_tray_child_has_alpha (NA_TRAY_CHILD (item)))
+ {
+ GtkWidget *widget = GTK_WIDGET (item);
+ GtkAllocation parent_allocation = { 0 };
+ GtkAllocation allocation;
+
+ /* if the parent doesn't have a window, our allocation is not relative to
+ * the context coordinates but to the parent's allocation */
+ if (! gtk_widget_get_has_window (parent))
+ gtk_widget_get_allocation (parent, &parent_allocation);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ allocation.x -= parent_allocation.x;
+ allocation.y -= parent_allocation.y;
+
+ cairo_save (parent_cr);
+ gdk_cairo_set_source_window (parent_cr,
+ gtk_widget_get_window (widget),
+ allocation.x,
+ allocation.y);
+ cairo_rectangle (parent_cr, allocation.x, allocation.y, allocation.width, allocation.height);
+ cairo_clip (parent_cr);
+ cairo_paint (parent_cr);
+ cairo_restore (parent_cr);
+ }
+
+ return TRUE;
+}
+
+static void
+na_tray_child_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_ORIENTATION:
+ /* whatever */
+ g_value_set_enum (value, GTK_ORIENTATION_HORIZONTAL);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+na_tray_child_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ case PROP_ORIENTATION:
+ /* we so don't care */
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static const gchar *
+na_tray_child_get_id (NaItem *item)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (item);
+
+ if (! child->id)
+ {
+ char *res_name = NULL;
+
+ na_tray_child_get_wm_class (child, &res_name, NULL);
+ child->id = g_strdup_printf ("%s-%lu", res_name, child->icon_window);
+ g_free (res_name);
+ }
+
+ return child->id;
+}
+
+static NaItemCategory
+na_tray_child_get_category (NaItem *item)
+{
+ const struct
+ {
+ const gchar *wm_class;
+ NaItemCategory category;
+ } wmclass_categories[] = {
+/* FIXME: get the same order as it used to be, without fake categories.
+ * from right to left:
+const char *ordered_roles[] = {
+ "keyboard",
+ "volume",
+ "bluetooth",
+ "network",
+ "battery",
+ NULL
+};
+
+const char *wmclass_roles[] = {
+ "Bluetooth-applet", "bluetooth",
+ "Mate-volume-control-applet", "volume",
+ "Nm-applet", "network",
+ "Mate-power-manager", "battery",
+ "keyboard", "keyboard",
+ NULL,
+};
+*/
+ { "Bluetooth-applet", NA_ITEM_CATEGORY_FAKE_BLUETOOTH },
+ { "Mate-volume-control-applet", NA_ITEM_CATEGORY_FAKE_VOLUME },
+ { "Nm-applet", NA_ITEM_CATEGORY_FAKE_NETWORK },
+ { "Mate-power-manager", NA_ITEM_CATEGORY_FAKE_BATTERY },
+ { "keyboard", NA_ITEM_CATEGORY_FAKE_KEYBOARD }
+ };
+ guint i;
+ NaItemCategory category = NA_ITEM_CATEGORY_APPLICATION_STATUS;
+ char *res_class = NULL;
+
+ na_tray_child_get_wm_class (NA_TRAY_CHILD (item), NULL, &res_class);
+
+ for (i = 0; i < G_N_ELEMENTS (wmclass_categories); i++)
+ {
+ if (g_strcmp0 (res_class, wmclass_categories[i].wm_class) == 0)
+ {
+ category = wmclass_categories[i].category;
+ break;
+ }
+ }
+
+ return category;
+}
+
+static void
+na_item_init (NaItemInterface *iface)
+{
+ iface->get_id = na_tray_child_get_id;
+ iface->get_category = na_tray_child_get_category;
+
+ iface->draw_on_parent = na_tray_child_draw_on_parent;
+}
+
+static void
+na_tray_child_init (NaTrayChild *child)
+{
+ child->id = NULL;
+}
+
+static void
+na_tray_child_class_init (NaTrayChildClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+
+ gobject_class = (GObjectClass *)klass;
+ widget_class = (GtkWidgetClass *)klass;
+
+ gobject_class->finalize = na_tray_child_finalize;
+ gobject_class->get_property = na_tray_child_get_property;
+ gobject_class->set_property = na_tray_child_set_property;
+
+ widget_class->style_set = na_tray_child_style_set;
+ widget_class->realize = na_tray_child_realize;
+ widget_class->get_preferred_width = na_tray_child_get_preferred_width;
+ widget_class->get_preferred_height = na_tray_child_get_preferred_height;
+ widget_class->size_allocate = na_tray_child_size_allocate;
+ widget_class->draw = na_tray_child_draw;
+
+ /* we don't really care actually */
+ g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
+}
+
+GtkWidget *
+na_tray_child_new (GdkScreen *screen,
+ Window icon_window)
+{
+ XWindowAttributes window_attributes;
+ Display *xdisplay;
+ NaTrayChild *child;
+ GdkVisual *visual;
+ gboolean visual_has_alpha;
+ int red_prec, green_prec, blue_prec, depth;
+ int result;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (icon_window != None, NULL);
+
+ xdisplay = GDK_SCREEN_XDISPLAY (screen);
+
+ /* We need to determine the visual of the window we are embedding and create
+ * the socket in the same visual.
+ */
+
+ gdk_error_trap_push ();
+ result = XGetWindowAttributes (xdisplay, icon_window,
+ &window_attributes);
+ gdk_error_trap_pop_ignored ();
+
+ if (!result) /* Window already gone */
+ return NULL;
+
+ visual = gdk_x11_screen_lookup_visual (screen,
+ window_attributes.visual->visualid);
+ if (!visual) /* Icon window is on another screen? */
+ return NULL;
+
+ child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
+ child->icon_window = icon_window;
+
+ gtk_widget_set_visual (GTK_WIDGET (child), visual);
+
+ /* We have alpha if the visual has something other than red, green,
+ * and blue */
+ gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
+ gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
+ gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
+ depth = gdk_visual_get_depth (visual);
+
+ visual_has_alpha = red_prec + blue_prec + green_prec < depth;
+ child->has_alpha = (visual_has_alpha &&
+ gdk_display_supports_composite (gdk_screen_get_display (screen)));
+
+ child->composited = child->has_alpha;
+
+ return GTK_WIDGET (child);
+}
+
+char *
+na_tray_child_get_title (NaTrayChild *child)
+{
+ char *retval = NULL;
+ GdkDisplay *display;
+ Atom utf8_string, atom, type;
+ int result;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gchar *val;
+
+ g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
+
+ display = gtk_widget_get_display (GTK_WIDGET (child));
+
+ utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
+ atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
+
+ gdk_error_trap_push ();
+
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ child->icon_window,
+ atom,
+ 0, G_MAXLONG,
+ False, utf8_string,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **)&val);
+
+ if (gdk_error_trap_pop () || result != Success)
+ return NULL;
+
+ if (type != utf8_string ||
+ format != 8 ||
+ nitems == 0)
+ {
+ if (val)
+ XFree (val);
+ return NULL;
+ }
+
+ if (!g_utf8_validate (val, nitems, NULL))
+ {
+ XFree (val);
+ return NULL;
+ }
+
+ retval = g_strndup (val, nitems);
+
+ XFree (val);
+
+ return retval;
+}
+
+/**
+ * na_tray_child_has_alpha;
+ * @child: a #NaTrayChild
+ *
+ * Checks if the child has an ARGB visual and real alpha transparence.
+ * (as opposed to faked alpha transparency with an parent-relative
+ * background)
+ *
+ * Return value: %TRUE if the child has an alpha transparency
+ */
+gboolean
+na_tray_child_has_alpha (NaTrayChild *child)
+{
+ g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
+
+ return child->has_alpha;
+}
+
+/**
+ * na_tray_child_set_composited;
+ * @child: a #NaTrayChild
+ * @composited: %TRUE if the child's window should be redirected
+ *
+ * Sets whether the #GdkWindow of the child should be set redirected
+ * using gdk_window_set_composited(). By default this is based off of
+ * na_tray_child_has_alpha(), but it may be useful to override it in
+ * certain circumstances; for example, if the #NaTrayChild is added
+ * to a parent window and that parent window is composited against the
+ * background.
+ */
+void
+na_tray_child_set_composited (NaTrayChild *child,
+ gboolean composited)
+{
+ g_return_if_fail (NA_IS_TRAY_CHILD (child));
+
+ if (child->composited == composited)
+ return;
+
+ child->composited = composited;
+ if (gtk_widget_get_realized (GTK_WIDGET (child)))
+ gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)),
+ composited);
+}
+
+/* If we are faking transparency with a window-relative background, force a
+ * redraw of the icon. This should be called if the background changes or if
+ * the child is shifted with respect to the background.
+ */
+void
+na_tray_child_force_redraw (NaTrayChild *child)
+{
+ GtkWidget *widget = GTK_WIDGET (child);
+
+ if (gtk_widget_get_mapped (widget) && child->parent_relative_bg)
+ {
+#if 1
+ /* Sending an ExposeEvent might cause redraw problems if the
+ * icon is expecting the server to clear-to-background before
+ * the redraw. It should be ok for GtkStatusIcon or EggTrayIcon.
+ */
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
+ XEvent xev;
+ GdkWindow *plug_window;
+ GtkAllocation allocation;
+
+ plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child));
+ gtk_widget_get_allocation (widget, &allocation);
+
+ xev.xexpose.type = Expose;
+ xev.xexpose.window = GDK_WINDOW_XID (plug_window);
+ xev.xexpose.x = 0;
+ xev.xexpose.y = 0;
+ xev.xexpose.width = allocation.width;
+ xev.xexpose.height = allocation.height;
+ xev.xexpose.count = 0;
+
+ gdk_error_trap_push ();
+ XSendEvent (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)),
+ xev.xexpose.window,
+ False, ExposureMask,
+ &xev);
+ /* We have to sync to reliably catch errors from the XSendEvent(),
+ * since that is asynchronous.
+ */
+ XSync (xdisplay, False);
+ gdk_error_trap_pop_ignored ();
+#else
+ /* Hiding and showing is the safe way to do it, but can result in more
+ * flickering.
+ */
+ gdk_window_hide (widget->window);
+ gdk_window_show (widget->window);
+#endif
+ }
+}
+
+/* from libwnck/xutils.c, comes as LGPLv2+ */
+static char *
+latin1_to_utf8 (const char *latin1)
+{
+ GString *str;
+ const char *p;
+
+ str = g_string_new (NULL);
+
+ p = latin1;
+ while (*p)
+ {
+ g_string_append_unichar (str, (gunichar) *p);
+ ++p;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+/* derived from libwnck/xutils.c, comes as LGPLv2+ */
+static void
+_get_wmclass (Display *xdisplay,
+ Window xwindow,
+ char **res_class,
+ char **res_name)
+{
+ XClassHint ch;
+
+ ch.res_name = NULL;
+ ch.res_class = NULL;
+
+ gdk_error_trap_push ();
+ XGetClassHint (xdisplay, xwindow, &ch);
+ gdk_error_trap_pop_ignored ();
+
+ if (res_class)
+ *res_class = NULL;
+
+ if (res_name)
+ *res_name = NULL;
+
+ if (ch.res_name)
+ {
+ if (res_name)
+ *res_name = latin1_to_utf8 (ch.res_name);
+
+ XFree (ch.res_name);
+ }
+
+ if (ch.res_class)
+ {
+ if (res_class)
+ *res_class = latin1_to_utf8 (ch.res_class);
+
+ XFree (ch.res_class);
+ }
+}
+
+/**
+ * na_tray_child_get_wm_class;
+ * @child: a #NaTrayChild
+ * @res_name: return location for a string containing the application name of
+ * @child, or %NULL
+ * @res_class: return location for a string containing the application class of
+ * @child, or %NULL
+ *
+ * Fetches the resource associated with @child.
+ */
+void
+na_tray_child_get_wm_class (NaTrayChild *child,
+ char **res_name,
+ char **res_class)
+{
+ GdkDisplay *display;
+
+ g_return_if_fail (NA_IS_TRAY_CHILD (child));
+
+ display = gtk_widget_get_display (GTK_WIDGET (child));
+
+ _get_wmclass (GDK_DISPLAY_XDISPLAY (display),
+ child->icon_window,
+ res_class,
+ res_name);
+}
diff --git a/applets/notification_area/system-tray/na-tray-child.h b/applets/notification_area/system-tray/na-tray-child.h
new file mode 100644
index 00000000..6641fe88
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray-child.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* na-tray-child.h
+ * Copyright (C) 2002 Anders Carlsson <[email protected]>
+ * Copyright (C) 2003-2006 Vincent Untz
+ * Copyright (C) 2008 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 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 St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __NA_TRAY_CHILD_H__
+#define __NA_TRAY_CHILD_H__
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtkx.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ())
+#define NA_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_CHILD, NaTrayChild))
+#define NA_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
+#define NA_IS_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_CHILD))
+#define NA_IS_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_CHILD))
+#define NA_TRAY_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
+
+typedef struct _NaTrayChild NaTrayChild;
+typedef struct _NaTrayChildClass NaTrayChildClass;
+typedef struct _NaTrayChildChild NaTrayChildChild;
+
+struct _NaTrayChild
+{
+ GtkSocket parent_instance;
+ Window icon_window;
+ guint has_alpha : 1;
+ guint composited : 1;
+ guint parent_relative_bg : 1;
+
+ gchar *id;
+};
+
+struct _NaTrayChildClass
+{
+ GtkSocketClass parent_class;
+};
+
+GType na_tray_child_get_type (void);
+
+GtkWidget *na_tray_child_new (GdkScreen *screen,
+ Window icon_window);
+char *na_tray_child_get_title (NaTrayChild *child);
+gboolean na_tray_child_has_alpha (NaTrayChild *child);
+void na_tray_child_set_composited (NaTrayChild *child,
+ gboolean composited);
+void na_tray_child_force_redraw (NaTrayChild *child);
+void na_tray_child_get_wm_class (NaTrayChild *child,
+ char **res_name,
+ char **res_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NA_TRAY_CHILD_H__ */
diff --git a/applets/notification_area/system-tray/na-tray-manager.c b/applets/notification_area/system-tray/na-tray-manager.c
new file mode 100644
index 00000000..94d3003d
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray-manager.c
@@ -0,0 +1,988 @@
+/* na-tray-manager.c
+ * Copyright (C) 2002 Anders Carlsson <[email protected]>
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Used to be: eggtraymanager.c
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "na-tray-manager.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+
+#include "na-marshal.h"
+
+/* Signals */
+enum
+{
+ TRAY_ICON_ADDED,
+ TRAY_ICON_REMOVED,
+ MESSAGE_SENT,
+ MESSAGE_CANCELLED,
+ LOST_SELECTION,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ORIENTATION
+};
+
+typedef struct
+{
+ long id, len;
+ long remaining_len;
+
+ long timeout;
+ char *str;
+#ifdef GDK_WINDOWING_X11
+ Window window;
+#endif
+} PendingMessage;
+
+static guint manager_signals[LAST_SIGNAL] = { 0 };
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+#define SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define SYSTEM_TRAY_ORIENTATION_VERT 1
+
+#ifdef GDK_WINDOWING_X11
+static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen);
+#endif
+
+static void na_tray_manager_finalize (GObject *object);
+static void na_tray_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void na_tray_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void na_tray_manager_unmanage (NaTrayManager *manager);
+
+G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
+
+static void
+na_tray_manager_init (NaTrayManager *manager)
+{
+ manager->invisible = NULL;
+ manager->socket_table = g_hash_table_new (NULL, NULL);
+
+ manager->padding = 0;
+ manager->icon_size = 0;
+
+ manager->fg.red = 0.0;
+ manager->fg.green = 0.0;
+ manager->fg.blue = 0.0;
+ manager->fg.alpha = 1.0;
+
+ manager->error.red = 1.0;
+ manager->error.green = 0.0;
+ manager->error.blue = 0.0;
+ manager->error.alpha = 1.0;
+
+ manager->warning.red = 1.0;
+ manager->warning.green = 1.0;
+ manager->warning.blue = 0.0;
+ manager->warning.alpha = 1.0;
+
+ manager->success.red = 0.0;
+ manager->success.green = 1.0;
+ manager->success.blue = 0.0;
+ manager->success.alpha = 1.0;
+}
+
+static void
+na_tray_manager_class_init (NaTrayManagerClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *)klass;
+
+ gobject_class->finalize = na_tray_manager_finalize;
+ gobject_class->set_property = na_tray_manager_set_property;
+ gobject_class->get_property = na_tray_manager_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ "orientation",
+ "orientation",
+ GTK_TYPE_ORIENTATION,
+ GTK_ORIENTATION_HORIZONTAL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ manager_signals[TRAY_ICON_ADDED] =
+ g_signal_new ("tray_icon_added",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SOCKET);
+
+ manager_signals[TRAY_ICON_REMOVED] =
+ g_signal_new ("tray_icon_removed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SOCKET);
+ manager_signals[MESSAGE_SENT] =
+ g_signal_new ("message_sent",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
+ NULL, NULL,
+ _na_marshal_VOID__OBJECT_STRING_LONG_LONG,
+ G_TYPE_NONE, 4,
+ GTK_TYPE_SOCKET,
+ G_TYPE_STRING,
+ G_TYPE_LONG,
+ G_TYPE_LONG);
+ manager_signals[MESSAGE_CANCELLED] =
+ g_signal_new ("message_cancelled",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
+ NULL, NULL,
+ _na_marshal_VOID__OBJECT_LONG,
+ G_TYPE_NONE, 2,
+ GTK_TYPE_SOCKET,
+ G_TYPE_LONG);
+ manager_signals[LOST_SELECTION] =
+ g_signal_new ("lost_selection",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+na_tray_manager_finalize (GObject *object)
+{
+ NaTrayManager *manager;
+
+ manager = NA_TRAY_MANAGER (object);
+
+ na_tray_manager_unmanage (manager);
+
+ g_list_free (manager->messages);
+ g_hash_table_destroy (manager->socket_table);
+
+ G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
+}
+
+static void
+na_tray_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NaTrayManager *manager = NA_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ na_tray_manager_set_orientation (manager, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+na_tray_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NaTrayManager *manager = NA_TRAY_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, manager->orientation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+NaTrayManager *
+na_tray_manager_new (void)
+{
+ NaTrayManager *manager;
+
+ manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
+
+ return manager;
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_plug_removed (GtkSocket *socket,
+ NaTrayManager *manager)
+{
+ NaTrayChild *child = NA_TRAY_CHILD (socket);
+
+ g_hash_table_remove (manager->socket_table,
+ GINT_TO_POINTER (child->icon_window));
+ g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
+
+ /* This destroys the socket. */
+ return FALSE;
+}
+
+static void
+na_tray_manager_handle_dock_request (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ Window icon_window = xevent->data.l[2];
+ GtkWidget *child;
+
+ if (g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (icon_window)))
+ {
+ /* We already got this notification earlier, ignore this one */
+ return;
+ }
+
+ child = na_tray_child_new (manager->screen, icon_window);
+ if (child == NULL) /* already gone or other error */
+ return;
+
+ g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
+ child);
+
+ /* If the child wasn't attached, then destroy it */
+
+ if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child))))
+ {
+ gtk_widget_destroy (child);
+ return;
+ }
+
+ g_signal_connect (child, "plug_removed",
+ G_CALLBACK (na_tray_manager_plug_removed), manager);
+
+ gtk_socket_add_id (GTK_SOCKET (child), icon_window);
+
+ if (!gtk_socket_get_plug_window (GTK_SOCKET (child)))
+ {
+ /* Embedding failed, we won't get a plug-removed signal */
+ /* This signal destroys the socket */
+ g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
+ return;
+ }
+
+ g_hash_table_insert (manager->socket_table,
+ GINT_TO_POINTER (icon_window), child);
+ gtk_widget_show (child);
+}
+
+static void
+pending_message_free (PendingMessage *message)
+{
+ g_free (message->str);
+ g_free (message);
+}
+
+static void
+na_tray_manager_handle_message_data (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GList *p;
+ int len;
+
+ /* Try to see if we can find the pending message in the list */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *msg = p->data;
+
+ if (xevent->window == msg->window)
+ {
+ /* Append the message */
+ len = MIN (msg->remaining_len, 20);
+
+ memcpy ((msg->str + msg->len - msg->remaining_len),
+ &xevent->data, len);
+ msg->remaining_len -= len;
+
+ if (msg->remaining_len == 0)
+ {
+ GtkSocket *socket;
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (msg->window));
+
+ if (socket)
+ g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
+ socket, msg->str, msg->id, msg->timeout);
+
+ pending_message_free (msg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ }
+
+ break;
+ }
+ }
+}
+
+static void
+na_tray_manager_handle_begin_message (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GtkSocket *socket;
+ GList *p;
+ PendingMessage *msg;
+ long timeout;
+ long len;
+ long id;
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (xevent->window));
+ /* we don't know about this tray icon, so ignore the message */
+ if (!socket)
+ return;
+
+ timeout = xevent->data.l[2];
+ len = xevent->data.l[3];
+ id = xevent->data.l[4];
+
+ /* Check if the same message is already in the queue and remove it if so */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *pmsg = p->data;
+
+ if (xevent->window == pmsg->window &&
+ id == pmsg->id)
+ {
+ /* Hmm, we found it, now remove it */
+ pending_message_free (pmsg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ break;
+ }
+ }
+
+ if (len == 0)
+ {
+ g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
+ socket, "", id, timeout);
+ }
+ else
+ {
+ /* Now add the new message to the queue */
+ msg = g_new0 (PendingMessage, 1);
+ msg->window = xevent->window;
+ msg->timeout = timeout;
+ msg->len = len;
+ msg->id = id;
+ msg->remaining_len = msg->len;
+ msg->str = g_malloc (msg->len + 1);
+ msg->str[msg->len] = '\0';
+ manager->messages = g_list_prepend (manager->messages, msg);
+ }
+}
+
+static void
+na_tray_manager_handle_cancel_message (NaTrayManager *manager,
+ XClientMessageEvent *xevent)
+{
+ GList *p;
+ GtkSocket *socket;
+ long id;
+
+ id = xevent->data.l[2];
+
+ /* Check if the message is in the queue and remove it if so */
+ for (p = manager->messages; p; p = p->next)
+ {
+ PendingMessage *msg = p->data;
+
+ if (xevent->window == msg->window &&
+ id == msg->id)
+ {
+ pending_message_free (msg);
+ manager->messages = g_list_remove_link (manager->messages, p);
+ g_list_free_1 (p);
+ break;
+ }
+ }
+
+ socket = g_hash_table_lookup (manager->socket_table,
+ GINT_TO_POINTER (xevent->window));
+
+ if (socket)
+ {
+ g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
+ socket, xevent->data.l[2]);
+ }
+}
+
+static GdkFilterReturn
+na_tray_manager_window_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xevent = (GdkXEvent *)xev;
+ NaTrayManager *manager = data;
+
+ if (xevent->type == ClientMessage)
+ {
+ /* We handle this client message here. See comment in
+ * na_tray_manager_handle_client_message_opcode() for details */
+ if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
+ {
+ na_tray_manager_handle_dock_request (manager,
+ (XClientMessageEvent *) xevent);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */
+ else if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE)
+ {
+ na_tray_manager_handle_begin_message (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */
+ else if (xevent->xclient.message_type == manager->opcode_atom &&
+ xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE)
+ {
+ na_tray_manager_handle_cancel_message (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ /* _NET_SYSTEM_TRAY_MESSAGE_DATA */
+ else if (xevent->xclient.message_type == manager->message_data_atom)
+ {
+ na_tray_manager_handle_message_data (manager,
+ (XClientMessageEvent *) event);
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ else if (xevent->type == SelectionClear)
+ {
+ g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
+ na_tray_manager_unmanage (manager);
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+#if 0
+//FIXME investigate why this doesn't work
+static gboolean
+na_tray_manager_selection_clear_event (GtkWidget *widget,
+ GdkEventSelection *event,
+ NaTrayManager *manager)
+{
+ g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
+ na_tray_manager_unmanage (manager);
+
+ return FALSE;
+}
+#endif
+#endif
+
+static void
+na_tray_manager_unmanage (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkDisplay *display;
+ guint32 timestamp;
+ GtkWidget *invisible;
+ GdkWindow *window;
+
+ if (manager->invisible == NULL)
+ return;
+
+ invisible = manager->invisible;
+ window = gtk_widget_get_window (invisible);
+
+ g_assert (GTK_IS_INVISIBLE (invisible));
+ g_assert (gtk_widget_get_realized (invisible));
+ g_assert (GDK_IS_WINDOW (window));
+
+ display = gtk_widget_get_display (invisible);
+
+ if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
+ window)
+ {
+ timestamp = gdk_x11_get_server_time (window);
+ gdk_selection_owner_set_for_display (display,
+ NULL,
+ manager->selection_atom,
+ timestamp,
+ TRUE);
+ }
+
+ gdk_window_remove_filter (window,
+ na_tray_manager_window_filter, manager);
+
+ manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
+ gtk_widget_destroy (invisible);
+ g_object_unref (G_OBJECT (invisible));
+#endif
+}
+
+static void
+na_tray_manager_set_orientation_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom orientation_atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_ORIENTATION");
+
+ data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
+ SYSTEM_TRAY_ORIENTATION_HORZ :
+ SYSTEM_TRAY_ORIENTATION_VERT;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ orientation_atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_visual_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Visual *xvisual;
+ Atom visual_atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ /* The visual property is a hint to the tray icons as to what visual they
+ * should use for their windows. If the X server has RGBA colormaps, then
+ * we tell the tray icons to use a RGBA colormap and we'll composite the
+ * icon onto its parents with real transparency. Otherwise, we just tell
+ * the icon to use our colormap, and we'll do some hacks with parent
+ * relative backgrounds to simulate transparency.
+ */
+
+ display = gtk_widget_get_display (manager->invisible);
+ visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_VISUAL");
+
+ if (gdk_screen_get_rgba_visual (manager->screen) != NULL &&
+ gdk_display_supports_composite (display))
+ {
+ xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen));
+ }
+ else
+ {
+ /* We actually want the visual of the tray where the icons will
+ * be embedded. In almost all cases, this will be the same as the visual
+ * of the screen.
+ */
+ xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen));
+ }
+
+ data[0] = XVisualIDFromVisual (xvisual);
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ visual_atom,
+ XA_VISUALID, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_padding_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_PADDING");
+
+ data[0] = manager->padding;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_icon_size_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom atom;
+ gulong data[1];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_ICON_SIZE");
+
+ data[0] = manager->icon_size;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 1);
+#endif
+}
+
+static void
+na_tray_manager_set_colors_property (NaTrayManager *manager)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *window;
+ GdkDisplay *display;
+ Atom atom;
+ gulong data[12];
+
+ g_return_if_fail (manager->invisible != NULL);
+ window = gtk_widget_get_window (manager->invisible);
+ g_return_if_fail (window != NULL);
+
+ display = gtk_widget_get_display (manager->invisible);
+ atom = gdk_x11_get_xatom_by_name_for_display (display,
+ "_NET_SYSTEM_TRAY_COLORS");
+
+ data[0] = manager->fg.red * 65535;
+ data[1] = manager->fg.green * 65535;
+ data[2] = manager->fg.blue * 65535;
+ data[3] = manager->error.red * 65535;
+ data[4] = manager->error.green * 65535;
+ data[5] = manager->error.blue * 65535;
+ data[6] = manager->warning.red * 65535;
+ data[7] = manager->warning.green * 65535;
+ data[8] = manager->warning.blue * 65535;
+ data[9] = manager->success.red * 65535;
+ data[10] = manager->success.green * 65535;
+ data[11] = manager->success.blue * 65535;
+
+ XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XID (window),
+ atom,
+ XA_CARDINAL, 32,
+ PropModeReplace,
+ (guchar *) &data, 12);
+#endif
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
+ GdkScreen *screen)
+{
+ GdkDisplay *display;
+ Screen *xscreen;
+ GtkWidget *invisible;
+ GdkWindow *window;
+ char *selection_atom_name;
+ guint32 timestamp;
+
+ g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
+ g_return_val_if_fail (manager->screen == NULL, FALSE);
+
+ /* If there's already a manager running on the screen
+ * we can't create another one.
+ */
+#if 0
+ if (na_tray_manager_check_running_screen_x11 (screen))
+ return FALSE;
+#endif
+
+ manager->screen = screen;
+
+ display = gdk_screen_get_display (screen);
+ xscreen = GDK_SCREEN_XSCREEN (screen);
+
+ invisible = gtk_invisible_new_for_screen (screen);
+ gtk_widget_realize (invisible);
+
+ gtk_widget_add_events (invisible,
+ GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
+
+ selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
+ gdk_screen_get_number (screen));
+ manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
+ g_free (selection_atom_name);
+
+ manager->invisible = invisible;
+ g_object_ref (G_OBJECT (manager->invisible));
+
+ na_tray_manager_set_orientation_property (manager);
+ na_tray_manager_set_visual_property (manager);
+ na_tray_manager_set_padding_property (manager);
+ na_tray_manager_set_icon_size_property (manager);
+ na_tray_manager_set_colors_property (manager);
+
+ window = gtk_widget_get_window (invisible);
+
+ timestamp = gdk_x11_get_server_time (window);
+
+ /* Check if we could set the selection owner successfully */
+ if (gdk_selection_owner_set_for_display (display,
+ window,
+ manager->selection_atom,
+ timestamp,
+ TRUE))
+ {
+ XClientMessageEvent xev;
+ GdkAtom opcode_atom;
+ GdkAtom message_data_atom;
+
+ xev.type = ClientMessage;
+ xev.window = RootWindowOfScreen (xscreen);
+ xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
+ "MANAGER");
+
+ xev.format = 32;
+ xev.data.l[0] = timestamp;
+ xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
+ manager->selection_atom);
+ xev.data.l[2] = GDK_WINDOW_XID (window);
+ xev.data.l[3] = 0; /* manager specific data */
+ xev.data.l[4] = 0; /* manager specific data */
+
+ XSendEvent (GDK_DISPLAY_XDISPLAY (display),
+ RootWindowOfScreen (xscreen),
+ False, StructureNotifyMask, (XEvent *)&xev);
+
+ opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
+ manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
+ opcode_atom);
+
+ message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
+ FALSE);
+
+ manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display,
+ message_data_atom);
+
+ /* Add a window filter */
+#if 0
+ /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
+ g_signal_connect (invisible, "selection-clear-event",
+ G_CALLBACK (na_tray_manager_selection_clear_event),
+ manager);
+#endif
+ /* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */
+ gdk_window_add_filter (window,
+ na_tray_manager_window_filter, manager);
+ return TRUE;
+ }
+ else
+ {
+ gtk_widget_destroy (invisible);
+ g_object_unref (invisible);
+ manager->invisible = NULL;
+
+ manager->screen = NULL;
+
+ return FALSE;
+ }
+}
+
+#endif
+
+gboolean
+na_tray_manager_manage_screen (NaTrayManager *manager,
+ GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+ g_return_val_if_fail (manager->screen == NULL, FALSE);
+
+#ifdef GDK_WINDOWING_X11
+ return na_tray_manager_manage_screen_x11 (manager, screen);
+#else
+ return FALSE;
+#endif
+}
+
+#ifdef GDK_WINDOWING_X11
+
+static gboolean
+na_tray_manager_check_running_screen_x11 (GdkScreen *screen)
+{
+ GdkDisplay *display;
+ Atom selection_atom;
+ char *selection_atom_name;
+
+ display = gdk_screen_get_display (screen);
+ selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
+ gdk_screen_get_number (screen));
+ selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
+ selection_atom_name);
+ g_free (selection_atom_name);
+
+ if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
+ selection_atom) != None)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+#endif
+
+gboolean
+na_tray_manager_check_running (GdkScreen *screen)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
+
+#ifdef GDK_WINDOWING_X11
+ return na_tray_manager_check_running_screen_x11 (screen);
+#else
+ return FALSE;
+#endif
+}
+
+void
+na_tray_manager_set_orientation (NaTrayManager *manager,
+ GtkOrientation orientation)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (manager->orientation != orientation)
+ {
+ manager->orientation = orientation;
+
+ na_tray_manager_set_orientation_property (manager);
+
+ g_object_notify (G_OBJECT (manager), "orientation");
+ }
+}
+
+void
+na_tray_manager_set_padding (NaTrayManager *manager,
+ gint padding)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (manager->padding != padding)
+ {
+ manager->padding = padding;
+
+ na_tray_manager_set_padding_property (manager);
+ }
+}
+
+void
+na_tray_manager_set_icon_size (NaTrayManager *manager,
+ gint icon_size)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (manager->icon_size != icon_size)
+ {
+ manager->icon_size = icon_size;
+
+ na_tray_manager_set_icon_size_property (manager);
+ }
+}
+
+void
+na_tray_manager_set_colors (NaTrayManager *manager,
+ GdkRGBA *fg,
+ GdkRGBA *error,
+ GdkRGBA *warning,
+ GdkRGBA *success)
+{
+ g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
+
+ if (!gdk_rgba_equal (&manager->fg, fg) ||
+ !gdk_rgba_equal (&manager->error, error) ||
+ !gdk_rgba_equal (&manager->warning, warning) ||
+ !gdk_rgba_equal (&manager->success, success))
+ {
+ manager->fg = *fg;
+ manager->error = *error;
+ manager->warning = *warning;
+ manager->success = *success;
+
+ na_tray_manager_set_colors_property (manager);
+ }
+}
+
+GtkOrientation
+na_tray_manager_get_orientation (NaTrayManager *manager)
+{
+ g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
+
+ return manager->orientation;
+}
diff --git a/applets/notification_area/system-tray/na-tray-manager.h b/applets/notification_area/system-tray/na-tray-manager.h
new file mode 100644
index 00000000..811791be
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray-manager.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* na-tray-manager.h
+ * Copyright (C) 2002 Anders Carlsson <[email protected]>
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Used to be: eggtraymanager.h
+ */
+
+#ifndef __NA_TRAY_MANAGER_H__
+#define __NA_TRAY_MANAGER_H__
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#include <gtk/gtk.h>
+
+#include "na-tray-child.h"
+
+G_BEGIN_DECLS
+
+#define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ())
+#define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager))
+#define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
+#define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER))
+#define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER))
+#define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
+
+typedef struct _NaTrayManager NaTrayManager;
+typedef struct _NaTrayManagerClass NaTrayManagerClass;
+
+struct _NaTrayManager
+{
+ GObject parent_instance;
+
+#ifdef GDK_WINDOWING_X11
+ GdkAtom selection_atom;
+ Atom opcode_atom;
+ Atom message_data_atom;
+#endif
+
+ GtkWidget *invisible;
+ GdkScreen *screen;
+ GtkOrientation orientation;
+ gint padding;
+ gint icon_size;
+ GdkRGBA fg;
+ GdkRGBA error;
+ GdkRGBA warning;
+ GdkRGBA success;
+
+ GList *messages;
+ GHashTable *socket_table;
+};
+
+struct _NaTrayManagerClass
+{
+ GObjectClass parent_class;
+
+ void (* tray_icon_added) (NaTrayManager *manager,
+ NaTrayChild *child);
+ void (* tray_icon_removed) (NaTrayManager *manager,
+ NaTrayChild *child);
+
+ void (* message_sent) (NaTrayManager *manager,
+ NaTrayChild *child,
+ const gchar *message,
+ glong id,
+ glong timeout);
+
+ void (* message_cancelled) (NaTrayManager *manager,
+ NaTrayChild *child,
+ glong id);
+
+ void (* lost_selection) (NaTrayManager *manager);
+};
+
+GType na_tray_manager_get_type (void);
+
+gboolean na_tray_manager_check_running (GdkScreen *screen);
+NaTrayManager *na_tray_manager_new (void);
+gboolean na_tray_manager_manage_screen (NaTrayManager *manager,
+ GdkScreen *screen);
+void na_tray_manager_set_orientation (NaTrayManager *manager,
+ GtkOrientation orientation);
+GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager);
+void na_tray_manager_set_padding (NaTrayManager *manager,
+ gint padding);
+void na_tray_manager_set_icon_size (NaTrayManager *manager,
+ gint padding);
+void na_tray_manager_set_colors (NaTrayManager *manager,
+ GdkRGBA *fg,
+ GdkRGBA *error,
+ GdkRGBA *warning,
+ GdkRGBA *success);
+
+G_END_DECLS
+
+#endif /* __NA_TRAY_MANAGER_H__ */
diff --git a/applets/notification_area/system-tray/na-tray.c b/applets/notification_area/system-tray/na-tray.c
new file mode 100644
index 00000000..c2488ecf
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2003-2006 Vincent Untz
+ * Copyright (C) 2007 Christian Persch
+ * Copyright (C) 2017 Colomban Wendling <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "na-tray-manager.h"
+#include "fixedtip.h"
+
+#include "na-tray.h"
+
+typedef struct
+{
+ NaTrayManager *tray_manager;
+ GSList *all_trays;
+ GHashTable *icon_table;
+ GHashTable *tip_table;
+} TraysScreen;
+
+struct _NaTrayPrivate
+{
+ GdkScreen *screen;
+ TraysScreen *trays_screen;
+
+ guint idle_redraw_id;
+
+ GtkOrientation orientation;
+ gint icon_padding;
+ gint icon_size;
+};
+
+typedef struct
+{
+ char *text;
+ glong id;
+ glong timeout;
+} IconTipBuffer;
+
+typedef struct
+{
+ NaTray *tray; /* tray containing the tray icon */
+ GtkWidget *icon; /* tray icon sending the message */
+ GtkWidget *fixedtip;
+ guint source_id;
+ glong id; /* id of the current message */
+ GSList *buffer; /* buffered messages */
+} IconTip;
+
+enum
+{
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_ICON_PADDING,
+ PROP_ICON_SIZE,
+ PROP_SCREEN
+};
+
+static gboolean initialized = FALSE;
+static TraysScreen *trays_screens = NULL;
+
+static void icon_tip_show_next (IconTip *icontip);
+
+/* NaTray */
+static void na_host_init (NaHostInterface *iface);
+static void na_tray_style_updated (NaHost *host,
+ GtkStyleContext *context);
+static void na_tray_force_redraw (NaHost *host);
+
+G_DEFINE_TYPE_WITH_CODE (NaTray, na_tray, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
+ G_IMPLEMENT_INTERFACE (NA_TYPE_HOST, na_host_init))
+
+static void
+na_host_init (NaHostInterface *iface)
+{
+ iface->force_redraw = na_tray_force_redraw;
+ iface->style_updated = na_tray_style_updated;
+}
+
+static NaTray *
+get_tray (TraysScreen *trays_screen)
+{
+ if (trays_screen->all_trays == NULL)
+ return NULL;
+
+ return trays_screen->all_trays->data;
+}
+
+static void
+tray_added (NaTrayManager *manager,
+ NaTrayChild *icon,
+ TraysScreen *trays_screen)
+{
+ NaTray *tray;
+ NaTrayPrivate *priv;
+
+ tray = get_tray (trays_screen);
+ if (tray == NULL)
+ return;
+
+ priv = tray->priv;
+
+ g_assert (priv->trays_screen == trays_screen);
+
+ g_hash_table_insert (trays_screen->icon_table, icon, tray);
+
+ na_host_emit_item_added (NA_HOST (tray), NA_ITEM (icon));
+
+ gtk_widget_show (GTK_WIDGET (icon));
+}
+
+static void
+tray_removed (NaTrayManager *manager,
+ NaTrayChild *icon,
+ TraysScreen *trays_screen)
+{
+ NaTray *tray;
+
+ tray = g_hash_table_lookup (trays_screen->icon_table, icon);
+ if (tray == NULL)
+ return;
+
+ g_assert (tray->priv->trays_screen == trays_screen);
+
+ na_host_emit_item_removed (NA_HOST (tray), NA_ITEM (icon));
+
+ g_hash_table_remove (trays_screen->icon_table, icon);
+ /* this will also destroy the tip associated to this icon */
+ g_hash_table_remove (trays_screen->tip_table, icon);
+}
+
+static void
+icon_tip_buffer_free (gpointer data,
+ gpointer userdata)
+{
+ IconTipBuffer *buffer;
+
+ buffer = data;
+
+ g_free (buffer->text);
+ buffer->text = NULL;
+
+ g_free (buffer);
+}
+
+static void
+icon_tip_free (gpointer data)
+{
+ IconTip *icontip;
+
+ if (data == NULL)
+ return;
+
+ icontip = data;
+
+ if (icontip->fixedtip != NULL)
+ gtk_widget_destroy (GTK_WIDGET (icontip->fixedtip));
+ icontip->fixedtip = NULL;
+
+ if (icontip->source_id != 0)
+ g_source_remove (icontip->source_id);
+ icontip->source_id = 0;
+
+ if (icontip->buffer != NULL)
+ {
+ g_slist_foreach (icontip->buffer, icon_tip_buffer_free, NULL);
+ g_slist_free (icontip->buffer);
+ }
+ icontip->buffer = NULL;
+
+ g_free (icontip);
+}
+
+static int
+icon_tip_buffer_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const IconTipBuffer *buffer_a = a;
+ const IconTipBuffer *buffer_b = b;
+
+ if (buffer_a == NULL || buffer_b == NULL)
+ return !(buffer_a == buffer_b);
+
+ return buffer_a->id - buffer_b->id;
+}
+
+static void
+icon_tip_show_next_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ icon_tip_show_next ((IconTip *) data);
+}
+
+static gboolean
+icon_tip_show_next_timeout (gpointer data)
+{
+ IconTip *icontip = (IconTip *) data;
+
+ icon_tip_show_next (icontip);
+
+ return FALSE;
+}
+
+static void
+icon_tip_show_next (IconTip *icontip)
+{
+ IconTipBuffer *buffer;
+
+ if (icontip->buffer == NULL)
+ {
+ /* this will also destroy the tip window */
+ g_hash_table_remove (icontip->tray->priv->trays_screen->tip_table,
+ icontip->icon);
+ return;
+ }
+
+ if (icontip->source_id != 0)
+ g_source_remove (icontip->source_id);
+ icontip->source_id = 0;
+
+ buffer = icontip->buffer->data;
+ icontip->buffer = g_slist_remove (icontip->buffer, buffer);
+
+ if (icontip->fixedtip == NULL)
+ {
+ icontip->fixedtip = na_fixed_tip_new (icontip->icon,
+ gtk_orientable_get_orientation (GTK_ORIENTABLE (icontip->tray)));
+
+ g_signal_connect (icontip->fixedtip, "clicked",
+ G_CALLBACK (icon_tip_show_next_clicked), icontip);
+ }
+
+ na_fixed_tip_set_markup (icontip->fixedtip, buffer->text);
+
+ if (!gtk_widget_get_mapped (icontip->fixedtip))
+ gtk_widget_show (icontip->fixedtip);
+
+ icontip->id = buffer->id;
+
+ if (buffer->timeout > 0)
+ icontip->source_id = g_timeout_add_seconds (buffer->timeout,
+ icon_tip_show_next_timeout,
+ icontip);
+
+ icon_tip_buffer_free (buffer, NULL);
+}
+
+static void
+message_sent (NaTrayManager *manager,
+ GtkWidget *icon,
+ const char *text,
+ glong id,
+ glong timeout,
+ TraysScreen *trays_screen)
+{
+ IconTip *icontip;
+ IconTipBuffer find_buffer;
+ IconTipBuffer *buffer;
+ gboolean show_now;
+
+ icontip = g_hash_table_lookup (trays_screen->tip_table, icon);
+
+ find_buffer.id = id;
+ if (icontip &&
+ (icontip->id == id ||
+ g_slist_find_custom (icontip->buffer, &find_buffer,
+ icon_tip_buffer_compare) != NULL))
+ /* we already have this message, so ignore it */
+ /* FIXME: in an ideal world, we'd remember all the past ids and ignore them
+ * too */
+ return;
+
+ show_now = FALSE;
+
+ if (icontip == NULL)
+ {
+ NaTray *tray;
+
+ tray = g_hash_table_lookup (trays_screen->icon_table, icon);
+ if (tray == NULL)
+ {
+ /* We don't know about the icon sending the message, so ignore it.
+ * But this should never happen since NaTrayManager shouldn't send
+ * us the message if there's no socket for it. */
+ g_critical ("Ignoring a message sent by a tray icon "
+ "we don't know: \"%s\".\n", text);
+ return;
+ }
+
+ icontip = g_new0 (IconTip, 1);
+ icontip->tray = tray;
+ icontip->icon = icon;
+
+ g_hash_table_insert (trays_screen->tip_table, icon, icontip);
+
+ show_now = TRUE;
+ }
+
+ buffer = g_new0 (IconTipBuffer, 1);
+
+ buffer->text = g_strdup (text);
+ buffer->id = id;
+ buffer->timeout = timeout;
+
+ icontip->buffer = g_slist_append (icontip->buffer, buffer);
+
+ if (show_now)
+ icon_tip_show_next (icontip);
+}
+
+static void
+message_cancelled (NaTrayManager *manager,
+ GtkWidget *icon,
+ glong id,
+ TraysScreen *trays_screen)
+{
+ IconTip *icontip;
+ IconTipBuffer find_buffer;
+ GSList *cancel_buffer_l;
+ IconTipBuffer *cancel_buffer;
+
+ icontip = g_hash_table_lookup (trays_screen->tip_table, icon);
+ if (icontip == NULL)
+ return;
+
+ if (icontip->id == id)
+ {
+ icon_tip_show_next (icontip);
+ return;
+ }
+
+ find_buffer.id = id;
+ cancel_buffer_l = g_slist_find_custom (icontip->buffer, &find_buffer,
+ icon_tip_buffer_compare);
+ if (cancel_buffer_l == NULL)
+ return;
+
+ cancel_buffer = cancel_buffer_l->data;
+ icon_tip_buffer_free (cancel_buffer, NULL);
+
+ icontip->buffer = g_slist_remove_link (icontip->buffer, cancel_buffer_l);
+ g_slist_free_1 (cancel_buffer_l);
+}
+
+static void
+update_orientation_for_messages (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ NaTray *tray;
+ IconTip *icontip;
+
+ if (value == NULL)
+ return;
+
+ icontip = value;
+ tray = data;
+ if (icontip->tray != tray)
+ return;
+
+ if (icontip->fixedtip)
+ na_fixed_tip_set_orientation (icontip->fixedtip, tray->priv->orientation);
+}
+
+static void
+update_size_and_orientation (NaTray *tray)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ /* This only happens when setting the property during object construction */
+ if (!priv->trays_screen)
+ return;
+
+ g_hash_table_foreach (priv->trays_screen->tip_table,
+ update_orientation_for_messages, tray);
+
+ if (get_tray (priv->trays_screen) == tray)
+ na_tray_manager_set_orientation (priv->trays_screen->tray_manager,
+ priv->orientation);
+}
+
+static void
+na_tray_init (NaTray *tray)
+{
+ NaTrayPrivate *priv;
+
+ priv = tray->priv = G_TYPE_INSTANCE_GET_PRIVATE (tray, NA_TYPE_TRAY, NaTrayPrivate);
+
+ priv->screen = NULL;
+ priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+ priv->icon_padding = 0;
+ priv->icon_size = 0;
+}
+
+static GObject *
+na_tray_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ NaTray *tray;
+ NaTrayPrivate *priv;
+ int screen_number;
+
+ object = G_OBJECT_CLASS (na_tray_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ tray = NA_TRAY (object);
+ priv = tray->priv;
+
+ g_assert (priv->screen != NULL);
+
+ if (!initialized)
+ {
+ trays_screens = g_new0 (TraysScreen, 1);
+ initialized = TRUE;
+ }
+
+ screen_number = gdk_screen_get_number (priv->screen);
+
+ if (trays_screens [screen_number].tray_manager == NULL)
+ {
+ NaTrayManager *tray_manager;
+
+ tray_manager = na_tray_manager_new ();
+
+ if (na_tray_manager_manage_screen (tray_manager, priv->screen))
+ {
+ trays_screens [screen_number].tray_manager = tray_manager;
+
+ g_signal_connect (tray_manager, "tray_icon_added",
+ G_CALLBACK (tray_added),
+ &trays_screens [screen_number]);
+ g_signal_connect (tray_manager, "tray_icon_removed",
+ G_CALLBACK (tray_removed),
+ &trays_screens [screen_number]);
+ g_signal_connect (tray_manager, "message_sent",
+ G_CALLBACK (message_sent),
+ &trays_screens [screen_number]);
+ g_signal_connect (tray_manager, "message_cancelled",
+ G_CALLBACK (message_cancelled),
+ &trays_screens [screen_number]);
+
+ trays_screens [screen_number].icon_table = g_hash_table_new (NULL,
+ NULL);
+ trays_screens [screen_number].tip_table = g_hash_table_new_full (
+ NULL,
+ NULL,
+ NULL,
+ icon_tip_free);
+ }
+ else
+ {
+ g_printerr ("System tray didn't get the system tray manager selection for screen %d\n",
+ screen_number);
+ g_object_unref (tray_manager);
+ }
+ }
+
+ priv->trays_screen = &trays_screens [screen_number];
+ trays_screens [screen_number].all_trays = g_slist_append (trays_screens [screen_number].all_trays,
+ tray);
+
+ update_size_and_orientation (tray);
+
+ return object;
+}
+
+static void
+na_tray_dispose (GObject *object)
+{
+ NaTray *tray = NA_TRAY (object);
+ NaTrayPrivate *priv = tray->priv;
+ TraysScreen *trays_screen = priv->trays_screen;
+
+ if (trays_screen != NULL)
+ {
+ trays_screen->all_trays = g_slist_remove (trays_screen->all_trays, tray);
+
+ if (trays_screen->all_trays == NULL)
+ {
+ /* Make sure we drop the manager selection */
+ g_object_unref (trays_screen->tray_manager);
+ trays_screen->tray_manager = NULL;
+
+ g_hash_table_destroy (trays_screen->icon_table);
+ trays_screen->icon_table = NULL;
+
+ g_hash_table_destroy (trays_screen->tip_table);
+ trays_screen->tip_table = NULL;
+ }
+ else
+ {
+ NaTray *new_tray;
+
+ new_tray = get_tray (trays_screen);
+ if (new_tray != NULL)
+ na_tray_manager_set_orientation (trays_screen->tray_manager,
+ gtk_orientable_get_orientation (GTK_ORIENTABLE (new_tray)));
+ }
+ }
+
+ priv->trays_screen = NULL;
+
+ if (priv->idle_redraw_id != 0)
+ {
+ g_source_remove (priv->idle_redraw_id);
+ priv->idle_redraw_id = 0;
+ }
+
+ G_OBJECT_CLASS (na_tray_parent_class)->dispose (object);
+}
+
+static void
+na_tray_set_orientation (NaTray *tray,
+ GtkOrientation orientation)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ if (orientation == priv->orientation)
+ return;
+
+ priv->orientation = orientation;
+
+ update_size_and_orientation (tray);
+}
+
+static void
+na_tray_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NaTray *tray = NA_TRAY (object);
+ NaTrayPrivate *priv = tray->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ na_tray_set_orientation (tray, g_value_get_enum (value));
+ break;
+ case PROP_ICON_PADDING:
+ na_tray_set_padding (tray, g_value_get_int (value));
+ break;
+ case PROP_ICON_SIZE:
+ na_tray_set_icon_size (tray, g_value_get_int (value));
+ break;
+ case PROP_SCREEN:
+ priv->screen = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+na_tray_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NaTray *tray = NA_TRAY (object);
+ NaTrayPrivate *priv = tray->priv;
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, tray->priv->orientation);
+ break;
+ case PROP_ICON_PADDING:
+ g_value_set_int (value, tray->priv->icon_padding);
+ break;
+ case PROP_ICON_SIZE:
+ g_value_set_int (value, tray->priv->icon_size);
+ break;
+ case PROP_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+na_tray_class_init (NaTrayClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = na_tray_constructor;
+ gobject_class->set_property = na_tray_set_property;
+ gobject_class->get_property = na_tray_get_property;
+ gobject_class->dispose = na_tray_dispose;
+
+ g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
+
+ g_object_class_override_property (gobject_class, PROP_ICON_PADDING, "icon-padding");
+ g_object_class_override_property (gobject_class, PROP_ICON_SIZE, "icon-size");
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SCREEN,
+ g_param_spec_object ("screen", "screen", "screen",
+ GDK_TYPE_SCREEN,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private (gobject_class, sizeof (NaTrayPrivate));
+}
+
+NaHost *
+na_tray_new_for_screen (GdkScreen *screen,
+ GtkOrientation orientation)
+{
+ return g_object_new (NA_TYPE_TRAY,
+ "screen", screen,
+ "orientation", orientation,
+ NULL);
+}
+
+void
+na_tray_set_padding (NaTray *tray,
+ gint padding)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ priv->icon_padding = padding;
+ if (get_tray (priv->trays_screen) == tray)
+ na_tray_manager_set_padding (priv->trays_screen->tray_manager, padding);
+}
+
+void
+na_tray_set_icon_size (NaTray *tray,
+ gint size)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ priv->icon_size = size;
+ if (get_tray (priv->trays_screen) == tray)
+ na_tray_manager_set_icon_size (priv->trays_screen->tray_manager, size);
+}
+
+static void
+na_tray_set_colors (NaTray *tray,
+ GdkRGBA *fg,
+ GdkRGBA *error,
+ GdkRGBA *warning,
+ GdkRGBA *success)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ if (get_tray (priv->trays_screen) == tray)
+ na_tray_manager_set_colors (priv->trays_screen->tray_manager, fg, error, warning, success);
+}
+
+static void
+na_tray_style_updated (NaHost *host,
+ GtkStyleContext *context)
+{
+ GdkRGBA fg;
+ GdkRGBA error;
+ GdkRGBA warning;
+ GdkRGBA success;
+
+ gtk_style_context_save (context);
+ gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
+
+ gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fg);
+
+ if (!gtk_style_context_lookup_color (context, "error_color", &error))
+ error = fg;
+ if (!gtk_style_context_lookup_color (context, "warning_color", &warning))
+ warning = fg;
+ if (!gtk_style_context_lookup_color (context, "success_color", &success))
+ success = fg;
+
+ gtk_style_context_restore (context);
+
+ na_tray_set_colors (NA_TRAY (host), &fg, &error, &warning, &success);
+}
+
+static gboolean
+idle_redraw_cb (NaTray *tray)
+{
+ NaTrayPrivate *priv = tray->priv;
+
+ g_hash_table_foreach (priv->trays_screen->icon_table,
+ (GHFunc) na_tray_child_force_redraw, NULL);
+
+ priv->idle_redraw_id = 0;
+
+ return FALSE;
+}
+
+static void
+na_tray_force_redraw (NaHost *host)
+{
+ NaTray *tray = NA_TRAY (host);
+ NaTrayPrivate *priv = tray->priv;
+
+ /* Force the icons to redraw their backgrounds.
+ */
+ if (priv->idle_redraw_id == 0)
+ priv->idle_redraw_id = g_idle_add ((GSourceFunc) idle_redraw_cb, tray);
+}
diff --git a/applets/notification_area/system-tray/na-tray.h b/applets/notification_area/system-tray/na-tray.h
new file mode 100644
index 00000000..8eaca36e
--- /dev/null
+++ b/applets/notification_area/system-tray/na-tray.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* na-tray-tray.h
+ * Copyright (C) 2002 Anders Carlsson <[email protected]>
+ * Copyright (C) 2003-2006 Vincent Untz
+ *
+ * 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Used to be: eggtraytray.h
+ */
+
+#ifndef __NA_TRAY_H__
+#define __NA_TRAY_H__
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#include <gtk/gtk.h>
+
+#include "na-host.h"
+
+G_BEGIN_DECLS
+
+#define NA_TYPE_TRAY (na_tray_get_type ())
+#define NA_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY, NaTray))
+#define NA_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY, NaTrayClass))
+#define NA_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY))
+#define NA_IS_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY))
+#define NA_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY, NaTrayClass))
+
+typedef struct _NaTray NaTray;
+typedef struct _NaTrayPrivate NaTrayPrivate;
+typedef struct _NaTrayClass NaTrayClass;
+
+struct _NaTray
+{
+ GObject parent_instance;
+
+ NaTrayPrivate *priv;
+};
+
+struct _NaTrayClass
+{
+ GObjectClass parent_class;
+};
+
+GType na_tray_get_type (void);
+NaHost *na_tray_new_for_screen (GdkScreen *screen,
+ GtkOrientation orientation);
+void na_tray_set_padding (NaTray *tray,
+ gint padding);
+void na_tray_set_icon_size (NaTray *tray,
+ gint icon_size);
+
+G_END_DECLS
+
+#endif /* __NA_TRAY_H__ */