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>
27 #include <string.h> /* strcmp(), strchr() */
28 #include <sys/stat.h> /* stat() */
29 #include <dirent.h> /* DIR, opendir(), readdir(), closedir() */
30 #include <stdlib.h> /* qsort() */
31 #include <stdio.h> /* remove(), perror() */
33 extern gliv_image
*current_image
;
35 static GList
*files_list
= NULL
;
36 static gint list_length
= 0;
38 /* The node to delete when no longer displayed. */
39 static GList
*to_destroy_node
= NULL
;
40 static gint to_destroy_number
;
42 gint
get_list_length(void)
47 GList
*get_list_head(void)
54 static void add_file_to_list(const gchar
* filename
)
56 if (is_loadable(filename
)) {
57 files_list
= g_list_prepend(files_list
, clean_filename(filename
));
62 static void add_to_list(const gchar
* name
, gboolean recursive
)
65 struct dirent
*dir_ent
;
69 if (stat(name
, &st
) < 0) {
74 if (S_ISDIR(st
.st_mode
)) {
77 /* Traverse recursively the directory. */
78 foreach_file(name
, add_file_to_list
);
81 /* Add every file in the directory. */
86 for (dir_ent
= readdir(dir
); dir_ent
!= NULL
;
87 dir_ent
= readdir(dir
)) {
89 full_path
= fast_build_filename(name
, dir_ent
->d_name
);
93 if (S_ISDIR(st
.st_mode
) == FALSE
)
94 add_file_to_list(full_path
);
102 add_file_to_list(name
);
107 void remove_from_list(GList
* node
)
110 files_list
= g_list_delete_link(files_list
, node
);
116 /* To shuffle the list, we simply sort with a random compare func. */
117 static gint
random_compar(gconstpointer unused1
, gconstpointer unused2
)
119 return g_random_int_range(-1, 3);
122 /* We want children to be after their parent, or the alphabetical order. */
123 G_GNUC_PURE
static gint
filename_compar(gconstpointer a
, gconstpointer b
)
125 const gchar
*file1
, *file2
, *ptr1
, *ptr2
;
126 gboolean ptr1_has_dir
, ptr2_has_dir
;
128 ptr1
= file1
= (const gchar
*) a
;
129 ptr2
= file2
= (const gchar
*) b
;
131 if (file1
[0] != file2
[0])
132 /* Comparing an absolute filename, and a relative one. */
133 return file1
[0] == '/' ? -1 : 1;
135 /* Skip identical characters in the paths. */
136 while (*ptr1
!= '\0' && *ptr1
== *ptr2
) {
142 /* The filenames were equal. */
145 /* Go back to the first different dir. */
146 while (*ptr1
!= '/' || *ptr2
!= '/') {
151 /* Skip the common '/'. */
155 ptr1_has_dir
= (strchr(ptr1
, '/') != NULL
);
156 ptr2_has_dir
= (strchr(ptr2
, '/') != NULL
);
158 if (ptr1_has_dir
== ptr2_has_dir
)
160 * Either the files are in the same directory,
161 * or they are not parent.
163 return strcmp(ptr1
, ptr2
);
165 /* One of the directory is parent of the other one. */
166 return ptr1_has_dir
? -1 : 1;
169 static void reorder_list(gboolean shuffle
)
171 GCompareFunc compare_func
;
173 compare_func
= shuffle
? random_compar
: filename_compar
;
174 files_list
= g_list_sort(files_list
, compare_func
);
177 /* Called by the menu. */
178 void reorder_files(gboolean shuffle
)
180 GList
*node
, *current_node
;
183 if (files_list
== NULL
)
186 current_node
= current_image
->node
;
188 reorder_list(shuffle
);
190 /* Search the new id for current_image. */
191 for (node
= files_list
; node
!= NULL
; node
= node
->next
) {
192 if (current_node
== node
)
197 after_reorder(new_id
);
200 G_GNUC_PURE
static gint
reverse_compar(gconstpointer a
, gconstpointer b
)
202 return -filename_compar(*((const gchar
**) a
), *((const gchar
**) b
));
206 * Used to build the images menus.
207 * The logic would be to sort the array, and then build the menus
208 * with calls to gtk_menu_shell_append().
209 * But as the latter appends to a list by searching the end each time,
210 * we reverse sort the array and then insert in the list head.
212 gchar
**get_sorted_files_array(void)
214 gchar
**array
, **array_ptr
;
217 if (list_length
== 0)
220 array_ptr
= array
= g_new(gchar
*, list_length
);
222 /* Fill the array. */
223 for (list_ptr
= files_list
; list_ptr
!= NULL
; list_ptr
= list_ptr
->next
) {
224 *array_ptr
= list_ptr
->data
;
228 qsort(array
, list_length
, sizeof(gchar
*), reverse_compar
);
233 /*** Initialization ***/
235 void init_list(gchar
** names
, gint num
, gboolean recursive
,
236 gboolean shuffle
, gboolean sort
)
238 for (; num
!= 0; names
++, num
--)
239 add_to_list(*names
, recursive
);
241 if (files_list
== NULL
) {
242 ERROR_MSG(_("No image found\n"));
247 reorder_list(shuffle
);
249 /* We inserted in the head. */
250 files_list
= g_list_reverse(files_list
);
253 /*** Misc. operations. ***/
255 /* To avoid a gcc warning. */
256 G_GNUC_PURE
static gint
strcmp_compar(gconstpointer a
, gconstpointer b
)
261 /* Move backward a node to the head, or to the next image. */
262 void place_next(const gchar
* filename
)
266 node
= (current_image
== NULL
) ? files_list
: current_image
->node
->next
;
267 node
= g_list_find_custom(node
, filename
, strcmp_compar
);
269 files_list
= g_list_remove_link(files_list
, node
);
271 if (current_image
== NULL
) {
272 /* Move to the head. */
273 node
->next
= files_list
;
275 files_list
->prev
= node
;
279 /* Move to the next image. */
280 node
->prev
= current_image
->node
;
281 node
->next
= current_image
->node
->next
;
283 if (node
->prev
!= NULL
)
284 node
->prev
->next
= node
;
286 if (node
->next
!= NULL
)
287 node
->next
->prev
= node
;
291 /* Returns TRUE if something has been inserted. */
292 gboolean
insert_after_current(gchar
** names
, gint nb
, gboolean shuffle
)
294 /* Backup the current list. */
295 GList
*files_list_old
= files_list
, *last
;
296 gint list_length_old
= list_length
;
298 /* Reinitialize the current list. */
301 for (; nb
!= 0; names
++, nb
--)
302 add_to_list(*names
, FALSE
);
304 if (files_list
== NULL
) {
305 /* No files were added. */
306 files_list
= files_list_old
;
307 list_length
= list_length_old
;
311 reorder_list(shuffle
);
313 if (current_image
== NULL
)
316 /* Search the last inserted. */
317 for (last
= files_list
; last
->next
!= NULL
; last
= last
->next
);
319 /* Merge the insertion end. */
320 last
->next
= current_image
->node
->next
;
322 last
->next
->prev
= last
;
324 /* Merge the insertion beginning. */
325 current_image
->node
->next
= files_list
;
326 files_list
->prev
= current_image
->node
;
328 files_list
= files_list_old
;
329 list_length
+= list_length_old
;
334 static gboolean
show_remove_dialog(const gchar
* title
, GtkLabel
* label
)
339 dialog
= GTK_DIALOG(gtk_dialog_new_with_buttons
340 (title
, get_current_window(),
341 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
342 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
343 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
, NULL
));
345 gtk_container_add(GTK_CONTAINER(dialog
->vbox
), GTK_WIDGET(label
));
346 gtk_widget_show_all(GTK_WIDGET(label
));
348 res
= gtk_dialog_run(dialog
);
350 gtk_widget_destroy(GTK_WIDGET(dialog
));
352 return res
== GTK_RESPONSE_ACCEPT
|| res
== GTK_RESPONSE_OK
||
353 res
== GTK_RESPONSE_YES
;
356 void confirm_remove_current(void)
358 gchar
*filename
, *msg
;
361 if (current_image
== NULL
)
364 filename
= current_image
->node
->data
;
365 msg
= g_strdup_printf(_("Do you really want to delete this file?\n%s\n"),
366 locale_to_utf8(filename
));
368 label
= GTK_LABEL(gtk_label_new(msg
));
370 if (show_remove_dialog(locale_to_utf8(filename
), label
)) {
371 to_destroy_node
= current_image
->node
;
372 to_destroy_number
= current_image
->number
;
374 if (remove(filename
) < 0)
381 /* Called when the displayed image is changed. */
382 void delete_selected_image(void)
384 if (to_destroy_node
== NULL
)
387 unload(to_destroy_node
);
389 if (current_image
->number
> to_destroy_number
)
390 current_image
->number
--;
392 remove_from_list(to_destroy_node
);
394 to_destroy_node
= NULL
;