libyui-gtk  2.44.5
 All Classes Functions
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 gboolean ygtk_image_draw_event (GtkWidget *widget, cairo_t *cr)
214 {
215  YGtkImage *image = YGTK_IMAGE (widget);
216 
217  GtkRequisition req;
218  gtk_widget_get_preferred_size (widget, &req, NULL);
219  int width = gtk_widget_get_allocated_width(widget);
220  int height = gtk_widget_get_allocated_height(widget);
221 
222  if (!image->loaded) {
223  if (image->alt_text) {
224  // show alt text if no image was loaded
225  PangoLayout *layout;
226  layout = gtk_widget_create_pango_layout (widget, image->alt_text);
227 
228  int x, y;
229  x = (width - req.width) / 2;
230  y = (height - req.height) / 2;
231 
232  cairo_move_to (cr, x, y);
233  pango_cairo_show_layout (cr, layout);
234 
235  g_object_unref (layout);
236  }
237  return FALSE;
238  }
239 
240  GdkPixbuf *pixbuf;
241  if (image->animated)
242  pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (image->animation->frame);
243  else
244  pixbuf = image->pixbuf;
245 
246  int x = 0, y = 0;
247  if (image->align == CENTER_IMAGE_ALIGN) {
248  x = (width - req.width) / 2;
249  y = (height - req.height) / 2;
250  }
251 
252  gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
253 
254  switch (image->align) {
255  case CENTER_IMAGE_ALIGN:
256  break;
257  case SCALE_IMAGE_ALIGN:
258  {
259  double scale_x = (double) gdk_pixbuf_get_width (pixbuf) / width;
260  double scale_y = (double) gdk_pixbuf_get_height (pixbuf) / height;
261  cairo_matrix_t matrix;
262  cairo_matrix_init_scale (&matrix, scale_x, scale_y);
263  cairo_pattern_set_matrix (cairo_get_source (cr), &matrix);
264  break;
265  }
266  case TILE_IMAGE_ALIGN:
267  cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
268  break;
269  }
270 
271  cairo_rectangle (cr, x, y, width, height);
272  cairo_fill (cr);
273 
274  return FALSE;
275 }
276 
277 static void ygtk_image_state_flags_changed (GtkWidget *widget, GtkStateFlags old_flags)
278 {
279  // it seems like we need to force a redraw in gtk3 when state changes
280  gtk_widget_queue_draw (widget);
281 }
282 
283 GtkWidget* ygtk_image_new (void)
284 {
285  return g_object_new (YGTK_TYPE_IMAGE, NULL);
286 }
287 
288 static void ygtk_image_class_init (YGtkImageClass *klass)
289 {
290  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
291  widget_class->draw = ygtk_image_draw_event;
292  widget_class->get_preferred_width = ygtk_image_get_preferred_width;
293  widget_class->get_preferred_height = ygtk_image_get_preferred_height;
294  widget_class->destroy = ygtk_image_destroy;
295  widget_class->state_flags_changed = ygtk_image_state_flags_changed;
296 }
297