e94d7529dddbd6dc43552f9fa9d5c86fb400aa91
[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, "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         pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
525
526         if (width && height)
527                 {
528                 rectangles = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
529                 if (rectangles)
530                         {
531                         pixbuf_set_rect_fill(rectangles, 0, 0, width, height, 255, 255, 255, 0);
532                         pixbuf_set_rect(rectangles, 0, 0 + (height / 3), width, height / 3,
533                                                                 0, 0, 0, 255,
534                                                                 1, 1, 1, 1);
535                         pixbuf_set_rect(rectangles, 0, 0 + (height / 3 + 1), width, height / 3 - 2,
536                                                                 255, 255, 255, 255,
537                                                                 1, 1, 1, 1);
538
539                         pixbuf_set_rect(rectangles, 0 + width / 3, 0 , width / 3, height,
540                                                                 0, 0, 0, 255,
541                                                                 1, 1, 1, 1);
542                         pixbuf_set_rect(rectangles, 0 + width / 3 + 1, 0, width / 3 - 2, height,
543                                                                 255, 255, 255, 255,
544                                                                 1, 1, 1, 1);
545                         return rectangles;
546                         }
547                 }
548
549         return NULL;
550 }
551
552 static gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
553                               OverlayRendererFlags flags)
554 {
555         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, flags);
556 }
557
558 static void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
559 {
560         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
561 }
562
563 static void image_overlay_remove(ImageWindow *imd, gint id)
564 {
565         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
566 }
567
568 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
569 {
570         GdkPixbuf *pixbuf;
571
572         if (osd->icon_id[flag]) return;
573
574         pixbuf = image_osd_icon_pixbuf(flag);
575         if (!pixbuf) return;
576
577         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
578                                                osd_icons[flag].x, osd_icons[flag].y,
579                                                OVL_RELATIVE);
580 }
581
582 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
583 {
584         if (osd->icon_id[flag])
585                 {
586                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
587                 osd->icon_id[flag] = 0;
588                 }
589 }
590
591 static void image_osd_icons_reset_time(OverlayStateData *osd)
592 {
593         gint i;
594
595         for (i = 0; i < IMAGE_OSD_COUNT; i++)
596                 {
597                 if (osd_icons[i].reset)
598                         {
599                         osd->icon_time[i] = 0;
600                         }
601                 }
602 }
603
604 static void image_osd_icons_update(OverlayStateData *osd)
605 {
606         gint i;
607
608         for (i = 0; i < IMAGE_OSD_COUNT; i++)
609                 {
610                 if (osd->icon_time[i] > 0)
611                         {
612                         image_osd_icon_show(osd, i);
613                         }
614                 else
615                         {
616                         image_osd_icon_hide(osd, i);
617                         }
618                 }
619 }
620
621 static void image_osd_icons_hide(OverlayStateData *osd)
622 {
623         gint i;
624
625         for (i = 0; i < IMAGE_OSD_COUNT; i++)
626                 {
627                 image_osd_icon_hide(osd, i);
628                 }
629 }
630
631 static void image_osd_info_show(OverlayStateData *osd, GdkPixbuf *pixbuf)
632 {
633         if (osd->ovl_info == 0)
634                 {
635                 osd->ovl_info = image_overlay_add(osd->imd, pixbuf, osd->x, osd->y, osd->origin);
636                 }
637         else
638                 {
639                 image_overlay_set(osd->imd, osd->ovl_info, pixbuf, osd->x, osd->y);
640                 }
641 }
642
643 static void image_osd_info_hide(OverlayStateData *osd)
644 {
645         if (osd->ovl_info == 0) return;
646
647         image_overlay_remove(osd->imd, osd->ovl_info);
648         osd->ovl_info = 0;
649 }
650
651 static gboolean image_osd_update_cb(gpointer data)
652 {
653         OverlayStateData *osd = data;
654
655         if (osd->show & OSD_SHOW_INFO)
656                 {
657                 /* redraw when the image was changed,
658                    with histogram we have to redraw also when loading is finished */
659                 if (osd->changed_states & IMAGE_STATE_IMAGE ||
660                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_HISTOGRAM) ||
661                     (osd->changed_states & IMAGE_STATE_LOADING && osd->show & OSD_SHOW_GUIDELINES) ||
662                     osd->notify & NOTIFY_HISTMAP)
663                         {
664                         GdkPixbuf *pixbuf;
665
666                         if (osd->show & OSD_SHOW_GUIDELINES)
667                                 {
668                                 ImageWindow *imd = osd->imd;
669                                 osd->x = ((PixbufRenderer *)imd->pr)->x_offset;
670                                 osd->y = ((PixbufRenderer *)imd->pr)->y_offset;
671                                 osd->origin = OVL_NORMAL;
672
673                                 pixbuf = image_osd_guidelines_render(osd);
674                                 if (pixbuf)
675                                         {
676                                         image_osd_info_show(osd, pixbuf);
677                                         g_object_unref(pixbuf);
678                                         }
679
680                                 osd->x = options->image_overlay.x;
681                                 osd->y = options->image_overlay.y;
682                                 osd->origin = OVL_RELATIVE;
683                                 }
684                         else
685                                 {
686                                 pixbuf = image_osd_info_render(osd);
687                                 if (pixbuf)
688                                         {
689                                         image_osd_info_show(osd, pixbuf);
690                                         g_object_unref(pixbuf);
691                                         }
692                                 else
693                                         {
694                                         image_osd_info_hide(osd);
695                                         }
696                                 }
697                         }
698                 }
699         else
700                 {
701                 image_osd_info_hide(osd);
702                 }
703
704         if (osd->show & OSD_SHOW_STATUS)
705                 {
706                 if (osd->changed_states & IMAGE_STATE_IMAGE)
707                         image_osd_icons_reset_time(osd);
708
709                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
710                         {
711                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
712                         image_osd_timer_schedule(osd);
713                         }
714
715                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
716                         {
717                         gint n = 0;
718
719                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
720                                 {
721                                 n = 1;
722                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
723                                 }
724
725                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
726                         image_osd_timer_schedule(osd);
727                         }
728
729                 image_osd_icons_update(osd);
730                 }
731         else
732                 {
733                 image_osd_icons_hide(osd);
734                 }
735
736         osd->changed_states = IMAGE_STATE_NONE;
737         osd->notify = 0;
738         osd->idle_id = 0;
739         return FALSE;
740 }
741
742 static void image_osd_update_schedule(OverlayStateData *osd, gboolean force)
743 {
744         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
745
746         if (!osd->idle_id)
747                 {
748                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
749                 }
750 }
751
752 void image_osd_update(ImageWindow *imd)
753 {
754         OverlayStateData *osd = image_get_osd_data(imd);
755
756         if (!osd) return;
757
758         image_osd_update_schedule(osd, TRUE);
759 }
760
761 static gboolean image_osd_timer_cb(gpointer data)
762 {
763         OverlayStateData *osd = data;
764         gboolean done = TRUE;
765         gboolean changed = FALSE;
766         gint i;
767
768         for (i = 0; i < IMAGE_OSD_COUNT; i++)
769                 {
770                 if (osd->icon_time[i] > 1)
771                         {
772                         osd->icon_time[i]--;
773                         if (osd->icon_time[i] < 2)
774                                 {
775                                 osd->icon_time[i] = 0;
776                                 changed = TRUE;
777                                 }
778                         else
779                                 {
780                                 done = FALSE;
781                                 }
782                         }
783                 }
784
785         if (changed) image_osd_update_schedule(osd, FALSE);
786
787         if (done)
788                 {
789                 osd->timer_id = 0;
790                 return FALSE;
791                 }
792
793         return TRUE;
794 }
795
796 static void image_osd_timer_schedule(OverlayStateData *osd)
797 {
798         if (!osd->timer_id)
799                 {
800                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
801                 }
802 }
803
804 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
805 {
806         OverlayStateData *osd = data;
807
808         osd->changed_states |= state;
809         image_osd_update_schedule(osd, FALSE);
810 }
811
812 static void image_osd_notify_cb(FileData *fd, NotifyType type, gpointer data)
813 {
814         OverlayStateData *osd = data;
815
816         if ((type & (NOTIFY_HISTMAP)) && osd->imd && fd == osd->imd->image_fd)
817                 {
818                 DEBUG_1("Notify osd: %s %04x", fd->path, type);
819                 osd->notify |= type;
820                 image_osd_update_schedule(osd, FALSE);
821                 }
822 }
823
824
825 static void image_osd_free(OverlayStateData *osd)
826 {
827         if (!osd) return;
828
829         if (osd->idle_id) g_source_remove(osd->idle_id);
830         if (osd->timer_id) g_source_remove(osd->timer_id);
831
832         file_data_unregister_notify_func(image_osd_notify_cb, osd);
833
834         if (osd->imd)
835                 {
836                 image_set_osd_data(osd->imd, NULL);
837                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
838
839                 image_set_state_func(osd->imd, NULL, NULL);
840
841                 image_osd_info_hide(osd);
842                 image_osd_icons_hide(osd);
843                 }
844
845         if (osd->histogram) histogram_free(osd->histogram);
846
847         g_free(osd);
848 }
849
850 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
851 {
852         OverlayStateData *osd = data;
853
854         osd->imd = NULL;
855         image_osd_free(osd);
856 }
857
858 static void image_osd_enable(ImageWindow *imd, OsdShowFlags show)
859 {
860         OverlayStateData *osd = image_get_osd_data(imd);
861
862         if (!osd)
863                 {
864                 osd = g_new0(OverlayStateData, 1);
865                 osd->imd = imd;
866                 osd->show = OSD_SHOW_NOTHING;
867                 osd->x = options->image_overlay.x;
868                 osd->y = options->image_overlay.y;
869                 osd->origin = OVL_RELATIVE;
870
871                 osd->histogram = histogram_new();
872
873                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
874                                                    G_CALLBACK(image_osd_destroy_cb), osd);
875                 image_set_osd_data(imd, osd);
876
877                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
878                 file_data_register_notify_func(image_osd_notify_cb, osd, NOTIFY_PRIORITY_LOW);
879                 }
880
881         if (show & OSD_SHOW_STATUS)
882                 image_osd_icon(imd, IMAGE_OSD_ICON, -1);
883
884         if (show != osd->show)
885                 image_osd_update_schedule(osd, TRUE);
886
887         osd->show = show;
888 }
889
890 void image_osd_set(ImageWindow *imd, OsdShowFlags show)
891 {
892         if (!imd) return;
893
894         image_osd_enable(imd, show);
895 }
896
897 OsdShowFlags image_osd_get(ImageWindow *imd)
898 {
899         OverlayStateData *osd = image_get_osd_data(imd);
900
901         return osd ? osd->show : OSD_SHOW_NOTHING;
902 }
903
904 Histogram *image_osd_get_histogram(ImageWindow *imd)
905 {
906         OverlayStateData *osd = image_get_osd_data(imd);
907
908         return osd ? osd->histogram : NULL;
909 }
910
911 void image_osd_copy_status(ImageWindow *src, ImageWindow *dest)
912 {
913         Histogram *h_src, *h_dest;
914         image_osd_set(dest, image_osd_get(src));
915
916         h_src = image_osd_get_histogram(src);
917         h_dest = image_osd_get_histogram(dest);
918
919         h_dest->histogram_mode = h_src->histogram_mode;
920         h_dest->histogram_channel = h_src->histogram_channel;
921
922 }
923
924 /* duration:
925     0 = hide
926     1 = show
927    2+ = show for duration tenths of a second
928    -1 = use default duration
929  */
930 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
931 {
932         OverlayStateData *osd = image_get_osd_data(imd);
933
934         if (!osd) return;
935
936         if (flag >= IMAGE_OSD_COUNT) return;
937         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
938         if (duration > 1) duration += 1;
939
940         osd->icon_time[flag] = duration;
941
942         image_osd_update_schedule(osd, FALSE);
943         image_osd_timer_schedule(osd);
944 }
945 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */