gliv-1.6
[gliv.git] / src / files_list.c
blob5a047b3572ae81c9dfdb470588f973a2d0cfff1d
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <gfc@altern.org>
21 /*******************
22 * The files list. *
23 *******************/
25 #include "gliv.h"
27 #include <ftw.h>
28 #include <string.h> /* strcmp(), strchr() */
29 #include <sys/stat.h> /* stat() */
30 #include <dirent.h> /* DIR, opendir(), readdir(), closedir() */
31 #include <stdlib.h> /* qsort() */
32 #include <stdio.h> /* perror() */
34 extern options_struct *options;
35 extern gliv_image *current_image;
37 static GList *files_list = NULL;
38 static gint list_length = 0;
40 gint get_list_length(void)
42 return list_length;
45 GList *get_list_head(void)
47 return files_list;
50 /*** Additions. ***/
52 static void add_file_to_list(const gchar * filename)
54 if (options->force || loader_by_filename(filename) != LOADER_NONE) {
55 files_list = g_list_prepend(files_list, clean_filename(filename));
56 list_length++;
60 /* Called only by ftw(). */
61 static gint ftw_add_to_list(const gchar * filename,
62 const struct stat *s, gint flag)
64 if (flag == FTW_F && S_ISDIR(s->st_mode) == FALSE)
65 add_file_to_list(filename);
67 return 0;
70 static void add_to_list(const gchar * name, gboolean recursive)
72 struct stat st;
73 struct dirent *dir_ent;
74 DIR *dir;
75 gchar *full_path;
77 if (stat(name, &st) < 0) {
78 perror(name);
79 return;
82 if (S_ISDIR(st.st_mode)) {
84 if (recursive)
85 /* Traverse recursively the directory. */
86 ftw(name, ftw_add_to_list, 32);
88 else {
89 /* Add every file in the directory. */
90 dir = opendir(name);
91 if (dir == NULL)
92 return;
94 for (dir_ent = readdir(dir); dir_ent != NULL;
95 dir_ent = readdir(dir)) {
97 full_path = fast_build_filename(name, dir_ent->d_name);
99 stat(full_path, &st);
101 if (S_ISDIR(st.st_mode) == FALSE)
102 add_file_to_list(full_path);
104 g_free(full_path);
106 closedir(dir);
108 } else
109 /* Add the file. */
110 add_file_to_list(name);
113 void remove_from_list(GList * node)
115 g_free(node->data);
116 files_list = g_list_delete_link(files_list, node);
117 list_length--;
120 /*** Sorting ***/
122 /* To shuffle the list, we simply sort with a random compare func. */
123 static gint random_compar(gconstpointer unused1, gconstpointer unused2)
125 return g_random_int_range(-1, 3);
128 /* We want children to be before their parent, or the alphabetical order. */
129 G_GNUC_PURE static gint filename_compar(gconstpointer a, gconstpointer b)
131 const gchar *file1, *file2, *ptr1, *ptr2;
132 gboolean ptr1_has_dir, ptr2_has_dir;
134 ptr1 = file1 = (const gchar *) a;
135 ptr2 = file2 = (const gchar *) b;
137 if (file1[0] != file2[0])
138 /* Comparing an absolute filename, and a relative one. */
139 return file1[0] == '/' ? -1 : 1;
141 /* Skip identical characters in the paths. */
142 while (*ptr1 != '\0' && *ptr1 == *ptr2) {
143 ptr1++;
144 ptr2++;
147 if (*ptr1 == *ptr2)
148 /* The filenames were equal. */
149 return 0;
151 /* Go back to the first different dir. */
152 while (*ptr1 != '/' || *ptr2 != '/') {
153 ptr1--;
154 ptr2--;
157 /* Skip the common '/'. */
158 ptr1++;
159 ptr2++;
161 ptr1_has_dir = (strchr(ptr1, '/') != NULL);
162 ptr2_has_dir = (strchr(ptr2, '/') != NULL);
164 if (ptr1_has_dir == ptr2_has_dir)
166 * Either the files are in the same directory,
167 * or they are not parent.
169 return strcmp(ptr1, ptr2);
171 /* One of the directory is parent of the other. */
172 return ptr1_has_dir ? -1 : 1;
176 static void reorder_list(gboolean shuffle)
178 GCompareFunc compare_func;
180 compare_func = shuffle ? random_compar : filename_compar;
181 files_list = g_list_sort(files_list, compare_func);
184 /* Called by the menu. */
185 void reorder_files(gboolean shuffle)
187 gchar *current_filename;
188 gint new_id = 0;
189 GList *node;
191 if (files_list == NULL)
192 return;
194 current_filename = current_image->node->data;
196 reorder_list(shuffle);
198 /* Search the new id for current_image. */
199 for (node = files_list; node != NULL; node = node->next) {
200 if (g_str_equal(current_filename, node->data))
201 break;
202 new_id++;
205 after_reorder(new_id);
208 G_GNUC_PURE static gint reverse_compar(gconstpointer a, gconstpointer b)
210 return -filename_compar(*((const gchar **) a), *((const gchar **) b));
214 * Used to build the images menus.
215 * The logic would be to sort the array, and then build the menus
216 * with calls to gtk_menu_shell_append().
217 * But as the latter appends to a list by searching the end each time,
218 * we reverse sort the array and then insert in the list head.
220 gchar **get_sorted_files_array(void)
222 gchar **array, **array_ptr;
223 GList *list_ptr;
225 if (list_length == 0)
226 return NULL;
228 array_ptr = array = g_new(gchar *, list_length);
230 /* Fill the array. */
231 for (list_ptr = files_list; list_ptr != NULL; list_ptr = list_ptr->next) {
232 *array_ptr = list_ptr->data;
233 array_ptr++;
236 qsort(array, list_length, sizeof(gchar *), reverse_compar);
238 return array;
241 /*** Initialization ***/
243 void init_list(gchar ** names, gint num, gboolean recursive, gboolean shuffle)
245 for (; num != 0; names++, num--)
246 add_to_list(*names, recursive);
248 if (files_list == NULL) {
249 ERROR_MSG(_("No image found\n"));
250 quit(1);
253 reorder_list(shuffle);
256 /*** Misc. operations. ***/
258 /* To avoid a gcc warning. */
259 G_GNUC_PURE static gint strcmp_compar(gconstpointer a, gconstpointer b)
261 return strcmp(a, b);
264 /* Move backward a node to the head, or to the next image. */
265 void place_next(const gchar * filename)
267 GList *node;
269 node = (current_image == NULL) ? files_list : current_image->node;
272 * With gtk+-1.2, this complains because in the g_list_find_custom prototype
273 * filename is a gchar *, not a const gchar *.
274 * This is not a problem since g_list_find_custom does not change filename.
276 node = g_list_find_custom(node, filename, strcmp_compar);
278 if (node == NULL)
279 return;
281 files_list = g_list_remove_link(files_list, node);
283 if (current_image == NULL) {
284 /* Move to the head. */
285 node->next = files_list;
286 files_list->prev = node;
288 files_list = node;
289 } else {
290 /* Move to the next image. */
291 node->next = current_image->node->next;
292 node->prev = current_image->node;
294 if (node->prev != NULL)
295 node->prev->next = node;
297 if (node->next != NULL)
298 node->next->prev = node;
300 current_image->node->next = node;
304 /* Returns TRUE if something has been inserted. */
305 gboolean insert_after_current(gchar ** names, gint nb, gboolean shuffle)
307 /* Backup the current list. */
308 GList *files_list_old = files_list, *last;
309 gint list_length_old = list_length;
311 /* Reinitialize the current list. */
312 files_list = NULL;
313 list_length = 0;
314 for (; nb != 0; names++, nb--)
315 add_to_list(*names, FALSE);
317 if (files_list == NULL) {
318 /* No files were added. */
319 files_list = files_list_old;
320 list_length = list_length_old;
321 return FALSE;
324 reorder_list(shuffle);
326 if (current_image == NULL)
327 return TRUE;
329 /* Search the last inserted. */
330 for (last = files_list; last->next != NULL; last = last->next);
332 last->next = current_image->node->next;
333 if (last->next)
334 last->next->prev = last;
336 /* Merge lists. */
337 current_image->node->next = files_list;
338 files_list->prev = current_image->node;
340 files_list = files_list_old;
341 list_length += list_length_old;
343 return TRUE;