/*
* 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"
#include "gtkcolorswatchprivate.h"
#include "gtkbox.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)
{
case PROP_LOADING:
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" , NULL, NULL,
FALSE ,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);
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 if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_PIXBUF))
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
self->contents = gtk_picture_new_for_pixbuf (g_value_get_object (value));
G_GNUC_END_IGNORE_DEPRECATIONS
gtk_widget_set_size_request (self->contents, 256 , 256 );
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_RGBA))
{
const GdkRGBA *color = g_value_get_boxed (value);
self->contents = gtk_color_swatch_new ();
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (self->contents), color);
gtk_widget_set_size_request (self->contents, 48 , 32 );
gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), G_TYPE_FILE))
{
GFile *file = g_value_get_object (value);
self->contents = gtk_label_new (g_file_peek_path (file));
gtk_label_set_ellipsize (GTK_LABEL (self->contents), PANGO_ELLIPSIZE_START);
gtk_widget_set_halign (self->contents, GTK_ALIGN_CENTER);
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
}
else if (g_type_is_a (G_VALUE_TYPE (value), GDK_TYPE_FILE_LIST))
{
GList *l;
self->contents = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10 );
gtk_widget_set_parent (self->contents, GTK_WIDGET (self));
for (l = g_value_get_boxed (value); l; l = l->next)
{
GFile *file = l->data;
GtkWidget *label = gtk_label_new (g_file_peek_path (file));
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_START);
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (self->contents), label);
}
}
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]);
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland