1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gtkimageprivate.h"
28
29#include "gtkiconhelperprivate.h"
30#include "gtkicontheme.h"
31#include "gtkintl.h"
32#include "gtkprivate.h"
33#include "gtksnapshot.h"
34#include "gtktypebuiltins.h"
35#include "gtkwidgetprivate.h"
36#include "gdkpixbufutilsprivate.h"
37
38#include <math.h>
39#include <string.h>
40#include <cairo-gobject.h>
41
42/**
43 * GtkImage:
44 *
45 * The `GtkImage` widget displays an image.
46 *
47 * ![An example GtkImage](image.png)
48 *
49 * Various kinds of object can be displayed as an image; most typically,
50 * you would load a `GdkTexture` from a file, using the convenience function
51 * [ctor@Gtk.Image.new_from_file], for instance:
52 *
53 * ```c
54 * GtkWidget *image = gtk_image_new_from_file ("myfile.png");
55 * ```
56 *
57 * If the file isn’t loaded successfully, the image will contain a
58 * “broken image” icon similar to that used in many web browsers.
59 *
60 * If you want to handle errors in loading the file yourself,
61 * for example by displaying an error message, then load the image with
62 * [ctor@Gdk.Texture.new_from_file], then create the `GtkImage` with
63 * [ctor@Gtk.Image.new_from_paintable].
64 *
65 * Sometimes an application will want to avoid depending on external data
66 * files, such as image files. See the documentation of `GResource` inside
67 * GIO, for details. In this case, [property@Gtk.Image:resource],
68 * [ctor@Gtk.Image.new_from_resource], and [method@Gtk.Image.set_from_resource]
69 * should be used.
70 *
71 * `GtkImage` displays its image as an icon, with a size that is determined
72 * by the application. See [class@Gtk.Picture] if you want to show an image
73 * at is actual size.
74 *
75 * ## CSS nodes
76 *
77 * `GtkImage` has a single CSS node with the name `image`. The style classes
78 * `.normal-icons` or `.large-icons` may appear, depending on the
79 * [property@Gtk.Image:icon-size] property.
80 *
81 * ## Accessibility
82 *
83 * `GtkImage` uses the `GTK_ACCESSIBLE_ROLE_IMG` role.
84 */
85
86typedef struct _GtkImageClass GtkImageClass;
87
88struct _GtkImage
89{
90 GtkWidget parent_instance;
91
92 GtkIconHelper *icon_helper;
93 GtkIconSize icon_size;
94
95 float baseline_align;
96
97 char *filename;
98 char *resource_path;
99};
100
101struct _GtkImageClass
102{
103 GtkWidgetClass parent_class;
104};
105
106
107static void gtk_image_snapshot (GtkWidget *widget,
108 GtkSnapshot *snapshot);
109static void gtk_image_unrealize (GtkWidget *widget);
110static void gtk_image_measure (GtkWidget *widget,
111 GtkOrientation orientation,
112 int for_size,
113 int *minimum,
114 int *natural,
115 int *minimum_baseline,
116 int *natural_baseline);
117
118static void gtk_image_css_changed (GtkWidget *widget,
119 GtkCssStyleChange *change);
120static void gtk_image_system_setting_changed (GtkWidget *widget,
121 GtkSystemSetting setting);
122static void gtk_image_finalize (GObject *object);
123
124static void gtk_image_set_property (GObject *object,
125 guint prop_id,
126 const GValue *value,
127 GParamSpec *pspec);
128static void gtk_image_get_property (GObject *object,
129 guint prop_id,
130 GValue *value,
131 GParamSpec *pspec);
132
133enum
134{
135 PROP_0,
136 PROP_PAINTABLE,
137 PROP_FILE,
138 PROP_ICON_SIZE,
139 PROP_PIXEL_SIZE,
140 PROP_ICON_NAME,
141 PROP_STORAGE_TYPE,
142 PROP_GICON,
143 PROP_RESOURCE,
144 PROP_USE_FALLBACK,
145 NUM_PROPERTIES
146};
147
148static GParamSpec *image_props[NUM_PROPERTIES] = { NULL, };
149
150G_DEFINE_TYPE (GtkImage, gtk_image, GTK_TYPE_WIDGET)
151
152static void
153gtk_image_class_init (GtkImageClass *class)
154{
155 GObjectClass *gobject_class;
156 GtkWidgetClass *widget_class;
157
158 gobject_class = G_OBJECT_CLASS (class);
159
160 gobject_class->set_property = gtk_image_set_property;
161 gobject_class->get_property = gtk_image_get_property;
162 gobject_class->finalize = gtk_image_finalize;
163
164 widget_class = GTK_WIDGET_CLASS (class);
165 widget_class->snapshot = gtk_image_snapshot;
166 widget_class->measure = gtk_image_measure;
167 widget_class->unrealize = gtk_image_unrealize;
168 widget_class->css_changed = gtk_image_css_changed;
169 widget_class->system_setting_changed = gtk_image_system_setting_changed;
170
171 /**
172 * GtkImage:paintable: (attributes org.gtk.Property.get=gtk_image_get_paintable org.gtk.Property.set=gtk_image_set_from_paintable)
173 *
174 * The `GdkPaintable` to display.
175 */
176 image_props[PROP_PAINTABLE] =
177 g_param_spec_object (name: "paintable",
178 P_("Paintable"),
179 P_("A GdkPaintable to display"),
180 GDK_TYPE_PAINTABLE,
181 GTK_PARAM_READWRITE);
182
183 /**
184 * GtkImage:file: (attributes org.gtk.Property.set=gtk_image_set_from_file)
185 *
186 * The `GFile to display.
187 */
188 image_props[PROP_FILE] =
189 g_param_spec_string (name: "file",
190 P_("Filename"),
191 P_("Filename to load and display"),
192 NULL,
193 GTK_PARAM_READWRITE);
194
195 /**
196 * GtkImage:icon-size: (attributes org.gtk.Property.get=gtk_image_get_icon_size org.gtk.Property.set=gtk_image_set_icon_size org.gtk.Property.set=gtk_image_set_icon_size)
197 *
198 * The symbolic size to display icons at.
199 */
200 image_props[PROP_ICON_SIZE] =
201 g_param_spec_enum (name: "icon-size",
202 P_("Icon size"),
203 P_("Symbolic size to use for icon set or named icon"),
204 enum_type: GTK_TYPE_ICON_SIZE,
205 default_value: GTK_ICON_SIZE_INHERIT,
206 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
207
208 /**
209 * GtkImage:pixel-size: (attributes org.gtk.Property.get=gtk_image_get_pixel_size org.gtk.Property.set=gtk_image_set_pixel_size)
210 *
211 * The size in pixels to display icons at.
212 *
213 * If set to a value != -1, this property overrides the
214 * [property@Gtk.Image:icon-size] property for images of type
215 * `GTK_IMAGE_ICON_NAME`.
216 */
217 image_props[PROP_PIXEL_SIZE] =
218 g_param_spec_int (name: "pixel-size",
219 P_("Pixel size"),
220 P_("Pixel size to use for named icon"),
221 minimum: -1, G_MAXINT,
222 default_value: -1,
223 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
224
225 /**
226 * GtkImage:icon-name: (attributes org.gtk.Property.get=gtk_image_get_icon_name org.gtk.Property.set=gtk_image_set_from_icon_name)
227 *
228 * The name of the icon in the icon theme.
229 *
230 * If the icon theme is changed, the image will be updated automatically.
231 */
232 image_props[PROP_ICON_NAME] =
233 g_param_spec_string (name: "icon-name",
234 P_("Icon Name"),
235 P_("The name of the icon from the icon theme"),
236 NULL,
237 GTK_PARAM_READWRITE);
238
239 /**
240 * GtkImage:gicon: (attributes org.gtk.Property.get=gtk_image_get_gicon org.gtk.Property.set=gtk_image_set_from_gicon)
241 *
242 * The `GIcon` displayed in the GtkImage.
243 *
244 * For themed icons, If the icon theme is changed, the image will be updated
245 * automatically.
246 */
247 image_props[PROP_GICON] =
248 g_param_spec_object (name: "gicon",
249 P_("Icon"),
250 P_("The GIcon being displayed"),
251 G_TYPE_ICON,
252 GTK_PARAM_READWRITE);
253
254 /**
255 * GtkImage:resource: (attributes org.gtk.Property.set=gtk_image_set_from_resource)
256 *
257 * A path to a resource file to display.
258 */
259 image_props[PROP_RESOURCE] =
260 g_param_spec_string (name: "resource",
261 P_("Resource"),
262 P_("The resource path being displayed"),
263 NULL,
264 GTK_PARAM_READWRITE);
265
266 /**
267 * GtkImage:storage-type: (attributes org.gtk.Property.get=gtk_image_get_storage_type)
268 *
269 * The representation being used for image data.
270 */
271 image_props[PROP_STORAGE_TYPE] =
272 g_param_spec_enum (name: "storage-type",
273 P_("Storage type"),
274 P_("The representation being used for image data"),
275 enum_type: GTK_TYPE_IMAGE_TYPE,
276 default_value: GTK_IMAGE_EMPTY,
277 GTK_PARAM_READABLE);
278
279 /**
280 * GtkImage:use-fallback:
281 *
282 * Whether the icon displayed in the `GtkImage` will use
283 * standard icon names fallback.
284 *
285 * The value of this property is only relevant for images of type
286 * %GTK_IMAGE_ICON_NAME and %GTK_IMAGE_GICON.
287 */
288 image_props[PROP_USE_FALLBACK] =
289 g_param_spec_boolean (name: "use-fallback",
290 P_("Use Fallback"),
291 P_("Whether to use icon names fallback"),
292 FALSE,
293 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
294
295 g_object_class_install_properties (oclass: gobject_class, n_pspecs: NUM_PROPERTIES, pspecs: image_props);
296
297 gtk_widget_class_set_css_name (widget_class, I_("image"));
298
299 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_IMG);
300}
301
302static void
303gtk_image_init (GtkImage *image)
304{
305 GtkCssNode *widget_node;
306
307 widget_node = gtk_widget_get_css_node (GTK_WIDGET (image));
308
309 image->icon_helper = gtk_icon_helper_new (css_node: widget_node, GTK_WIDGET (image));
310}
311
312static void
313gtk_image_finalize (GObject *object)
314{
315 GtkImage *image = GTK_IMAGE (object);
316
317 gtk_image_clear (image);
318
319 g_clear_object (&image->icon_helper);
320
321 g_free (mem: image->filename);
322 g_free (mem: image->resource_path);
323
324 G_OBJECT_CLASS (gtk_image_parent_class)->finalize (object);
325};
326
327static void
328gtk_image_set_property (GObject *object,
329 guint prop_id,
330 const GValue *value,
331 GParamSpec *pspec)
332{
333 GtkImage *image = GTK_IMAGE (object);
334
335 switch (prop_id)
336 {
337 case PROP_PAINTABLE:
338 gtk_image_set_from_paintable (image, paintable: g_value_get_object (value));
339 break;
340 case PROP_FILE:
341 gtk_image_set_from_file (image, filename: g_value_get_string (value));
342 break;
343 case PROP_ICON_SIZE:
344 gtk_image_set_icon_size (image, icon_size: g_value_get_enum (value));
345 break;
346 case PROP_PIXEL_SIZE:
347 gtk_image_set_pixel_size (image, pixel_size: g_value_get_int (value));
348 break;
349 case PROP_ICON_NAME:
350 gtk_image_set_from_icon_name (image, icon_name: g_value_get_string (value));
351 break;
352 case PROP_GICON:
353 gtk_image_set_from_gicon (image, icon: g_value_get_object (value));
354 break;
355 case PROP_RESOURCE:
356 gtk_image_set_from_resource (image, resource_path: g_value_get_string (value));
357 break;
358
359 case PROP_USE_FALLBACK:
360 if (_gtk_icon_helper_set_use_fallback (self: image->icon_helper, use_fallback: g_value_get_boolean (value)))
361 g_object_notify_by_pspec (object, pspec);
362 break;
363
364 default:
365 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366 break;
367 }
368}
369
370static void
371gtk_image_get_property (GObject *object,
372 guint prop_id,
373 GValue *value,
374 GParamSpec *pspec)
375{
376 GtkImage *image = GTK_IMAGE (object);
377
378 switch (prop_id)
379 {
380 case PROP_PAINTABLE:
381 g_value_set_object (value, v_object: _gtk_icon_helper_peek_paintable (self: image->icon_helper));
382 break;
383 case PROP_FILE:
384 g_value_set_string (value, v_string: image->filename);
385 break;
386 case PROP_ICON_SIZE:
387 g_value_set_enum (value, v_enum: image->icon_size);
388 break;
389 case PROP_PIXEL_SIZE:
390 g_value_set_int (value, v_int: _gtk_icon_helper_get_pixel_size (self: image->icon_helper));
391 break;
392 case PROP_ICON_NAME:
393 g_value_set_string (value, v_string: _gtk_icon_helper_get_icon_name (self: image->icon_helper));
394 break;
395 case PROP_GICON:
396 g_value_set_object (value, v_object: _gtk_icon_helper_peek_gicon (self: image->icon_helper));
397 break;
398 case PROP_RESOURCE:
399 g_value_set_string (value, v_string: image->resource_path);
400 break;
401 case PROP_USE_FALLBACK:
402 g_value_set_boolean (value, v_boolean: _gtk_icon_helper_get_use_fallback (self: image->icon_helper));
403 break;
404 case PROP_STORAGE_TYPE:
405 g_value_set_enum (value, v_enum: _gtk_icon_helper_get_storage_type (self: image->icon_helper));
406 break;
407 default:
408 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
409 break;
410 }
411}
412
413
414/**
415 * gtk_image_new_from_file:
416 * @filename: (type filename): a filename
417 *
418 * Creates a new `GtkImage` displaying the file @filename.
419 *
420 * If the file isn’t found or can’t be loaded, the resulting `GtkImage`
421 * will display a “broken image” icon. This function never returns %NULL,
422 * it always returns a valid `GtkImage` widget.
423 *
424 * If you need to detect failures to load the file, use
425 * [ctor@Gdk.Texture.new_from_file] to load the file yourself,
426 * then create the `GtkImage` from the texture.
427 *
428 * The storage type (see [method@Gtk.Image.get_storage_type])
429 * of the returned image is not defined, it will be whatever
430 * is appropriate for displaying the file.
431 *
432 * Returns: a new `GtkImage`
433 */
434GtkWidget*
435gtk_image_new_from_file (const char *filename)
436{
437 GtkImage *image;
438
439 image = g_object_new (GTK_TYPE_IMAGE, NULL);
440
441 gtk_image_set_from_file (image, filename);
442
443 return GTK_WIDGET (image);
444}
445
446/**
447 * gtk_image_new_from_resource:
448 * @resource_path: a resource path
449 *
450 * Creates a new `GtkImage` displaying the resource file @resource_path.
451 *
452 * If the file isn’t found or can’t be loaded, the resulting `GtkImage` will
453 * display a “broken image” icon. This function never returns %NULL,
454 * it always returns a valid `GtkImage` widget.
455 *
456 * If you need to detect failures to load the file, use
457 * [ctor@GdkPixbuf.Pixbuf.new_from_file] to load the file yourself,
458 * then create the `GtkImage` from the pixbuf.
459 *
460 * The storage type (see [method@Gtk.Image.get_storage_type]) of
461 * the returned image is not defined, it will be whatever is
462 * appropriate for displaying the file.
463 *
464 * Returns: a new `GtkImage`
465 */
466GtkWidget*
467gtk_image_new_from_resource (const char *resource_path)
468{
469 GtkImage *image;
470
471 image = g_object_new (GTK_TYPE_IMAGE, NULL);
472
473 gtk_image_set_from_resource (image, resource_path);
474
475 return GTK_WIDGET (image);
476}
477
478/**
479 * gtk_image_new_from_pixbuf:
480 * @pixbuf: (nullable): a `GdkPixbuf`
481 *
482 * Creates a new `GtkImage` displaying @pixbuf.
483 *
484 * The `GtkImage` does not assume a reference to the pixbuf; you still
485 * need to unref it if you own references. `GtkImage` will add its own
486 * reference rather than adopting yours.
487 *
488 * This is a helper for [ctor@Gtk.Image.new_from_paintable], and you can't
489 * get back the exact pixbuf once this is called, only a texture.
490 *
491 * Note that this function just creates an `GtkImage` from the pixbuf.
492 * The `GtkImage` created will not react to state changes. Should you
493 * want that, you should use [ctor@Gtk.Image.new_from_icon_name].
494 *
495 * Returns: a new `GtkImage`
496 */
497GtkWidget*
498gtk_image_new_from_pixbuf (GdkPixbuf *pixbuf)
499{
500 GtkImage *image;
501
502 image = g_object_new (GTK_TYPE_IMAGE, NULL);
503
504 gtk_image_set_from_pixbuf (image, pixbuf);
505
506 return GTK_WIDGET (image);
507}
508
509/**
510 * gtk_image_new_from_paintable:
511 * @paintable: (nullable): a `GdkPaintable`
512 *
513 * Creates a new `GtkImage` displaying @paintable.
514 *
515 * The `GtkImage` does not assume a reference to the paintable; you still
516 * need to unref it if you own references. `GtkImage` will add its own
517 * reference rather than adopting yours.
518 *
519 * The `GtkImage` will track changes to the @paintable and update
520 * its size and contents in response to it.
521 *
522 * Returns: a new `GtkImage`
523 */
524GtkWidget*
525gtk_image_new_from_paintable (GdkPaintable *paintable)
526{
527 GtkImage *image;
528
529 image = g_object_new (GTK_TYPE_IMAGE, NULL);
530
531 gtk_image_set_from_paintable (image, paintable);
532
533 return GTK_WIDGET (image);
534}
535
536/**
537 * gtk_image_new_from_icon_name:
538 * @icon_name: (nullable): an icon name
539 *
540 * Creates a `GtkImage` displaying an icon from the current icon theme.
541 *
542 * If the icon name isn’t known, a “broken image” icon will be
543 * displayed instead. If the current icon theme is changed, the icon
544 * will be updated appropriately.
545 *
546 * Returns: a new `GtkImage` displaying the themed icon
547 */
548GtkWidget*
549gtk_image_new_from_icon_name (const char *icon_name)
550{
551 GtkImage *image;
552
553 image = g_object_new (GTK_TYPE_IMAGE, NULL);
554
555 gtk_image_set_from_icon_name (image, icon_name);
556
557 return GTK_WIDGET (image);
558}
559
560/**
561 * gtk_image_new_from_gicon:
562 * @icon: an icon
563 *
564 * Creates a `GtkImage` displaying an icon from the current icon theme.
565 *
566 * If the icon name isn’t known, a “broken image” icon will be
567 * displayed instead. If the current icon theme is changed, the icon
568 * will be updated appropriately.
569 *
570 * Returns: a new `GtkImage` displaying the themed icon
571 */
572GtkWidget*
573gtk_image_new_from_gicon (GIcon *icon)
574{
575 GtkImage *image;
576
577 image = g_object_new (GTK_TYPE_IMAGE, NULL);
578
579 gtk_image_set_from_gicon (image, icon);
580
581 return GTK_WIDGET (image);
582}
583
584/**
585 * gtk_image_set_from_file: (attributes org.gtk.Method.set_property=file)
586 * @image: a `GtkImage`
587 * @filename: (type filename) (nullable): a filename
588 *
589 * Sets a `GtkImage` to show a file.
590 *
591 * See [ctor@Gtk.Image.new_from_file] for details.
592 */
593void
594gtk_image_set_from_file (GtkImage *image,
595 const char *filename)
596{
597 int scale_factor;
598 GdkPaintable *paintable;
599
600 g_return_if_fail (GTK_IS_IMAGE (image));
601
602 g_object_freeze_notify (G_OBJECT (image));
603
604 gtk_image_clear (image);
605
606 if (filename == NULL)
607 {
608 image->filename = NULL;
609 g_object_thaw_notify (G_OBJECT (image));
610 return;
611 }
612
613 scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (image));
614 paintable = gdk_paintable_new_from_path_scaled (path: filename, scale_factor);
615
616 if (paintable == NULL)
617 {
618 gtk_image_set_from_icon_name (image, icon_name: "image-missing");
619 g_object_thaw_notify (G_OBJECT (image));
620 return;
621 }
622
623 gtk_image_set_from_paintable (image, paintable);
624
625 g_object_unref (object: paintable);
626
627 image->filename = g_strdup (str: filename);
628
629 g_object_thaw_notify (G_OBJECT (image));
630}
631
632#ifndef GDK_PIXBUF_MAGIC_NUMBER
633#define GDK_PIXBUF_MAGIC_NUMBER (0x47646b50) /* 'GdkP' */
634#endif
635
636static gboolean
637resource_is_pixdata (const char *resource_path)
638{
639 const guint8 *stream;
640 guint32 magic;
641 gsize data_size;
642 GBytes *bytes;
643 gboolean ret = FALSE;
644
645 bytes = g_resources_lookup_data (path: resource_path, lookup_flags: 0, NULL);
646 if (bytes == NULL)
647 return FALSE;
648
649 stream = g_bytes_get_data (bytes, size: &data_size);
650 if (data_size < sizeof(guint32))
651 goto out;
652
653 magic = (stream[0] << 24) + (stream[1] << 16) + (stream[2] << 8) + stream[3];
654 if (magic == GDK_PIXBUF_MAGIC_NUMBER)
655 ret = TRUE;
656
657out:
658 g_bytes_unref (bytes);
659 return ret;
660}
661
662/**
663 * gtk_image_set_from_resource: (attributes org.gtk.Method.set_property=resource)
664 * @image: a `GtkImage`
665 * @resource_path: (nullable): a resource path
666 *
667 * Sets a `GtkImage` to show a resource.
668 *
669 * See [ctor@Gtk.Image.new_from_resource] for details.
670 */
671void
672gtk_image_set_from_resource (GtkImage *image,
673 const char *resource_path)
674{
675 int scale_factor;
676 GdkPaintable *paintable;
677
678 g_return_if_fail (GTK_IS_IMAGE (image));
679
680 g_object_freeze_notify (G_OBJECT (image));
681
682 gtk_image_clear (image);
683
684 if (resource_path == NULL)
685 {
686 g_object_thaw_notify (G_OBJECT (image));
687 return;
688 }
689
690 if (resource_is_pixdata (resource_path))
691 {
692 g_warning ("GdkPixdata format images are not supported, remove the \"to-pixdata\" option from your GResource files");
693 paintable = NULL;
694 }
695 else
696 {
697 scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (image));
698 paintable = gdk_paintable_new_from_resource_scaled (path: resource_path, scale_factor);
699 }
700
701 if (paintable == NULL)
702 {
703 gtk_image_set_from_icon_name (image, icon_name: "image-missing");
704 g_object_thaw_notify (G_OBJECT (image));
705 return;
706 }
707
708 gtk_image_set_from_paintable (image, paintable);
709
710 g_object_unref (object: paintable);
711
712 image->resource_path = g_strdup (str: resource_path);
713
714 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_RESOURCE]);
715
716 g_object_thaw_notify (G_OBJECT (image));
717}
718
719
720/**
721 * gtk_image_set_from_pixbuf: (attributes org.gtk.Method.set_property=paintable)
722 * @image: a `GtkImage`
723 * @pixbuf: (nullable): a `GdkPixbuf` or `NULL`
724 *
725 * Sets a `GtkImage` to show a `GdkPixbuf`.
726 *
727 * See [ctor@Gtk.Image.new_from_pixbuf] for details.
728 *
729 * Note: This is a helper for [method@Gtk.Image.set_from_paintable],
730 * and you can't get back the exact pixbuf once this is called,
731 * only a paintable.
732 */
733void
734gtk_image_set_from_pixbuf (GtkImage *image,
735 GdkPixbuf *pixbuf)
736{
737 GdkTexture *texture;
738
739 g_return_if_fail (GTK_IS_IMAGE (image));
740 g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
741
742 if (pixbuf)
743 texture = gdk_texture_new_for_pixbuf (pixbuf);
744 else
745 texture = NULL;
746
747 gtk_image_set_from_paintable (image, paintable: GDK_PAINTABLE (ptr: texture));
748
749 if (texture)
750 g_object_unref (object: texture);
751}
752
753/**
754 * gtk_image_set_from_icon_name: (attributes org.gtk.Method.set_property=icon-name)
755 * @image: a `GtkImage`
756 * @icon_name: (nullable): an icon name
757 *
758 * Sets a `GtkImage` to show a named icon.
759 *
760 * See [ctor@Gtk.Image.new_from_icon_name] for details.
761 */
762void
763gtk_image_set_from_icon_name (GtkImage *image,
764 const char *icon_name)
765{
766 g_return_if_fail (GTK_IS_IMAGE (image));
767
768 g_object_freeze_notify (G_OBJECT (image));
769
770 gtk_image_clear (image);
771
772 if (icon_name)
773 _gtk_icon_helper_set_icon_name (self: image->icon_helper, icon_name);
774
775 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_ICON_NAME]);
776
777 g_object_thaw_notify (G_OBJECT (image));
778}
779
780/**
781 * gtk_image_set_from_gicon: (attributes org.gtk.Method.set_property=gicon)
782 * @image: a `GtkImage`
783 * @icon: an icon
784 *
785 * Sets a `GtkImage` to show a `GIcon`.
786 *
787 * See [ctor@Gtk.Image.new_from_gicon] for details.
788 */
789void
790gtk_image_set_from_gicon (GtkImage *image,
791 GIcon *icon)
792{
793 g_return_if_fail (GTK_IS_IMAGE (image));
794
795 g_object_freeze_notify (G_OBJECT (image));
796
797 if (icon)
798 g_object_ref (icon);
799
800 gtk_image_clear (image);
801
802 if (icon)
803 {
804 _gtk_icon_helper_set_gicon (self: image->icon_helper, gicon: icon);
805 g_object_unref (object: icon);
806 }
807
808 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_GICON]);
809
810 g_object_thaw_notify (G_OBJECT (image));
811}
812
813static void
814gtk_image_paintable_invalidate_contents (GdkPaintable *paintable,
815 GtkImage *image)
816{
817 gtk_widget_queue_draw (GTK_WIDGET (image));
818}
819
820static void
821gtk_image_paintable_invalidate_size (GdkPaintable *paintable,
822 GtkImage *image)
823{
824 gtk_icon_helper_invalidate (self: image->icon_helper);
825}
826
827/**
828 * gtk_image_set_from_paintable: (attributes org.gtk.Method.set_property=paintable)
829 * @image: a `GtkImage`
830 * @paintable: (nullable): a `GdkPaintable`
831 *
832 * Sets a `GtkImage` to show a `GdkPaintable`.
833 *
834 * See [ctor@Gtk.Image.new_from_paintable] for details.
835 */
836void
837gtk_image_set_from_paintable (GtkImage *image,
838 GdkPaintable *paintable)
839{
840 g_return_if_fail (GTK_IS_IMAGE (image));
841 g_return_if_fail (paintable == NULL || GDK_IS_PAINTABLE (paintable));
842
843 g_object_freeze_notify (G_OBJECT (image));
844
845 if (paintable)
846 g_object_ref (paintable);
847
848 gtk_image_clear (image);
849
850 if (paintable)
851 {
852 const guint flags = gdk_paintable_get_flags (paintable);
853
854 _gtk_icon_helper_set_paintable (self: image->icon_helper, paintable);
855
856 if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
857 g_signal_connect (paintable,
858 "invalidate-contents",
859 G_CALLBACK (gtk_image_paintable_invalidate_contents),
860 image);
861
862 if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
863 g_signal_connect (paintable,
864 "invalidate-size",
865 G_CALLBACK (gtk_image_paintable_invalidate_size),
866 image);
867 g_object_unref (object: paintable);
868 }
869
870 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_PAINTABLE]);
871
872 g_object_thaw_notify (G_OBJECT (image));
873}
874
875/**
876 * gtk_image_get_storage_type: (attributes org.gtk.Method.get_property=storage-type)
877 * @image: a `GtkImage`
878 *
879 * Gets the type of representation being used by the `GtkImage`
880 * to store image data.
881 *
882 * If the `GtkImage` has no image data, the return value will
883 * be %GTK_IMAGE_EMPTY.
884 *
885 * Returns: image representation being used
886 */
887GtkImageType
888gtk_image_get_storage_type (GtkImage *image)
889{
890 g_return_val_if_fail (GTK_IS_IMAGE (image), GTK_IMAGE_EMPTY);
891
892 return _gtk_icon_helper_get_storage_type (self: image->icon_helper);
893}
894
895/**
896 * gtk_image_get_paintable: (attributes org.gtk.Method.get_property=paintable)
897 * @image: a `GtkImage`
898 *
899 * Gets the image `GdkPaintable` being displayed by the `GtkImage`.
900 *
901 * The storage type of the image must be %GTK_IMAGE_EMPTY or
902 * %GTK_IMAGE_PAINTABLE (see [method@Gtk.Image.get_storage_type]).
903 * The caller of this function does not own a reference to the
904 * returned paintable.
905 *
906 * Returns: (nullable) (transfer none): the displayed paintable
907 */
908GdkPaintable *
909gtk_image_get_paintable (GtkImage *image)
910{
911 g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
912
913 return _gtk_icon_helper_peek_paintable (self: image->icon_helper);
914}
915
916/**
917 * gtk_image_get_icon_name: (attributes org.gtk.Method.get_property=icon-name)
918 * @image: a `GtkImage`
919 *
920 * Gets the icon name and size being displayed by the `GtkImage`.
921 *
922 * The storage type of the image must be %GTK_IMAGE_EMPTY or
923 * %GTK_IMAGE_ICON_NAME (see [method@Gtk.Image.get_storage_type]).
924 * The returned string is owned by the `GtkImage` and should not
925 * be freed.
926 *
927 * Returns: (transfer none) (nullable): the icon name
928 */
929const char *
930gtk_image_get_icon_name (GtkImage *image)
931{
932 g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
933
934 return _gtk_icon_helper_get_icon_name (self: image->icon_helper);
935}
936
937/**
938 * gtk_image_get_gicon: (attributes org.gtk.Method.get_property=gicon)
939 * @image: a `GtkImage`
940 *
941 * Gets the `GIcon` being displayed by the `GtkImage`.
942 *
943 * The storage type of the image must be %GTK_IMAGE_EMPTY or
944 * %GTK_IMAGE_GICON (see [method@Gtk.Image.get_storage_type]).
945 * The caller of this function does not own a reference to the
946 * returned `GIcon`.
947 *
948 * Returns: (transfer none) (nullable): a `GIcon`
949 **/
950GIcon *
951gtk_image_get_gicon (GtkImage *image)
952{
953 g_return_val_if_fail (GTK_IS_IMAGE (image), NULL);
954
955 return _gtk_icon_helper_peek_gicon (self: image->icon_helper);
956}
957
958/**
959 * gtk_image_new:
960 *
961 * Creates a new empty `GtkImage` widget.
962 *
963 * Returns: a newly created `GtkImage` widget.
964 */
965GtkWidget*
966gtk_image_new (void)
967{
968 return g_object_new (GTK_TYPE_IMAGE, NULL);
969}
970
971static void
972gtk_image_unrealize (GtkWidget *widget)
973{
974 GtkImage *image = GTK_IMAGE (widget);
975
976 gtk_icon_helper_invalidate (self: image->icon_helper);
977
978 GTK_WIDGET_CLASS (gtk_image_parent_class)->unrealize (widget);
979}
980
981static float
982gtk_image_get_baseline_align (GtkImage *image)
983{
984 PangoContext *pango_context;
985 PangoFontMetrics *metrics;
986
987 if (image->baseline_align == 0.0)
988 {
989 pango_context = gtk_widget_get_pango_context (GTK_WIDGET (image));
990 metrics = pango_context_get_metrics (context: pango_context, NULL, NULL);
991 image->baseline_align =
992 (float)pango_font_metrics_get_ascent (metrics) /
993 (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics));
994
995 pango_font_metrics_unref (metrics);
996 }
997
998 return image->baseline_align;
999}
1000
1001static void
1002gtk_image_snapshot (GtkWidget *widget,
1003 GtkSnapshot *snapshot)
1004{
1005 GtkImage *image = GTK_IMAGE (widget);
1006 double ratio;
1007 int x, y, width, height, baseline;
1008 double w, h;
1009
1010 width = gtk_widget_get_width (widget);
1011 height = gtk_widget_get_height (widget);
1012 ratio = gdk_paintable_get_intrinsic_aspect_ratio (paintable: GDK_PAINTABLE (ptr: image->icon_helper));
1013
1014 if (ratio == 0)
1015 {
1016 gdk_paintable_snapshot (paintable: GDK_PAINTABLE (ptr: image->icon_helper), snapshot, width, height);
1017 }
1018 else
1019 {
1020 double image_ratio = (double) width / height;
1021
1022 if (ratio > image_ratio)
1023 {
1024 w = width;
1025 h = width / ratio;
1026 }
1027 else
1028 {
1029 w = height * ratio;
1030 h = height;
1031 }
1032
1033 x = (width - ceil (x: w)) / 2;
1034
1035 baseline = gtk_widget_get_allocated_baseline (widget);
1036 if (baseline == -1)
1037 y = floor(x: height - ceil (x: h)) / 2;
1038 else
1039 y = CLAMP (baseline - h * gtk_image_get_baseline_align (image), 0, height - ceil (h));
1040
1041 if (x != 0 || y != 0)
1042 {
1043 gtk_snapshot_save (snapshot);
1044 gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (x, y));
1045 gdk_paintable_snapshot (paintable: GDK_PAINTABLE (ptr: image->icon_helper), snapshot, width: w, height: h);
1046 gtk_snapshot_restore (snapshot);
1047 }
1048 else
1049 {
1050 gdk_paintable_snapshot (paintable: GDK_PAINTABLE (ptr: image->icon_helper), snapshot, width: w, height: h);
1051 }
1052 }
1053}
1054
1055static void
1056gtk_image_notify_for_storage_type (GtkImage *image,
1057 GtkImageType storage_type)
1058{
1059 switch (storage_type)
1060 {
1061 case GTK_IMAGE_ICON_NAME:
1062 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_ICON_NAME]);
1063 break;
1064 case GTK_IMAGE_GICON:
1065 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_GICON]);
1066 break;
1067 case GTK_IMAGE_PAINTABLE:
1068 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_PAINTABLE]);
1069 break;
1070 case GTK_IMAGE_EMPTY:
1071 default:
1072 break;
1073 }
1074}
1075
1076void
1077gtk_image_set_from_definition (GtkImage *image,
1078 GtkImageDefinition *def)
1079{
1080 g_return_if_fail (GTK_IS_IMAGE (image));
1081
1082 g_object_freeze_notify (G_OBJECT (image));
1083
1084 gtk_image_clear (image);
1085
1086 if (def != NULL)
1087 {
1088 _gtk_icon_helper_set_definition (self: image->icon_helper, def);
1089
1090 gtk_image_notify_for_storage_type (image, storage_type: gtk_image_definition_get_storage_type (def));
1091 }
1092
1093 g_object_thaw_notify (G_OBJECT (image));
1094}
1095
1096GtkImageDefinition *
1097gtk_image_get_definition (GtkImage *image)
1098{
1099 return gtk_icon_helper_get_definition (self: image->icon_helper);
1100}
1101
1102/**
1103 * gtk_image_clear:
1104 * @image: a `GtkImage`
1105 *
1106 * Resets the image to be empty.
1107 */
1108void
1109gtk_image_clear (GtkImage *image)
1110{
1111 GtkImageType storage_type;
1112
1113 g_object_freeze_notify (G_OBJECT (image));
1114 storage_type = gtk_image_get_storage_type (image);
1115
1116 if (storage_type != GTK_IMAGE_EMPTY)
1117 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_STORAGE_TYPE]);
1118
1119 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_ICON_SIZE]);
1120
1121 gtk_image_notify_for_storage_type (image, storage_type);
1122
1123 if (image->filename)
1124 {
1125 g_free (mem: image->filename);
1126 image->filename = NULL;
1127 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_FILE]);
1128 }
1129
1130 if (image->resource_path)
1131 {
1132 g_free (mem: image->resource_path);
1133 image->resource_path = NULL;
1134 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_RESOURCE]);
1135 }
1136
1137 if (storage_type == GTK_IMAGE_PAINTABLE)
1138 {
1139 GdkPaintable *paintable = _gtk_icon_helper_peek_paintable (self: image->icon_helper);
1140 const guint flags = gdk_paintable_get_flags (paintable);
1141
1142 if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
1143 g_signal_handlers_disconnect_by_func (paintable,
1144 gtk_image_paintable_invalidate_contents,
1145 image);
1146
1147 if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0)
1148 g_signal_handlers_disconnect_by_func (paintable,
1149 gtk_image_paintable_invalidate_size,
1150 image);
1151 }
1152
1153 _gtk_icon_helper_clear (self: image->icon_helper);
1154
1155 g_object_thaw_notify (G_OBJECT (image));
1156}
1157
1158static void
1159gtk_image_measure (GtkWidget *widget,
1160 GtkOrientation orientation,
1161 int for_size,
1162 int *minimum,
1163 int *natural,
1164 int *minimum_baseline,
1165 int *natural_baseline)
1166{
1167 GtkImage *image = GTK_IMAGE (widget);
1168 float baseline_align;
1169
1170 *minimum = *natural = gtk_icon_helper_get_size (self: image->icon_helper);
1171
1172 if (orientation == GTK_ORIENTATION_VERTICAL)
1173 {
1174 baseline_align = gtk_image_get_baseline_align (GTK_IMAGE (widget));
1175 if (minimum_baseline)
1176 *minimum_baseline = *minimum * baseline_align;
1177 if (natural_baseline)
1178 *natural_baseline = *natural * baseline_align;
1179 }
1180}
1181
1182static void
1183gtk_image_css_changed (GtkWidget *widget,
1184 GtkCssStyleChange *change)
1185{
1186 GtkImage *image = GTK_IMAGE (widget);
1187
1188 gtk_icon_helper_invalidate_for_change (self: image->icon_helper, change);
1189
1190 GTK_WIDGET_CLASS (gtk_image_parent_class)->css_changed (widget, change);
1191
1192 image->baseline_align = 0.0;
1193}
1194
1195static void
1196gtk_image_system_setting_changed (GtkWidget *widget,
1197 GtkSystemSetting setting)
1198{
1199 GtkImage *image = GTK_IMAGE (widget);
1200
1201 if (setting == GTK_SYSTEM_SETTING_ICON_THEME)
1202 gtk_icon_helper_invalidate (self: image->icon_helper);
1203
1204 GTK_WIDGET_CLASS (gtk_image_parent_class)->system_setting_changed (widget, setting);
1205}
1206
1207/**
1208 * gtk_image_set_pixel_size: (attributes org.gtk.Method.set_property=pixel-size)
1209 * @image: a `GtkImage`
1210 * @pixel_size: the new pixel size
1211 *
1212 * Sets the pixel size to use for named icons.
1213 *
1214 * If the pixel size is set to a value != -1, it is used instead
1215 * of the icon size set by [method@Gtk.Image.set_from_icon_name].
1216 */
1217void
1218gtk_image_set_pixel_size (GtkImage *image,
1219 int pixel_size)
1220{
1221 g_return_if_fail (GTK_IS_IMAGE (image));
1222
1223 if (_gtk_icon_helper_set_pixel_size (self: image->icon_helper, pixel_size))
1224 {
1225 if (gtk_widget_get_visible (GTK_WIDGET (image)))
1226 gtk_widget_queue_resize (GTK_WIDGET (image));
1227 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_PIXEL_SIZE]);
1228 }
1229}
1230
1231/**
1232 * gtk_image_get_pixel_size: (attributes org.gtk.Method.get_property=pixel-size)
1233 * @image: a `GtkImage`
1234 *
1235 * Gets the pixel size used for named icons.
1236 *
1237 * Returns: the pixel size used for named icons.
1238 */
1239int
1240gtk_image_get_pixel_size (GtkImage *image)
1241{
1242 g_return_val_if_fail (GTK_IS_IMAGE (image), -1);
1243
1244 return _gtk_icon_helper_get_pixel_size (self: image->icon_helper);
1245}
1246
1247/**
1248 * gtk_image_set_icon_size: (attributes org.gtk.Method.set_property=icon-size)
1249 * @image: a `GtkImage`
1250 * @icon_size: the new icon size
1251 *
1252 * Suggests an icon size to the theme for named icons.
1253 */
1254void
1255gtk_image_set_icon_size (GtkImage *image,
1256 GtkIconSize icon_size)
1257{
1258 g_return_if_fail (GTK_IS_IMAGE (image));
1259
1260 if (image->icon_size == icon_size)
1261 return;
1262
1263 image->icon_size = icon_size;
1264 gtk_icon_size_set_style_classes (cssnode: gtk_widget_get_css_node (GTK_WIDGET (image)), icon_size);
1265 g_object_notify_by_pspec (G_OBJECT (image), pspec: image_props[PROP_ICON_SIZE]);
1266}
1267
1268/**
1269 * gtk_image_get_icon_size: (attributes org.gtk.Method.get_property=icon-size)
1270 * @image: a `GtkImage`
1271 *
1272 * Gets the icon size used by the @image when rendering icons.
1273 *
1274 * Returns: the image size used by icons
1275 */
1276GtkIconSize
1277gtk_image_get_icon_size (GtkImage *image)
1278{
1279 g_return_val_if_fail (GTK_IS_IMAGE (image), GTK_ICON_SIZE_INHERIT);
1280
1281 return image->icon_size;
1282}
1283
1284void
1285gtk_image_get_image_size (GtkImage *image,
1286 int *width,
1287 int *height)
1288{
1289 *width = *height = gtk_icon_helper_get_size (self: image->icon_helper);
1290}
1291

source code of gtk/gtk/gtkimage.c