Import part of the notification area applet, and use it to add a
notification are to the panel. A bit warty, but we don't know how we want the final UI to look anyway. (The fact that transparency doesn't work is a known bug.) svn path=/trunk/; revision=44
This commit is contained in:
parent
d8f9d1a448
commit
8c61d46586
@ -17,6 +17,10 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
|||||||
[The prefix for our gettext translation domains.])
|
[The prefix for our gettext translation domains.])
|
||||||
|
|
||||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0)
|
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0)
|
||||||
|
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
||||||
|
|
||||||
|
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||||
|
AM_PATH_GLIB_2_0()
|
||||||
|
|
||||||
changequote(,)dnl
|
changequote(,)dnl
|
||||||
if test "x$GCC" = "xyes"; then
|
if test "x$GCC" = "xyes"; then
|
||||||
|
@ -4,10 +4,12 @@ const Mainloop = imports.mainloop;
|
|||||||
|
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Tidy = imports.gi.Tidy;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const PANEL_HEIGHT = 32;
|
const PANEL_HEIGHT = 32;
|
||||||
|
const TRAY_HEIGHT = 24;
|
||||||
const PANEL_BACKGROUND_COLOR = new Clutter.Color();
|
const PANEL_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
PANEL_BACKGROUND_COLOR.from_pixel(0xeeddccff);
|
PANEL_BACKGROUND_COLOR.from_pixel(0xeeddccff);
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ Panel.prototype = {
|
|||||||
width: global.screen_width+2,
|
width: global.screen_width+2,
|
||||||
height: PANEL_HEIGHT+1,
|
height: PANEL_HEIGHT+1,
|
||||||
border_width: 1});
|
border_width: 1});
|
||||||
background.set_position(-1, -1)
|
background.set_position(-1, -1);
|
||||||
this._group.add_actor(background);
|
this._group.add_actor(background);
|
||||||
|
|
||||||
let message = new Clutter.Label({ font_name: "Sans Bold 16px",
|
let message = new Clutter.Label({ font_name: "Sans Bold 16px",
|
||||||
@ -35,11 +37,35 @@ Panel.prototype = {
|
|||||||
message.set_position(5, 5);
|
message.set_position(5, 5);
|
||||||
this._group.add_actor(message);
|
this._group.add_actor(message);
|
||||||
|
|
||||||
|
this._grid = new Tidy.Grid({ height: TRAY_HEIGHT,
|
||||||
|
valign: 0.5,
|
||||||
|
end_align: true,
|
||||||
|
column_gap: 2 })
|
||||||
|
this._group.add_actor(this._grid);
|
||||||
|
|
||||||
this._clock = new Clutter.Label({ font_name: "Sans Bold 16px",
|
this._clock = new Clutter.Label({ font_name: "Sans Bold 16px",
|
||||||
text: "" });
|
text: "" });
|
||||||
this._clock.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
this._grid.add_actor(this._clock);
|
||||||
this._clock.set_position(global.screen_width - 5, 5);
|
|
||||||
this._group.add_actor(this._clock);
|
this._grid.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
|
this._grid.set_position(global.screen_width - 2, (PANEL_HEIGHT - TRAY_HEIGHT) / 2);
|
||||||
|
|
||||||
|
this._traymanager = new Shell.TrayManager();
|
||||||
|
let panel = this;
|
||||||
|
this._traymanager.connect('tray-icon-added',
|
||||||
|
function(o, icon) {
|
||||||
|
panel._grid.add_actor(icon);
|
||||||
|
/* bump the clock back to the end */
|
||||||
|
panel._grid.remove_actor(panel._clock);
|
||||||
|
panel._grid.add_actor(panel._clock);
|
||||||
|
panel._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
|
});
|
||||||
|
this._traymanager.connect('tray-icon-removed',
|
||||||
|
function(o, icon) {
|
||||||
|
panel._grid.remove_actor(icon);
|
||||||
|
panel._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
||||||
|
});
|
||||||
|
this._traymanager.manage_stage(global.stage);
|
||||||
|
|
||||||
message.connect('button-press-event',
|
message.connect('button-press-event',
|
||||||
function(o, event) {
|
function(o, event) {
|
||||||
@ -68,8 +94,6 @@ Panel.prototype = {
|
|||||||
|
|
||||||
_updateClock: function() {
|
_updateClock: function() {
|
||||||
this._clock.set_text(new Date().toLocaleFormat("%H:%M"));
|
this._clock.set_text(new Date().toLocaleFormat("%H:%M"));
|
||||||
this._clock.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
NULL =
|
NULL =
|
||||||
|
|
||||||
GLIB_GENMARSHAL = `pkg-config --variable=glib_genmarshal glib-2.0`
|
|
||||||
GLIB_MKENUMS = `pkg-config --variable=glib_mkenums glib-2.0`
|
|
||||||
|
|
||||||
tidy_cflags = \
|
tidy_cflags = \
|
||||||
-I$(top_srcdir)/src \
|
-I$(top_srcdir)/src \
|
||||||
-DPREFIX=\""$(prefix)"\" \
|
-DPREFIX=\""$(prefix)"\" \
|
||||||
@ -20,7 +17,7 @@ tidy_built_sources = \
|
|||||||
|
|
||||||
BUILT_SOURCES += $(tidy_built_sources)
|
BUILT_SOURCES += $(tidy_built_sources)
|
||||||
|
|
||||||
STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
|
TIDY_STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
|
||||||
|
|
||||||
# please, keep this sorted alphabetically
|
# please, keep this sorted alphabetically
|
||||||
tidy_source_h = \
|
tidy_source_h = \
|
||||||
@ -100,7 +97,7 @@ libtidy_1_0_la_SOURCES = \
|
|||||||
libtidy_1_0_la_CPPFLAGS = $(tidy_cflags)
|
libtidy_1_0_la_CPPFLAGS = $(tidy_cflags)
|
||||||
libtidy_1_0_la_LDFLAGS = $(LDADD)
|
libtidy_1_0_la_LDFLAGS = $(LDADD)
|
||||||
|
|
||||||
CLEANFILES += $(STAMP_FILES) $(BUILT_SOURCES)
|
CLEANFILES += $(TIDY_STAMP_FILES) $(BUILT_SOURCES)
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
tidy/tidy-enum-types.h.in \
|
tidy/tidy-enum-types.h.in \
|
||||||
|
57
src/Makefile-tray.am
Normal file
57
src/Makefile-tray.am
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
tray_cflags = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-DG_DISABLE_DEPRECATED \
|
||||||
|
-DG_LOG_DOMAIN=\"notification_area\" \
|
||||||
|
$(TRAY_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
tray_built_sources = \
|
||||||
|
na-marshal.h \
|
||||||
|
na-marshal.c
|
||||||
|
|
||||||
|
BUILT_SOURCES += $(tray_built_sources)
|
||||||
|
|
||||||
|
TRAY_STAMP_FILES = stamp-na-marshal.h
|
||||||
|
|
||||||
|
# please, keep this sorted alphabetically
|
||||||
|
tray_source = \
|
||||||
|
tray/na-tray-child.c \
|
||||||
|
tray/na-tray-child.h \
|
||||||
|
tray/na-tray-manager.c \
|
||||||
|
tray/na-tray-manager.h \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
na-marshal.h: stamp-na-marshal.h
|
||||||
|
@true
|
||||||
|
stamp-na-marshal.h: Makefile tray/na-marshal.list
|
||||||
|
$(GLIB_GENMARSHAL) \
|
||||||
|
--prefix=_na_marshal \
|
||||||
|
--header \
|
||||||
|
$(srcdir)/tray/na-marshal.list > xgen-tmh && \
|
||||||
|
(cmp -s xgen-tmh na-marshal.h || cp -f xgen-tmh na-marshal.h) && \
|
||||||
|
rm -f xgen-tmh && \
|
||||||
|
echo timestamp > $(@F)
|
||||||
|
|
||||||
|
na-marshal.c: Makefile tray/na-marshal.list
|
||||||
|
(echo "#include \"na-marshal.h\"" ; \
|
||||||
|
$(GLIB_GENMARSHAL) \
|
||||||
|
--prefix=_na_marshal \
|
||||||
|
--body \
|
||||||
|
$(srcdir)/tray/na-marshal.list ) > xgen-tmc && \
|
||||||
|
cp -f xgen-tmc na-marshal.c && \
|
||||||
|
rm -f xgen-tmc
|
||||||
|
|
||||||
|
lib_LTLIBRARIES += libtray.la
|
||||||
|
|
||||||
|
libtray_la_LIBADD = $(TRAY_LIBS)
|
||||||
|
libtray_la_SOURCES = \
|
||||||
|
$(tray_source) \
|
||||||
|
$(tray_built_sources) \
|
||||||
|
$(NULL)
|
||||||
|
libtray_la_CPPFLAGS = $(tray_cflags)
|
||||||
|
libtray_la_LDFLAGS = $(LDADD)
|
||||||
|
|
||||||
|
CLEANFILES += $(TRAY_STAMP_FILES) $(BUILT_SOURCES)
|
||||||
|
|
||||||
|
EXTRA_DIST += \
|
||||||
|
tray/na-marshal.list
|
@ -2,9 +2,11 @@ BUILT_SOURCES =
|
|||||||
CLEANFILES =
|
CLEANFILES =
|
||||||
|
|
||||||
include Makefile-tidy.am
|
include Makefile-tidy.am
|
||||||
|
include Makefile-tray.am
|
||||||
|
|
||||||
gnome_shell_cflags = \
|
gnome_shell_cflags = \
|
||||||
$(MUTTER_PLUGIN_CFLAGS) \
|
$(MUTTER_PLUGIN_CFLAGS) \
|
||||||
|
-Itray \
|
||||||
-DGETTEXT_PACKAGE=gnome-shell \
|
-DGETTEXT_PACKAGE=gnome-shell \
|
||||||
-DJSDIR=\"$(pkgdatadir)/js\"
|
-DJSDIR=\"$(pkgdatadir)/js\"
|
||||||
|
|
||||||
@ -16,10 +18,15 @@ libgnome_shell_la_SOURCES = \
|
|||||||
shell-process.c \
|
shell-process.c \
|
||||||
shell-process.h \
|
shell-process.h \
|
||||||
shell-global.c \
|
shell-global.c \
|
||||||
shell-global.h
|
shell-global.h \
|
||||||
|
shell-tray-manager.c \
|
||||||
|
shell-tray-manager.h
|
||||||
|
|
||||||
libgnome_shell_la_LDFLAGS = -avoid-version -module
|
libgnome_shell_la_LDFLAGS = -avoid-version -module
|
||||||
libgnome_shell_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
|
libgnome_shell_la_LIBADD = \
|
||||||
|
$(MUTTER_PLUGIN_LIBS) \
|
||||||
|
libtidy-1.0.la \
|
||||||
|
libtray.la
|
||||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||||
|
|
||||||
# We can't have any undefined symbols when g-ir-scanner dlopens the library
|
# We can't have any undefined symbols when g-ir-scanner dlopens the library
|
||||||
@ -33,7 +40,10 @@ libgnome_shell_introspect_la_SOURCES = \
|
|||||||
# The dummy -rpath here is needed to convince libtool to build a
|
# The dummy -rpath here is needed to convince libtool to build a
|
||||||
# noinst_LTLIBRARY shared
|
# noinst_LTLIBRARY shared
|
||||||
libgnome_shell_introspect_la_LDFLAGS = -avoid-version -module -rpath $(libdir)
|
libgnome_shell_introspect_la_LDFLAGS = -avoid-version -module -rpath $(libdir)
|
||||||
libgnome_shell_introspect_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
|
libgnome_shell_introspect_la_LIBADD = \
|
||||||
|
$(MUTTER_PLUGIN_LIBS) \
|
||||||
|
libtidy-1.0.la \
|
||||||
|
libtray.la
|
||||||
libgnome_shell_introspect_la_CPPFLAGS = $(gnome_shell_cflags)
|
libgnome_shell_introspect_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||||
|
|
||||||
typelibdir = $(pkglibdir)/girepository
|
typelibdir = $(pkglibdir)/girepository
|
||||||
@ -49,7 +59,7 @@ Shell-0.1.gir: libgnome-shell-introspect.la $(libgnome_shell_la_SOURCES) Makefil
|
|||||||
--include=Clutter-0.8 \
|
--include=Clutter-0.8 \
|
||||||
--include=Meta-2.25 \
|
--include=Meta-2.25 \
|
||||||
--library=gnome-shell-introspect \
|
--library=gnome-shell-introspect \
|
||||||
- $(libgnome_shell_la_SOURCES) \
|
$(libgnome_shell_la_SOURCES) \
|
||||||
$(libgnome_shell_la_CPPFLAGS) \
|
$(libgnome_shell_la_CPPFLAGS) \
|
||||||
-o $@.tmp
|
-o $@.tmp
|
||||||
sed 's/gnome-shell-introspect/gnome-shell/' < $@.tmp > $@ && rm $@.tmp
|
sed 's/gnome-shell-introspect/gnome-shell/' < $@.tmp > $@ && rm $@.tmp
|
||||||
|
215
src/shell-tray-manager.c
Normal file
215
src/shell-tray-manager.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
#include <clutter/glx/clutter-glx.h>
|
||||||
|
#include <clutter/x11/clutter-x11.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "shell-tray-manager.h"
|
||||||
|
#include "na-tray-manager.h"
|
||||||
|
|
||||||
|
struct _ShellTrayManagerPrivate {
|
||||||
|
NaTrayManager *na_manager;
|
||||||
|
ClutterStage *stage;
|
||||||
|
GdkWindow *stage_window;
|
||||||
|
|
||||||
|
GHashTable *icons;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ShellTrayManager *manager;
|
||||||
|
GtkWidget *socket;
|
||||||
|
GtkWidget *window;
|
||||||
|
ClutterActor *actor;
|
||||||
|
} ShellTrayManagerChild;
|
||||||
|
|
||||||
|
/* Signals */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TRAY_ICON_ADDED,
|
||||||
|
TRAY_ICON_REMOVED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (ShellTrayManager, shell_tray_manager, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
static guint shell_tray_manager_signals [LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
|
||||||
|
static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_tray_icon (gpointer data)
|
||||||
|
{
|
||||||
|
ShellTrayManagerChild *child = data;
|
||||||
|
|
||||||
|
gtk_widget_hide (child->window);
|
||||||
|
gtk_widget_destroy (child->window);
|
||||||
|
g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA,
|
||||||
|
0, 0, NULL, NULL, child);
|
||||||
|
g_object_unref (child->actor);
|
||||||
|
g_slice_free (ShellTrayManagerChild, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_tray_manager_init (ShellTrayManager *manager)
|
||||||
|
{
|
||||||
|
manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHELL_TYPE_TRAY_MANAGER,
|
||||||
|
ShellTrayManagerPrivate);
|
||||||
|
manager->priv->na_manager = na_tray_manager_new ();
|
||||||
|
|
||||||
|
manager->priv->icons = g_hash_table_new_full (NULL, NULL,
|
||||||
|
NULL, free_tray_icon);
|
||||||
|
|
||||||
|
g_signal_connect (manager->priv->na_manager, "tray-icon-added",
|
||||||
|
G_CALLBACK (na_tray_icon_added), manager);
|
||||||
|
g_signal_connect (manager->priv->na_manager, "tray-icon-removed",
|
||||||
|
G_CALLBACK (na_tray_icon_removed), manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_tray_manager_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
|
||||||
|
|
||||||
|
g_object_unref (manager->priv->na_manager);
|
||||||
|
g_object_unref (manager->priv->stage);
|
||||||
|
g_object_unref (manager->priv->stage_window);
|
||||||
|
g_hash_table_destroy (manager->priv->icons);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_tray_manager_class_init (ShellTrayManagerClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (klass, sizeof (ShellTrayManagerPrivate));
|
||||||
|
|
||||||
|
gobject_class->finalize = shell_tray_manager_finalize;
|
||||||
|
|
||||||
|
shell_tray_manager_signals[TRAY_ICON_ADDED] =
|
||||||
|
g_signal_new ("tray-icon-added",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_added),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__OBJECT,
|
||||||
|
G_TYPE_NONE, 1,
|
||||||
|
CLUTTER_TYPE_ACTOR);
|
||||||
|
shell_tray_manager_signals[TRAY_ICON_REMOVED] =
|
||||||
|
g_signal_new ("tray-icon-removed",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_removed),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__OBJECT,
|
||||||
|
G_TYPE_NONE, 1,
|
||||||
|
CLUTTER_TYPE_ACTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellTrayManager *
|
||||||
|
shell_tray_manager_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (SHELL_TYPE_TRAY_MANAGER, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_tray_manager_manage_stage (ShellTrayManager *manager,
|
||||||
|
ClutterStage *stage)
|
||||||
|
{
|
||||||
|
Window stage_xwin;
|
||||||
|
|
||||||
|
g_return_if_fail (manager->priv->stage == NULL);
|
||||||
|
|
||||||
|
manager->priv->stage = g_object_ref (stage);
|
||||||
|
stage_xwin = clutter_x11_get_stage_window (stage);
|
||||||
|
manager->priv->stage_window = gdk_window_lookup (stage_xwin);
|
||||||
|
if (manager->priv->stage_window)
|
||||||
|
g_object_ref (manager->priv->stage_window);
|
||||||
|
else
|
||||||
|
manager->priv->stage_window = gdk_window_foreign_new (stage_xwin);
|
||||||
|
|
||||||
|
na_tray_manager_manage_screen (manager->priv->na_manager,
|
||||||
|
gdk_drawable_get_screen (GDK_DRAWABLE (manager->priv->stage_window)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
actor_moved (GObject *object, GParamSpec *param, gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellTrayManagerChild *child = user_data;
|
||||||
|
ClutterActor *actor = child->actor;
|
||||||
|
int wx = 0, wy = 0, x, y, ax, ay;
|
||||||
|
|
||||||
|
/* Find the actor's new coordinates in terms of the stage (which is
|
||||||
|
* child->window's parent window.
|
||||||
|
*/
|
||||||
|
while (actor)
|
||||||
|
{
|
||||||
|
clutter_actor_get_position (actor, &x, &y);
|
||||||
|
clutter_actor_get_anchor_point (actor, &ax, &ay);
|
||||||
|
|
||||||
|
wx += x - ax;
|
||||||
|
wy += y - ay;
|
||||||
|
|
||||||
|
actor = clutter_actor_get_parent (actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_move (GTK_WINDOW (child->window), wx, wy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellTrayManager *manager = user_data;
|
||||||
|
GtkWidget *win;
|
||||||
|
ClutterActor *icon;
|
||||||
|
ShellTrayManagerChild *child;
|
||||||
|
|
||||||
|
win = gtk_window_new (GTK_WINDOW_POPUP);
|
||||||
|
gtk_container_add (GTK_CONTAINER (win), socket);
|
||||||
|
|
||||||
|
gtk_widget_set_size_request (win, 24, 24);
|
||||||
|
gtk_widget_realize (win);
|
||||||
|
|
||||||
|
gtk_widget_set_parent_window (win, manager->priv->stage_window);
|
||||||
|
gdk_window_reparent (win->window, manager->priv->stage_window, 0, 0);
|
||||||
|
gtk_widget_show_all (win);
|
||||||
|
|
||||||
|
icon = clutter_glx_texture_pixmap_new_with_window (GDK_WINDOW_XWINDOW (win->window));
|
||||||
|
clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (icon), TRUE);
|
||||||
|
clutter_actor_set_size (icon, 24, 24);
|
||||||
|
|
||||||
|
child = g_slice_new (ShellTrayManagerChild);
|
||||||
|
child->window = win;
|
||||||
|
child->socket = socket;
|
||||||
|
child->actor = g_object_ref (icon);
|
||||||
|
g_hash_table_insert (manager->priv->icons, socket, child);
|
||||||
|
|
||||||
|
g_signal_connect (child->actor, "notify::x",
|
||||||
|
G_CALLBACK (actor_moved), child);
|
||||||
|
g_signal_connect (child->actor, "notify::y",
|
||||||
|
G_CALLBACK (actor_moved), child);
|
||||||
|
|
||||||
|
g_signal_emit (manager,
|
||||||
|
shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
|
||||||
|
icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *socket,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellTrayManager *manager = user_data;
|
||||||
|
ShellTrayManagerChild *child;
|
||||||
|
|
||||||
|
child = g_hash_table_lookup (manager->priv->icons, socket);
|
||||||
|
g_return_if_fail (child != NULL);
|
||||||
|
|
||||||
|
g_signal_emit (manager,
|
||||||
|
shell_tray_manager_signals[TRAY_ICON_REMOVED], 0,
|
||||||
|
child->actor);
|
||||||
|
g_hash_table_remove (manager->priv->icons, socket);
|
||||||
|
}
|
47
src/shell-tray-manager.h
Normal file
47
src/shell-tray-manager.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#ifndef __SHELL_TRAY_MANAGER_H__
|
||||||
|
#define __SHELL_TRAY_MANAGER_H__
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define SHELL_TYPE_TRAY_MANAGER (shell_tray_manager_get_type ())
|
||||||
|
#define SHELL_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_TRAY_MANAGER, ShellTrayManager))
|
||||||
|
#define SHELL_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_TRAY_MANAGER, ShellTrayManagerClass))
|
||||||
|
#define SHELL_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_TRAY_MANAGER))
|
||||||
|
#define SHELL_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_TRAY_MANAGER))
|
||||||
|
#define SHELL_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_TRAY_MANAGER, ShellTrayManagerClass))
|
||||||
|
|
||||||
|
typedef struct _ShellTrayManager ShellTrayManager;
|
||||||
|
typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate;
|
||||||
|
typedef struct _ShellTrayManagerClass ShellTrayManagerClass;
|
||||||
|
|
||||||
|
struct _ShellTrayManager
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
ShellTrayManagerPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ShellTrayManagerClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* tray_icon_added) (ShellTrayManager *manager,
|
||||||
|
ClutterActor *icon);
|
||||||
|
void (* tray_icon_removed) (ShellTrayManager *manager,
|
||||||
|
ClutterActor *icon);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
GType shell_tray_manager_get_type (void);
|
||||||
|
|
||||||
|
ShellTrayManager *shell_tray_manager_new (void);
|
||||||
|
void shell_tray_manager_manage_stage (ShellTrayManager *manager,
|
||||||
|
ClutterStage *stage);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SHELL_TRAY_MANAGER_H__ */
|
3
src/tray/na-marshal.list
Normal file
3
src/tray/na-marshal.list
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
VOID:OBJECT,OBJECT
|
||||||
|
VOID:OBJECT,STRING,LONG,LONG
|
||||||
|
VOID:OBJECT,LONG
|
386
src/tray/na-tray-child.c
Normal file
386
src/tray/na-tray-child.c
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
/* na-tray-child.c
|
||||||
|
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
||||||
|
* 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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "na-tray-child.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET)
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_tray_child_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
gboolean visual_has_alpha;
|
||||||
|
|
||||||
|
GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
|
||||||
|
|
||||||
|
/* We have alpha if the visual has something other than red, green, and blue */
|
||||||
|
visual_has_alpha = visual->red_prec + visual->blue_prec + visual->green_prec < visual->depth;
|
||||||
|
|
||||||
|
if (visual_has_alpha && gdk_display_supports_composite (gtk_widget_get_display (widget)))
|
||||||
|
{
|
||||||
|
/* We have real transparency with an ARGB visual and the Composite extension.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Set a transparent background */
|
||||||
|
GdkColor transparent = { 0, 0, 0, 0 }; /* only pixel=0 matters */
|
||||||
|
gdk_window_set_background(widget->window, &transparent);
|
||||||
|
gdk_window_set_composited (widget->window, TRUE);
|
||||||
|
|
||||||
|
child->is_composited = TRUE;
|
||||||
|
child->parent_relative_bg = FALSE;
|
||||||
|
}
|
||||||
|
else if (visual == gdk_window_get_visual (gdk_window_get_parent (widget->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_back_pixmap (widget->window, NULL, TRUE);
|
||||||
|
|
||||||
|
child->is_composited = FALSE;
|
||||||
|
child->parent_relative_bg = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nothing to do; the icon will sit on top of an ugly gray box */
|
||||||
|
|
||||||
|
child->is_composited = FALSE;
|
||||||
|
child->parent_relative_bg = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_set_app_paintable (GTK_WIDGET (child),
|
||||||
|
child->parent_relative_bg || child->is_composited);
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* This is adapted from code that was commented out in na-tray-manager.c; the code
|
||||||
|
* in na-tray-manager.c wouldn't have worked reliably, this will. So maybe it can
|
||||||
|
* be reenabled. On other hand, things seem to be working fine without it.
|
||||||
|
*
|
||||||
|
* If reenabling, you need to hook it up in na_tray_child_class_init().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
na_tray_child_size_request (GtkWidget *widget,
|
||||||
|
GtkRequisition *request)
|
||||||
|
{
|
||||||
|
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_request (widget, request);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the icons have a meaningful size ..
|
||||||
|
*/
|
||||||
|
if ((request->width < 16) || (request->height < 16))
|
||||||
|
{
|
||||||
|
gint nw = MAX (24, request->width);
|
||||||
|
gint nh = MAX (24, request->height);
|
||||||
|
g_warning (_("tray icon has requested a size of (%i x %i), resizing to (%i x %i)"),
|
||||||
|
req.width, req.height, nw, nh);
|
||||||
|
request->width = nw;
|
||||||
|
request->height = nh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_tray_child_size_allocate (GtkWidget *widget,
|
||||||
|
GtkAllocation *allocation)
|
||||||
|
{
|
||||||
|
NaTrayChild *child = NA_TRAY_CHILD (widget);
|
||||||
|
|
||||||
|
gboolean moved = allocation->x != widget->allocation.x || allocation->y != widget->allocation.y;
|
||||||
|
gboolean 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_MAPPED (widget))
|
||||||
|
{
|
||||||
|
if (na_tray_child_is_composited (child))
|
||||||
|
gdk_window_invalidate_rect (gdk_window_get_parent (widget->window),
|
||||||
|
&widget->allocation, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget, allocation);
|
||||||
|
|
||||||
|
if ((moved || resized) && GTK_WIDGET_MAPPED (widget))
|
||||||
|
{
|
||||||
|
if (na_tray_child_is_composited (NA_TRAY_CHILD (widget)))
|
||||||
|
gdk_window_invalidate_rect (gdk_window_get_parent (widget->window),
|
||||||
|
&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_expose_event (GtkWidget *widget,
|
||||||
|
GdkEventExpose *event)
|
||||||
|
{
|
||||||
|
NaTrayChild *child = NA_TRAY_CHILD (widget);
|
||||||
|
|
||||||
|
if (na_tray_child_is_composited (child))
|
||||||
|
{
|
||||||
|
/* Clear to transparent */
|
||||||
|
cairo_t *cr = gdk_cairo_create (widget->window);
|
||||||
|
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
||||||
|
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||||
|
gdk_cairo_region (cr, event->region);
|
||||||
|
cairo_fill (cr);
|
||||||
|
cairo_destroy (cr);
|
||||||
|
}
|
||||||
|
else if (child->parent_relative_bg)
|
||||||
|
{
|
||||||
|
/* Clear to parent-relative pixmap */
|
||||||
|
gdk_window_clear_area (widget->window,
|
||||||
|
event->area.x, event->area.y,
|
||||||
|
event->area.width, event->area.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_tray_child_init (NaTrayChild *child)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
widget_class->style_set = na_tray_child_style_set;
|
||||||
|
widget_class->realize = na_tray_child_realize;
|
||||||
|
widget_class->size_allocate = na_tray_child_size_allocate;
|
||||||
|
widget_class->expose_event = na_tray_child_expose_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *
|
||||||
|
na_tray_child_new (GdkScreen *screen,
|
||||||
|
Window icon_window)
|
||||||
|
{
|
||||||
|
XWindowAttributes window_attributes;
|
||||||
|
Display *xdisplay;
|
||||||
|
NaTrayChild *child;
|
||||||
|
GdkVisual *visual;
|
||||||
|
GdkColormap *colormap;
|
||||||
|
gboolean new_colormap;
|
||||||
|
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 ();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
new_colormap = FALSE;
|
||||||
|
|
||||||
|
if (visual == gdk_screen_get_rgb_visual (screen))
|
||||||
|
colormap = gdk_screen_get_rgb_colormap (screen);
|
||||||
|
else if (visual == gdk_screen_get_rgba_visual (screen))
|
||||||
|
colormap = gdk_screen_get_rgba_colormap (screen);
|
||||||
|
else if (visual == gdk_screen_get_system_visual (screen))
|
||||||
|
colormap = gdk_screen_get_system_colormap (screen);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
colormap = gdk_colormap_new (visual, FALSE);
|
||||||
|
new_colormap = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
|
||||||
|
child->icon_window = icon_window;
|
||||||
|
|
||||||
|
gtk_widget_set_colormap (GTK_WIDGET (child), colormap);
|
||||||
|
|
||||||
|
if (new_colormap)
|
||||||
|
g_object_unref (colormap);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
na_tray_child_is_composited (NaTrayChild *child)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
|
||||||
|
|
||||||
|
return child->is_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 shifed with respect to the background.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
na_tray_child_force_redraw (NaTrayChild *child)
|
||||||
|
{
|
||||||
|
GtkWidget *widget = GTK_WIDGET (child);
|
||||||
|
|
||||||
|
if (GTK_WIDGET_MAPPED (child) && 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;
|
||||||
|
|
||||||
|
xev.xexpose.type = Expose;
|
||||||
|
xev.xexpose.window = GDK_WINDOW_XWINDOW (GTK_SOCKET (child)->plug_window);
|
||||||
|
xev.xexpose.x = 0;
|
||||||
|
xev.xexpose.y = 0;
|
||||||
|
xev.xexpose.width = widget->allocation.width;
|
||||||
|
xev.xexpose.height = widget->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 ();
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
}
|
65
src/tray/na-tray-child.h
Normal file
65
src/tray/na-tray-child.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* na-tray-child.h
|
||||||
|
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
||||||
|
* 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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NA_TRAY_CHILD_H__
|
||||||
|
#define __NA_TRAY_CHILD_H__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#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 is_composited : 1;
|
||||||
|
guint parent_relative_bg : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
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_is_composited (NaTrayChild *child);
|
||||||
|
void na_tray_child_force_redraw (NaTrayChild *child);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __NA_TRAY_CHILD_H__ */
|
842
src/tray/na-tray-manager.c
Normal file
842
src/tray/na-tray-manager.c
Normal file
@ -0,0 +1,842 @@
|
|||||||
|
/* na-tray-manager.c
|
||||||
|
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
||||||
|
* 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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Used to be: eggtraymanager.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libintl.h>
|
||||||
|
|
||||||
|
#include "na-tray-manager.h"
|
||||||
|
|
||||||
|
#include <gdkconfig.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
#if defined (GDK_WINDOWING_X11)
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#elif defined (GDK_WINDOWING_WIN32)
|
||||||
|
#include <gdk/gdkwin32.h>
|
||||||
|
#endif
|
||||||
|
#include <gtk/gtkinvisible.h>
|
||||||
|
#include <gtk/gtksocket.h>
|
||||||
|
#include <gtk/gtkwindow.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];
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#if defined (GDK_WINDOWING_X11)
|
||||||
|
/* Nothing */
|
||||||
|
#elif defined (GDK_WINDOWING_WIN32)
|
||||||
|
g_warning ("Port NaTrayManager to Win32");
|
||||||
|
#else
|
||||||
|
g_warning ("Port NaTrayManager to this GTK+ backend");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (child)->plug_window)
|
||||||
|
{
|
||||||
|
/* Embedding failed, we won't get a plug-removed signal */
|
||||||
|
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
|
||||||
|
gtk_widget_destroy (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 GdkFilterReturn
|
||||||
|
na_tray_manager_handle_client_message_message_data (GdkXEvent *xev,
|
||||||
|
GdkEvent *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
XClientMessageEvent *xevent;
|
||||||
|
NaTrayManager *manager;
|
||||||
|
GList *p;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
xevent = (XClientMessageEvent *) xev;
|
||||||
|
manager = data;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GDK_FILTER_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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 &&
|
||||||
|
xevent->data.l[4] == 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = xevent->data.l[2];
|
||||||
|
len = xevent->data.l[3];
|
||||||
|
id = xevent->data.l[4];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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 &&
|
||||||
|
xevent->data.l[4] == 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_handle_client_message_opcode (GdkXEvent *xev,
|
||||||
|
GdkEvent *event,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
XClientMessageEvent *xevent;
|
||||||
|
NaTrayManager *manager;
|
||||||
|
|
||||||
|
xevent = (XClientMessageEvent *) xev;
|
||||||
|
manager = data;
|
||||||
|
|
||||||
|
switch (xevent->data.l[1])
|
||||||
|
{
|
||||||
|
case SYSTEM_TRAY_REQUEST_DOCK:
|
||||||
|
/* Ignore this one since we don't know on which window this was received
|
||||||
|
* and so we can't know for which screen this is. It will be handled
|
||||||
|
* in na_tray_manager_window_filter() since we also receive it there */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYSTEM_TRAY_BEGIN_MESSAGE:
|
||||||
|
na_tray_manager_handle_begin_message (manager, xevent);
|
||||||
|
return GDK_FILTER_REMOVE;
|
||||||
|
|
||||||
|
case SYSTEM_TRAY_CANCEL_MESSAGE:
|
||||||
|
na_tray_manager_handle_cancel_message (manager, xevent);
|
||||||
|
return GDK_FILTER_REMOVE;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GDK_FILTER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (manager->invisible == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
invisible = manager->invisible;
|
||||||
|
g_assert (GTK_IS_INVISIBLE (invisible));
|
||||||
|
g_assert (GTK_WIDGET_REALIZED (invisible));
|
||||||
|
g_assert (GDK_IS_WINDOW (invisible->window));
|
||||||
|
|
||||||
|
display = gtk_widget_get_display (invisible);
|
||||||
|
|
||||||
|
if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
|
||||||
|
invisible->window)
|
||||||
|
{
|
||||||
|
timestamp = gdk_x11_get_server_time (invisible->window);
|
||||||
|
gdk_selection_owner_set_for_display (display,
|
||||||
|
NULL,
|
||||||
|
manager->selection_atom,
|
||||||
|
timestamp,
|
||||||
|
TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME: we should also use gdk_remove_client_message_filter when it's
|
||||||
|
//available
|
||||||
|
// See bug #351254
|
||||||
|
gdk_window_remove_filter (invisible->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
|
||||||
|
GdkDisplay *display;
|
||||||
|
Atom orientation_atom;
|
||||||
|
gulong data[1];
|
||||||
|
|
||||||
|
if (!manager->invisible || !manager->invisible->window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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_XWINDOW (manager->invisible->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
|
||||||
|
GdkDisplay *display;
|
||||||
|
Visual *xvisual;
|
||||||
|
Atom visual_atom;
|
||||||
|
gulong data[1];
|
||||||
|
|
||||||
|
if (!manager->invisible || !manager->invisible->window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
GdkColormap *colormap = gdk_screen_get_default_colormap (manager->screen);
|
||||||
|
xvisual = GDK_VISUAL_XVISUAL (gdk_colormap_get_visual (colormap));
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = XVisualIDFromVisual (xvisual);
|
||||||
|
|
||||||
|
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
||||||
|
GDK_WINDOW_XWINDOW (manager->invisible->window),
|
||||||
|
visual_atom,
|
||||||
|
XA_VISUALID, 32,
|
||||||
|
PropModeReplace,
|
||||||
|
(guchar *) &data, 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GDK_WINDOWING_X11
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
|
||||||
|
GdkScreen *screen)
|
||||||
|
{
|
||||||
|
GdkDisplay *display;
|
||||||
|
Screen *xscreen;
|
||||||
|
GtkWidget *invisible;
|
||||||
|
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);
|
||||||
|
|
||||||
|
timestamp = gdk_x11_get_server_time (invisible->window);
|
||||||
|
|
||||||
|
/* Check if we could set the selection owner successfully */
|
||||||
|
if (gdk_selection_owner_set_for_display (display,
|
||||||
|
invisible->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_XWINDOW (invisible->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);
|
||||||
|
|
||||||
|
/* 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 (invisible->window,
|
||||||
|
na_tray_manager_window_filter, manager);
|
||||||
|
/* This is for SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE */
|
||||||
|
gdk_display_add_client_message_filter (display, opcode_atom,
|
||||||
|
na_tray_manager_handle_client_message_opcode,
|
||||||
|
manager);
|
||||||
|
/* This is for _NET_SYSTEM_TRAY_MESSAGE_DATA */
|
||||||
|
gdk_display_add_client_message_filter (display, message_data_atom,
|
||||||
|
na_tray_manager_handle_client_message_message_data,
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkOrientation
|
||||||
|
na_tray_manager_get_orientation (NaTrayManager *manager)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
|
||||||
|
|
||||||
|
return manager->orientation;
|
||||||
|
}
|
97
src/tray/na-tray-manager.h
Normal file
97
src/tray/na-tray-manager.h
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* na-tray-manager.h
|
||||||
|
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
|
||||||
|
* 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., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Used to be: eggtraymanager.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NA_TRAY_MANAGER_H__
|
||||||
|
#define __NA_TRAY_MANAGER_H__
|
||||||
|
|
||||||
|
#include <gtk/gtkwidget.h>
|
||||||
|
#ifdef GDK_WINDOWING_X11
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GtkWidget *invisible;
|
||||||
|
GdkScreen *screen;
|
||||||
|
GtkOrientation orientation;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __NA_TRAY_MANAGER_H__ */
|
Loading…
Reference in New Issue
Block a user