Wrong initialization for nb_inserted in the deserialization.
[gliv.git] / src / next_image.c
blob24cf365d0a386f7ed5a9a4ff806b8fba635a2532
1 /*
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 ****************************/
25 #include "gliv.h"
26 #include "next_image.h"
27 #include "options.h"
28 #include "gliv_image.h"
29 #include "rendering.h"
30 #include "loading.h"
31 #include "files_list.h"
32 #include "textures.h"
33 #include "messages.h"
34 #include "main.h"
35 #include "str_utils.h"
36 #include "menus.h"
37 #include "callbacks.h"
39 extern rt_struct *rt;
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)
62 next_image = NULL;
64 previous_image = NULL;
67 if (next_image != NULL && current_image->node->next != next_image->node) {
68 destroy_image(next_image);
69 next_image = NULL;
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)
85 if (node == NULL)
86 return (dir == 1) ? get_list_head() : get_list_end();
88 if (dir == 1)
89 return node->next;
91 /* dir == -1 */
92 return node->prev;
95 static gliv_image *load_next_node(GList ** node, gint dir)
97 gliv_image *im = NULL;
98 GList *next;
100 while ((next = next_node(*node, dir)) != NULL) {
101 im = load_file(next->data);
102 if (im == NULL)
103 /* Delete bad images from the list. */
104 remove_from_list(next);
105 else {
106 *node = next;
107 break;
111 return im;
114 static gliv_image *load_in_direction(gint dir)
116 gliv_image *im = NULL;
117 GList *node;
118 gint id;
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)
130 return next_image;
133 if (current_image == NULL) {
134 /* First time. */
135 node = NULL;
136 id = 0;
137 } else {
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. */
146 node = NULL;
147 id = (dir == 1) ? 0 : (get_list_length() - 1);
148 im = load_next_node(&node, dir);
151 if (im != NULL) {
152 im->node = node;
153 im->number = id;
156 return im;
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);
168 *backup = NULL;
171 if (options->one_image)
172 destroy_image(current_image);
173 else {
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)
182 gliv_image **backup;
183 gliv_image **next;
184 gliv_image *im;
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;
204 return TRUE;
207 if (is_loading() || current_image == NULL)
208 return TRUE;
210 load_finished = FALSE;
212 /* We process subsequent next image calls in the same loop. */
213 while (dir * dir == 1) {
215 if (dir < 0) {
216 /* dirr == -1 */
217 backup = &next_image;
218 next = &previous_image;
219 } else {
220 /* dir == 1 */
221 backup = &previous_image;
222 next = &next_image;
226 * next_direction can be changed by us when called
227 * by process_events().
229 next_direction = 0;
231 /* Be sure there is a next image. */
232 if (*next == NULL)
233 *next = load_in_direction(dir);
235 im = *next;
236 if (im == NULL) {
237 /* There is no next image to load. */
238 load_finished = TRUE;
239 return FALSE;
242 destroy_unused(backup, next);
244 current_image = im;
245 *next = NULL;
247 render();
249 process_events();
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;
259 return 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
266 * not change.
268 void open_next_image(gboolean keep_next)
270 if (is_loading())
271 return;
273 if (keep_next) {
274 if (previous_image != next_image)
275 destroy_image(previous_image);
276 } else
277 unload_images();
279 prioritize_textures(current_image, FALSE);
280 previous_image = current_image;
282 current_image = load_in_direction(1);
283 render();
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)
296 return NULL;
298 current_image = load_in_direction(1);
299 if (current_image == NULL)
300 DIALOG_MSG(_("No image found"));
301 else
302 render();
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)
313 start_slide_show();
316 /*** Random loading ***/
318 void open_file(const gchar * filename, gboolean add_dir, gboolean shuffle_flag)
320 gliv_image *im;
321 gint nb_inserted;
323 if (is_loading())
324 return;
326 /* Try to load the requested image. */
327 im = load_file(filename);
328 if (im == NULL)
329 return;
331 if (add_dir) {
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);
337 g_free(dir);
338 if (nb_inserted == 0)
339 return;
341 /* Put the requested image at current+1 position in the file list. */
342 place_next(filename);
343 } else {
344 nb_inserted = insert_after_current((gchar **) & filename, 1,
345 shuffle_flag, !shuffle_flag);
346 if (nb_inserted == 0)
347 return;
350 if (current_image == NULL) {
351 im->node = get_list_head();
352 im->number = 0;
353 } else {
354 im->node = current_image->node->next;
355 im->number = current_image->number + 1;
358 open_next_image(nb_inserted == 1);
362 * Reloading.
363 * Used for example, when an option is changed such as dithering or mipmap.
366 static gliv_image *reload_image(gliv_image * im)
368 gliv_image *new;
370 if (im == NULL)
371 return NULL;
373 new = load_file(im->node->data);
374 if (new == NULL)
375 return im;
377 new->node = im->node;
378 new->number = im->number;
380 destroy_image(im);
382 return new;
385 void reload_all_images(void)
387 if (is_loading())
388 return;
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);
398 else {
399 previous_image = reload_image(previous_image);
400 next_image = reload_image(next_image);
404 /*** Menu ***/
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;
410 GList *next_node;
412 if (dir == 1) {
413 prev = &previous_image;
414 next = &next_image;
416 next_node = current_image->node->next;
417 if (next_node != NULL)
418 next_node = next_node->next;
419 } else {
420 /* dir == -1 */
421 prev = &next_image;
422 next = &previous_image;
424 next_node = current_image->node->prev;
425 if (next_node != NULL)
426 next_node = next_node->prev;
429 if (*next == NULL)
430 return FALSE;
432 /* Check if the requested image is just following the current one. */
433 if (filename == (*next)->node->data) {
434 load_direction(dir);
435 return TRUE;
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) {
443 gint number;
444 gliv_image *new;
446 destroy_image(*prev);
447 *prev = *next;
448 *next = NULL;
450 number = current_image->number + 2 * dir;
452 new = load_file(filename);
453 if (new == NULL) {
454 remove_from_list(next_node);
455 return TRUE;
458 destroy_image(current_image);
459 current_image = new;
460 current_image->node = next_node;
461 current_image->number = number;
463 render();
464 return TRUE;
467 return FALSE;
470 void menu_load(const gchar * filename)
472 GList *node;
473 gint number = 0;
474 gliv_image *im;
476 if (is_loading() || filename == current_image->node->data ||
477 check_direction(filename, -1) || check_direction(filename, 1))
478 return;
480 for (node = get_list_head(); node != NULL; node = node->next) {
481 if (filename == node->data)
482 break;
484 number++;
487 if (node == NULL)
488 /* The image is not in the list anymore. */
489 return;
491 im = load_file(filename);
492 if (im == NULL) {
493 remove_from_list(node);
494 return;
497 unload_images();
499 current_image = im;
500 current_image->node = node;
501 current_image->number = number;
503 render();
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)
512 next_image = NULL;
514 previous_image = NULL;
516 } else if (next_image != NULL && next_image->node == node) {
517 destroy_image(next_image);
518 next_image = NULL;
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;
531 next_image = NULL;
534 /*** Slide show ***/
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. */
555 stop_slide_show();
556 return FALSE;
559 return TRUE;
562 void start_slide_show(void)
564 stop_slide_show();
566 if (options->duration < 0 || current_image == NULL)
567 return;
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())
583 stop_slide_show();
584 else
585 start_slide_show();