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