Simplify vflist_get_formatted()
[geeqie.git] / src / slideshow.c
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
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 along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "main.h"
23 #include "collect.h"
24 #include "image.h"
25 #include "slideshow.h"
26 #include "filedata.h"
27
28 #include "layout.h"
29 #include "layout_image.h"
30 #include "ui_fileops.h"
31
32
33 static void slideshow_timer_stop(SlideShowData *ss);
34
35
36 void slideshow_free(SlideShowData *ss)
37 {
38         if (!ss) return;
39
40         slideshow_timer_stop(ss);
41
42         if (ss->stop_func) ss->stop_func(ss, ss->stop_data);
43
44         if (ss->filelist) filelist_free(ss->filelist);
45         if (ss->cd) collection_unref(ss->cd);
46         file_data_unref(ss->dir_fd);
47
48         g_list_free(ss->list);
49         g_list_free(ss->list_done);
50
51         file_data_unref(ss->slide_fd);
52
53         g_free(ss);
54 }
55
56 static GList *generate_list(SlideShowData *ss)
57 {
58         GList *list = NULL;
59
60         if (ss->from_selection)
61                 {
62                 list = layout_selection_list_by_index(ss->lw);
63                 }
64         else
65                 {
66                 guint i;
67                 for (i = 0; i < ss->slide_count; i++)
68                         {
69                         list = g_list_prepend(list, GINT_TO_POINTER(i));
70                         }
71                 list = g_list_reverse(list);
72                 }
73
74         return list;
75 }
76
77 static void ptr_array_add(gpointer data, GPtrArray *array)
78 {
79         g_ptr_array_add(array, data);
80 }
81
82 static void list_prepend(gpointer data, GList **list)
83 {
84         *list = g_list_prepend(*list, data);
85 }
86
87 static GPtrArray *generate_ptr_array_from_list(GList *src_list)
88 {
89         GPtrArray *arr = g_ptr_array_sized_new(g_list_length(src_list));
90
91         g_list_foreach(src_list, (GFunc) ptr_array_add, arr);
92
93         return arr;
94 }
95
96 static void swap(GPtrArray *array, guint index1, guint index2)
97 {
98         gpointer temp = g_ptr_array_index(array, index1);
99
100         g_ptr_array_index(array, index1) = g_ptr_array_index(array, index2);
101         g_ptr_array_index(array, index2) = temp;
102 }
103
104 static void ptr_array_random_shuffle(GPtrArray *array)
105 {
106         guint i;
107         for (i = 0; i < array->len; ++i)
108                 {
109                 guint p = (double)rand() / ((double)RAND_MAX + 1.0) * array->len;
110                 swap(array, i, p);
111                 }
112 }
113
114 static GList *generate_random_list(SlideShowData *ss)
115 {
116         GList *src_list;
117         GPtrArray *src_array;
118         GList *list = NULL;
119
120         src_list = generate_list(ss);
121         src_array = generate_ptr_array_from_list(src_list);
122         g_list_free(src_list);
123
124         ptr_array_random_shuffle(src_array);
125         g_ptr_array_foreach(src_array, (GFunc) list_prepend, &list);
126         g_ptr_array_free(src_array, TRUE);
127
128         return list;
129 }
130
131 static void slideshow_list_init(SlideShowData *ss, gint start_index)
132 {
133         if (ss->list_done)
134                 {
135                 g_list_free(ss->list_done);
136                 ss->list_done = NULL;
137                 }
138
139         if (ss->list) g_list_free(ss->list);
140
141         if (options->slideshow.random)
142                 {
143                 ss->list = generate_random_list(ss);
144                 }
145         else
146                 {
147                 ss->list = generate_list(ss);
148                 if (start_index >= 0)
149                         {
150                         /* start with specified image by skipping to it */
151                         gint i = 0;
152
153                         while (ss->list && i < start_index)
154                                 {
155                                 ss->list_done = g_list_prepend(ss->list_done, ss->list->data);
156                                 ss->list = g_list_remove(ss->list, ss->list->data);
157                                 i++;
158                                 }
159                         }
160                 }
161 }
162
163 gboolean slideshow_should_continue(SlideShowData *ss)
164 {
165         FileData *imd_fd;
166         FileData *dir_fd;
167
168         if (!ss) return FALSE;
169
170         if (ss->lw)
171                 imd_fd = layout_image_get_fd(ss->lw);
172         else
173                 imd_fd = image_get_fd(ss->imd);
174
175         if ( ((imd_fd == NULL) != (ss->slide_fd == NULL)) ||
176             (imd_fd && ss->slide_fd && imd_fd != ss->slide_fd) ) return FALSE;
177
178         if (ss->filelist) return TRUE;
179
180         if (ss->cd)
181                 {
182                 if (g_list_length(ss->cd->list) == ss->slide_count)
183                         return TRUE;
184                 else
185                         return FALSE;
186                 }
187
188         dir_fd = ss->lw->dir_fd;
189
190         if (dir_fd && ss->dir_fd && dir_fd == ss->dir_fd)
191                 {
192                 if (ss->from_selection && ss->slide_count == layout_selection_count(ss->lw, NULL)) return TRUE;
193                 if (!ss->from_selection && ss->slide_count == layout_list_count(ss->lw, NULL)) return TRUE;
194                 }
195
196         return FALSE;
197 }
198
199 static gboolean slideshow_step(SlideShowData *ss, gboolean forward)
200 {
201         gint row;
202
203         if (!slideshow_should_continue(ss))
204                 {
205                 return FALSE;
206                 }
207
208         if (forward)
209                 {
210                 if (!ss->list) return TRUE;
211
212                 row = GPOINTER_TO_INT(ss->list->data);
213                 ss->list_done = g_list_prepend(ss->list_done, ss->list->data);
214                 ss->list = g_list_remove(ss->list, ss->list->data);
215                 }
216         else
217                 {
218                 if (!ss->list_done || !ss->list_done->next) return TRUE;
219
220                 ss->list = g_list_prepend(ss->list, ss->list_done->data);
221                 ss->list_done = g_list_remove(ss->list_done, ss->list_done->data);
222                 row = GPOINTER_TO_INT(ss->list_done->data);
223                 }
224
225         file_data_unref(ss->slide_fd);
226         ss->slide_fd = NULL;
227
228         if (ss->filelist)
229                 {
230                 ss->slide_fd = file_data_ref((FileData *)g_list_nth_data(ss->filelist, row));
231                 if (ss->lw)
232                         layout_set_fd(ss->lw, ss->slide_fd);
233                 else
234                         image_change_fd(ss->imd, ss->slide_fd, image_zoom_get_default(ss->imd));
235                 }
236         else if (ss->cd)
237                 {
238                 CollectInfo *info;
239
240                 info = g_list_nth_data(ss->cd->list, row);
241                 ss->slide_fd = file_data_ref(info->fd);
242
243                 if (ss->lw)
244                         image_change_from_collection(ss->lw->image, ss->cd, info, image_zoom_get_default(ss->lw->image));
245                 else
246                         image_change_from_collection(ss->imd, ss->cd, info, image_zoom_get_default(ss->imd));
247                 }
248         else
249                 {
250                 ss->slide_fd = file_data_ref(layout_list_get_fd(ss->lw, row));
251
252                 if (ss->from_selection)
253                         {
254                         layout_set_fd(ss->lw, ss->slide_fd);
255                         layout_status_update_info(ss->lw, NULL);
256                         }
257                 else
258                         {
259                         layout_image_set_index(ss->lw, row);
260                         }
261                 }
262
263         if (!ss->list && options->slideshow.repeat)
264                 {
265                 slideshow_list_init(ss, -1);
266                 }
267
268         if (!ss->list)
269                 {
270                 return FALSE;
271                 }
272
273         /* read ahead */
274         if (options->image.enable_read_ahead && (!ss->lw || ss->from_selection))
275                 {
276                 gint r;
277                 if (forward)
278                         {
279                         if (!ss->list) return TRUE;
280                         r = GPOINTER_TO_INT(ss->list->data);
281                         }
282                 else
283                         {
284                         if (!ss->list_done || !ss->list_done->next) return TRUE;
285                         r = GPOINTER_TO_INT(ss->list_done->next->data);
286                         }
287
288                 if (ss->filelist)
289                         {
290                         image_prebuffer_set(ss->imd, g_list_nth_data(ss->filelist, r));
291                         }
292                 else if (ss->cd)
293                         {
294                         CollectInfo *info;
295                         info = g_list_nth_data(ss->cd->list, r);
296                         if (info) image_prebuffer_set(ss->imd, info->fd);
297                         }
298                 else if (ss->from_selection)
299                         {
300                         image_prebuffer_set(ss->lw->image, layout_list_get_fd(ss->lw, r));
301                         }
302                 }
303
304         return TRUE;
305 }
306
307 static void slideshow_timer_reset(SlideShowData *ss);
308
309 static gboolean slideshow_loop_cb(gpointer data)
310 {
311         SlideShowData *ss = data;
312
313         if (ss->paused) return TRUE;
314
315         if (!slideshow_step(ss, TRUE))
316                 {
317                 ss->timeout_id = 0;
318                 slideshow_free(ss);
319                 return FALSE;
320                 }
321
322         /* Check if the user has changed the timer interval */
323         slideshow_timer_reset(ss);
324
325         return TRUE;
326 }
327
328 static void slideshow_timer_stop(SlideShowData *ss)
329 {
330         if (!ss->timeout_id) return;
331
332         g_source_remove(ss->timeout_id);
333         ss->timeout_id = 0;
334 }
335
336 static void slideshow_timer_reset(SlideShowData *ss)
337 {
338         if (options->slideshow.delay < 1) options->slideshow.delay = 1;
339
340         if (ss->timeout_id) g_source_remove(ss->timeout_id);
341         ss->timeout_id = g_timeout_add(options->slideshow.delay * 1000 / SLIDESHOW_SUBSECOND_PRECISION,
342                                        slideshow_loop_cb, ss);
343 }
344
345 static void slideshow_move(SlideShowData *ss, gboolean forward)
346 {
347         if (!ss) return;
348
349         if (!slideshow_step(ss, forward))
350                 {
351                 slideshow_free(ss);
352                 return;
353                 }
354
355         slideshow_timer_reset(ss);
356 }
357
358 void slideshow_next(SlideShowData *ss)
359 {
360         slideshow_move(ss, TRUE);
361 }
362
363 void slideshow_prev(SlideShowData *ss)
364 {
365         slideshow_move(ss, FALSE);
366 }
367
368 static SlideShowData *real_slideshow_start(LayoutWindow *target_lw, ImageWindow *imd,
369                                            GList *filelist, gint start_point,
370                                            CollectionData *cd, CollectInfo *start_info,
371                                            void (*stop_func)(SlideShowData *, gpointer), gpointer stop_data)
372 {
373         SlideShowData *ss;
374         gint start_index = -1;
375
376         if (!filelist && !cd && layout_list_count(target_lw, NULL) < 1) return NULL;
377
378         ss = g_new0(SlideShowData, 1);
379
380         ss->lw = target_lw;
381         ss->imd = imd; /** @FIXME ss->imd is used only for img-view.c and can be dropped with it */
382         ss->filelist = filelist;
383         ss->cd = cd;
384
385         if (ss->filelist)
386                 {
387                 ss->slide_count = g_list_length(ss->filelist);
388                 }
389         else if (ss->cd)
390                 {
391                 collection_ref(ss->cd);
392                 ss->slide_count = g_list_length(ss->cd->list);
393                 if (!options->slideshow.random && start_info)
394                         {
395                         start_index = g_list_index(ss->cd->list, start_info);
396                         }
397                 }
398         else
399                 {
400                 /* layout method */
401
402                 ss->slide_count = layout_selection_count(ss->lw, NULL);
403                 ss->dir_fd = file_data_ref(ss->lw->dir_fd);
404                 if (ss->slide_count < 2)
405                         {
406                         ss->slide_count = layout_list_count(ss->lw, NULL);
407                         if (!options->slideshow.random && start_point >= 0 && (guint) start_point < ss->slide_count)
408                                 {
409                                 start_index = start_point;
410                                 }
411                         }
412                 else
413                         {
414                         ss->from_selection = TRUE;
415                         }
416                 }
417
418         slideshow_list_init(ss, start_index);
419
420         if (ss->lw)
421                 ss->slide_fd = file_data_ref(layout_image_get_fd(ss->lw));
422         else
423                 ss->slide_fd = file_data_ref(image_get_fd(ss->imd));
424
425         if (slideshow_step(ss, TRUE))
426                 {
427                 slideshow_timer_reset(ss);
428
429                 ss->stop_func = stop_func;
430                 ss->stop_data = stop_data;
431                 }
432         else
433                 {
434                 slideshow_free(ss);
435                 ss = NULL;
436                 }
437
438         return ss;
439 }
440
441 SlideShowData *slideshow_start_from_filelist(LayoutWindow *target_lw, ImageWindow *imd, GList *list,
442                                               void (*stop_func)(SlideShowData *, gpointer), gpointer stop_data)
443 {
444         return real_slideshow_start(target_lw, imd, list, -1, NULL, NULL, stop_func, stop_data);
445 }
446
447 SlideShowData *slideshow_start_from_collection(LayoutWindow *target_lw, ImageWindow *imd, CollectionData *cd,
448                                                void (*stop_func)(SlideShowData *, gpointer), gpointer stop_data,
449                                                CollectInfo *start_info)
450 {
451         return real_slideshow_start(target_lw, imd, NULL, -1, cd, start_info, stop_func, stop_data);
452 }
453
454 SlideShowData *slideshow_start(LayoutWindow *lw, gint start_point,
455                                void (*stop_func)(SlideShowData *, gpointer), gpointer stop_data)
456 {
457         return real_slideshow_start(lw, NULL, NULL, start_point, NULL, NULL, stop_func, stop_data);
458 }
459
460 gboolean slideshow_paused(SlideShowData *ss)
461 {
462         if (!ss) return FALSE;
463
464         return ss->paused;
465 }
466
467 void slideshow_pause_set(SlideShowData *ss, gboolean paused)
468 {
469         if (!ss) return;
470
471         ss->paused = paused;
472 }
473
474 gboolean slideshow_pause_toggle(SlideShowData *ss)
475 {
476         slideshow_pause_set(ss, !slideshow_paused(ss));
477         return slideshow_paused(ss);
478 }
479 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */