First implementation of marks for all photos.
[geeqie.git] / src / image-overlay.c
1 /*
2  * GQview
3  * (C) 2006 John Ellis
4  *
5  * Author: John Ellis
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include "gqview.h"
13 #include "image-overlay.h"
14
15 #include "collect.h"
16 #include "filelist.h"
17 #include "image.h"
18 #include "img-view.h"
19 #include "layout.h"
20 #include "pixbuf-renderer.h"
21 #include "pixbuf_util.h"
22
23
24 /*
25  *----------------------------------------------------------------------------
26  * image overlay
27  *----------------------------------------------------------------------------
28  */
29
30 typedef struct _OverlayStateData OverlayStateData;
31 struct _OverlayStateData {
32         ImageWindow *imd;
33         ImageState changed_states;
34
35         gint show_info;
36         gint show_status;
37
38         gint ovl_info;
39
40         gint icon_time[IMAGE_OSD_COUNT];
41         gint icon_id[IMAGE_OSD_COUNT];
42
43         gint idle_id;
44         gint timer_id;
45         gulong destroy_id;
46 };
47
48
49 typedef struct _OSDIcon OSDIcon;
50 struct _OSDIcon {
51         gint reset;     /* reset on new image */
52         gint x;         /* x, y offset */
53         gint y;
54         gchar *key;     /* inline pixbuf */
55 };
56
57 static OSDIcon osd_icons[] = {
58         {  TRUE,   0,   0, NULL },                      /* none */
59         {  TRUE, -10, -10, NULL },                      /* auto rotated */
60         {  TRUE, -10, -10, NULL },                      /* user rotated */
61         {  TRUE, -40, -10, NULL },                      /* color embedded */
62         {  TRUE, -70, -10, NULL },                      /* first image */
63         {  TRUE, -70, -10, NULL },                      /* last image */
64         { FALSE, -70, -10, NULL },                      /* osd enabled */
65         { FALSE, 0, 0, NULL }
66 };
67
68 #define OSD_DATA "overlay-data"
69
70 #define OSD_INFO_X 10
71 #define OSD_INFO_Y -10
72
73 #define IMAGE_OSD_DEFAULT_DURATION 30
74
75
76 static void image_osd_timer_schedule(OverlayStateData *osd);
77
78
79 static GdkPixbuf *image_osd_info_render(ImageWindow *imd)
80 {
81         GdkPixbuf *pixbuf;
82         gint width, height;
83         PangoLayout *layout;
84         const gchar *name;
85         gchar *name_escaped;
86         gchar *text;
87         gchar *size;
88         gint n, t;
89         CollectionData *cd;
90         CollectInfo *info;
91     gchar *ct;
92     int i;
93     
94         name = image_get_name(imd);
95         if (name)
96                 {
97                 name_escaped = g_markup_escape_text(name, -1);
98                 }
99         else
100                 {
101                 name_escaped = NULL;
102                 }
103
104         cd = image_get_collection(imd, &info);
105         if (cd)
106                 {
107                 gchar *buf;
108
109                 t = g_list_length(cd->list);
110                 n = g_list_index(cd->list, info) + 1;
111                 buf = g_markup_escape_text((cd->name) ? cd->name : _("Untitled"), -1);
112                 ct = g_strdup_printf("<i>%s</i>\n", buf);
113                 g_free(buf);
114                 }
115         else
116                 {
117                 LayoutWindow *lw;
118
119                 lw = layout_find_by_image(imd);
120                 if (lw)
121                         {
122                         if (lw->slideshow)
123                                 {
124                                 n = g_list_length(lw->slideshow->list_done);
125                                 t = n + g_list_length(lw->slideshow->list);
126                                 if (n == 0) n = t;
127                                 }
128                         else
129                                 {
130                                 t = layout_list_count(lw, NULL);
131                                 n = layout_list_get_index(lw, image_get_path(lw->image)) + 1;
132                                 }
133                         }
134                 else if (view_window_find_image(imd, &n, &t))
135                         {
136                         n++;
137                         }
138                 else
139                         {
140                         t = 1;
141                         n = 1;
142                         }
143
144                 if (n < 1) n = 1;
145                 if (t < 1) t = 1;
146
147                 ct = g_strdup("");
148                 }
149
150         size = text_from_size_abrev(imd->size);
151         if (!name_escaped)
152                 {
153                 text = g_strdup_printf(_("Untitled"));
154                 }
155         else if (imd->unknown)
156                 {
157                 text = g_strdup_printf("%s(%d/%d) <b>%s</b>\n%s - %s", ct,
158                                        n, t, name_escaped,
159                                        text_from_time(imd->mtime), size);
160                 }
161         else
162                 {
163                 gint w, h;
164
165                 if (imd->delay_flip &&
166                     imd->il && imd->il->pixbuf &&
167                     image_get_pixbuf(imd) != imd->il->pixbuf)
168                         {
169                         w = gdk_pixbuf_get_width(imd->il->pixbuf);
170                         h = gdk_pixbuf_get_height(imd->il->pixbuf);
171                         }
172                 else
173                         {
174                         pixbuf_renderer_get_image_size(PIXBUF_RENDERER(imd->pr), &w, &h);
175                         }
176
177                 text = g_strdup_printf("%s(%d/%d) <b>%s</b>\n%d x %d - %s - %s", ct,
178                                        n, t, name_escaped,
179                                        w, h,
180                                        text_from_time(imd->mtime), size);
181
182         
183
184         }
185         g_free(size);
186         g_free(ct);
187         g_free(name_escaped);
188     for (i=0; i < FILEDATA_MARKS_SIZE; i++) {
189         
190     
191         layout = gtk_widget_create_pango_layout(imd->pr, NULL);
192         pango_layout_set_markup(layout, text, -1);
193         g_free(text);
194
195         pango_layout_get_pixel_size(layout, &width, &height);
196
197         width += 10;
198         height += 10;
199
200         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
201         pixbuf_set_rect_fill(pixbuf, 3, 3, width-6, height-6, 240, 240, 240, 210);
202         pixbuf_set_rect(pixbuf, 0, 0, width, height, 240, 240, 240, 80, 1, 1, 1, 1);
203         pixbuf_set_rect(pixbuf, 1, 1, width-2, height-2, 240, 240, 240, 130, 1, 1, 1, 1);
204         pixbuf_set_rect(pixbuf, 2, 2, width-4, height-4, 240, 240, 240, 180, 1, 1, 1, 1);
205         pixbuf_pixel_set(pixbuf, 0, 0, 0, 0, 0, 0);
206         pixbuf_pixel_set(pixbuf, width - 1, 0, 0, 0, 0, 0);
207         pixbuf_pixel_set(pixbuf, 0, height - 1, 0, 0, 0, 0);
208         pixbuf_pixel_set(pixbuf, width - 1, height - 1, 0, 0, 0, 0);
209
210         pixbuf_draw_layout(pixbuf, layout, imd->pr, 5, 5, 0, 0, 0, 255);
211
212         g_object_unref(G_OBJECT(layout));
213
214         return pixbuf;
215 }
216
217 static GdkPixbuf *image_osd_icon_pixbuf(ImageOSDFlag flag)
218 {
219         static GdkPixbuf **icons = NULL;
220         GdkPixbuf *icon = NULL;
221
222         if (!icons) icons = g_new0(GdkPixbuf *, IMAGE_OSD_COUNT);
223         if (icons[flag]) return icons[flag];
224
225         if (osd_icons[flag].key)
226                 {
227                 icon = pixbuf_inline(osd_icons[flag].key);
228                 }
229
230         if (!icon)
231                 {
232                 icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24);
233                 pixbuf_set_rect_fill(icon, 1, 1, 22, 22, 255, 255, 255, 200);
234                 pixbuf_set_rect(icon, 0, 0, 24, 24, 0, 0, 0, 128, 1, 1, 1, 1);
235                 switch (flag)
236                         {
237                         case IMAGE_OSD_ROTATE_AUTO:
238                                 pixbuf_set_rect(icon, 3, 8, 11, 12,
239                                                 0, 0, 0, 255,
240                                                 3, 0, 3, 0);
241                                 pixbuf_draw_triangle(icon, 14, 3, 6, 12,
242                                                      20, 9, 14, 15, 14, 3,
243                                                      0, 0, 0, 255);
244                                 break;
245                         case IMAGE_OSD_ROTATE_USER:
246                                 break;
247                         case IMAGE_OSD_COLOR:
248                                 pixbuf_set_rect_fill(icon, 3, 3, 18, 6, 200, 0, 0, 255);
249                                 pixbuf_set_rect_fill(icon, 3, 9, 18, 6, 0, 200, 0, 255);
250                                 pixbuf_set_rect_fill(icon, 3, 15, 18, 6, 0, 0, 200, 255);
251                                 break;
252                         case IMAGE_OSD_FIRST:
253                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 3, 0);
254                                 pixbuf_draw_triangle(icon, 6, 5, 12, 6,
255                                                      12, 5, 18, 11, 6, 11,
256                                                      0, 0, 0, 255);
257                                 break;
258                         case IMAGE_OSD_LAST:
259                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 0, 3);
260                                 pixbuf_draw_triangle(icon, 6, 12, 12, 6,
261                                                      12, 18, 6, 12, 18, 12,
262                                                      0, 0, 0, 255);
263                                 break;
264                         case IMAGE_OSD_ICON:
265                                 pixbuf_set_rect_fill(icon, 11, 3, 3, 12, 0, 0, 0, 255);
266                                 pixbuf_set_rect_fill(icon, 11, 17, 3, 3, 0, 0, 0, 255);
267                                 break;
268                         default:
269                                 break;
270                         }
271                 }
272
273         icons[flag] = icon;
274
275         return icon;
276 }
277
278 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
279 {
280         GdkPixbuf *pixbuf;
281
282         if (osd->icon_id[flag]) return;
283
284         pixbuf = image_osd_icon_pixbuf(flag);
285         if (!pixbuf) return;
286
287         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
288                                                osd_icons[flag].x, osd_icons[flag].y,
289                                                TRUE, FALSE);
290 }
291
292 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
293 {
294         if (osd->icon_id[flag])
295                 {
296                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
297                 osd->icon_id[flag] = 0;
298                 }
299 }
300
301 static gint image_osd_update_cb(gpointer data)
302 {
303         OverlayStateData *osd = data;
304
305         if (osd->show_info)
306                 {
307                 if (osd->changed_states & IMAGE_STATE_IMAGE)
308                         {
309                         GdkPixbuf *pixbuf;
310
311                         pixbuf = image_osd_info_render(osd->imd);
312                         if (osd->ovl_info == 0)
313                                 {
314                                 osd->ovl_info = image_overlay_add(osd->imd, pixbuf,
315                                                                   OSD_INFO_X, OSD_INFO_Y, TRUE, FALSE);
316                                 }
317                         else
318                                 {
319                                 image_overlay_set(osd->imd, osd->ovl_info, pixbuf, OSD_INFO_X, OSD_INFO_Y);
320                                 }
321                         g_object_unref(pixbuf);
322                         }
323                 }
324         else
325                 {
326                 if (osd->ovl_info)
327                         {
328                         image_overlay_remove(osd->imd, osd->ovl_info);
329                         osd->ovl_info = 0;
330                         }
331                 }
332
333         if (osd->show_status)
334                 {
335                 gint i;
336
337                 if (osd->changed_states & IMAGE_STATE_IMAGE)
338                         {
339                         for (i = 0; i < IMAGE_OSD_COUNT; i++)
340                                 {
341                                 if (osd_icons[i].reset) osd->icon_time[i] = 0;
342                                 }
343                         }
344
345                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
346                         {
347                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
348                         image_osd_timer_schedule(osd);
349                         }
350
351                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
352                         {
353                         gint n = 0;
354
355                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
356                                 {
357                                 n = 1;
358                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
359                                 }
360
361                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
362                         image_osd_timer_schedule(osd);
363                         }
364
365                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
366                         {
367                         if (osd->icon_time[i] > 0)
368                                 {
369                                 image_osd_icon_show(osd, i);
370                                 }
371                         else
372                                 {
373                                 image_osd_icon_hide(osd, i);
374                                 }
375                         }
376                 }
377         else
378                 {
379                 gint i;
380
381                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
382                         {
383                         image_osd_icon_hide(osd, i);
384                         }
385                 }
386
387         osd->changed_states = IMAGE_STATE_NONE;
388         osd->idle_id = -1;
389         return FALSE;
390 }
391
392 static void image_osd_update_schedule(OverlayStateData *osd, gint force)
393 {
394         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
395
396         if (osd->idle_id == -1)
397                 {
398                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
399                 }
400 }
401
402 void image_osd_update(ImageWindow *imd)
403 {
404         OverlayStateData *osd;
405
406         if (!imd) return;
407
408         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
409         if (!osd) return;
410
411         image_osd_update_schedule(osd, TRUE);
412 }
413
414 static gint image_osd_timer_cb(gpointer data)
415 {
416         OverlayStateData *osd = data;
417         gint done = TRUE;
418         gint changed = FALSE;
419         gint i;
420
421         for (i = 0; i < IMAGE_OSD_COUNT; i++)
422                 {
423                 if (osd->icon_time[i] > 1)
424                         {
425                         osd->icon_time[i]--;
426                         if (osd->icon_time[i] < 2)
427                                 {
428                                 osd->icon_time[i] = 0;
429                                 changed = TRUE;
430                                 }
431                         else
432                                 {
433                                 done = FALSE;
434                                 }
435                         }
436                 }
437
438         if (changed) image_osd_update_schedule(osd, FALSE);
439
440         if (done)
441                 {
442                 osd->timer_id = -1;
443                 return FALSE;
444                 }
445
446         return TRUE;
447 }
448
449 static void image_osd_timer_schedule(OverlayStateData *osd)
450 {
451         if (osd->timer_id == -1)
452                 {
453                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
454                 }
455 }
456
457 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
458 {
459         OverlayStateData *osd = data;
460
461         osd->changed_states |= state;
462         image_osd_update_schedule(osd, FALSE);
463 }
464
465 static void image_osd_free(OverlayStateData *osd)
466 {
467         if (!osd) return;
468
469         if (osd->idle_id != -1) g_source_remove(osd->idle_id);
470         if (osd->timer_id != -1) g_source_remove(osd->timer_id);
471
472         if (osd->imd)
473                 {
474                 gint i;
475
476                 g_object_set_data(G_OBJECT(osd->imd->pr), "IMAGE_OVERLAY_DATA", NULL);
477                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
478
479                 image_set_state_func(osd->imd, NULL, NULL);
480                 image_overlay_remove(osd->imd, osd->ovl_info);
481
482                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
483                         {
484                         image_osd_icon_hide(osd, i);
485                         }
486                 }
487
488         g_free(osd);
489 }
490
491 static void image_osd_remove(ImageWindow *imd)
492 {
493         OverlayStateData *osd;
494
495         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
496         image_osd_free(osd);
497 }
498
499 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
500 {
501         OverlayStateData *osd = data;
502
503         osd->imd = NULL;
504         image_osd_free(osd);
505 }
506
507 static void image_osd_enable(ImageWindow *imd, gint info, gint status)
508 {
509         OverlayStateData *osd;
510
511         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
512         if (!osd)
513                 {
514                 osd = g_new0(OverlayStateData, 1);
515                 osd->imd = imd;
516                 osd->idle_id = -1;
517                 osd->timer_id = -1;
518
519                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
520                                                    G_CALLBACK(image_osd_destroy_cb), osd);
521                 g_object_set_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA", osd);
522
523                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
524                 }
525
526         if (osd->show_info != info ||
527             osd->show_status != status)
528                 {
529                 osd->show_info = info;
530                 osd->show_status = status;
531
532                 image_osd_update_schedule(osd, TRUE);
533                 }
534 }
535
536 void image_osd_set(ImageWindow *imd, gint info, gint status)
537 {
538         if (!imd) return;
539
540         if (!info && !status)
541                 {
542                 image_osd_remove(imd);
543                 return;
544                 }
545
546         image_osd_enable(imd, info, status);
547 }
548
549 gint image_osd_get(ImageWindow *imd, gint *info, gint *status)
550 {
551         OverlayStateData *osd;
552
553         if (!imd) return FALSE;
554
555         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
556         if (!osd) return FALSE;
557
558         if (info) *info = osd->show_info;
559         if (status) *status = osd->show_status;
560
561         return TRUE;
562 }
563
564 /* duration:
565     0 = hide
566     1 = show
567    2+ = show for duration tenths of a second
568    -1 = use default duration
569  */
570 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
571 {
572         OverlayStateData *osd;
573
574         if (!imd) return;
575
576         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
577         if (!osd) return;
578
579         if (flag < IMAGE_OSD_NONE || flag >= IMAGE_OSD_COUNT) return;
580         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
581         if (duration > 1) duration += 1;
582
583         osd->icon_time[flag] = duration;
584
585         image_osd_update_schedule(osd, FALSE);
586         image_osd_timer_schedule(osd);
587 }
588