[gnome-builder/wip/chergert/dspy] dspy: start on dspy plugin
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/dspy] dspy: start on dspy plugin
- Date: Fri, 12 Apr 2019 03:59:12 +0000 (UTC)
commit e6c48b1608b1a0864e965212781e3213b20cacb4
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 | 120 +++++++
src/plugins/dspy/dspy-name-view.h | 40 +++
src/plugins/dspy/dspy-name-view.ui | 128 +++++++
src/plugins/dspy/dspy-name.c | 289 +++++++++++++++
src/plugins/dspy/dspy-name.h | 49 +++
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 | 22 ++
src/plugins/meson.build | 2 +
25 files changed, 2096 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..f31f0f943
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-view.c
@@ -0,0 +1,120 @@
+/* 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"
+
+struct _DspyNameView
+{
+ GtkBin parent;
+
+ GDBusConnection *connection;
+ DspyName *name;
+
+ GtkLabel *address_label;
+ GtkLabel *name_label;
+ GtkLabel *unique_label;
+} DspyNameViewPrivate;
+
+G_DEFINE_TYPE (DspyNameView, dspy_name_view, GTK_TYPE_BIN)
+
+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, unique_label);
+
+ g_type_ensure (DZL_TYPE_THREE_GRID);
+}
+
+static void
+dspy_name_view_init (DspyNameView *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+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);
+}
+
+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_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));
+}
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..37ace2705
--- /dev/null
+++ b/src/plugins/dspy/dspy-name-view.ui
@@ -0,0 +1,128 @@
+<?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">18</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1.0</property>
+ <property name="label" translatable="yes">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="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">view-refresh-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="column">right</property>
+ <property name="row">0</property>
+ </packing>
+ </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-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..b3e937d9f
--- /dev/null
+++ b/src/plugins/dspy/meson.build
@@ -0,0 +1,22 @@
+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',
+ '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]