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>
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)
45 GList
*get_list_head(void)
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
));
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
);
70 static void add_to_list(const gchar
* name
, gboolean recursive
)
73 struct dirent
*dir_ent
;
77 if (stat(name
, &st
) < 0) {
82 if (S_ISDIR(st
.st_mode
)) {
85 /* Traverse recursively the directory. */
86 ftw(name
, ftw_add_to_list
, 32);
89 /* Add every file in the directory. */
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
);
101 if (S_ISDIR(st
.st_mode
) == FALSE
)
102 add_file_to_list(full_path
);
110 add_file_to_list(name
);
113 void remove_from_list(GList
* node
)
116 files_list
= g_list_delete_link(files_list
, node
);
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
) {
148 /* The filenames were equal. */
151 /* Go back to the first different dir. */
152 while (*ptr1
!= '/' || *ptr2
!= '/') {
157 /* Skip the common '/'. */
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
;
191 if (files_list
== NULL
)
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
))
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
;
225 if (list_length
== 0)
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
;
236 qsort(array
, list_length
, sizeof(gchar
*), reverse_compar
);
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"));
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
)
264 /* Move backward a node to the head, or to the next image. */
265 void place_next(const gchar
* filename
)
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
);
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
;
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. */
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
;
324 reorder_list(shuffle
);
326 if (current_image
== NULL
)
329 /* Search the last inserted. */
330 for (last
= files_list
; last
->next
!= NULL
; last
= last
->next
);
332 last
->next
= current_image
->node
->next
;
334 last
->next
->prev
= last
;
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
;