libyui-gtk  2.42.2
 All Classes
ygtkimage.c
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /* YGtkImage widget */
6 // check the header file for information about this widget
7 
8 #include <yui/Libyui_config.h>
9 #include "ygdkmngloader.h"
10 #include "ygtkimage.h"
11 #include <gtk/gtk.h>
12 
13 G_DEFINE_TYPE (YGtkImage, ygtk_image, GTK_TYPE_DRAWING_AREA)
14 
15 static void ygtk_image_init (YGtkImage *image)
16 {
17 }
18 
19 static void ygtk_image_free_pixbuf (YGtkImage *image)
20 {
21  if (image->animated) {
22  if (image->animation) {
23  g_object_unref (G_OBJECT (image->animation->pixbuf));
24  if (image->animation->timeout_id)
25  g_source_remove (image->animation->timeout_id);
26  g_free (image->animation);
27  image->animation = NULL;
28  }
29  }
30  else {
31  if (image->pixbuf) {
32  g_object_unref (G_OBJECT (image->pixbuf));
33  image->pixbuf = NULL;
34  }
35  }
36 }
37 
38 static void ygtk_image_destroy (GtkWidget *widget)
39 {
40  YGtkImage *image = YGTK_IMAGE (widget);
41  if (image->alt_text)
42  g_free (image->alt_text);
43  image->alt_text = NULL;
44  ygtk_image_free_pixbuf (image);
45  GTK_WIDGET_CLASS (ygtk_image_parent_class)->destroy (widget);
46 }
47 
48 static void ygtk_image_set_pixbuf (YGtkImage *image, GdkPixbuf *pixbuf, const char *error_msg)
49 {
50  ygtk_image_free_pixbuf (image);
51  gtk_widget_queue_resize (GTK_WIDGET (image));
52 
53  if (pixbuf) {
54  image->animated = FALSE;
55  image->pixbuf = pixbuf;
56  image->loaded = TRUE;
57  }
58 /* else
59  g_warning ("Couldn't load image - %s", error_msg);*/
60 }
61 
62 static gboolean ygtk_image_advance_frame_cb (gpointer data)
63 {
64  YGtkImage *image = (YGtkImage *) data;
65  struct _YGtkImageAnimation *animation = image->animation;
66 
67  if (!animation->frame) // no frame yet loaded
68  animation->frame = gdk_pixbuf_animation_get_iter (animation->pixbuf, NULL);
69  else
70  if (gdk_pixbuf_animation_iter_advance (animation->frame, NULL))
71  gtk_widget_queue_draw (GTK_WIDGET (image));
72 
73  // shedule next frame
74  int delay = gdk_pixbuf_animation_iter_get_delay_time (animation->frame);
75  if (delay != -1)
76  animation->timeout_id = g_timeout_add (delay, ygtk_image_advance_frame_cb, data);
77  return FALSE;
78 }
79 
80 static void ygtk_image_set_animation (YGtkImage *image, GdkPixbufAnimation *pixbuf,
81  const char *error_msg)
82 {
83  ygtk_image_free_pixbuf (image);
84  gtk_widget_queue_resize (GTK_WIDGET (image));
85 
86  if (pixbuf) {
87  image->animated = TRUE;
88  image->animation = g_new0 (struct _YGtkImageAnimation, 1);
89  image->animation->pixbuf = pixbuf;
90  image->loaded = TRUE;
91  ygtk_image_advance_frame_cb (image);
92  }
93  else if (error_msg)
94  g_warning ("Couldn't load image - %s", error_msg);
95 }
96 
97 void ygtk_image_set_from_file (YGtkImage *image, const char *filename, gboolean anim)
98 {
99  GError *error = 0;
100  if (anim) {
101  GdkPixbufAnimation *pixbuf;
102  if (ygdk_mng_pixbuf_is_file_mng (filename))
103  pixbuf = ygdk_mng_pixbuf_new_from_file (filename, &error);
104  else
105  pixbuf = gdk_pixbuf_animation_new_from_file (filename, &error);
106  ygtk_image_set_animation (image, pixbuf, error ? error->message : "(undefined)");
107  }
108  else {
109  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, &error);
110  ygtk_image_set_pixbuf (image, pixbuf, error ? error->message : "(undefined)");
111  }
112 }
113 
114 void ygtk_image_set_from_pixbuf (YGtkImage *image, GdkPixbuf *pixbuf)
115 {
116  ygtk_image_set_pixbuf (image, pixbuf, NULL);
117 }
118 
119 static void ygtk_image_loaded_cb (GdkPixbufLoader *loader, YGtkImage *image)
120 {
121  if (image->animated) {
122  if (image->animation) {
123  // a new frame loaded -- just redraw the widget
124  if (gdk_pixbuf_animation_iter_on_currently_loading_frame
125  (image->animation->frame))
126  gtk_widget_queue_draw (GTK_WIDGET (image));
127  }
128  else {
129  GdkPixbufAnimation *pixbuf = gdk_pixbuf_loader_get_animation (loader);
130  g_object_ref (G_OBJECT (pixbuf));
131  ygtk_image_set_animation (image, pixbuf, "on block data reading callback");
132  }
133  }
134  else {
135  GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
136  g_object_ref (G_OBJECT (pixbuf));
137  ygtk_image_set_pixbuf (image, pixbuf, "on block data reading callback");
138  }
139 }
140 
141 void ygtk_image_set_from_data (YGtkImage *image, const guint8 *data, long data_size, gboolean anim)
142 {
143  GError *error = 0;
144  if (anim && ygdk_mng_pixbuf_is_data_mng (data, data_size)) {
145  GdkPixbufAnimation *pixbuf;
146  pixbuf = ygdk_mng_pixbuf_new_from_data (data, data_size, &error);
147  ygtk_image_set_animation (image, pixbuf, error ? error->message : "(undefined)");
148  }
149  else {
150  image->animated = anim;
151  GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
152  g_signal_connect (G_OBJECT (loader), "area-prepared",
153  G_CALLBACK (ygtk_image_loaded_cb), image);
154  if (!gdk_pixbuf_loader_write (loader, data, data_size, &error))
155  g_warning ("Could not load image from data blocks: %s", error->message);
156  gdk_pixbuf_loader_close (loader, &error);
157  }
158 }
159 
160 void ygtk_image_set_props (YGtkImage *image, YGtkImageAlign align, const gchar *alt_text)
161 {
162  image->align = align;
163  if (image->alt_text)
164  g_free (image->alt_text);
165  if (alt_text)
166  image->alt_text = g_strdup (alt_text);
167  gtk_widget_queue_draw (GTK_WIDGET (image));
168 }
169 
170 static void ygtk_image_size_request (GtkWidget *widget, GtkRequisition *requisition)
171 {
172  YGtkImage *image = YGTK_IMAGE (widget);
173  int width = 0, height = 0;
174  if (image->loaded) {
175  if (image->animated) {
176  width = gdk_pixbuf_animation_get_width (image->animation->pixbuf);
177  height = gdk_pixbuf_animation_get_height (image->animation->pixbuf);
178  }
179  else {
180  width = gdk_pixbuf_get_width (image->pixbuf);
181  height = gdk_pixbuf_get_height (image->pixbuf);
182  }
183  }
184  else if (image->alt_text) {
185  PangoLayout *layout;
186  layout = gtk_widget_create_pango_layout (widget, image->alt_text);
187  pango_layout_get_pixel_size (layout, &width, &height);
188  }
189  requisition->width = width;
190  requisition->height = height;
191 }
192 
193 static void
194 ygtk_image_get_preferred_width (GtkWidget *widget,
195  gint *minimal_width,
196  gint *natural_width)
197 {
198  GtkRequisition requisition;
199  ygtk_image_size_request (widget, &requisition);
200  *minimal_width = *natural_width = requisition.width;
201 }
202 
203 static void
204 ygtk_image_get_preferred_height (GtkWidget *widget,
205  gint *minimal_height,
206  gint *natural_height)
207 {
208  GtkRequisition requisition;
209  ygtk_image_size_request (widget, &requisition);
210  *minimal_height = *natural_height = requisition.height;
211 }
212 
213 static GdkPixbuf *ygtk_image_render_state (GtkWidget *widget, GdkPixbuf *pixbuf)
214 { // as in GtkImage
215  GtkIconSource *source = gtk_icon_source_new();
216  GtkStyleContext *style = gtk_widget_get_style_context (widget);
217  gtk_style_context_save (style);
218  gtk_style_context_set_state (style, gtk_widget_get_state_flags (widget));
219 
220  GdkPixbuf *rendered;
221  gtk_icon_source_set_pixbuf (source, pixbuf);
222  gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR);
223  gtk_icon_source_set_size_wildcarded (source, FALSE);
224  rendered = gtk_render_icon_pixbuf (style, source, (GtkIconSize)-1);
225 
226  gtk_style_context_restore (style);
227  gtk_icon_source_free (source);
228  return rendered;
229 }
230 
231 static gboolean ygtk_image_draw_event (GtkWidget *widget, cairo_t *cr)
232 {
233  YGtkImage *image = YGTK_IMAGE (widget);
234 
235  GtkRequisition req;
236  gtk_widget_get_preferred_size (widget, &req, NULL);
237  int width = gtk_widget_get_allocated_width(widget);
238  int height = gtk_widget_get_allocated_height(widget);
239 
240  if (!image->loaded) {
241  if (image->alt_text) {
242  // show alt text if no image was loaded
243  PangoLayout *layout;
244  layout = gtk_widget_create_pango_layout (widget, image->alt_text);
245 
246  int x, y;
247  x = (width - req.width) / 2;
248  y = (height - req.height) / 2;
249 
250  cairo_move_to (cr, x, y);
251  pango_cairo_show_layout (cr, layout);
252 
253  g_object_unref (layout);
254  }
255  return FALSE;
256  }
257 
258  GdkPixbuf *pixbuf;
259  if (image->animated)
260  pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (image->animation->frame);
261  else
262  pixbuf = image->pixbuf;
263 
264  gboolean needs_transform = gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_INSENSITIVE;
265  if (needs_transform)
266  pixbuf = ygtk_image_render_state (widget, pixbuf);
267  int x = 0, y = 0;
268  if (image->align == CENTER_IMAGE_ALIGN) {
269  x = (width - req.width) / 2;
270  y = (height - req.height) / 2;
271  }
272 
273  gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
274 
275  switch (image->align) {
276  case CENTER_IMAGE_ALIGN:
277  break;
278  case SCALE_IMAGE_ALIGN:
279  {
280  double scale_x = (double) gdk_pixbuf_get_width (pixbuf) / width;
281  double scale_y = (double) gdk_pixbuf_get_height (pixbuf) / height;
282  cairo_matrix_t matrix;
283  cairo_matrix_init_scale (&matrix, scale_x, scale_y);
284  cairo_pattern_set_matrix (cairo_get_source (cr), &matrix);
285  break;
286  }
287  case TILE_IMAGE_ALIGN:
288  cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
289  break;
290  }
291 
292  cairo_rectangle (cr, x, y, width, height);
293  cairo_fill (cr);
294 
295  if (needs_transform)
296  g_object_unref (G_OBJECT (pixbuf));
297  return FALSE;
298 }
299 
300 static void ygtk_image_state_flags_changed (GtkWidget *widget, GtkStateFlags old_flags)
301 {
302  // it seems like we need to force a redraw in gtk3 when state changes
303  gtk_widget_queue_draw (widget);
304 }
305 
306 GtkWidget* ygtk_image_new (void)
307 {
308  return g_object_new (YGTK_TYPE_IMAGE, NULL);
309 }
310 
311 static void ygtk_image_class_init (YGtkImageClass *klass)
312 {
313  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
314  widget_class->draw = ygtk_image_draw_event;
315  widget_class->get_preferred_width = ygtk_image_get_preferred_width;
316  widget_class->get_preferred_height = ygtk_image_get_preferred_height;
317  widget_class->destroy = ygtk_image_destroy;
318  widget_class->state_flags_changed = ygtk_image_state_flags_changed;
319 }
320