From 414bbf42aaac2a57c1945074c2713368314c9064 Mon Sep 17 00:00:00 2001 From: foudfou Date: Mon, 30 Jan 2012 22:07:21 +0100 Subject: [PATCH 1/2] begin attempt to restore windows to their correct screen (dual-head) --- src/modules/FiretrayHandler.jsm | 31 + src/modules/ctypes/gdk.jsm | 3 + testing/Makefile | 5 +- testing/prop-editor.c | 1345 +++++++++++++++++++++++++++++++ testing/prop-editor.h | 32 + testing/teststatusicon | Bin 0 -> 49642 bytes testing/teststatusicon.c | 358 ++++++++ 7 files changed, 1773 insertions(+), 1 deletion(-) create mode 100644 testing/prop-editor.c create mode 100644 testing/prop-editor.h create mode 100755 testing/teststatusicon create mode 100644 testing/teststatusicon.c diff --git a/src/modules/FiretrayHandler.jsm b/src/modules/FiretrayHandler.jsm index af83cd7..3b7ab01 100644 --- a/src/modules/FiretrayHandler.jsm +++ b/src/modules/FiretrayHandler.jsm @@ -114,10 +114,41 @@ firetray.Handler = { }); firetray.VersionChange.watch(); + // TEST - check Screens and Monitors ! + try { + Cu.import("resource://firetray/ctypes/x11.jsm"); + Cu.import("resource://firetray/ctypes/xinerama.jsm"); + LOG("XINERAMA: "+xinerama.XineramaIsActive(x11.current.Display)); + + Cu.import("resource://firetray/ctypes/gdk.jsm"); + LOG("SCREENS: "+gdk.gdk_display_get_n_screens(gdk.gdk_display_get_default())); + + // GdkScreen *screen = gtk_widget_get_screen (widget); + // gint i = gdk_screen_get_monitor_at_window (screen, + // widget->window); + + + // let dl = gdk.gdk_display_list_devices(gdk.gdk_display_get_default()); // "should not be freed" + // LOG("LENGTH="+gobject.g_list_length(dl)); + // let mozt_func = gobject.GFunc_t(firetray.Handler.glist_foreach); + // gobject.g_list_foreach(dl, mozt_func, null); + + } catch(x) {ERROR(x);} + this.initialized = true; return true; }, + // TEST + glist_foreach: function(a1, a2) { + LOG("glist_foreach: "+a1); + let gdkDev = ctypes.cast(a1, gdk.GdkDevice.ptr); + let devName = gdk.gdk_device_get_name(gdkDev); + LOG(devName.readString()); + LOG(gdk.gdk_device_get_source(gdkDev)); + LOG(gdk.gdk_device_get_mode(gdkDev)); + }, + shutdown: function() { firetray.PrefListener.unregister(); diff --git a/src/modules/ctypes/gdk.jsm b/src/modules/ctypes/gdk.jsm index 84df88e..0711c58 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/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 0000000000000000000000000000000000000000..8aa58f6373c9f1ad3409fc14b229cd9004d1cfe0 GIT binary patch literal 49642 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 VvKj+U;rbNMF5KnFjd`92{y(v=+Y0~y literal 0 HcmV?d00001 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; +} From e96699d5302d0e4fbf42247e6871d3d76dc5b8ff Mon Sep 17 00:00:00 2001 From: foudfou Date: Sat, 4 Feb 2012 14:18:20 +0100 Subject: [PATCH 2/2] fix restoreWindowPositionAndSize on dual-screen --- src/modules/FiretrayHandler.jsm | 31 ----------------------------- src/modules/ctypes/xinerama.jsm | 23 +++++++++++++++++++++ src/modules/gtk2/FiretrayWindow.jsm | 4 ++-- 3 files changed, 25 insertions(+), 33 deletions(-) create mode 100644 src/modules/ctypes/xinerama.jsm diff --git a/src/modules/FiretrayHandler.jsm b/src/modules/FiretrayHandler.jsm index 3b7ab01..af83cd7 100644 --- a/src/modules/FiretrayHandler.jsm +++ b/src/modules/FiretrayHandler.jsm @@ -114,41 +114,10 @@ firetray.Handler = { }); firetray.VersionChange.watch(); - // TEST - check Screens and Monitors ! - try { - Cu.import("resource://firetray/ctypes/x11.jsm"); - Cu.import("resource://firetray/ctypes/xinerama.jsm"); - LOG("XINERAMA: "+xinerama.XineramaIsActive(x11.current.Display)); - - Cu.import("resource://firetray/ctypes/gdk.jsm"); - LOG("SCREENS: "+gdk.gdk_display_get_n_screens(gdk.gdk_display_get_default())); - - // GdkScreen *screen = gtk_widget_get_screen (widget); - // gint i = gdk_screen_get_monitor_at_window (screen, - // widget->window); - - - // let dl = gdk.gdk_display_list_devices(gdk.gdk_display_get_default()); // "should not be freed" - // LOG("LENGTH="+gobject.g_list_length(dl)); - // let mozt_func = gobject.GFunc_t(firetray.Handler.glist_foreach); - // gobject.g_list_foreach(dl, mozt_func, null); - - } catch(x) {ERROR(x);} - this.initialized = true; return true; }, - // TEST - glist_foreach: function(a1, a2) { - LOG("glist_foreach: "+a1); - let gdkDev = ctypes.cast(a1, gdk.GdkDevice.ptr); - let devName = gdk.gdk_device_get_name(gdkDev); - LOG(devName.readString()); - LOG(gdk.gdk_device_get_source(gdkDev)); - LOG(gdk.gdk_device_get_mode(gdkDev)); - }, - shutdown: function() { firetray.PrefListener.unregister(); 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 edc07d4..dba01e1 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);