d6a0a7e2718d8d580b5b497ae205538d1ba24779
[geeqie.git] / src / image.c
1 /*
2  * GQview
3  * (C) 2005 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
13 #include "gqview.h"
14 #include "image.h"
15
16
17 #include "image-load.h"
18 #include "collect.h"
19 #include "exif.h"
20 #include "pixbuf-renderer.h"
21 #include "pixbuf_util.h"
22 #include "ui_fileops.h"
23
24 #include <math.h>
25
26
27 /* size of the image loader buffer (512 bytes x defined number) */
28 #define IMAGE_LOAD_BUFFER_COUNT 8
29
30 /* define this so that more bytes are read per idle loop on larger images (> 1MB) */
31 #define IMAGE_THROTTLE_LARGER_IMAGES 1
32
33 /* throttle factor to increase read bytes by (2 is double, 3 is triple, etc.) */
34 #define IMAGE_THROTTLE_FACTOR 4
35
36 /* the file size at which throttling take place */
37 #define IMAGE_THROTTLE_THRESHOLD 1048576
38
39 #define IMAGE_AUTO_REFRESH_TIME 3000
40
41
42 static GList *image_list = NULL;
43
44
45 static void image_update_title(ImageWindow *imd);
46
47 /*
48  *-------------------------------------------------------------------
49  * 'signals'
50  *-------------------------------------------------------------------
51  */
52
53 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
54 {
55         ImageWindow *imd = data;
56
57         if (imd->func_button)
58                 {
59                 imd->func_button(imd, event->button, event->time,
60                                  event->x, event->y, event->state, imd->data_button);
61                 }
62 }
63
64 static void image_scroll_notify_cb(PixbufRenderer *pr, gpointer data)
65 {
66         ImageWindow *imd = data;
67
68         if (imd->func_scroll_notify && pr->scale)
69                 {
70                 imd->func_scroll_notify(imd,
71                                         (gint)((gdouble)pr->x_scroll / pr->scale),
72                                         (gint)((gdouble)pr->y_scroll / pr->scale),
73                                         (gint)((gdouble)pr->image_width - pr->vis_width / pr->scale),
74                                         (gint)((gdouble)pr->image_height - pr->vis_height / pr->scale),
75                                         imd->data_scroll_notify);
76                 }
77 }
78
79 static void image_update_util(ImageWindow *imd)
80 {
81         if (imd->func_update) imd->func_update(imd, imd->data_update);
82 }
83
84 static void image_zoom_cb(PixbufRenderer *pr, gdouble zoom, gpointer data)
85 {
86         ImageWindow *imd = data;
87
88         if (imd->title_show_zoom) image_update_title(imd);
89         image_update_util(imd);
90 }
91
92 static void image_complete_util(ImageWindow *imd, gint preload)
93 {
94         if (imd->il && image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il)) return;
95
96         if (debug) printf("image load completed \"%s\" (%s)\n",
97                           (preload) ? imd->read_ahead_path : imd->image_path,
98                           (preload) ? "preload" : "current");
99
100         if (!preload) imd->completed = TRUE;
101         if (imd->func_complete) imd->func_complete(imd, preload, imd->data_complete);
102 }
103
104 static void image_render_complete_cb(PixbufRenderer *pr, gpointer data)
105 {
106         ImageWindow *imd = data;
107
108         image_complete_util(imd, FALSE);
109 }
110
111 static void image_new_util(ImageWindow *imd)
112 {
113         if (imd->func_new) imd->func_new(imd, imd->data_new);
114 }
115
116 /*
117  *-------------------------------------------------------------------
118  * misc
119  *-------------------------------------------------------------------
120  */
121
122 static void image_update_title(ImageWindow *imd)
123 {
124         gchar *title = NULL;
125         gchar *zoom = NULL;
126         gchar *collection = NULL;
127
128         if (!imd->top_window) return;
129
130         if (imd->collection && collection_to_number(imd->collection) >= 0)
131                 {
132                 const gchar *name;
133                 name = imd->collection->name;
134                 if (!name) name = _("Untitled");
135                 collection = g_strdup_printf(" (Collection %s)", name);
136                 }
137
138         if (imd->title_show_zoom)
139                 {
140                 gchar *buf = image_zoom_get_as_text(imd);
141                 zoom = g_strconcat(" [", buf, "]", NULL);
142                 g_free(buf);
143                 }
144
145         title = g_strdup_printf("%s%s%s%s%s%s",
146                 imd->title ? imd->title : "",
147                 imd->image_name ? imd->image_name : "",
148                 zoom ? zoom : "",
149                 collection ? collection : "",
150                 imd->image_name ? " - " : "",
151                 imd->title_right ? imd->title_right : "");
152
153         gtk_window_set_title(GTK_WINDOW(imd->top_window), title);
154
155         g_free(title);
156         g_free(zoom);
157         g_free(collection);
158 }
159
160 /*
161  *-------------------------------------------------------------------
162  * rotation, flip, etc.
163  *-------------------------------------------------------------------
164  */
165
166 static void image_alter_real(ImageWindow *imd, AlterType type, gint clamp)
167 {
168         PixbufRenderer *pr;
169         GdkPixbuf *new = NULL;
170         gint x, y;
171         gint t;
172
173         pr = (PixbufRenderer *)imd->pr;
174
175         imd->delay_alter_type = ALTER_NONE;
176
177         if (!pr->pixbuf) return;
178
179         x = pr->x_scroll + (pr->vis_width / 2);
180         y = pr->y_scroll + (pr->vis_height / 2);
181
182         switch (type)
183                 {
184                 case ALTER_ROTATE_90:
185                         new = pixbuf_copy_rotate_90(pr->pixbuf, FALSE);
186                         t = x;
187                         x = pr->height - y;
188                         y = t;
189                         break;
190                 case ALTER_ROTATE_90_CC:
191                         new = pixbuf_copy_rotate_90(pr->pixbuf, TRUE);
192                         t = x;
193                         x = y;
194                         y = pr->width - t;
195                         break;
196                 case ALTER_ROTATE_180:
197                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, TRUE);
198                         x = pr->width - x;
199                         y = pr->height - y;
200                         break;
201                 case ALTER_MIRROR:
202                         new = pixbuf_copy_mirror(pr->pixbuf, TRUE, FALSE);
203                         x = pr->width - x;
204                         break;
205                 case ALTER_FLIP:
206                         new = pixbuf_copy_mirror(pr->pixbuf, FALSE, TRUE);
207                         y = pr->height - y;
208                         break;
209                 case ALTER_NONE:
210                 default:
211                         return;
212                         break;
213                 }
214
215         if (!new) return;
216
217         pixbuf_renderer_set_pixbuf(pr, new, pr->zoom);
218         g_object_unref(new);
219
220         if (clamp && pr->zoom != 0.0)
221                 {
222                 pixbuf_renderer_scroll(pr, x - (pr->vis_width / 2), y - (pr->vis_height / 2));
223                 }
224 }
225
226 static void image_post_process(ImageWindow *imd, gint clamp)
227 {
228         if (exif_rotate_enable && image_get_pixbuf(imd))
229                 {
230                 ExifData *ed;
231                 gint orientation;
232
233                 ed = exif_read(imd->image_path);
234                 if (ed && exif_get_integer(ed, "Orientation", &orientation))
235                         {
236                         /* see http://jpegclub.org/exif_orientation.html 
237                           1        2       3      4         5            6           7          8
238
239                         888888  888888      88  88      8888888888  88                  88  8888888888
240                         88          88      88  88      88  88      88  88          88  88      88  88
241                         8888      8888    8888  8888    88          8888888888  8888888888          88
242                         88          88      88  88
243                         88          88  888888  888888
244                         */
245
246                         switch (orientation)
247                                 {
248                                 case EXIF_ORIENTATION_TOP_LEFT:
249                                         /* normal -- nothing to do */
250                                         break;
251                                 case EXIF_ORIENTATION_TOP_RIGHT:
252                                         /* mirrored */
253                                         imd->delay_alter_type = ALTER_MIRROR;
254                                         break;
255                                 case EXIF_ORIENTATION_BOTTOM_RIGHT:
256                                         /* upside down */
257                                         imd->delay_alter_type = ALTER_ROTATE_180;
258                                         break;
259                                 case EXIF_ORIENTATION_BOTTOM_LEFT:
260                                         /* flipped */
261                                         imd->delay_alter_type = ALTER_FLIP;
262                                         break;
263                                 case EXIF_ORIENTATION_LEFT_TOP:
264                                         /* not implemented -- too wacky to fix in one step */
265                                         break;
266                                 case EXIF_ORIENTATION_RIGHT_TOP:
267                                         /* rotated -90 (270) */
268                                         imd->delay_alter_type = ALTER_ROTATE_90;
269                                         break;
270                                 case EXIF_ORIENTATION_RIGHT_BOTTOM:
271                                         /* not implemented -- too wacky to fix in one step */
272                                         break;
273                                 case EXIF_ORIENTATION_LEFT_BOTTOM:
274                                         /* rotated 90 */
275                                         imd->delay_alter_type = ALTER_ROTATE_90_CC;
276                                         break;
277                                 default:
278                                         /* The other values are out of range */
279                                         break;
280                                 }
281                         }
282                 exif_free(ed);
283                 }
284
285         if (imd->delay_alter_type != ALTER_NONE)
286                 {
287                 image_alter_real(imd, imd->delay_alter_type, clamp);
288                 }
289 }
290
291 /*
292  *-------------------------------------------------------------------
293  * read ahead (prebuffer)
294  *-------------------------------------------------------------------
295  */
296
297 static void image_read_ahead_cancel(ImageWindow *imd)
298 {
299         if (debug) printf("read ahead cancelled for :%s\n", imd->read_ahead_path);
300
301         image_loader_free(imd->read_ahead_il);
302         imd->read_ahead_il = NULL;
303
304         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
305         imd->read_ahead_pixbuf = NULL;
306
307         g_free(imd->read_ahead_path);
308         imd->read_ahead_path = NULL;
309 }
310
311 static void image_read_ahead_done_cb(ImageLoader *il, gpointer data)
312 {
313         ImageWindow *imd = data;
314
315         if (debug) printf("read ahead done for :%s\n", imd->read_ahead_path);
316
317         imd->read_ahead_pixbuf = image_loader_get_pixbuf(imd->read_ahead_il);
318         if (imd->read_ahead_pixbuf)
319                 {
320                 g_object_ref(imd->read_ahead_pixbuf);
321                 }
322         else
323                 {
324                 imd->read_ahead_pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
325                 }
326         image_loader_free(imd->read_ahead_il);
327         imd->read_ahead_il = NULL;
328
329         image_complete_util(imd, TRUE);
330 }
331
332 static void image_read_ahead_error_cb(ImageLoader *il, gpointer data)
333 {
334         /* we even treat errors as success, maybe at least some of the file was ok */
335         image_read_ahead_done_cb(il, data);
336 }
337
338 static void image_read_ahead_start(ImageWindow *imd)
339 {
340         /* already started ? */
341         if (!imd->read_ahead_path || imd->read_ahead_il || imd->read_ahead_pixbuf) return;
342
343         /* still loading ?, do later */
344         if (imd->il) return;
345
346         if (debug) printf("read ahead started for :%s\n", imd->read_ahead_path);
347
348         imd->read_ahead_il = image_loader_new(imd->read_ahead_path);
349
350         image_loader_set_error_func(imd->read_ahead_il, image_read_ahead_error_cb, imd);
351         if (!image_loader_start(imd->read_ahead_il, image_read_ahead_done_cb, imd))
352                 {
353                 image_read_ahead_cancel(imd);
354                 image_complete_util(imd, TRUE);
355                 }
356 }
357
358 static void image_read_ahead_set(ImageWindow *imd, const gchar *path)
359 {
360         if (imd->read_ahead_path && path && strcmp(imd->read_ahead_path, path) == 0) return;
361
362         image_read_ahead_cancel(imd);
363
364         imd->read_ahead_path = g_strdup(path);
365
366         if (debug) printf("read ahead set to :%s\n", imd->read_ahead_path);
367
368         image_read_ahead_start(imd);
369 }
370
371 /*
372  *-------------------------------------------------------------------
373  * post buffering
374  *-------------------------------------------------------------------
375  */
376
377 static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
378 {
379         g_free(imd->prev_path);
380         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
381
382         if (path && pixbuf)
383                 {
384                 imd->prev_path = g_strdup(path);
385                         
386                 g_object_ref(pixbuf);
387                 imd->prev_pixbuf = pixbuf;
388                 }
389         else
390                 {
391                 imd->prev_path = NULL;
392                 imd->prev_pixbuf = NULL;
393                 }
394
395         if (debug) printf("post buffer set: %s\n", path);
396 }
397
398 static gint image_post_buffer_get(ImageWindow *imd)
399 {
400         gint success;
401
402         if (imd->prev_pixbuf &&
403             imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
404                 {
405                 image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
406                 success = TRUE;
407                 }
408         else
409                 {
410                 success = FALSE;
411                 }
412
413         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
414         imd->prev_pixbuf = NULL;
415
416         g_free(imd->prev_path);
417         imd->prev_path = NULL;
418
419         return success;
420 }
421
422 /*
423  *-------------------------------------------------------------------
424  * loading
425  *-------------------------------------------------------------------
426  */
427
428 static void image_load_pixbuf_ready(ImageWindow *imd)
429 {
430         if (image_get_pixbuf(imd) || !imd->il) return;
431
432         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
433 }
434
435 static void image_load_area_cb(ImageLoader *il, guint x, guint y, guint w, guint h, gpointer data)
436 {
437         ImageWindow *imd = data;
438         PixbufRenderer *pr;
439
440         pr = (PixbufRenderer *)imd->pr;
441
442         if (imd->delay_flip &&
443             pr->pixbuf != image_loader_get_pixbuf(il))
444                 {
445                 return;
446                 }
447
448         if (!pr->pixbuf) image_load_pixbuf_ready(imd);
449
450         pixbuf_renderer_area_changed(pr, x, y, w, h);
451 }
452
453 static void image_load_done_cb(ImageLoader *il, gpointer data)
454 {
455         ImageWindow *imd = data;
456
457         if (debug) printf ("image done\n");
458
459         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
460
461         if (imd->delay_flip &&
462             image_get_pixbuf(imd) != image_loader_get_pixbuf(imd->il))
463                 {
464                 g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
465                 image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
466                 }
467
468         image_loader_free(imd->il);
469         imd->il = NULL;
470
471         image_post_process(imd, TRUE);
472
473         image_read_ahead_start(imd);
474 }
475
476 static void image_load_error_cb(ImageLoader *il, gpointer data)
477 {
478         if (debug) printf ("image error\n");
479
480         /* even on error handle it like it was done,
481          * since we have a pixbuf with _something_ */
482
483         image_load_done_cb(il, data);
484 }
485
486 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
487 static void image_load_buffer_throttle(ImageLoader *il)
488 {
489         if (!il || il->bytes_total < IMAGE_THROTTLE_THRESHOLD) return;
490
491         /* Larger image files usually have larger chunks of data per pixel...
492          * So increase the buffer read size so that the rendering chunks called
493          * are also larger.
494          */
495
496         image_loader_set_buffer_size(il, IMAGE_LOAD_BUFFER_COUNT * IMAGE_THROTTLE_FACTOR);
497 }
498 #endif
499
500 /* this read ahead is located here merely for the callbacks, above */
501
502 static gint image_read_ahead_check(ImageWindow *imd)
503 {
504         if (!imd->read_ahead_path) return FALSE;
505         if (imd->il) return FALSE;
506
507         if (!imd->image_path || strcmp(imd->read_ahead_path, imd->image_path) != 0)
508                 {
509                 image_read_ahead_cancel(imd);
510                 return FALSE;
511                 }
512
513         if (imd->read_ahead_il)
514                 {
515                 imd->il = imd->read_ahead_il;
516                 imd->read_ahead_il = NULL;
517
518                 /* override the old signals */
519                 image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
520                 image_loader_set_error_func(imd->il, image_load_error_cb, imd);
521                 image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
522
523 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
524                 image_load_buffer_throttle(imd->il);
525 #endif
526
527                 /* do this one directly (probably should add a set func) */
528                 imd->il->func_done = image_load_done_cb;
529
530                 g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
531
532                 if (!imd->delay_flip)
533                         {
534                         image_change_pixbuf(imd, image_loader_get_pixbuf(imd->il), image_zoom_get(imd));
535                         }
536
537                 image_read_ahead_cancel(imd);
538                 return TRUE;
539                 }
540         else if (imd->read_ahead_pixbuf)
541                 {
542                 image_change_pixbuf(imd, imd->read_ahead_pixbuf, image_zoom_get(imd));
543                 g_object_unref(imd->read_ahead_pixbuf);
544                 imd->read_ahead_pixbuf = NULL;
545
546                 image_read_ahead_cancel(imd);
547
548                 image_post_process(imd, FALSE);
549                 return TRUE;
550                 }
551
552         image_read_ahead_cancel(imd);
553         return FALSE;
554 }
555
556 static gint image_load_begin(ImageWindow *imd, const gchar *path)
557 {
558         if (debug) printf ("image begin \n");
559
560         if (imd->il) return FALSE;
561
562         imd->completed = FALSE;
563         g_object_set(G_OBJECT(imd->pr), "complete", FALSE, NULL);
564
565         if (image_post_buffer_get(imd))
566                 {
567                 if (debug) printf("from post buffer: %s\n", imd->image_path);
568                 return TRUE;
569                 }
570
571         if (image_read_ahead_check(imd))
572                 {
573                 if (debug) printf("from read ahead buffer: %s\n", imd->image_path);
574                 return TRUE;
575                 }
576
577         if (!imd->delay_flip && image_get_pixbuf(imd))
578                 {
579                 PixbufRenderer *pr;
580
581                 pr = PIXBUF_RENDERER(imd->pr);
582                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
583                 pr->pixbuf = NULL;
584                 }
585
586         g_object_set(G_OBJECT(imd->pr), "loading", TRUE, NULL);
587
588         imd->il = image_loader_new(path);
589
590         image_loader_set_area_ready_func(imd->il, image_load_area_cb, imd);
591         image_loader_set_error_func(imd->il, image_load_error_cb, imd);
592         image_loader_set_buffer_size(imd->il, IMAGE_LOAD_BUFFER_COUNT);
593
594         if (!image_loader_start(imd->il, image_load_done_cb, imd))
595                 {
596                 if (debug) printf("image start error\n");
597
598                 g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
599
600                 image_loader_free(imd->il);
601                 imd->il = NULL;
602
603                 image_complete_util(imd, FALSE);
604
605                 return FALSE;
606                 }
607
608 #ifdef IMAGE_THROTTLE_LARGER_IMAGES
609         image_load_buffer_throttle(imd->il);
610 #endif
611
612         if (!imd->delay_flip && !image_get_pixbuf(imd)) image_load_pixbuf_ready(imd);
613
614         return TRUE;
615 }
616
617 static void image_reset(ImageWindow *imd)
618 {
619         /* stops anything currently being done */
620
621         if (debug) printf("image reset\n");
622
623         g_object_set(G_OBJECT(imd->pr), "loading", FALSE, NULL);
624
625         image_loader_free(imd->il);
626         imd->il = NULL;
627
628         imd->delay_alter_type = ALTER_NONE;
629 }
630
631 /*
632  *-------------------------------------------------------------------
633  * image changer
634  *-------------------------------------------------------------------
635  */
636
637 static void image_change_complete(ImageWindow *imd, gdouble zoom, gint new)
638 {
639         image_reset(imd);
640
641         if (imd->image_path && isfile(imd->image_path))
642                 {
643                 PixbufRenderer *pr;
644
645                 pr = PIXBUF_RENDERER(imd->pr);
646                 pr->zoom = zoom;        /* store the zoom, needed by the loader */
647
648                 if (image_load_begin(imd, imd->image_path))
649                         {
650                         imd->unknown = FALSE;
651                         }
652                 else
653                         {
654                         GdkPixbuf *pixbuf;
655
656                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
657                         image_change_pixbuf(imd, pixbuf, zoom);
658                         g_object_unref(pixbuf);
659
660                         imd->unknown = TRUE;
661                         }
662                 imd->size = filesize(imd->image_path);
663                 imd->mtime = filetime(imd->image_path);
664                 }
665         else
666                 {
667                 if (imd->image_path)
668                         {
669                         GdkPixbuf *pixbuf;
670
671                         pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
672                         image_change_pixbuf(imd, pixbuf, zoom);
673                         g_object_unref(pixbuf);
674                         imd->mtime = filetime(imd->image_path);
675                         }
676                 else
677                         {
678                         image_change_pixbuf(imd, NULL, zoom);
679                         imd->mtime = 0;
680                         }
681                 imd->unknown = TRUE;
682                 imd->size = 0;
683                 }
684
685         image_update_util(imd);
686 }
687
688 static void image_change_real(ImageWindow *imd, const gchar *path,
689                               CollectionData *cd, CollectInfo *info, gdouble zoom)
690 {
691         GdkPixbuf *pixbuf;
692         GdkPixbuf *prev_pixbuf = NULL;
693         gchar *prev_path = NULL;
694         gint prev_clear = FALSE;
695
696         imd->collection = cd;
697         imd->collection_info = info;
698
699         pixbuf = image_get_pixbuf(imd);
700
701         if (enable_read_ahead && imd->image_path && pixbuf)
702                 {
703                 if (imd->il)
704                         {
705                         /* current image is not finished */
706                         prev_clear = TRUE;
707                         }
708                 else
709                         {
710                         prev_path = g_strdup(imd->image_path);
711                         prev_pixbuf = pixbuf;
712                         g_object_ref(prev_pixbuf);
713                         }
714                 }
715
716         g_free(imd->image_path);
717         imd->image_path = g_strdup(path);
718         imd->image_name = filename_from_path(imd->image_path);
719
720         image_change_complete(imd, zoom, TRUE);
721
722         if (prev_pixbuf)
723                 {
724                 image_post_buffer_set(imd, prev_path, prev_pixbuf);
725                 g_free(prev_path);
726                 g_object_unref(prev_pixbuf);
727                 }
728         else if (prev_clear)
729                 {
730                 image_post_buffer_set(imd, NULL, NULL);
731                 }
732
733         image_update_title(imd);
734         image_new_util(imd);
735 }
736
737 /*
738  *-------------------------------------------------------------------
739  * focus stuff
740  *-------------------------------------------------------------------
741  */
742
743 static void image_focus_paint(ImageWindow *imd, gint has_focus, GdkRectangle *area)
744 {
745         GtkWidget *widget;
746
747         widget = imd->widget;
748         if (!widget->window) return;
749
750         if (has_focus)
751                 {
752                 gtk_paint_focus (widget->style, widget->window, GTK_STATE_ACTIVE,
753                                  area, widget, "image_window",
754                                  widget->allocation.x, widget->allocation.y,
755                                  widget->allocation.width - 1, widget->allocation.height - 1);  
756                 }
757         else
758                 {
759                 gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
760                                   area, widget, "image_window",
761                                   widget->allocation.x, widget->allocation.y,
762                                   widget->allocation.width - 1, widget->allocation.height - 1);
763                 }
764 }
765
766 static gint image_focus_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
767 {
768         ImageWindow *imd = data;
769
770         image_focus_paint(imd, GTK_WIDGET_HAS_FOCUS(widget), &event->area);
771         return TRUE;
772 }
773
774 static gint image_focus_in_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
775 {
776         ImageWindow *imd = data;
777
778         GTK_WIDGET_SET_FLAGS(imd->widget, GTK_HAS_FOCUS);
779         image_focus_paint(imd, TRUE, NULL);
780
781         return TRUE;
782 }
783
784 static gint image_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, gpointer data)
785 {
786         ImageWindow *imd = data;
787
788         GTK_WIDGET_UNSET_FLAGS(imd->widget, GTK_HAS_FOCUS);
789         image_focus_paint(imd, FALSE, NULL);
790
791         return TRUE;
792 }
793
794 gint image_overlay_add(ImageWindow *imd, GdkPixbuf *pixbuf, gint x, gint y,
795                        gint relative, gint always)
796 {
797         return pixbuf_renderer_overlay_add((PixbufRenderer *)imd->pr, pixbuf, x, y, relative, always);
798 }
799
800 void image_overlay_set(ImageWindow *imd, gint id, GdkPixbuf *pixbuf, gint x, gint y)
801 {
802         pixbuf_renderer_overlay_set((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
803 }
804
805 gint image_overlay_get(ImageWindow *imd, gint id, GdkPixbuf **pixbuf, gint *x, gint *y)
806 {
807         return pixbuf_renderer_overlay_get((PixbufRenderer *)imd->pr, id, pixbuf, x, y);
808 }
809
810 void image_overlay_remove(ImageWindow *imd, gint id)
811 {
812         pixbuf_renderer_overlay_remove((PixbufRenderer *)imd->pr, id);
813 }
814
815 static gint image_scroll_cb(GtkWidget *widget, GdkEventScroll *event, gpointer data)
816 {
817         ImageWindow *imd = data;
818
819         if (imd->func_scroll &&
820             event && event->type == GDK_SCROLL)
821                 {
822                 imd->func_scroll(imd, event->direction, event->time,
823                                  event->x, event->y, event->state, imd->data_scroll);
824                 return TRUE;
825                 }
826
827         return FALSE;
828 }
829
830 /*
831  *-------------------------------------------------------------------
832  * public interface
833  *-------------------------------------------------------------------
834  */
835
836 void image_attach_window(ImageWindow *imd, GtkWidget *window,
837                          const gchar *title, const gchar *title_right, gint show_zoom)
838 {
839         imd->top_window = window;
840         g_free(imd->title);
841         imd->title = g_strdup(title);
842         g_free(imd->title_right);
843         imd->title_right = g_strdup(title_right);
844         imd->title_show_zoom = show_zoom;
845
846         if (!fit_window) window = NULL;
847
848         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)window);
849
850         image_update_title(imd);
851 }
852
853 void image_set_update_func(ImageWindow *imd,
854                            void (*func)(ImageWindow *imd, gpointer data),
855                            gpointer data)
856 {
857         imd->func_update = func;
858         imd->data_update = data;
859 }
860
861 void image_set_complete_func(ImageWindow *imd,
862                              void (*func)(ImageWindow *, gint preload, gpointer),
863                              gpointer data)
864 {
865         imd->func_complete = func;
866         imd->data_complete = data;
867 }
868
869 void image_set_new_func(ImageWindow *imd,
870                         void (*func)(ImageWindow *, gpointer),
871                         gpointer data)
872 {
873         imd->func_new = func;
874         imd->data_new = data;
875 }
876
877
878 void image_set_button_func(ImageWindow *imd,
879                            void (*func)(ImageWindow *, gint button, guint32 time, gdouble x, gdouble y, guint state, gpointer),
880                            gpointer data)
881 {
882         imd->func_button = func;
883         imd->data_button = data;
884 }
885
886 void image_set_scroll_func(ImageWindow *imd,
887                            void (*func)(ImageWindow *, GdkScrollDirection direction, guint32 time, gdouble x, gdouble y, guint state, gpointer),
888                            gpointer data)
889 {
890         imd->func_scroll = func;
891         imd->data_scroll = data;
892 }
893
894 void image_set_scroll_notify_func(ImageWindow *imd,
895                                   void (*func)(ImageWindow *imd, gint x, gint y, gint width, gint height, gpointer data),
896                                   gpointer data)
897 {
898         imd->func_scroll_notify = func;
899         imd->data_scroll_notify = data;
900 }
901
902 /* path, name */
903
904 const gchar *image_get_path(ImageWindow *imd)
905 {
906         return imd->image_path;
907 }
908
909 const gchar *image_get_name(ImageWindow *imd)
910 {
911         return imd->image_name;
912 }
913
914 /* merely changes path string, does not change the image! */
915 void image_set_path(ImageWindow *imd, const gchar *newpath)
916 {
917         g_free(imd->image_path);
918         imd->image_path = g_strdup(newpath);
919         imd->image_name = filename_from_path(imd->image_path);
920
921         image_update_title(imd);
922         image_new_util(imd);
923 }
924
925 /* load a new image */
926
927 void image_change_path(ImageWindow *imd, const gchar *path, gdouble zoom)
928 {
929         if (imd->image_path == path ||
930             (path && imd->image_path && !strcmp(path, imd->image_path)) ) return;
931
932         image_change_real(imd, path, NULL, NULL, zoom);
933 }
934
935 GdkPixbuf *image_get_pixbuf(ImageWindow *imd)
936 {
937         return pixbuf_renderer_get_pixbuf((PixbufRenderer *)imd->pr);
938 }
939
940 void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom)
941 {
942         pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom);
943         image_new_util(imd);
944 }
945
946 void image_change_from_collection(ImageWindow *imd, CollectionData *cd, CollectInfo *info, gdouble zoom)
947 {
948         if (!cd || !info || !g_list_find(cd->list, info)) return;
949
950         image_change_real(imd, info->path, cd, info, zoom);
951 }
952
953 CollectionData *image_get_collection(ImageWindow *imd, CollectInfo **info)
954 {
955         if (collection_to_number(imd->collection) >= 0)
956                 {
957                 if (g_list_find(imd->collection->list, imd->collection_info) != NULL)
958                         {
959                         if (info) *info = imd->collection_info;
960                         }
961                 else
962                         {
963                         if (info) *info = NULL;
964                         }
965                 return imd->collection;
966                 }
967
968         if (info) *info = NULL;
969         return NULL;
970 }
971
972 static void image_loader_sync_data(ImageLoader *il, gpointer data)
973 {
974         /* change data for the callbacks directly */
975
976         il->data_area_ready = data;
977         il->data_error = data;
978         il->data_done = data;
979         il->data_percent = data;
980 }
981
982 /* this is more like a move function
983  * it moves most data from source to imd
984  */
985 void image_change_from_image(ImageWindow *imd, ImageWindow *source)
986 {
987         if (imd == source) return;
988
989         imd->unknown = source->unknown;
990
991         imd->collection = source->collection;
992         imd->collection_info = source->collection_info;
993         imd->size = source->size;
994         imd->mtime = source->mtime;
995
996         image_set_path(imd, image_get_path(source));
997
998         image_loader_free(imd->il);
999         imd->il = NULL;
1000
1001         if (source->il)
1002                 {
1003                 imd->il = source->il;
1004                 source->il = NULL;
1005
1006                 image_loader_sync_data(imd->il, imd);
1007
1008                 imd->delay_alter_type = source->delay_alter_type;
1009                 source->delay_alter_type = ALTER_NONE;
1010                 }
1011
1012         image_loader_free(imd->read_ahead_il);
1013         imd->read_ahead_il = source->read_ahead_il;
1014         source->read_ahead_il = NULL;
1015         if (imd->read_ahead_il) image_loader_sync_data(imd->read_ahead_il, imd);
1016
1017         if (imd->read_ahead_pixbuf) g_object_unref(imd->read_ahead_pixbuf);
1018         imd->read_ahead_pixbuf = source->read_ahead_pixbuf;
1019         source->read_ahead_pixbuf = NULL;
1020
1021         g_free(imd->read_ahead_path);
1022         imd->read_ahead_path = source->read_ahead_path;
1023         source->read_ahead_path = NULL;
1024
1025         if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
1026         imd->prev_pixbuf = source->prev_pixbuf;
1027         source->prev_pixbuf = NULL;
1028
1029         g_free(imd->prev_path);
1030         imd->prev_path = source->prev_path;
1031         source->prev_path = NULL;
1032
1033         imd->completed = source->completed;
1034
1035         pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
1036 }
1037
1038 /* manipulation */
1039
1040 void image_area_changed(ImageWindow *imd, gint x, gint y, gint width, gint height)
1041 {
1042         pixbuf_renderer_area_changed((PixbufRenderer *)imd->pr, x, y, width, height);
1043 }
1044
1045 void image_reload(ImageWindow *imd)
1046 {
1047         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1048
1049         image_change_complete(imd, image_zoom_get(imd), FALSE);
1050 }
1051
1052 void image_scroll(ImageWindow *imd, gint x, gint y)
1053 {
1054         pixbuf_renderer_scroll((PixbufRenderer *)imd->pr, x, y);
1055 }
1056
1057 void image_scroll_to_point(ImageWindow *imd, gint x, gint y,
1058                            gdouble x_align, gdouble y_align)
1059 {
1060         pixbuf_renderer_scroll_to_point((PixbufRenderer *)imd->pr, x, y, x_align, y_align);
1061 }
1062
1063 void image_alter(ImageWindow *imd, AlterType type)
1064 {
1065         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1066
1067         if (imd->il)
1068                 {
1069                 /* still loading, wait till done */
1070                 imd->delay_alter_type = type;
1071                 return;
1072                 }
1073
1074         image_alter_real(imd, type, TRUE);
1075 }
1076
1077 void image_zoom_adjust(ImageWindow *imd, gdouble increment)
1078 {
1079         pixbuf_renderer_zoom_adjust((PixbufRenderer *)imd->pr, increment);
1080 }
1081
1082 void image_zoom_adjust_at_point(ImageWindow *imd, gdouble increment, gint x, gint y)
1083 {
1084         pixbuf_renderer_zoom_adjust_at_point((PixbufRenderer *)imd->pr, increment, x, y);
1085 }
1086
1087 void image_zoom_set_limits(ImageWindow *imd, gdouble min, gdouble max)
1088 {
1089         pixbuf_renderer_zoom_set_limits((PixbufRenderer *)imd->pr, min, max);
1090 }
1091
1092 void image_zoom_set(ImageWindow *imd, gdouble zoom)
1093 {
1094         pixbuf_renderer_zoom_set((PixbufRenderer *)imd->pr, zoom);
1095 }
1096
1097 void image_zoom_set_fill_geometry(ImageWindow *imd, gint vertical)
1098 {
1099         PixbufRenderer *pr;
1100         gdouble zoom;
1101         gint width, height;
1102
1103         pr = (PixbufRenderer *)imd->pr;
1104
1105         if (!pixbuf_renderer_get_pixbuf(pr) ||
1106             !pixbuf_renderer_get_image_size(pr, &width, &height)) return;
1107
1108         if (vertical)
1109                 {
1110                 zoom = (gdouble)pr->window_height / height;
1111                 }
1112         else
1113                 {
1114                 zoom = (gdouble)pr->window_width / width;
1115                 }
1116
1117         if (zoom < 1.0)
1118                 {
1119                 zoom = 0.0 - 1.0 / zoom;
1120                 }
1121
1122         pixbuf_renderer_zoom_set(pr, zoom);
1123 }
1124
1125 gdouble image_zoom_get(ImageWindow *imd)
1126 {
1127         return pixbuf_renderer_zoom_get((PixbufRenderer *)imd->pr);
1128 }
1129
1130 gdouble image_zoom_get_real(ImageWindow *imd)
1131 {
1132         return pixbuf_renderer_zoom_get_scale((PixbufRenderer *)imd->pr);
1133 }
1134
1135 gchar *image_zoom_get_as_text(ImageWindow *imd)
1136 {
1137         gdouble zoom;
1138         gdouble scale;
1139         gdouble l = 1.0;
1140         gdouble r = 1.0;
1141         gint pl = 0;
1142         gint pr = 0;
1143         gchar *approx = " ";
1144
1145         zoom = image_zoom_get(imd);
1146         scale = image_zoom_get_real(imd);
1147
1148         if (zoom > 0.0)
1149                 {
1150                 l = zoom;
1151                 }
1152         else if (zoom < 0.0)
1153                 {
1154                 r = 0.0 - zoom;
1155                 }
1156         else if (zoom == 0.0 && scale != 0.0)
1157                 {
1158                 if (scale >= 1.0)
1159                         {
1160                         l = scale;
1161                         }
1162                 else
1163                         {
1164                         r = 1.0 / scale;
1165                         }
1166                 approx = " ~";
1167                 }
1168
1169         if (rint(l) != l) pl = 1;
1170         if (rint(r) != r) pr = 1;
1171
1172         return g_strdup_printf("%.*f :%s%.*f", pl, l, approx, pr, r);
1173 }
1174
1175 gdouble image_zoom_get_default(ImageWindow *imd, gint mode)
1176 {
1177         gdouble zoom;
1178
1179         if (mode == ZOOM_RESET_ORIGINAL)
1180                 {
1181                 zoom = 1.0;
1182                 }
1183         else if (mode == ZOOM_RESET_FIT_WINDOW)
1184                 {
1185                 zoom = 0.0;
1186                 }
1187         else
1188                 {
1189                 if (imd)
1190                         {
1191                         zoom = image_zoom_get(imd);
1192                         }
1193                 else
1194                         {
1195                         zoom = 1.0;
1196                         }
1197                 }
1198
1199         return zoom;
1200 }
1201
1202 /* read ahead */
1203
1204 void image_prebuffer_set(ImageWindow *imd, const gchar *path)
1205 {
1206         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1207
1208         if (path)
1209                 {
1210                 image_read_ahead_set(imd, path);
1211                 }
1212         else
1213                 {
1214                 image_read_ahead_cancel(imd);
1215                 }
1216 }
1217
1218 static gint image_auto_refresh_cb(gpointer data)
1219 {
1220         ImageWindow *imd = data;
1221         time_t newtime;
1222         
1223         if (!imd || !image_get_pixbuf(imd) ||
1224             imd->il || !imd->image_path ||
1225             !update_on_time_change) return TRUE;
1226
1227         newtime = filetime(imd->image_path);
1228         if (newtime > 0 && newtime != imd->mtime)
1229                 {
1230                 imd->mtime = newtime;
1231                 image_reload(imd);
1232                 }
1233
1234         return TRUE;
1235 }
1236
1237 /* image auto refresh on time stamp change, in 1/1000's second, -1 disables */
1238
1239 void image_auto_refresh(ImageWindow *imd, gint interval)
1240 {
1241         if (!imd) return;
1242         if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
1243
1244         if (imd->auto_refresh_id > -1)
1245                 {
1246                 g_source_remove(imd->auto_refresh_id);
1247                 imd->auto_refresh_id = -1;
1248                 imd->auto_refresh_interval = -1;
1249                 }
1250
1251         if (interval < 0) return;
1252
1253         if (interval == 0) interval = IMAGE_AUTO_REFRESH_TIME;
1254
1255         imd->auto_refresh_id = g_timeout_add((guint32)interval, image_auto_refresh_cb, imd);
1256         imd->auto_refresh_interval = interval;
1257 }
1258
1259 void image_top_window_set_sync(ImageWindow *imd, gint allow_sync)
1260 {
1261         imd->top_window_sync = allow_sync;
1262
1263         g_object_set(G_OBJECT(imd->pr), "window_fit", allow_sync, NULL);
1264 }
1265
1266 void image_background_set_black(ImageWindow *imd, gint black)
1267 {
1268         pixbuf_renderer_set_black((PixbufRenderer *)imd->pr, black);
1269 }
1270
1271 void image_background_set_color(ImageWindow *imd, GdkColor *color)
1272 {
1273         pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
1274 }
1275
1276 void image_set_delay_flip(ImageWindow *imd, gint delay)
1277 {
1278         if (!imd ||
1279             imd->delay_flip == delay) return;
1280
1281         imd->delay_flip = delay;
1282
1283         g_object_set(G_OBJECT(imd->pr), "delay_flip", delay, NULL);
1284
1285         if (!imd->delay_flip && imd->il)
1286                 {
1287                 PixbufRenderer *pr;
1288
1289                 pr = PIXBUF_RENDERER(imd->pr);
1290                 if (pr->pixbuf) g_object_unref(pr->pixbuf);
1291                 pr->pixbuf = NULL;
1292
1293                 image_load_pixbuf_ready(imd);
1294                 }
1295 }
1296
1297 void image_to_root_window(ImageWindow *imd, gint scaled)
1298 {
1299         GdkScreen *screen;
1300         GdkWindow *rootwindow;
1301         GdkPixmap *pixmap;
1302         GdkPixbuf *pixbuf;
1303         GdkPixbuf *pb;
1304         gint width, height;
1305
1306         if (!imd) return;
1307
1308         pixbuf = image_get_pixbuf(imd);
1309         if (!pixbuf) return;
1310
1311         screen = gtk_widget_get_screen(imd->widget);
1312         rootwindow = gdk_screen_get_root_window(screen);
1313         if (gdk_drawable_get_visual(rootwindow) != gdk_visual_get_system()) return;
1314
1315         if (scaled)
1316                 {
1317                 width = gdk_screen_width();
1318                 height = gdk_screen_height();
1319                 }
1320         else
1321                 {
1322                 pixbuf_renderer_get_scaled_size((PixbufRenderer *)imd->pr, &width, &height);
1323                 }
1324
1325         pb = gdk_pixbuf_scale_simple(pixbuf, width, height, (GdkInterpType)zoom_quality);
1326
1327         gdk_pixbuf_render_pixmap_and_mask (pb, &pixmap, NULL, 128);
1328         gdk_window_set_back_pixmap(rootwindow, pixmap, FALSE);
1329         gdk_window_clear(rootwindow);
1330         g_object_unref(pb);
1331         g_object_unref(pixmap);
1332
1333         gdk_flush();
1334 }
1335
1336 /*
1337  *-------------------------------------------------------------------
1338  * prefs sync
1339  *-------------------------------------------------------------------
1340  */
1341
1342 static void image_options_set(ImageWindow *imd)
1343 {
1344         g_object_set(G_OBJECT(imd->pr), "zoom_quality", zoom_quality,
1345                                         "zoom_2pass", two_pass_zoom,
1346                                         "zoom_expand", zoom_to_fit_expands,
1347                                         "dither_quality", dither_quality,
1348                                         "scroll_reset", scroll_reset_method,
1349                                         "cache_display", tile_cache_max,
1350                                         "window_fit", (imd->top_window_sync && fit_window),
1351                                         "window_limit", limit_window_size,
1352                                         "window_limit_value", max_window_size,
1353                                         NULL);
1354
1355         pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window);
1356 }
1357
1358 void image_options_sync(void)
1359 {
1360         GList *work;
1361
1362         work = image_list;
1363         while (work)
1364                 {
1365                 ImageWindow *imd;
1366
1367                 imd = work->data;
1368                 work = work->next;
1369
1370                 image_options_set(imd);
1371                 }
1372 }
1373
1374 /*
1375  *-------------------------------------------------------------------
1376  * init / destroy
1377  *-------------------------------------------------------------------
1378  */
1379
1380 static void image_free(ImageWindow *imd)
1381 {
1382         image_list = g_list_remove(image_list, imd);
1383
1384         image_reset(imd);
1385
1386         image_read_ahead_cancel(imd);
1387         image_post_buffer_set(imd, NULL, NULL);
1388         image_auto_refresh(imd, -1);
1389
1390         g_free(imd->image_path);
1391         g_free(imd->title);
1392         g_free(imd->title_right);
1393
1394         g_free(imd);
1395 }
1396
1397 static void image_destroy_cb(GtkObject *widget, gpointer data)
1398 {
1399         ImageWindow *imd = data;
1400         image_free(imd);
1401 }
1402
1403 ImageWindow *image_new(gint frame)
1404 {
1405         ImageWindow *imd;
1406
1407         imd = g_new0(ImageWindow, 1);
1408
1409         imd->top_window = NULL;
1410         imd->title = NULL;
1411         imd->title_right = NULL;
1412         imd->title_show_zoom = FALSE;
1413
1414         imd->unknown = TRUE;
1415
1416         imd->has_frame = frame;
1417         imd->top_window_sync = FALSE;
1418
1419         imd->delay_alter_type = ALTER_NONE;
1420
1421         imd->read_ahead_il = NULL;
1422         imd->read_ahead_pixbuf = NULL;
1423         imd->read_ahead_path = NULL;
1424
1425         imd->completed = FALSE;
1426
1427         imd->auto_refresh_id = -1;
1428         imd->auto_refresh_interval = -1;
1429
1430         imd->delay_flip = FALSE;
1431
1432         imd->func_update = NULL;
1433         imd->func_complete = NULL;
1434         imd->func_tile_request = NULL;
1435         imd->func_tile_dispose = NULL;
1436
1437         imd->func_button = NULL;
1438         imd->func_scroll = NULL;
1439
1440         imd->pr = GTK_WIDGET(pixbuf_renderer_new());
1441
1442         image_options_set(imd);
1443
1444         if (imd->has_frame)
1445                 {
1446                 imd->widget = gtk_frame_new(NULL);
1447                 gtk_frame_set_shadow_type(GTK_FRAME(imd->widget), GTK_SHADOW_IN);
1448                 gtk_container_add(GTK_CONTAINER(imd->widget), imd->pr);
1449                 gtk_widget_show(imd->pr);
1450
1451                 GTK_WIDGET_SET_FLAGS(imd->widget, GTK_CAN_FOCUS);
1452                 g_signal_connect(G_OBJECT(imd->widget), "focus_in_event",
1453                                  G_CALLBACK(image_focus_in_cb), imd);
1454                 g_signal_connect(G_OBJECT(imd->widget), "focus_out_event",
1455                                  G_CALLBACK(image_focus_out_cb), imd);
1456
1457                 g_signal_connect_after(G_OBJECT(imd->widget), "expose_event",
1458                                        G_CALLBACK(image_focus_expose), imd);
1459                 }
1460         else
1461                 {
1462                 imd->widget = imd->pr;
1463                 }
1464
1465         g_signal_connect(G_OBJECT(imd->pr), "clicked",
1466                          G_CALLBACK(image_click_cb), imd);
1467         g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
1468                          G_CALLBACK(image_scroll_notify_cb), imd);
1469
1470         g_signal_connect(G_OBJECT(imd->pr), "scroll_event",
1471                          G_CALLBACK(image_scroll_cb), imd);
1472
1473         g_signal_connect(G_OBJECT(imd->pr), "destroy",
1474                          G_CALLBACK(image_destroy_cb), imd);
1475
1476         g_signal_connect(G_OBJECT(imd->pr), "zoom",
1477                          G_CALLBACK(image_zoom_cb), imd);
1478         g_signal_connect(G_OBJECT(imd->pr), "render_complete",
1479                          G_CALLBACK(image_render_complete_cb), imd);
1480
1481         image_list = g_list_append(image_list, imd);
1482
1483         return imd;
1484 }
1485