[gtk+/parasite2: 1/38] Initial import
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/parasite2: 1/38] Initial import
- Date: Thu, 8 May 2014 22:15:56 +0000 (UTC)
commit b0a2dedc3776811e7579690b595b504b01ce82ff
Author: Matthias Clasen <mclasen redhat com>
Date: Fri May 2 21:48:33 2014 -0400
Initial import
This is a copy of https://github.com/chipx86/gtkparasite.git
with minimal edits to make it build.
configure.ac | 2 +
modules/Makefile.am | 2 +-
modules/other/Makefile.am | 1 +
modules/other/parasite/Makefile.am | 52 +++
modules/other/parasite/button-path.c | 103 ++++++
modules/other/parasite/button-path.h | 60 ++++
modules/other/parasite/classes-list.c | 344 +++++++++++++++++++
modules/other/parasite/classes-list.h | 58 ++++
modules/other/parasite/css-editor.c | 388 +++++++++++++++++++++
modules/other/parasite/css-editor.h | 59 ++++
modules/other/parasite/inspect-button.c | 242 +++++++++++++
modules/other/parasite/module.c | 40 +++
modules/other/parasite/object-hierarchy.c | 126 +++++++
modules/other/parasite/object-hierarchy.h | 58 ++++
modules/other/parasite/parasite.h | 69 ++++
modules/other/parasite/prop-list.c | 367 ++++++++++++++++++++
modules/other/parasite/prop-list.h | 60 ++++
modules/other/parasite/property-cell-renderer.c | 410 ++++++++++++++++++++++
modules/other/parasite/property-cell-renderer.h | 70 ++++
modules/other/parasite/python-hooks.c | 238 +++++++++++++
modules/other/parasite/python-hooks.h | 38 ++
modules/other/parasite/python-shell.c | 409 ++++++++++++++++++++++
modules/other/parasite/python-shell.h | 68 ++++
modules/other/parasite/themes.c | 177 ++++++++++
modules/other/parasite/themes.h | 56 +++
modules/other/parasite/widget-tree.c | 418 +++++++++++++++++++++++
modules/other/parasite/widget-tree.h | 78 +++++
modules/other/parasite/window.c | 318 +++++++++++++++++
28 files changed, 4310 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 70912f3..195d874 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1898,6 +1898,8 @@ modules/printbackends/lpr/Makefile
modules/printbackends/file/Makefile
modules/printbackends/papi/Makefile
modules/printbackends/test/Makefile
+modules/other/Makefile
+modules/other/parasite/Makefile
])
AC_OUTPUT
diff --git a/modules/Makefile.am b/modules/Makefile.am
index f8e7bb8..34b26ac 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -1,6 +1,6 @@
include $(top_srcdir)/Makefile.decl
-SUBDIRS = input
+SUBDIRS = input other
if OS_UNIX
SUBDIRS += printbackends
diff --git a/modules/other/Makefile.am b/modules/other/Makefile.am
new file mode 100644
index 0000000..e168924
--- /dev/null
+++ b/modules/other/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = parasite
diff --git a/modules/other/parasite/Makefile.am b/modules/other/parasite/Makefile.am
new file mode 100644
index 0000000..03243d6
--- /dev/null
+++ b/modules/other/parasite/Makefile.am
@@ -0,0 +1,52 @@
+moduledir = $(libdir)/gtk-3.0/modules
+
+module_LTLIBRARIES = libgtkparasite.la
+
+libgtkparasite_la_SOURCES = \
+ inspect-button.c \
+ module.c \
+ parasite.h \
+ prop-list.h \
+ prop-list.c \
+ property-cell-renderer.c \
+ property-cell-renderer.h \
+ python-hooks.c \
+ python-hooks.h \
+ python-shell.c \
+ python-shell.h \
+ widget-tree.h \
+ widget-tree.c \
+ window.c \
+ button-path.h \
+ button-path.c \
+ classes-list.h \
+ classes-list.c \
+ css-editor.h \
+ css-editor.c \
+ object-hierarchy.h \
+ object-hierarchy.c \
+ themes.h \
+ themes.c
+
+libgtkparasite_la_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gtk \
+ -I$(top_builddir)/gtk \
+ -I$(top_srcdir)/gdk \
+ -I$(top_builddir)/gdk \
+ -DGTK_DATADIR=\"$(datadir)\" \
+ -DGTK_COMPILATION \
+ $(GTK_DEP_CFLAGS) \
+ $(GTK_DEBUG_FLAGS)
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+libgtkparasite_la_LDFLAGS = -avoid-version -module $(no_undefined)
+
+libgtkparasite_la_LIBADD = \
+ $(top_builddir)/gtk/libgtk-3.la \
+ $(GTK_DEP_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/other/parasite/button-path.c b/modules/other/parasite/button-path.c
new file mode 100644
index 0000000..b8f3510
--- /dev/null
+++ b/modules/other/parasite/button-path.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "button-path.h"
+
+struct _ParasiteButtonPathPrivate
+{
+ GtkWidget *sw;
+ GtkWidget *button_box;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteButtonPath, parasite_buttonpath, GTK_TYPE_BOX)
+
+static void
+parasite_buttonpath_init (ParasiteButtonPath *bp)
+{
+ GtkWidget *label;
+
+ bp->priv = parasite_buttonpath_get_instance_private (bp);
+
+ g_object_set (bp,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ NULL);
+
+ bp->priv->sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar-policy", GTK_POLICY_NEVER,
+ "hexpand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (bp), bp->priv->sw);
+
+ bp->priv->button_box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "hexpand", TRUE,
+ "margin", 6,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (bp->priv->button_box), "linked");
+ gtk_container_add (GTK_CONTAINER (bp->priv->sw), bp->priv->button_box);
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", "Choose a widget through the inspector",
+ "xalign", 0.5,
+ "hexpand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (bp->priv->button_box), label);
+}
+
+static void
+parasite_buttonpath_class_init(ParasiteButtonPathClass *klass)
+{
+}
+
+GtkWidget *
+parasite_buttonpath_new ()
+{
+ return GTK_WIDGET (g_object_new (PARASITE_TYPE_BUTTONPATH, NULL));
+}
+
+void
+parasite_buttonpath_set_widget(ParasiteButtonPath *bp,
+ GtkWidget *widget)
+{
+ char *path, **words;
+ int i;
+ GtkWidget *b;
+ GtkContainer *box = GTK_CONTAINER (bp->priv->button_box);
+
+ gtk_container_foreach (box, (GtkCallback)gtk_widget_destroy, NULL);
+
+ path = gtk_widget_path_to_string (gtk_widget_get_path (widget));
+ words = g_strsplit (path, " ", 0);
+
+ for (i = 0; i < g_strv_length (words); i++)
+ {
+ b = gtk_button_new_with_label (words[i]);
+ gtk_widget_show (b);
+ gtk_container_add (box, b);
+ }
+
+ g_strfreev (words);
+ g_free (path);
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/button-path.h b/modules/other/parasite/button-path.h
new file mode 100644
index 0000000..e05424c
--- /dev/null
+++ b/modules/other/parasite/button-path.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_BUTTONPATH_H_
+#define _GTKPARASITE_BUTTONPATH_H_
+
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_BUTTONPATH (parasite_buttonpath_get_type())
+#define PARASITE_BUTTONPATH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_BUTTONPATH,
ParasiteButtonPath))
+#define PARASITE_BUTTONPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_BUTTONPATH,
ParasiteButtonPathClass))
+#define PARASITE_IS_BUTTONPATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_BUTTONPATH))
+#define PARASITE_IS_BUTTONPATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_BUTTONPATH))
+#define PARASITE_BUTTONPATH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_BUTTONPATH,
ParasiteButtonPathClass))
+
+
+typedef struct _ParasiteButtonPathPrivate ParasiteButtonPathPrivate;
+
+typedef struct _ParasiteButtonPath {
+ GtkBox parent;
+ ParasiteButtonPathPrivate *priv;
+} ParasiteButtonPath;
+
+typedef struct _ParasiteButtonPathClass {
+ GtkBoxClass parent;
+} ParasiteButtonPathClass;
+
+
+G_BEGIN_DECLS
+
+GType parasite_buttonpath_get_type ();
+GtkWidget *parasite_buttonpath_new ();
+void parasite_buttonpath_set_widget (ParasiteButtonPath *bp,
+ GtkWidget *widget);
+
+G_END_DECLS
+
+
+#endif // _GTKPARASITE_BUTTONPATH_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/classes-list.c b/modules/other/parasite/classes-list.c
new file mode 100644
index 0000000..6164c4c
--- /dev/null
+++ b/modules/other/parasite/classes-list.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "classes-list.h"
+#include "parasite.h"
+
+enum
+{
+ COLUMN_ENABLED,
+ COLUMN_NAME,
+ COLUMN_USER,
+ NUM_COLUMNS
+};
+
+typedef struct
+{
+ gboolean enabled;
+ gboolean user;
+} ParasiteClassesListByContext;
+
+struct _ParasiteClassesListPrivate
+{
+ GtkWidget *toolbar;
+ GtkWidget *view;
+ GtkListStore *model;
+ GHashTable *contexts;
+ GtkStyleContext *current_context;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteClassesList, parasite_classeslist, GTK_TYPE_BOX)
+
+static void
+enabled_toggled (GtkCellRendererToggle *renderer, gchar *path, ParasiteClassesList *cl)
+{
+ GtkTreeIter iter;
+ gboolean enabled;
+ GHashTable *context;
+ ParasiteClassesListByContext *c;
+ gchar *name;
+
+ if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (cl->priv->model), &iter, path))
+ {
+ g_warning ("Parasite: Couldn't find the css class path for %s.", path);
+ return;
+ }
+
+ gtk_tree_model_get (GTK_TREE_MODEL (cl->priv->model), &iter,
+ COLUMN_ENABLED, &enabled,
+ COLUMN_NAME, &name,
+ -1);
+ enabled = !enabled;
+ gtk_list_store_set (cl->priv->model, &iter,
+ COLUMN_ENABLED, enabled,
+ -1);
+
+ context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+ if (context)
+ {
+ c = g_hash_table_lookup (context, name);
+ if (c)
+ {
+ c->enabled = enabled;
+ if (enabled)
+ {
+ gtk_style_context_add_class (cl->priv->current_context, name);
+ }
+ else
+ {
+ gtk_style_context_remove_class (cl->priv->current_context, name);
+ }
+ }
+ else
+ {
+ g_warning ("Parasite: Couldn't find the css class %s in the class hash table.", name);
+ }
+ }
+ else
+ {
+ g_warning ("Parasite: Couldn't find the hash table for the style context for css class %s.", name);
+ }
+}
+
+static void
+add_clicked (GtkButton *button, ParasiteClassesList *cl)
+{
+ GtkWidget *dialog, *content_area, *entry;
+
+ dialog = gtk_dialog_new_with_buttons ("New class",
+ GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (cl))),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ "_OK", GTK_RESPONSE_OK,
+ "Cancel", GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ entry = g_object_new (GTK_TYPE_ENTRY,
+ "visible", TRUE,
+ "margin", 5,
+ "placeholder-text", "Class name",
+ "activates-default", TRUE,
+ NULL);
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_container_add (GTK_CONTAINER (content_area), entry);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry));
+ GHashTable *context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+
+ if (*name && !g_hash_table_contains (context, name))
+ {
+ GtkTreeIter tree_iter;
+
+ gtk_style_context_add_class (cl->priv->current_context, name);
+
+ ParasiteClassesListByContext *c = g_new0 (ParasiteClassesListByContext, 1);
+ c->enabled = TRUE;
+ c->user = TRUE;
+ g_hash_table_insert (context, (gpointer)g_strdup (name), c);
+
+ gtk_list_store_append (cl->priv->model, &tree_iter);
+ gtk_list_store_set (cl->priv->model, &tree_iter,
+ COLUMN_ENABLED, TRUE,
+ COLUMN_NAME, name,
+ COLUMN_USER, TRUE,
+ -1);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+read_classes_from_style_context (ParasiteClassesList *cl)
+{
+ GList *l, *classes;
+ ParasiteClassesListByContext *c;
+ GtkTreeIter tree_iter;
+ GHashTable *hash_context;
+
+ hash_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ classes = gtk_style_context_list_classes (cl->priv->current_context);
+
+ for (l = classes; l; l = l->next)
+ {
+ c = g_new0 (ParasiteClassesListByContext, 1);
+ c->enabled = TRUE;
+ g_hash_table_insert (hash_context, g_strdup (l->data), c);
+
+ gtk_list_store_append (cl->priv->model, &tree_iter);
+ gtk_list_store_set (cl->priv->model, &tree_iter,
+ COLUMN_ENABLED, TRUE,
+ COLUMN_NAME, l->data,
+ COLUMN_USER, FALSE,
+ -1);
+ }
+ g_list_free (classes);
+ g_hash_table_replace (cl->priv->contexts, cl->priv->current_context, hash_context);
+}
+
+static void
+restore_defaults_clicked (GtkButton *button, ParasiteClassesList *cl)
+{
+ GHashTableIter hash_iter;
+ gchar *name;
+ ParasiteClassesListByContext *c;
+ GHashTable *hash_context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+
+ g_hash_table_iter_init (&hash_iter, hash_context);
+ while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c))
+ {
+ if (c->user)
+ {
+ gtk_style_context_remove_class (cl->priv->current_context, name);
+ }
+ else if (!c->enabled)
+ {
+ gtk_style_context_add_class (cl->priv->current_context, name);
+ }
+ }
+
+ gtk_list_store_clear (cl->priv->model);
+ read_classes_from_style_context (cl);
+}
+
+static void
+create_toolbar (ParasiteClassesList *cl)
+{
+ GtkWidget *button;
+
+ cl->priv->toolbar = g_object_new (GTK_TYPE_TOOLBAR,
+ "icon-size", GTK_ICON_SIZE_SMALL_TOOLBAR,
+ "sensitive", FALSE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (cl), cl->priv->toolbar);
+
+ button = g_object_new (GTK_TYPE_TOOL_BUTTON,
+ "icon-name", "add",
+ "tooltip-text", "Add a class",
+ NULL);
+ g_signal_connect (button, "clicked", G_CALLBACK (add_clicked), cl);
+ gtk_container_add (GTK_CONTAINER (cl->priv->toolbar), button);
+
+ button = g_object_new (GTK_TYPE_TOOL_BUTTON,
+ "icon-name", "revert",
+ "tooltip-text", "Restore defaults for this widget",
+ NULL);
+ g_signal_connect (button, "clicked", G_CALLBACK (restore_defaults_clicked), cl);
+ gtk_container_add (GTK_CONTAINER (cl->priv->toolbar), button);
+}
+
+static void
+draw_name_column (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ ParasiteClassesList *cl)
+{
+ gboolean user;
+
+ gtk_tree_model_get (model, iter, COLUMN_USER, &user, -1);
+ if (user)
+ {
+ g_object_set (renderer, "style", PANGO_STYLE_ITALIC, NULL);
+ }
+ else
+ {
+ g_object_set (renderer, "style", PANGO_STYLE_NORMAL, NULL);
+ }
+}
+
+static void
+parasite_classeslist_init (ParasiteClassesList *cl)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *sw;
+
+ g_object_set (cl, "orientation", GTK_ORIENTATION_VERTICAL, NULL);
+
+ cl->priv = parasite_classeslist_get_instance_private (cl);
+
+ create_toolbar (cl);
+
+ sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "expand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (cl), sw);
+
+ cl->priv->contexts = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_hash_table_destroy);
+ cl->priv->model = gtk_list_store_new (NUM_COLUMNS,
+ G_TYPE_BOOLEAN, // COLUMN_ENABLED
+ G_TYPE_STRING, // COLUMN_NAME
+ G_TYPE_BOOLEAN); // COLUMN_USER
+ cl->priv->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (cl->priv->model));
+ gtk_container_add (GTK_CONTAINER (sw), cl->priv->view);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (renderer, "toggled", G_CALLBACK (enabled_toggled), cl);
+ column = gtk_tree_view_column_new_with_attributes ("", renderer,
+ "active", COLUMN_ENABLED,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (cl->priv->view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "scale", TREE_TEXT_SCALE, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
+ "text", COLUMN_NAME,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc)draw_name_column, cl,
NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (cl->priv->view), column);
+}
+
+void
+parasite_classeslist_set_widget (ParasiteClassesList *cl,
+ GtkWidget *widget)
+{
+ GtkStyleContext *widget_context;
+ GHashTable *hash_context;
+ GtkTreeIter tree_iter;
+ ParasiteClassesListByContext *c;
+
+ gtk_list_store_clear (cl->priv->model);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (cl), TRUE);
+ widget_context = gtk_widget_get_style_context (widget);
+
+ cl->priv->current_context = widget_context;
+ gtk_widget_set_sensitive (cl->priv->toolbar, TRUE);
+
+ hash_context = g_hash_table_lookup (cl->priv->contexts, widget_context);
+ if (hash_context)
+ {
+ GHashTableIter hash_iter;
+ gchar *name;
+
+ g_hash_table_iter_init (&hash_iter, hash_context);
+ while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c))
+ {
+ gtk_list_store_append (cl->priv->model, &tree_iter);
+ gtk_list_store_set (cl->priv->model, &tree_iter,
+ COLUMN_ENABLED, c->enabled,
+ COLUMN_NAME, name,
+ COLUMN_USER, c->user,
+ -1);
+ }
+ }
+ else
+ {
+ read_classes_from_style_context (cl);
+ }
+}
+
+static void
+parasite_classeslist_class_init (ParasiteClassesListClass *klass)
+{
+}
+
+GtkWidget *
+parasite_classeslist_new ()
+{
+ return GTK_WIDGET (g_object_new (PARASITE_TYPE_CLASSESLIST, NULL));
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/classes-list.h b/modules/other/parasite/classes-list.h
new file mode 100644
index 0000000..19bc800
--- /dev/null
+++ b/modules/other/parasite/classes-list.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GTKPARASITE_CLASSESLIST_H_
+#define _GTKPARASITE_CLASSESLIST_H_
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_CLASSESLIST (parasite_classeslist_get_type())
+#define PARASITE_CLASSESLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_CLASSESLIST,
ParasiteClassesList))
+#define PARASITE_CLASSESLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_CLASSESLIST,
ParasiteClassesListClass))
+#define PARASITE_IS_CLASSESLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_CLASSESLIST))
+#define PARASITE_IS_CLASSESLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_CLASSESLIST))
+#define PARASITE_CLASSESLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_CLASSESLIST,
ParasiteClassesListClass))
+
+
+typedef struct _ParasiteClassesListPrivate ParasiteClassesListPrivate;
+
+typedef struct _ParasiteClassesList {
+ GtkBox parent;
+ ParasiteClassesListPrivate *priv;
+} ParasiteClassesList;
+
+typedef struct _ParasiteClassesListClass {
+ GtkBoxClass parent;
+} ParasiteClassesListClass;
+
+G_BEGIN_DECLS
+
+GType parasite_classeslist_get_type ();
+GtkWidget *parasite_classeslist_new ();
+void parasite_classeslist_set_widget (ParasiteClassesList* classeslist,
+ GtkWidget *widget);
+
+G_END_DECLS
+
+#endif // _GTKPARASITE_CLASSESLIST_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/css-editor.c b/modules/other/parasite/css-editor.c
new file mode 100644
index 0000000..ca4dd5b
--- /dev/null
+++ b/modules/other/parasite/css-editor.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "css-editor.h"
+#include "parasite.h"
+
+#define PARASITE_CSSEDITOR_TEXT "parasite-csseditor-text"
+#define PARASITE_CSSEDITOR_PROVIDER "parasite-csseditor-provider"
+
+enum
+{
+ COLUMN_ENABLED,
+ COLUMN_NAME,
+ COLUMN_USER,
+ NUM_COLUMNS
+};
+
+enum
+{
+ PROP_0,
+ PROP_GLOBAL
+};
+
+typedef struct
+{
+ gboolean enabled;
+ gboolean user;
+} ParasiteCssEditorByContext;
+
+struct _ParasiteCssEditorPrivate
+{
+ GtkWidget *toolbar;
+ GtkTextBuffer *text;
+ GtkCssProvider *provider;
+ gboolean global;
+ GtkStyleContext *selected_context;
+ GtkToggleToolButton *disable_button;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteCssEditor, parasite_csseditor, GTK_TYPE_BOX)
+
+static const gchar *initial_text_global =
+ "/*\n"
+ "You can type here any CSS rule recognized by GTK+.\n"
+ "You can temporarily disable this custom CSS by clicking on the \"Pause\" button above.\n\n"
+ "Changes are applied instantly and globally, for the whole application.\n"
+ "*/\n\n";
+static const gchar *initial_text_widget =
+ "/*\n"
+ "You can type here any CSS rule recognized by GTK+.\n"
+ "You can temporarily disable this custom CSS by clicking on the \"Pause\" button above.\n\n"
+ "Changes are applied instantly, only for this selected widget.\n"
+ "*/\n\n";
+
+static void
+disable_toggled (GtkToggleToolButton *button, ParasiteCssEditor *editor)
+{
+ if (gtk_toggle_tool_button_get_active (button))
+ {
+ if (editor->priv->global)
+ {
+ gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
+ GTK_STYLE_PROVIDER (editor->priv->provider));
+ }
+ else if (editor->priv->selected_context)
+ {
+ gtk_style_context_remove_provider (editor->priv->selected_context,
+ GTK_STYLE_PROVIDER (g_object_get_data (G_OBJECT
(editor->priv->selected_context), PARASITE_CSSEDITOR_PROVIDER)));
+ }
+ }
+ else
+ {
+ if (editor->priv->global)
+ {
+ gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+ GTK_STYLE_PROVIDER (editor->priv->provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ }
+ else if (editor->priv->selected_context)
+ {
+ gtk_style_context_add_provider (editor->priv->selected_context,
+ GTK_STYLE_PROVIDER (g_object_get_data (G_OBJECT
(editor->priv->selected_context), PARASITE_CSSEDITOR_PROVIDER)),
+ G_MAXUINT);
+ }
+ }
+}
+
+static void
+create_toolbar (ParasiteCssEditor *editor)
+{
+ editor->priv->toolbar = g_object_new (GTK_TYPE_TOOLBAR,
+ "icon-size", GTK_ICON_SIZE_SMALL_TOOLBAR,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (editor), editor->priv->toolbar);
+
+ editor->priv->disable_button = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON,
+ "icon-name", "media-playback-pause",
+ "tooltip-text", "Disable this custom css",
+ NULL);
+ g_signal_connect (editor->priv->disable_button,
+ "toggled",
+ G_CALLBACK (disable_toggled),
+ editor);
+ gtk_container_add (GTK_CONTAINER (editor->priv->toolbar),
+ GTK_WIDGET (editor->priv->disable_button));
+}
+
+static void
+apply_system_font (GtkWidget *widget)
+{
+ GSettings *s = g_settings_new ("org.gnome.desktop.interface");
+ gchar *font_name = g_settings_get_string (s, "monospace-font-name");
+ PangoFontDescription *font_desc = pango_font_description_from_string (font_name);
+
+ gtk_widget_override_font (widget, font_desc);
+
+ pango_font_description_free (font_desc);
+ g_free (font_name);
+ g_object_unref (s);
+}
+
+static gchar *
+get_current_text (GtkTextBuffer *buffer)
+{
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ gtk_text_buffer_remove_all_tags (buffer, &start, &end);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static void
+text_changed (GtkTextBuffer *buffer, ParasiteCssEditor *editor)
+{
+ GtkCssProvider *provider;
+ char *text;
+
+ if (editor->priv->global)
+ provider = editor->priv->provider;
+ else if (editor->priv->selected_context)
+ provider = g_object_get_data (G_OBJECT (editor->priv->selected_context), PARASITE_CSSEDITOR_PROVIDER);
+ else
+ return;
+
+ text = get_current_text (buffer);
+ gtk_css_provider_load_from_data (provider, text, -1, NULL);
+ g_free (text);
+
+ gtk_style_context_reset_widgets (gdk_screen_get_default ());
+}
+
+static void
+show_parsing_error (GtkCssProvider *provider,
+ GtkCssSection *section,
+ const GError *error,
+ ParasiteCssEditor *editor)
+{
+ GtkTextIter start, end;
+ const char *tag_name;
+ GtkTextBuffer *buffer = GTK_TEXT_BUFFER (editor->priv->text);
+
+ gtk_text_buffer_get_iter_at_line_index (buffer,
+ &start,
+ gtk_css_section_get_start_line (section),
+ gtk_css_section_get_start_position (section));
+ gtk_text_buffer_get_iter_at_line_index (buffer,
+ &end,
+ gtk_css_section_get_end_line (section),
+ gtk_css_section_get_end_position (section));
+
+ if (g_error_matches (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED))
+ tag_name = "warning";
+ else
+ tag_name = "error";
+
+ gtk_text_buffer_apply_tag_by_name (buffer, tag_name, &start, &end);
+}
+
+static void
+create_text_widget (ParasiteCssEditor *editor)
+{
+ GtkWidget *sw, *view;
+
+ editor->priv->text = gtk_text_buffer_new (NULL);
+
+ if (editor->priv->global)
+ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (editor->priv->text), initial_text_global, -1);
+ else
+ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (editor->priv->text), initial_text_widget, -1);
+
+ g_signal_connect (editor->priv->text, "changed", G_CALLBACK (text_changed), editor);
+
+ gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (editor->priv->text),
+ "warning",
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (editor->priv->text),
+ "error",
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+
+ sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "expand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (editor), sw);
+
+ view = g_object_new (GTK_TYPE_TEXT_VIEW,
+ "buffer", editor->priv->text,
+ "wrap-mode", GTK_WRAP_WORD,
+ NULL);
+ apply_system_font (view);
+ gtk_container_add (GTK_CONTAINER (sw), view);
+}
+
+static void
+create_provider (ParasiteCssEditor *editor)
+{
+ GtkCssProvider *provider = gtk_css_provider_new ();
+
+ if (editor->priv->global)
+ {
+ editor->priv->provider = provider;
+ gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+ GTK_STYLE_PROVIDER (editor->priv->provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ }
+ else if (editor->priv->selected_context)
+ {
+ gtk_style_context_add_provider (editor->priv->selected_context,
+ GTK_STYLE_PROVIDER (provider),
+ G_MAXUINT);
+ g_object_set_data (G_OBJECT (editor->priv->selected_context),
+ PARASITE_CSSEDITOR_PROVIDER,
+ provider);
+ }
+
+ g_signal_connect (provider,
+ "parsing-error",
+ G_CALLBACK (show_parsing_error),
+ editor);
+}
+
+static void
+parasite_csseditor_init (ParasiteCssEditor *editor)
+{
+ editor->priv = parasite_csseditor_get_instance_private (editor);
+}
+
+static void
+constructed (GObject *object)
+{
+ ParasiteCssEditor *editor = PARASITE_CSSEDITOR (object);
+
+ g_object_set (editor,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "sensitive", editor->priv->global,
+ NULL);
+
+ create_toolbar (editor);
+ create_provider (editor);
+ create_text_widget (editor);
+}
+
+static void
+get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ParasiteCssEditor *editor = PARASITE_CSSEDITOR (object);
+
+ switch (param_id)
+ {
+ case PROP_GLOBAL:
+ g_value_set_boolean (value, editor->priv->global);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ParasiteCssEditor *editor = PARASITE_CSSEDITOR (object);
+
+ switch (param_id)
+ {
+ case PROP_GLOBAL:
+ editor->priv->global = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+parasite_csseditor_class_init (ParasiteCssEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->constructed = constructed;
+
+ g_object_class_install_property (object_class,
+ PROP_GLOBAL,
+ g_param_spec_boolean ("global",
+ "Global",
+ "Whether this editor changes the whole application
or just the selected widget",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GtkWidget *
+parasite_csseditor_new (gboolean global)
+{
+ return GTK_WIDGET (g_object_new (PARASITE_TYPE_CSSEDITOR,
+ "global", global,
+ NULL));
+}
+
+void
+parasite_csseditor_set_widget (ParasiteCssEditor *editor, GtkWidget *widget)
+{
+ gchar *text;
+ GtkCssProvider *provider;
+
+ g_return_if_fail (PARASITE_IS_CSSEDITOR (editor));
+ g_return_if_fail (!editor->priv->global);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
+
+ if (editor->priv->selected_context)
+ {
+ text = get_current_text (GTK_TEXT_BUFFER (editor->priv->text));
+ g_object_set_data_full (G_OBJECT (editor->priv->selected_context),
+ PARASITE_CSSEDITOR_TEXT,
+ text,
+ g_free);
+ }
+
+ editor->priv->selected_context = gtk_widget_get_style_context (widget);
+
+ provider = g_object_get_data (G_OBJECT (editor->priv->selected_context), PARASITE_CSSEDITOR_PROVIDER);
+ if (!provider)
+ {
+ create_provider (editor);
+ }
+
+ text = g_object_get_data (G_OBJECT (editor->priv->selected_context), PARASITE_CSSEDITOR_TEXT);
+ if (text)
+ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (editor->priv->text), text, -1);
+ else
+ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (editor->priv->text), initial_text_widget, -1);
+
+ disable_toggled (editor->priv->disable_button, editor);
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/css-editor.h b/modules/other/parasite/css-editor.h
new file mode 100644
index 0000000..e7d58dd
--- /dev/null
+++ b/modules/other/parasite/css-editor.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GTKPARASITE_CSSEDITOR_H_
+#define _GTKPARASITE_CSSEDITOR_H_
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_CSSEDITOR (parasite_csseditor_get_type())
+#define PARASITE_CSSEDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_CSSEDITOR,
ParasiteCssEditor))
+#define PARASITE_CSSEDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_CSSEDITOR,
ParasiteCssEditorClass))
+#define PARASITE_IS_CSSEDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_CSSEDITOR))
+#define PARASITE_IS_CSSEDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_CSSEDITOR))
+#define PARASITE_CSSEDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_CSSEDITOR,
ParasiteCssEditorClass))
+
+
+typedef struct _ParasiteCssEditorPrivate ParasiteCssEditorPrivate;
+
+typedef struct _ParasiteCssEditor {
+ GtkBox parent;
+ ParasiteCssEditorPrivate *priv;
+} ParasiteCssEditor;
+
+typedef struct _ParasiteCssEditorClass {
+ GtkBoxClass parent;
+} ParasiteCssEditorClass;
+
+G_BEGIN_DECLS
+
+GType parasite_csseditor_get_type ();
+
+GtkWidget *parasite_csseditor_new (gboolean global);
+void parasite_csseditor_set_widget (ParasiteCssEditor *editor,
+ GtkWidget *widget);
+
+G_END_DECLS
+
+#endif // _GTKPARASITE_CSSEDITOR_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/inspect-button.c b/modules/other/parasite/inspect-button.c
new file mode 100644
index 0000000..b875ef6
--- /dev/null
+++ b/modules/other/parasite/inspect-button.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "parasite.h"
+#include "widget-tree.h"
+
+static void
+on_inspect_widget(GtkWidget *grab_window,
+ GdkEventButton *event,
+ ParasiteWindow *parasite)
+{
+ gdk_device_ungrab (gtk_get_current_event_device (), event->time);
+ gtk_widget_hide(parasite->highlight_window);
+
+ if (parasite->selected_window != NULL)
+ {
+ GtkWidget *toplevel = NULL;
+ GtkWidget *widget = NULL;
+
+ gdk_window_get_user_data(
+ gdk_window_get_toplevel(parasite->selected_window),
+ (gpointer*)&toplevel);
+
+ gdk_window_get_user_data (parasite->selected_window, (gpointer*)&widget);
+
+ if (toplevel)
+ {
+ parasite_widget_tree_scan (PARASITE_WIDGET_TREE (parasite->widget_tree),
+ toplevel);
+ }
+
+ if (widget)
+ {
+ parasite_widget_tree_select_object (PARASITE_WIDGET_TREE (parasite->widget_tree),
+ G_OBJECT (widget));
+ }
+ }
+}
+
+static void
+on_highlight_window_show (GtkWidget *window, ParasiteWindow *parasite)
+{
+ if (gtk_widget_is_composited (parasite->window))
+ {
+ gtk_widget_set_opacity (parasite->highlight_window, 0.2);
+ }
+ else
+ {
+ /*
+ * TODO: Do something different when there's no compositing manager.
+ * Draw a border or something.
+ */
+ }
+}
+
+static void
+ensure_highlight_window (ParasiteWindow *parasite)
+{
+ GdkRGBA color;
+
+ if (parasite->highlight_window != NULL)
+ return;
+
+ color.red = 0;
+ color.green = 0;
+ color.blue = 65535;
+ color.alpha = 1;
+
+ parasite->highlight_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_override_background_color (parasite->highlight_window,
+ GTK_STATE_NORMAL,
+ &color);
+
+ g_signal_connect (parasite->highlight_window, "show", G_CALLBACK (on_highlight_window_show), parasite);
+}
+
+static void
+on_highlight_widget(GtkWidget *grab_window,
+ GdkEventMotion *event,
+ ParasiteWindow *parasite)
+{
+ GdkWindow *selected_window;
+ gint x, y, width, height;
+
+ ensure_highlight_window (parasite);
+
+ gtk_widget_hide (parasite->highlight_window);
+
+ selected_window = gdk_device_get_window_at_position (gtk_get_current_event_device (),
+ NULL,
+ NULL);
+ if (selected_window == NULL)
+ {
+ /* This window isn't in-process. Ignore it. */
+ parasite->selected_window = NULL;
+ return;
+ }
+
+ if (gdk_window_get_toplevel(selected_window) == gtk_widget_get_window(parasite->window))
+ {
+ /* Don't hilight things in the parasite window */
+ parasite->selected_window = NULL;
+ return;
+ }
+
+ parasite->selected_window = selected_window;
+
+ gdk_window_get_origin (selected_window, &x, &y);
+ width = gdk_window_get_width (selected_window);
+ height = gdk_window_get_height (selected_window);
+
+ gtk_window_move (GTK_WINDOW (parasite->highlight_window), x, y);
+ gtk_window_resize (GTK_WINDOW (parasite->highlight_window), width, height);
+ gtk_widget_show (parasite->highlight_window);
+}
+
+static void
+on_inspect_button_release (GtkWidget *button,
+ GdkEventButton *event,
+ ParasiteWindow *parasite)
+{
+ GdkCursor *cursor;
+ GdkEventMask events;
+
+ events = GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+
+ if (parasite->grab_window == NULL)
+ {
+ parasite->grab_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_show(parasite->grab_window);
+ gtk_window_resize (GTK_WINDOW (parasite->grab_window), 1, 1);
+ gtk_window_move (GTK_WINDOW (parasite->grab_window), -100, -100);
+ gtk_widget_add_events (parasite->grab_window, events);
+
+ g_signal_connect (parasite->grab_window, "button_release_event", G_CALLBACK (on_inspect_widget),
parasite);
+ g_signal_connect (parasite->grab_window, "motion_notify_event", G_CALLBACK(on_highlight_widget),
parasite);
+ }
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (button), GDK_CROSSHAIR);
+ gdk_device_grab (gtk_get_current_event_device (),
+ gtk_widget_get_window (parasite->grab_window),
+ GDK_OWNERSHIP_WINDOW,
+ FALSE,
+ events,
+ cursor,
+ event->time);
+ g_object_unref (cursor);
+}
+
+
+GtkWidget *
+gtkparasite_inspect_button_new(ParasiteWindow *parasite)
+{
+ GtkWidget *button;
+
+ button = gtk_button_new_from_icon_name ("find", GTK_ICON_SIZE_BUTTON);
+ gtk_widget_set_tooltip_text (button, "Inspect");
+ g_signal_connect(G_OBJECT(button), "button_release_event",
+ G_CALLBACK(on_inspect_button_release), parasite);
+
+ return button;
+}
+
+static gboolean
+on_flash_timeout(ParasiteWindow *parasite)
+{
+ parasite->flash_count++;
+
+ if (parasite->flash_count == 8)
+ {
+ parasite->flash_cnx = 0;
+ return FALSE;
+ }
+
+ if (parasite->flash_count % 2 == 0)
+ {
+ if (gtk_widget_get_visible(parasite->highlight_window))
+ gtk_widget_hide(parasite->highlight_window);
+ else
+ gtk_widget_show(parasite->highlight_window);
+ }
+
+ return TRUE;
+}
+
+void
+gtkparasite_flash_widget(ParasiteWindow *parasite, GtkWidget *widget)
+{
+ gint x, y, width, height;
+ GdkWindow *parent_window;
+
+ if (!gtk_widget_get_visible(widget) || !gtk_widget_get_mapped(widget))
+ return;
+
+ ensure_highlight_window(parasite);
+
+ parent_window = gtk_widget_get_parent_window(widget);
+ if (parent_window != NULL) {
+ GtkAllocation alloc;
+ gtk_widget_get_allocation(widget, &alloc);
+
+ gdk_window_get_origin(parent_window, &x, &y);
+ x += alloc.x;
+ y += alloc.y;
+
+ width = alloc.width;
+ height = alloc.height;
+
+ gtk_window_move(GTK_WINDOW(parasite->highlight_window), x, y);
+ gtk_window_resize(GTK_WINDOW(parasite->highlight_window), width, height);
+ gtk_widget_show(parasite->highlight_window);
+
+ if (parasite->flash_cnx != 0)
+ g_source_remove(parasite->flash_cnx);
+
+ parasite->flash_count = 0;
+ parasite->flash_cnx = g_timeout_add(150, (GSourceFunc)on_flash_timeout,
+ parasite);
+ }
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/module.c b/modules/other/parasite/module.c
new file mode 100644
index 0000000..3c72060
--- /dev/null
+++ b/modules/other/parasite/module.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <glib.h>
+
+#include "config.h"
+#include "parasite.h"
+#include "python-hooks.h"
+
+
+void
+gtk_module_init(gint *argc, gchar ***argv)
+{
+#ifdef ENABLE_PYTHON
+ parasite_python_init();
+#endif
+
+ gtkparasite_window_create();
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/object-hierarchy.c b/modules/other/parasite/object-hierarchy.c
new file mode 100644
index 0000000..3dba5af
--- /dev/null
+++ b/modules/other/parasite/object-hierarchy.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "object-hierarchy.h"
+#include "parasite.h"
+
+enum
+{
+ COLUMN_OBJECT_NAME,
+ NUM_COLUMNS
+};
+
+struct _ParasiteObjectHierarchyPrivate
+{
+ GtkTreeStore *model;
+ GtkTreeView *tree;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteObjectHierarchy, parasite_objecthierarchy, GTK_TYPE_BOX)
+
+static void
+parasite_objecthierarchy_init (ParasiteObjectHierarchy *oh)
+{
+ oh->priv = parasite_objecthierarchy_get_instance_private (oh);
+}
+
+static void
+constructed (GObject *object)
+{
+ ParasiteObjectHierarchy *oh = PARASITE_OBJECTHIERARCHY (object);
+ GtkWidget *sw;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ g_object_set (object,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ NULL);
+
+ sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "expand", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (object), sw);
+
+ oh->priv->model = gtk_tree_store_new (NUM_COLUMNS,
+ G_TYPE_STRING); // COLUMN_OBJECT_NAME
+ oh->priv->tree = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (oh->priv->model)));
+ gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (oh->priv->tree));
+
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "scale", TREE_TEXT_SCALE, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Object Hierarchy", renderer,
+ "text", COLUMN_OBJECT_NAME,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (oh->priv->tree), column);
+}
+
+static void
+parasite_objecthierarchy_class_init (ParasiteObjectHierarchyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = constructed;
+}
+
+GtkWidget *
+parasite_objecthierarchy_new (void)
+{
+ return GTK_WIDGET (g_object_new (PARASITE_TYPE_OBJECTHIERARCHY,
+ NULL));
+}
+
+void
+parasite_objecthierarchy_set_object (ParasiteObjectHierarchy *oh, GObject *object)
+{
+ GObjectClass *klass = G_OBJECT_GET_CLASS (object);
+ const gchar *class_name;
+ GtkTreeIter iter, parent;
+ GSList *list = NULL, *l;
+
+ gtk_tree_store_clear (oh->priv->model);
+
+ do
+ {
+ class_name = G_OBJECT_CLASS_NAME (klass);
+ list = g_slist_append (list, (gpointer)class_name);
+ }
+ while ((klass = g_type_class_peek_parent (klass))) ;
+ list = g_slist_reverse (list);
+
+ for (l = list; l; l = l->next)
+ {
+ gtk_tree_store_append (oh->priv->model, &iter, l == list ? NULL : &parent);
+ gtk_tree_store_set (oh->priv->model, &iter,
+ COLUMN_OBJECT_NAME, l->data,
+ -1);
+ parent = iter;
+ }
+
+ g_slist_free (list);
+
+ gtk_tree_view_expand_all (oh->priv->tree);
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (oh->priv->tree),
+ &iter);
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/object-hierarchy.h b/modules/other/parasite/object-hierarchy.h
new file mode 100644
index 0000000..6eab63a
--- /dev/null
+++ b/modules/other/parasite/object-hierarchy.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GTKPARASITE_OBJECTHIERARCHY_H_
+#define _GTKPARASITE_OBJECTHIERARCHY_H_
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_OBJECTHIERARCHY (parasite_objecthierarchy_get_type())
+#define PARASITE_OBJECTHIERARCHY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
PARASITE_TYPE_OBJECTHIERARCHY, ParasiteObjectHierarchy))
+#define PARASITE_OBJECTHIERARCHY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
PARASITE_TYPE_OBJECTHIERARCHY, ParasiteObjectHierarchyClass))
+#define PARASITE_IS_OBJECTHIERARCHY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
PARASITE_TYPE_OBJECTHIERARCHY))
+#define PARASITE_IS_OBJECTHIERARCHY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
PARASITE_TYPE_OBJECTHIERARCHY))
+#define PARASITE_OBJECTHIERARCHY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
PARASITE_TYPE_OBJECTHIERARCHY, ParasiteObjectHierarchyClass))
+
+
+typedef struct _ParasiteObjectHierarchyPrivate ParasiteObjectHierarchyPrivate;
+
+typedef struct _ParasiteObjectHierarchy {
+ GtkBox parent;
+ ParasiteObjectHierarchyPrivate *priv;
+} ParasiteObjectHierarchy;
+
+typedef struct _ParasiteObjectHierarchyClass {
+ GtkBoxClass parent;
+} ParasiteObjectHierarchyClass;
+
+G_BEGIN_DECLS
+
+GType parasite_objecthierarchy_get_type (void);
+GtkWidget *parasite_objecthierarchy_new (void);
+void parasite_objecthierarchy_set_object (ParasiteObjectHierarchy *editor,
+ GObject *object);
+
+G_END_DECLS
+
+#endif // _GTKPARASITE_OBJECTHIERARCHY_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/parasite.h b/modules/other/parasite/parasite.h
new file mode 100644
index 0000000..8ff6d09
--- /dev/null
+++ b/modules/other/parasite/parasite.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_H_
+#define _GTKPARASITE_H_
+
+
+#include <gtk/gtk.h>
+
+
+#define TREE_TEXT_SCALE 0.8
+#define TREE_CHECKBOX_SIZE (gint)(0.8 * 13)
+
+
+typedef struct
+{
+ GtkWidget *window;
+ GtkWidget *widget_tree;
+ GtkWidget *prop_list;
+ GtkWidget *python_shell;
+ GtkWidget *button_path;
+ GtkWidget *classes_list;
+ GtkWidget *widget_css_editor;
+ GtkWidget *oh;
+
+ GtkWidget *grab_window;
+ GtkWidget *highlight_window;
+
+ GtkWidget *widget_popup;
+
+ GdkWindow *selected_window;
+
+ gboolean edit_mode_enabled;
+
+ int flash_count;
+ int flash_cnx;
+
+} ParasiteWindow;
+
+
+void gtkparasite_window_create();
+
+void gtkparasite_flash_widget(ParasiteWindow *parasite, GtkWidget *widget);
+
+GtkWidget *gtkparasite_inspect_button_new(ParasiteWindow *parasite);
+
+
+#endif // _GTKPARASITE_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/prop-list.c b/modules/other/parasite/prop-list.c
new file mode 100644
index 0000000..90d395e
--- /dev/null
+++ b/modules/other/parasite/prop-list.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "parasite.h"
+#include "prop-list.h"
+#include "property-cell-renderer.h"
+
+
+enum
+{
+ COLUMN_NAME,
+ COLUMN_VALUE,
+ COLUMN_DEFINED_AT,
+ COLUMN_OBJECT,
+ COLUMN_TOOLTIP,
+ COLUMN_RO,
+ NUM_COLUMNS
+};
+
+enum
+{
+ PROP_0,
+ PROP_WIDGET_TREE
+};
+
+struct _ParasitePropListPrivate
+{
+ GObject *object;
+ GtkListStore *model;
+ GHashTable *prop_iters;
+ GList *signal_cnxs;
+ GtkWidget *widget_tree;
+ GtkTreeViewColumn *property_column;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasitePropList, parasite_proplist, GTK_TYPE_TREE_VIEW)
+
+static void
+parasite_proplist_init (ParasitePropList *pl)
+{
+ pl->priv = parasite_proplist_get_instance_private (pl);
+}
+
+static gboolean
+query_tooltip_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip,
+ ParasitePropList *pl)
+{
+ GtkTreeIter iter;
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+ GtkTreePath *path = NULL;
+ gchar *tooltip_text;
+
+ if (!gtk_tree_view_get_tooltip_context (tree_view,
+ &x,
+ &y,
+ keyboard_tip,
+ &model,
+ &path,
+ &iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter, COLUMN_TOOLTIP, &tooltip_text, -1);
+ gtk_tooltip_set_text (tooltip, tooltip_text);
+
+ gtk_tree_view_set_tooltip_cell (tree_view,
+ tooltip,
+ path,
+ pl->priv->property_column,
+ NULL);
+
+ gtk_tree_path_free (path);
+ g_free (tooltip_text);
+
+ return TRUE;
+}
+
+static void
+draw_columns (GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ ParasitePropList *pl)
+{
+ gboolean ro;
+
+ gtk_tree_model_get (model, iter, COLUMN_RO, &ro, -1);
+ if (ro)
+ {
+ g_object_set (renderer, "foreground", "#a7aba7", NULL);
+ }
+ else
+ {
+ g_object_set (renderer, "foreground-set", FALSE, NULL);
+ }
+}
+
+static void
+constructed (GObject *object)
+{
+ ParasitePropList *pl = PARASITE_PROPLIST (object);
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ pl->priv->prop_iters = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify) gtk_tree_iter_free);
+
+ pl->priv->model = gtk_list_store_new(NUM_COLUMNS,
+ G_TYPE_STRING, // COLUMN_NAME
+ G_TYPE_STRING, // COLUMN_VALUE
+ G_TYPE_STRING, // COLUMN_DEFINED_AT
+ G_TYPE_OBJECT, // COLUMN_OBJECT
+ G_TYPE_STRING, // COLUMN_TOOLTIP
+ G_TYPE_BOOLEAN);// COLUMN_RO
+ gtk_tree_view_set_model (GTK_TREE_VIEW (pl),
+ GTK_TREE_MODEL (pl->priv->model));
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set (renderer, "scale", TREE_TEXT_SCALE, NULL);
+ pl->priv->property_column = gtk_tree_view_column_new_with_attributes ("Property",
+ renderer,
+ "text", COLUMN_NAME,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (pl), pl->priv->property_column);
+ g_object_set (pl->priv->property_column,
+ "resizable", TRUE,
+ "sort-order", GTK_SORT_ASCENDING,
+ "sort-column-id", COLUMN_NAME,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (pl->priv->property_column,
+ renderer,
+ (GtkTreeCellDataFunc) draw_columns,
+ pl,
+ NULL);
+
+ renderer = parasite_property_cell_renderer_new ();
+ g_object_set_data (G_OBJECT (renderer), "parasite-widget-tree", pl->priv->widget_tree);
+ g_object_set (renderer,
+ "scale", TREE_TEXT_SCALE,
+ "editable", TRUE,
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Value", renderer,
+ "text", COLUMN_VALUE,
+ "object", COLUMN_OBJECT,
+ "name", COLUMN_NAME,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (pl), column);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (pl->priv->model),
+ COLUMN_NAME,
+ GTK_SORT_ASCENDING);
+ gtk_tree_view_column_set_cell_data_func (column,
+ renderer,
+ (GtkTreeCellDataFunc) draw_columns,
+ pl,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer, "scale", TREE_TEXT_SCALE, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Defined at",
+ renderer,
+ "text", COLUMN_DEFINED_AT,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (pl), column);
+ gtk_tree_view_column_set_cell_data_func (column,
+ renderer,
+ (GtkTreeCellDataFunc) draw_columns,
+ pl,
+ NULL);
+
+ g_object_set (object, "has-tooltip", TRUE, NULL);
+ g_signal_connect (object, "query-tooltip", G_CALLBACK (query_tooltip_cb), pl);
+}
+
+static void
+get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ParasitePropList *pl = PARASITE_PROPLIST (object);
+
+ switch (param_id)
+ {
+ case PROP_WIDGET_TREE:
+ g_value_take_object (value, pl->priv->widget_tree);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ParasitePropList *pl = PARASITE_PROPLIST (object);
+
+ switch (param_id)
+ {
+ case PROP_WIDGET_TREE:
+ pl->priv->widget_tree = g_value_get_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+parasite_proplist_class_init (ParasitePropListClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->constructed = constructed;
+
+ g_object_class_install_property (object_class,
+ PROP_WIDGET_TREE,
+ g_param_spec_object ("widget-tree",
+ "Widget Tree",
+ "Widget tree",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+parasite_prop_list_update_prop (ParasitePropList *pl,
+ GtkTreeIter *iter,
+ GParamSpec *prop)
+{
+ GValue gvalue = {0};
+ char *value;
+
+ g_value_init(&gvalue, prop->value_type);
+ g_object_get_property (pl->priv->object, prop->name, &gvalue);
+
+ if (G_VALUE_HOLDS_ENUM (&gvalue))
+ {
+ GEnumClass *enum_class = G_PARAM_SPEC_ENUM(prop)->enum_class;
+ GEnumValue *enum_value = g_enum_get_value(enum_class, g_value_get_enum(&gvalue));
+
+ value = g_strdup (enum_value->value_name);
+ }
+ else
+ {
+ value = g_strdup_value_contents(&gvalue);
+ }
+
+ gtk_list_store_set (pl->priv->model, iter,
+ COLUMN_NAME, prop->name,
+ COLUMN_VALUE, value,
+ COLUMN_DEFINED_AT, g_type_name (prop->owner_type),
+ COLUMN_OBJECT, pl->priv->object,
+ COLUMN_TOOLTIP, g_param_spec_get_blurb (prop),
+ COLUMN_RO, !(prop->flags & G_PARAM_WRITABLE),
+ -1);
+
+ g_free (value);
+ g_value_unset (&gvalue);
+}
+
+static void
+parasite_proplist_prop_changed_cb (GObject *pspec,
+ GParamSpec *prop,
+ ParasitePropList *pl)
+{
+ GtkTreeIter *iter = g_hash_table_lookup(pl->priv->prop_iters, prop->name);
+
+ if (iter != NULL)
+ parasite_prop_list_update_prop (pl, iter, prop);
+}
+
+GtkWidget *
+parasite_proplist_new (GtkWidget *widget_tree)
+{
+ return g_object_new (PARASITE_TYPE_PROPLIST,
+ "widget-tree", widget_tree,
+ NULL);
+}
+
+void
+parasite_proplist_set_object (ParasitePropList* pl, GObject *object)
+{
+ GtkTreeIter iter;
+ GParamSpec **props;
+ guint num_properties;
+ guint i;
+ GList *l;
+
+ pl->priv->object = object;
+
+ for (l = pl->priv->signal_cnxs; l != NULL; l = l->next)
+ {
+ gulong id = GPOINTER_TO_UINT (l->data);
+
+ if (g_signal_handler_is_connected (object, id))
+ g_signal_handler_disconnect (object, id);
+ }
+
+ g_list_free (pl->priv->signal_cnxs);
+ pl->priv->signal_cnxs = NULL;
+
+ g_hash_table_remove_all (pl->priv->prop_iters);
+ gtk_list_store_clear (pl->priv->model);
+
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &num_properties);
+ for (i = 0; i < num_properties; i++)
+ {
+ GParamSpec *prop = props[i];
+ char *signal_name;
+
+ if (! (prop->flags & G_PARAM_READABLE))
+ continue;
+
+ gtk_list_store_append (pl->priv->model, &iter);
+ parasite_prop_list_update_prop (pl, &iter, prop);
+
+ g_hash_table_insert (pl->priv->prop_iters, (gpointer) prop->name, gtk_tree_iter_copy (&iter));
+
+ /* Listen for updates */
+ signal_name = g_strdup_printf ("notify::%s", prop->name);
+
+ pl->priv->signal_cnxs =
+ g_list_prepend (pl->priv->signal_cnxs, GINT_TO_POINTER(
+ g_signal_connect(object, signal_name,
+ G_CALLBACK (parasite_proplist_prop_changed_cb),
+ pl)));
+
+ g_free (signal_name);
+ }
+}
+
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/prop-list.h b/modules/other/parasite/prop-list.h
new file mode 100644
index 0000000..d5eed22
--- /dev/null
+++ b/modules/other/parasite/prop-list.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_PROPLIST_H_
+#define _GTKPARASITE_PROPLIST_H_
+
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_PROPLIST (parasite_proplist_get_type())
+#define PARASITE_PROPLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_PROPLIST,
ParasitePropList))
+#define PARASITE_PROPLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_PROPLIST,
ParasitePropListClass))
+#define PARASITE_IS_PROPLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_PROPLIST))
+#define PARASITE_IS_PROPLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_PROPLIST))
+#define PARASITE_PROPLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_PROPLIST,
ParasitePropListClass))
+
+
+typedef struct _ParasitePropListPrivate ParasitePropListPrivate;
+
+typedef struct _ParasitePropList {
+ GtkTreeView parent;
+ ParasitePropListPrivate *priv;
+} ParasitePropList;
+
+typedef struct _ParasitePropListClass {
+ GtkTreeViewClass parent;
+} ParasitePropListClass;
+
+
+G_BEGIN_DECLS
+
+GType parasite_proplist_get_type (void);
+GtkWidget *parasite_proplist_new (GtkWidget *widget_tree);
+void parasite_proplist_set_object (ParasitePropList *proplist,
+ GObject *object);
+
+G_END_DECLS
+
+#endif // _GTKPARASITE_PROPLIST_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/property-cell-renderer.c b/modules/other/parasite/property-cell-renderer.c
new file mode 100644
index 0000000..16a7d0f
--- /dev/null
+++ b/modules/other/parasite/property-cell-renderer.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "parasite.h"
+#include "property-cell-renderer.h"
+#include "widget-tree.h"
+
+struct _ParasitePropertyCellRendererPrivate
+{
+ GObject *object;
+ char *name;
+};
+
+enum
+{
+ PROP_0,
+ PROP_OBJECT,
+ PROP_NAME
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasitePropertyCellRenderer, parasite_property_cell_renderer,
GTK_TYPE_CELL_RENDERER_TEXT);
+
+static void
+parasite_property_cell_renderer_init(ParasitePropertyCellRenderer *renderer)
+{
+ renderer->priv = parasite_property_cell_renderer_get_instance_private (renderer);
+}
+
+static void
+get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ParasitePropertyCellRenderer *r = PARASITE_PROPERTY_CELL_RENDERER (object);
+
+ switch (param_id)
+ {
+ case PROP_OBJECT:
+ g_value_set_object(value, r->priv->object);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string(value, r->priv->name);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ParasitePropertyCellRenderer *r = PARASITE_PROPERTY_CELL_RENDERER (object);
+
+ switch (param_id)
+ {
+ case PROP_OBJECT:
+ r->priv->object = g_value_get_object (value);
+ break;
+
+ case PROP_NAME:
+ g_free (r->priv->name);
+ r->priv->name = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+stop_editing(GtkCellEditable *editable, GtkCellRenderer *renderer)
+{
+ GObject *object;
+ const char *name;
+ GValue gvalue = {0};
+ GParamSpec *prop;
+
+ object = g_object_get_data(G_OBJECT(editable), "_prop_object");
+ name = g_object_get_data(G_OBJECT(editable), "_prop_name");
+
+ prop = g_object_class_find_property(G_OBJECT_GET_CLASS(object), name);
+ g_value_init(&gvalue, prop->value_type);
+
+ if (GTK_IS_ENTRY(editable))
+ {
+ gboolean canceled;
+ g_object_get(editable, "editing_canceled", &canceled, NULL);
+ gtk_cell_renderer_stop_editing(renderer, canceled);
+
+ if (canceled)
+ return;
+
+ if (GTK_IS_SPIN_BUTTON(editable))
+ {
+ double value =
+ g_ascii_strtod(gtk_entry_get_text(GTK_ENTRY(editable)), NULL);
+
+ if (G_IS_PARAM_SPEC_INT(prop))
+ g_value_set_int(&gvalue, (gint)value);
+ else if G_IS_PARAM_SPEC_UINT(prop)
+ g_value_set_uint(&gvalue, (guint)value);
+ else if G_IS_PARAM_SPEC_INT64(prop)
+ g_value_set_int64(&gvalue, (gint64)value);
+ else if G_IS_PARAM_SPEC_UINT64(prop)
+ g_value_set_uint64(&gvalue, (guint64)value);
+ else if G_IS_PARAM_SPEC_LONG(prop)
+ g_value_set_long(&gvalue, (glong)value);
+ else if G_IS_PARAM_SPEC_ULONG(prop)
+ g_value_set_ulong(&gvalue, (gulong)value);
+ else if G_IS_PARAM_SPEC_DOUBLE(prop)
+ g_value_set_double(&gvalue, (gdouble)value);
+ else
+ return;
+ }
+ else
+ {
+ g_value_set_string(&gvalue,
+ gtk_entry_get_text(GTK_ENTRY(editable)));
+ }
+ }
+ else if (GTK_IS_COMBO_BOX(editable))
+ {
+ // We have no way of getting the canceled state for a GtkComboBox.
+ gtk_cell_renderer_stop_editing(renderer, FALSE);
+
+ if (G_IS_PARAM_SPEC_BOOLEAN(prop))
+ {
+ g_value_set_boolean(&gvalue,
+ gtk_combo_box_get_active(GTK_COMBO_BOX(editable)) == 1);
+ }
+ else if (G_IS_PARAM_SPEC_ENUM(prop))
+ {
+ char *enum_name = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (editable));
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ if (enum_name == NULL)
+ return;
+
+ enum_class = G_PARAM_SPEC_ENUM(prop)->enum_class;
+ enum_value = g_enum_get_value_by_name(enum_class, enum_name);
+ g_value_set_enum(&gvalue, enum_value->value);
+
+ g_free(enum_name);
+ }
+ }
+
+ g_object_set_property(object, name, &gvalue);
+ g_value_unset(&gvalue);
+}
+
+static GtkCellEditable *
+start_editing (GtkCellRenderer *renderer,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ PangoFontDescription *font_desc;
+ GtkCellEditable *editable = NULL;
+ GObject *object;
+ const char *name;
+ GValue gvalue = {0};
+ GParamSpec *prop;
+
+ g_object_get(renderer,
+ "object", &object,
+ "name", &name,
+ NULL);
+
+ prop = g_object_class_find_property(G_OBJECT_GET_CLASS(object), name);
+ g_value_init(&gvalue, prop->value_type);
+ g_object_get_property(object, name, &gvalue);
+
+ if (G_VALUE_HOLDS_OBJECT (&gvalue))
+ {
+ ParasiteWidgetTree *widget_tree = g_object_get_data (G_OBJECT (renderer), "parasite-widget-tree");
+ GObject *prop_object = g_value_get_object (&gvalue);
+ GtkTreeIter iter;
+
+ if (prop_object)
+ {
+ /* First check if the value is already in the tree (happens with 'parent' for instance) */
+ if (parasite_widget_tree_find_object (widget_tree, prop_object, &iter))
+ {
+ parasite_widget_tree_select_object (widget_tree, prop_object);
+ }
+ else if (parasite_widget_tree_find_object (widget_tree, object, &iter))
+ {
+ parasite_widget_tree_append_object (widget_tree, prop_object, &iter);
+ parasite_widget_tree_select_object (widget_tree, prop_object);
+ }
+ else
+ {
+ g_warning ("Parasite: couldn't find the widget in the tree");
+ }
+ }
+ g_value_unset (&gvalue);
+ return NULL;
+ }
+ else
+ {
+ if (!(prop->flags & G_PARAM_WRITABLE))
+ return NULL;
+
+ if (G_VALUE_HOLDS_ENUM(&gvalue) || G_VALUE_HOLDS_BOOLEAN(&gvalue))
+ {
+ GtkWidget *combobox = gtk_combo_box_text_new ();
+ gtk_widget_show(combobox);
+ g_object_set(G_OBJECT(combobox), "has-frame", FALSE, NULL);
+ GList *renderers;
+
+ if (G_VALUE_HOLDS_BOOLEAN(&gvalue))
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combobox), "FALSE");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combobox), "TRUE");
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX (combobox), g_value_get_boolean (&gvalue) ? 1 : 0);
+ }
+ else if (G_VALUE_HOLDS_ENUM(&gvalue))
+ {
+ gint value = g_value_get_enum(&gvalue);
+ GEnumClass *enum_class = G_PARAM_SPEC_ENUM(prop)->enum_class;
+ guint i;
+
+ for (i = 0; i < enum_class->n_values; i++)
+ {
+ GEnumValue *enum_value = &enum_class->values[i];
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combobox),
+ enum_value->value_name);
+
+ if (enum_value->value == value)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), i);
+ }
+
+ }
+
+ renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(combobox));
+ g_object_set(G_OBJECT(renderers->data), "scale", TREE_TEXT_SCALE, NULL);
+ g_list_free(renderers);
+
+ editable = GTK_CELL_EDITABLE(combobox);
+ }
+ else if (G_VALUE_HOLDS_STRING(&gvalue))
+ {
+ GtkWidget *entry = gtk_entry_new();
+ gtk_widget_show(entry);
+ gtk_entry_set_text(GTK_ENTRY(entry), g_value_get_string(&gvalue));
+
+ editable = GTK_CELL_EDITABLE(entry);
+ }
+ else if (G_VALUE_HOLDS_INT(&gvalue) ||
+ G_VALUE_HOLDS_UINT(&gvalue) ||
+ G_VALUE_HOLDS_INT64(&gvalue) ||
+ G_VALUE_HOLDS_UINT64(&gvalue) ||
+ G_VALUE_HOLDS_LONG(&gvalue) ||
+ G_VALUE_HOLDS_ULONG(&gvalue) ||
+ G_VALUE_HOLDS_DOUBLE(&gvalue))
+ {
+ double min, max, value;
+ GtkWidget *spinbutton;
+ guint digits = 0;
+
+ if (G_VALUE_HOLDS_INT(&gvalue))
+ {
+ GParamSpecInt *paramspec = G_PARAM_SPEC_INT(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_int(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_UINT(&gvalue))
+ {
+ GParamSpecUInt *paramspec = G_PARAM_SPEC_UINT(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_uint(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_INT64(&gvalue))
+ {
+ GParamSpecInt64 *paramspec = G_PARAM_SPEC_INT64(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_int64(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_UINT64(&gvalue))
+ {
+ GParamSpecUInt64 *paramspec = G_PARAM_SPEC_UINT64(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_uint64(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_LONG(&gvalue))
+ {
+ GParamSpecLong *paramspec = G_PARAM_SPEC_LONG(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_long(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_ULONG(&gvalue))
+ {
+ GParamSpecULong *paramspec = G_PARAM_SPEC_ULONG(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_ulong(&gvalue);
+ }
+ else if (G_VALUE_HOLDS_DOUBLE(&gvalue))
+ {
+ GParamSpecDouble *paramspec = G_PARAM_SPEC_DOUBLE(prop);
+ min = paramspec->minimum;
+ max = paramspec->maximum;
+ value = g_value_get_double(&gvalue);
+ digits = 2;
+ }
+ else
+ {
+ // Shouldn't really be able to happen.
+ return NULL;
+ }
+
+ spinbutton = gtk_spin_button_new_with_range(min, max, 1);
+ gtk_widget_show(spinbutton);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), value);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinbutton), digits);
+
+ editable = GTK_CELL_EDITABLE(spinbutton);
+ }
+ }
+ g_value_unset(&gvalue);
+
+ if (!editable)
+ return NULL;
+
+ font_desc = pango_font_description_new();
+ pango_font_description_set_size(font_desc, 8 * PANGO_SCALE);
+ gtk_widget_override_font (GTK_WIDGET (editable), font_desc);
+ pango_font_description_free(font_desc);
+
+ g_signal_connect(editable, "editing_done", G_CALLBACK (stop_editing), renderer);
+ g_object_set_data_full (G_OBJECT (editable), "_prop_name", g_strdup (name), g_free);
+ g_object_set_data (G_OBJECT (editable), "_prop_object", object);
+
+ return editable;
+}
+
+static void
+parasite_property_cell_renderer_class_init (ParasitePropertyCellRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ cell_class->start_editing = start_editing;
+
+ g_object_class_install_property(object_class,
+ PROP_OBJECT,
+ g_param_spec_object ("object",
+ "Object",
+ "The object owning the property",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property(object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "Name",
+ "The property name",
+ NULL,
+ G_PARAM_READWRITE));
+}
+
+GtkCellRenderer *
+parasite_property_cell_renderer_new(void)
+{
+ return g_object_new(PARASITE_TYPE_PROPERTY_CELL_RENDERER, NULL);
+}
+
+
+// vim: set et ts=4:
diff --git a/modules/other/parasite/property-cell-renderer.h b/modules/other/parasite/property-cell-renderer.h
new file mode 100644
index 0000000..cc88def
--- /dev/null
+++ b/modules/other/parasite/property-cell-renderer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_PROPERTY_CELL_RENDERER_H_
+#define _GTKPARASITE_PROPERTY_CELL_RENDERER_H_
+
+
+#include <gtk/gtk.h>
+
+
+#define PARASITE_TYPE_PROPERTY_CELL_RENDERER (parasite_property_cell_renderer_get_type())
+#define PARASITE_PROPERTY_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
PARASITE_TYPE_PROPERTY_CELL_RENDERER, ParasitePropertyCellRenderer))
+#define PARASITE_PROPERTY_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
PARASITE_TYPE_PROPERTY_CELL_RENDERER, ParasitePropertyCellRendererClass))
+#define PARASITE_IS_PROPERTY_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
PARASITE_TYPE_PROPERTY_CELL_RENDERER))
+#define PARASITE_IS_PROPERTY_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
PARASITE_TYPE_PROPERTY_CELL_RENDERER))
+#define PARASITE_PROPERTY_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),
PARASITE_TYPE_PROPERTY_CELL_RENDERER, ParasitePropertyCellRendererClass))
+
+typedef struct _ParasitePropertyCellRendererPrivate ParasitePropertyCellRendererPrivate;
+
+typedef struct
+{
+ GtkCellRendererText parent;
+ ParasitePropertyCellRendererPrivate *priv;
+} ParasitePropertyCellRenderer;
+
+typedef struct
+{
+ GtkCellRendererTextClass parent;
+
+ // Padding for future expansion
+ void (*reserved0)(void);
+ void (*reserved1)(void);
+ void (*reserved2)(void);
+ void (*reserved3)(void);
+
+} ParasitePropertyCellRendererClass;
+
+
+G_BEGIN_DECLS
+
+
+GType parasite_property_cell_renderer_get_type();
+GtkCellRenderer *parasite_property_cell_renderer_new();
+
+
+G_END_DECLS
+
+
+#endif // _GTKPARASITE_PROPERTY_CELL_RENDERER_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/python-hooks.c b/modules/other/parasite/python-hooks.c
new file mode 100644
index 0000000..2148ec3
--- /dev/null
+++ b/modules/other/parasite/python-hooks.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <dlfcn.h>
+#include "config.h"
+#include <signal.h>
+
+#ifdef ENABLE_PYTHON
+# include <Python.h>
+# include <pygobject.h>
+#endif
+
+#include "python-hooks.h"
+
+static gboolean python_enabled = FALSE;
+
+#ifdef ENABLE_PYTHON
+static GString *captured_stdout = NULL;
+static GString *captured_stderr = NULL;
+
+
+static PyObject *
+capture_stdout(PyObject *self, PyObject *args)
+{
+ char *str = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return NULL;
+
+ g_string_append(captured_stdout, str);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+capture_stderr(PyObject *self, PyObject *args)
+{
+ char *str = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return NULL;
+
+ g_string_append(captured_stderr, str);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+wrap_gobj(PyObject *self, PyObject *args)
+{
+ void *addr;
+ GObject *obj;
+
+ if (!PyArg_ParseTuple(args, "l", &addr))
+ return NULL;
+
+ if (!G_IS_OBJECT(addr))
+ return NULL; // XXX
+
+ obj = G_OBJECT(addr);
+
+ if (!obj)
+ return NULL; // XXX
+
+ return pygobject_new(obj);
+}
+
+static PyMethodDef parasite_python_methods[] = {
+ {"capture_stdout", capture_stdout, METH_VARARGS, "Captures stdout"},
+ {"capture_stderr", capture_stderr, METH_VARARGS, "Captures stderr"},
+ {"gobj", wrap_gobj, METH_VARARGS, "Wraps a C GObject"},
+ {NULL, NULL, 0, NULL}
+};
+
+
+static gboolean
+is_blacklisted(void)
+{
+ const char *prgname = g_get_prgname();
+
+ return (!strcmp(prgname, "gimp"));
+}
+#endif // ENABLE_PYTHON
+
+void
+parasite_python_init(void)
+{
+#ifdef ENABLE_PYTHON
+ int res;
+ struct sigaction old_sigint;
+
+ if (is_blacklisted())
+ return;
+
+ /* This prevents errors such as "undefined symbol: PyExc_ImportError" */
+ if (!dlopen(PYTHON_SHARED_LIB, RTLD_NOW | RTLD_GLOBAL))
+ {
+ g_error("%s\n", dlerror());
+ return;
+ }
+
+ captured_stdout = g_string_new("");
+ captured_stderr = g_string_new("");
+
+ /* Back up and later restore SIGINT so Python doesn't steal it from us. */
+ res = sigaction(SIGINT, NULL, &old_sigint);
+
+ if (!Py_IsInitialized())
+ Py_Initialize();
+
+ res = sigaction(SIGINT, &old_sigint, NULL);
+
+ Py_InitModule("parasite", parasite_python_methods);
+ PyRun_SimpleString(
+ "import parasite\n"
+ "import sys\n"
+ "\n"
+ "class StdoutCatcher:\n"
+ " def write(self, str):\n"
+ " parasite.capture_stdout(str)\n"
+ "\n"
+ "class StderrCatcher:\n"
+ " def write(self, str):\n"
+ " parasite.capture_stderr(str)\n"
+ "\n"
+ );
+
+ if (!pygobject_init(-1, -1, -1))
+ {
+ fprintf(stderr, "Error initializing pygobject support.\n");
+ PyErr_Print();
+ return;
+ }
+
+ char *argv[] = { "", NULL };
+ PySys_SetArgv(0, argv);
+
+ if (!PyImport_ImportModule("gi._gobject"))
+ {
+ PyErr_SetString(PyExc_ImportError, "could not import gi.gobject");
+ return;
+ }
+ if (!PyImport_ImportModule("gi.repository"))
+ {
+ PyErr_SetString(PyExc_ImportError, "could not import gi.repository");
+ return;
+ }
+ if (!PyImport_ImportModule("gi.repository.Gtk"))
+ {
+ PyErr_SetString(PyExc_ImportError, "could not import gtk");
+ return;
+ }
+
+ python_enabled = TRUE;
+#endif // ENABLE_PYTHON
+}
+
+void
+parasite_python_run(const char *command,
+ ParasitePythonLogger stdout_logger,
+ ParasitePythonLogger stderr_logger,
+ gpointer user_data)
+{
+#ifdef ENABLE_PYTHON
+ PyGILState_STATE gstate;
+ PyObject *module;
+ PyObject *dict;
+ PyObject *obj;
+
+ gstate = PyGILState_Ensure();
+
+ module = PyImport_AddModule("__main__");
+ dict = PyModule_GetDict(module);
+
+ PyRun_SimpleString("old_stdout = sys.stdout\n"
+ "old_stderr = sys.stderr\n"
+ "sys.stdout = StdoutCatcher()\n"
+ "sys.stderr = StderrCatcher()\n");
+
+ obj = PyRun_String(command, Py_single_input, dict, dict);
+
+ PyRun_SimpleString("sys.stdout = old_stdout\n"
+ "sys.stderr = old_stderr\n");
+
+ if (stdout_logger != NULL)
+ stdout_logger(captured_stdout->str, user_data);
+
+ if (stderr_logger != NULL)
+ stderr_logger(captured_stderr->str, user_data);
+
+ // Print any returned object
+ if (obj != NULL && obj != Py_None) {
+ PyObject *repr = PyObject_Repr(obj);
+ if (repr != NULL) {
+ char *string = PyString_AsString(repr);
+
+ stdout_logger(string, user_data);
+ stdout_logger("\n", user_data);
+ }
+
+ Py_XDECREF(repr);
+ }
+ Py_XDECREF(obj);
+
+ PyGILState_Release(gstate);
+ g_string_erase(captured_stdout, 0, -1);
+ g_string_erase(captured_stderr, 0, -1);
+#endif // ENABLE_PYTHON
+}
+
+gboolean
+parasite_python_is_enabled(void)
+{
+ return python_enabled;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/python-hooks.h b/modules/other/parasite/python-hooks.h
new file mode 100644
index 0000000..d6bf1bd
--- /dev/null
+++ b/modules/other/parasite/python-hooks.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_PYTHON_MODULE_H_
+#define _GTKPARASITE_PYTHON_MODULE_H_
+
+#include <glib.h>
+
+
+typedef void (*ParasitePythonLogger)(const char *text, gpointer user_data);
+
+void parasite_python_init(void);
+void parasite_python_run(const char *command,
+ ParasitePythonLogger stdout_logger,
+ ParasitePythonLogger stderr_logger,
+ gpointer user_data);
+gboolean parasite_python_is_enabled(void);
+
+#endif // _GTKPARASITE_PYTHON_MODULE_H_
diff --git a/modules/other/parasite/python-shell.c b/modules/other/parasite/python-shell.c
new file mode 100644
index 0000000..53fc5c6
--- /dev/null
+++ b/modules/other/parasite/python-shell.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include "python-hooks.h"
+#include "python-shell.h"
+
+#define MAX_HISTORY_LENGTH 20
+
+struct _ParasitePythonShellPrivate
+{
+ GtkWidget *textview;
+
+ GtkTextMark *scroll_mark;
+ GtkTextMark *line_start_mark;
+
+ GQueue *history;
+ GList *cur_history_item;
+
+ GString *pending_command;
+ gboolean in_block;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasitePythonShell, parasite_python_shell, GTK_TYPE_BOX);
+
+/* Widget functions */
+static void parasite_python_shell_finalize (GObject *obj);
+
+/* Python integration */
+static void parasite_python_shell_write_prompt(GtkWidget *python_shell);
+static char *parasite_python_shell_get_input(GtkWidget *python_shell);
+
+/* Callbacks */
+static gboolean parasite_python_shell_key_press_cb(GtkWidget *textview,
+ GdkEventKey *event,
+ GtkWidget *python_shell);
+static void
+parasite_python_shell_class_init(ParasitePythonShellClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->finalize = parasite_python_shell_finalize;
+}
+
+static void
+parasite_python_shell_init (ParasitePythonShell *python_shell)
+{
+ GtkWidget *swin;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ PangoFontDescription *font_desc;
+
+ python_shell->priv = parasite_python_shell_get_instance_private (python_shell);
+
+ python_shell->priv->history = g_queue_new();
+
+ gtk_box_set_spacing(GTK_BOX(python_shell), 6);
+
+ swin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_show(swin);
+ gtk_box_pack_start(GTK_BOX(python_shell), swin, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
+ GTK_SHADOW_IN);
+
+ python_shell->priv->textview = gtk_text_view_new();
+ gtk_widget_show(python_shell->priv->textview);
+ gtk_container_add(GTK_CONTAINER(swin), python_shell->priv->textview);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(python_shell->priv->textview), TRUE);
+ gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(python_shell->priv->textview), 3);
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(python_shell->priv->textview), 3);
+ gtk_text_view_set_right_margin(GTK_TEXT_VIEW(python_shell->priv->textview), 3);
+
+ g_signal_connect(python_shell->priv->textview, "key_press_event",
+ G_CALLBACK(parasite_python_shell_key_press_cb),
+ python_shell);
+
+ /* Make the textview monospaced */
+ font_desc = pango_font_description_from_string("monospace");
+ pango_font_description_set_size(font_desc, 8 * PANGO_SCALE);
+ gtk_widget_override_font(python_shell->priv->textview, font_desc);
+ pango_font_description_free(font_desc);
+
+ /* Create the end-of-buffer mark */
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(python_shell->priv->textview));
+ gtk_text_buffer_get_end_iter(buffer, &iter);
+ python_shell->priv->scroll_mark = gtk_text_buffer_create_mark(buffer, "scroll_mark",
+ &iter, FALSE);
+
+ /* Create the beginning-of-line mark */
+ python_shell->priv->line_start_mark = gtk_text_buffer_create_mark(buffer,
+ "line_start_mark",
+ &iter, TRUE);
+
+ /* Register some tags */
+ gtk_text_buffer_create_tag(buffer, "stdout", NULL);
+ gtk_text_buffer_create_tag(buffer, "stderr",
+ "foreground", "red",
+ "paragraph-background", "#FFFFE0",
+ NULL);
+ gtk_text_buffer_create_tag(buffer, "prompt",
+ "foreground", "blue",
+ NULL);
+
+ parasite_python_shell_write_prompt(GTK_WIDGET(python_shell));
+}
+
+static void
+parasite_python_shell_finalize(GObject *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+
+ g_queue_free(priv->history);
+}
+
+static void
+parasite_python_shell_log_stdout(const char *text, gpointer python_shell)
+{
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ text, "stdout");
+}
+
+static void
+parasite_python_shell_log_stderr(const char *text, gpointer python_shell)
+{
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ text, "stderr");
+}
+
+static void
+parasite_python_shell_write_prompt(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter iter;
+ const char *prompt = (priv->pending_command == NULL ? ">>> " : "... ");
+
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ prompt, "prompt");
+
+ gtk_text_buffer_get_end_iter(buffer, &iter);
+ gtk_text_buffer_move_mark(buffer, priv->line_start_mark, &iter);
+}
+
+static void
+parasite_python_shell_process_line(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+ char *command = parasite_python_shell_get_input(python_shell);
+ char last_char;
+
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ "\n", NULL);
+
+ if (*command != '\0')
+ {
+ /* Save this command in the history. */
+ g_queue_push_head(priv->history, command);
+ priv->cur_history_item = NULL;
+
+ if (g_queue_get_length(priv->history) > MAX_HISTORY_LENGTH)
+ g_free(g_queue_pop_tail(priv->history));
+ }
+
+ last_char = command[MAX(0, strlen(command) - 1)];
+
+ if (last_char == ':' || last_char == '\\' ||
+ (priv->in_block && g_ascii_isspace(command[0])))
+ {
+ printf("in block.. %c, %d, %d\n",
+ last_char, priv->in_block,
+ g_ascii_isspace(command[0]));
+ /* This is a multi-line expression */
+ if (priv->pending_command == NULL)
+ priv->pending_command = g_string_new(command);
+ else
+ g_string_append(priv->pending_command, command);
+
+ g_string_append_c(priv->pending_command, '\n');
+
+ if (last_char == ':')
+ priv->in_block = TRUE;
+ }
+ else
+ {
+ if (priv->pending_command != NULL)
+ {
+ g_string_append(priv->pending_command, command);
+ g_string_append_c(priv->pending_command, '\n');
+
+ /* We're not actually leaking this. It's in the history. */
+ command = g_string_free(priv->pending_command, FALSE);
+ }
+
+ parasite_python_run(command,
+ parasite_python_shell_log_stdout,
+ parasite_python_shell_log_stderr,
+ python_shell);
+
+ if (priv->pending_command != NULL)
+ {
+ /* Now do the cleanup. */
+ g_free(command);
+ priv->pending_command = NULL;
+ priv->in_block = FALSE;
+ }
+ }
+
+ parasite_python_shell_write_prompt(python_shell);
+}
+
+static void
+parasite_python_shell_replace_input(GtkWidget *python_shell,
+ const char *text)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_end_iter(buffer, &end_iter);
+
+ gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
+ gtk_text_buffer_insert(buffer, &end_iter, text, -1);
+}
+
+static char *
+parasite_python_shell_get_input(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_end_iter(buffer, &end_iter);
+
+ return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
+}
+
+static const char *
+parasite_python_shell_get_history_back(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+
+ if (priv->cur_history_item == NULL)
+ {
+ priv->cur_history_item = g_queue_peek_head_link(priv->history);
+
+ if (priv->cur_history_item == NULL)
+ return "";
+ }
+ else if (priv->cur_history_item->next != NULL)
+ priv->cur_history_item = priv->cur_history_item->next;
+
+ return (const char *)priv->cur_history_item->data;
+}
+
+static const char *
+parasite_python_shell_get_history_forward(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+
+ if (priv->cur_history_item == NULL || priv->cur_history_item->prev == NULL)
+ {
+ priv->cur_history_item = NULL;
+ return "";
+ }
+
+ priv->cur_history_item = priv->cur_history_item->prev;
+
+ return (const char *)priv->cur_history_item->data;
+}
+
+static gboolean
+parasite_python_shell_key_press_cb(GtkWidget *textview,
+ GdkEventKey *event,
+ GtkWidget *python_shell)
+{
+ if (event->keyval == GDK_KEY_Return)
+ {
+ parasite_python_shell_process_line(python_shell);
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_Up)
+ {
+ parasite_python_shell_replace_input(python_shell,
+ parasite_python_shell_get_history_back(python_shell));
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_Down)
+ {
+ parasite_python_shell_replace_input(python_shell,
+ parasite_python_shell_get_history_forward(python_shell));
+ return TRUE;
+ }
+ else if (event->string != NULL)
+ {
+ ParasitePythonShellPrivate *priv = PARASITE_PYTHON_SHELL(python_shell)->priv;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextMark *insert_mark = gtk_text_buffer_get_insert(buffer);
+ GtkTextMark *selection_mark =
+ gtk_text_buffer_get_selection_bound(buffer);
+ GtkTextIter insert_iter;
+ GtkTextIter selection_iter;
+ GtkTextIter start_iter;
+ gint cmp_start_insert;
+ gint cmp_start_select;
+ gint cmp_insert_select;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, insert_mark);
+ gtk_text_buffer_get_iter_at_mark(buffer, &selection_iter,
+ selection_mark);
+
+ cmp_start_insert = gtk_text_iter_compare(&start_iter, &insert_iter);
+ cmp_start_select = gtk_text_iter_compare(&start_iter, &selection_iter);
+ cmp_insert_select = gtk_text_iter_compare(&insert_iter,
+ &selection_iter);
+
+ if (cmp_start_insert == 0 && cmp_start_select == 0 &&
+ (event->keyval == GDK_KEY_BackSpace ||
+ event->keyval == GDK_KEY_Left))
+ {
+ return TRUE;
+ }
+ if (cmp_start_insert <= 0 && cmp_start_select <= 0)
+ {
+ return FALSE;
+ }
+ else if (cmp_start_insert > 0 && cmp_start_select > 0)
+ {
+ gtk_text_buffer_place_cursor(buffer, &start_iter);
+ }
+ else if (cmp_insert_select < 0)
+ {
+ gtk_text_buffer_move_mark(buffer, insert_mark, &start_iter);
+ }
+ else if (cmp_insert_select > 0)
+ {
+ gtk_text_buffer_move_mark(buffer, selection_mark, &start_iter);
+ }
+ }
+
+ return FALSE;
+}
+
+GtkWidget *
+parasite_python_shell_new(void)
+{
+ return g_object_new(PARASITE_TYPE_PYTHON_SHELL, NULL);
+}
+
+void
+parasite_python_shell_append_text(ParasitePythonShell *python_shell,
+ const char *str,
+ const char *tag)
+{
+ ParasitePythonShellPrivate *priv = python_shell->priv;
+
+ GtkTextIter end;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
+
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_move_mark(buffer, mark, &end);
+ gtk_text_buffer_insert_with_tags_by_name(buffer, &end, str, -1, tag, NULL);
+ gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(priv->textview), mark,
+ 0, TRUE, 0, 1);
+}
+
+void
+parasite_python_shell_focus(ParasitePythonShell *python_shell)
+{
+ gtk_widget_grab_focus (python_shell->priv->textview);
+}
+
+// vim: set et ts=4:
diff --git a/modules/other/parasite/python-shell.h b/modules/other/parasite/python-shell.h
new file mode 100644
index 0000000..8d4f58d
--- /dev/null
+++ b/modules/other/parasite/python-shell.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _PARASITE_PYTHON_SHELL_H_
+#define _PARASITE_PYTHON_SHELL_H_
+
+typedef struct _ParasitePythonShell ParasitePythonShell;
+typedef struct _ParasitePythonShellClass ParasitePythonShellClass;
+typedef struct _ParasitePythonShellPrivate ParasitePythonShellPrivate;
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_PYTHON_SHELL (parasite_python_shell_get_type())
+#define PARASITE_PYTHON_SHELL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShell))
+#define PARASITE_PYTHON_SHELL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShellClass))
+#define PARASITE_IS_PYTHON_SHELL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_PYTHON_SHELL))
+#define PARASITE_IS_PYTHON_SHELL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_PYTHON_SHELL))
+#define PARASITE_PYTHON_SHELL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShellClass))
+
+
+struct _ParasitePythonShell
+{
+ GtkBox parent_object;
+ ParasitePythonShellPrivate *priv;
+};
+
+struct _ParasitePythonShellClass
+{
+ GtkBoxClass parent_class;
+};
+
+G_BEGIN_DECLS
+
+GType parasite_python_shell_get_type(void);
+
+GtkWidget *parasite_python_shell_new(void);
+void parasite_python_shell_append_text(ParasitePythonShell *python_shell,
+ const char *str,
+ const char *tag);
+void parasite_python_shell_focus(ParasitePythonShell *python_shell);
+
+G_END_DECLS
+
+#endif // _PARASITE_PYTHON_SHELL_H_
diff --git a/modules/other/parasite/themes.c b/modules/other/parasite/themes.c
new file mode 100644
index 0000000..ed0ff5c
--- /dev/null
+++ b/modules/other/parasite/themes.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "themes.h"
+
+struct _ParasiteThemesPrivate
+{
+ int dummy;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteThemes, parasite_themes, GTK_TYPE_LIST_BOX)
+
+static void
+parasite_themes_init (ParasiteThemes *pt)
+{
+ pt->priv = parasite_themes_get_instance_private (pt);
+}
+
+static GtkWidget *
+create_dark (ParasiteThemes *pt)
+{
+ GtkWidget *b, *l, *s;
+
+ b = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "margin", 10,
+ NULL);
+
+ l = g_object_new (GTK_TYPE_LABEL,
+ "label", "Use dark variant",
+ "hexpand", TRUE,
+ "xalign", 0.0,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (b), l);
+
+ s = gtk_switch_new ();
+ g_object_bind_property (s, "active",
+ gtk_settings_get_default (), "gtk-application-prefer-dark-theme",
+ G_BINDING_BIDIRECTIONAL);
+ gtk_container_add (GTK_CONTAINER (b), s);
+
+ return b;
+}
+
+static void
+fill_gtk (const char *path, GHashTable *t)
+{
+ const gchar *dir_entry;
+ GDir *dir = g_dir_open (path, 0, NULL);
+
+ if (!dir)
+ return;
+
+ while ((dir_entry = g_dir_read_name (dir)))
+ {
+ char *filename = g_build_filename (path, dir_entry, "gtk-3.0", NULL);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_DIR)
+ && !g_hash_table_contains (t, dir_entry))
+ g_hash_table_add (t, g_strdup (dir_entry));
+
+ g_free (filename);
+ }
+}
+
+static void
+gtk_changed (GtkComboBox *c, ParasiteThemes *pt)
+{
+ char *theme = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (c));
+
+ g_object_set (gtk_settings_get_default (),
+ "gtk-theme-name", theme,
+ NULL);
+ g_free (theme);
+}
+
+static GtkWidget *
+create_gtk (ParasiteThemes *pt)
+{
+ GtkWidget *b, *l, *c;
+ GHashTable *t;
+ char *theme, *default_theme, *path;
+ GHashTableIter iter;
+ int i, pos;
+ GSettings *settings;
+
+ b = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "margin", 10,
+ NULL);
+
+ l = g_object_new (GTK_TYPE_LABEL,
+ "label", "GTK+ Theme",
+ "hexpand", TRUE,
+ "xalign", 0.0,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (b), l);
+
+ t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ fill_gtk (GTK_DATADIR "/themes", t);
+ path = g_build_filename (g_get_user_data_dir (), "themes", NULL);
+ fill_gtk (path, t);
+ g_free (path);
+
+ c = gtk_combo_box_text_new ();
+ gtk_container_add (GTK_CONTAINER (b), c);
+
+ settings = g_settings_new ("org.gnome.desktop.interface");
+ default_theme = g_settings_get_string (settings, "gtk-theme");
+ g_object_unref (settings);
+
+ g_hash_table_iter_init (&iter, t);
+ pos = i = 0;
+ while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (c), theme);
+ if (g_strcmp0 (theme, default_theme) == 0)
+ pos = i;
+ i++;
+ }
+ g_hash_table_destroy (t);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (c), pos);
+ g_signal_connect (c, "changed", G_CALLBACK (gtk_changed), pt);
+
+ return b;
+}
+
+static void
+constructed (GObject *object)
+{
+ ParasiteThemes *pt = PARASITE_THEMES (object);
+ GtkContainer *box = GTK_CONTAINER (object);
+
+ g_object_set (object,
+ "selection-mode", GTK_SELECTION_NONE,
+ NULL);
+
+ gtk_container_add (box, create_dark (pt));
+ gtk_container_add (box, create_gtk (pt));
+}
+
+static void
+parasite_themes_class_init (ParasiteThemesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = constructed;
+}
+
+GtkWidget *
+parasite_themes_new (void)
+{
+ return GTK_WIDGET (g_object_new (PARASITE_TYPE_THEMES, NULL));
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/themes.h b/modules/other/parasite/themes.h
new file mode 100644
index 0000000..9f76650
--- /dev/null
+++ b/modules/other/parasite/themes.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GTKPARASITE_THEMES_H_
+#define _GTKPARASITE_THEMES_H_
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_THEMES (parasite_themes_get_type())
+#define PARASITE_THEMES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_THEMES,
ParasiteThemes))
+#define PARASITE_THEMES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_THEMES,
ParasiteThemesClass))
+#define PARASITE_IS_THEMES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_THEMES))
+#define PARASITE_IS_THEMES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_THEMES))
+#define PARASITE_THEMES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_THEMES,
ParasiteThemesClass))
+
+
+typedef struct _ParasiteThemesPrivate ParasiteThemesPrivate;
+
+typedef struct _ParasiteThemes {
+ GtkListBox parent;
+ ParasiteThemesPrivate *priv;
+} ParasiteThemes;
+
+typedef struct _ParasiteThemesClass {
+ GtkListBoxClass parent;
+} ParasiteThemesClass;
+
+G_BEGIN_DECLS
+
+GType parasite_themes_get_type (void);
+GtkWidget *parasite_themes_new (void);
+
+G_END_DECLS
+
+#endif // _GTKPARASITE_THEMES_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/widget-tree.c b/modules/other/parasite/widget-tree.c
new file mode 100644
index 0000000..e35c505
--- /dev/null
+++ b/modules/other/parasite/widget-tree.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "parasite.h"
+#include "prop-list.h"
+#include "widget-tree.h"
+#include <string.h>
+
+enum
+{
+ OBJECT,
+ OBJECT_TYPE,
+ OBJECT_NAME,
+ WIDGET_REALIZED,
+ WIDGET_VISIBLE,
+ WIDGET_MAPPED,
+ OBJECT_ADDRESS,
+ SENSITIVE,
+ NUM_COLUMNS
+};
+
+
+enum
+{
+ WIDGET_CHANGED,
+ LAST_SIGNAL
+};
+
+
+struct _ParasiteWidgetTreePrivate
+{
+ GtkTreeStore *model;
+ GHashTable *iters;
+};
+
+static guint widget_tree_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE (ParasiteWidgetTree, parasite_widget_tree, GTK_TYPE_TREE_VIEW)
+
+static void
+parasite_widget_tree_on_widget_selected(GtkTreeSelection *selection,
+ ParasiteWidgetTree *widget_tree)
+{
+ g_signal_emit(widget_tree, widget_tree_signals[WIDGET_CHANGED], 0);
+}
+
+
+static void
+parasite_widget_tree_init (ParasiteWidgetTree *widget_tree)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+
+ widget_tree->priv = parasite_widget_tree_get_instance_private (widget_tree);
+
+ widget_tree->priv->iters = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) gtk_tree_iter_free);
+
+ widget_tree->priv->model = gtk_tree_store_new(
+ NUM_COLUMNS,
+ G_TYPE_POINTER, // OBJECT
+ G_TYPE_STRING, // OBJECT_TYPE
+ G_TYPE_STRING, // OBJECT_NAME
+ G_TYPE_BOOLEAN, // WIDGET_REALIZED
+ G_TYPE_BOOLEAN, // WIDGET_VISIBLE
+ G_TYPE_BOOLEAN, // WIDGET_MAPPED
+ G_TYPE_STRING, // OBJECT_ADDRESS
+ G_TYPE_BOOLEAN);// SENSITIVE
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(widget_tree),
+ GTK_TREE_MODEL(widget_tree->priv->model));
+ gtk_tree_view_set_enable_search(GTK_TREE_VIEW(widget_tree), TRUE);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(widget_tree), OBJECT_NAME);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget_tree));
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(parasite_widget_tree_on_widget_selected),
+ widget_tree);
+
+ // Widget column
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "scale", TREE_TEXT_SCALE, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Widget", renderer,
+ "text", OBJECT_TYPE,
+ "sensitive", SENSITIVE,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+
+ // Name column
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "scale", TREE_TEXT_SCALE, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Name", renderer,
+ "text", OBJECT_NAME,
+ "sensitive", SENSITIVE,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+
+ // Realized column
+ renderer = gtk_cell_renderer_toggle_new();
+ g_object_set(G_OBJECT(renderer),
+ "activatable", TRUE,
+ "indicator-size", TREE_CHECKBOX_SIZE,
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes("Realized",
+ renderer,
+ "active", WIDGET_REALIZED,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+
+ // Mapped column
+ renderer = gtk_cell_renderer_toggle_new();
+ g_object_set(G_OBJECT(renderer),
+ "activatable", TRUE,
+ "indicator-size", TREE_CHECKBOX_SIZE,
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes("Mapped",
+ renderer,
+ "active", WIDGET_MAPPED,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+
+ // Visible column
+ renderer = gtk_cell_renderer_toggle_new();
+ g_object_set(G_OBJECT(renderer),
+ "activatable", TRUE,
+ "indicator-size", TREE_CHECKBOX_SIZE,
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes("Visible",
+ renderer,
+ "active", WIDGET_VISIBLE,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+
+ // Poinder Address column
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer),
+ "scale", TREE_TEXT_SCALE,
+ "family", "monospace",
+ NULL);
+ column = gtk_tree_view_column_new_with_attributes("Pointer Address",
+ renderer,
+ "text", OBJECT_ADDRESS,
+ "sensitive", SENSITIVE,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(widget_tree), column);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+
+ parasite_widget_tree_append_object (widget_tree, G_OBJECT (gtk_settings_get_default ()), NULL);
+}
+
+
+static void
+parasite_widget_tree_class_init(ParasiteWidgetTreeClass *klass)
+{
+ klass->widget_changed = NULL;
+
+ widget_tree_signals[WIDGET_CHANGED] =
+ g_signal_new("widget-changed",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
+ G_STRUCT_OFFSET(ParasiteWidgetTreeClass, widget_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+GtkWidget *
+parasite_widget_tree_new ()
+{
+ return g_object_new (PARASITE_TYPE_WIDGET_TREE, NULL);
+}
+
+
+GObject *
+parasite_widget_tree_get_selected_object (ParasiteWidgetTree *widget_tree)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget_tree));
+
+ if (gtk_tree_selection_get_selected (sel, &model, &iter))
+ {
+ GObject *object;
+ gtk_tree_model_get (model, &iter,
+ OBJECT, &object,
+ -1);
+ return object;
+ }
+
+ return NULL;
+}
+
+static void
+on_container_forall(GtkWidget *widget, gpointer data)
+{
+ GList **list = (GList **)data;
+
+ *list = g_list_append(*list, widget);
+}
+
+void
+parasite_widget_tree_append_object (ParasiteWidgetTree *widget_tree,
+ GObject *object,
+ GtkTreeIter *parent_iter)
+{
+ GtkTreeIter iter;
+ const char *class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object));
+ const char *name = NULL;
+ char *address;
+ gboolean realized;
+ gboolean mapped;
+ gboolean visible;
+ gboolean is_widget;
+ GList *l;
+
+ realized = mapped = visible = FALSE;
+
+ is_widget = GTK_IS_WIDGET (object);
+ if (is_widget)
+ {
+ GtkWidget *widget = GTK_WIDGET (object);
+ name = gtk_widget_get_name (GTK_WIDGET (object));
+
+ realized = gtk_widget_get_realized (widget);
+ mapped = gtk_widget_get_mapped (widget);
+ visible = gtk_widget_get_visible (widget);
+ }
+
+ if (name == NULL || g_strcmp0 (name, class_name) == 0)
+ {
+ if (GTK_IS_LABEL (object))
+ {
+ name = gtk_label_get_text (GTK_LABEL (object));
+ }
+ else if (GTK_IS_BUTTON (object))
+ {
+ name = gtk_button_get_label (GTK_BUTTON (object));
+ }
+ else if (GTK_IS_WINDOW (object))
+ {
+ name = gtk_window_get_title (GTK_WINDOW (object));
+ }
+ else
+ {
+ name = "";
+ }
+ }
+
+ address = g_strdup_printf ("%p", object);
+
+ gtk_tree_store_append (widget_tree->priv->model, &iter, parent_iter);
+ gtk_tree_store_set (widget_tree->priv->model, &iter,
+ OBJECT, object,
+ OBJECT_TYPE, class_name,
+ OBJECT_NAME, name,
+ WIDGET_REALIZED, realized,
+ WIDGET_MAPPED, mapped,
+ WIDGET_VISIBLE, visible,
+ OBJECT_ADDRESS, address,
+ SENSITIVE, !is_widget || (realized && mapped && visible),
+ -1);
+ g_hash_table_insert (widget_tree->priv->iters, object, gtk_tree_iter_copy (&iter));
+
+ g_free(address);
+
+ if (GTK_IS_CONTAINER (object))
+ {
+ GList* children = NULL;
+
+ /* Pick up all children, including those that are internal. */
+ gtk_container_forall (GTK_CONTAINER (object),
+ on_container_forall, &children);
+
+ for (l = children; l != NULL; l = l->next)
+ {
+ parasite_widget_tree_append_object (widget_tree, l->data, &iter);
+ }
+
+ g_list_free(children);
+ }
+}
+
+void
+parasite_widget_tree_scan (ParasiteWidgetTree *widget_tree,
+ GtkWidget *window)
+{
+ gtk_tree_store_clear (widget_tree->priv->model);
+ g_hash_table_remove_all (widget_tree->priv->iters);
+ parasite_widget_tree_append_object (widget_tree, G_OBJECT (gtk_settings_get_default ()), NULL);
+ parasite_widget_tree_append_object (widget_tree, G_OBJECT (window), NULL);
+
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (widget_tree));
+}
+
+
+/*
+static GList *
+get_parents(GtkWidget *widget,
+ GList *parents)
+{
+ GtkWidget *parent = gtk_widget_get_parent(widget);
+
+ parents = g_list_prepend(parents, widget);
+
+ if (parent != NULL)
+ return get_parents(parent, parents);
+
+ return parents;
+}
+
+gboolean
+parasite_widget_tree_find_widget (ParasiteWidgetTree *widget_tree,
+ GtkWidget *widget,
+ GtkTreeIter *iter)
+{
+ GList *parents = get_parents (widget, NULL);
+ GList *l;
+ GtkTreeIter inner_iter, parent_iter = {0};
+ gboolean found = FALSE;
+ gboolean in_root = TRUE;
+
+ for (l = parents; l != NULL; l = l->next)
+ {
+ GtkWidget *cur_widget = GTK_WIDGET (l->data);
+ gboolean valid;
+ found = FALSE;
+
+ for (valid = gtk_tree_model_iter_children (widget_tree->priv->model,
+ &inner_iter,
+ in_root ? NULL : &parent_iter);
+ valid;
+ valid = gtk_tree_model_iter_next (widget_tree->priv->model, &inner_iter))
+ {
+ GtkWidget *iter_widget;
+ gtk_tree_model_get (widget_tree->priv->model,
+ &inner_iter,
+ WIDGET, &iter_widget,
+ -1);
+ if (iter_widget == cur_widget)
+ {
+ parent_iter = inner_iter;
+ in_root = FALSE;
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ g_list_free(parents);
+
+ *iter = inner_iter;
+ return found;
+}
+*/
+
+gboolean
+parasite_widget_tree_find_object (ParasiteWidgetTree *widget_tree,
+ GObject *object,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter *internal_iter = g_hash_table_lookup (widget_tree->priv->iters, object);
+ if (internal_iter)
+ {
+ *iter = *internal_iter;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+parasite_widget_tree_select_object (ParasiteWidgetTree *widget_tree,
+ GObject *object)
+{
+ GtkTreeIter iter;
+
+ if (parasite_widget_tree_find_object (widget_tree, object, &iter))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (widget_tree->priv->model), &iter);
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(widget_tree), path);
+ gtk_tree_selection_select_iter(
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(widget_tree)),
+ &iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget_tree), path, NULL, FALSE, 0, 0);
+ }
+
+}
+
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/widget-tree.h b/modules/other/parasite/widget-tree.h
new file mode 100644
index 0000000..f3c5017
--- /dev/null
+++ b/modules/other/parasite/widget-tree.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_WIDGET_TREE_H_
+#define _GTKPARASITE_WIDGET_TREE_H_
+
+
+#include <gtk/gtk.h>
+
+
+#define PARASITE_TYPE_WIDGET_TREE (parasite_widget_tree_get_type())
+#define PARASITE_WIDGET_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_WIDGET_TREE,
ParasiteWidgetTree))
+#define PARASITE_WIDGET_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_WIDGET_TREE,
ParasiteWidgetTreeClass))
+#define PARASITE_IS_WIDGET_TREE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_WIDGET_TREE))
+#define PARASITE_IS_WIDGET_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_WIDGET_TREE))
+#define PARASITE_WIDGET_TREE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PARASITE_TYPE_WIDGET_TREE,
ParasiteWidgetTreeClass))
+
+
+typedef struct _ParasiteWidgetTreePrivate ParasiteWidgetTreePrivate;
+
+typedef struct _ParasiteWidgetTree {
+ GtkTreeView parent;
+
+ // Private
+ ParasiteWidgetTreePrivate *priv;
+} ParasiteWidgetTree;
+
+typedef struct _ParasiteWidgetTreeClass {
+ GtkTreeViewClass parent;
+
+ void (*widget_changed)(ParasiteWidgetTree *tree);
+} ParasiteWidgetTreeClass;
+
+
+G_BEGIN_DECLS
+
+
+GType parasite_widget_tree_get_type (void);
+GtkWidget *parasite_widget_tree_new (void);
+
+GObject *parasite_widget_tree_get_selected_object (ParasiteWidgetTree *widget_tree);
+
+void parasite_widget_tree_scan (ParasiteWidgetTree *widget_tree,
+ GtkWidget *window);
+void parasite_widget_tree_select_object (ParasiteWidgetTree *widget_tree,
+ GObject *object);
+void parasite_widget_tree_append_object (ParasiteWidgetTree *widget_tree,
+ GObject *object,
+ GtkTreeIter *parent_iter);
+gboolean parasite_widget_tree_find_object (ParasiteWidgetTree *widget_tree,
+ GObject *object,
+ GtkTreeIter *iter);
+
+G_END_DECLS
+
+
+#endif // _GTKPARASITE_WIDGETTREE_H_
+
+// vim: set et sw=4 ts=4:
diff --git a/modules/other/parasite/window.c b/modules/other/parasite/window.c
new file mode 100644
index 0000000..56670dd
--- /dev/null
+++ b/modules/other/parasite/window.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ * Copyright (c) 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "parasite.h"
+#include "prop-list.h"
+#include "classes-list.h"
+#include "css-editor.h"
+#include "object-hierarchy.h"
+#include "widget-tree.h"
+#include "python-hooks.h"
+#include "python-shell.h"
+#include "button-path.h"
+#include "themes.h"
+
+static void
+on_widget_tree_selection_changed (ParasiteWidgetTree *widget_tree,
+ ParasiteWindow *parasite)
+{
+ GObject *selected = parasite_widget_tree_get_selected_object (widget_tree);
+
+ if (selected != NULL)
+ {
+ parasite_proplist_set_object (PARASITE_PROPLIST (parasite->prop_list),
+ selected);
+ parasite_objecthierarchy_set_object (PARASITE_OBJECTHIERARCHY (parasite->oh),
+ selected);
+
+ if (GTK_IS_WIDGET (selected))
+ {
+ GtkWidget *widget = GTK_WIDGET (selected);
+
+ gtkparasite_flash_widget(parasite, widget);
+ parasite_buttonpath_set_widget (PARASITE_BUTTONPATH (parasite->button_path), widget);
+ parasite_classeslist_set_widget (PARASITE_CLASSESLIST (parasite->classes_list), widget);
+ parasite_csseditor_set_widget (PARASITE_CSSEDITOR (parasite->widget_css_editor), widget);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (parasite->classes_list, FALSE);
+ gtk_widget_set_sensitive (parasite->widget_css_editor, FALSE);
+ }
+ }
+}
+
+
+static gboolean
+on_widget_tree_button_press(ParasiteWidgetTree *widget_tree,
+ GdkEventButton *event,
+ ParasiteWindow *parasite)
+{
+ if (event->button == 3)
+ {
+ gtk_menu_popup(GTK_MENU(parasite->widget_popup), NULL, NULL,
+ NULL, NULL, event->button, event->time);
+ }
+
+ return FALSE;
+}
+
+
+static void
+on_send_widget_to_shell_activate(GtkWidget *menuitem,
+ ParasiteWindow *parasite)
+{
+ char *str;
+ GObject *object;
+
+ object = parasite_widget_tree_get_selected_object (PARASITE_WIDGET_TREE (parasite->widget_tree));
+
+ if (!object)
+ return;
+
+ str = g_strdup_printf ("parasite.gobj(%p)", object);
+ parasite_python_shell_append_text (PARASITE_PYTHON_SHELL (parasite->python_shell),
+ str,
+ NULL);
+
+ g_free(str);
+ parasite_python_shell_focus (PARASITE_PYTHON_SHELL (parasite->python_shell));
+}
+
+
+static GtkWidget *
+create_widget_list_pane(ParasiteWindow *parasite)
+{
+ GtkWidget *swin;
+
+ swin = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar-policy", GTK_POLICY_ALWAYS,
+ "shadow-type", GTK_SHADOW_IN,
+ "width-request", 250,
+ "expand", TRUE,
+ NULL);
+
+ parasite->widget_tree = parasite_widget_tree_new();
+ gtk_container_add(GTK_CONTAINER(swin), parasite->widget_tree);
+
+ g_signal_connect(G_OBJECT(parasite->widget_tree),
+ "widget-changed",
+ G_CALLBACK(on_widget_tree_selection_changed),
+ parasite);
+
+ if (parasite_python_is_enabled())
+ {
+ g_signal_connect(G_OBJECT(parasite->widget_tree),
+ "button-press-event",
+ G_CALLBACK(on_widget_tree_button_press),
+ parasite);
+ }
+
+ return swin;
+}
+
+static GtkWidget *
+create_prop_list_pane(ParasiteWindow *parasite)
+{
+ GtkWidget *swin;
+
+ swin = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ "vscrollbar-policy", GTK_POLICY_ALWAYS,
+ "shadow-type", GTK_SHADOW_IN,
+ "width-request", 250,
+ NULL);
+
+ parasite->prop_list = parasite_proplist_new (parasite->widget_tree);
+ gtk_container_add(GTK_CONTAINER(swin), parasite->prop_list);
+
+ return swin;
+}
+
+static void
+on_show_graphic_updates_toggled(GtkWidget *toggle_button,
+ ParasiteWindow *parasite)
+{
+ gdk_window_set_debug_updates(
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
+}
+
+static GtkWidget *
+create_toolbar (ParasiteWindow *window) {
+ GtkWidget *button;
+ GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ GtkStyleContext *context = gtk_widget_get_style_context (box);
+ GtkWidget *image;
+
+ gtk_style_context_add_class (context, "linked");
+
+ button = gtkparasite_inspect_button_new (window);
+ gtk_container_add (GTK_CONTAINER (box), button);
+
+ button = gtk_toggle_button_new ();
+ image = gtk_image_new_from_icon_name ("view-refresh", GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (button), image);
+ gtk_widget_set_tooltip_text (button, "Show Graphic Updates");
+ gtk_container_add (GTK_CONTAINER (box), button);
+ g_signal_connect (button,
+ "toggled",
+ G_CALLBACK (on_show_graphic_updates_toggled),
+ window);
+
+ gtk_widget_show_all (box);
+ return box;
+}
+
+static void
+delete_window (GtkWidget *widget) {
+ GApplication *app = g_application_get_default ();
+
+ gtk_widget_hide (widget);
+
+ if (app)
+ g_application_quit (app);
+ else
+ exit (0);
+}
+
+void
+gtkparasite_window_create()
+{
+ ParasiteWindow *window;
+ GtkWidget *vpaned, *hpaned;
+ GtkWidget *header;
+ GtkWidget *box;
+ GtkWidget *nb;
+ char *title;
+
+ window = g_new0(ParasiteWindow, 1);
+
+ /*
+ * Create the top-level window.
+ */
+ window->window = g_object_new (GTK_TYPE_WINDOW,
+ "default-height", 500,
+ "default-width", 1000,
+ NULL);
+ g_signal_connect (window->window,
+ "delete-event",
+ G_CALLBACK (delete_window),
+ NULL);
+
+ title = g_strdup_printf("Parasite - %s", g_get_application_name());
+ gtk_window_set_title (GTK_WINDOW (window->window), title);
+
+ header = gtk_header_bar_new ();
+ gtk_header_bar_set_title (GTK_HEADER_BAR (header), title);
+ gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header), TRUE);
+ gtk_window_set_titlebar (GTK_WINDOW (window->window), header);
+ gtk_header_bar_pack_start (GTK_HEADER_BAR (header), create_toolbar (window));
+
+ g_free(title);
+
+ nb = g_object_new (GTK_TYPE_NOTEBOOK,
+ "show-border", FALSE,
+ "margin-left", 6,
+ "margin-right", 6,
+ "margin-bottom", 6,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (window->window), nb);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ box,
+ gtk_label_new ("Widget Tree"));
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ parasite_themes_new (),
+ gtk_label_new ("Themes"));
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ parasite_csseditor_new (TRUE),
+ gtk_label_new ("Custom CSS"));
+
+ window->button_path = parasite_buttonpath_new ();
+ gtk_container_add (GTK_CONTAINER (box), window->button_path);
+
+ hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_container_add (GTK_CONTAINER (box), hpaned);
+
+ vpaned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
+ gtk_paned_pack1 (GTK_PANED (hpaned), vpaned, TRUE, FALSE);
+ gtk_paned_pack1 (GTK_PANED (vpaned), create_widget_list_pane (window), TRUE, FALSE);
+
+ nb = g_object_new (GTK_TYPE_NOTEBOOK,
+ "enable-popup", TRUE,
+ "show-border", FALSE,
+ NULL);
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ create_prop_list_pane (window),
+ gtk_label_new ("GObject Properties"));
+
+ window->oh = parasite_objecthierarchy_new ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ window->oh,
+ gtk_label_new ("Hierarchy"));
+
+ window->classes_list = parasite_classeslist_new ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ window->classes_list,
+ gtk_label_new ("CSS Classes"));
+
+ window->widget_css_editor = parasite_csseditor_new (FALSE);
+ gtk_notebook_append_page (GTK_NOTEBOOK (nb),
+ window->widget_css_editor,
+ gtk_label_new ("Custom CSS"));
+
+ gtk_paned_pack2 (GTK_PANED (hpaned), nb, FALSE, FALSE);
+
+ if (parasite_python_is_enabled())
+ {
+ GtkWidget *menuitem;
+
+ window->python_shell = parasite_python_shell_new();
+ gtk_paned_pack2(GTK_PANED(vpaned), window->python_shell, FALSE, FALSE);
+
+ /*
+ * XXX Eventually we'll want to put more in here besides the menu
+ * item we define below. At that point, we'll need to make this
+ * more generic.
+ */
+ window->widget_popup = gtk_menu_new();
+ gtk_widget_show(window->widget_popup);
+
+ menuitem = gtk_menu_item_new_with_label("Send Widget to Shell");
+ gtk_widget_show(menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(window->widget_popup), menuitem);
+
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(on_send_widget_to_shell_activate), window);
+ }
+
+ gtk_widget_show_all (window->window);
+}
+
+// vim: set et sw=4 ts=4:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]