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