diff options
Diffstat (limited to 'subprojects/libgd/libgd/gd-main-icon-box-icon.c')
-rw-r--r-- | subprojects/libgd/libgd/gd-main-icon-box-icon.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/subprojects/libgd/libgd/gd-main-icon-box-icon.c b/subprojects/libgd/libgd/gd-main-icon-box-icon.c new file mode 100644 index 0000000..e05efba --- /dev/null +++ b/subprojects/libgd/libgd/gd-main-icon-box-icon.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2017 Red Hat, Inc. + * + * This program 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 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Debarshi Ray <debarshir@gnome.org> + * + */ + +#include "gd-main-icon-box-icon.h" + +#include <cairo.h> +#include <glib.h> + +struct _GdMainIconBoxIcon +{ + GtkDrawingArea parent_instance; + GdMainBoxItem *item; + cairo_surface_t *surface_zoomed; + gdouble x; + gdouble y; +}; + +enum +{ + PROP_ITEM = 1, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (GdMainIconBoxIcon, gd_main_icon_box_icon, GTK_TYPE_DRAWING_AREA) + +static cairo_surface_t * +gd_zoom_image_surface (cairo_surface_t *surface, gint width_zoomed, gint height_zoomed) +{ + cairo_t *cr; + cairo_format_t format; + cairo_pattern_t *pattern; + cairo_surface_t *zoomed = NULL; + cairo_surface_type_t surface_type; + gdouble scale_x; + gdouble scale_y; + gdouble zoom_x; + gdouble zoom_y; + gint height; + gint width; + + g_return_val_if_fail (surface != NULL, NULL); + + surface_type = cairo_surface_get_type (surface); + g_return_val_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE, NULL); + + format = cairo_image_surface_get_format (surface); + zoomed = cairo_surface_create_similar_image (surface, format, width_zoomed, height_zoomed); + cairo_surface_get_device_scale (surface, &scale_x, &scale_y); + cairo_surface_set_device_scale (zoomed, scale_x, scale_y); + + cr = cairo_create (zoomed); + + pattern = cairo_get_source (cr); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + + height = cairo_image_surface_get_height (surface); + width = cairo_image_surface_get_width (surface); + zoom_x = (double) width_zoomed / (gdouble) width; + zoom_y = (double) height_zoomed / (gdouble) height; + cairo_scale (cr, zoom_x, zoom_y); + cairo_set_source_surface (cr, surface, 0, 0); + + cairo_paint (cr); + cairo_destroy (cr); + + return zoomed; +} + +static void +gd_main_icon_box_icon_get_preferred_size (GdMainIconBoxIcon *self, gint *minimum, gint *natural) +{ + cairo_surface_t *surface; + cairo_surface_type_t surface_type; + gint height_scaled; + gint width_scaled; + gint scale_factor; + gint size = 0; + gint size_scaled; + + surface = gd_main_box_item_get_icon (self->item); + if (surface == NULL) + goto out; + + surface_type = cairo_surface_get_type (surface); + g_return_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE); + + scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); + height_scaled = cairo_image_surface_get_height (surface); + width_scaled = cairo_image_surface_get_width (surface); + + size_scaled = MAX (height_scaled, width_scaled); + size = size_scaled / scale_factor; + + out: + if (minimum != NULL) + *minimum = size; + + if (natural != NULL) + *natural = size; +} + +static void +gd_main_icon_box_icon_notify_icon (GdMainIconBoxIcon *self) +{ + g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy); + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +static gboolean +gd_main_icon_box_icon_draw (GtkWidget *widget, cairo_t *cr) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget); + + if (self->surface_zoomed == NULL) + goto out; + + cairo_save (cr); + cairo_set_source_surface (cr, self->surface_zoomed, self->x, self->y); + cairo_paint (cr); + cairo_restore (cr); + + out: + return GDK_EVENT_PROPAGATE; +} + +static void +gd_main_icon_box_icon_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget); + gd_main_icon_box_icon_get_preferred_size (self, minimum, natural); +} + +static void +gd_main_icon_box_icon_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget); + gd_main_icon_box_icon_get_preferred_size (self, minimum, natural); +} + +static void +gd_main_icon_box_icon_size_allocate (GtkWidget *widget, GtkAllocation *allocation) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (widget); + cairo_surface_t *surface; + cairo_surface_type_t surface_type; + gdouble zoom; + gint allocation_height_scaled; + gint allocation_width_scaled; + gint height_scaled; + gint height_zoomed_scaled; + gint scale_factor; + gint width_scaled; + gint width_zoomed_scaled; + + GTK_WIDGET_CLASS (gd_main_icon_box_icon_parent_class)->size_allocate (widget, allocation); + + surface = gd_main_box_item_get_icon (self->item); + if (surface == NULL) + { + g_return_if_fail (self->surface_zoomed == NULL); + return; + } + + surface_type = cairo_surface_get_type (surface); + g_return_if_fail (surface_type == CAIRO_SURFACE_TYPE_IMAGE); + + scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); + + allocation_height_scaled = allocation->height * scale_factor; + allocation_width_scaled = allocation->width * scale_factor; + + if (self->surface_zoomed != NULL) + { + height_zoomed_scaled = cairo_image_surface_get_height (self->surface_zoomed); + width_zoomed_scaled = cairo_image_surface_get_width (self->surface_zoomed); + if (height_zoomed_scaled == allocation_height_scaled && width_zoomed_scaled == allocation_width_scaled) + return; + } + + height_scaled = cairo_image_surface_get_height (surface); + width_scaled = cairo_image_surface_get_width (surface); + + if (height_scaled > width_scaled && allocation_height_scaled > height_scaled) + { + zoom = (gdouble) allocation_height_scaled / (gdouble) height_scaled; + height_zoomed_scaled = allocation_height_scaled; + width_zoomed_scaled = (gint) (zoom * (gdouble) width_scaled + 0.5); + + if (allocation_width_scaled < width_zoomed_scaled) + { + zoom = (gdouble) allocation_width_scaled / (gdouble) width_zoomed_scaled; + height_zoomed_scaled = (gint) (zoom * (gdouble) height_zoomed_scaled + 0.5); + width_zoomed_scaled = allocation_width_scaled; + } + } + else if (height_scaled <= width_scaled && allocation_width_scaled > width_scaled) + { + zoom = (gdouble) allocation_width_scaled / (gdouble) width_scaled; + height_zoomed_scaled = (gint) (zoom * (gdouble) height_scaled + 0.5); + width_zoomed_scaled = allocation_width_scaled; + + if (allocation_height_scaled < height_zoomed_scaled) + { + zoom = (gdouble) allocation_height_scaled / (gdouble) height_zoomed_scaled; + height_zoomed_scaled = allocation_height_scaled; + width_zoomed_scaled = (gint) (zoom * (gdouble) width_zoomed_scaled + 0.5); + } + } + else + { + height_zoomed_scaled = height_scaled; + width_zoomed_scaled = width_scaled; + } + + g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy); + self->surface_zoomed = gd_zoom_image_surface (surface, width_zoomed_scaled, height_zoomed_scaled); + + self->x = (gdouble) (allocation_width_scaled - width_zoomed_scaled) / (2.0 * (gdouble) scale_factor); + self->y = (gdouble) (allocation_height_scaled - height_zoomed_scaled) / (2.0 * (gdouble) scale_factor); +} + +static void +gd_main_icon_box_icon_dispose (GObject *obj) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (obj); + + g_clear_object (&self->item); + + G_OBJECT_CLASS (gd_main_icon_box_icon_parent_class)->dispose (obj); +} + +static void +gd_main_icon_box_icon_finalize (GObject *obj) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (obj); + + g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy); + + G_OBJECT_CLASS (gd_main_icon_box_icon_parent_class)->finalize (obj); +} + +static void +gd_main_icon_box_icon_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (object); + + switch (property_id) + { + case PROP_ITEM: + g_value_set_object (value, gd_main_icon_box_icon_get_item (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_icon_box_icon_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + GdMainIconBoxIcon *self = GD_MAIN_ICON_BOX_ICON (object); + + switch (property_id) + { + case PROP_ITEM: + gd_main_icon_box_icon_set_item (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_icon_box_icon_init (GdMainIconBoxIcon *self) +{ +} + +static void +gd_main_icon_box_icon_class_init (GdMainIconBoxIconClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass); + + oclass->dispose = gd_main_icon_box_icon_dispose; + oclass->finalize = gd_main_icon_box_icon_finalize; + oclass->get_property = gd_main_icon_box_icon_get_property; + oclass->set_property = gd_main_icon_box_icon_set_property; + wclass->draw = gd_main_icon_box_icon_draw; + wclass->get_preferred_height = gd_main_icon_box_icon_get_preferred_height; + wclass->get_preferred_width = gd_main_icon_box_icon_get_preferred_width; + wclass->size_allocate = gd_main_icon_box_icon_size_allocate; + + properties[PROP_ITEM] = g_param_spec_object ("item", + "Item", + "An item that is rendered by the widget", + GD_TYPE_MAIN_BOX_ITEM, + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +GtkWidget * +gd_main_icon_box_icon_new (GdMainBoxItem *item) +{ + g_return_val_if_fail (item == NULL || GD_IS_MAIN_BOX_ITEM (item), NULL); + return g_object_new (GD_TYPE_MAIN_ICON_BOX_ICON, "item", item, NULL); +} + +GdMainBoxItem * +gd_main_icon_box_icon_get_item (GdMainIconBoxIcon *self) +{ + g_return_val_if_fail (GD_IS_MAIN_ICON_BOX_ICON (self), NULL); + return self->item; +} + +void +gd_main_icon_box_icon_set_item (GdMainIconBoxIcon *self, GdMainBoxItem *item) +{ + g_return_if_fail (GD_IS_MAIN_ICON_BOX_ICON (self)); + g_return_if_fail (item == NULL || GD_IS_MAIN_BOX_ITEM (item)); + + if (self->item == item) + return; + + if (self->item != NULL) + g_signal_handlers_disconnect_by_func (self->item, gd_main_icon_box_icon_notify_icon, self); + + g_clear_pointer (&self->surface_zoomed, (GDestroyNotify) cairo_surface_destroy); + g_set_object (&self->item, item); + + if (self->item != NULL) + { + g_signal_connect_object (self->item, + "notify::icon", + G_CALLBACK (gd_main_icon_box_icon_notify_icon), + self, + G_CONNECT_SWAPPED); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]); + gtk_widget_queue_resize (GTK_WIDGET (self)); +} |