Fix #678: The image overlay should show the full image path, not just the filename
[geeqie.git] / src / image-overlay.c
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "image-overlay.h"
24
25 #include "filedata.h"
26 #include "histogram.h"
27 #include "image.h"
28 #include "img-view.h"
29 #include "layout.h"
30 #include "osd.h"
31 #include "pixbuf-renderer.h"
32 #include "pixbuf_util.h"
33 #include "ui_fileops.h"
34 #include "image-load.h"
35
36 /*
37  *----------------------------------------------------------------------------
38  * image overlay
39  *----------------------------------------------------------------------------
40  */
41
42
43 typedef struct _OverlayStateData OverlayStateData;
44 struct _OverlayStateData {
45         ImageWindow *imd;
46         ImageState changed_states;
47         NotifyType notify;
48
49         Histogram *histogram;
50
51         OsdShowFlags show;
52         OverlayRendererFlags origin;
53
54         gint ovl_info;
55
56         gint x;
57         gint y;
58
59         gint icon_time[IMAGE_OSD_COUNT];
60         gint icon_id[IMAGE_OSD_COUNT];
61
62         guint idle_id; /* event source id */
63         guint timer_id; /* event source id */
64         gulong destroy_id;
65 };
66
67
68 typedef struct _OSDIcon OSDIcon;
69 struct _OSDIcon {
70         gboolean reset; /* reset on new image */
71         gint x;         /* x, y offset */
72         gint y;
73         gchar *key;     /* inline pixbuf */
74 };
75
76 static OSDIcon osd_icons[] = {
77         {  TRUE,   0,   0, NULL },                      /* none */
78         {  TRUE, -10, -10, NULL },                      /* auto rotated */
79         {  TRUE, -10, -10, NULL },                      /* user rotated */
80         {  TRUE, -40, -10, NULL },                      /* color embedded */
81         {  TRUE, -70, -10, NULL },                      /* first image */
82         {  TRUE, -70, -10, NULL },                      /* last image */
83         { FALSE, -70, -10, NULL },                      /* osd enabled */
84         { FALSE, 0, 0, NULL }
85 };
86
87 #define OSD_DATA "overlay-data"
88
89 #define IMAGE_OSD_DEFAULT_DURATION 30
90
91 #define HISTOGRAM_HEIGHT 140
92 #define HISTOGRAM_WIDTH  256
93
94 static void image_osd_timer_schedule(OverlayStateData *osd);
95
96 void set_image_overlay_template_string(gchar **template_string, const gchar *value)
97 {
98         g_assert(template_string);
99
100         g_free(*template_string);
101         *template_string = g_strdup(value);
102 }
103
104
105 void set_default_image_overlay_template_string(gchar **template_string)
106 {
107         set_image_overlay_template_string(template_string, DEFAULT_OVERLAY_INFO);
108 }
109
110 void set_image_overlay_font_string(gchar **font_string, const gchar *value)
111 {
112         g_assert(font_string);
113
114         g_free(*font_string);
115         *font_string = g_strdup(value);
116 }
117
118 static OverlayStateData *image_get_osd_data(ImageWindow *imd)
119 {
120         OverlayStateData *osd;
121
122         if (!imd) return NULL;
123
124         g_assert(imd->pr);
125
126         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
127         return osd;
128 }
129
130 static void image_set_osd_data(ImageWindow *imd, OverlayStateData *osd)
131 {
132         g_assert(imd);
133         g_assert(imd->pr);
134         g_object_set_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA", osd);
135 }
136
137 /*
138  *----------------------------------------------------------------------------
139  * image histogram
140  *----------------------------------------------------------------------------
141  */
142
143
144 void image_osd_histogram_toggle_channel(ImageWindow *imd)
145 {
146         OverlayStateData *osd = image_get_osd_data(imd);
147
148         if (!osd || !osd->histogram) return;
149
150         histogram_toggle_channel(osd->histogram);
151         image_osd_update(imd);
152 }
153
154 void image_osd_histogram_toggle_mode(ImageWindow *imd)
155 {
156         OverlayStateData *osd = image_get_osd_data(imd);
157
158         if (!osd || !osd->histogram) return;
159
160         histogram_toggle_mode(osd->histogram);
161         image_osd_update(imd);
162 }
163
164 void image_osd_histogram_set_channel(ImageWindow *imd, gint chan)
165 {
166         OverlayStateData *osd = image_get_osd_data(imd);
167
168         if (!osd || !osd->histogram) return;
169
170         histogram_set_channel(osd->histogram, chan);
171         image_osd_update(imd);
172 }
173
174 void image_osd_histogram_set_mode(ImageWindow *imd, gint mode)
175 {
176         OverlayStateData *osd = image_get_osd_data(imd);
177
178         if (!osd || !osd->histogram) return;
179
180         histogram_set_mode(osd->histogram, mode);
181         image_osd_update(imd);
182 }
183
184 gint image_osd_histogram_get_channel(ImageWindow *imd)
185 {
186         OverlayStateData *osd = image_get_osd_data(imd);
187
188         if (!osd || !osd->histogram) return HCHAN_DEFAULT;
189
190         return histogram_get_channel(osd->histogram);
191 }
192
193 gint image_osd_histogram_get_mode(ImageWindow *imd)
194 {
195         OverlayStateData *osd = image_get_osd_data(imd);
196
197         if (!osd || !osd->histogram) return 0;
198
199         return histogram_get_mode(osd->histogram);
200 }
201
202 void image_osd_toggle(ImageWindow *imd)
203 {
204         OsdShowFlags show;
205
206         if (!imd) return;
207
208         show = image_osd_get(imd);
209         if (show == OSD_SHOW_NOTHING)
210                 {
211                 image_osd_set(imd, OSD_SHOW_INFO | OSD_SHOW_STATUS);
212                 return;
213                 }
214         else
215                 {
216                 if (show & OSD_SHOW_GUIDELINES)
217                         {
218                         image_osd_set(imd, OSD_SHOW_NOTHING);
219                         }
220                 else if (show & OSD_SHOW_HISTOGRAM)
221                         {
222                         image_osd_set(imd, OSD_SHOW_GUIDELINES);
223                         image_osd_set(imd, show | ~OSD_SHOW_HISTOGRAM);
224                         }
225                 else
226                         {
227                         image_osd_set(imd, show | OSD_SHOW_HISTOGRAM);
228                         }
229                 }
230 }
231
232 static GdkPixbuf *image_osd_info_render(OverlayStateData *osd)
233 {
234         GdkPixbuf *pixbuf = NULL;
235         gint width, height;
236         PangoLayout *layout;
237         const gchar *name;
238         gchar *text;
239         gboolean with_hist;
240         const HistMap *histmap = NULL;
241         ImageWindow *imd = osd->imd;
242         FileData *fd = image_get_fd(imd);
243         PangoFontDescription *font_desc;
244
245         if (!fd) return NULL;
246
247         name = image_get_name(imd);
248         if (name)
249                 {
250                 gint n, t;
251                 CollectionData *cd;
252                 CollectInfo *info;
253                 GHashTable *vars;
254
255                 vars = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
256
257                 cd = image_get_collection(imd, &info);
258                 if (cd)
259                         {
260                         t = g_list_length(cd->list);
261                         n = g_list_index(cd->list, info) + 1;
262                         if (cd->name)
263                                 {
264                                 if (file_extension_match(cd->name, GQ_COLLECTION_EXT))
265                                         osd_template_insert(vars, "collection", remove_extension_from_path(cd->name), OSDT_FREE);
266                                 else
267                                         osd_template_insert(vars, "collection", cd->name, OSDT_NONE);
268                                 }
269                         else
270                                 {
271                                 osd_template_insert(vars, "collection", _("Untitled"), OSDT_NONE);
272                                 }
273                         }
274                 else
275                         {
276                         LayoutWindow *lw = layout_find_by_image(imd);
277                         if (lw)
278                                 {
279                                 if (lw->slideshow)
280                                         {
281                                         n = g_list_length(lw->slideshow->list_done);
282                                         t = n + g_list_length(lw->slideshow->list);
283                                         if (n == 0) n = t;
284                                         }
285                                 else
286                                         {
287                                         t = layout_list_count(lw, NULL);
288                                         n = layout_list_get_index(lw, image_get_fd(lw->image)) + 1;
289                                         }
290                                 }
291                         else if (view_window_find_image(imd, &n, &t))
292                                 {
293                                 n++;
294                                 }
295                         else
296                                 {
297                                 t = 1;
298                                 n = 1;
299                                 }
300
301                         if (n < 1) n = 1;
302                         if (t < 1) t = 1;
303
304                         osd_template_insert(vars, "collection", NULL, OSDT_NONE);
305                         }
306
307                 osd_template_insert(vars, "number", g_strdup_printf("%d", n), OSDT_NO_DUP);
308                 osd_template_insert(vars, "total", g_strdup_printf("%d", t), OSDT_NO_DUP);
309                 osd_template_insert(vars, "name", (gchar *) name, OSDT_NONE);
310                 osd_template_insert(vars, "path", (gchar *) image_get_path(imd), OSDT_NONE);
311                 osd_template_insert(vars, "date", imd->image_fd ? ((gchar *) text_from_time(imd->image_fd->date)) : "", OSDT_NONE);
312                 osd_template_insert(vars, "size", imd->image_fd ? (text_from_size_abrev(imd->image_fd->size)) : g_strdup(""), OSDT_FREE);
313                 osd_template_insert(vars, "zoom", image_zoom_get_as_text(imd), OSDT_FREE);
314
315                 if (!imd->unknown)
316                         {
317                         gint w, h;
318                         GdkPixbuf *load_pixbuf = image_loader_get_pixbuf(imd->il);
319
320                         if (imd->delay_flip &&
321                             imd->il && load_pixbuf &&
322                             image_get_pixbuf(imd) != load_pixbuf)
323                                 {
324                                 w = gdk_pixbuf_get_width(load_pixbuf);
325                                 h = gdk_pixbuf_get_height(load_pixbuf);
326                                 }
327                         else
328                                 {
329                                 image_get_image_size(imd, &w, &h);
330                                 }
331
332
333                         osd_template_insert(vars, "width", g_strdup_printf("%d", w), OSDT_NO_DUP);
334                         osd_template_insert(vars, "height", g_strdup_printf("%d", h), OSDT_NO_DUP);
335                         osd_template_insert(vars, "res", g_strdup_printf("%d Ã— %d", w, h), OSDT_FREE);
336                         }
337                 else
338                         {
339                         osd_template_insert(vars, "width", NULL, OSDT_NONE);
340                         osd_template_insert(vars, "height", NULL, OSDT_NONE);
341                         osd_template_insert(vars, "res", NULL, OSDT_NONE);
342                         }
343
344                 text = image_osd_mkinfo(options->image_overlay.template_string, imd->image_fd, vars);
345                 g_hash_table_destroy(vars);
346
347         } else {
348                 /* When does this occur ?? */
349                 text = g_markup_escape_text(_("Untitled"), -1);
350         }
351
352         with_hist = ((osd->show & OSD_SHOW_HISTOGRAM) && osd->histogram);
353         if (with_hist)
354                 {
355                 histmap = histmap_get(imd->image_fd);
356                 if (!histmap)
357                         {
358                         histmap_start_idle(imd->image_fd);
359                         with_hist = FALSE;
360                         }
361                 }
362
363
364         {
365                 gint active_marks = 0;
366                 gint mark;
367                 gchar *text2;
368
369                 for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
370                         {
371                         active_marks += file_data_get_mark(fd, mark);
372                         }
373
374                 if (active_marks > 0)
375                         {
376                         GString *buf = g_string_sized_new(FILEDATA_MARKS_SIZE * 2);
377
378                         for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
379                                 {
380                                 g_string_append_printf(buf, file_data_get_mark(fd, mark) ? " <span background='#FF00FF'>%c</span>" : " %c", '1' + (mark < 9 ? mark : -1) );
381                                 }
382
383                         if (*text)
384                                 text2 = g_strdup_printf("%s\n%s", text, buf->str);
385                         else
386                                 text2 = g_strdup(buf->str);
387                         g_string_free(buf, TRUE);
388                         g_free(text);
389                         text = text2;
390                         }
391
392                 if (with_hist)
393                         {
394                         gchar *escaped_histogram_label = g_markup_escape_text(histogram_label(osd->histogram), -1);
395                         if (*text)
396                                 text2 = g_strdup_printf("%s\n%s", text, escaped_histogram_label);
397                         else
398                                 text2 = g_strdup(escaped_histogram_label);
399                         g_free(escaped_histogram_label);
400                         g_free(text);
401                         text = text2;
402                         }
403         }
404
405         font_desc = pango_font_description_from_string(options->image_overlay.font);
406         layout = gtk_widget_create_pango_layout(imd->pr, NULL);
407         pango_layout_set_font_description(layout, font_desc);
408
409         pango_layout_set_markup(layout, text, -1);
410         g_free(text);
411
412         pango_layout_get_pixel_size(layout, &width, &height);
413         /* with empty text width is set to 0, but not height) */
414         if (width == 0)
415                 height = 0;
416         else if (height == 0)
417                 width = 0;
418         if (width > 0) width += 10;
419         if (height > 0) height += 10;
420
421         if (with_hist)
422                 {
423                 if (width < HISTOGRAM_WIDTH + 10) width = HISTOGRAM_WIDTH + 10;
424                 height += HISTOGRAM_HEIGHT + 5;
425                 }
426
427         if (width > 0 && height > 0)
428                 {
429                 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
430                 pixbuf_set_rect_fill(pixbuf, 3, 3, width-6, height-6, options->image_overlay.background_red, options->image_overlay.background_green,
431                                                                                                                         options->image_overlay.background_blue, options->image_overlay.background_alpha);
432                 pixbuf_set_rect(pixbuf, 0, 0, width, height, 240, 240, 240, 80, 1, 1, 1, 1);
433                 pixbuf_set_rect(pixbuf, 1, 1, width-2, height-2, 240, 240, 240, 130, 1, 1, 1, 1);
434                 pixbuf_set_rect(pixbuf, 2, 2, width-4, height-4, 240, 240, 240, 180, 1, 1, 1, 1);
435                 pixbuf_pixel_set(pixbuf, 0, 0, 0, 0, 0, 0);
436                 pixbuf_pixel_set(pixbuf, width - 1, 0, 0, 0, 0, 0);
437                 pixbuf_pixel_set(pixbuf, 0, height - 1, 0, 0, 0, 0);
438                 pixbuf_pixel_set(pixbuf, width - 1, height - 1, 0, 0, 0, 0);
439
440                 if (with_hist)
441                         {
442                         gint x = 5;
443                         gint y = height - HISTOGRAM_HEIGHT - 5;
444                         gint w = width - 10;
445
446                         pixbuf_set_rect_fill(pixbuf, x, y, w, HISTOGRAM_HEIGHT, 220, 220, 220, 210);
447                         histogram_draw(osd->histogram, histmap, pixbuf, x, y, w, HISTOGRAM_HEIGHT);
448                         }
449                 pixbuf_draw_layout(pixbuf, layout, imd->pr, 5, 5, options->image_overlay.text_red, options->image_overlay.text_green,
450                                                                                                                         options->image_overlay.text_blue, options->image_overlay.text_alpha);
451         }
452
453         g_object_unref(G_OBJECT(layout));
454
455         return pixbuf;
456 }
457
458 static GdkPixbuf *image_osd_icon_pixbuf(ImageOSDFlag flag)
459 {
460         static GdkPixbuf **icons = NULL;
461         GdkPixbuf *icon = NULL;
462
463         if (!icons) icons = g_new0(GdkPixbuf *, IMAGE_OSD_COUNT);
464         if (icons[flag]) return icons[flag];
465
466         if (osd_icons[flag].key)
467                 {
468                 icon = pixbuf_inline(osd_icons[flag].key);
469                 }
470
471         if (!icon)
472                 {
473                 icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24);
474                 pixbuf_set_rect_fill(icon, 1, 1, 22, 22, 255, 255, 255, 200);
475                 pixbuf_set_rect(icon, 0, 0, 24, 24, 0, 0, 0, 128, 1, 1, 1, 1);
476                 switch (flag)
477                         {
478                         case IMAGE_OSD_ROTATE_AUTO:
479                                 pixbuf_set_rect(icon, 3, 8, 11, 12,
480                                                 0, 0, 0, 255,
481                                                 3, 0, 3, 0);
482                                 pixbuf_draw_triangle(icon, 14, 3, 6, 12,
483                                                      20, 9, 14, 15, 14, 3,
484                                                      0, 0, 0, 255);
485                                 break;
486                         case IMAGE_OSD_ROTATE_USER:
487                                 break;
488                         case IMAGE_OSD_COLOR:
489                                 pixbuf_set_rect_fill(icon, 3, 3, 18, 6, 200, 0, 0, 255);
490                                 pixbuf_set_rect_fill(icon, 3, 9, 18, 6, 0, 200, 0, 255);
491                                 pixbuf_set_rect_fill(icon, 3, 15, 18, 6, 0, 0, 200, 255);
492                                 break;
493                         case IMAGE_OSD_FIRST:
494                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 3, 0);
495                                 pixbuf_draw_triangle(icon, 6, 5, 12, 6,
496                                                      12, 5, 18, 11, 6, 11,
497                                                      0, 0, 0, 255);
498                                 break;
499                         case IMAGE_OSD_LAST:
500                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 0, 3);
501                                 pixbuf_draw_triangle(icon, 6, 12, 12, 6,
502                                                      12, 18, 6, 12, 18, 12,
503                                                      0, 0, 0, 255);
504                                 break;
505                         case IMAGE_OSD_ICON:
506                                 pixbuf_set_rect_fill(icon, 11, 3, 3, 12, 0, 0, 0, 255);
507                                 pixbuf_set_rect_fill(icon, 11, 17, 3, 3, 0, 0, 0, 255);
508                                 break;
509                         default:
510                                 break;
511                         }
512                 }
513
514         icons[flag] = icon;
515
516         return icon;
517 }
518
519 static GdkPixbuf *image_osd_guidelines_render(OverlayStateData *osd)
520 {
521         gint width, height;
522         GdkPixbuf *rectangles;
523         ImageWindow *imd = osd->imd;
524
525         pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
526
527         if (width && height)
528                 {
529                 rectangles = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
530                 if (rectangles)
531                         {
532                         pixbuf_set_rect_fill(rectangles, 0, 0, width, height, 255, 255, 255, 0);
533                         pixbuf_set_rect(rectangles, 0, 0 + (height / 3), width, height / 3,
534                                                                 0, 0, 0, 255,
535                                                                 1, 1, 1, 1);
536                         pixbuf_set_rect(rectangles, 0, 0 + (height / 3 + 1), width, height / 3 - 2,
537                                                                 255, 255, 255, 255,
538                                                                 1, 1, 1, 1);
539
540                         pixbuf_set_rect(rectangles, 0 + width / 3, 0 , width / 3, height,
541                                                                 0, 0, 0, 255,
542                                                                 1, 1, 1, 1);
543                         pixbuf_set_rect(rectangles, 0 + width / 3 + 1, 0, width / 3 - 2, height,
544                                                                 255, 255, 255, 255,
545                                                                 1, 1, 1, 1);
546                         return rectangles;
547                         }
548                 }
549
550         return NULL;
551 }
552
553 static gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
554                               OverlayRendererFlags flags)
555 {
556         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, flags);
557 }
558
559 static void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
560 {
561         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
562 }
563
564 static void image_overlay_remove(ImageWindow *imd, gint id)
565 {
566         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
567 }
568
569 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
570 {
571         GdkPixbuf *pixbuf;
572
573         if (osd->icon_id[flag]) return;
574
575         pixbuf = image_osd_icon_pixbuf(flag);
576         if (!pixbuf) return;
577
578         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
579                                                osd_icons[flag].x, osd_icons[flag].y,
580                                                OVL_RELATIVE);
581 }
582
583 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
584 {
585         if (osd->icon_id[flag])
586                 {
587                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
588                 osd->icon_id[flag] = 0;
589                 }
590 }
591
592 static void image_osd_icons_reset_time(OverlayStateData *osd)
593 {
594         gint i;
595
596         for (i = 0; i < IMAGE_OSD_COUNT; i++)
597                 {
598                 if (osd_icons[i].reset)
599                         {
600                         osd->icon_time[i] = 0;
601                         }
602                 }
603 }
604
605 static void image_osd_icons_update(OverlayStateData *osd)
606 {
607         gint i;
608
609         for (i = 0; i < IMAGE_OSD_COUNT; i++)
610                 {
611                 if (osd->icon_time[i] > 0)
612                         {
613                         image_osd_icon_show(osd, i);
614                         }
615                 else
616                         {
617                         image_osd_icon_hide(osd, i);
618                         }
619                 }
620 }
621
622 static void image_osd_icons_hide(OverlayStateData *osd)
623 {
624         gint i;
625
626         for (i = 0; i < IMAGE_OSD_COUNT; i++)
627                 {
628                 image_osd_icon_hide(osd, i);
629                 }
630 }
631
632 static void image_osd_info_show(OverlayStateData *osd, GdkPixbuf *pixbuf)
633 {
634         if (osd->ovl_info == 0)
635                 {
636                 osd->ovl_info = image_overlay_add(osd->imd, pixbuf, osd->x, osd->y, osd->origin);
637                 }
638         else
639                 {
640                 image_overlay_set(osd->imd, osd->ovl_info, pixbuf, osd->x, osd->y);
641                 }
642 }
643
644 static void image_osd_info_hide(OverlayStateData *osd)
645 {
646         if (osd->ovl_info == 0) return;
647
648         image_overlay_remove(osd->imd, osd->ovl_info);
649         osd->ovl_info = 0;
650 }
651
652 static gboolean image_osd_update_cb(gpointer data)
653 {
654         OverlayStateData *osd = data;
655
656         if (osd->show & OSD_SHOW_INFO)
657                 {
658                 /* redraw when the image was changed,
659                    with histogram we have to redraw also when loading is finished */
660                 if (osd->changed_states & IMAGE_STATE_IMAGE ||
661                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_HISTOGRAM) ||
662                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_GUIDELINES) ||
663                     osd->notify & NOTIFY_HISTMAP)
664                         {
665                         GdkPixbuf *pixbuf;
666
667                         if (osd->show & OSD_SHOW_GUIDELINES)
668                                 {
669                                 ImageWindow *imd = osd->imd;
670                                 osd->x = ((PixbufRenderer *)imd->pr)->x_offset;
671                                 osd->y = ((PixbufRenderer *)imd->pr)->y_offset;
672                                 osd->origin = OVL_NORMAL;
673
674                                 pixbuf = image_osd_guidelines_render(osd);
675                                 if (pixbuf)
676                                         {
677                                         image_osd_info_show(osd, pixbuf);
678                                         g_object_unref(pixbuf);
679                                         }
680
681                                 osd->x = options->image_overlay.x;
682                                 osd->y = options->image_overlay.y;
683                                 osd->origin = OVL_RELATIVE;
684                                 }
685                         else
686                                 {
687                                 pixbuf = image_osd_info_render(osd);
688                                 if (pixbuf)
689                                         {
690                                         image_osd_info_show(osd, pixbuf);
691                                         g_object_unref(pixbuf);
692                                         }
693                                 else
694                                         {
695                                         image_osd_info_hide(osd);
696                                         }
697                                 }
698                         }
699                 }
700         else
701                 {
702                 image_osd_info_hide(osd);
703                 }
704
705         if (osd->show & OSD_SHOW_STATUS)
706                 {
707                 if (osd->changed_states & IMAGE_STATE_IMAGE)
708                         image_osd_icons_reset_time(osd);
709
710                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
711                         {
712                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
713                         image_osd_timer_schedule(osd);
714                         }
715
716                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
717                         {
718                         gint n = 0;
719
720                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
721                                 {
722                                 n = 1;
723                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
724                                 }
725
726                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
727                         image_osd_timer_schedule(osd);
728                         }
729
730                 image_osd_icons_update(osd);
731                 }
732         else
733                 {
734                 image_osd_icons_hide(osd);
735                 }
736
737         osd->changed_states = IMAGE_STATE_NONE;
738         osd->notify = 0;
739         osd->idle_id = 0;
740         return FALSE;
741 }
742
743 static void image_osd_update_schedule(OverlayStateData *osd, gboolean force)
744 {
745         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
746
747         if (!osd->idle_id)
748                 {
749                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
750                 }
751 }
752
753 void image_osd_update(ImageWindow *imd)
754 {
755         OverlayStateData *osd = image_get_osd_data(imd);
756
757         if (!osd) return;
758
759         image_osd_update_schedule(osd, TRUE);
760 }
761
762 static gboolean image_osd_timer_cb(gpointer data)
763 {
764         OverlayStateData *osd = data;
765         gboolean done = TRUE;
766         gboolean changed = FALSE;
767         gint i;
768
769         for (i = 0; i < IMAGE_OSD_COUNT; i++)
770                 {
771                 if (osd->icon_time[i] > 1)
772                         {
773                         osd->icon_time[i]--;
774                         if (osd->icon_time[i] < 2)
775                                 {
776                                 osd->icon_time[i] = 0;
777                                 changed = TRUE;
778                                 }
779                         else
780                                 {
781                                 done = FALSE;
782                                 }
783                         }
784                 }
785
786         if (changed) image_osd_update_schedule(osd, FALSE);
787
788         if (done)
789                 {
790                 osd->timer_id = 0;
791                 return FALSE;
792                 }
793
794         return TRUE;
795 }
796
797 static void image_osd_timer_schedule(OverlayStateData *osd)
798 {
799         if (!osd->timer_id)
800                 {
801                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
802                 }
803 }
804
805 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
806 {
807         OverlayStateData *osd = data;
808
809         osd->changed_states |= state;
810         image_osd_update_schedule(osd, FALSE);
811 }
812
813 static void image_osd_notify_cb(FileData *fd, NotifyType type, gpointer data)
814 {
815         OverlayStateData *osd = data;
816
817         if ((type & (NOTIFY_HISTMAP)) && osd->imd && fd == osd->imd->image_fd)
818                 {
819                 DEBUG_1("Notify osd: %s %04x", fd->path, type);
820                 osd->notify |= type;
821                 image_osd_update_schedule(osd, FALSE);
822                 }
823 }
824
825
826 static void image_osd_free(OverlayStateData *osd)
827 {
828         if (!osd) return;
829
830         if (osd->idle_id) g_source_remove(osd->idle_id);
831         if (osd->timer_id) g_source_remove(osd->timer_id);
832
833         file_data_unregister_notify_func(image_osd_notify_cb, osd);
834
835         if (osd->imd)
836                 {
837                 image_set_osd_data(osd->imd, NULL);
838                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
839
840                 image_set_state_func(osd->imd, NULL, NULL);
841
842                 image_osd_info_hide(osd);
843                 image_osd_icons_hide(osd);
844                 }
845
846         if (osd->histogram) histogram_free(osd->histogram);
847
848         g_free(osd);
849 }
850
851 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
852 {
853         OverlayStateData *osd = data;
854
855         osd->imd = NULL;
856         image_osd_free(osd);
857 }
858
859 static void image_osd_enable(ImageWindow *imd, OsdShowFlags show)
860 {
861         OverlayStateData *osd = image_get_osd_data(imd);
862
863         if (!osd)
864                 {
865                 osd = g_new0(OverlayStateData, 1);
866                 osd->imd = imd;
867                 osd->show = OSD_SHOW_NOTHING;
868                 osd->x = options->image_overlay.x;
869                 osd->y = options->image_overlay.y;
870                 osd->origin = OVL_RELATIVE;
871
872                 osd->histogram = histogram_new();
873
874                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
875                                                    G_CALLBACK(image_osd_destroy_cb), osd);
876                 image_set_osd_data(imd, osd);
877
878                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
879                 file_data_register_notify_func(image_osd_notify_cb, osd, NOTIFY_PRIORITY_LOW);
880                 }
881
882         if (show & OSD_SHOW_STATUS)
883                 image_osd_icon(imd, IMAGE_OSD_ICON, -1);
884
885         if (show != osd->show)
886                 image_osd_update_schedule(osd, TRUE);
887
888         osd->show = show;
889 }
890
891 void image_osd_set(ImageWindow *imd, OsdShowFlags show)
892 {
893         if (!imd) return;
894
895         image_osd_enable(imd, show);
896 }
897
898 OsdShowFlags image_osd_get(ImageWindow *imd)
899 {
900         OverlayStateData *osd = image_get_osd_data(imd);
901
902         return osd ? osd->show : OSD_SHOW_NOTHING;
903 }
904
905 Histogram *image_osd_get_histogram(ImageWindow *imd)
906 {
907         OverlayStateData *osd = image_get_osd_data(imd);
908
909         return osd ? osd->histogram : NULL;
910 }
911
912 void image_osd_copy_status(ImageWindow *src, ImageWindow *dest)
913 {
914         Histogram *h_src, *h_dest;
915         image_osd_set(dest, image_osd_get(src));
916
917         h_src = image_osd_get_histogram(src);
918         h_dest = image_osd_get_histogram(dest);
919
920         h_dest->histogram_mode = h_src->histogram_mode;
921         h_dest->histogram_channel = h_src->histogram_channel;
922
923 }
924
925 /* duration:
926     0 = hide
927     1 = show
928    2+ = show for duration tenths of a second
929    -1 = use default duration
930  */
931 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
932 {
933         OverlayStateData *osd = image_get_osd_data(imd);
934
935         if (!osd) return;
936
937         if (flag >= IMAGE_OSD_COUNT) return;
938         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
939         if (duration > 1) duration += 1;
940
941         osd->icon_time[flag] = duration;
942
943         image_osd_update_schedule(osd, FALSE);
944         image_osd_timer_schedule(osd);
945 }
946 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */