Rename few functions and replace fullscreen info with image overlay template string.
[geeqie.git] / src / image-overlay.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "main.h"
13 #include "image-overlay.h"
14
15 #include "collect.h"
16 #include "exif.h"
17 #include "filelist.h"
18 #include "image.h"
19 #include "img-view.h"
20 #include "layout.h"
21 #include "pixbuf-renderer.h"
22 #include "pixbuf_util.h"
23 #include "histogram.h"
24
25
26 /*
27  *----------------------------------------------------------------------------
28  * image overlay
29  *----------------------------------------------------------------------------
30  */
31
32 typedef struct _OverlayStateData OverlayStateData;
33 struct _OverlayStateData {
34         ImageWindow *imd;
35         ImageState changed_states;
36
37         gint show_info;
38         gint show_status;
39
40         gint ovl_info;
41
42         gint icon_time[IMAGE_OSD_COUNT];
43         gint icon_id[IMAGE_OSD_COUNT];
44
45         gint idle_id;
46         gint timer_id;
47         gulong destroy_id;
48 };
49
50
51 typedef struct _OSDIcon OSDIcon;
52 struct _OSDIcon {
53         gint reset;     /* reset on new image */
54         gint x;         /* x, y offset */
55         gint y;
56         gchar *key;     /* inline pixbuf */
57 };
58
59 static OSDIcon osd_icons[] = {
60         {  TRUE,   0,   0, NULL },                      /* none */
61         {  TRUE, -10, -10, NULL },                      /* auto rotated */
62         {  TRUE, -10, -10, NULL },                      /* user rotated */
63         {  TRUE, -40, -10, NULL },                      /* color embedded */
64         {  TRUE, -70, -10, NULL },                      /* first image */
65         {  TRUE, -70, -10, NULL },                      /* last image */
66         { FALSE, -70, -10, NULL },                      /* osd enabled */
67         { FALSE, 0, 0, NULL }
68 };
69
70 #define OSD_DATA "overlay-data"
71
72 #define OSD_INFO_X 10
73 #define OSD_INFO_Y -10
74
75 #define IMAGE_OSD_DEFAULT_DURATION 30
76
77 #define HISTOGRAM_HEIGHT 140
78
79 void set_default_image_overlay_template_string(ConfOptions *options)
80 {
81         if (options->image_overlay.common.template_string) g_free(options->image_overlay.common.template_string);
82         options->image_overlay.common.template_string = g_strdup(DEFAULT_OVERLAY_INFO);
83 }
84
85 /*
86  *----------------------------------------------------------------------------
87  * image histogram
88  *----------------------------------------------------------------------------
89  */
90
91
92 #define HIST_PREPARE(imd, lw)                          \
93        LayoutWindow *lw = NULL;                        \
94        if (imd)                                        \
95                lw = layout_find_by_image(imd);
96
97 void image_osd_histogram_onoff_toggle(ImageWindow *imd, gint x)
98 {
99         HIST_PREPARE(imd, lw)
100         if (lw)
101                 {
102                 lw->histogram_enabled = !!(x);
103                 if (lw->histogram_enabled && !lw->histogram)
104                         lw->histogram = histogram_new();
105                 }
106 }
107
108 gint image_osd_histogram_onoff_status(ImageWindow *imd)
109 {
110        HIST_PREPARE(imd, lw)
111        return lw ?  lw->histogram_enabled : FALSE;
112 }
113
114 void image_osd_histogram_chan_toggle(ImageWindow *imd)
115 {
116        HIST_PREPARE(imd, lw)
117        if (lw && lw->histogram)
118                histogram_set_channel(lw->histogram, (histogram_get_channel(lw->histogram) +1)%HCHAN_COUNT);
119 }
120
121 void image_osd_histogram_log_toggle(ImageWindow *imd)
122 {
123        HIST_PREPARE(imd,lw)
124        if (lw && lw->histogram)
125                histogram_set_mode(lw->histogram, !histogram_get_mode(lw->histogram));
126 }
127
128
129
130 static void image_osd_timer_schedule(OverlayStateData *osd);
131
132 static gchar *image_osd_mkinfo(const gchar *str, ImageWindow *imd, GHashTable *vars)
133 {
134         gchar delim = '%', imp = '|', sep[] = " - ";
135         gchar *start, *end;
136         gint pos, prev;
137         gint last;
138         gchar *name, *data;
139         GString *new;
140         gchar *ret;
141         ExifData *exif;
142
143         if (!str || !*str) return g_strdup("");
144
145         new = g_string_new(str);
146
147         exif = exif_read_fd(imd->image_fd);
148         prev = 0;
149         last = FALSE;
150
151         while (TRUE)
152                 {
153                 gint was_digit = 0;
154                 gint limit = 0;
155                 gchar *trunc = NULL;
156                 gchar *p;
157
158                 start = strchr(new->str, delim);
159                 if (!start)
160                         break;
161                 end = strchr(start+1, delim);
162                 if (!end)
163                         break;
164
165                 for (p = end; p > start; p--)
166                         {
167                         if (*p == ':' && was_digit)
168                                 {
169                                 trunc = p;
170                                 break;
171                                 }
172                         was_digit = g_ascii_isdigit(*p);
173                         }
174
175                 if (trunc) limit = atoi(trunc+1);
176
177                 name = g_strndup(start+1, ((limit > 0) ? trunc : end)-start-1);
178
179                 pos = start-new->str;
180                 data = g_strdup(g_hash_table_lookup(vars, name));
181                 if (data && strcmp(name, "zoom") == 0) imd->overlay_show_zoom = TRUE;
182                 if (!data && exif)
183                         data = exif_get_data_as_text(exif, name);
184                 if (data && *data && limit > 0 && strlen(data) > limit + 3)
185                         {
186                         gchar *new_data = g_strdup_printf("%-*.*s...", limit, limit, data);
187                         g_free(data);
188                         data = new_data;
189                         }
190
191                 g_string_erase(new, pos, end-start+1);
192                 if (data)
193                         g_string_insert(new, pos, data);
194                 if (pos-prev == 2 && new->str[pos-1] == imp)
195                         {
196                         g_string_erase(new, --pos, 1);
197                         if (last && data)
198                                 {
199                                 g_string_insert(new, pos, sep);
200                                 pos += strlen(sep);
201                                 }
202                         }
203
204                 prev = data ? pos+strlen(data)-1 : pos-1;
205                 last = data ? TRUE : last;
206                 g_free(name);
207                 g_free(data);
208                 }
209
210         /* search and destroy empty lines */
211         end = new->str;
212         while ((start = strchr(end, '\n')))
213                 {
214                 end = start;
215                 while (*++(end) == '\n')
216                         ;
217                 g_string_erase(new, start-new->str, end-start-1);
218                 }
219
220         g_strchomp(new->str);
221
222         ret = new->str;
223         g_string_free(new, FALSE);
224
225         return ret;
226 }
227
228 static GdkPixbuf *image_osd_info_render(ImageWindow *imd)
229 {
230         GdkPixbuf *pixbuf = NULL;
231         gint width, height;
232         PangoLayout *layout;
233         const gchar *name;
234         gchar *name_escaped;
235         gchar *text;
236         gchar *size;
237         gint n, t;
238         CollectionData *cd;
239         CollectInfo *info;
240         GdkPixbuf *imgpixbuf = NULL;
241         LayoutWindow *lw = NULL;
242         gint with_hist = 0;
243         gchar *ct;
244         gint w, h;
245         GHashTable *vars;
246
247         vars = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
248
249         name = image_get_name(imd);
250         if (name)
251                 {
252                 name_escaped = g_markup_escape_text(name, -1);
253                 }
254         else
255                 {
256                 name_escaped = NULL;
257                 }
258
259         cd = image_get_collection(imd, &info);
260         if (cd)
261                 {
262                 gchar *buf;
263
264                 t = g_list_length(cd->list);
265                 n = g_list_index(cd->list, info) + 1;
266                 buf = g_markup_escape_text((cd->name) ? cd->name : _("Untitled"), -1);
267                 ct = g_strdup_printf("<i>%s</i>\n", buf);
268                 g_free(buf);
269                 }
270         else
271                 {
272                 lw = layout_find_by_image(imd);
273                 if (lw)
274                         {
275                         if (lw->slideshow)
276                                 {
277                                 n = g_list_length(lw->slideshow->list_done);
278                                 t = n + g_list_length(lw->slideshow->list);
279                                 if (n == 0) n = t;
280                                 }
281                         else
282                                 {
283                                 t = layout_list_count(lw, NULL);
284                                 n = layout_list_get_index(lw, image_get_path(lw->image)) + 1;
285                                 }
286                         }
287                 else if (view_window_find_image(imd, &n, &t))
288                         {
289                         n++;
290                         }
291                 else
292                         {
293                         t = 1;
294                         n = 1;
295                         }
296
297                 if (n < 1) n = 1;
298                 if (t < 1) t = 1;
299
300                 ct = g_strdup("");
301                 }
302
303         size = text_from_size_abrev(imd->size);
304         if (!imd->unknown)
305                 {
306                 if (imd->delay_flip &&
307                     imd->il && imd->il->pixbuf &&
308                     image_get_pixbuf(imd) != imd->il->pixbuf)
309                         {
310                         w = gdk_pixbuf_get_width(imd->il->pixbuf);
311                         h = gdk_pixbuf_get_height(imd->il->pixbuf);
312                         imgpixbuf = imd->il->pixbuf;
313                         }
314                 else
315                         {
316                         pixbuf_renderer_get_image_size(PIXBUF_RENDERER(imd->pr), &w, &h);
317                         imgpixbuf = (PIXBUF_RENDERER(imd->pr))->pixbuf;
318                         }
319                 if (!lw)
320                         lw = layout_find_by_image(imd);
321
322                 if (imgpixbuf && lw && lw->histogram && lw->histogram_enabled
323                               && (!imd->il || imd->il->done))
324                         with_hist=1;
325
326                 g_hash_table_insert(vars, "width", g_strdup_printf("%d", w));
327                 g_hash_table_insert(vars, "height", g_strdup_printf("%d", h));
328                 g_hash_table_insert(vars, "res", g_strdup_printf("%d Ã— %d", w, h));
329                 }
330
331         g_hash_table_insert(vars, "collection", g_strdup(ct));
332         g_hash_table_insert(vars, "number", g_strdup_printf("%d", n));
333         g_hash_table_insert(vars, "total", g_strdup_printf("%d", t));
334         g_hash_table_insert(vars, "name", g_strdup(name_escaped));
335         g_hash_table_insert(vars, "date", g_strdup(text_from_time(imd->mtime)));
336         g_hash_table_insert(vars, "size", g_strdup(size));
337         g_hash_table_insert(vars, "zoom", image_zoom_get_as_text(imd));
338
339         if (!name_escaped)
340                 {
341                 text = g_strdup_printf(_("Untitled"));
342                 }
343         else
344                 {
345                 text = image_osd_mkinfo(options->image_overlay.common.template_string, imd, vars);
346                 }
347
348         g_free(size);
349         g_free(ct);
350         g_free(name_escaped);
351         g_hash_table_destroy(vars);
352
353         {
354         FileData *fd = image_get_fd(imd);
355
356         if (fd) /* fd may be null after file deletion */
357                 {
358                 gint active_marks = 0;
359                 gint mark;
360                 gchar *text2;
361
362                 for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
363                         {
364                         active_marks += fd->marks[mark];
365                         }
366
367
368                 if (active_marks > 0)
369                         {
370                         GString *buf = g_string_sized_new(FILEDATA_MARKS_SIZE * 2);
371
372                         for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
373                                 {
374                                 g_string_append_printf(buf, fd->marks[mark] ? " <span background='#FF00FF'>%c</span>" : " %c", '1' + mark);
375                                 }
376
377                         if (*text)
378                                 text2 = g_strdup_printf("%s\n%s", text, buf->str);
379                         else
380                                 text2 = g_strdup(buf->str);
381                         g_string_free(buf, TRUE);
382                         g_free(text);
383                         text = text2;
384                         }
385
386                 if (with_hist)
387                         {
388                         if (*text)
389                                 text2 = g_strdup_printf("%s\n%s", text, histogram_label(lw->histogram));
390                         else
391                                 text2 = g_strdup(histogram_label(lw->histogram));
392                         g_free(text);
393                         text = text2;
394                         }
395                 }
396         }
397
398         layout = gtk_widget_create_pango_layout(imd->pr, NULL);
399         pango_layout_set_markup(layout, text, -1);
400         g_free(text);
401
402         pango_layout_get_pixel_size(layout, &width, &height);
403         /* with empty text width is set to 0, but not height) */
404         if (width == 0)
405                 height = 0;
406         else if (height == 0)
407                 width = 0;
408         if (width > 0) width += 10;
409         if (height > 0) height += 10;
410
411         if (with_hist)
412                 {
413                 histogram_read(lw->histogram, imgpixbuf);
414                 if (width < 266) width = 266;
415                 height += HISTOGRAM_HEIGHT + 5;
416                 }
417
418         if (width > 0 && height > 0)
419                 {
420                 /* TODO: make osd color configurable --Zas */
421                 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
422                 pixbuf_set_rect_fill(pixbuf, 3, 3, width-6, height-6, 240, 240, 240, 210);
423                 pixbuf_set_rect(pixbuf, 0, 0, width, height, 240, 240, 240, 80, 1, 1, 1, 1);
424                 pixbuf_set_rect(pixbuf, 1, 1, width-2, height-2, 240, 240, 240, 130, 1, 1, 1, 1);
425                 pixbuf_set_rect(pixbuf, 2, 2, width-4, height-4, 240, 240, 240, 180, 1, 1, 1, 1);
426                 pixbuf_pixel_set(pixbuf, 0, 0, 0, 0, 0, 0);
427                 pixbuf_pixel_set(pixbuf, width - 1, 0, 0, 0, 0, 0);
428                 pixbuf_pixel_set(pixbuf, 0, height - 1, 0, 0, 0, 0);
429                 pixbuf_pixel_set(pixbuf, width - 1, height - 1, 0, 0, 0, 0);
430
431                 if (with_hist)
432                         histogram_draw(lw->histogram, pixbuf, 5, height - HISTOGRAM_HEIGHT - 5 , width - 10, HISTOGRAM_HEIGHT);
433
434                 pixbuf_draw_layout(pixbuf, layout, imd->pr, 5, 5, 0, 0, 0, 255);
435         }
436
437         g_object_unref(G_OBJECT(layout));
438
439         return pixbuf;
440 }
441
442 static GdkPixbuf *image_osd_icon_pixbuf(ImageOSDFlag flag)
443 {
444         static GdkPixbuf **icons = NULL;
445         GdkPixbuf *icon = NULL;
446
447         if (!icons) icons = g_new0(GdkPixbuf *, IMAGE_OSD_COUNT);
448         if (icons[flag]) return icons[flag];
449
450         if (osd_icons[flag].key)
451                 {
452                 icon = pixbuf_inline(osd_icons[flag].key);
453                 }
454
455         if (!icon)
456                 {
457                 icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24);
458                 pixbuf_set_rect_fill(icon, 1, 1, 22, 22, 255, 255, 255, 200);
459                 pixbuf_set_rect(icon, 0, 0, 24, 24, 0, 0, 0, 128, 1, 1, 1, 1);
460                 switch (flag)
461                         {
462                         case IMAGE_OSD_ROTATE_AUTO:
463                                 pixbuf_set_rect(icon, 3, 8, 11, 12,
464                                                 0, 0, 0, 255,
465                                                 3, 0, 3, 0);
466                                 pixbuf_draw_triangle(icon, 14, 3, 6, 12,
467                                                      20, 9, 14, 15, 14, 3,
468                                                      0, 0, 0, 255);
469                                 break;
470                         case IMAGE_OSD_ROTATE_USER:
471                                 break;
472                         case IMAGE_OSD_COLOR:
473                                 pixbuf_set_rect_fill(icon, 3, 3, 18, 6, 200, 0, 0, 255);
474                                 pixbuf_set_rect_fill(icon, 3, 9, 18, 6, 0, 200, 0, 255);
475                                 pixbuf_set_rect_fill(icon, 3, 15, 18, 6, 0, 0, 200, 255);
476                                 break;
477                         case IMAGE_OSD_FIRST:
478                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 3, 0);
479                                 pixbuf_draw_triangle(icon, 6, 5, 12, 6,
480                                                      12, 5, 18, 11, 6, 11,
481                                                      0, 0, 0, 255);
482                                 break;
483                         case IMAGE_OSD_LAST:
484                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 0, 3);
485                                 pixbuf_draw_triangle(icon, 6, 12, 12, 6,
486                                                      12, 18, 6, 12, 18, 12,
487                                                      0, 0, 0, 255);
488                                 break;
489                         case IMAGE_OSD_ICON:
490                                 pixbuf_set_rect_fill(icon, 11, 3, 3, 12, 0, 0, 0, 255);
491                                 pixbuf_set_rect_fill(icon, 11, 17, 3, 3, 0, 0, 0, 255);
492                                 break;
493                         default:
494                                 break;
495                         }
496                 }
497
498         icons[flag] = icon;
499
500         return icon;
501 }
502
503 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
504 {
505         GdkPixbuf *pixbuf;
506
507         if (osd->icon_id[flag]) return;
508
509         pixbuf = image_osd_icon_pixbuf(flag);
510         if (!pixbuf) return;
511
512         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
513                                                osd_icons[flag].x, osd_icons[flag].y,
514                                                TRUE, FALSE);
515 }
516
517 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
518 {
519         if (osd->icon_id[flag])
520                 {
521                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
522                 osd->icon_id[flag] = 0;
523                 }
524 }
525
526 static gint image_osd_update_cb(gpointer data)
527 {
528         OverlayStateData *osd = data;
529
530         osd->imd->overlay_show_zoom = FALSE;
531
532         if (osd->show_info)
533                 {
534                 if (osd->changed_states & IMAGE_STATE_IMAGE)
535                         {
536                         GdkPixbuf *pixbuf;
537
538                         pixbuf = image_osd_info_render(osd->imd);
539                         if (pixbuf)
540                                 {
541                                 if (osd->ovl_info == 0)
542                                         {
543                                         osd->ovl_info = image_overlay_add(osd->imd, pixbuf,
544                                                                           OSD_INFO_X, OSD_INFO_Y, TRUE, FALSE);
545                                         }
546                                 else
547                                         {
548                                         image_overlay_set(osd->imd, osd->ovl_info, pixbuf, OSD_INFO_X, OSD_INFO_Y);
549                                         }
550                                 g_object_unref(pixbuf);
551                                 }
552                         else if (osd->ovl_info)
553                                 {
554                                 image_overlay_remove(osd->imd, osd->ovl_info);
555                                 osd->ovl_info = 0;
556                                 }
557                         }
558                 }
559         else
560                 {
561                 if (osd->ovl_info)
562                         {
563                         image_overlay_remove(osd->imd, osd->ovl_info);
564                         osd->ovl_info = 0;
565                         }
566                 }
567
568         if (osd->show_status)
569                 {
570                 gint i;
571
572                 if (osd->changed_states & IMAGE_STATE_IMAGE)
573                         {
574                         for (i = 0; i < IMAGE_OSD_COUNT; i++)
575                                 {
576                                 if (osd_icons[i].reset) osd->icon_time[i] = 0;
577                                 }
578                         }
579
580                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
581                         {
582                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
583                         image_osd_timer_schedule(osd);
584                         }
585
586                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
587                         {
588                         gint n = 0;
589
590                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
591                                 {
592                                 n = 1;
593                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
594                                 }
595
596                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
597                         image_osd_timer_schedule(osd);
598                         }
599
600                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
601                         {
602                         if (osd->icon_time[i] > 0)
603                                 {
604                                 image_osd_icon_show(osd, i);
605                                 }
606                         else
607                                 {
608                                 image_osd_icon_hide(osd, i);
609                                 }
610                         }
611                 }
612         else
613                 {
614                 gint i;
615
616                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
617                         {
618                         image_osd_icon_hide(osd, i);
619                         }
620                 }
621
622         if (osd->imd->il && osd->imd->il->done)
623                 osd->changed_states = IMAGE_STATE_NONE;
624         osd->idle_id = -1;
625         return FALSE;
626 }
627
628 static void image_osd_update_schedule(OverlayStateData *osd, gint force)
629 {
630         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
631
632         if (osd->idle_id == -1)
633                 {
634                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
635                 }
636 }
637
638 void image_osd_update(ImageWindow *imd)
639 {
640         OverlayStateData *osd;
641
642         if (!imd) return;
643
644         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
645         if (!osd) return;
646
647         image_osd_update_schedule(osd, TRUE);
648 }
649
650 static gint image_osd_timer_cb(gpointer data)
651 {
652         OverlayStateData *osd = data;
653         gint done = TRUE;
654         gint changed = FALSE;
655         gint i;
656
657         for (i = 0; i < IMAGE_OSD_COUNT; i++)
658                 {
659                 if (osd->icon_time[i] > 1)
660                         {
661                         osd->icon_time[i]--;
662                         if (osd->icon_time[i] < 2)
663                                 {
664                                 osd->icon_time[i] = 0;
665                                 changed = TRUE;
666                                 }
667                         else
668                                 {
669                                 done = FALSE;
670                                 }
671                         }
672                 }
673
674         if (changed) image_osd_update_schedule(osd, FALSE);
675
676         if (done)
677                 {
678                 osd->timer_id = -1;
679                 return FALSE;
680                 }
681
682         return TRUE;
683 }
684
685 static void image_osd_timer_schedule(OverlayStateData *osd)
686 {
687         if (osd->timer_id == -1)
688                 {
689                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
690                 }
691 }
692
693 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
694 {
695         OverlayStateData *osd = data;
696
697         osd->changed_states |= state;
698         image_osd_update_schedule(osd, FALSE);
699 }
700
701 static void image_osd_free(OverlayStateData *osd)
702 {
703         if (!osd) return;
704
705         if (osd->idle_id != -1) g_source_remove(osd->idle_id);
706         if (osd->timer_id != -1) g_source_remove(osd->timer_id);
707
708         if (osd->imd)
709                 {
710                 gint i;
711
712                 g_object_set_data(G_OBJECT(osd->imd->pr), "IMAGE_OVERLAY_DATA", NULL);
713                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
714
715                 image_set_state_func(osd->imd, NULL, NULL);
716                 image_overlay_remove(osd->imd, osd->ovl_info);
717
718                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
719                         {
720                         image_osd_icon_hide(osd, i);
721                         }
722                 }
723
724         g_free(osd);
725 }
726
727 static void image_osd_remove(ImageWindow *imd)
728 {
729         OverlayStateData *osd;
730
731         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
732         image_osd_free(osd);
733 }
734
735 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
736 {
737         OverlayStateData *osd = data;
738
739         osd->imd = NULL;
740         image_osd_free(osd);
741 }
742
743 static void image_osd_enable(ImageWindow *imd, gint info, gint status)
744 {
745         OverlayStateData *osd;
746
747         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
748         if (!osd)
749                 {
750                 osd = g_new0(OverlayStateData, 1);
751                 osd->imd = imd;
752                 osd->idle_id = -1;
753                 osd->timer_id = -1;
754
755                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
756                                                    G_CALLBACK(image_osd_destroy_cb), osd);
757                 g_object_set_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA", osd);
758
759                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
760                 }
761
762         if (osd->show_info != info ||
763             osd->show_status != status)
764                 {
765                 osd->show_info = info;
766                 osd->show_status = status;
767
768                 image_osd_update_schedule(osd, TRUE);
769                 }
770 }
771
772 void image_osd_set(ImageWindow *imd, gint info, gint status)
773 {
774         if (!imd) return;
775
776         if (!info && !status)
777                 {
778                 image_osd_remove(imd);
779                 return;
780                 }
781
782         image_osd_enable(imd, info, status);
783 }
784
785 gint image_osd_get(ImageWindow *imd, gint *info, gint *status)
786 {
787         OverlayStateData *osd;
788
789         if (!imd) return FALSE;
790
791         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
792         if (!osd) return FALSE;
793
794         if (info) *info = osd->show_info;
795         if (status) *status = osd->show_status;
796
797         return TRUE;
798 }
799
800 /* duration:
801     0 = hide
802     1 = show
803    2+ = show for duration tenths of a second
804    -1 = use default duration
805  */
806 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
807 {
808         OverlayStateData *osd;
809
810         if (!imd) return;
811
812         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
813         if (!osd) return;
814
815         if (flag < IMAGE_OSD_NONE || flag >= IMAGE_OSD_COUNT) return;
816         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
817         if (duration > 1) duration += 1;
818
819         osd->icon_time[flag] = duration;
820
821         image_osd_update_schedule(osd, FALSE);
822         image_osd_timer_schedule(osd);
823 }