diff options
-rw-r--r-- | gtk/inspector/clipboard.c | 316 | ||||
-rw-r--r-- | gtk/inspector/clipboard.h | 38 | ||||
-rw-r--r-- | gtk/inspector/clipboard.ui | 98 | ||||
-rw-r--r-- | gtk/inspector/gtkdataviewer.c | 368 | ||||
-rw-r--r-- | gtk/inspector/gtkdataviewer.h | 47 | ||||
-rw-r--r-- | gtk/inspector/init.c | 2 | ||||
-rw-r--r-- | gtk/inspector/meson.build | 2 | ||||
-rw-r--r-- | gtk/inspector/window.c | 3 | ||||
-rw-r--r-- | gtk/inspector/window.h | 1 | ||||
-rw-r--r-- | gtk/inspector/window.ui | 18 |
10 files changed, 893 insertions, 0 deletions
diff --git a/gtk/inspector/clipboard.c b/gtk/inspector/clipboard.c new file mode 100644 index 0000000000..5c7ae7b163 --- /dev/null +++ b/gtk/inspector/clipboard.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include "clipboard.h" +#include "gtkdataviewer.h" +#include "window.h" + +#include "gtkbinlayout.h" +#include "gtkbox.h" +#include "gtkdebug.h" +#include "gtklabel.h" +#include "gtklistbox.h" +#include "gtktogglebutton.h" + +struct _GtkInspectorClipboard +{ + GtkWidget parent; + + GdkDisplay *display; + + GtkWidget *swin; + + GtkWidget *clipboard_formats; + GtkWidget *clipboard_info; + + GtkWidget *primary_formats; + GtkWidget *primary_info; +}; + +typedef struct _GtkInspectorClipboardClass +{ + GtkWidgetClass parent_class; +} GtkInspectorClipboardClass; + +G_DEFINE_TYPE (GtkInspectorClipboard, gtk_inspector_clipboard, GTK_TYPE_WIDGET) + +static void +load_gtype_value (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GdkClipboard *clipboard = GDK_CLIPBOARD (source); + GtkDataViewer *viewer = data; + const GValue *value; + GError *error = NULL; + + value = gdk_clipboard_read_value_finish (clipboard, res, &error); + if (value == NULL) + gtk_data_viewer_load_error (viewer, error); + else + gtk_data_viewer_load_value (viewer, value); + + g_object_unref (viewer); +} + +static gboolean +load_gtype (GtkDataViewer *viewer, + GCancellable *cancellable, + gpointer gtype) +{ + GdkClipboard *clipboard = g_object_get_data (G_OBJECT (viewer), "clipboard"); + + gdk_clipboard_read_value_async (clipboard, + GPOINTER_TO_SIZE (gtype), + G_PRIORITY_DEFAULT, + cancellable, + load_gtype_value, + g_object_ref (viewer)); + + return TRUE; +} + +static void +load_mime_type_stream (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GdkClipboard *clipboard = GDK_CLIPBOARD (source); + GtkDataViewer *viewer = data; + GInputStream *stream; + GError *error = NULL; + const char *mime_type; + + stream = gdk_clipboard_read_finish (clipboard, res, &mime_type, &error); + if (stream == NULL) + gtk_data_viewer_load_error (viewer, error); + else + gtk_data_viewer_load_stream (viewer, stream, mime_type); + + g_object_unref (viewer); +} + +static gboolean +load_mime_type (GtkDataViewer *viewer, + GCancellable *cancellable, + gpointer mime_type) +{ + GdkClipboard *clipboard = g_object_get_data (G_OBJECT (viewer), "clipboard"); + + gdk_clipboard_read_async (clipboard, + (const char *[2]) { mime_type, NULL }, + G_PRIORITY_DEFAULT, + cancellable, + load_mime_type_stream, + g_object_ref (viewer)); + + return TRUE; +} + +static void +add_content_type_row (GtkInspectorClipboard *self, + GtkListBox *list, + const char *type_name, + GdkClipboard *clipboard, + GCallback load_func, + gpointer load_func_data) +{ + GtkWidget *row, *vbox, *hbox, *label, *viewer, *button; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40); + gtk_box_append (GTK_BOX (vbox), hbox); + + label = gtk_label_new (type_name); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_BASELINE); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_widget_set_hexpand (label, TRUE); + gtk_box_append (GTK_BOX (hbox), label); + + button = gtk_toggle_button_new_with_label (_("Show")); + gtk_widget_set_halign (button, GTK_ALIGN_END); + gtk_widget_set_valign (button, GTK_ALIGN_BASELINE); + gtk_box_append (GTK_BOX (hbox), button); + + viewer = gtk_data_viewer_new (); + g_signal_connect (viewer, "load", load_func, load_func_data); + g_object_set_data (G_OBJECT (viewer), "clipboard", clipboard); + g_object_bind_property (G_OBJECT (button), "active", + G_OBJECT (viewer), "visible", + G_BINDING_SYNC_CREATE); + gtk_box_append (GTK_BOX (vbox), viewer); + + row = gtk_list_box_row_new (); + gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), vbox); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE); + + gtk_list_box_insert (list, row, -1); +} + +static void +init_formats (GtkInspectorClipboard *self, + GtkListBox *list, + GdkClipboard *clipboard) +{ + GtkListBoxRow *row; + GdkContentFormats *formats; + const char * const *mime_types; + const GType *gtypes; + gsize i, n; + + while ((row = gtk_list_box_get_row_at_index (list, 1))) + gtk_list_box_remove (list, GTK_WIDGET (row)); + + formats = gdk_clipboard_get_formats (clipboard); + + gtypes = gdk_content_formats_get_gtypes (formats, &n); + for (i = 0; i < n; i++) + add_content_type_row (self, list, g_type_name (gtypes[i]), clipboard, G_CALLBACK (load_gtype), GSIZE_TO_POINTER (gtypes[i])); + + mime_types = gdk_content_formats_get_mime_types (formats, &n); + for (i = 0; i < n; i++) + add_content_type_row (self, list, mime_types[i], clipboard, G_CALLBACK (load_mime_type), (gpointer) mime_types[i]); +} + +static void +init_info (GtkInspectorClipboard *self, + GtkLabel *label, + GdkClipboard *clipboard) +{ + GdkContentFormats *formats; + + formats = gdk_clipboard_get_formats (clipboard); + if (gdk_content_formats_get_gtypes (formats, NULL) == NULL && + gdk_content_formats_get_mime_types (formats, NULL) == NULL) + { + gtk_label_set_text (label, C_("clipboard", "empty")); + return; + } + + if (gdk_clipboard_is_local (clipboard)) + gtk_label_set_text (label, C_("clipboard", "local")); + else + gtk_label_set_text (label, C_("clipboard", "remote")); +} + +static void +clipboard_notify (GdkClipboard *clipboard, + GParamSpec *pspec, + GtkInspectorClipboard *self) +{ + if (g_str_equal (pspec->name, "formats")) + { + init_formats (self, GTK_LIST_BOX (self->clipboard_formats), clipboard); + } + + init_info (self, GTK_LABEL (self->clipboard_info), clipboard); +} + +static void +primary_notify (GdkClipboard *clipboard, + GParamSpec *pspec, + GtkInspectorClipboard *self) +{ + if (g_str_equal (pspec->name, "formats")) + { + init_formats (self, GTK_LIST_BOX (self->primary_formats), clipboard); + } + + g_print ("%s: %s\n", pspec->name, gdk_content_formats_to_string (gdk_clipboard_get_formats (clipboard))); + init_info (self, GTK_LABEL (self->primary_info), clipboard); +} + +static void +gtk_inspector_clipboard_unset_display (GtkInspectorClipboard *self) +{ + GdkClipboard *clipboard; + + if (self->display == NULL) + return; + + clipboard = gdk_display_get_clipboard (self->display); + g_signal_handlers_disconnect_by_func (clipboard, clipboard_notify, self); + + clipboard = gdk_display_get_primary_clipboard (self->display); + g_signal_handlers_disconnect_by_func (clipboard, primary_notify, self); +} + +static void +gtk_inspector_clipboard_init (GtkInspectorClipboard *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +gtk_inspector_clipboard_dispose (GObject *object) +{ + GtkInspectorClipboard *self = GTK_INSPECTOR_CLIPBOARD (object); + + gtk_inspector_clipboard_unset_display (self); + + g_clear_pointer (&self->swin, gtk_widget_unparent); + + G_OBJECT_CLASS (gtk_inspector_clipboard_parent_class)->dispose (object); +} + +static void +gtk_inspector_clipboard_class_init (GtkInspectorClipboardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gtk_inspector_clipboard_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/clipboard.ui"); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, swin); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, clipboard_formats); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, clipboard_info); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, primary_formats); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorClipboard, primary_info); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); +} + +void +gtk_inspector_clipboard_set_display (GtkInspectorClipboard *self, + GdkDisplay *display) +{ + GdkClipboard *clipboard; + + gtk_inspector_clipboard_unset_display (self); + + self->display = display; + + if (display == NULL) + return; + + clipboard = gdk_display_get_clipboard (display); + g_signal_connect (clipboard, "notify", G_CALLBACK (clipboard_notify), self); + init_formats (self, GTK_LIST_BOX (self->clipboard_formats), clipboard); + init_info (self, GTK_LABEL (self->clipboard_info), clipboard); + + clipboard = gdk_display_get_primary_clipboard (display); + g_signal_connect (clipboard, "notify", G_CALLBACK (primary_notify), self); + init_formats (self, GTK_LIST_BOX (self->primary_formats), clipboard); + init_info (self, GTK_LABEL (self->primary_info), clipboard); +} + diff --git a/gtk/inspector/clipboard.h b/gtk/inspector/clipboard.h new file mode 100644 index 0000000000..6bc7ee9619 --- /dev/null +++ b/gtk/inspector/clipboard.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _GTK_INSPECTOR_CLIPBOARD_H_ +#define _GTK_INSPECTOR_CLIPBOARD_H_ + +#include <gtk/gtkwidget.h> + +#define GTK_TYPE_INSPECTOR_CLIPBOARD (gtk_inspector_clipboard_get_type()) +#define GTK_INSPECTOR_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_INSPECTOR_CLIPBOARD, GtkInspectorClipboard)) +#define GTK_INSPECTOR_IS_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_INSPECTOR_CLIPBOARD)) + +typedef struct _GtkInspectorClipboard GtkInspectorClipboard; + +G_BEGIN_DECLS + +GType gtk_inspector_clipboard_get_type (void); + +void gtk_inspector_clipboard_set_display (GtkInspectorClipboard *self, + GdkDisplay *display); + +G_END_DECLS + +#endif // _GTK_INSPECTOR_CLIPBOARD_H_ diff --git a/gtk/inspector/clipboard.ui b/gtk/inspector/clipboard.ui new file mode 100644 index 0000000000..ef83f037e9 --- /dev/null +++ b/gtk/inspector/clipboard.ui @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface domain="gtk40"> + <template class="GtkInspectorClipboard" parent="GtkWidget"> + <child> + <object class="GtkScrolledWindow" id="swin"> + <property name="hscrollbar-policy">never</property> + <child> + <object class="GtkBox" id="box"> + <property name="orientation">vertical</property> + <property name="margin-start">60</property> + <property name="margin-end">60</property> + <property name="margin-top">60</property> + <property name="margin-bottom">60</property> + <property name="spacing">10</property> + <child> + <object class="GtkFrame"> + <child> + <object class="GtkListBox" id="clipboard_formats"> + <property name="selection-mode">none</property> + <style> + <class name="rich-list"/> + </style> + <child> + <object class="GtkListBoxRow"> + <property name="activatable">0</property> + <child> + <object class="GtkBox"> + <property name="spacing">40</property> + <child> + <object class="GtkLabel"> + <property name="label" translatable="yes">Clipboard</property> + <property name="halign">start</property> + <property name="valign">baseline</property> + <property name="xalign">0.0</property> + </object> + </child> + <child> + <object class="GtkLabel" id="clipboard_info"> + <property name="selectable">1</property> + <property name="halign">end</property> + <property name="valign">baseline</property> + <property name="ellipsize">end</property> + <property name="hexpand">1</property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkFrame"> + <child> + <object class="GtkListBox" id="primary_formats"> + <property name="selection-mode">none</property> + <style> + <class name="rich-list"/> + </style> + <child> + <object class="GtkListBoxRow"> + <property name="activatable">0</property> + <child> + <object class="GtkBox"> + <property name="spacing">40</property> + <child> + <object class="GtkLabel"> + <property name="label" translatable="yes">Primary</property> + <property name="halign">start</property> + <property name="valign">baseline</property> + <property name="xalign">0.0</property> + </object> + </child> + <child> + <object class="GtkLabel" id="primary_info"> + <property name="selectable">1</property> + <property name="halign">end</property> + <property name="valign">baseline</property> + <property name="ellipsize">end</property> + <property name="hexpand">1</property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </template> +</interface> diff --git a/gtk/inspector/gtkdataviewer.c b/gtk/inspector/gtkdataviewer.c new file mode 100644 index 0000000000..acfafea88b --- /dev/null +++ b/gtk/inspector/gtkdataviewer.c @@ -0,0 +1,368 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#include "config.h" + +#include "gtkdataviewer.h" + +#include "gtkbinlayout.h" +#include "gtklabel.h" +#include "gtkpicture.h" + +struct _GtkDataViewer +{ + GtkWidget parent_instance; + + GtkWidget *contents; + GCancellable *cancellable; + GError *error; + + enum { + NOT_LOADED = 0, + LOADING_DONE, + LOADING_EXTERNALLY, + LOADING_INTERNALLY, + LOADING_FAILED + } loading; +}; + +enum +{ + PROP_0, + PROP_LOADING, + + N_PROPS +}; + +enum { + LOAD, + LAST_SIGNAL +}; + +G_DEFINE_TYPE (GtkDataViewer, gtk_data_viewer, GTK_TYPE_WIDGET) + +static GParamSpec *properties[N_PROPS] = { NULL, }; +static guint signals[LAST_SIGNAL]; + +static void +gtk_data_viewer_ensure_loaded (GtkDataViewer *self) +{ + gboolean started_loading; + + if (self->loading != NOT_LOADED) + return; + + self->loading = LOADING_EXTERNALLY; + self->cancellable = g_cancellable_new (); + g_signal_emit (self, signals[LOAD], 0, self->cancellable, &started_loading); + + if (!started_loading) + { + self->loading = LOADING_FAILED; /* avoid notify::is_loading */ + gtk_data_viewer_load_error (self, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Nothing to load")); + } + + g_assert (self->loading != NOT_LOADED); + + if (gtk_data_viewer_is_loading (self)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); +} + +static void +gtk_data_viewer_realize (GtkWidget *widget) +{ + GtkDataViewer *self = GTK_DATA_VIEWER (widget); + + GTK_WIDGET_CLASS (gtk_data_viewer_parent_class)->realize (widget); + + gtk_data_viewer_ensure_loaded (self); +} + +static void +gtk_data_viewer_unrealize (GtkWidget *widget) +{ + GtkDataViewer *self = GTK_DATA_VIEWER (widget); + + GTK_WIDGET_CLASS (gtk_data_viewer_parent_class)->unrealize (widget); + + gtk_data_viewer_reset (self); +} + +static void +gtk_data_viewer_dispose (GObject *object) +{ + //GtkDataViewer *self = GTK_DATA_VIEWER (object); + + G_OBJECT_CLASS (gtk_data_viewer_parent_class)->dispose (object); +} + +static void +gtk_data_viewer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GtkDataViewer *self = GTK_DATA_VIEWER (object); + + switch (property_id) + { + case PROP_LOADING: + g_value_set_boolean (value, gtk_data_viewer_is_loading (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gtk_data_viewer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + //GtkDataViewer *self = GTK_DATA_VIEWER (object); + + switch (property_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gtk_data_viewer_class_init (GtkDataViewerClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + widget_class->realize = gtk_data_viewer_realize; + widget_class->unrealize = gtk_data_viewer_unrealize; + + gobject_class->dispose = gtk_data_viewer_dispose; + gobject_class->get_property = gtk_data_viewer_get_property; + gobject_class->set_property = gtk_data_viewer_set_property; + + properties[PROP_LOADING] = + g_param_spec_boolean ("loading", + "Loading", + "If the widget is currently loading the data to display", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, N_PROPS, properties); + + signals[LOAD] = + g_signal_new ("load", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_first_wins, NULL, + NULL, + G_TYPE_BOOLEAN, 1, + G_TYPE_CANCELLABLE); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_css_name (widget_class, "frame"); +} + +static void +gtk_data_viewer_init (GtkDataViewer *self) +{ +} + +GtkWidget * +gtk_data_viewer_new (void) +{ + return g_object_new (GTK_TYPE_DATA_VIEWER, NULL); +} + +gboolean +gtk_data_viewer_is_loading (GtkDataViewer *self) +{ + g_return_val_if_fail (GTK_IS_DATA_VIEWER (self), FALSE); + + return self->loading == LOADING_EXTERNALLY || + self->loading == LOADING_INTERNALLY; +} + +void +gtk_data_viewer_reset (GtkDataViewer *self) +{ + gboolean was_loading; + + g_return_if_fail (GTK_IS_DATA_VIEWER (self)); + + g_object_freeze_notify (G_OBJECT (self)); + + was_loading = gtk_data_viewer_is_loading (self); + + g_clear_pointer (&self->contents, gtk_widget_unparent); + g_clear_error (&self->error); + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + self->loading = NOT_LOADED; + + if (gtk_widget_get_realized (GTK_WIDGET (self))) + gtk_data_viewer_ensure_loaded (self); + + if (was_loading != gtk_data_viewer_is_loading (self)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); + + g_object_thaw_notify (G_OBJECT (self)); +} + +void +gtk_data_viewer_load_value (GtkDataViewer *self, + const GValue *value) +{ + gboolean was_loading; + + g_return_if_fail (GTK_IS_DATA_VIEWER (self)); + + was_loading = gtk_data_viewer_is_loading (self); + self->loading = LOADING_DONE; + + g_clear_pointer (&self->contents, gtk_widget_unparent); + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_STRING)) + { + self->contents = gtk_label_new (g_value_get_string (value)); + gtk_label_set_wrap (GTK_LABEL (self->contents), TRUE); + gtk_widget_set_parent (self->contents, GTK_WIDGET (self)); + } + else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_PAINTABLE)) + { + self->contents = gtk_picture_new_for_paintable (g_value_get_object (value)); + gtk_widget_set_size_request (self->contents, 256, 256); + gtk_widget_set_parent (self->contents, GTK_WIDGET (self)); + } + else + { + gtk_data_viewer_load_error (self, + g_error_new (G_IO_ERROR, + G_IO_ERROR_FAILED, + "Cannot display objects of type \"%s\"", G_VALUE_TYPE_NAME (value))); + } + + if (was_loading) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); +} + +static void +gtk_data_viewer_load_stream_done (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GtkDataViewer *self = data; + GError *error = NULL; + GValue value = G_VALUE_INIT; + + if (!gdk_content_deserialize_finish (res, &value, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + gtk_data_viewer_load_error (self, error); + else + g_clear_error (&error); + + g_object_unref (self); + return; + } + + gtk_data_viewer_load_value (self, &value); + g_object_unref (self); + g_value_unset (&value); +} + +void +gtk_data_viewer_load_stream (GtkDataViewer *self, + GInputStream *stream, + const char *mime_type) +{ + GdkContentFormats *formats; + const GType *gtypes; + gboolean was_loading; + + g_return_if_fail (GTK_IS_DATA_VIEWER (self)); + g_return_if_fail (G_IS_INPUT_STREAM (stream)); + g_return_if_fail (mime_type != NULL); + + was_loading = gtk_data_viewer_is_loading (self); + self->loading = LOADING_INTERNALLY; + if (self->cancellable == NULL) + self->cancellable = g_cancellable_new (); + + formats = gdk_content_formats_new (&mime_type, 1); + formats = gdk_content_formats_union_deserialize_gtypes (formats); + gtypes = gdk_content_formats_get_gtypes (formats, NULL); + if (gtypes) + { + gdk_content_deserialize_async (stream, + mime_type, + gtypes[0], + G_PRIORITY_DEFAULT, + self->cancellable, + gtk_data_viewer_load_stream_done, + g_object_ref (self)); + + if (!was_loading) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); + } + else + { + gtk_data_viewer_load_error (self, + g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, + "Cannot display data of type \"%s\"", mime_type)); + } + + gdk_content_formats_unref (formats); +} + +void +gtk_data_viewer_load_error (GtkDataViewer *self, + GError *error) +{ + gboolean was_loading; + + g_return_if_fail (GTK_IS_DATA_VIEWER (self)); + + was_loading = gtk_data_viewer_is_loading (self); + self->loading = LOADING_FAILED; + + g_clear_pointer (&self->contents, gtk_widget_unparent); + g_clear_error (&self->error); + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); + + self->error = error; + self->contents = gtk_label_new (error->message); + gtk_widget_add_css_class (self->contents, "error"); + gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER); + gtk_widget_set_valign (self->contents, GTK_ALIGN_CENTER); + gtk_widget_set_parent (self->contents, GTK_WIDGET (self)); + + if (was_loading) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); +} + diff --git a/gtk/inspector/gtkdataviewer.h b/gtk/inspector/gtkdataviewer.h new file mode 100644 index 0000000000..37ce4bc750 --- /dev/null +++ b/gtk/inspector/gtkdataviewer.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2021 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Benjamin Otte <otte@gnome.org> + */ + +#ifndef __GTK_DATA_VIEWER_H__ +#define __GTK_DATA_VIEWER_H__ + +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_DATA_VIEWER (gtk_data_viewer_get_type ()) + +G_DECLARE_FINAL_TYPE (GtkDataViewer, gtk_data_viewer, GTK, DATA_VIEWER, GtkWidget) + +GtkWidget * gtk_data_viewer_new (void); + +gboolean gtk_data_viewer_is_loading (GtkDataViewer *self); + +void gtk_data_viewer_reset (GtkDataViewer *self); + +void gtk_data_viewer_load_value (GtkDataViewer *self, + const GValue *value); +void gtk_data_viewer_load_stream (GtkDataViewer *self, + GInputStream *stream, + const char *mime_type); +void gtk_data_viewer_load_error (GtkDataViewer *self, + GError *error); + +G_END_DECLS + +#endif /* __GTK_DATA_VIEWER_H__ */ diff --git a/gtk/inspector/init.c b/gtk/inspector/init.c index ed489baee3..0e0691a16d 100644 --- a/gtk/inspector/init.c +++ b/gtk/inspector/init.c @@ -27,6 +27,7 @@ #include "a11y.h" #include "actions.h" #include "cellrenderergraph.h" +#include "clipboard.h" #include "controllers.h" #include "css-editor.h" #include "css-node-tree.h" @@ -64,6 +65,7 @@ gtk_inspector_init (void) g_type_ensure (GTK_TYPE_GRAPH_DATA); g_type_ensure (GTK_TYPE_INSPECTOR_A11Y); g_type_ensure (GTK_TYPE_INSPECTOR_ACTIONS); + g_type_ensure (GTK_TYPE_INSPECTOR_CLIPBOARD); g_type_ensure (GTK_TYPE_INSPECTOR_CONTROLLERS); g_type_ensure (GTK_TYPE_INSPECTOR_CSS_EDITOR); g_type_ensure (GTK_TYPE_INSPECTOR_CSS_NODE_TREE); diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 0ae9927011..422eab7f24 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -5,6 +5,7 @@ inspector_sources = files( 'actions.c', 'baselineoverlay.c', 'cellrenderergraph.c', + 'clipboard.c', 'controllers.c', 'css-editor.c', 'css-node-tree.c', @@ -13,6 +14,7 @@ inspector_sources = files( 'general.c', 'graphdata.c', 'gtktreemodelcssnode.c', + 'gtkdataviewer.c', 'highlightoverlay.c', 'init.c', 'inspect-button.c', diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 02b108d882..c2f6c432d0 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -30,6 +30,7 @@ #include "init.h" #include "window.h" #include "prop-list.h" +#include "clipboard.h" #include "controllers.h" #include "css-editor.h" #include "css-node-tree.h" @@ -291,6 +292,7 @@ gtk_inspector_window_constructed (GObject *object) gtk_inspector_css_editor_set_display (GTK_INSPECTOR_CSS_EDITOR (iw->css_editor), iw->inspected_display); gtk_inspector_visual_set_display (GTK_INSPECTOR_VISUAL (iw->visual), iw->inspected_display); gtk_inspector_general_set_display (GTK_INSPECTOR_GENERAL (iw->general), iw->inspected_display); + gtk_inspector_clipboard_set_display (GTK_INSPECTOR_CLIPBOARD (iw->clipboard), iw->inspected_display); gtk_inspector_logs_set_display (GTK_INSPECTOR_LOGS (iw->logs), iw->inspected_display); gtk_inspector_css_node_tree_set_display (GTK_INSPECTOR_CSS_NODE_TREE (iw->widget_css_node_tree), iw->inspected_display); } @@ -645,6 +647,7 @@ gtk_inspector_window_class_init (GtkInspectorWindowClass *klass) gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, css_editor); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, visual); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, general); + gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, clipboard); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, logs); gtk_widget_class_bind_template_child (widget_class, GtkInspectorWindow, go_up_button); diff --git a/gtk/inspector/window.h b/gtk/inspector/window.h index 90841a8ae4..efaec419a5 100644 --- a/gtk/inspector/window.h +++ b/gtk/inspector/window.h @@ -76,6 +76,7 @@ typedef struct GtkWidget *sidebar_revealer; GtkWidget *css_editor; GtkWidget *visual; + GtkWidget *clipboard; GtkWidget *general; GtkWidget *logs; diff --git a/gtk/inspector/window.ui b/gtk/inspector/window.ui index c76af022c6..7ecb543841 100644 --- a/gtk/inspector/window.ui +++ b/gtk/inspector/window.ui @@ -132,6 +132,14 @@ </child> <child> <object class="GtkStackPage"> + <property name="name">clipboard</property> + <property name="child"> + <object class="GtkBox"/> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> <property name="name">statistics</property> <property name="child"> <object class="GtkToggleButton" id="record_statistics_button"> @@ -589,6 +597,16 @@ </child> <child> <object class="GtkStackPage"> + <property name="name">clipboard</property> + <property name="title" translatable="yes">Clipboard</property> + <property name="child"> + <object class="GtkInspectorClipboard" id="clipboard"> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> <property name="name">statistics</property> <property name="title" translatable="yes">Statistics</property> <property name="child"> |