2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /****************************
22 * Which image to load next *
23 ****************************/
26 #include "next_image.h"
28 #include "gliv_image.h"
29 #include "rendering.h"
31 #include "files_list.h"
35 #include "str_utils.h"
37 #include "callbacks.h"
40 extern options_struct
*options
;
41 extern gliv_image
*current_image
;
43 /* We can have previous_image == next_image. */
44 static gliv_image
*previous_image
= NULL
;
45 static gliv_image
*next_image
= NULL
;
47 static GtkMenuItem
*slide_show_menu
;
48 static guint slide_show_timer
= 0;
51 * Once the images list has been reordered we must take care
52 * of the current image position and destroy the previous and
53 * next images since they changed.
55 void after_reorder(gint new_id
)
57 if (previous_image
!= NULL
&&
58 current_image
->node
->prev
!= previous_image
->node
) {
60 destroy_image(previous_image
);
61 if (previous_image
== next_image
)
64 previous_image
= NULL
;
67 if (next_image
!= NULL
&& current_image
->node
->next
!= next_image
->node
) {
68 destroy_image(next_image
);
72 current_image
->number
= new_id
;
73 fill_ident(current_image
);
75 refresh(REFRESH_STATUS
);
77 g_free(current_image
->ident
);
78 current_image
->ident
= NULL
;
81 /*** Next/previous image ***/
83 static GList
*next_node(GList
* node
, gint dir
)
86 return (dir
== 1) ? get_list_head() : get_list_end();
95 static gliv_image
*load_next_node(GList
** node
, gint dir
)
97 gliv_image
*im
= NULL
;
100 while ((next
= next_node(*node
, dir
)) != NULL
) {
101 im
= load_file(next
->data
);
103 /* Delete bad images from the list. */
104 remove_from_list(next
);
114 static gliv_image
*load_in_direction(gint dir
)
116 gliv_image
*im
= NULL
;
120 if (get_list_length() == 2 && options
->loop
) {
122 * When there are only two images, and we are looping,
123 * next_image and previous_image are equal.
126 if (previous_image
!= NULL
)
127 return previous_image
;
129 if (next_image
!= NULL
)
133 if (current_image
== NULL
) {
138 node
= current_image
->node
;
139 id
= current_image
->number
+ dir
;
142 im
= load_next_node(&node
, dir
);
144 if (im
== NULL
&& options
->loop
&& get_list_length() > 1) {
145 /* Loop at the end. */
147 id
= (dir
== 1) ? 0 : (get_list_length() - 1);
148 im
= load_next_node(&node
, dir
);
159 /* Called when switching images. */
160 static void destroy_unused(gliv_image
** backup
, gliv_image
** next
)
163 * If there are only two images,
164 * we can have *backup == *next.
166 if (*backup
!= *next
) {
167 destroy_image(*backup
);
171 if (options
->one_image
)
172 destroy_image(current_image
);
174 prioritize_textures(current_image
, FALSE
);
175 *backup
= current_image
;
179 /* Return FALSE if there are no more images. */
180 gboolean
load_direction(gint dir
)
186 static volatile gint next_direction
;
189 * Before loading the next image in a prebuffer we let GTK process
190 * remaining events and if there is a request for the next
191 * image in the events pending then -> crash.
192 * With this boolean we process loading request only if we know
193 * all loadings are finished.
194 * IOW: we are not reentrant.
196 static gboolean load_finished
= TRUE
;
198 if (load_finished
== FALSE
) {
200 * We come from process_events() called here, so we inform
201 * that we want to load the next image.
203 next_direction
= dir
;
207 if (is_loading() || current_image
== NULL
)
210 load_finished
= FALSE
;
212 /* We process subsequent next image calls in the same loop. */
213 while (dir
* dir
== 1) {
217 backup
= &next_image
;
218 next
= &previous_image
;
221 backup
= &previous_image
;
226 * next_direction can be changed by us when called
227 * by process_events().
231 /* Be sure there is a next image. */
233 *next
= load_in_direction(dir
);
237 /* There is no next image to load. */
238 load_finished
= TRUE
;
242 destroy_unused(backup
, next
);
251 if (options
->one_image
== FALSE
)
252 *next
= load_in_direction(dir
);
254 /* Usually next_direction == 0, and the while loop ends. */
255 dir
= next_direction
;
258 load_finished
= TRUE
;
263 * After many images are opened we want to load
264 * the next image but not the preloaded one.
265 * Maybe the image after the one we will load does
268 void open_next_image(gboolean keep_next
)
274 if (previous_image
!= next_image
)
275 destroy_image(previous_image
);
279 prioritize_textures(current_image
, FALSE
);
280 previous_image
= current_image
;
282 current_image
= load_in_direction(1);
285 if (options
->one_image
== FALSE
&& next_image
== NULL
)
286 next_image
= load_in_direction(1);
289 /*** First/second image ***/
291 gliv_image
*load_first_image(void)
293 install_segv_handler();
295 if (get_list_head() == NULL
)
298 current_image
= load_in_direction(1);
299 if (current_image
== NULL
)
300 DIALOG_MSG(_("No image found"));
304 return current_image
;
307 void load_second_image(void)
309 if (next_image
== NULL
&& options
->one_image
== FALSE
)
310 next_image
= load_in_direction(1);
312 if (options
->start_show
)
316 /*** Random loading ***/
318 void open_file(const gchar
* filename
, gboolean add_dir
, gboolean shuffle_flag
)
326 /* Try to load the requested image. */
327 im
= load_file(filename
);
332 /* Add all files in the directory in the list. */
333 gchar
*dir
= g_path_get_dirname(filename
);
335 nb_inserted
= insert_after_current(&dir
, 1,
336 shuffle_flag
, !shuffle_flag
);
338 if (nb_inserted
== 0)
341 /* Put the requested image at current+1 position in the file list. */
342 place_next(filename
);
344 nb_inserted
= insert_after_current((gchar
**) & filename
, 1,
345 shuffle_flag
, !shuffle_flag
);
346 if (nb_inserted
== 0)
350 if (current_image
== NULL
) {
351 im
->node
= get_list_head();
354 im
->node
= current_image
->node
->next
;
355 im
->number
= current_image
->number
+ 1;
358 open_next_image(nb_inserted
== 1);
363 * Used for example, when an option is changed such as dithering or mipmap.
366 static gliv_image
*reload_image(gliv_image
* im
)
373 new = load_file(im
->node
->data
);
377 new->node
= im
->node
;
378 new->number
= im
->number
;
385 void reload_all_images(void)
390 current_image
= reload_image(current_image
);
391 prioritize_textures(current_image
, TRUE
);
393 rt
->filtering_enabled
= FALSE
;
394 refresh(REFRESH_NOW
);
396 if (previous_image
== next_image
)
397 previous_image
= next_image
= reload_image(previous_image
);
399 previous_image
= reload_image(previous_image
);
400 next_image
= reload_image(next_image
);
406 /* Check if we can optimize the loading asked by the images menu. */
407 static gboolean
check_direction(const gchar
* filename
, gint dir
)
409 gliv_image
**prev
, **next
;
413 prev
= &previous_image
;
416 next_node
= current_image
->node
->next
;
417 if (next_node
!= NULL
)
418 next_node
= next_node
->next
;
422 next
= &previous_image
;
424 next_node
= current_image
->node
->prev
;
425 if (next_node
!= NULL
)
426 next_node
= next_node
->prev
;
432 /* Check if the requested image is just following the current one. */
433 if (filename
== (*next
)->node
->data
) {
439 * Check if the requested image is just before the previous one,
440 * or just after the next one.
442 if (next_node
!= NULL
&& filename
== next_node
->data
) {
446 destroy_image(*prev
);
450 number
= current_image
->number
+ 2 * dir
;
452 new = load_file(filename
);
454 remove_from_list(next_node
);
458 destroy_image(current_image
);
460 current_image
->node
= next_node
;
461 current_image
->number
= number
;
470 void menu_load(const gchar
* filename
)
476 if (is_loading() || filename
== current_image
->node
->data
||
477 check_direction(filename
, -1) || check_direction(filename
, 1))
480 for (node
= get_list_head(); node
!= NULL
; node
= node
->next
) {
481 if (filename
== node
->data
)
488 /* The image is not in the list anymore. */
491 im
= load_file(filename
);
493 remove_from_list(node
);
500 current_image
->node
= node
;
501 current_image
->number
= number
;
506 /* Called when destroying a file. */
507 void unload(GList
* node
)
509 if (previous_image
!= NULL
&& previous_image
->node
== node
) {
510 destroy_image(previous_image
);
511 if (previous_image
== next_image
)
514 previous_image
= NULL
;
516 } else if (next_image
!= NULL
&& next_image
->node
== node
) {
517 destroy_image(next_image
);
522 /* Called when we want only one image in memory. */
523 void unload_images(void)
525 destroy_image(previous_image
);
527 if (previous_image
!= next_image
)
528 destroy_image(next_image
);
530 previous_image
= NULL
;
536 void set_slide_show_menu(GtkMenuItem
* item
)
538 slide_show_menu
= item
;
541 static void stop_slide_show(void)
543 if (slide_show_started()) {
544 g_source_remove(slide_show_timer
);
545 slide_show_timer
= 0;
548 set_menu_label(slide_show_menu
, _("Start the slide show"), TRUE
);
551 static gboolean
slide_show_next(void)
553 if (load_direction(1) == FALSE
) {
554 /* No more images. */
562 void start_slide_show(void)
566 if (options
->duration
< 0 || current_image
== NULL
)
569 slide_show_timer
= g_timeout_add(options
->duration
* 1000,
570 (GSourceFunc
) slide_show_next
, NULL
);
572 set_menu_label(slide_show_menu
, _("Stop the slide show"), TRUE
);
575 gboolean
slide_show_started(void)
577 return slide_show_timer
!= 0;
580 void toggle_slide_show(void)
582 if (slide_show_started())