diff --git a/src/modules/ctypes/gdk.jsm b/src/modules/ctypes/gdk.jsm index 1606c79..a7194d9 100644 --- a/src/modules/ctypes/gdk.jsm +++ b/src/modules/ctypes/gdk.jsm @@ -251,6 +251,9 @@ function gdk_defines(lib) { lib.lazy_bind("gdk_atom_intern", this.GdkAtom, gobject.gchar.ptr, gobject.gboolean); lib.lazy_bind("gdk_property_change", ctypes.void_t, this.GdkWindow.ptr, this.GdkAtom, this.GdkAtom, gobject.gint, this.GdkPropMode, gobject.guchar.ptr, gobject.gint); + lib.lazy_bind("gdk_display_get_n_screens", gobject.gint, this.GdkDisplay.ptr); + lib.lazy_bind("gdk_display_get_screen", this.GdkScreen.ptr, this.GdkDisplay.ptr, gobject.gint); + } new ctypes_library(GDK_LIBNAME, GDK_ABIS, gdk_defines, this); diff --git a/src/modules/ctypes/xinerama.jsm b/src/modules/ctypes/xinerama.jsm new file mode 100644 index 0000000..0d37b7a --- /dev/null +++ b/src/modules/ctypes/xinerama.jsm @@ -0,0 +1,23 @@ +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +var EXPORTED_SYMBOLS = [ "xinerama" ]; + +const XINERAMA_LIBNAME = "Xinerama"; +const XINERAMA_ABIS = [ 1 ]; + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/ctypes.jsm"); +Cu.import("resource://firetray/ctypes/ctypes-utils.jsm"); +Cu.import("resource://firetray/logging.jsm"); +Cu.import("resource://firetray/ctypes/x11.jsm"); + + +function xinerama_defines(lib) { + lib.lazy_bind("XineramaIsActive", x11.Bool, x11.Display.ptr); +} + +new ctypes_library(XINERAMA_LIBNAME, XINERAMA_ABIS, xinerama_defines, this); diff --git a/src/modules/gtk2/FiretrayWindow.jsm b/src/modules/gtk2/FiretrayWindow.jsm index 9d430f5..2b6cbd6 100644 --- a/src/modules/gtk2/FiretrayWindow.jsm +++ b/src/modules/gtk2/FiretrayWindow.jsm @@ -195,11 +195,11 @@ firetray.Window = { firetray.Handler.windows[xid].savedY = gy.value; firetray.Handler.windows[xid].savedWidth = gwidth.value; firetray.Handler.windows[xid].savedHeight = gheight.value; - LOG("save: gx="+gx+", gy="+gy+", gwidth="+gwidth+", gheight="+gheight); + LOG("save: gx="+gx.value+", gy="+gy.value+", gwidth="+gwidth.value+", gheight="+gheight.value); }, restoreWindowPositionAndSize: function(xid) { - if (!firetray.Handler.windows[xid].savedX) + if ("undefined" === typeof(firetray.Handler.windows[xid].savedX)) return; // windows[xid].saved* may not be initialized LOG("restore: x="+firetray.Handler.windows[xid].savedX+", y="+firetray.Handler.windows[xid].savedY+", w="+firetray.Handler.windows[xid].savedWidth+", h="+firetray.Handler.windows[xid].savedHeight); diff --git a/testing/Makefile b/testing/Makefile index da8150c..fac4438 100644 --- a/testing/Makefile +++ b/testing/Makefile @@ -1,6 +1,6 @@ includes := $(shell pkg-config --libs --cflags gtk+-2.0) executables := gtk_icon_example trayicon hide xtypes x11XGetWindowProp \ - window_state_event xev_desktop + window_state_event xev_desktop teststatusicon .PHONY: all all: $(executables) @@ -29,3 +29,6 @@ window_state_event: window_state_event.c xev_desktop: xev_desktop.c gcc -lXm -lXt -lX11 -o xev_desktop xev_desktop.c + +teststatusicon: teststatusicon.c prop-editor.c + gcc $(includes) -o teststatusicon teststatusicon.c prop-editor.c diff --git a/testing/prop-editor.c b/testing/prop-editor.c new file mode 100644 index 0000000..7adff87 --- /dev/null +++ b/testing/prop-editor.c @@ -0,0 +1,1345 @@ +/* prop-editor.c + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 + +#include + +#include "prop-editor.h" + + +typedef struct +{ + gpointer instance; + GObject *alive_object; + guint id; +} DisconnectData; + +static void +disconnect_func (gpointer data) +{ + DisconnectData *dd = data; + + g_signal_handler_disconnect (dd->instance, dd->id); +} + +static void +signal_removed (gpointer data, + GClosure *closure) +{ + DisconnectData *dd = data; + + g_object_steal_data (dd->alive_object, "alive-object-data"); + g_free (dd); +} + +static gboolean +is_child_property (GParamSpec *pspec) +{ + return g_param_spec_get_qdata (pspec, g_quark_from_string ("is-child-prop")) != NULL; +} + +static void +mark_child_property (GParamSpec *pspec) +{ + g_param_spec_set_qdata (pspec, g_quark_from_string ("is-child-prop"), + GINT_TO_POINTER (TRUE)); +} + +static void +g_object_connect_property (GObject *object, + GParamSpec *spec, + GCallback func, + gpointer data, + GObject *alive_object) +{ + GClosure *closure; + gchar *with_detail; + DisconnectData *dd; + + if (is_child_property (spec)) + with_detail = g_strconcat ("child-notify::", spec->name, NULL); + else + with_detail = g_strconcat ("notify::", spec->name, NULL); + + dd = g_new (DisconnectData, 1); + + closure = g_cclosure_new (func, data, NULL); + + g_closure_add_invalidate_notifier (closure, dd, signal_removed); + + dd->id = g_signal_connect_closure (object, with_detail, + closure, FALSE); + + dd->instance = object; + dd->alive_object = alive_object; + + g_object_set_data_full (G_OBJECT (alive_object), + "alive-object-data", + dd, + disconnect_func); + + g_free (with_detail); +} + +typedef struct +{ + GObject *obj; + GParamSpec *spec; + gint modified_id; +} ObjectProperty; + +static void +free_object_property (ObjectProperty *p) +{ + g_free (p); +} + +static void +connect_controller (GObject *controller, + const gchar *signal, + GObject *model, + GParamSpec *spec, + GCallback func) +{ + ObjectProperty *p; + + p = g_new (ObjectProperty, 1); + p->obj = model; + p->spec = spec; + + p->modified_id = g_signal_connect_data (controller, signal, func, p, + (GClosureNotify)free_object_property, + 0); + g_object_set_data (controller, "object-property", p); +} + +static void +block_controller (GObject *controller) +{ + ObjectProperty *p = g_object_get_data (controller, "object-property"); + + if (p) + g_signal_handler_block (controller, p->modified_id); +} + +static void +unblock_controller (GObject *controller) +{ + ObjectProperty *p = g_object_get_data (controller, "object-property"); + + if (p) + g_signal_handler_unblock (controller, p->modified_id); +} + +static void +int_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (int) adj->value, NULL); + } + else + g_object_set (p->obj, p->spec->name, (int) adj->value, NULL); +} + +static void +get_property_value (GObject *object, GParamSpec *pspec, GValue *value) +{ + if (is_child_property (pspec)) + { + GtkWidget *widget = GTK_WIDGET (object); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_get_property (GTK_CONTAINER (parent), + widget, pspec->name, value); + } + else + g_object_get_property (object, pspec->name, value); +} + +static void +int_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_INT); + + get_property_value (object, pspec, &val); + + if (g_value_get_int (&val) != (int)adj->value) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_int (&val)); + unblock_controller (G_OBJECT (adj)); + } + + g_value_unset (&val); +} + +static void +uint_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (guint) adj->value, NULL); + } + else + g_object_set (p->obj, p->spec->name, (guint) adj->value, NULL); +} + +static void +uint_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_UINT); + get_property_value (object, pspec, &val); + + if (g_value_get_uint (&val) != (guint)adj->value) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_uint (&val)); + unblock_controller (G_OBJECT (adj)); + } + + g_value_unset (&val); +} + +static void +float_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (float) adj->value, NULL); + } + else + g_object_set (p->obj, p->spec->name, (float) adj->value, NULL); +} + +static void +float_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_FLOAT); + get_property_value (object, pspec, &val); + + if (g_value_get_float (&val) != (float) adj->value) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_float (&val)); + unblock_controller (G_OBJECT (adj)); + } + + g_value_unset (&val); +} + +static void +double_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (double) adj->value, NULL); + } + else + g_object_set (p->obj, p->spec->name, (double) adj->value, NULL); +} + +static void +double_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_DOUBLE); + get_property_value (object, pspec, &val); + + if (g_value_get_double (&val) != adj->value) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_double (&val)); + unblock_controller (G_OBJECT (adj)); + } + + g_value_unset (&val); +} + +static void +string_modified (GtkEntry *entry, gpointer data) +{ + ObjectProperty *p = data; + const gchar *text; + + text = gtk_entry_get_text (entry); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, text, NULL); + } + else + g_object_set (p->obj, p->spec->name, text, NULL); +} + +static void +string_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + GValue val = { 0, }; + const gchar *str; + const gchar *text; + + g_value_init (&val, G_TYPE_STRING); + get_property_value (object, pspec, &val); + + str = g_value_get_string (&val); + if (str == NULL) + str = ""; + text = gtk_entry_get_text (entry); + + if (strcmp (str, text) != 0) + { + block_controller (G_OBJECT (entry)); + gtk_entry_set_text (entry, str); + unblock_controller (G_OBJECT (entry)); + } + + g_value_unset (&val); +} + +static void +bool_modified (GtkToggleButton *tb, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (int) tb->active, NULL); + } + else + g_object_set (p->obj, p->spec->name, (int) tb->active, NULL); +} + +static void +bool_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_BOOLEAN); + get_property_value (object, pspec, &val); + + if (g_value_get_boolean (&val) != tb->active) + { + block_controller (G_OBJECT (tb)); + gtk_toggle_button_set_active (tb, g_value_get_boolean (&val)); + unblock_controller (G_OBJECT (tb)); + } + + gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ? + "TRUE" : "FALSE"); + + g_value_unset (&val); +} + + +static void +enum_modified (GtkComboBox *cb, gpointer data) +{ + ObjectProperty *p = data; + gint i; + GEnumClass *eclass; + + eclass = G_ENUM_CLASS (g_type_class_peek (p->spec->value_type)); + + i = gtk_combo_box_get_active (cb); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, eclass->values[i].value, NULL); + } + else + g_object_set (p->obj, p->spec->name, eclass->values[i].value, NULL); +} + +static void +enum_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkComboBox *cb = GTK_COMBO_BOX (data); + GValue val = { 0, }; + GEnumClass *eclass; + gint i; + + eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type)); + + g_value_init (&val, pspec->value_type); + get_property_value (object, pspec, &val); + + i = 0; + while (i < eclass->n_values) + { + if (eclass->values[i].value == g_value_get_enum (&val)) + break; + ++i; + } + + if (gtk_combo_box_get_active (cb) != i) + { + block_controller (G_OBJECT (cb)); + gtk_combo_box_set_active (cb, i); + unblock_controller (G_OBJECT (cb)); + } + + g_value_unset (&val); + +} + +static void +flags_modified (GtkCheckButton *button, gpointer data) +{ + ObjectProperty *p = data; + gboolean active; + GFlagsClass *fclass; + guint flags; + gint i; + + fclass = G_FLAGS_CLASS (g_type_class_peek (p->spec->value_type)); + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index")); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_get (GTK_CONTAINER (parent), + widget, p->spec->name, &flags, NULL); + if (active) + flags |= fclass->values[i].value; + else + flags &= ~fclass->values[i].value; + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, flags, NULL); + } + else + { + g_object_get (p->obj, p->spec->name, &flags, NULL); + + if (active) + flags |= fclass->values[i].value; + else + flags &= ~fclass->values[i].value; + + g_object_set (p->obj, p->spec->name, flags, NULL); + } +} + +static void +flags_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GList *children, *c; + GValue val = { 0, }; + GFlagsClass *fclass; + guint flags; + gint i; + + fclass = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type)); + + g_value_init (&val, pspec->value_type); + get_property_value (object, pspec, &val); + flags = g_value_get_flags (&val); + g_value_unset (&val); + + children = gtk_container_get_children (GTK_CONTAINER (data)); + + for (c = children, i = 0; c; c = c->next, i++) + { + block_controller (G_OBJECT (c->data)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data), + (fclass->values[i].value & flags) != 0); + unblock_controller (G_OBJECT (c->data)); + } + + g_list_free (children); +} + +static gunichar +unichar_get_value (GtkEntry *entry) +{ + const gchar *text = gtk_entry_get_text (entry); + + if (text[0]) + return g_utf8_get_char (text); + else + return 0; +} + +static void +unichar_modified (GtkEntry *entry, gpointer data) +{ + ObjectProperty *p = data; + gunichar val = unichar_get_value (entry); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, val, NULL); + } + else + g_object_set (p->obj, p->spec->name, val, NULL); +} + +static void +unichar_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + gunichar new_val; + gunichar old_val = unichar_get_value (entry); + GValue val = { 0, }; + gchar buf[7]; + gint len; + + g_value_init (&val, pspec->value_type); + get_property_value (object, pspec, &val); + new_val = (gunichar)g_value_get_uint (&val); + + if (new_val != old_val) + { + if (!new_val) + len = 0; + else + len = g_unichar_to_utf8 (new_val, buf); + + buf[len] = '\0'; + + block_controller (G_OBJECT (entry)); + gtk_entry_set_text (entry, buf); + unblock_controller (G_OBJECT (entry)); + } +} + +static void +pointer_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkLabel *label = GTK_LABEL (data); + gchar *str; + gpointer ptr; + + g_object_get (object, pspec->name, &ptr, NULL); + + str = g_strdup_printf ("Pointer: %p", ptr); + gtk_label_set_text (label, str); + g_free (str); +} + +static gchar * +object_label (GObject *obj, GParamSpec *pspec) +{ + const gchar *name; + + if (obj) + name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); + else if (pspec) + name = g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)); + else + name = "unknown"; + return g_strdup_printf ("Object: %p (%s)", obj, name); +} + +static void +object_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkWidget *label, *button; + gchar *str; + GObject *obj; + + GList *children = gtk_container_get_children (GTK_CONTAINER (data)); + label = GTK_WIDGET (children->data); + button = GTK_WIDGET (children->next->data); + g_object_get (object, pspec->name, &obj, NULL); + g_list_free (children); + + str = object_label (obj, pspec); + + gtk_label_set_text (GTK_LABEL (label), str); + gtk_widget_set_sensitive (button, G_IS_OBJECT (obj)); + + if (obj) + g_object_unref (obj); + + g_free (str); +} + +static void +model_destroy (gpointer data) +{ + g_object_steal_data (data, "model-object"); + gtk_widget_destroy (data); +} + +static void +window_destroy (gpointer data) +{ + g_object_steal_data (data, "prop-editor-win"); +} + +static void +object_properties (GtkWidget *button, + GObject *object) +{ + gchar *name; + GObject *obj; + + name = (gchar *) g_object_get_data (G_OBJECT (button), "property-name"); + g_object_get (object, name, &obj, NULL); + if (G_IS_OBJECT (obj)) + create_prop_editor (obj, 0); +} + +static void +color_modified (GtkColorButton *cb, gpointer data) +{ + ObjectProperty *p = data; + GdkColor color; + + gtk_color_button_get_color (cb, &color); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, &color, NULL); + } + else + g_object_set (p->obj, p->spec->name, &color, NULL); +} + +static void +color_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkColorButton *cb = GTK_COLOR_BUTTON (data); + GValue val = { 0, }; + GdkColor *color; + GdkColor cb_color; + + g_value_init (&val, GDK_TYPE_COLOR); + get_property_value (object, pspec, &val); + + color = g_value_get_boxed (&val); + gtk_color_button_get_color (cb, &cb_color); + + if (color != NULL && !gdk_color_equal (color, &cb_color)) + { + block_controller (G_OBJECT (cb)); + gtk_color_button_set_color (cb, color); + unblock_controller (G_OBJECT (cb)); + } + + g_value_unset (&val); +} + +static GtkWidget * +property_widget (GObject *object, + GParamSpec *spec, + gboolean can_modify) +{ + GtkWidget *prop_edit; + GtkAdjustment *adj; + gchar *msg; + GType type = G_PARAM_SPEC_TYPE (spec); + + if (type == G_TYPE_PARAM_INT) + { + adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value, + G_PARAM_SPEC_INT (spec)->minimum, + G_PARAM_SPEC_INT (spec)->maximum, + 1, + MAX ((G_PARAM_SPEC_INT (spec)->maximum - + G_PARAM_SPEC_INT (spec)->minimum) / 10, 1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 1.0, 0); + + g_object_connect_property (object, spec, + G_CALLBACK (int_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (int_modified)); + } + else if (type == G_TYPE_PARAM_UINT) + { + adj = GTK_ADJUSTMENT ( + gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value, + G_PARAM_SPEC_UINT (spec)->minimum, + G_PARAM_SPEC_UINT (spec)->maximum, + 1, + MAX ((G_PARAM_SPEC_UINT (spec)->maximum - + G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 1.0, 0); + + g_object_connect_property (object, spec, + G_CALLBACK (uint_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (uint_modified)); + } + else if (type == G_TYPE_PARAM_FLOAT) + { + + adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value, + G_PARAM_SPEC_FLOAT (spec)->minimum, + G_PARAM_SPEC_FLOAT (spec)->maximum, + 0.1, + MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum - + G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 0.1, 2); + + g_object_connect_property (object, spec, + G_CALLBACK (float_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (float_modified)); + } + else if (type == G_TYPE_PARAM_DOUBLE) + { + adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value, + G_PARAM_SPEC_DOUBLE (spec)->minimum, + G_PARAM_SPEC_DOUBLE (spec)->maximum, + 0.1, + MAX ((G_PARAM_SPEC_DOUBLE (spec)->maximum - + G_PARAM_SPEC_DOUBLE (spec)->minimum) / 10, 0.1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 0.1, 2); + + g_object_connect_property (object, spec, + G_CALLBACK (double_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (double_modified)); + } + else if (type == G_TYPE_PARAM_STRING) + { + prop_edit = gtk_entry_new (); + + g_object_connect_property (object, spec, + G_CALLBACK (string_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (string_modified)); + } + else if (type == G_TYPE_PARAM_BOOLEAN) + { + prop_edit = gtk_toggle_button_new_with_label (""); + + g_object_connect_property (object, spec, + G_CALLBACK (bool_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "toggled", + object, spec, G_CALLBACK (bool_modified)); + } + else if (type == G_TYPE_PARAM_ENUM) + { + { + GEnumClass *eclass; + gint j; + + prop_edit = gtk_combo_box_new_text (); + + eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type)); + + j = 0; + while (j < eclass->n_values) + { + gtk_combo_box_append_text (GTK_COMBO_BOX (prop_edit), + eclass->values[j].value_name); + ++j; + } + + g_type_class_unref (eclass); + + g_object_connect_property (object, spec, + G_CALLBACK (enum_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (enum_modified)); + } + } + else if (type == G_TYPE_PARAM_FLAGS) + { + { + GFlagsClass *fclass; + gint j; + + prop_edit = gtk_vbox_new (FALSE, 0); + + fclass = G_FLAGS_CLASS (g_type_class_ref (spec->value_type)); + + for (j = 0; j < fclass->n_values; j++) + { + GtkWidget *b; + + b = gtk_check_button_new_with_label (fclass->values[j].value_name); + g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j)); + gtk_widget_show (b); + gtk_box_pack_start (GTK_BOX (prop_edit), b, FALSE, FALSE, 0); + if (can_modify) + connect_controller (G_OBJECT (b), "toggled", + object, spec, G_CALLBACK (flags_modified)); + } + + g_type_class_unref (fclass); + + g_object_connect_property (object, spec, + G_CALLBACK (flags_changed), + prop_edit, G_OBJECT (prop_edit)); + } + } + else if (type == G_TYPE_PARAM_UNICHAR) + { + prop_edit = gtk_entry_new (); + gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1); + + g_object_connect_property (object, spec, + G_CALLBACK (unichar_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (unichar_modified)); + } + else if (type == G_TYPE_PARAM_POINTER) + { + prop_edit = gtk_label_new (""); + + g_object_connect_property (object, spec, + G_CALLBACK (pointer_changed), + prop_edit, G_OBJECT (prop_edit)); + } + else if (type == G_TYPE_PARAM_OBJECT) + { + GtkWidget *label, *button; + + prop_edit = gtk_hbox_new (FALSE, 5); + + label = gtk_label_new (""); + button = gtk_button_new_with_label ("Properties"); + g_object_set_data (G_OBJECT (button), "property-name", spec->name); + g_signal_connect (button, "clicked", + G_CALLBACK (object_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + g_object_connect_property (object, spec, + G_CALLBACK (object_changed), + prop_edit, G_OBJECT (label)); + } + else if (type == G_TYPE_PARAM_BOXED && + G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_COLOR) + { + prop_edit = gtk_color_button_new (); + + g_object_connect_property (object, spec, + G_CALLBACK (color_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "color-set", + object, spec, G_CALLBACK (color_modified)); + } + else + { + msg = g_strdup_printf ("uneditable property type: %s", + g_type_name (G_PARAM_SPEC_TYPE (spec))); + prop_edit = gtk_label_new (msg); + g_free (msg); + gtk_misc_set_alignment (GTK_MISC (prop_edit), 0.0, 0.5); + } + + return prop_edit; +} + +static GtkWidget * +properties_from_type (GObject *object, + GType type) +{ + GtkWidget *prop_edit; + GtkWidget *label; + GtkWidget *sw; + GtkWidget *vbox; + GtkWidget *table; + GParamSpec **specs; + guint n_specs; + int i; + + if (G_TYPE_IS_INTERFACE (type)) + { + gpointer vtable = g_type_default_interface_peek (type); + specs = g_object_interface_list_properties (vtable, &n_specs); + } + else + { + GObjectClass *class = G_OBJECT_CLASS (g_type_class_peek (type)); + specs = g_object_class_list_properties (class, &n_specs); + } + + if (n_specs == 0) { + g_free (specs); + return NULL; + } + + table = gtk_table_new (n_specs, 2, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + + i = 0; + while (i < n_specs) + { + GParamSpec *spec = specs[i]; + gboolean can_modify; + + prop_edit = NULL; + + can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 && + (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); + + if ((spec->flags & G_PARAM_READABLE) == 0) + { + /* can't display unreadable properties */ + ++i; + continue; + } + + if (spec->owner_type != type) + { + /* we're only interested in params of type */ + ++i; + continue; + } + + label = gtk_label_new (g_param_spec_get_nick (spec)); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, i, i + 1); + + prop_edit = property_widget (object, spec, can_modify); + gtk_table_attach_defaults (GTK_TABLE (table), prop_edit, 1, 2, i, i + 1); + + if (prop_edit) + { + if (!can_modify) + gtk_widget_set_sensitive (prop_edit, FALSE); + + if (g_param_spec_get_blurb (spec)) + gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec)); + + /* set initial value */ + g_object_notify (object, spec->name); + } + + ++i; + } + + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox); + + g_free (specs); + + return sw; +} + +static GtkWidget * +child_properties_from_object (GObject *object) +{ + GtkWidget *prop_edit; + GtkWidget *label; + GtkWidget *sw; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *parent; + GParamSpec **specs; + guint n_specs; + gint i; + + if (!GTK_IS_WIDGET (object)) + return NULL; + + parent = gtk_widget_get_parent (GTK_WIDGET (object)); + + if (!parent) + return NULL; + + specs = gtk_container_class_list_child_properties (G_OBJECT_GET_CLASS (parent), &n_specs); + + table = gtk_table_new (n_specs, 2, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + + i = 0; + while (i < n_specs) + { + GParamSpec *spec = specs[i]; + gboolean can_modify; + + prop_edit = NULL; + + can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 && + (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); + + if ((spec->flags & G_PARAM_READABLE) == 0) + { + /* can't display unreadable properties */ + ++i; + continue; + } + + label = gtk_label_new (g_param_spec_get_nick (spec)); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, i, i + 1); + + mark_child_property (spec); + prop_edit = property_widget (object, spec, can_modify); + gtk_table_attach_defaults (GTK_TABLE (table), prop_edit, 1, 2, i, i + 1); + + if (prop_edit) + { + if (!can_modify) + gtk_widget_set_sensitive (prop_edit, FALSE); + + if (g_param_spec_get_blurb (spec)) + gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec)); + + /* set initial value */ + gtk_widget_child_notify (GTK_WIDGET (object), spec->name); + } + + ++i; + } + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox); + + g_free (specs); + + return sw; +} + +static void +child_properties (GtkWidget *button, + GObject *object) +{ + create_prop_editor (object, 0); +} + +static GtkWidget * +children_from_object (GObject *object) +{ + GList *children, *c; + GtkWidget *table, *label, *prop_edit, *button, *vbox, *sw; + gchar *str; + gint i; + + if (!GTK_IS_CONTAINER (object)) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (object)); + + table = gtk_table_new (g_list_length (children), 2, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + + for (c = children, i = 0; c; c = c->next, i++) + { + object = c->data; + + label = gtk_label_new ("Child"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, i, i + 1); + + prop_edit = gtk_hbox_new (FALSE, 5); + + str = object_label (object, NULL); + label = gtk_label_new (str); + g_free (str); + button = gtk_button_new_with_label ("Properties"); + g_signal_connect (button, "clicked", + G_CALLBACK (child_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + gtk_table_attach_defaults (GTK_TABLE (table), prop_edit, 1, 2, i, i + 1); + } + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox); + + g_list_free (children); + + return sw; +} + +static GtkWidget * +cells_from_object (GObject *object) +{ + GList *cells, *c; + GtkWidget *table, *label, *prop_edit, *button, *vbox, *sw; + gchar *str; + gint i; + + if (!GTK_IS_CELL_LAYOUT (object)) + return NULL; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object)); + + table = gtk_table_new (g_list_length (cells), 2, FALSE); + gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10); + gtk_table_set_row_spacings (GTK_TABLE (table), 3); + + for (c = cells, i = 0; c; c = c->next, i++) + { + object = c->data; + + label = gtk_label_new ("Cell"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, i, i + 1); + + prop_edit = gtk_hbox_new (FALSE, 5); + + str = object_label (object, NULL); + label = gtk_label_new (str); + g_free (str); + button = gtk_button_new_with_label ("Properties"); + g_signal_connect (button, "clicked", + G_CALLBACK (child_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + gtk_table_attach_defaults (GTK_TABLE (table), prop_edit, 1, 2, i, i + 1); + } + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox); + + g_list_free (cells); + + return sw; +} + +/* Pass zero for type if you want all properties */ +GtkWidget* +create_prop_editor (GObject *object, + GType type) +{ + GtkWidget *win; + GtkWidget *notebook; + GtkWidget *properties; + GtkWidget *label; + gchar *title; + GType *ifaces; + guint n_ifaces; + + if ((win = g_object_get_data (G_OBJECT (object), "prop-editor-win"))) + { + gtk_window_present (GTK_WINDOW (win)); + return win; + } + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + if (GTK_IS_WIDGET (object)) + gtk_window_set_screen (GTK_WINDOW (win), + gtk_widget_get_screen (GTK_WIDGET (object))); + + /* hold a weak ref to the object we're editing */ + g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy); + g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy); + + if (type == 0) + { + notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT); + + gtk_container_add (GTK_CONTAINER (win), notebook); + + type = G_TYPE_FROM_INSTANCE (object); + + title = g_strdup_printf ("Properties of %s widget", g_type_name (type)); + gtk_window_set_title (GTK_WINDOW (win), title); + g_free (title); + + while (type) + { + properties = properties_from_type (object, type); + if (properties) + { + label = gtk_label_new (g_type_name (type)); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + type = g_type_parent (type); + } + + ifaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), &n_ifaces); + while (n_ifaces--) + { + properties = properties_from_type (object, ifaces[n_ifaces]); + if (properties) + { + label = gtk_label_new (g_type_name (ifaces[n_ifaces])); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + } + + g_free (ifaces); + + properties = child_properties_from_object (object); + if (properties) + { + label = gtk_label_new ("Child properties"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + properties = children_from_object (object); + if (properties) + { + label = gtk_label_new ("Children"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + properties = cells_from_object (object); + if (properties) + { + label = gtk_label_new ("Cell renderers"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + } + else + { + properties = properties_from_type (object, type); + gtk_container_add (GTK_CONTAINER (win), properties); + title = g_strdup_printf ("Properties of %s", g_type_name (type)); + gtk_window_set_title (GTK_WINDOW (win), title); + g_free (title); + } + + gtk_window_set_default_size (GTK_WINDOW (win), -1, 400); + + gtk_widget_show_all (win); + + return win; +} + diff --git a/testing/prop-editor.h b/testing/prop-editor.h new file mode 100644 index 0000000..377f817 --- /dev/null +++ b/testing/prop-editor.h @@ -0,0 +1,32 @@ +/* prop-editor.h + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 + +#ifndef __PROP_EDITOR_H__ +#define __PROP_EDITOR_H__ + +G_BEGIN_DECLS + +GtkWidget *create_prop_editor (GObject *object, + GType type); + +G_END_DECLS + +#endif /* __PROP_EDITOR_H__ */ diff --git a/testing/teststatusicon b/testing/teststatusicon new file mode 100755 index 0000000..8aa58f6 Binary files /dev/null and b/testing/teststatusicon differ diff --git a/testing/teststatusicon.c b/testing/teststatusicon.c new file mode 100644 index 0000000..4829bf0 --- /dev/null +++ b/testing/teststatusicon.c @@ -0,0 +1,358 @@ +/* gtkstatusicon-x11.c: + * + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Authors: + * Mark McLoughlin + */ + +#include +#include + +#include "prop-editor.h" + +typedef enum +{ + TEST_STATUS_INFO, + TEST_STATUS_QUESTION +} TestStatus; + +static TestStatus status = TEST_STATUS_INFO; +static gint timeout = 0; +static GSList *icons = NULL; + +static void +size_changed_cb (GtkStatusIcon *icon, + int size) +{ + g_print ("status icon %p size-changed size = %d\n", icon, size); +} + +static void +embedded_changed_cb (GtkStatusIcon *icon) +{ + g_print ("status icon %p embedded changed to %d\n", icon, + gtk_status_icon_is_embedded (icon)); +} + +static void +orientation_changed_cb (GtkStatusIcon *icon) +{ + GtkOrientation orientation; + + g_object_get (icon, "orientation", &orientation, NULL); + g_print ("status icon %p orientation changed to %d\n", icon, orientation); +} + +static void +screen_changed_cb (GtkStatusIcon *icon) +{ + g_print ("status icon %p screen changed to %p\n", icon, + gtk_status_icon_get_screen (icon)); +} + +static void +update_icons (void) +{ + GSList *l; + gchar *icon_name; + gchar *tooltip; + + if (status == TEST_STATUS_INFO) + { + icon_name = GTK_STOCK_DIALOG_INFO; + tooltip = "Some Information ..."; + } + else + { + icon_name = GTK_STOCK_DIALOG_QUESTION; + tooltip = "Some Question ..."; + } + + for (l = icons; l; l = l->next) + { + GtkStatusIcon *status_icon = l->data; + + gtk_status_icon_set_from_icon_name (status_icon, icon_name); + gtk_status_icon_set_tooltip_text (status_icon, tooltip); + } +} + +static gboolean +timeout_handler (gpointer data) +{ + if (status == TEST_STATUS_INFO) + status = TEST_STATUS_QUESTION; + else + status = TEST_STATUS_INFO; + + update_icons (); + + return TRUE; +} + +static void +blink_toggle_toggled (GtkToggleButton *toggle) +{ + GSList *l; + + for (l = icons; l; l = l->next) + gtk_status_icon_set_blinking (GTK_STATUS_ICON (l->data), + gtk_toggle_button_get_active (toggle)); +} + +static void +visible_toggle_toggled (GtkToggleButton *toggle) +{ + GSList *l; + + for (l = icons; l; l = l->next) + gtk_status_icon_set_visible (GTK_STATUS_ICON (l->data), + gtk_toggle_button_get_active (toggle)); +} + +static void +timeout_toggle_toggled (GtkToggleButton *toggle) +{ + if (timeout) + { + g_source_remove (timeout); + timeout = 0; + } + else + { + timeout = gdk_threads_add_timeout (2000, timeout_handler, NULL); + } +} + +static void +icon_activated (GtkStatusIcon *icon) +{ + GtkWidget *dialog; + GtkWidget *toggle; + + dialog = g_object_get_data (G_OBJECT (icon), "test-status-icon-dialog"); + if (dialog == NULL) + { + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CLOSE, + "You wanna test the status icon ?"); + + gtk_window_set_screen (GTK_WINDOW (dialog), gtk_status_icon_get_screen (icon)); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); + + g_object_set_data_full (G_OBJECT (icon), "test-status-icon-dialog", + dialog, (GDestroyNotify) gtk_widget_destroy); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_hide), NULL); + g_signal_connect (dialog, "delete_event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + + toggle = gtk_toggle_button_new_with_mnemonic ("_Show the icon"); + gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + gtk_status_icon_get_visible (icon)); + g_signal_connect (toggle, "toggled", + G_CALLBACK (visible_toggle_toggled), NULL); + + toggle = gtk_toggle_button_new_with_mnemonic ("_Blink the icon"); + gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + gtk_status_icon_get_blinking (icon)); + g_signal_connect (toggle, "toggled", + G_CALLBACK (blink_toggle_toggled), NULL); + + toggle = gtk_toggle_button_new_with_mnemonic ("_Change images"); + gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + timeout != 0); + g_signal_connect (toggle, "toggled", + G_CALLBACK (timeout_toggle_toggled), NULL); + } + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +check_activated (GtkCheckMenuItem *item) +{ + GSList *l; + GdkScreen *screen; + + screen = NULL; + + for (l = icons; l; l = l->next) + { + GtkStatusIcon *icon = l->data; + GdkScreen *orig_screen; + + orig_screen = gtk_status_icon_get_screen (icon); + + if (screen != NULL) + gtk_status_icon_set_screen (icon, screen); + + screen = orig_screen; + + gtk_status_icon_set_blinking (icon, + gtk_check_menu_item_get_active (item)); + } + + g_assert (screen != NULL); + + gtk_status_icon_set_screen (GTK_STATUS_ICON (icons->data), screen); +} + +static void +do_properties (GtkMenuItem *item, + GtkStatusIcon *icon) +{ + static GtkWidget *editor = NULL; + + if (editor == NULL) { + editor = create_prop_editor (G_OBJECT (icon), GTK_TYPE_STATUS_ICON); + g_signal_connect (editor, "destroy", G_CALLBACK (gtk_widget_destroyed), &editor); + } + + gtk_window_present (GTK_WINDOW (editor)); +} + +static void +do_quit (GtkMenuItem *item) +{ + GSList *l; + + for (l = icons; l; l = l->next) + { + GtkStatusIcon *icon = l->data; + + gtk_status_icon_set_visible (icon, FALSE); + g_object_unref (icon); + } + + g_slist_free (icons); + icons = NULL; + + gtk_main_quit (); +} + +static void +do_exit (GtkMenuItem *item) +{ + exit (0); +} + +static void +popup_menu (GtkStatusIcon *icon, + guint button, + guint32 activate_time) +{ + GtkWidget *menu, *menuitem; + + menu = gtk_menu_new (); + + gtk_menu_set_screen (GTK_MENU (menu), + gtk_status_icon_get_screen (icon)); + + menuitem = gtk_check_menu_item_new_with_label ("Blink"); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), + gtk_status_icon_get_blinking (icon)); + g_signal_connect (menuitem, "activate", G_CALLBACK (check_activated), NULL); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + + gtk_widget_show (menuitem); + + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL); + g_signal_connect (menuitem, "activate", G_CALLBACK (do_properties), icon); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + + gtk_widget_show (menuitem); + + menuitem = gtk_menu_item_new_with_label ("Quit"); + g_signal_connect (menuitem, "activate", G_CALLBACK (do_quit), NULL); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + + gtk_widget_show (menuitem); + + menuitem = gtk_menu_item_new_with_label ("Exit abruptly"); + g_signal_connect (menuitem, "activate", G_CALLBACK (do_exit), NULL); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + + gtk_widget_show (menuitem); + + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, + gtk_status_icon_position_menu, icon, + button, activate_time); +} + +int +main (int argc, char **argv) +{ + GdkDisplay *display; + guint n_screens, i; + + gtk_init (&argc, &argv); + + display = gdk_display_get_default (); + + n_screens = gdk_display_get_n_screens (display); + + for (i = 0; i < n_screens; i++) + { + GtkStatusIcon *icon; + + icon = gtk_status_icon_new (); + gtk_status_icon_set_screen (icon, gdk_display_get_screen (display, i)); + update_icons (); + + g_signal_connect (icon, "size-changed", G_CALLBACK (size_changed_cb), NULL); + g_signal_connect (icon, "notify::embedded", G_CALLBACK (embedded_changed_cb), NULL); + g_signal_connect (icon, "notify::orientation", G_CALLBACK (orientation_changed_cb), NULL); + g_signal_connect (icon, "notify::screen", G_CALLBACK (screen_changed_cb), NULL); + g_print ("icon size %d\n", gtk_status_icon_get_size (icon)); + gtk_status_icon_set_blinking (GTK_STATUS_ICON (icon), FALSE); + + g_signal_connect (icon, "activate", + G_CALLBACK (icon_activated), NULL); + + g_signal_connect (icon, "popup-menu", + G_CALLBACK (popup_menu), NULL); + + icons = g_slist_append (icons, icon); + + update_icons (); + + timeout = gdk_threads_add_timeout (2000, timeout_handler, icon); + } + + gtk_main (); + + return 0; +}