Include a Other Software section in Help file
[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         if (!imd) return;
206
207         show = image_osd_get(imd);
208         if (show == OSD_SHOW_NOTHING)
209                 {
210                 image_osd_set(imd, OSD_SHOW_INFO | OSD_SHOW_STATUS);
211                 return;
212                 }
213         else
214                 {
215                 if (show & OSD_SHOW_GUIDELINES)
216                         {
217                         image_osd_set(imd, OSD_SHOW_NOTHING);
218                         }
219                 else if (show & OSD_SHOW_HISTOGRAM)
220                         {
221                         image_osd_set(imd, OSD_SHOW_GUIDELINES);
222                         image_osd_set(imd, show | ~OSD_SHOW_HISTOGRAM);
223                         }
224                 else
225                         {
226                         image_osd_set(imd, show | OSD_SHOW_HISTOGRAM);
227                         }
228                 }
229 }
230
231 static GdkPixbuf *image_osd_info_render(OverlayStateData *osd)
232 {
233         GdkPixbuf *pixbuf = NULL;
234         gint width, height;
235         PangoLayout *layout;
236         const gchar *name;
237         gchar *text;
238         gboolean with_hist;
239         const HistMap *histmap = NULL;
240         ImageWindow *imd = osd->imd;
241         FileData *fd = image_get_fd(imd);
242         PangoFontDescription *font_desc;
243
244         if (!fd) return NULL;
245
246         name = image_get_name(imd);
247         if (name)
248                 {
249                 gint n, t;
250                 CollectionData *cd;
251                 CollectInfo *info;
252                 GHashTable *vars;
253
254                 vars = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
255
256                 cd = image_get_collection(imd, &info);
257                 if (cd)
258                         {
259                         t = g_list_length(cd->list);
260                         n = g_list_index(cd->list, info) + 1;
261                         if (cd->name)
262                                 {
263                                 if (file_extension_match(cd->name, GQ_COLLECTION_EXT))
264                                         osd_template_insert(vars, "collection", remove_extension_from_path(cd->name), OSDT_FREE);
265                                 else
266                                         osd_template_insert(vars, "collection", cd->name, OSDT_NONE);
267                                 }
268                         else
269                                 {
270                                 osd_template_insert(vars, "collection", _("Untitled"), OSDT_NONE);
271                                 }
272                         }
273                 else
274                         {
275                         LayoutWindow *lw = layout_find_by_image(imd);
276                         if (lw)
277                                 {
278                                 if (lw->slideshow)
279                                         {
280                                         n = g_list_length(lw->slideshow->list_done);
281                                         t = n + g_list_length(lw->slideshow->list);
282                                         if (n == 0) n = t;
283                                         }
284                                 else
285                                         {
286                                         t = layout_list_count(lw, NULL);
287                                         n = layout_list_get_index(lw, image_get_fd(lw->image)) + 1;
288                                         }
289                                 }
290                         else if (view_window_find_image(imd, &n, &t))
291                                 {
292                                 n++;
293                                 }
294                         else
295                                 {
296                                 t = 1;
297                                 n = 1;
298                                 }
299
300                         if (n < 1) n = 1;
301                         if (t < 1) t = 1;
302
303                         osd_template_insert(vars, "collection", NULL, OSDT_NONE);
304                         }
305
306                 osd_template_insert(vars, "number", g_strdup_printf("%d", n), OSDT_NO_DUP);
307                 osd_template_insert(vars, "total", g_strdup_printf("%d", t), OSDT_NO_DUP);
308                 osd_template_insert(vars, "name", (gchar *) name, OSDT_NONE);
309                 osd_template_insert(vars, "path", (gchar *) image_get_path(imd), OSDT_NONE);
310                 osd_template_insert(vars, "date", imd->image_fd ? ((gchar *) text_from_time(imd->image_fd->date)) : "", OSDT_NONE);
311                 osd_template_insert(vars, "size", imd->image_fd ? (text_from_size_abrev(imd->image_fd->size)) : g_strdup(""), OSDT_FREE);
312                 osd_template_insert(vars, "zoom", image_zoom_get_as_text(imd), OSDT_FREE);
313
314                 if (!imd->unknown)
315                         {
316                         gint w, h;
317                         GdkPixbuf *load_pixbuf = image_loader_get_pixbuf(imd->il);
318
319                         if (imd->delay_flip &&
320                             imd->il && load_pixbuf &&
321                             image_get_pixbuf(imd) != load_pixbuf)
322                                 {
323                                 w = gdk_pixbuf_get_width(load_pixbuf);
324                                 h = gdk_pixbuf_get_height(load_pixbuf);
325                                 }
326                         else
327                                 {
328                                 image_get_image_size(imd, &w, &h);
329                                 }
330
331
332                         osd_template_insert(vars, "width", g_strdup_printf("%d", w), OSDT_NO_DUP);
333                         osd_template_insert(vars, "height", g_strdup_printf("%d", h), OSDT_NO_DUP);
334                         osd_template_insert(vars, "res", g_strdup_printf("%d Ã— %d", w, h), OSDT_FREE);
335                         }
336                 else
337                         {
338                         osd_template_insert(vars, "width", NULL, OSDT_NONE);
339                         osd_template_insert(vars, "height", NULL, OSDT_NONE);
340                         osd_template_insert(vars, "res", NULL, OSDT_NONE);
341                         }
342
343                 text = image_osd_mkinfo(options->image_overlay.template_string, imd->image_fd, vars);
344                 g_hash_table_destroy(vars);
345
346         } else {
347                 /* When does this occur ?? */
348                 text = g_markup_escape_text(_("Untitled"), -1);
349         }
350
351         with_hist = ((osd->show & OSD_SHOW_HISTOGRAM) && osd->histogram);
352         if (with_hist)
353                 {
354                 histmap = histmap_get(imd->image_fd);
355                 if (!histmap)
356                         {
357                         histmap_start_idle(imd->image_fd);
358                         with_hist = FALSE;
359                         }
360                 }
361
362
363         {
364                 gint active_marks = 0;
365                 gint mark;
366                 gchar *text2;
367
368                 for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
369                         {
370                         active_marks += file_data_get_mark(fd, mark);
371                         }
372
373                 if (active_marks > 0)
374                         {
375                         GString *buf = g_string_sized_new(FILEDATA_MARKS_SIZE * 2);
376
377                         for (mark = 0; mark < FILEDATA_MARKS_SIZE; mark++)
378                                 {
379                                 g_string_append_printf(buf, file_data_get_mark(fd, mark) ? " <span background='#FF00FF'>%c</span>" : " %c", '1' + (mark < 9 ? mark : -1) );
380                                 }
381
382                         if (*text)
383                                 text2 = g_strdup_printf("%s\n%s", text, buf->str);
384                         else
385                                 text2 = g_strdup(buf->str);
386                         g_string_free(buf, TRUE);
387                         g_free(text);
388                         text = text2;
389                         }
390
391                 if (with_hist)
392                         {
393                         gchar *escaped_histogram_label = g_markup_escape_text(histogram_label(osd->histogram), -1);
394                         if (*text)
395                                 text2 = g_strdup_printf("%s\n%s", text, escaped_histogram_label);
396                         else
397                                 text2 = g_strdup(escaped_histogram_label);
398                         g_free(escaped_histogram_label);
399                         g_free(text);
400                         text = text2;
401                         }
402         }
403
404         font_desc = pango_font_description_from_string(options->image_overlay.font);
405         layout = gtk_widget_create_pango_layout(imd->pr, NULL);
406         pango_layout_set_font_description(layout, font_desc);
407
408         pango_layout_set_markup(layout, text, -1);
409         g_free(text);
410
411         pango_layout_get_pixel_size(layout, &width, &height);
412         /* with empty text width is set to 0, but not height) */
413         if (width == 0)
414                 height = 0;
415         else if (height == 0)
416                 width = 0;
417         if (width > 0) width += 10;
418         if (height > 0) height += 10;
419
420         if (with_hist)
421                 {
422                 if (width < HISTOGRAM_WIDTH + 10) width = HISTOGRAM_WIDTH + 10;
423                 height += HISTOGRAM_HEIGHT + 5;
424                 }
425
426         if (width > 0 && height > 0)
427                 {
428                 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
429                 pixbuf_set_rect_fill(pixbuf, 3, 3, width-6, height-6, options->image_overlay.background_red, options->image_overlay.background_green,
430                                                                                                                         options->image_overlay.background_blue, options->image_overlay.background_alpha);
431                 pixbuf_set_rect(pixbuf, 0, 0, width, height, 240, 240, 240, 80, 1, 1, 1, 1);
432                 pixbuf_set_rect(pixbuf, 1, 1, width-2, height-2, 240, 240, 240, 130, 1, 1, 1, 1);
433                 pixbuf_set_rect(pixbuf, 2, 2, width-4, height-4, 240, 240, 240, 180, 1, 1, 1, 1);
434                 pixbuf_pixel_set(pixbuf, 0, 0, 0, 0, 0, 0);
435                 pixbuf_pixel_set(pixbuf, width - 1, 0, 0, 0, 0, 0);
436                 pixbuf_pixel_set(pixbuf, 0, height - 1, 0, 0, 0, 0);
437                 pixbuf_pixel_set(pixbuf, width - 1, height - 1, 0, 0, 0, 0);
438
439                 if (with_hist)
440                         {
441                         gint x = 5;
442                         gint y = height - HISTOGRAM_HEIGHT - 5;
443                         gint w = width - 10;
444
445                         pixbuf_set_rect_fill(pixbuf, x, y, w, HISTOGRAM_HEIGHT, 220, 220, 220, 210);
446                         histogram_draw(osd->histogram, histmap, pixbuf, x, y, w, HISTOGRAM_HEIGHT);
447                         }
448                 pixbuf_draw_layout(pixbuf, layout, imd->pr, 5, 5, options->image_overlay.text_red, options->image_overlay.text_green,
449                                                                                                                         options->image_overlay.text_blue, options->image_overlay.text_alpha);
450         }
451
452         g_object_unref(G_OBJECT(layout));
453
454         return pixbuf;
455 }
456
457 static GdkPixbuf *image_osd_icon_pixbuf(ImageOSDFlag flag)
458 {
459         static GdkPixbuf **icons = NULL;
460         GdkPixbuf *icon = NULL;
461
462         if (!icons) icons = g_new0(GdkPixbuf *, IMAGE_OSD_COUNT);
463         if (icons[flag]) return icons[flag];
464
465         if (osd_icons[flag].key)
466                 {
467                 icon = pixbuf_inline(osd_icons[flag].key);
468                 }
469
470         if (!icon)
471                 {
472                 icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24);
473                 pixbuf_set_rect_fill(icon, 1, 1, 22, 22, 255, 255, 255, 200);
474                 pixbuf_set_rect(icon, 0, 0, 24, 24, 0, 0, 0, 128, 1, 1, 1, 1);
475                 switch (flag)
476                         {
477                         case IMAGE_OSD_ROTATE_AUTO:
478                                 pixbuf_set_rect(icon, 3, 8, 11, 12,
479                                                 0, 0, 0, 255,
480                                                 3, 0, 3, 0);
481                                 pixbuf_draw_triangle(icon, 14, 3, 6, 12,
482                                                      20, 9, 14, 15, 14, 3,
483                                                      0, 0, 0, 255);
484                                 break;
485                         case IMAGE_OSD_ROTATE_USER:
486                                 break;
487                         case IMAGE_OSD_COLOR:
488                                 pixbuf_set_rect_fill(icon, 3, 3, 18, 6, 200, 0, 0, 255);
489                                 pixbuf_set_rect_fill(icon, 3, 9, 18, 6, 0, 200, 0, 255);
490                                 pixbuf_set_rect_fill(icon, 3, 15, 18, 6, 0, 0, 200, 255);
491                                 break;
492                         case IMAGE_OSD_FIRST:
493                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 3, 0);
494                                 pixbuf_draw_triangle(icon, 6, 5, 12, 6,
495                                                      12, 5, 18, 11, 6, 11,
496                                                      0, 0, 0, 255);
497                                 break;
498                         case IMAGE_OSD_LAST:
499                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 0, 3);
500                                 pixbuf_draw_triangle(icon, 6, 12, 12, 6,
501                                                      12, 18, 6, 12, 18, 12,
502                                                      0, 0, 0, 255);
503                                 break;
504                         case IMAGE_OSD_ICON:
505                                 pixbuf_set_rect_fill(icon, 11, 3, 3, 12, 0, 0, 0, 255);
506                                 pixbuf_set_rect_fill(icon, 11, 17, 3, 3, 0, 0, 0, 255);
507                                 break;
508                         default:
509                                 break;
510                         }
511                 }
512
513         icons[flag] = icon;
514
515         return icon;
516 }
517
518 static GdkPixbuf *image_osd_guidelines_render(OverlayStateData *osd)
519 {
520         gint width, height;
521         GdkPixbuf *rectangles;
522         ImageWindow *imd = osd->imd;
523
524 /** @FIXME guidelines does not work with revised draw signal handling
525  */
526         //~ pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
527
528         //~ if (width && height)
529                 //~ {
530                 //~ rectangles = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
531                 //~ if (rectangles)
532                         //~ {
533                         //~ pixbuf_set_rect_fill(rectangles, 0, 0, width, height, 255, 255, 255, 0);
534                         //~ pixbuf_set_rect(rectangles, 0, 0 + (height / 3), width, height / 3,
535                                                                 //~ 0, 0, 0, 255,
536                                                                 //~ 1, 1, 1, 1);
537                         //~ pixbuf_set_rect(rectangles, 0, 0 + (height / 3 + 1), width, height / 3 - 2,
538                                                                 //~ 255, 255, 255, 255,
539                                                                 //~ 1, 1, 1, 1);
540
541                         //~ pixbuf_set_rect(rectangles, 0 + width / 3, 0 , width / 3, height,
542                                                                 //~ 0, 0, 0, 255,
543                                                                 //~ 1, 1, 1, 1);
544                         //~ pixbuf_set_rect(rectangles, 0 + width / 3 + 1, 0, width / 3 - 2, height,
545                                                                 //~ 255, 255, 255, 255,
546                                                                 //~ 1, 1, 1, 1);
547                         //~ return rectangles;
548                         //~ }
549                 //~ }
550
551         return NULL;
552 }
553
554 static gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
555                               OverlayRendererFlags flags)
556 {
557         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, flags);
558 }
559
560 static void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
561 {
562         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
563 }
564
565 static void image_overlay_remove(ImageWindow *imd, gint id)
566 {
567         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
568 }
569
570 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
571 {
572         GdkPixbuf *pixbuf;
573
574         if (osd->icon_id[flag]) return;
575
576         pixbuf = image_osd_icon_pixbuf(flag);
577         if (!pixbuf) return;
578
579         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
580                                                osd_icons[flag].x, osd_icons[flag].y,
581                                                OVL_RELATIVE);
582 }
583
584 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
585 {
586         if (osd->icon_id[flag])
587                 {
588                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
589                 osd->icon_id[flag] = 0;
590                 }
591 }
592
593 static void image_osd_icons_reset_time(OverlayStateData *osd)
594 {
595         gint i;
596
597         for (i = 0; i < IMAGE_OSD_COUNT; i++)
598                 {
599                 if (osd_icons[i].reset)
600                         {
601                         osd->icon_time[i] = 0;
602                         }
603                 }
604 }
605
606 static void image_osd_icons_update(OverlayStateData *osd)
607 {
608         gint i;
609
610         for (i = 0; i < IMAGE_OSD_COUNT; i++)
611                 {
612                 if (osd->icon_time[i] > 0)
613                         {
614                         image_osd_icon_show(osd, i);
615                         }
616                 else
617                         {
618                         image_osd_icon_hide(osd, i);
619                         }
620                 }
621 }
622
623 static void image_osd_icons_hide(OverlayStateData *osd)
624 {
625         gint i;
626
627         for (i = 0; i < IMAGE_OSD_COUNT; i++)
628                 {
629                 image_osd_icon_hide(osd, i);
630                 }
631 }
632
633 static void image_osd_info_show(OverlayStateData *osd, GdkPixbuf *pixbuf)
634 {
635         if (osd->ovl_info == 0)
636                 {
637                 osd->ovl_info = image_overlay_add(osd->imd, pixbuf, osd->x, osd->y, osd->origin);
638                 }
639         else
640                 {
641                 image_overlay_set(osd->imd, osd->ovl_info, pixbuf, osd->x, osd->y);
642                 }
643 }
644
645 static void image_osd_info_hide(OverlayStateData *osd)
646 {
647         if (osd->ovl_info == 0) return;
648
649         image_overlay_remove(osd->imd, osd->ovl_info);
650         osd->ovl_info = 0;
651 }
652
653 static gboolean image_osd_update_cb(gpointer data)
654 {
655         OverlayStateData *osd = data;
656
657         if (osd->show & OSD_SHOW_INFO)
658                 {
659                 /* redraw when the image was changed,
660                    with histogram we have to redraw also when loading is finished */
661                 if (osd->changed_states & IMAGE_STATE_IMAGE ||
662                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_HISTOGRAM) ||
663                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_GUIDELINES) ||
664                     osd->notify & NOTIFY_HISTMAP)
665                         {
666                         GdkPixbuf *pixbuf;
667
668                         if (osd->show & OSD_SHOW_GUIDELINES)
669                                 {
670                                 ImageWindow *imd = osd->imd;
671                                 osd->x = ((PixbufRenderer *)imd->pr)->x_offset;
672                                 osd->y = ((PixbufRenderer *)imd->pr)->y_offset;
673                                 osd->origin = OVL_NORMAL;
674
675                                 pixbuf = image_osd_guidelines_render(osd);
676                                 if (pixbuf)
677                                         {
678                                         image_osd_info_show(osd, pixbuf);
679                                         g_object_unref(pixbuf);
680                                         }
681
682                                 osd->x = options->image_overlay.x;
683                                 osd->y = options->image_overlay.y;
684                                 osd->origin = OVL_RELATIVE;
685                                 }
686                         else
687                                 {
688                                 pixbuf = image_osd_info_render(osd);
689                                 if (pixbuf)
690                                         {
691                                         image_osd_info_show(osd, pixbuf);
692                                         g_object_unref(pixbuf);
693                                         }
694                                 else
695                                         {
696                                         image_osd_info_hide(osd);
697                                         }
698                                 }
699                         }
700                 }
701         else
702                 {
703                 image_osd_info_hide(osd);
704                 }
705
706         if (osd->show & OSD_SHOW_STATUS)
707                 {
708                 if (osd->changed_states & IMAGE_STATE_IMAGE)
709                         image_osd_icons_reset_time(osd);
710
711                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
712                         {
713                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
714                         image_osd_timer_schedule(osd);
715                         }
716
717                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
718                         {
719                         gint n = 0;
720
721                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
722                                 {
723                                 n = 1;
724                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
725                                 }
726
727                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
728                         image_osd_timer_schedule(osd);
729                         }
730
731                 image_osd_icons_update(osd);
732                 }
733         else
734                 {
735                 image_osd_icons_hide(osd);
736                 }
737
738         osd->changed_states = IMAGE_STATE_NONE;
739         osd->notify = 0;
740         osd->idle_id = 0;
741         return FALSE;
742 }
743
744 static void image_osd_update_schedule(OverlayStateData *osd, gboolean force)
745 {
746         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
747
748         if (!osd->idle_id)
749                 {
750                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
751                 }
752 }
753
754 void image_osd_update(ImageWindow *imd)
755 {
756         OverlayStateData *osd = image_get_osd_data(imd);
757
758         if (!osd) return;
759
760         image_osd_update_schedule(osd, TRUE);
761 }
762
763 static gboolean image_osd_timer_cb(gpointer data)
764 {
765         OverlayStateData *osd = data;
766         gboolean done = TRUE;
767         gboolean changed = FALSE;
768         gint i;
769
770         for (i = 0; i < IMAGE_OSD_COUNT; i++)
771                 {
772                 if (osd->icon_time[i] > 1)
773                         {
774                         osd->icon_time[i]--;
775                         if (osd->icon_time[i] < 2)
776                                 {
777                                 osd->icon_time[i] = 0;
778                                 changed = TRUE;
779                                 }
780                         else
781                                 {
782                                 done = FALSE;
783                                 }
784                         }
785                 }
786
787         if (changed) image_osd_update_schedule(osd, FALSE);
788
789         if (done)
790                 {
791                 osd->timer_id = 0;
792                 return FALSE;
793                 }
794
795         return TRUE;
796 }
797
798 static void image_osd_timer_schedule(OverlayStateData *osd)
799 {
800         if (!osd->timer_id)
801                 {
802                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
803                 }
804 }
805
806 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
807 {
808         OverlayStateData *osd = data;
809
810         osd->changed_states |= state;
811         image_osd_update_schedule(osd, FALSE);
812 }
813
814 static void image_osd_notify_cb(FileData *fd, NotifyType type, gpointer data)
815 {
816         OverlayStateData *osd = data;
817
818         if ((type & (NOTIFY_HISTMAP)) && osd->imd && fd == osd->imd->image_fd)
819                 {
820                 DEBUG_1("Notify osd: %s %04x", fd->path, type);
821                 osd->notify |= type;
822                 image_osd_update_schedule(osd, FALSE);
823                 }
824 }
825
826
827 static void image_osd_free(OverlayStateData *osd)
828 {
829         if (!osd) return;
830
831         if (osd->idle_id) g_source_remove(osd->idle_id);
832         if (osd->timer_id) g_source_remove(osd->timer_id);
833
834         file_data_unregister_notify_func(image_osd_notify_cb, osd);
835
836         if (osd->imd)
837                 {
838                 image_set_osd_data(osd->imd, NULL);
839                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
840
841                 image_set_state_func(osd->imd, NULL, NULL);
842
843                 image_osd_info_hide(osd);
844                 image_osd_icons_hide(osd);
845                 }
846
847         if (osd->histogram) histogram_free(osd->histogram);
848
849         g_free(osd);
850 }
851
852 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
853 {
854         OverlayStateData *osd = data;
855
856         osd->imd = NULL;
857         image_osd_free(osd);
858 }
859
860 static void image_osd_enable(ImageWindow *imd, OsdShowFlags show)
861 {
862         OverlayStateData *osd = image_get_osd_data(imd);
863
864         if (!osd)
865                 {
866                 osd = g_new0(OverlayStateData, 1);
867                 osd->imd = imd;
868                 osd->show = OSD_SHOW_NOTHING;
869                 osd->x = options->image_overlay.x;
870                 osd->y = options->image_overlay.y;
871                 osd->origin = OVL_RELATIVE;
872
873                 osd->histogram = histogram_new();
874
875                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
876                                                    G_CALLBACK(image_osd_destroy_cb), osd);
877                 image_set_osd_data(imd, osd);
878
879                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
880                 file_data_register_notify_func(image_osd_notify_cb, osd, NOTIFY_PRIORITY_LOW);
881                 }
882
883         if (show & OSD_SHOW_STATUS)
884                 image_osd_icon(imd, IMAGE_OSD_ICON, -1);
885
886         if (show != osd->show)
887                 image_osd_update_schedule(osd, TRUE);
888
889         osd->show = show;
890 }
891
892 void image_osd_set(ImageWindow *imd, OsdShowFlags show)
893 {
894         if (!imd) return;
895
896         image_osd_enable(imd, show);
897 }
898
899 OsdShowFlags image_osd_get(ImageWindow *imd)
900 {
901         OverlayStateData *osd = image_get_osd_data(imd);
902
903         return osd ? osd->show : OSD_SHOW_NOTHING;
904 }
905
906 Histogram *image_osd_get_histogram(ImageWindow *imd)
907 {
908         OverlayStateData *osd = image_get_osd_data(imd);
909
910         return osd ? osd->histogram : NULL;
911 }
912
913 void image_osd_copy_status(ImageWindow *src, ImageWindow *dest)
914 {
915         Histogram *h_src, *h_dest;
916         image_osd_set(dest, image_osd_get(src));
917
918         h_src = image_osd_get_histogram(src);
919         h_dest = image_osd_get_histogram(dest);
920
921         h_dest->histogram_mode = h_src->histogram_mode;
922         h_dest->histogram_channel = h_src->histogram_channel;
923
924 }
925
926 /* duration:
927     0 = hide
928     1 = show
929    2+ = show for duration tenths of a second
930    -1 = use default duration
931  */
932 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
933 {
934         OverlayStateData *osd = image_get_osd_data(imd);
935
936         if (!osd) return;
937
938         if (flag >= IMAGE_OSD_COUNT) return;
939         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
940         if (duration > 1) duration += 1;
941
942         osd->icon_time[flag] = duration;
943
944         image_osd_update_schedule(osd, FALSE);
945         image_osd_timer_schedule(osd);
946 }
947 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */