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