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