[gnome-builder/wip/chergert/dspy: 2/11] dspy: start on dspy plugin
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/dspy: 2/11] dspy: start on dspy plugin
- Date: Sun, 14 Apr 2019 20:35:03 +0000 (UTC)
commit 24f4354098e91aeff5f5827f66fa235e231c93af
Author: Christian Hergert <chergert redhat com>
Date: Thu Apr 11 14:41:12 2019 -0700
dspy: start on dspy plugin
meson_options.txt | 1 +
src/plugins/dspy/dspy-connection-model.c | 521 ++++++++++++++++++++++++++++
src/plugins/dspy/dspy-connection-model.h | 42 +++
src/plugins/dspy/dspy-connection-row.c | 116 +++++++
src/plugins/dspy/dspy-connection-row.h | 41 +++
src/plugins/dspy/dspy-connection-row.ui | 19 +
src/plugins/dspy/dspy-name-row.c | 125 +++++++
src/plugins/dspy/dspy-name-row.h | 36 ++
src/plugins/dspy/dspy-name-row.ui | 33 ++
src/plugins/dspy/dspy-name-view.c | 205 +++++++++++
src/plugins/dspy/dspy-name-view.h | 40 +++
src/plugins/dspy/dspy-name-view.ui | 154 ++++++++
src/plugins/dspy/dspy-name.c | 289 +++++++++++++++
src/plugins/dspy/dspy-name.h | 49 +++
src/plugins/dspy/dspy-path-model.c | 472 +++++++++++++++++++++++++
src/plugins/dspy/dspy-path-model.h | 36 ++
src/plugins/dspy/dspy-plugin.c | 34 ++
src/plugins/dspy/dspy.gresource.xml | 11 +
src/plugins/dspy/dspy.plugin | 9 +
src/plugins/dspy/gbp-dspy-surface.c | 208 +++++++++++
src/plugins/dspy/gbp-dspy-surface.h | 33 ++
src/plugins/dspy/gbp-dspy-surface.ui | 79 +++++
src/plugins/dspy/gbp-dspy-workspace-addin.c | 92 +++++
src/plugins/dspy/gbp-dspy-workspace-addin.h | 31 ++
src/plugins/dspy/gtk/menus.ui | 15 +
src/plugins/dspy/meson.build | 23 ++
src/plugins/meson.build | 2 +
27 files changed, 2716 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index 57fe9301c..3d6fc2e40 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -31,6 +31,7 @@ option('plugin_color_picker', type: 'boolean')
option('plugin_ctags', type: 'boolean')
option('plugin_devhelp', type: 'boolean')
option('plugin_deviced', type: 'boolean', value: false)
+option('plugin_dspy', type: 'boolean')
option('plugin_editorconfig', type: 'boolean')
option('plugin_eslint', type: 'boolean')
option('plugin_file_search', type: 'boolean')
diff --git a/src/plugins/dspy/dspy-connection-model.c b/src/plugins/dspy/dspy-connection-model.c
new file mode 100644
index 000000000..72a5f5412
--- /dev/null
+++ b/src/plugins/dspy/dspy-connection-model.c
@@ -0,0 +1,521 @@
+/* dspy-connection-model.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-connection-model"
+
+#include "config.h"
+
+#include "dspy-connection-model.h"
+#include "dspy-name.h"
+
+struct _DspyConnectionModel
+{
+ GObject parent;
+
+ GDBusConnection *connection;
+ GCancellable *cancellable;
+ GSequence *names;
+ GDBusProxy *bus_proxy;
+ gchar *address;
+ GBusType bus_type;
+
+ guint name_owner_changed_handler;
+};
+
+enum {
+ PROP_0,
+ PROP_CONNECTION,
+ N_PROPS
+};
+
+static guint
+dspy_connection_model_get_n_items (GListModel *model)
+{
+ DspyConnectionModel *self = DSPY_CONNECTION_MODEL (model);
+ return g_sequence_get_length (self->names);
+}
+
+static GType
+dspy_connection_model_get_item_type (GListModel *model)
+{
+ /* XXX: switch to type */
+ return G_TYPE_OBJECT;
+}
+
+static gpointer
+dspy_connection_model_get_item (GListModel *model,
+ guint position)
+{
+ DspyConnectionModel *self = DSPY_CONNECTION_MODEL (model);
+ GSequenceIter *iter = g_sequence_get_iter_at_pos (self->names, position);
+
+ if (g_sequence_iter_is_end (iter))
+ return NULL;
+
+ return g_object_ref (g_sequence_get (iter));
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = dspy_connection_model_get_item_type;
+ iface->get_n_items = dspy_connection_model_get_n_items;
+ iface->get_item = dspy_connection_model_get_item;
+}
+
+G_DEFINE_TYPE_WITH_CODE (DspyConnectionModel, dspy_connection_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+dspy_connection_model_finalize (GObject *object)
+{
+ DspyConnectionModel *self = (DspyConnectionModel *)object;
+
+ g_assert (self->connection == NULL);
+ g_assert (self->cancellable == NULL);
+
+ g_clear_object (&self->connection);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->bus_proxy);
+ g_clear_pointer (&self->names, g_sequence_free);
+ g_clear_pointer (&self->address, g_free);
+
+ G_OBJECT_CLASS (dspy_connection_model_parent_class)->finalize (object);
+}
+
+static void
+dspy_connection_model_dispose (GObject *object)
+{
+ DspyConnectionModel *self = (DspyConnectionModel *)object;
+
+ dspy_connection_model_set_connection (self, NULL);
+
+ G_OBJECT_CLASS (dspy_connection_model_parent_class)->dispose (object);
+}
+
+static void
+dspy_connection_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ DspyConnectionModel *self = DSPY_CONNECTION_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, dspy_connection_model_get_connection (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dspy_connection_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ DspyConnectionModel *self = DSPY_CONNECTION_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ dspy_connection_model_set_connection (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dspy_connection_model_class_init (DspyConnectionModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dspy_connection_model_dispose;
+ object_class->finalize = dspy_connection_model_finalize;
+ object_class->get_property = dspy_connection_model_get_property;
+ object_class->set_property = dspy_connection_model_set_property;
+
+ /**
+ * DspyConnectionModel:connection:
+ *
+ * The "connection" property contains the #GDBusConnection that will be monitored
+ * for changes to the bus.
+ */
+ properties [PROP_CONNECTION] =
+ g_param_spec_object ("connection",
+ "Connection",
+ "A GDBus connection for the source of bus changes",
+ G_TYPE_DBUS_CONNECTION,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+dspy_connection_model_init (DspyConnectionModel *self)
+{
+ self->names = g_sequence_new (g_object_unref);
+}
+
+static void
+dspy_connection_model_get_name_owner_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = (GDBusProxy *)object;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(DspyName) name = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (DSPY_IS_NAME (name));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DSPY_IS_NAME (name));
+
+ if ((ret = g_dbus_proxy_call_finish (proxy, result, &error)))
+ {
+ const gchar *owner = NULL;
+
+ g_variant_get (ret, "(&s)", &owner);
+ dspy_name_set_owner (name, owner);
+ }
+}
+
+static void
+dspy_connection_model_get_process_id_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = (GDBusProxy *)object;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(DspyName) name = user_data;
+ g_autoptr(GError) error = NULL;
+ GPid pid = 0;
+
+ g_assert (DSPY_IS_NAME (name));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DSPY_IS_NAME (name));
+
+ if ((ret = g_dbus_proxy_call_finish (proxy, result, &error)))
+ g_variant_get (ret, "(u)", &pid);
+
+ dspy_name_set_pid (name, pid);
+}
+
+static void
+dspy_connection_model_add_names (DspyConnectionModel *self,
+ const gchar * const *names,
+ gboolean activatable)
+{
+ g_assert (DSPY_IS_CONNECTION_MODEL (self));
+ g_assert (names != NULL);
+
+ for (guint i = 0; names[i]; i++)
+ {
+ g_autoptr(DspyName) name = dspy_name_new (names[i], activatable);
+ GSequenceIter *seq;
+ guint removed = 0;
+
+ /* Skip if we already know about the name, but replace
+ * the item if we found something activatable.
+ */
+ if ((seq = g_sequence_lookup (self->names,
+ name,
+ (GCompareDataFunc) dspy_name_compare,
+ NULL)))
+ {
+ if (!activatable)
+ continue;
+ g_sequence_remove (seq);
+ removed++;
+ }
+
+ seq = g_sequence_insert_sorted (self->names,
+ g_object_ref (name),
+ (GCompareDataFunc) dspy_name_compare,
+ NULL);
+
+ g_list_model_items_changed (G_LIST_MODEL (self),
+ g_sequence_iter_get_position (seq),
+ removed, 1);
+
+ g_dbus_proxy_call (self->bus_proxy,
+ "GetConnectionUnixProcessID",
+ g_variant_new ("(s)", names[i]),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ dspy_connection_model_get_process_id_cb,
+ g_object_ref (name));
+
+ if (names[i][0] != ':')
+ g_dbus_proxy_call (self->bus_proxy,
+ "GetNameOwner",
+ g_variant_new ("(s)", names[i]),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ dspy_connection_model_get_name_owner_cb,
+ g_object_ref (name));
+ }
+}
+
+static void
+dspy_connection_model_name_owner_changed_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *params,
+ gpointer user_data)
+{
+ DspyConnectionModel *self = user_data;
+ g_autoptr(DspyName) name = NULL;
+ GSequenceIter *seq;
+ const gchar *vname;
+ const gchar *vold_name;
+ const gchar *vnew_name;
+
+ g_assert (G_IS_DBUS_CONNECTION (connection));
+ g_assert (DSPY_IS_CONNECTION_MODEL (self));
+
+ g_variant_get (params, "(&s&s&s)", &vname, &vold_name, &vnew_name);
+
+ name = dspy_name_new (vname, FALSE);
+ seq = g_sequence_lookup (self->names,
+ name,
+ (GCompareDataFunc) dspy_name_compare,
+ NULL);
+
+ if (seq == NULL && vnew_name[0])
+ {
+ const gchar *names[] = { vname, NULL };
+ dspy_connection_model_add_names (self, names, FALSE);
+ }
+ else if (!vnew_name[0])
+ {
+ guint position = g_sequence_iter_get_position (seq);
+ g_sequence_remove (seq);
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
+ }
+
+#if 0
+ g_print ("%s %s %s %s %s\n",
+ sender_name, object_path, interface_name, signal_name,
+ g_variant_print (params, TRUE));
+#endif
+}
+
+static void
+dspy_connection_model_list_activatable_names_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = (GDBusProxy *)object;
+ g_autoptr(DspyConnectionModel) self = user_data;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) strv = NULL;
+
+ g_assert (G_IS_DBUS_PROXY (proxy));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DSPY_IS_CONNECTION_MODEL (self));
+
+ if (!(ret = g_dbus_proxy_call_finish (proxy, result, &error)))
+ {
+ g_warning ("Failed to list activatable names: %s", error->message);
+ return;
+ }
+
+ g_variant_get (ret, "(^as)", &strv);
+ dspy_connection_model_add_names (self, (const gchar * const *)strv, TRUE);
+}
+
+static void
+dspy_connection_model_list_names_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = (GDBusProxy *)object;
+ g_autoptr(DspyConnectionModel) self = user_data;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) strv = NULL;
+
+ g_assert (G_IS_DBUS_PROXY (proxy));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DSPY_IS_CONNECTION_MODEL (self));
+
+ if (!(ret = g_dbus_proxy_call_finish (proxy, result, &error)))
+ {
+ g_warning ("Failed to list names: %s", error->message);
+ return;
+ }
+
+ g_variant_get (ret, "(^as)", &strv);
+ dspy_connection_model_add_names (self, (const gchar * const *)strv, FALSE);
+}
+
+DspyConnectionModel *
+dspy_connection_model_new (void)
+{
+ return g_object_new (DSPY_TYPE_CONNECTION_MODEL, NULL);
+}
+
+/**
+ * dspy_connection_model_get_connection:
+ * @self: a #DspyConnectionModel
+ *
+ * Gets the #GDBusConnection used for the model.
+ *
+ * Returns: (transfer none) (nullable): a #GDBusConnection or %NULL
+ */
+GDBusConnection *
+dspy_connection_model_get_connection (DspyConnectionModel *self)
+{
+ g_return_val_if_fail (DSPY_IS_CONNECTION_MODEL (self), NULL);
+
+ return self->connection;
+}
+
+void
+dspy_connection_model_set_connection (DspyConnectionModel *self,
+ GDBusConnection *connection)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_MODEL (self));
+
+ if (self->connection == connection)
+ return;
+
+ if (self->connection != NULL)
+ {
+ g_cancellable_cancel (self->cancellable);
+ g_dbus_connection_signal_unsubscribe (self->connection,
+ self->name_owner_changed_handler);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->connection);
+ g_clear_object (&self->bus_proxy);
+ g_clear_pointer (&self->names, g_sequence_free);
+ self->names = g_sequence_new (g_object_unref);
+ }
+
+ g_assert (self->cancellable == NULL);
+ g_assert (self->connection == NULL);
+ g_assert (self->bus_proxy == NULL);
+ g_assert (g_sequence_is_empty (self->names));
+
+ if (connection != NULL)
+ {
+ g_autoptr(GError) error = NULL;
+
+ self->connection = g_object_ref (connection);
+ self->cancellable = g_cancellable_new ();
+ self->name_owner_changed_handler =
+ g_dbus_connection_signal_subscribe (self->connection,
+ NULL,
+ "org.freedesktop.DBus",
+ "NameOwnerChanged",
+ NULL,
+ NULL,
+ 0,
+ dspy_connection_model_name_owner_changed_cb,
+ self,
+ NULL);
+ self->bus_proxy = g_dbus_proxy_new_sync (self->connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ self->cancellable, &error);
+
+ if (self->bus_proxy == NULL)
+ {
+ g_warning ("Failed to create DBus proxy: %s", error->message);
+ goto notify;
+ }
+
+ g_dbus_proxy_call (self->bus_proxy,
+ "ListActivatableNames",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ dspy_connection_model_list_activatable_names_cb,
+ g_object_ref (self));
+
+ g_dbus_proxy_call (self->bus_proxy,
+ "ListNames",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ dspy_connection_model_list_names_cb,
+ g_object_ref (self));
+ }
+
+notify:
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONNECTION]);
+}
+
+GBusType
+dspy_connection_model_get_bus_type (DspyConnectionModel *self)
+{
+ g_return_val_if_fail (DSPY_IS_CONNECTION_MODEL (self), G_BUS_TYPE_NONE);
+
+ return self->bus_type;
+}
+
+void
+dspy_connection_model_set_bus_type (DspyConnectionModel *self,
+ GBusType bus_type)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_MODEL (self));
+
+ self->bus_type = bus_type;
+}
+
+const gchar *
+dspy_connection_model_get_address (DspyConnectionModel *self)
+{
+ g_return_val_if_fail (DSPY_IS_CONNECTION_MODEL (self), NULL);
+
+ return self->address;
+}
+
+void
+dspy_connection_model_set_address (DspyConnectionModel *self,
+ const gchar *address)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_MODEL (self));
+
+ if (g_strcmp0 (self->address, address) != 0)
+ {
+ g_free (self->address);
+ self->address = g_strdup (address);
+ }
+}
diff --git a/src/plugins/dspy/dspy-connection-model.h b/src/plugins/dspy/dspy-connection-model.h
new file mode 100644
index 000000000..0e442ecbc
--- /dev/null
+++ b/src/plugins/dspy/dspy-connection-model.h
@@ -0,0 +1,42 @@
+/* dspy-connection-model.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_CONNECTION_MODEL (dspy_connection_model_get_type())
+
+G_DECLARE_FINAL_TYPE (DspyConnectionModel, dspy_connection_model, DSPY, CONNECTION_MODEL, GObject)
+
+DspyConnectionModel *dspy_connection_model_new (void);
+GBusType dspy_connection_model_get_bus_type (DspyConnectionModel *self);
+void dspy_connection_model_set_bus_type (DspyConnectionModel *self,
+ GBusType bus_type);
+const gchar *dspy_connection_model_get_address (DspyConnectionModel *self);
+void dspy_connection_model_set_address (DspyConnectionModel *self,
+ const gchar *address);
+GDBusConnection *dspy_connection_model_get_connection (DspyConnectionModel *self);
+void dspy_connection_model_set_connection (DspyConnectionModel *self,
+ GDBusConnection *connection);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-connection-row.c b/src/plugins/dspy/dspy-connection-row.c
new file mode 100644
index 000000000..c06dc4b7d
--- /dev/null
+++ b/src/plugins/dspy/dspy-connection-row.c
@@ -0,0 +1,116 @@
+/* dspy-connection-row.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-connection-row"
+
+#include "config.h"
+
+#include "dspy-connection-row.h"
+
+struct _DspyConnectionRow
+{
+ GtkListBoxRow parent;
+ GtkLabel *label;
+ gchar *address;
+ GBusType bus_type;
+};
+
+G_DEFINE_TYPE (DspyConnectionRow, dspy_connection_row, GTK_TYPE_LIST_BOX_ROW)
+
+static void
+dspy_connection_row_finalize (GObject *object)
+{
+ DspyConnectionRow *self = (DspyConnectionRow *)object;
+
+ g_clear_pointer (&self->address, g_free);
+
+ G_OBJECT_CLASS (dspy_connection_row_parent_class)->finalize (object);
+}
+
+static void
+dspy_connection_row_class_init (DspyConnectionRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = dspy_connection_row_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/plugins/dspy/dspy-connection-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, DspyConnectionRow, label);
+}
+
+static void
+dspy_connection_row_init (DspyConnectionRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+DspyConnectionRow *
+dspy_connection_row_new (void)
+{
+ return g_object_new (DSPY_TYPE_CONNECTION_ROW, NULL);
+}
+
+const gchar *
+dspy_connection_row_get_address (DspyConnectionRow *self)
+{
+ g_return_val_if_fail (DSPY_IS_CONNECTION_ROW (self), NULL);
+
+ return self->address;
+}
+
+void
+dspy_connection_row_set_address (DspyConnectionRow *self,
+ const gchar *address)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_ROW (self));
+
+ if (g_strcmp0 (address, self->address) != 0)
+ {
+ g_free (self->address);
+ self->address = g_strdup (address);
+ }
+}
+
+GBusType
+dspy_connection_row_get_bus_type (DspyConnectionRow *self)
+{
+ g_return_val_if_fail (DSPY_IS_CONNECTION_ROW (self), 0);
+
+ return self->bus_type;
+}
+
+void
+dspy_connection_row_set_bus_type (DspyConnectionRow *self,
+ GBusType bus_type)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_ROW (self));
+
+ self->bus_type = bus_type;
+}
+
+void
+dspy_connection_row_set_title (DspyConnectionRow *self,
+ const gchar *title)
+{
+ g_return_if_fail (DSPY_IS_CONNECTION_ROW (self));
+
+ gtk_label_set_label (self->label, title);
+}
diff --git a/src/plugins/dspy/dspy-connection-row.h b/src/plugins/dspy/dspy-connection-row.h
new file mode 100644
index 000000000..3b5ca7a4a
--- /dev/null
+++ b/src/plugins/dspy/dspy-connection-row.h
@@ -0,0 +1,41 @@
+/* dspy-connection-row.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_CONNECTION_ROW (dspy_connection_row_get_type())
+
+G_DECLARE_FINAL_TYPE (DspyConnectionRow, dspy_connection_row, DSPY, CONNECTION_ROW, GtkListBoxRow)
+
+DspyConnectionRow *dspy_connection_row_new (void);
+const gchar *dspy_connection_row_get_address (DspyConnectionRow *self);
+void dspy_connection_row_set_address (DspyConnectionRow *self,
+ const gchar *address);
+GBusType dspy_connection_row_get_bus_type (DspyConnectionRow *self);
+void dspy_connection_row_set_bus_type (DspyConnectionRow *self,
+ GBusType bus_type);
+void dspy_connection_row_set_title (DspyConnectionRow *self,
+ const gchar *title);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-connection-row.ui b/src/plugins/dspy/dspy-connection-row.ui
new file mode 100644
index 000000000..06428c2b1
--- /dev/null
+++ b/src/plugins/dspy/dspy-connection-row.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="DspyConnectionRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="margin">6</property>
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/dspy/dspy-name-row.c b/src/plugins/dspy/dspy-name-row.c
new file mode 100644
index 000000000..054e78acb
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-row.c
@@ -0,0 +1,125 @@
+/* dspy-name-row.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-name-row"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "dspy-name-row.h"
+
+struct _DspyNameRow
+{
+ GtkListBoxRow parent;
+
+ DspyName *name;
+
+ GtkLabel *label;
+ GtkLabel *subtitle;
+};
+
+G_DEFINE_TYPE (DspyNameRow, dspy_name_row, GTK_TYPE_LIST_BOX_ROW)
+
+static void
+update_subtitle (DspyNameRow *self)
+{
+ g_autoptr(GString) str = g_string_new (NULL);
+ GPid pid = dspy_name_get_pid (self->name);
+
+ if (dspy_name_get_activatable (self->name))
+ g_string_append_printf (str, _("%s: %s"), _("Activatable"), _("Yes"));
+ else
+ g_string_append_printf (str, _("%s: %s"), _("Activatable"), _("No"));
+
+ if (pid != 0)
+ {
+ g_string_append (str, ", ");
+ g_string_append_printf (str, _("%s: %u"), _("Pid"), pid);
+ }
+
+ gtk_label_set_label (self->subtitle, str->str);
+}
+
+static void
+dspy_name_row_finalize (GObject *object)
+{
+ DspyNameRow *self = (DspyNameRow *)object;
+
+ g_clear_object (&self->name);
+
+ G_OBJECT_CLASS (dspy_name_row_parent_class)->finalize (object);
+}
+
+static void
+dspy_name_row_class_init (DspyNameRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = dspy_name_row_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/plugins/dspy/dspy-name-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, DspyNameRow, label);
+ gtk_widget_class_bind_template_child (widget_class, DspyNameRow, subtitle);
+}
+
+static void
+dspy_name_row_init (DspyNameRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+DspyNameRow *
+dspy_name_row_new (DspyName *name)
+{
+ DspyNameRow *self;
+
+ g_return_val_if_fail (DSPY_IS_NAME (name), NULL);
+
+ self = g_object_new (DSPY_TYPE_NAME_ROW, NULL);
+ self->name = g_object_ref (name);
+
+ gtk_label_set_label (self->label, dspy_name_get_name (name));
+
+ g_signal_connect_object (name,
+ "notify::pid",
+ G_CALLBACK (update_subtitle),
+ self,
+ G_CONNECT_SWAPPED);
+
+ update_subtitle (self);
+
+ return g_steal_pointer (&self);
+}
+
+/**
+ * dspy_name_row_get_name:
+ * @self: a #DspyNameRow
+ *
+ * Returns: (transfer none): a #DspyNameRow or %NULL
+ */
+DspyName *
+dspy_name_row_get_name (DspyNameRow *self)
+{
+ g_return_val_if_fail (DSPY_IS_NAME_ROW (self), NULL);
+
+ return self->name;
+}
diff --git a/src/plugins/dspy/dspy-name-row.h b/src/plugins/dspy/dspy-name-row.h
new file mode 100644
index 000000000..39d6c4ce8
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-row.h
@@ -0,0 +1,36 @@
+/* dspy-name-row.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "dspy-name.h"
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_NAME_ROW (dspy_name_row_get_type())
+
+G_DECLARE_FINAL_TYPE (DspyNameRow, dspy_name_row, DSPY, NAME_ROW, GtkListBoxRow)
+
+DspyNameRow *dspy_name_row_new (DspyName *name);
+DspyName *dspy_name_row_get_name (DspyNameRow *self);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-name-row.ui b/src/plugins/dspy/dspy-name-row.ui
new file mode 100644
index 000000000..6fa701b5d
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-row.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="DspyNameRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="margin">6</property>
+ <property name="spacing">3</property>
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="subtitle">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ <property name="visible">true</property>
+ <property name="ellipsize">end</property>
+ <attributes>
+ <attribute name="foreground-alpha" value="39321"/>
+ <attribute name="scale" value="0.75"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/dspy/dspy-name-view.c b/src/plugins/dspy/dspy-name-view.c
new file mode 100644
index 000000000..e71d45c22
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-view.c
@@ -0,0 +1,205 @@
+/* dspy-name-view.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-name-view"
+
+#include "config.h"
+
+#include <dazzle.h>
+
+#include "dspy-name-view.h"
+#include "dspy-path-model.h"
+
+struct _DspyNameView
+{
+ GtkBin parent;
+
+ GDBusConnection *connection;
+ DspyName *name;
+
+ GtkLabel *address_label;
+ GtkLabel *name_label;
+ GtkLabel *unique_label;
+ GtkTreeView *tree_view;
+ GtkButton *refresh_button;
+} DspyNameViewPrivate;
+
+G_DEFINE_TYPE (DspyNameView, dspy_name_view, GTK_TYPE_BIN)
+
+static void
+on_refresh_button_clicked_cb (DspyNameView *self,
+ GtkButton *button)
+{
+ g_autoptr(DspyPathModel) path_model = NULL;
+
+ g_assert (DSPY_IS_NAME_VIEW (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+ if (self->name == NULL)
+ return;
+
+ gtk_label_set_label (self->name_label, dspy_name_get_name (self->name));
+ gtk_label_set_label (self->unique_label, dspy_name_get_owner (self->name));
+
+ path_model = dspy_path_model_new (self->connection, self->name);
+ gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (path_model));
+}
+
+static void
+on_tree_view_row_activated_cb (DspyNameView *self,
+ GtkTreePath *tree_path,
+ GtkTreeViewColumn *column,
+ GtkTreeView *tree_view)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter, children;
+
+ g_assert (DSPY_IS_NAME_VIEW (self));
+ g_assert (tree_path != NULL);
+ g_assert (!column || GTK_IS_TREE_VIEW_COLUMN (column));
+ g_assert (GTK_IS_TREE_VIEW (tree_view));
+
+ if (gtk_tree_view_row_expanded (tree_view, tree_path))
+ {
+ gtk_tree_view_collapse_row (tree_view, tree_path);
+ return;
+ }
+
+ /* Quick cheat to always expand two-levels, so we can see things easier.
+ * Once we have a "type" of row to deal with, we can be more selective.
+ */
+
+ gtk_tree_view_expand_row (tree_view, tree_path, FALSE);
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (gtk_tree_model_get_iter (model, &iter, tree_path) &&
+ gtk_tree_model_iter_children (model, &children, &iter))
+ {
+ g_autoptr(GtkTreePath) copy = gtk_tree_path_copy (tree_path);
+
+ gtk_tree_path_down (copy);
+
+ do
+ {
+ gtk_tree_view_expand_row (tree_view, copy, FALSE);
+ gtk_tree_path_next (copy);
+ }
+ while (gtk_tree_model_iter_next (model, &children));
+ }
+}
+
+static void
+dspy_name_view_finalize (GObject *object)
+{
+ DspyNameView *self = (DspyNameView *)object;
+
+ g_clear_object (&self->connection);
+ g_clear_object (&self->name);
+
+ G_OBJECT_CLASS (dspy_name_view_parent_class)->finalize (object);
+}
+
+static void
+dspy_name_view_class_init (DspyNameViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = dspy_name_view_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/plugins/dspy/dspy-name-view.ui");
+ gtk_widget_class_bind_template_child (widget_class, DspyNameView, address_label);
+ gtk_widget_class_bind_template_child (widget_class, DspyNameView, name_label);
+ gtk_widget_class_bind_template_child (widget_class, DspyNameView, refresh_button);
+ gtk_widget_class_bind_template_child (widget_class, DspyNameView, tree_view);
+ gtk_widget_class_bind_template_child (widget_class, DspyNameView, unique_label);
+
+ g_type_ensure (DZL_TYPE_THREE_GRID);
+}
+
+static void
+dspy_name_view_init (DspyNameView *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->refresh_button,
+ "clicked",
+ G_CALLBACK (on_refresh_button_clicked_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->tree_view,
+ "row-activated",
+ G_CALLBACK (on_tree_view_row_activated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+DspyNameView *
+dspy_name_view_new (void)
+{
+ return g_object_new (DSPY_TYPE_NAME_VIEW, NULL);
+}
+
+static void
+dspy_name_view_clear (DspyNameView *self)
+{
+ g_return_if_fail (DSPY_IS_NAME_VIEW (self));
+
+ gtk_label_set_label (self->address_label, NULL);
+ gtk_label_set_label (self->name_label, NULL);
+ gtk_label_set_label (self->unique_label, NULL);
+ gtk_tree_view_set_model (self->tree_view, NULL);
+}
+
+void
+dspy_name_view_set_name (DspyNameView *self,
+ GDBusConnection *connection,
+ GBusType bus_type,
+ const gchar *address,
+ DspyName *name)
+{
+ g_autofree gchar *bus_address = NULL;
+ g_autoptr(DspyPathModel) path_model = NULL;
+
+ g_return_if_fail (DSPY_IS_NAME_VIEW (self));
+
+ if (self->connection == connection && self->name == name)
+ return;
+
+ dspy_name_view_clear (self);
+
+ if (name == NULL)
+ return;
+
+ g_set_object (&self->connection, connection);
+ g_set_object (&self->name, name);
+
+ if (bus_type != G_BUS_TYPE_NONE)
+ address = bus_address = g_dbus_address_get_for_bus_sync (bus_type, NULL, NULL);
+
+ gtk_label_set_label (self->address_label, address);
+ gtk_label_set_label (self->name_label, dspy_name_get_name (name));
+ gtk_label_set_label (self->unique_label, dspy_name_get_owner (name));
+
+ path_model = dspy_path_model_new (self->connection, self->name);
+ gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (path_model));
+}
diff --git a/src/plugins/dspy/dspy-name-view.h b/src/plugins/dspy/dspy-name-view.h
new file mode 100644
index 000000000..05d3eabf7
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-view.h
@@ -0,0 +1,40 @@
+/* dspy-name-view.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "dspy-name.h"
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_NAME_VIEW (dspy_name_view_get_type())
+
+G_DECLARE_FINAL_TYPE (DspyNameView, dspy_name_view, DSPY, NAME_VIEW, GtkBin)
+
+DspyNameView *dspy_name_view_new (void);
+void dspy_name_view_set_name (DspyNameView *self,
+ GDBusConnection *connection,
+ GBusType bus_type,
+ const gchar *address,
+ DspyName *name);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-name-view.ui b/src/plugins/dspy/dspy-name-view.ui
new file mode 100644
index 000000000..800fe94fc
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-view.ui
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="DspyNameView" parent="GtkBin">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="DzlThreeGrid">
+ <property name="column-spacing">12</property>
+ <property name="row-spacing">0</property>
+ <property name="halign">center</property>
+ <property name="margin-top">18</property>
+ <property name="margin-start">18</property>
+ <property name="margin-end">18</property>
+ <property name="margin-bottom">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1.0</property>
+ <property name="label" translatable="yes">Bus Address</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1.0</property>
+ <property name="label" translatable="yes">Name</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1.0</property>
+ <property name="label" translatable="yes">Unique Name</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">left</property>
+ <property name="row">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="address_label">
+ <property name="hexpand">true</property>
+ <property name="xalign">0.0</property>
+ <property name="visible">true</property>
+ <property name="width-chars">50</property>
+ <property name="tooltip-text" translatable="yes">See
http://dbus.freedesktop.org/doc/dbus-specification.html#addresses</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="hexpand">true</property>
+ <property name="xalign">0.0</property>
+ <property name="visible">true</property>
+ <property name="width-chars">50</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="unique_label">
+ <property name="hexpand">true</property>
+ <property name="xalign">0.0</property>
+ <property name="visible">true</property>
+ <property name="width-chars">50</property>
+ </object>
+ <packing>
+ <property name="column">center</property>
+ <property name="row">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="refresh_button">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Reload</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="column">right</property>
+ <property name="row">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeView" id="tree_view">
+ <property name="headers-visible">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="expand">true</property>
+ <property name="title" translatable="yes">Object Path</property>
+ <child>
+ <object class="GtkCellRendererText" id="text_cell">
+ <property name="xalign">0.0</property>
+ <property name="ypad">3</property>
+ <property name="xpad">3</property>
+ </object>
+ <attributes>
+ <attribute name="markup">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">vertical</property>
+ <widgets>
+ <widget name="name_label"/>
+ <widget name="unique_label"/>
+ <widget name="address_label"/>
+ <widget name="refresh_button"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/src/plugins/dspy/dspy-name.c b/src/plugins/dspy/dspy-name.c
new file mode 100644
index 000000000..7473473bb
--- /dev/null
+++ b/src/plugins/dspy/dspy-name.c
@@ -0,0 +1,289 @@
+/* dspy-name.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-name"
+
+#include "dspy-name.h"
+
+typedef struct
+{
+ gchar *name;
+ gchar *owner;
+ GPid pid;
+ guint activatable : 1;
+} DspyNamePrivate;
+
+enum {
+ PROP_0,
+ PROP_ACTIVATABLE,
+ PROP_NAME,
+ PROP_OWNER,
+ PROP_PID,
+ N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (DspyName, dspy_name, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+dspy_name_finalize (GObject *object)
+{
+ DspyName *self = (DspyName *)object;
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_clear_pointer (&priv->name, g_free);
+ g_clear_pointer (&priv->owner, g_free);
+
+ G_OBJECT_CLASS (dspy_name_parent_class)->finalize (object);
+}
+
+static void
+dspy_name_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ DspyName *self = DSPY_NAME (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVATABLE:
+ g_value_set_boolean (value, dspy_name_get_activatable (self));
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, dspy_name_get_name (self));
+ break;
+
+ case PROP_OWNER:
+ g_value_set_string (value, dspy_name_get_owner (self));
+ break;
+
+ case PROP_PID:
+ g_value_set_uint (value, dspy_name_get_pid (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dspy_name_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ DspyName *self = DSPY_NAME (object);
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVATABLE:
+ priv->activatable = g_value_get_boolean (value);
+ break;
+
+ case PROP_NAME:
+ priv->name = g_value_dup_string (value);
+ break;
+
+ case PROP_OWNER:
+ dspy_name_set_owner (self, g_value_get_string (value));
+ break;
+
+ case PROP_PID:
+ dspy_name_set_pid (self, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+dspy_name_class_init (DspyNameClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = dspy_name_finalize;
+ object_class->get_property = dspy_name_get_property;
+ object_class->set_property = dspy_name_set_property;
+
+ properties [PROP_ACTIVATABLE] =
+ g_param_spec_boolean ("activatable",
+ "Activatable",
+ "Activatable",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NAME] =
+ g_param_spec_string ("name",
+ "Name",
+ "The peer name",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_OWNER] =
+ g_param_spec_string ("owner",
+ "Owner",
+ "The owner of the DBus name",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PID] =
+ g_param_spec_uint ("pid",
+ "Pid",
+ "The pid of the peer",
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+dspy_name_init (DspyName *self)
+{
+}
+
+DspyName *
+dspy_name_new (const gchar *name,
+ gboolean activatable)
+{
+ return g_object_new (DSPY_TYPE_NAME,
+ "activatable", activatable,
+ "name", name,
+ NULL);
+}
+
+gboolean
+dspy_name_get_activatable (DspyName *self)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_val_if_fail (DSPY_IS_NAME (self), FALSE);
+
+ return priv->activatable;
+}
+
+const gchar *
+dspy_name_get_name (DspyName *self)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ return priv->name;
+}
+
+void
+dspy_name_set_name (DspyName *self,
+ const gchar *name)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_if_fail (DSPY_IS_NAME (self));
+
+ if (g_strcmp0 (name, priv->name) != 0)
+ {
+ g_free (priv->name);
+ priv->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+ }
+}
+
+gint
+dspy_name_compare (gconstpointer a,
+ gconstpointer b)
+{
+ DspyName *item1 = DSPY_NAME ((gpointer)a);
+ DspyName *item2 = DSPY_NAME ((gpointer)b);
+ const gchar *name1 = dspy_name_get_name (item1);
+ const gchar *name2 = dspy_name_get_name (item2);
+
+ if (name1[0] != name2[0])
+ {
+ if (name1[0] == ':')
+ return 1;
+ if (name2[0] == ':')
+ return -1;
+ }
+
+ /* Sort numbers like :1.300 better */
+ if (g_str_has_prefix (name1, ":1.") &&
+ g_str_has_prefix (name2, ":1."))
+ {
+ gint i1 = g_ascii_strtoll (name1 + 3, NULL, 10);
+ gint i2 = g_ascii_strtoll (name2 + 3, NULL, 10);
+
+ return i1 - i2;
+ }
+
+ return g_strcmp0 (name1, name2);
+}
+
+GPid
+dspy_name_get_pid (DspyName *self)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_val_if_fail (DSPY_IS_NAME (self), 0);
+
+ return priv->pid;
+}
+
+void
+dspy_name_set_pid (DspyName *self,
+ GPid pid)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_if_fail (DSPY_IS_NAME (self));
+
+ if (priv->pid != pid)
+ {
+ priv->pid = pid;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PID]);
+ }
+}
+
+const gchar *
+dspy_name_get_owner (DspyName *self)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_val_if_fail (DSPY_IS_NAME (self), NULL);
+
+ return priv->owner ? priv->owner : priv->name;
+}
+
+void
+dspy_name_set_owner (DspyName *self,
+ const gchar *owner)
+{
+ DspyNamePrivate *priv = dspy_name_get_instance_private (self);
+
+ g_return_if_fail (DSPY_IS_NAME (self));
+
+ if (g_strcmp0 (owner, priv->owner) != 0)
+ {
+ g_free (priv->owner);
+ priv->owner = g_strdup (owner);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_OWNER]);
+ }
+}
diff --git a/src/plugins/dspy/dspy-name.h b/src/plugins/dspy/dspy-name.h
new file mode 100644
index 000000000..eca03b177
--- /dev/null
+++ b/src/plugins/dspy/dspy-name.h
@@ -0,0 +1,49 @@
+/* dspy-name.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_NAME (dspy_name_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (DspyName, dspy_name, DSPY, NAME, GObject)
+
+struct _DspyNameClass
+{
+ GObjectClass parent_class;
+};
+
+DspyName *dspy_name_new (const gchar *name,
+ gboolean activatable);
+gboolean dspy_name_get_activatable (DspyName *self);
+GPid dspy_name_get_pid (DspyName *self);
+void dspy_name_set_pid (DspyName *self,
+ GPid pid);
+const gchar *dspy_name_get_name (DspyName *self);
+const gchar *dspy_name_get_owner (DspyName *self);
+void dspy_name_set_owner (DspyName *self,
+ const gchar *owner);
+gint dspy_name_compare (gconstpointer a,
+ gconstpointer b);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-path-model.c b/src/plugins/dspy/dspy-path-model.c
new file mode 100644
index 000000000..db629f9ed
--- /dev/null
+++ b/src/plugins/dspy/dspy-path-model.c
@@ -0,0 +1,472 @@
+/* dspy-path-model.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "dspy-path-model"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "dspy-path-model.h"
+
+struct _DspyPathModel
+{
+ GtkTreeStore store;
+ GCancellable *cancellable;
+ GDBusConnection *connection;
+ DspyName *name;
+};
+
+G_DEFINE_TYPE (DspyPathModel, dspy_path_model, GTK_TYPE_TREE_STORE)
+
+static GHashTable *simple_types;
+
+static void dspy_path_model_introspect (DspyPathModel *self,
+ const gchar *path);
+
+static gint
+compare_iface (gconstpointer a,
+ gconstpointer b)
+{
+ const GDBusInterfaceInfo * const *info1 = a;
+ const GDBusInterfaceInfo * const *info2 = b;
+
+ return g_strcmp0 ((*info1)->name, (*info2)->name);
+}
+
+static gint
+compare_method (gconstpointer a,
+ gconstpointer b)
+{
+ const GDBusMethodInfo * const *info1 = a;
+ const GDBusMethodInfo * const *info2 = b;
+
+ return g_strcmp0 ((*info1)->name, (*info2)->name);
+}
+
+static gint
+compare_property (gconstpointer a,
+ gconstpointer b)
+{
+ const GDBusPropertyInfo * const *info1 = a;
+ const GDBusPropertyInfo * const *info2 = b;
+
+ return g_strcmp0 ((*info1)->name, (*info2)->name);
+}
+
+static gint
+compare_signal (gconstpointer a,
+ gconstpointer b)
+{
+ const GDBusSignalInfo * const *info1 = a;
+ const GDBusSignalInfo * const *info2 = b;
+
+ return g_strcmp0 ((*info1)->name, (*info2)->name);
+}
+
+static void
+dspy_path_model_finalize (GObject *object)
+{
+ DspyPathModel *self = (DspyPathModel *)object;
+
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->connection);
+ g_clear_object (&self->name);
+
+ G_OBJECT_CLASS (dspy_path_model_parent_class)->finalize (object);
+}
+
+static void
+add_signature (GString *str,
+ const gchar *signature)
+{
+ const gchar *tmp;
+
+ /* TODO: decode signature into human text */
+
+ if ((tmp = g_hash_table_lookup (simple_types, signature)))
+ signature = tmp;
+
+ g_string_append_printf (str, "<span weight='bold' fgalpha='32767'>%s</span>", signature);
+}
+
+static gchar *
+prop_to_string (GDBusPropertyInfo *prop)
+{
+ GString *str = g_string_new (NULL);
+
+ g_string_append (str, prop->name);
+
+ g_string_append_c (str, ' ');
+ add_signature (str, prop->signature);
+
+ g_string_append_c (str, ' ');
+ g_string_append (str, "<span size='smaller' fgalpha='32767'>(");
+ if (prop->flags == (G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
+ g_string_append (str, _("read/write"));
+ else if (prop->flags == G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
+ g_string_append (str, _("read-only"));
+ else if (prop->flags == G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
+ g_string_append (str, _("write-only"));
+ g_string_append (str, ")</span>");
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+add_arg_name (GString *str,
+ const gchar *name)
+{
+ g_string_append_printf (str, "<span fgalpha='32767'>%s</span>", name);
+}
+
+static gchar *
+method_to_string (GDBusMethodInfo *method)
+{
+ GString *str = g_string_new (NULL);
+
+ g_string_append (str, method->name);
+
+ g_string_append (str, " (");
+
+ for (guint i = 0; method->in_args[i] != NULL; i++)
+ {
+ GDBusArgInfo *arg = method->in_args[i];
+
+ if (i > 0)
+ g_string_append (str, ", ");
+
+ add_signature (str, arg->signature);
+ g_string_append_c (str, ' ');
+ add_arg_name (str, arg->name);
+ }
+
+ g_string_append (str, ") ↦ (");
+
+ for (guint i = 0; method->out_args[i] != NULL; i++)
+ {
+ GDBusArgInfo *arg = method->out_args[i];
+
+ if (i > 0)
+ g_string_append (str, ", ");
+
+ add_signature (str, arg->signature);
+ g_string_append_c (str, ' ');
+ add_arg_name (str, arg->name);
+ }
+
+ g_string_append_c (str, ')');
+
+ return g_string_free (str, FALSE);
+}
+
+static gchar *
+signal_to_string (GDBusSignalInfo *sig)
+{
+ GString *str = g_string_new (NULL);
+
+ g_string_append (str, sig->name);
+ g_string_append (str, " (");
+
+ for (guint i = 0; sig->args[i] != NULL; i++)
+ {
+ GDBusArgInfo *arg = sig->args[i];
+
+ if (i > 0)
+ g_string_append (str, ", ");
+
+ add_signature (str, arg->signature);
+ g_string_append_c (str, ' ');
+ add_arg_name (str, arg->name);
+ }
+
+ g_string_append_c (str, ')');
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+dspy_path_model_dispose (GObject *object)
+{
+ DspyPathModel *self = (DspyPathModel *)object;
+
+ g_cancellable_cancel (self->cancellable);
+
+ G_OBJECT_CLASS (dspy_path_model_parent_class)->dispose (object);
+}
+
+static void
+dspy_path_model_class_init (DspyPathModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dspy_path_model_dispose;
+ object_class->finalize = dspy_path_model_finalize;
+
+ simple_types = g_hash_table_new (g_str_hash, g_str_equal);
+
+#define INSERT(k,v) \
+ g_hash_table_insert (simple_types, (gchar *)k, (gchar *)v)
+ INSERT ("n", "int16");
+ INSERT ("q", "uint16");
+ INSERT ("i", "int32");
+ INSERT ("u", "uint32");
+ INSERT ("x", "int64");
+ INSERT ("t", "uint64");
+ INSERT ("s", "string");
+ INSERT ("b", "boolean");
+ INSERT ("y", "byte");
+ INSERT ("o", "Object Path");
+ INSERT ("g", "Signature");
+ INSERT ("d", "double");
+ INSERT ("v", "Variant");
+ INSERT ("h", "File Descriptor");
+ INSERT ("as", "string[]");
+ INSERT ("a{sv}", "Vardict");
+ INSERT ("ay", "Byte Array");
+#undef INSERT
+}
+
+static void
+dspy_path_model_init (DspyPathModel *self)
+{
+ GType types[] = { G_TYPE_STRING };
+
+ gtk_tree_store_set_column_types (GTK_TREE_STORE (self), G_N_ELEMENTS (types), types);
+
+ self->cancellable = g_cancellable_new ();
+}
+
+static void
+dspy_path_model_introspect_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusConnection *connection = (GDBusConnection *)object;
+ g_autoptr(GDBusNodeInfo) node_info = NULL;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GTask) task = user_data;
+ DspyPathModel *self;
+ const gchar *path;
+ const gchar *xml;
+
+ g_assert (G_IS_DBUS_CONNECTION (connection));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
+
+ if (!(ret = g_dbus_connection_call_finish (connection, result, &error)))
+ {
+ /* XXX: We might not be authorized, we should propagate that to user */
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ path = g_task_get_task_data (task);
+ xml = NULL;
+
+ g_variant_get (ret, "(&s)", &xml);
+
+ if (!(node_info = g_dbus_node_info_new_for_xml (xml, &error)))
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ /* First, queue up all the paths to load while we keep ourselves
+ * busy filling out this part of the tree.
+ */
+ for (guint i = 0; node_info->nodes[i] != NULL; i++)
+ {
+ g_autofree gchar *subpath = NULL;
+
+ subpath = g_strdup_printf ("%s/%s",
+ g_str_equal (path, "/") ? "" : path,
+ node_info->nodes[i]->path);
+ dspy_path_model_introspect (self, subpath);
+ }
+
+ if (node_info->interfaces[0] != NULL)
+ {
+ GtkTreeIter iter;
+ GtkTreeIter ifaceiter;
+ GtkTreeIter groupiter;
+
+ qsort (node_info->interfaces,
+ g_strv_length ((gchar **)node_info->interfaces),
+ sizeof (gpointer),
+ compare_iface);
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &iter, NULL);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &iter,
+ 0, path,
+ -1);
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &groupiter, &iter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &groupiter,
+ 0, _("<b>Interfaces</b>"),
+ -1);
+
+ for (guint i = 0; node_info->interfaces[i] != NULL; i++)
+ {
+ GDBusInterfaceInfo *iface = node_info->interfaces[i];
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &ifaceiter, &groupiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &ifaceiter,
+ 0, iface->name,
+ -1);
+
+ if (iface->properties[0] != NULL)
+ {
+ GtkTreeIter propsiter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &propsiter, &ifaceiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &propsiter,
+ 0, _("<b>Properties</b>"),
+ -1);
+
+ qsort (iface->properties,
+ g_strv_length ((gchar **)iface->properties),
+ sizeof (gpointer),
+ compare_property);
+
+ for (guint j = 0; iface->properties[j] != NULL; j++)
+ {
+ GDBusPropertyInfo *prop = iface->properties[j];
+ g_autofree gchar *propstr = prop_to_string (prop);
+ GtkTreeIter propiter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &propiter, &propsiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &propiter,
+ 0, propstr,
+ -1);
+ }
+ }
+
+ if (iface->signals[0] != NULL)
+ {
+ GtkTreeIter signalsiter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &signalsiter, &ifaceiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &signalsiter,
+ 0, _("<b>Signals</b>"),
+ -1);
+
+ qsort (iface->signals,
+ g_strv_length ((gchar **)iface->signals),
+ sizeof (gpointer),
+ compare_signal);
+
+ for (guint j = 0; iface->signals[j] != NULL; j++)
+ {
+ GDBusSignalInfo *sig = iface->signals[j];
+ g_autofree gchar *signalstr = signal_to_string (sig);
+ GtkTreeIter signaliter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &signaliter, &signalsiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &signaliter,
+ 0, signalstr,
+ -1);
+ }
+ }
+
+ if (iface->methods[0] != NULL)
+ {
+ GtkTreeIter methodsiter;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &methodsiter, &ifaceiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &methodsiter,
+ 0, _("<b>Methods</b>"),
+ -1);
+
+ qsort (iface->methods,
+ g_strv_length ((gchar **)iface->methods),
+ sizeof (gpointer),
+ compare_method);
+
+ for (guint j = 0; iface->methods[j] != NULL; j++)
+ {
+ GDBusMethodInfo *method = iface->methods[j];
+ g_autofree gchar *methodstr = method_to_string (method);
+ GtkTreeIter methoditer;
+
+ gtk_tree_store_append (GTK_TREE_STORE (self), &methoditer, &methodsiter);
+ gtk_tree_store_set (GTK_TREE_STORE (self), &methoditer,
+ 0, methodstr,
+ -1);
+ }
+ }
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+dspy_path_model_introspect (DspyPathModel *self,
+ const gchar *object_path)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (DSPY_IS_PATH_MODEL (self));
+ g_assert (G_IS_DBUS_CONNECTION (self->connection));
+ g_assert (DSPY_IS_NAME (self->name));
+ g_assert (object_path != NULL);
+
+ g_debug ("Introspecting DBus XML of peer %s path %s",
+ dspy_name_get_owner (self->name),
+ object_path);
+
+ task = g_task_new (self, self->cancellable, NULL, NULL);
+ g_task_set_task_data (task, g_strdup (object_path), g_free);
+
+ g_dbus_connection_call (self->connection,
+ dspy_name_get_owner (self->name),
+ object_path,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect",
+ NULL, /* params */
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ dspy_path_model_introspect_cb,
+ g_steal_pointer (&task));
+}
+
+DspyPathModel *
+dspy_path_model_new (GDBusConnection *connection,
+ DspyName *name)
+{
+ DspyPathModel *self;
+
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (DSPY_IS_NAME (name), NULL);
+
+ self = g_object_new (DSPY_TYPE_PATH_MODEL, NULL);
+ self->connection = g_object_ref (connection);
+ self->name = g_object_ref (name);
+
+ dspy_path_model_introspect (self, "/");
+
+ return g_steal_pointer (&self);
+}
diff --git a/src/plugins/dspy/dspy-path-model.h b/src/plugins/dspy/dspy-path-model.h
new file mode 100644
index 000000000..471d0dc48
--- /dev/null
+++ b/src/plugins/dspy/dspy-path-model.h
@@ -0,0 +1,36 @@
+/* dspy-path-model.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "dspy-name.h"
+
+G_BEGIN_DECLS
+
+#define DSPY_TYPE_PATH_MODEL (dspy_path_model_get_type())
+
+G_DECLARE_FINAL_TYPE (DspyPathModel, dspy_path_model, DSPY, PATH_MODEL, GtkTreeStore)
+
+DspyPathModel *dspy_path_model_new (GDBusConnection *connection,
+ DspyName *name);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/dspy-plugin.c b/src/plugins/dspy/dspy-plugin.c
new file mode 100644
index 000000000..85bd90efc
--- /dev/null
+++ b/src/plugins/dspy/dspy-plugin.c
@@ -0,0 +1,34 @@
+/* dspy-plugin.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <libide-editor.h>
+#include <libpeas/peas.h>
+
+#include "gbp-dspy-workspace-addin.h"
+
+_IDE_EXTERN void
+_gbp_dspy_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_WORKSPACE_ADDIN,
+ GBP_TYPE_DSPY_WORKSPACE_ADDIN);
+}
diff --git a/src/plugins/dspy/dspy.gresource.xml b/src/plugins/dspy/dspy.gresource.xml
new file mode 100644
index 000000000..c2063d1fc
--- /dev/null
+++ b/src/plugins/dspy/dspy.gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/plugins/dspy">
+ <file>dspy.plugin</file>
+ <file>gtk/menus.ui</file>
+ <file preprocess="xml-stripblanks">dspy-connection-row.ui</file>
+ <file preprocess="xml-stripblanks">dspy-name-row.ui</file>
+ <file preprocess="xml-stripblanks">dspy-name-view.ui</file>
+ <file preprocess="xml-stripblanks">gbp-dspy-surface.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/plugins/dspy/dspy.plugin b/src/plugins/dspy/dspy.plugin
new file mode 100644
index 000000000..b98a209a9
--- /dev/null
+++ b/src/plugins/dspy/dspy.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Authors=Christian Hergert <christian hergert me>
+Builtin=true
+Copyright=Copyright © 2019 Christian Hergert
+Description=Explore DBus session and system connections
+Embedded=_gbp_dspy_register_types
+Module=dspy
+Name=DBus Connection Explorer
+X-Workspace-Kind=primary;editor;
diff --git a/src/plugins/dspy/gbp-dspy-surface.c b/src/plugins/dspy/gbp-dspy-surface.c
new file mode 100644
index 000000000..46d63dbc0
--- /dev/null
+++ b/src/plugins/dspy/gbp-dspy-surface.c
@@ -0,0 +1,208 @@
+/* gbp-dspy-surface.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-dspy-surface"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#include "dspy-connection-model.h"
+#include "dspy-connection-row.h"
+#include "dspy-name.h"
+#include "dspy-name-row.h"
+#include "dspy-name-view.h"
+
+#include "gbp-dspy-surface.h"
+
+struct _GbpDspySurface
+{
+ IdeSurface parent_instance;
+
+ GtkListBox *connections_list_box;
+ GtkScrolledWindow *connections_scroller;
+ GtkPaned *paned;
+ GtkScrolledWindow *names_scroller;
+ GtkListBox *names_list_box;
+ GtkStack *view_stack;
+ DspyNameView *name_view;
+
+ DspyConnectionModel *model;
+};
+
+G_DEFINE_TYPE (GbpDspySurface, gbp_dspy_surface, IDE_TYPE_SURFACE)
+
+static GtkWidget *
+create_names_row (gpointer item,
+ gpointer user_data)
+{
+ DspyNameRow *row;
+ DspyName *name = item;
+
+ g_assert (DSPY_IS_NAME (name));
+
+ row = dspy_name_row_new (name);
+ gtk_widget_show (GTK_WIDGET (row));
+
+ return GTK_WIDGET (row);
+}
+
+static void
+name_row_activated_cb (GbpDspySurface *self,
+ DspyNameRow *row,
+ GtkListBox *list_box)
+{
+ g_assert (GBP_IS_DSPY_SURFACE (self));
+ g_assert (DSPY_IS_NAME_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ dspy_name_view_set_name (self->name_view,
+ dspy_connection_model_get_connection (self->model),
+ dspy_connection_model_get_bus_type (self->model),
+ dspy_connection_model_get_address (self->model),
+ dspy_name_row_get_name (row));
+ gtk_stack_set_visible_child_name (self->view_stack, "name");
+}
+
+static void
+connection_row_activated_cb (GbpDspySurface *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ g_autoptr(GDBusConnection) bus = NULL;
+ g_autoptr(DspyConnectionModel) model = NULL;
+ g_autoptr(GError) error = NULL;
+ const gchar *addr = NULL;
+ GBusType bus_type;
+
+ g_assert (GBP_IS_DSPY_SURFACE (self));
+ g_assert (DSPY_IS_CONNECTION_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ if ((bus_type = dspy_connection_row_get_bus_type (DSPY_CONNECTION_ROW (row))))
+ bus = g_bus_get_sync (bus_type, NULL, &error);
+ else if ((addr = dspy_connection_row_get_address (DSPY_CONNECTION_ROW (row))))
+ bus = g_dbus_connection_new_for_address_sync (addr,
+ (G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION |
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT),
+ NULL, NULL, &error);
+ else
+ g_return_if_reached ();
+
+ if (error != NULL)
+ {
+ g_critical ("Failed to connect to bus: %s", error->message);
+ return;
+ }
+
+ model = dspy_connection_model_new ();
+ dspy_connection_model_set_connection (model, bus);
+ dspy_connection_model_set_bus_type (model, bus_type);
+ dspy_connection_model_set_address (model, addr);
+ gtk_list_box_bind_model (self->names_list_box, G_LIST_MODEL (model), create_names_row, NULL, NULL);
+ g_set_object (&self->model, model);
+}
+
+static void
+add_connection (GbpDspySurface *self,
+ const gchar *name,
+ GBusType bus_type,
+ const gchar *addr)
+{
+ DspyConnectionRow *row;
+
+ g_assert (GBP_IS_DSPY_SURFACE (self));
+
+ row = dspy_connection_row_new ();
+ dspy_connection_row_set_title (row, name);
+
+ if (bus_type != G_BUS_TYPE_NONE)
+ dspy_connection_row_set_bus_type (row, bus_type);
+ else if (addr)
+ dspy_connection_row_set_address (row, addr);
+ else
+ g_return_if_reached ();
+
+ gtk_container_add (GTK_CONTAINER (self->connections_list_box), GTK_WIDGET (row));
+
+ gtk_widget_show (GTK_WIDGET (row));
+}
+
+static void
+gbp_dspy_surface_finalize (GObject *object)
+{
+ GbpDspySurface *self = (GbpDspySurface *)object;
+
+ g_clear_object (&self->model);
+
+ G_OBJECT_CLASS (gbp_dspy_surface_parent_class)->finalize (object);
+}
+
+static void
+gbp_dspy_surface_class_init (GbpDspySurfaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gbp_dspy_surface_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/plugins/dspy/gbp-dspy-surface.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, connections_list_box);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, connections_scroller);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, paned);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, names_list_box);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, names_scroller);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, name_view);
+ gtk_widget_class_bind_template_child (widget_class, GbpDspySurface, view_stack);
+
+ g_type_ensure (DSPY_TYPE_NAME_VIEW);
+}
+
+static void
+gbp_dspy_surface_init (GbpDspySurface *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_widget_set_name (GTK_WIDGET (self), "dspy");
+ ide_surface_set_icon_name (IDE_SURFACE (self), "edit-find-symbolic");
+ ide_surface_set_title (IDE_SURFACE (self), _("DBus Inspector"));
+
+ g_signal_connect_object (self->connections_list_box,
+ "row-activated",
+ G_CALLBACK (connection_row_activated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->names_list_box,
+ "row-activated",
+ G_CALLBACK (name_row_activated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ add_connection (self, _("System Bus"), G_BUS_TYPE_SYSTEM, NULL);
+ add_connection (self, _("Session Bus"), G_BUS_TYPE_SESSION, NULL);
+}
+
+GbpDspySurface *
+gbp_dspy_surface_new (void)
+{
+ return g_object_new (GBP_TYPE_DSPY_SURFACE, NULL);
+}
diff --git a/src/plugins/dspy/gbp-dspy-surface.h b/src/plugins/dspy/gbp-dspy-surface.h
new file mode 100644
index 000000000..2ec6a35f7
--- /dev/null
+++ b/src/plugins/dspy/gbp-dspy-surface.h
@@ -0,0 +1,33 @@
+/* gbp-dspy-surface.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DSPY_SURFACE (gbp_dspy_surface_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDspySurface, gbp_dspy_surface, GBP, DSPY_SURFACE, IdeSurface)
+
+GbpDspySurface *gbp_dspy_surface_new (void);
+
+G_END_DECLS
diff --git a/src/plugins/dspy/gbp-dspy-surface.ui b/src/plugins/dspy/gbp-dspy-surface.ui
new file mode 100644
index 000000000..294fefef0
--- /dev/null
+++ b/src/plugins/dspy/gbp-dspy-surface.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpDspySurface" parent="IdeSurface">
+ <child>
+ <object class="GtkPaned" id="paned">
+ <property name="orientation">horizontal</property>
+ <property name="position">300</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkPaned" id="left_paned">
+ <property name="orientation">vertical</property>
+ <property name="position">75</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkScrolledWindow" id="connections_scroller">
+ <property name="propagate-natural-height">true</property>
+ <property name="propagate-natural-width">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="connections_list_box">
+ <property name="selection-mode">browse</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="shrink">false</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="names_scroller">
+ <property name="propagate-natural-height">true</property>
+ <property name="propagate-natural-width">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="names_list_box">
+ <property name="selection-mode">browse</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="shrink">false</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="shrink">false</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStack" id="view_stack">
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="DzlEmptyState" id="empty_state">
+ <property name="icon-name">edit-find-symbolic</property>
+ <property name="title" translatable="yes">Select a Connection</property>
+ <property name="subtitle" translatable="yes">Select a connection and name on the bus to view
details</property>
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="name">empty</property>
+ </packing>
+ </child>
+ <child>
+ <object class="DspyNameView" id="name_view">
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="name">name</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/dspy/gbp-dspy-workspace-addin.c b/src/plugins/dspy/gbp-dspy-workspace-addin.c
new file mode 100644
index 000000000..4ddc43e18
--- /dev/null
+++ b/src/plugins/dspy/gbp-dspy-workspace-addin.c
@@ -0,0 +1,92 @@
+/* gbp-dspy-workspace-addin.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-dspy-workspace-addin"
+
+#include <libide-editor.h>
+
+#include "dspy-connection-model.h"
+
+#include "gbp-dspy-surface.h"
+#include "gbp-dspy-workspace-addin.h"
+
+struct _GbpDspyWorkspaceAddin
+{
+ GObject parent_instance;
+ DspyConnectionModel *model;
+ GbpDspySurface *surface;
+};
+
+static void
+gbp_dspy_workspace_addin_load (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpDspyWorkspaceAddin *self = (GbpDspyWorkspaceAddin *)addin;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_DSPY_WORKSPACE_ADDIN (addin));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace) || IDE_IS_EDITOR_WORKSPACE (workspace));
+
+ self->surface = gbp_dspy_surface_new ();
+ g_signal_connect (self->surface,
+ "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &self->surface);
+ ide_workspace_add_surface (workspace, IDE_SURFACE (self->surface));
+ gtk_widget_show (GTK_WIDGET (self->surface));
+
+ //self->model = dspy_connection_model_new ();
+ //dspy_connection_model_set_connection (self->model, g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL));
+}
+
+static void
+gbp_dspy_workspace_addin_unload (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpDspyWorkspaceAddin *self = (GbpDspyWorkspaceAddin *)addin;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_DSPY_WORKSPACE_ADDIN (addin));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace) || IDE_IS_EDITOR_WORKSPACE (workspace));
+
+ if (self->surface != NULL)
+ gtk_widget_destroy (GTK_WIDGET (self->surface));
+
+}
+
+static void
+workspace_addin_iface_init (IdeWorkspaceAddinInterface *iface)
+{
+ iface->load = gbp_dspy_workspace_addin_load;
+ iface->unload = gbp_dspy_workspace_addin_unload;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpDspyWorkspaceAddin, gbp_dspy_workspace_addin, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKSPACE_ADDIN, workspace_addin_iface_init))
+
+static void
+gbp_dspy_workspace_addin_class_init (GbpDspyWorkspaceAddinClass *klass)
+{
+}
+
+static void
+gbp_dspy_workspace_addin_init (GbpDspyWorkspaceAddin *self)
+{
+}
diff --git a/src/plugins/dspy/gbp-dspy-workspace-addin.h b/src/plugins/dspy/gbp-dspy-workspace-addin.h
new file mode 100644
index 000000000..e4cfec50e
--- /dev/null
+++ b/src/plugins/dspy/gbp-dspy-workspace-addin.h
@@ -0,0 +1,31 @@
+/* gbp-dspy-workspace-addin.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DSPY_WORKSPACE_ADDIN (gbp_dspy_workspace_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDspyWorkspaceAddin, gbp_dspy_workspace_addin, GBP, DSPY_WORKSPACE_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/dspy/gtk/menus.ui b/src/plugins/dspy/gtk/menus.ui
new file mode 100644
index 000000000..03ae767e4
--- /dev/null
+++ b/src/plugins/dspy/gtk/menus.ui
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <menu id="ide-primary-workspace-surfaces-menu">
+ <section id="ide-primary-workspace-surfaces-menu-section">
+ <item>
+ <attribute name="id">surface-menu-dspy</attribute>
+ <attribute name="label" translatable="yes">DBus Inspector</attribute>
+ <attribute name="role">normal</attribute>
+ <attribute name="action">win.surface</attribute>
+ <attribute name="target">dspy</attribute>
+ <attribute name="verb-icon-name">edit-find-symbolic</attribute>
+ </item>
+ </section>
+ </menu>
+</interface>
diff --git a/src/plugins/dspy/meson.build b/src/plugins/dspy/meson.build
new file mode 100644
index 000000000..8b84d5638
--- /dev/null
+++ b/src/plugins/dspy/meson.build
@@ -0,0 +1,23 @@
+if get_option('plugin_dspy')
+
+plugins_sources += files([
+ 'dspy-plugin.c',
+ 'dspy-connection-model.c',
+ 'dspy-connection-row.c',
+ 'dspy-name.c',
+ 'dspy-name-row.c',
+ 'dspy-name-view.c',
+ 'dspy-path-model.c',
+ 'gbp-dspy-surface.c',
+ 'gbp-dspy-workspace-addin.c',
+])
+
+plugin_dspy_resources = gnome.compile_resources(
+ 'dspy-resources',
+ 'dspy.gresource.xml',
+ c_name: 'gbp_dspy',
+)
+
+plugins_sources += plugin_dspy_resources
+
+endif
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 0c5fd6aa4..4feeb4a41 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -56,6 +56,7 @@ subdir('devhelp')
subdir('deviceui')
subdir('deviced')
subdir('doap')
+subdir('dspy')
subdir('editor')
subdir('editorconfig')
subdir('emacs')
@@ -143,6 +144,7 @@ status += [
'CTags ................. : @0@'.format(get_option('plugin_ctags')),
'Devhelp ............... : @0@'.format(get_option('plugin_devhelp')),
'Deviced ............... : @0@'.format(get_option('plugin_deviced')),
+ 'DBus Spy .............. : @0@'.format(get_option('plugin_dspy')),
'Editorconfig .......... : @0@'.format(get_option('plugin_editorconfig')),
'ESLint ................ : @0@'.format(get_option('plugin_eslint')),
'File Search ........... : @0@'.format(get_option('plugin_file_search')),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]