spiv: Fix image list counter
[gfxprim.git] / demos / spiv / image_list.c
blobcf1d18c09a21e4572dc2f9eb47ff495f10542073
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
31 #include <loaders/GP_Loader.h>
32 #include <core/GP_Debug.h>
34 #include "image_list.h"
36 struct image_list {
37 /* list we got from the app */
38 const char **args;
39 unsigned int cur_arg;
40 unsigned int max_arg;
41 /* counters for files in corresponding arg */
42 int *arg_file_counts;
44 /* path to the currently loaded image */
45 char path[1024];
46 int path_loaded:1;
48 /* directory handling */
49 int in_dir:1;
51 int cur_file;
52 int max_file;
53 struct dirent **dir_files;
56 static int dir_cmp(const struct dirent **a, const struct dirent **b)
58 return strcasecmp((*a)->d_name, (*b)->d_name);
61 static int dir_filter(const struct dirent *d)
63 /* Ignore some filenames */
64 if (!strcmp(d->d_name, "."))
65 return 0;
67 if (!strcmp(d->d_name, ".."))
68 return 0;
70 //TODO: filter out directories
72 if (!GP_LoaderByFilename(d->d_name))
73 return 0;
75 GP_DEBUG(4, "Adding file '%s'", d->d_name);
77 return 1;
80 static void try_load_dir(struct image_list *self)
82 struct stat sb;
83 const char *path = self->args[self->cur_arg];
85 if (stat(path, &sb)) {
86 GP_WARN("Failed to stat '%s': %s", path, strerror(errno));
87 return;
90 if (!(sb.st_mode & S_IFDIR)) {
91 self->arg_file_counts[self->cur_arg] = 1;
92 return;
95 GP_DEBUG(1, "Loading directory '%s' content.", path);
97 int ret = scandir(path, &self->dir_files, dir_filter, dir_cmp);
99 if (ret == -1) {
100 GP_WARN("Failed to scandir '%s': %s", path, strerror(errno));
101 return;
104 if (self->arg_file_counts[self->cur_arg] != ret) {
105 GP_DEBUG(1, "Updating arg counter to %i", ret);
106 self->arg_file_counts[self->cur_arg] = ret;
109 if (ret == 0) {
110 GP_DEBUG(1, "There are no files in '%s'", path);
111 return;
114 self->max_file = ret;
115 self->cur_file = 0;
116 self->in_dir = 1;
119 static void exit_dir(struct image_list *self)
121 int i;
123 GP_DEBUG(1, "Freeing directory '%s' content.",
124 self->args[self->cur_arg]);
126 for (i = 0; i < self->max_file; i++)
127 free(self->dir_files[i]);
129 free(self->dir_files);
131 self->in_dir = 0;
134 static void next_img(struct image_list *self)
136 if (self->in_dir) {
137 if (++self->cur_file == self->max_file) {
138 exit_dir(self);
139 } else {
140 self->path_loaded = 0;
141 return;
145 if (++self->cur_arg == self->max_arg)
146 self->cur_arg = 0;
148 try_load_dir(self);
150 self->path_loaded = 0;
153 static void prev_img(struct image_list *self)
155 if (self->in_dir) {
156 if (self->cur_file-- == 0) {
157 exit_dir(self);
158 } else {
159 self->path_loaded = 0;
160 return;
164 /* If we are at first image -> wrap around argv */
165 if (self->cur_arg == 0)
166 self->cur_arg = self->max_arg - 1;
167 else
168 self->cur_arg--;
170 try_load_dir(self);
172 /* if in directory, select last image in it */
173 if (self->in_dir)
174 self->cur_file = self->max_file - 1;
176 self->path_loaded = 0;
180 * Sets current image, if we are in directory.
182 static void set_dir_cur_img(struct image_list *self, int img)
184 if (!self->in_dir) {
185 GP_BUG("Not in directory at %s",
186 self->args[self->cur_arg]);
187 return;
190 if (img < 0 || img >= self->max_file) {
191 GP_BUG("Invalid image index %i", img);
192 return;
195 /* allready there */
196 if (self->cur_file == img)
197 return;
199 self->cur_file = img;
200 self->path_loaded = 0;
204 * Returns current argument from arg list we are in.
206 * Either it's image file or directory.
208 static const char *cur_arg(struct image_list *self)
210 return self->args[self->cur_arg];
214 * Sets current argument from arg list.
216 static void set_cur_arg(struct image_list *self, int arg)
218 if (arg < 0 || arg >= (int)self->max_arg) {
219 GP_BUG("Invalid argument index %i", arg);
220 return;
223 if (self->in_dir) {
224 if ((int)self->cur_arg != arg) {
225 exit_dir(self);
226 self->cur_arg = arg;
227 self->path_loaded = 0;
228 try_load_dir(self);
229 } else {
230 set_dir_cur_img(self, 0);
232 return;
235 if ((int)self->cur_arg == arg)
236 return;
238 self->cur_arg = arg;
239 self->path_loaded = 0;
241 try_load_dir(self);
244 static void load_path(struct image_list *self)
246 if (self->in_dir) {
247 //TODO: eliminate double /
248 snprintf(self->path, sizeof(self->path), "%s/%s",
249 cur_arg(self),
250 self->dir_files[self->cur_file]->d_name);
252 } else {
253 snprintf(self->path, sizeof(self->path), "%s", cur_arg(self));
256 self->path_loaded = 1;
259 const char *image_list_move(struct image_list *self, int direction)
261 GP_DEBUG(2, "Moving list by %i", direction);
263 int i;
265 for (i = 0; i < direction; i++)
266 next_img(self);
268 for (i = 0; i > direction; i--)
269 prev_img(self);
271 return image_list_img_path(self);
274 const char *image_list_dir_move(struct image_list *self, int direction)
276 if (!self->in_dir) {
277 GP_DEBUG(2, "Not in directory");
278 return image_list_move(self, direction);
281 if (direction > 0) {
282 if (self->cur_file == self->max_file - 1) {
283 GP_DEBUG(2, "Moving after dir '%s'", cur_arg(self));
284 next_img(self);
285 } else {
286 GP_DEBUG(2, "Moving to last image in dir '%s'",
287 cur_arg(self));
288 set_dir_cur_img(self, self->max_file - 1);
290 } else {
291 if (self->cur_file == 0) {
292 GP_DEBUG(2, "Moving before dir '%s'", cur_arg(self));
293 prev_img(self);
294 } else {
295 GP_DEBUG(2, "Moving to first image in dir '%s'",
296 cur_arg(self));
297 set_dir_cur_img(self, 0);
301 return image_list_img_path(self);
304 const char *image_list_first(struct image_list *self)
306 GP_DEBUG(2, "Moving to the first image in the list");
308 set_cur_arg(self, 0);
310 if (self->in_dir)
311 set_dir_cur_img(self, 0);
313 return image_list_img_path(self);
316 const char *image_list_last(struct image_list *self)
318 GP_DEBUG(2, "Moving to the last image in the list");
320 set_cur_arg(self, self->max_arg - 1);
322 if (self->in_dir)
323 set_dir_cur_img(self, self->max_file - 1);
325 return image_list_img_path(self);
328 static unsigned int count_img_to(struct image_list *self, unsigned int arg_to)
330 unsigned int cur_arg = self->cur_arg;
331 unsigned int cur_file = self->cur_file;
332 unsigned int count = 0, i;
334 for (i = 0; i < arg_to; i++) {
335 /* cache number of images in arg */
336 if (self->arg_file_counts[i] == -1)
337 set_cur_arg(self, i);
340 * if the counter is still at -1
341 * directory couldn't be loaded
343 if (self->arg_file_counts[i] != -1)
344 count += self->arg_file_counts[i];
347 /* restore the original position */
348 set_cur_arg(self, cur_arg);
350 if (self->in_dir)
351 set_dir_cur_img(self, cur_file);
353 return count;
356 unsigned int image_list_count(struct image_list *self)
358 return count_img_to(self, self->max_arg);
361 unsigned int image_list_pos(struct image_list *self)
363 if (!self->in_dir)
364 return count_img_to(self, self->cur_arg);
366 return count_img_to(self, self->cur_arg) + self->cur_file;
369 unsigned int image_list_dir_count(struct image_list *self)
371 if (!self->in_dir)
372 return 0;
374 return self->max_file;
377 unsigned int image_list_dir_pos(struct image_list *self)
379 if (!self->in_dir)
380 return 0;
382 return self->cur_file;
385 struct image_list *image_list_create(const char *args[])
387 struct image_list *self;
388 size_t file_count_size;
389 unsigned int i;
391 GP_DEBUG(1, "Creating image list");
393 self = malloc(sizeof(struct image_list));
395 if (self == NULL) {
396 GP_WARN("Malloc failed");
397 return NULL;
400 self->args = args;
401 self->cur_arg = 0;
403 self->path_loaded = 0;
405 self->dir_files = 0;
406 self->in_dir = 0;
408 self->max_arg = 0;
409 while (args[++self->max_arg] != NULL);
411 file_count_size = self->max_arg * sizeof(int);
412 self->arg_file_counts = malloc(file_count_size);
414 if (self->arg_file_counts == NULL) {
415 GP_WARN("Malloc failed");
416 free(self);
417 return NULL;
420 for (i = 0; i < self->max_arg; i++)
421 self->arg_file_counts[i] = -1;
423 try_load_dir(self);
425 return self;
428 void image_list_destroy(struct image_list *self)
430 if (self->in_dir)
431 exit_dir(self);
433 free(self->arg_file_counts);
434 free(self);
437 const char *image_list_img_path(struct image_list *self)
439 if (!self->path_loaded)
440 load_path(self);
442 GP_DEBUG(2, "Returning path '%s'", self->path);
444 return self->path;