Indentation and white lines minor fixes.
[geeqie.git] / src / image-overlay.c
1 /*
2  * Geeqie
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, *text2;
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         g_free(size);
184         g_free(ct);
185         g_free(name_escaped);
186
187         {
188         GString *buf = g_string_sized_new(FILEDATA_MARKS_SIZE * 2);
189         FileData *fd = image_get_fd(imd);
190         
191         for (i=0; i < FILEDATA_MARKS_SIZE; i++) 
192                 {
193                         
194                 g_string_append_printf(buf, fd->marks[i] ? " <span background='#FF00FF'>%c</span>" : " %c", '1' + i);
195                 }
196         text2 = g_strdup_printf("%s\n%s", text, buf->str);
197         }
198         
199         layout = gtk_widget_create_pango_layout(imd->pr, NULL);
200         pango_layout_set_markup(layout, text2, -1);
201         g_free(text2);
202         g_free(text);
203     
204         pango_layout_get_pixel_size(layout, &width, &height);
205
206         width += 10;
207         height += 10;
208
209         pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
210         pixbuf_set_rect_fill(pixbuf, 3, 3, width-6, height-6, 240, 240, 240, 210);
211         pixbuf_set_rect(pixbuf, 0, 0, width, height, 240, 240, 240, 80, 1, 1, 1, 1);
212         pixbuf_set_rect(pixbuf, 1, 1, width-2, height-2, 240, 240, 240, 130, 1, 1, 1, 1);
213         pixbuf_set_rect(pixbuf, 2, 2, width-4, height-4, 240, 240, 240, 180, 1, 1, 1, 1);
214         pixbuf_pixel_set(pixbuf, 0, 0, 0, 0, 0, 0);
215         pixbuf_pixel_set(pixbuf, width - 1, 0, 0, 0, 0, 0);
216         pixbuf_pixel_set(pixbuf, 0, height - 1, 0, 0, 0, 0);
217         pixbuf_pixel_set(pixbuf, width - 1, height - 1, 0, 0, 0, 0);
218
219         pixbuf_draw_layout(pixbuf, layout, imd->pr, 5, 5, 0, 0, 0, 255);
220
221         g_object_unref(G_OBJECT(layout));
222
223         return pixbuf;
224 }
225
226 static GdkPixbuf *image_osd_icon_pixbuf(ImageOSDFlag flag)
227 {
228         static GdkPixbuf **icons = NULL;
229         GdkPixbuf *icon = NULL;
230
231         if (!icons) icons = g_new0(GdkPixbuf *, IMAGE_OSD_COUNT);
232         if (icons[flag]) return icons[flag];
233
234         if (osd_icons[flag].key)
235                 {
236                 icon = pixbuf_inline(osd_icons[flag].key);
237                 }
238
239         if (!icon)
240                 {
241                 icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 24, 24);
242                 pixbuf_set_rect_fill(icon, 1, 1, 22, 22, 255, 255, 255, 200);
243                 pixbuf_set_rect(icon, 0, 0, 24, 24, 0, 0, 0, 128, 1, 1, 1, 1);
244                 switch (flag)
245                         {
246                         case IMAGE_OSD_ROTATE_AUTO:
247                                 pixbuf_set_rect(icon, 3, 8, 11, 12,
248                                                 0, 0, 0, 255,
249                                                 3, 0, 3, 0);
250                                 pixbuf_draw_triangle(icon, 14, 3, 6, 12,
251                                                      20, 9, 14, 15, 14, 3,
252                                                      0, 0, 0, 255);
253                                 break;
254                         case IMAGE_OSD_ROTATE_USER:
255                                 break;
256                         case IMAGE_OSD_COLOR:
257                                 pixbuf_set_rect_fill(icon, 3, 3, 18, 6, 200, 0, 0, 255);
258                                 pixbuf_set_rect_fill(icon, 3, 9, 18, 6, 0, 200, 0, 255);
259                                 pixbuf_set_rect_fill(icon, 3, 15, 18, 6, 0, 0, 200, 255);
260                                 break;
261                         case IMAGE_OSD_FIRST:
262                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 3, 0);
263                                 pixbuf_draw_triangle(icon, 6, 5, 12, 6,
264                                                      12, 5, 18, 11, 6, 11,
265                                                      0, 0, 0, 255);
266                                 break;
267                         case IMAGE_OSD_LAST:
268                                 pixbuf_set_rect(icon, 3, 3, 18, 18, 0, 0, 0, 200, 3, 3, 0, 3);
269                                 pixbuf_draw_triangle(icon, 6, 12, 12, 6,
270                                                      12, 18, 6, 12, 18, 12,
271                                                      0, 0, 0, 255);
272                                 break;
273                         case IMAGE_OSD_ICON:
274                                 pixbuf_set_rect_fill(icon, 11, 3, 3, 12, 0, 0, 0, 255);
275                                 pixbuf_set_rect_fill(icon, 11, 17, 3, 3, 0, 0, 0, 255);
276                                 break;
277                         default:
278                                 break;
279                         }
280                 }
281
282         icons[flag] = icon;
283
284         return icon;
285 }
286
287 static void image_osd_icon_show(OverlayStateData *osd, ImageOSDFlag flag)
288 {
289         GdkPixbuf *pixbuf;
290
291         if (osd->icon_id[flag]) return;
292
293         pixbuf = image_osd_icon_pixbuf(flag);
294         if (!pixbuf) return;
295
296         osd->icon_id[flag] = image_overlay_add(osd->imd, pixbuf,
297                                                osd_icons[flag].x, osd_icons[flag].y,
298                                                TRUE, FALSE);
299 }
300
301 static void image_osd_icon_hide(OverlayStateData *osd, ImageOSDFlag flag)
302 {
303         if (osd->icon_id[flag])
304                 {
305                 image_overlay_remove(osd->imd, osd->icon_id[flag]);
306                 osd->icon_id[flag] = 0;
307                 }
308 }
309
310 static gint image_osd_update_cb(gpointer data)
311 {
312         OverlayStateData *osd = data;
313
314         if (osd->show_info)
315                 {
316                 if (osd->changed_states & IMAGE_STATE_IMAGE)
317                         {
318                         GdkPixbuf *pixbuf;
319
320                         pixbuf = image_osd_info_render(osd->imd);
321                         if (osd->ovl_info == 0)
322                                 {
323                                 osd->ovl_info = image_overlay_add(osd->imd, pixbuf,
324                                                                   OSD_INFO_X, OSD_INFO_Y, TRUE, FALSE);
325                                 }
326                         else
327                                 {
328                                 image_overlay_set(osd->imd, osd->ovl_info, pixbuf, OSD_INFO_X, OSD_INFO_Y);
329                                 }
330                         g_object_unref(pixbuf);
331                         }
332                 }
333         else
334                 {
335                 if (osd->ovl_info)
336                         {
337                         image_overlay_remove(osd->imd, osd->ovl_info);
338                         osd->ovl_info = 0;
339                         }
340                 }
341
342         if (osd->show_status)
343                 {
344                 gint i;
345
346                 if (osd->changed_states & IMAGE_STATE_IMAGE)
347                         {
348                         for (i = 0; i < IMAGE_OSD_COUNT; i++)
349                                 {
350                                 if (osd_icons[i].reset) osd->icon_time[i] = 0;
351                                 }
352                         }
353
354                 if (osd->changed_states & IMAGE_STATE_COLOR_ADJ)
355                         {
356                         osd->icon_time[IMAGE_OSD_COLOR] = IMAGE_OSD_DEFAULT_DURATION + 1;
357                         image_osd_timer_schedule(osd);
358                         }
359
360                 if (osd->changed_states & IMAGE_STATE_ROTATE_AUTO)
361                         {
362                         gint n = 0;
363
364                         if (osd->imd->state & IMAGE_STATE_ROTATE_AUTO)
365                                 {
366                                 n = 1;
367                                 if (!osd->imd->cm) n += IMAGE_OSD_DEFAULT_DURATION;
368                                 }
369
370                         osd->icon_time[IMAGE_OSD_ROTATE_AUTO] = n;
371                         image_osd_timer_schedule(osd);
372                         }
373
374                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
375                         {
376                         if (osd->icon_time[i] > 0)
377                                 {
378                                 image_osd_icon_show(osd, i);
379                                 }
380                         else
381                                 {
382                                 image_osd_icon_hide(osd, i);
383                                 }
384                         }
385                 }
386         else
387                 {
388                 gint i;
389
390                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
391                         {
392                         image_osd_icon_hide(osd, i);
393                         }
394                 }
395
396         osd->changed_states = IMAGE_STATE_NONE;
397         osd->idle_id = -1;
398         return FALSE;
399 }
400
401 static void image_osd_update_schedule(OverlayStateData *osd, gint force)
402 {
403         if (force) osd->changed_states |= IMAGE_STATE_IMAGE;
404
405         if (osd->idle_id == -1)
406                 {
407                 osd->idle_id = g_idle_add_full(G_PRIORITY_HIGH, image_osd_update_cb, osd, NULL);
408                 }
409 }
410
411 void image_osd_update(ImageWindow *imd)
412 {
413         OverlayStateData *osd;
414
415         if (!imd) return;
416
417         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
418         if (!osd) return;
419
420         image_osd_update_schedule(osd, TRUE);
421 }
422
423 static gint image_osd_timer_cb(gpointer data)
424 {
425         OverlayStateData *osd = data;
426         gint done = TRUE;
427         gint changed = FALSE;
428         gint i;
429
430         for (i = 0; i < IMAGE_OSD_COUNT; i++)
431                 {
432                 if (osd->icon_time[i] > 1)
433                         {
434                         osd->icon_time[i]--;
435                         if (osd->icon_time[i] < 2)
436                                 {
437                                 osd->icon_time[i] = 0;
438                                 changed = TRUE;
439                                 }
440                         else
441                                 {
442                                 done = FALSE;
443                                 }
444                         }
445                 }
446
447         if (changed) image_osd_update_schedule(osd, FALSE);
448
449         if (done)
450                 {
451                 osd->timer_id = -1;
452                 return FALSE;
453                 }
454
455         return TRUE;
456 }
457
458 static void image_osd_timer_schedule(OverlayStateData *osd)
459 {
460         if (osd->timer_id == -1)
461                 {
462                 osd->timer_id = g_timeout_add(100, image_osd_timer_cb, osd);
463                 }
464 }
465
466 static void image_osd_state_cb(ImageWindow *imd, ImageState state, gpointer data)
467 {
468         OverlayStateData *osd = data;
469
470         osd->changed_states |= state;
471         image_osd_update_schedule(osd, FALSE);
472 }
473
474 static void image_osd_free(OverlayStateData *osd)
475 {
476         if (!osd) return;
477
478         if (osd->idle_id != -1) g_source_remove(osd->idle_id);
479         if (osd->timer_id != -1) g_source_remove(osd->timer_id);
480
481         if (osd->imd)
482                 {
483                 gint i;
484
485                 g_object_set_data(G_OBJECT(osd->imd->pr), "IMAGE_OVERLAY_DATA", NULL);
486                 g_signal_handler_disconnect(osd->imd->pr, osd->destroy_id);
487
488                 image_set_state_func(osd->imd, NULL, NULL);
489                 image_overlay_remove(osd->imd, osd->ovl_info);
490
491                 for (i = 0; i < IMAGE_OSD_COUNT; i++)
492                         {
493                         image_osd_icon_hide(osd, i);
494                         }
495                 }
496
497         g_free(osd);
498 }
499
500 static void image_osd_remove(ImageWindow *imd)
501 {
502         OverlayStateData *osd;
503
504         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
505         image_osd_free(osd);
506 }
507
508 static void image_osd_destroy_cb(GtkWidget *widget, gpointer data)
509 {
510         OverlayStateData *osd = data;
511
512         osd->imd = NULL;
513         image_osd_free(osd);
514 }
515
516 static void image_osd_enable(ImageWindow *imd, gint info, gint status)
517 {
518         OverlayStateData *osd;
519
520         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
521         if (!osd)
522                 {
523                 osd = g_new0(OverlayStateData, 1);
524                 osd->imd = imd;
525                 osd->idle_id = -1;
526                 osd->timer_id = -1;
527
528                 osd->destroy_id = g_signal_connect(G_OBJECT(imd->pr), "destroy",
529                                                    G_CALLBACK(image_osd_destroy_cb), osd);
530                 g_object_set_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA", osd);
531
532                 image_set_state_func(osd->imd, image_osd_state_cb, osd);
533                 }
534
535         if (osd->show_info != info ||
536             osd->show_status != status)
537                 {
538                 osd->show_info = info;
539                 osd->show_status = status;
540
541                 image_osd_update_schedule(osd, TRUE);
542                 }
543 }
544
545 void image_osd_set(ImageWindow *imd, gint info, gint status)
546 {
547         if (!imd) return;
548
549         if (!info && !status)
550                 {
551                 image_osd_remove(imd);
552                 return;
553                 }
554
555         image_osd_enable(imd, info, status);
556 }
557
558 gint image_osd_get(ImageWindow *imd, gint *info, gint *status)
559 {
560         OverlayStateData *osd;
561
562         if (!imd) return FALSE;
563
564         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
565         if (!osd) return FALSE;
566
567         if (info) *info = osd->show_info;
568         if (status) *status = osd->show_status;
569
570         return TRUE;
571 }
572
573 /* duration:
574     0 = hide
575     1 = show
576    2+ = show for duration tenths of a second
577    -1 = use default duration
578  */
579 void image_osd_icon(ImageWindow *imd, ImageOSDFlag flag, gint duration)
580 {
581         OverlayStateData *osd;
582
583         if (!imd) return;
584
585         osd = g_object_get_data(G_OBJECT(imd->pr), "IMAGE_OVERLAY_DATA");
586         if (!osd) return;
587
588         if (flag < IMAGE_OSD_NONE || flag >= IMAGE_OSD_COUNT) return;
589         if (duration < 0) duration = IMAGE_OSD_DEFAULT_DURATION;
590         if (duration > 1) duration += 1;
591
592         osd->icon_time[flag] = duration;
593
594         image_osd_update_schedule(osd, FALSE);
595         image_osd_timer_schedule(osd);
596 }