1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id: image_view_draw.c,v 1.5 2003/07/20 14:38:10 makeinu Exp $
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif /* HAVE_CONFIG_H */
27 
28 #include "cursors.h"
29 #include "gimv_anim.h"
30 #include "gimv_image_view.h"
31 #include "gimv_thumb_cache.h"
32 #include "prefs.h"
33 
34 
35 static gboolean cb_image_configure         (GtkWidget         *widget,
36                                             GdkEventConfigure *event,
37                                             GimvImageView     *iv);
38 static gboolean cb_image_expose            (GtkWidget         *widget,
39                                             GdkEventExpose    *event,
40                                             GimvImageView     *iv);
41 
42 /* virtual functions */
43 static GtkWidget   *imageview_draw_create               (GimvImageView *iv);
44 static void         imageview_draw_create_thumbnail     (GimvImageView *iv,
45                                                          const gchar *type);
46 
47 static gboolean     imageview_draw_is_playable          (GimvImageView *iv,
48                                                          GimvImageInfo *info);
49 static void         imageview_animation_play            (GimvImageView *iv);
50 static void         imageview_animation_stop            (GimvImageView *iv);
51 #if 0
52 static void         imageview_animation_pause           (GimvImageView *iv);
53 #endif
54 static GimvImageViewPlayableStatus
55                     imageview_draw_get_status           (GimvImageView *iv);
56 
57 
58 static GimvImageViewPlayableIF imageview_draw_playable_table = {
59    is_playable_fn:      imageview_draw_is_playable,
60    is_seekable_fn:      NULL,
61    play_fn:             imageview_animation_play,
62    stop_fn:             imageview_animation_stop,
63 #if 0
64    pause_fn:            imageview_animation_pause,
65 #else
66    pause_fn:            NULL,
67 #endif
68    forward_fn:          NULL,
69    reverse_fn:          NULL,
70    seek_fn:             NULL,
71    eject_fn:            NULL,
72    get_status_fn:       imageview_draw_get_status,
73    get_length_fn:       NULL,
74    get_position_fn:     NULL,
75 };
76 
77 
78 GimvImageViewPlugin imageview_draw_vfunc_table = {
79    label:               GIMV_IMAGE_VIEW_DEFAULT_VIEW_MODE,
80    priority_hint:       G_PRIORITY_LOW,
81    is_supported_fn:     NULL,
82    create_fn:           imageview_draw_create,
83    create_thumbnail_fn: imageview_draw_create_thumbnail,
84    fullscreen_fn:       NULL,
85 
86    scalable:            NULL,
87    rotatable:           NULL,
88    playable:            &imageview_draw_playable_table,
89 };
90 
91 
92 static GHashTable *animation_id_table        = NULL;
93 static GHashTable *create_thumbnail_id_table = NULL;
94 
95 
96 /*****************************************************************************
97  *
98  *   callback functions
99  *
100  *****************************************************************************/
101 static void
cb_destroy(GtkWidget * widget,GimvImageView * iv)102 cb_destroy (GtkWidget *widget, GimvImageView *iv)
103 {
104    g_return_if_fail (iv);
105 
106    imageview_animation_stop (iv);
107 }
108 
109 
110 static void
cb_load_end_create_thumbnail(GimvImageView * iv,GimvImageInfo * info,gboolean cancel,gpointer data)111 cb_load_end_create_thumbnail (GimvImageView *iv, GimvImageInfo *info,
112                               gboolean cancel, gpointer data)
113 {
114    GimvImage *imcache;
115    gchar *filename;
116    gboolean free_buf = GPOINTER_TO_INT (data);
117    gpointer id_p;
118    guint id;
119 
120    g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv));
121 
122    id_p = g_hash_table_lookup (create_thumbnail_id_table, iv);
123    id = GPOINTER_TO_UINT (id_p);
124    if (id > 0)
125       gtk_signal_disconnect (GTK_OBJECT (iv), id);
126    g_hash_table_remove (create_thumbnail_id_table, iv);
127 
128    if (cancel) return;
129    if (!iv->image) return;
130    if (iv->info != iv->loader->info) return;
131 
132    filename = gimv_image_info_get_path_with_archive (iv->info);
133 
134    /* FIXME: conf.cache_write_type is hard coded */
135    imcache = gimv_thumb_cache_save (filename,
136                                     conf.cache_write_type,
137                                     iv->image,
138                                     iv->info);
139 
140    g_free (filename);
141 
142    if (free_buf)
143       gimv_image_view_free_image_buf (iv);
144 
145    if (imcache) {
146       gimv_image_unref (imcache);
147       gtk_signal_emit_by_name (GTK_OBJECT (iv),
148                                "thumbnail_created",
149                                iv->info);
150    }
151 }
152 
153 
154 static void
cb_draw_area_map(GtkWidget * widget,GimvImageView * iv)155 cb_draw_area_map (GtkWidget *widget, GimvImageView *iv)
156 {
157    if (iv->bg_color) {
158       gimv_image_view_set_bg_color (iv,
159                                     iv->bg_color->red,
160                                     iv->bg_color->green,
161                                     iv->bg_color->blue);
162    }
163 
164    /* set cursor */
165    if (!iv->cursor)
166       iv->cursor = cursor_get (iv->draw_area->window, CURSOR_HAND_OPEN);
167    gdk_window_set_cursor (iv->draw_area->window, iv->cursor);
168 }
169 
170 
171 static gboolean
cb_image_configure(GtkWidget * widget,GdkEventConfigure * event,GimvImageView * iv)172 cb_image_configure (GtkWidget *widget, GdkEventConfigure *event, GimvImageView *iv)
173 {
174    gint width, height;
175    gint fwidth, fheight;
176    gint x_pos, y_pos;
177 
178    gimv_image_view_get_view_position (iv, &x_pos, &y_pos);
179    gimv_image_view_get_image_size (iv, &width, &height);
180    gimv_image_view_get_image_frame_size (iv, &fwidth, &fheight);
181 
182    if (fwidth < width) {
183       if (x_pos < 0 || x_pos < 0 - fwidth || x_pos > width)
184          x_pos = 0;
185    } else {
186       x_pos = (width - fwidth) / 2;
187    }
188 
189    if (fheight < height) {
190       if (y_pos < 0 || y_pos < 0 - fheight || y_pos > height)
191          y_pos = 0;
192    } else {
193 
194       y_pos = (height - fheight) / 2;
195    }
196 
197    gimv_image_view_set_view_position (iv, x_pos, y_pos);
198    gimv_image_view_draw_image (iv);
199 
200    return TRUE;
201 }
202 
203 
204 static gboolean
cb_image_expose(GtkWidget * widget,GdkEventExpose * event,GimvImageView * iv)205 cb_image_expose (GtkWidget *widget, GdkEventExpose *event, GimvImageView *iv)
206 {
207    gimv_image_view_draw_image (iv);
208    return TRUE;
209 }
210 
211 
212 
213 /*****************************************************************************
214  *
215  *   other private functions
216  *
217  *****************************************************************************/
218 static gboolean
timeout_animation(gpointer data)219 timeout_animation (gpointer data)
220 {
221    GimvImageView *iv = data;
222    gint idx, interval;
223 
224    if (!iv->image) goto END;
225    if (!GIMV_IS_ANIM (iv->image)) goto END;
226 
227    idx = gimv_anim_iterate ((GimvAnim *) iv->image);
228 
229    /* repeat */
230    if (idx < 0) {
231       if (!gimv_anim_seek ((GimvAnim *) iv->image, 0))
232          goto END;
233    }
234 
235    gimv_image_view_show_image (iv);
236 
237    interval = gimv_anim_get_interval ((GimvAnim *) iv->image);
238    if (interval > 0) {
239       guint timer = gtk_timeout_add (interval, timeout_animation, iv);
240       g_hash_table_insert (animation_id_table,
241                            iv, GUINT_TO_POINTER (timer));
242    } else {
243       goto END;
244    }
245 
246    return FALSE;
247 
248 END:
249    g_hash_table_remove (animation_id_table, iv);
250    gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop);
251    return FALSE;
252 }
253 
254 
255 static gboolean
idle_animation_play(gpointer data)256 idle_animation_play (gpointer data)
257 {
258    GimvImageView *iv = data;
259    gint interval;
260 
261    if (!GIMV_IS_IMAGE_VIEW (iv)) goto END;
262    if (!iv->info) goto END;
263 
264    if (!gimv_image_info_is_animation (iv->info)) goto END;
265 
266    imageview_animation_stop (iv);
267 
268    interval = gimv_anim_get_interval ((GimvAnim *) iv->image);
269    if (interval > 0) {
270       guint timer = gtk_timeout_add (interval, timeout_animation, iv);
271       g_hash_table_insert (animation_id_table,
272                            iv, GUINT_TO_POINTER (timer));
273       gimv_image_view_playable_set_status (iv, GimvImageViewPlayablePlay);
274    } else {
275       goto END;
276    }
277 
278    return FALSE;
279 
280 END:
281    g_hash_table_remove (animation_id_table, iv);
282    gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop);
283    return FALSE;
284 }
285 
286 
287 
288 /*****************************************************************************
289  *
290  *   Virtual functions
291  *
292  *****************************************************************************/
293 static GtkWidget *
imageview_draw_create(GimvImageView * iv)294 imageview_draw_create (GimvImageView *iv)
295 {
296    GtkWidget *widget;
297 
298    if (!animation_id_table)
299       animation_id_table
300          = g_hash_table_new (g_direct_hash, g_direct_equal);
301 
302    if (!create_thumbnail_id_table)
303       create_thumbnail_id_table
304          = g_hash_table_new (g_direct_hash, g_direct_equal);
305 
306    widget = gtk_drawing_area_new ();
307 
308    gtk_signal_connect       (GTK_OBJECT (widget), "destroy",
309                              GTK_SIGNAL_FUNC (cb_destroy), iv);
310    gtk_signal_connect_after (GTK_OBJECT (widget), "map",
311                              GTK_SIGNAL_FUNC (cb_draw_area_map), iv);
312    gtk_signal_connect       (GTK_OBJECT (widget), "configure_event",
313                              GTK_SIGNAL_FUNC (cb_image_configure), iv);
314    gtk_signal_connect       (GTK_OBJECT (widget), "expose_event",
315                              GTK_SIGNAL_FUNC (cb_image_expose), iv);
316 
317    gtk_widget_add_events (widget,
318                           GDK_FOCUS_CHANGE
319                           | GDK_BUTTON_PRESS_MASK | GDK_2BUTTON_PRESS
320                           | GDK_KEY_PRESS | GDK_KEY_RELEASE
321                           | GDK_BUTTON_RELEASE_MASK
322                           | GDK_POINTER_MOTION_MASK
323                           | GDK_POINTER_MOTION_HINT_MASK);
324 
325    return widget;
326 }
327 
328 
329 static void
imageview_draw_create_thumbnail(GimvImageView * iv,const gchar * cache_write_type)330 imageview_draw_create_thumbnail (GimvImageView *iv, const gchar *cache_write_type)
331 {
332    if (!iv->image) {
333       gpointer id_p;
334       guint id;
335 
336       id_p = g_hash_table_lookup (create_thumbnail_id_table, iv);
337       id = GPOINTER_TO_UINT (id_p);
338       if (id > 0)
339          gtk_signal_disconnect (GTK_OBJECT (iv), id);
340       id = gtk_signal_connect (GTK_OBJECT (iv), "load_end",
341                                GTK_SIGNAL_FUNC (cb_load_end_create_thumbnail),
342                                GINT_TO_POINTER (TRUE));
343       g_hash_table_insert (create_thumbnail_id_table,
344                            iv, GUINT_TO_POINTER (id));
345       gimv_image_view_load_image_buf (iv);
346    } else {
347       cb_load_end_create_thumbnail (iv, iv->info,
348                                     FALSE, GINT_TO_POINTER (FALSE));
349    }
350 }
351 
352 
353 static gboolean
imageview_draw_is_playable(GimvImageView * iv,GimvImageInfo * info)354 imageview_draw_is_playable (GimvImageView *iv, GimvImageInfo *info)
355 {
356    g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), FALSE);
357    if (!info) return FALSE;
358 
359    return gimv_image_info_is_animation (info);
360 }
361 
362 
363 static void
imageview_animation_play(GimvImageView * iv)364 imageview_animation_play (GimvImageView *iv)
365 {
366    g_return_if_fail (GIMV_IS_IMAGE_VIEW (iv));
367    gtk_idle_add (idle_animation_play, iv);
368 }
369 
370 
371 #if 0
372 static void
373 imageview_animation_stop (GimvImageView *iv)
374 {
375    g_return_if_fail (iv);
376 
377    imageview_animation_pause (iv);
378 
379    if (!iv->info) return;
380    if (!gimv_image_info_is_animation (iv->info)) return;
381 
382    gimv_anim_seek ((GimvAnim *) iv->image, 0);
383 
384    imageview_show_image (iv);
385 
386    imageview_playable_set_status (iv, GimvImageViewPlayableStop);
387 }
388 
389 
390 static void
391 imageview_animation_pause (GimvImageView *iv)
392 {
393    gpointer id_p;
394    guint id;
395 
396    g_return_if_fail (iv);
397 
398    if (!animation_id_table) return;
399 
400    id_p = g_hash_table_lookup (animation_id_table, iv);
401    id = GPOINTER_TO_UINT (id_p);
402    if (id > 0)
403       gtk_timeout_remove (id);
404    g_hash_table_remove (animation_id_table, iv);
405 
406    imageview_playable_set_status (iv, GimvImageViewPlayablePause);
407 }
408 
409 #else
410 
411 static void
imageview_animation_stop(GimvImageView * iv)412 imageview_animation_stop (GimvImageView *iv)
413 {
414    gpointer id_p;
415    guint id;
416 
417    g_return_if_fail (iv);
418 
419    if (!animation_id_table) return;
420 
421    id_p = g_hash_table_lookup (animation_id_table, iv);
422    id = GPOINTER_TO_UINT (id_p);
423    if (id > 0)
424       gtk_timeout_remove (id);
425    g_hash_table_remove (animation_id_table, iv);
426 
427    gimv_image_view_playable_set_status (iv, GimvImageViewPlayableStop);
428 }
429 
430 #endif
431 
432 
433 static GimvImageViewPlayableStatus
imageview_draw_get_status(GimvImageView * iv)434 imageview_draw_get_status (GimvImageView *iv)
435 {
436    gpointer timer_p;
437    guint timer;
438 
439    g_return_val_if_fail (GIMV_IS_IMAGE_VIEW (iv), GimvImageViewPlayableDisable);
440 
441    if (!iv->info || !gimv_image_info_is_animation (iv->info))
442       return GimvImageViewPlayableDisable;
443 
444    timer_p = g_hash_table_lookup (animation_id_table, iv);
445    timer = GPOINTER_TO_UINT (timer_p);
446    if (timer > 0)
447       return GimvImageViewPlayablePlay;
448    else
449       return GimvImageViewPlayableStop;
450 }
451