0.8.0
[geeqie.git] / src / utilops.c
1 /*
2  * GQview image viewer
3  * (C)2000 John Ellis
4  *
5  * Author: John Ellis
6  *
7  */
8
9 #include "gqview.h"
10
11 enum {
12         DIALOG_NEW_DIR,
13         DIALOG_COPY,
14         DIALOG_MOVE,
15         DIALOG_DELETE,
16         DIALOG_RENAME
17 };
18
19 typedef struct _FileDataMult FileDataMult;
20 struct _FileDataMult
21 {
22         gint confirm_all;
23         gint confirmed;
24         gint skip;
25         GList *source_list;
26         GList *source_next;
27         gchar *dest_base;
28         gchar *source;
29         gchar *dest;
30         gint copy;
31 };
32
33 typedef struct _FileDataSingle FileDataSingle;
34 struct _FileDataSingle
35 {
36         gint confirmed;
37         gchar *source;
38         gchar *dest;
39         gint copy;
40 };
41
42 static FileDataMult *file_data_multiple_new(GList *source_list, gchar *dest, gint copy);
43 static void file_data_multiple_free(FileDataMult *fdm);
44 static void file_util_move_multiple(FileDataMult *fdm);
45 static void file_util_move_multiple_ok_cb(GtkWidget *widget, gpointer data);
46 static void file_util_move_multiple_all_cb(GtkWidget *widget, gpointer data);
47 static void file_util_move_multiple_skip_cb(GtkWidget *widget, gpointer data);
48 static void file_util_move_multiple_cancel_cb(GtkWidget *widget, gpointer data);
49 static void file_util_move_multiple(FileDataMult *fdm);
50
51 static FileDataSingle *file_data_single_new(gchar *source, gchar *dest, gint copy);
52 static void file_data_single_free(FileDataSingle *fds);
53 static void file_util_move_single_ok_cb(GtkWidget *widget, gpointer data);
54 static void file_util_move_single(FileDataSingle *fds);
55 static void file_util_move_single_cancel_cb(GtkWidget *widget, gpointer data);
56 static void file_util_move_do(FileDialog *fd);
57 static void file_util_move_check(FileDialog *fd);
58 static void file_util_move_cb(GtkWidget *widget, gpointer data);
59 static void file_util_move_enter_cb(gchar *path, gpointer data);
60 static void file_util_move_completion_sync_cb(gchar *path, gpointer data);
61 static void real_file_util_move(gchar *source_path, GList *source_list, gchar *dest_path, gint copy);
62
63 static void file_util_delete_multiple_ok_cb(GtkWidget *w, gpointer data);
64 static void file_util_delete_multiple_cancel_cb(GtkWidget *w, gpointer data);
65 static void file_util_delete_multiple(GList *source_list);
66 static void file_util_delete_ok_cb(GtkWidget *w, gpointer data);
67 static void file_util_delete_cancel_cb(GtkWidget *w, gpointer data);
68 static void file_util_delete_single(gchar *path);
69
70 static void file_util_rename_multiple_ok_cb(GtkWidget *w, gpointer data);
71 static void file_util_rename_multiple_cancel_cb(GtkWidget *w, gpointer data);
72 static void file_util_rename_multiple(FileDialog *fd);
73 static void file_util_rename_multiple_cb(GtkWidget *w, gpointer data);
74 static void file_util_rename_multiple_select_cb(GtkWidget *clist,
75                 gint row, gint column, GdkEventButton *bevent, gpointer data);
76 static void file_util_rename_multiple_do(GList *source_list);
77
78 static void file_util_rename_single_ok_cb(GtkWidget *w, gpointer data);
79 static void file_util_rename_single_cancel_cb(GtkWidget *w, gpointer data);
80 static void file_util_rename_single(FileDataSingle *fds);
81 static void file_util_rename_single_cb(GtkWidget *w, gpointer data);
82 static void file_util_rename_single_do(gchar *source_path);
83
84 static void file_util_create_dir_do(gchar *source, gchar *path);
85 static void file_util_create_dir_cb(GtkWidget *w, gpointer data);
86
87 /*
88  *--------------------------------------------------------------------------
89  * Move and Copy routines
90  *--------------------------------------------------------------------------
91  */
92
93 /*
94  * Multi file move
95  */
96
97 static FileDataMult *file_data_multiple_new(GList *source_list, gchar *dest, gint copy)
98 {
99         FileDataMult *fdm = g_new0(FileDataMult, 1);
100         fdm->confirm_all = FALSE;
101         fdm->confirmed = FALSE;
102         fdm->skip = FALSE;
103         fdm->source_list = source_list;
104         fdm->source_next = fdm->source_list;
105         fdm->dest_base = g_strdup(dest);
106         fdm->source = NULL;
107         fdm->dest = NULL;
108         fdm->copy = copy;
109         return fdm;
110 }
111
112 static void file_data_multiple_free(FileDataMult *fdm)
113 {
114         free_selected_list(fdm->source_list);
115         g_free(fdm->dest_base);
116         g_free(fdm->dest);
117         g_free(fdm);
118 }
119
120 static void file_util_move_multiple_ok_cb(GtkWidget *widget, gpointer data)
121 {
122         FileDataMult *fdm = data;
123         fdm->confirmed = TRUE;
124         file_util_move_multiple(fdm);
125 }
126
127 static void file_util_move_multiple_all_cb(GtkWidget *widget, gpointer data)
128 {
129         FileDataMult *fdm = data;
130         fdm->confirm_all = TRUE;
131         file_util_move_multiple(fdm);
132 }
133
134 static void file_util_move_multiple_skip_cb(GtkWidget *widget, gpointer data)
135 {
136         FileDataMult *fdm = data;
137         fdm->skip = TRUE;
138         file_util_move_multiple(fdm);
139 }
140
141 static void file_util_move_multiple_cancel_cb(GtkWidget *widget, gpointer data)
142 {
143         FileDataMult *fdm = data;
144         file_data_multiple_free(fdm);
145 }
146
147 static void file_util_move_multiple(FileDataMult *fdm)
148 {
149         while (fdm->dest || fdm->source_next)
150                 {
151                 if (!fdm->dest)
152                         {
153                         GList *work = fdm->source_next;
154                         fdm->source = work->data;
155                         fdm->dest = g_strconcat(fdm->dest_base, "/", filename_from_path(fdm->source), NULL);
156                         fdm->source_next = work->next;
157                         }
158
159                 if (fdm->dest && fdm->source && strcmp(fdm->dest, fdm->source) == 0)
160                         {
161                         ConfirmDialog *cd;
162                         gchar *title;
163                         gchar *text;
164                         if (fdm->copy)
165                                 {
166                                 title = _("Source to copy matches destination");
167                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto itself."), fdm->dest);
168                                 }
169                         else
170                                 {
171                                 title = _("Source to move matches destination");
172                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto itself."), fdm->dest);
173                                 }
174                         cd = confirm_dialog_new(title, text, file_util_move_multiple_cancel_cb, fdm);
175                         confirm_dialog_add(cd, _("Continue"), file_util_move_multiple_skip_cb);
176                         g_free(text);
177                         return;
178                         }
179                 else if (isfile(fdm->dest) && !fdm->confirmed && !fdm->confirm_all && !fdm->skip)
180                         {
181                         ConfirmDialog *cd;
182                         gchar *text = g_strdup_printf(_("Overwrite file:\n %s\n with:\b %s"), fdm->dest, fdm->source);
183                         cd = confirm_dialog_new_with_image(_("Overwrite file"), text,
184                                                 fdm->dest, fdm->source,
185                                                 file_util_move_multiple_cancel_cb, fdm);
186                         confirm_dialog_add(cd, _("Skip"), file_util_move_multiple_skip_cb);
187                         confirm_dialog_add(cd, _("Yes to all"), file_util_move_multiple_all_cb);
188                         confirm_dialog_add(cd, _("Yes"), file_util_move_multiple_ok_cb);
189                         g_free(text);
190                         return;
191                         }
192                 else
193                         {
194                         gint success = FALSE;
195                         if (fdm->skip)
196                                 {
197                                 success = TRUE;
198                                 fdm->skip = FALSE;
199                                 }
200                         else
201                                 {
202                                 if (fdm->copy)
203                                         {
204                                         success = copy_file(fdm->source, fdm->dest);
205                                         }
206                                 else
207                                         {
208                                         if (move_file(fdm->source, fdm->dest))
209                                                 {
210                                                 success = TRUE;
211                                                 file_is_gone(fdm->source, fdm->source_list);
212                                                 }
213                                         }
214                                 }
215                         if (!success)
216                                 {
217                                 ConfirmDialog *cd;
218                                 gchar *title;
219                                 gchar *text;
220                                 if (fdm->copy)
221                                         {
222                                         title = _("Error copying file");
223                                         text = g_strdup_printf(_("Unable to copy file:\n%sto:\n%s\n during multiple file copy."), fdm->source, fdm->dest);
224                                         }
225                                 else
226                                         {
227                                         title = _("Error moving file");
228                                         text = g_strdup_printf(_("Unable to move file:\n%sto:\n%s\n during multiple file move."), fdm->source, fdm->dest);
229                                         }
230                                 cd = confirm_dialog_new(title, text, file_util_move_multiple_cancel_cb, fdm);
231                                 confirm_dialog_add(cd, _("Continue"), file_util_move_multiple_skip_cb);
232                                 g_free(text);
233                                 return;
234                                 }
235                         fdm->confirmed = FALSE;
236                         g_free(fdm->dest);
237                         fdm->dest = NULL;
238                         }
239                 }
240
241         file_data_multiple_free(fdm);
242 }
243
244 /*
245  * Single file move
246  */
247
248 static FileDataSingle *file_data_single_new(gchar *source, gchar *dest, gint copy)
249 {
250         FileDataSingle *fds = g_new0(FileDataSingle, 1);
251         fds->confirmed = FALSE;
252         fds->source = g_strdup(source);
253         fds->dest = g_strdup(dest);
254         fds->copy = copy;
255         return fds;
256 }
257
258 static void file_data_single_free(FileDataSingle *fds)
259 {
260         g_free(fds->source);
261         g_free(fds->dest);
262         g_free(fds);
263 }
264
265 static void file_util_move_single_ok_cb(GtkWidget *widget, gpointer data)
266 {
267         FileDataSingle *fds = data;
268         fds->confirmed = TRUE;
269         file_util_move_single(fds);
270 }
271
272 static void file_util_move_single_cancel_cb(GtkWidget *widget, gpointer data)
273 {
274         FileDataSingle *fds = data;
275         file_data_single_free(fds);
276 }
277
278 static void file_util_move_single(FileDataSingle *fds)
279 {
280         if (fds->dest && fds->source && strcmp(fds->dest, fds->source) == 0)
281                 {
282                 warning_dialog(_("Source matches destination"),
283                                _("Source and destination are the same, operation cancelled."));
284                 }
285         else if (isfile(fds->dest) && !fds->confirmed)
286                 {
287                 ConfirmDialog *cd;
288                 gchar *text = g_strdup_printf(_("Overwrite file:\n%s\n with:\n%s"), fds->dest, fds->source);
289                 cd = confirm_dialog_new_with_image(_("Overwrite file"), text,
290                                                    fds->dest, fds->source,
291                                                    file_util_move_single_cancel_cb, fds);
292                 confirm_dialog_add(cd, _("Overwrite"), file_util_move_single_ok_cb);
293                 g_free(text);
294                 return;
295                 }
296         else
297                 {
298                 gint success = FALSE;
299                 if (fds->copy)
300                         {
301                         success = copy_file(fds->source, fds->dest);
302                         }
303                 else
304                         {
305                         if (move_file(fds->source, fds->dest))
306                                 {
307                                 success = TRUE;
308                                 file_is_gone(fds->source, NULL);
309                                 }
310                         }
311                 if (!success)
312                         {
313                         gchar *title;
314                         gchar *text;
315                         if (fds->copy)
316                                 {
317                                 title = _("Error copying file");
318                                 text = g_strdup_printf(_("Unable to copy file:\n%s\nto:\n%s"), fds->source, fds->dest);
319                                 }
320                         else
321                                 {
322                                 title = _("Error moving file");
323                                 text = g_strdup_printf(_("Unable to move file:\n%s\nto:\n%s"), fds->source, fds->dest);
324                                 }
325                         warning_dialog(title, text);
326                         g_free(text);
327                         }
328                 }
329
330         file_data_single_free(fds);
331 }
332
333 /*
334  * file move dialog
335  */
336
337 static void file_util_move_do(FileDialog *fd)
338 {
339         tab_completion_append_to_history(fd->entry, fd->dest_path);
340         if (fd->multiple_files)
341                 {
342                 file_util_move_multiple(file_data_multiple_new(fd->source_list, fd->dest_path, fd->type));
343                 fd->source_list = NULL;
344                 }
345         else
346                 {
347                 if (isdir(fd->dest_path))
348                         {
349                         gchar *buf = g_strconcat(fd->dest_path, "/", filename_from_path(fd->source_path), NULL);
350                         g_free(fd->dest_path);
351                         fd->dest_path = buf;
352                         }
353                 file_util_move_single(file_data_single_new(fd->source_path, fd->dest_path, fd->type));
354                 }
355
356         generic_dialog_close(NULL, fd);
357 }
358
359 static void file_util_move_check(FileDialog *fd)
360 {
361         g_free(fd->dest_path);
362         fd->dest_path = remove_trailing_slash(gtk_entry_get_text(GTK_ENTRY(fd->entry)));
363
364         if (fd->multiple_files && !isdir(fd->dest_path))
365                 {
366                 if (isfile(fd->dest_path))
367                         warning_dialog(_("Invalid destination"), _("When operating with multiple files, please select\n a directory, not file."));
368                 else
369                         warning_dialog(_("Invalid directory"), _("Please select an existing directory"));
370                 return;
371                 }
372
373         file_util_move_do(fd);
374 }
375
376 static void file_util_move_cb(GtkWidget *widget, gpointer data)
377 {
378         FileDialog *fd = data;
379         file_util_move_check(fd);
380 }
381
382 static void file_util_move_enter_cb(gchar *path, gpointer data)
383 {
384         FileDialog *fd = data;
385         file_util_move_check(fd);
386 }
387
388 static void file_util_move_completion_sync_cb(gchar *path, gpointer data)
389 {
390         FileDialog *fd = data;
391         destination_widget_sync_to_entry(fd->entry);
392 }
393
394 static void real_file_util_move(gchar *source_path, GList *source_list, gchar *dest_path, gint copy)
395 {
396         FileDialog *fd;
397         gchar *path = NULL;
398         gint multiple;
399         gchar *text;
400         gchar *title;
401         gchar *op_text;
402         GtkWidget *tabcomp;
403         GtkWidget *dest;
404         gchar *last_path;
405
406         if (!source_path && !source_list) return;
407
408         if (source_path)
409                 {
410                 path = g_strdup(source_path);
411                 multiple = FALSE;
412                 }
413         else if (source_list->next)
414                 {
415                 multiple = TRUE;
416                 }
417         else
418                 {
419                 path = g_strdup(source_list->data);
420                 free_selected_list(source_list);
421                 source_list = NULL;
422                 multiple = FALSE;
423                 }
424
425         if (copy)
426                 {
427                 title = _("GQview - copy");
428                 op_text = _("Copy");
429                 if (path)
430                         text = g_strdup_printf(_("Copy file:\n%s\nto:"), path);
431                 else
432                         text = g_strdup_printf(_("Copy multiple files from:\n%s\nto:"), dest_path);
433                 }
434         else
435                 {
436                 title = _("GQview - move");
437                 op_text = _("Move");
438                 if (path)
439                         text = g_strdup_printf(_("Move file:\n%s\nto:"), path);
440                 else
441                         text = g_strdup_printf(_("Move multiple files from:\n%s\nto:"), dest_path);
442                 }
443
444         fd = generic_dialog_new(title, text, op_text, _("Cancel"),
445                 file_util_move_cb, generic_dialog_close);
446
447         g_free(text);
448
449         fd->type = copy;
450         fd->source_path = path;
451         fd->source_list = source_list;
452         fd->multiple_files = multiple;
453
454         tabcomp = tab_completion_new_with_history(&fd->entry, fd->dialog, dest_path,
455                                            "move_copy", 32, file_util_move_enter_cb, fd);
456         last_path = tab_completion_set_to_last_history(fd->entry);
457         if (last_path)
458                 {
459                 fd->dest_path = g_strdup(last_path);
460                 }
461         else
462                 {
463                 fd->dest_path = g_strdup(dest_path);
464                 }
465                                            
466 /*      tabcomp = tab_completion_new(&fd->entry, fd->dialog, fd->dest_path, file_util_move_enter_cb, fd);
467 */
468         gtk_box_pack_start(GTK_BOX(fd->vbox), tabcomp, FALSE, FALSE, 0);
469         gtk_widget_show(tabcomp);
470
471         gtk_widget_grab_focus(fd->entry);
472
473         dest = destination_widget_new(fd->dest_path, fd->entry);
474
475         tab_completion_add_tab_func(fd->entry, file_util_move_completion_sync_cb, fd);
476
477         gtk_box_pack_start(GTK_BOX(fd->vbox), dest, TRUE, TRUE, 0);
478 }
479
480 void file_util_move(gchar *source_path, GList *source_list, gchar *dest_path)
481 {
482         real_file_util_move(source_path, source_list, dest_path, FALSE);
483 }
484
485 void file_util_copy(gchar *source_path, GList *source_list, gchar *dest_path)
486 {
487         real_file_util_move(source_path, source_list, dest_path, TRUE);
488 }
489
490 /*
491  *--------------------------------------------------------------------------
492  * Delete routines
493  *--------------------------------------------------------------------------
494  */
495
496 /*
497  * delete multiple files
498  */
499
500 static void file_util_delete_multiple_ok_cb(GtkWidget *w, gpointer data)
501 {
502         GList *source_list = data;
503
504         while(source_list)
505                 {
506                 gchar *path = source_list->data;
507                 source_list = g_list_remove(source_list, path);
508                 if (unlink (path) < 0)
509                         {
510                         ConfirmDialog *cd;
511                         gchar *text;
512                         if (source_list)
513                                 {
514                                 text = g_strdup_printf(_("Unable to delete file:\n %s\n Continue multiple delete operation?"), path);
515                                 cd = confirm_dialog_new(_("Delete failed"), text, file_util_delete_multiple_cancel_cb, source_list);
516                                 confirm_dialog_add(cd, _("Continue"), file_util_delete_multiple_ok_cb);
517                                 }
518                         else
519                                 {
520                                 text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
521                                 warning_dialog(_("Delete failed"), text);
522                                 }
523                         g_free(text);
524                         g_free(path);
525                         return;
526                         }
527                 else
528                         {
529                         file_is_gone(path, source_list);
530                         }
531                 g_free(path);
532                 }
533 }
534
535 static void file_util_delete_multiple_cancel_cb(GtkWidget *w, gpointer data)
536 {
537         GList *source_list = data;
538         free_selected_list(source_list);
539 }
540
541 static void file_util_delete_multiple(GList *source_list)
542 {
543         if (!confirm_delete)
544                 {
545                 file_util_delete_multiple_ok_cb(NULL, source_list);
546                 }
547         else
548                 {
549                 ConfirmDialog *cd;
550                 cd = confirm_dialog_new(_("Delete files"), _("About to delete multiple files..."), file_util_delete_multiple_cancel_cb, source_list);
551                 confirm_dialog_add(cd, _("Delete"), file_util_delete_multiple_ok_cb);
552                 }
553 }
554
555 /*
556  * delete single file
557  */
558
559 static void file_util_delete_ok_cb(GtkWidget *w, gpointer data)
560 {
561         gchar *path = data;
562
563         if (unlink (path) < 0)
564                 {
565                 gchar *text = g_strdup_printf(_("Unable to delete file:\n%s"), path);
566                 warning_dialog(_("File deletion failed"), text);
567                 g_free(text);
568                 }
569         else
570                 {
571                 file_is_gone(path, NULL);
572                 }
573
574         g_free(path);
575 }
576
577 static void file_util_delete_cancel_cb(GtkWidget *w, gpointer data)
578 {
579         gchar *path = data;
580         g_free(path);
581 }
582
583 static void file_util_delete_single(gchar *path)
584 {
585         gchar *buf = g_strdup(path);
586
587         if (!confirm_delete)
588                 {
589                 file_util_delete_ok_cb(NULL, buf);
590                 }
591         else
592                 {
593                 ConfirmDialog *cd;
594                 gchar *text = g_strdup_printf(_("About to delete the file:\n %s"), buf);
595                 cd = confirm_dialog_new(_("Delete file"), text, file_util_delete_cancel_cb, buf);
596                 confirm_dialog_add(cd, _("Delete"), file_util_delete_ok_cb);
597                 g_free(text);
598                 }
599 }
600
601 void file_util_delete(gchar *source_path, GList *source_list)
602 {
603         if (!source_path && !source_list) return;
604
605         if (source_path)
606                 {
607                 file_util_delete_single(source_path);
608                 }
609         else if (!source_list->next)
610                 {
611                 file_util_delete_single(source_list->data);
612                 free_selected_list(source_list);
613                 }
614         else
615                 {
616                 file_util_delete_multiple(source_list);
617                 }
618 }
619
620 /*
621  *--------------------------------------------------------------------------
622  * Rename routines
623  *--------------------------------------------------------------------------
624  */
625
626 /*
627  * rename multiple files
628  */
629
630 static void file_util_rename_multiple_ok_cb(GtkWidget *w, gpointer data)
631 {
632         FileDialog *fd = data;
633         if (!GTK_WIDGET_VISIBLE(fd->dialog)) gtk_widget_show(fd->dialog);
634         fd->type = TRUE;
635         file_util_rename_multiple(fd);
636 }
637
638 static void file_util_rename_multiple_cancel_cb(GtkWidget *w, gpointer data)
639 {
640         FileDialog *fd = data;
641         if (!GTK_WIDGET_VISIBLE(fd->dialog)) gtk_widget_show(fd->dialog);
642         return;
643 }
644
645 static void file_util_rename_multiple(FileDialog *fd)
646 {
647         if (isfile(fd->dest_path) && !fd->type)
648                 {
649                 ConfirmDialog *cd;
650                 gchar *text = g_strdup_printf(_("Overwrite file:\n%s\nby renaming:\n%s"), fd->dest_path, fd->source_path);
651                 cd = confirm_dialog_new_with_image(_("Overwrite file"), text,
652                                                    fd->dest_path, fd->source_path,
653                                                    file_util_rename_multiple_cancel_cb, fd);
654                 confirm_dialog_add(cd, _("Overwrite"), file_util_rename_multiple_ok_cb);
655                 g_free(text);
656                 gtk_widget_hide(fd->dialog);
657                 return;
658                 }
659         else
660                 {
661                 if (rename (fd->source_path, fd->dest_path) < 0)
662                         {
663                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\n to:\n%s"), filename_from_path(fd->source_path), filename_from_path(fd->dest_path));
664                         warning_dialog(_("Error renaming file"), text);
665                         g_free(text);
666                         }
667                 else
668                         {
669                         gint row;
670                         gint n;
671                         GtkWidget *clist;
672                         gchar *path;
673
674                         file_is_renamed(fd->source_path, fd->dest_path);
675
676                         clist = gtk_object_get_user_data(GTK_OBJECT(fd->entry));
677                         path = gtk_object_get_user_data(GTK_OBJECT(clist));
678                         row = gtk_clist_find_row_from_data(GTK_CLIST(clist), path);
679
680                         n = g_list_length(GTK_CLIST(clist)->row_list);
681                         if (debug) printf("r=%d n=%d\n", row, n);
682                         if (n - 1 > row)
683                                 n = row;
684                         else if (n > 1)
685                                 n = row - 1;
686                         else
687                                 n = -1;
688
689                         if (n >= 0)
690                                 {
691                                 gtk_object_set_user_data(GTK_OBJECT(clist), NULL);
692                                 gtk_clist_remove(GTK_CLIST(clist), row);
693                                 gtk_clist_select_row(GTK_CLIST(clist), n, -1);
694                                 }
695                         else
696                                 {
697                                 if (debug) printf("closed by #%d\n", n);
698                                 generic_dialog_close(NULL, fd);
699                                 }
700                         }
701                 }
702 }
703
704 static void file_util_rename_multiple_cb(GtkWidget *w, gpointer data)
705 {
706         FileDialog *fd = data;
707         gchar *base;
708         gchar *name;
709
710         name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
711         base = remove_level_from_path(fd->source_path);
712         g_free(fd->dest_path);
713         fd->dest_path = g_strconcat(base, "/", name, NULL);
714         g_free(base);
715
716         if (strlen(name) == 0 || strcmp(fd->source_path, fd->dest_path) == 0)
717                 {
718                 return;
719                 }
720
721         fd->type = FALSE;
722         file_util_rename_multiple(fd);
723 }
724
725 static void file_util_rename_multiple_select_cb(GtkWidget *clist,
726                 gint row, gint column, GdkEventButton *bevent, gpointer data)
727 {
728         FileDialog *fd = data;
729         GtkWidget *label;
730         gchar *name;
731         gchar *path;
732
733         label = gtk_object_get_user_data(GTK_OBJECT(fd->dialog));
734         path = gtk_clist_get_row_data(GTK_CLIST(clist), row);
735         g_free(fd->source_path);
736         fd->source_path = g_strdup(path);
737         gtk_object_set_user_data(GTK_OBJECT(clist), path);
738         name = filename_from_path(fd->source_path);
739
740         gtk_label_set(GTK_LABEL(label), name);
741         gtk_entry_set_text(GTK_ENTRY(fd->entry), name);
742
743         gtk_widget_grab_focus(fd->entry);
744 }
745
746 static void file_util_rename_multiple_do(GList *source_list)
747 {
748         FileDialog *fd;
749         GtkWidget *scrolled;
750         GtkWidget *clist;
751         GtkWidget *label;
752         GList *work;
753
754         fd = generic_dialog_new(_("GQview - rename"), _("Rename multiple files:"), _("Rename"), _("Cancel"),
755                 file_util_rename_multiple_cb, generic_dialog_close);
756
757         fd->source_path = g_strdup(source_list->data);
758         fd->dest_path = NULL;
759
760         scrolled = gtk_scrolled_window_new(NULL, NULL);
761         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
762                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
763         gtk_box_pack_start(GTK_BOX(fd->vbox), scrolled, TRUE, TRUE, 0);
764         gtk_widget_show(scrolled);
765
766         clist=gtk_clist_new (1);
767         gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 0, TRUE);
768         gtk_signal_connect (GTK_OBJECT (clist), "select_row",(GtkSignalFunc) file_util_rename_multiple_select_cb, fd);
769         gtk_widget_set_usize(clist, 250, 150);
770         gtk_container_add (GTK_CONTAINER (scrolled), clist);
771         gtk_widget_show (clist);
772
773         gtk_object_set_user_data(GTK_OBJECT(clist), source_list->data);
774
775         work = source_list;
776         while(work)
777                 {
778                 gint row;
779                 gchar *buf[2];
780                 buf[0] = filename_from_path(work->data);
781                 buf[1] = NULL;
782                 row = gtk_clist_append(GTK_CLIST(clist), buf);
783                 gtk_clist_set_row_data_full(GTK_CLIST(clist), row,
784                                 work->data, (GtkDestroyNotify) g_free);
785                 work = work->next;
786                 }
787
788         g_list_free(source_list);
789
790         label = gtk_label_new(_("Rename:"));
791         gtk_box_pack_start(GTK_BOX(fd->vbox), label, FALSE, FALSE, 0);
792         gtk_widget_show(label);
793
794         label = gtk_label_new(filename_from_path(fd->source_path));
795         gtk_box_pack_start(GTK_BOX(fd->vbox), label, FALSE, FALSE, 0);
796         gtk_widget_show(label);
797         gtk_object_set_user_data(GTK_OBJECT(fd->dialog), label);
798
799         label = gtk_label_new(_("to:"));
800         gtk_box_pack_start(GTK_BOX(fd->vbox), label, FALSE, FALSE, 0);
801         gtk_widget_show(label);
802
803         fd->entry = gtk_entry_new();
804         gtk_entry_set_text(GTK_ENTRY(fd->entry), filename_from_path(fd->source_path));
805         gtk_box_pack_start(GTK_BOX(fd->vbox), fd->entry, FALSE, FALSE, 0);
806         gtk_widget_grab_focus(fd->entry);
807         gtk_widget_show(fd->entry);
808
809         gtk_object_set_user_data(GTK_OBJECT(fd->entry), clist);
810 }
811
812 /*
813  * rename single file
814  */
815
816 static void file_util_rename_single_ok_cb(GtkWidget *w, gpointer data)
817 {
818         FileDataSingle *fds = data;
819         fds->confirmed = TRUE;
820         file_util_rename_single(fds);
821 }
822
823 static void file_util_rename_single_cancel_cb(GtkWidget *w, gpointer data)
824 {
825         FileDataSingle *fds = data;
826         file_data_single_free(fds);
827 }
828
829 static void file_util_rename_single(FileDataSingle *fds)
830 {
831         if (isfile(fds->dest) && !fds->confirmed)
832                 {
833                 ConfirmDialog *cd;
834                 gchar *text = g_strdup_printf(_("Overwrite file:\n%s\nby renaming:\n%s"), fds->dest,fds->source);
835                 cd = confirm_dialog_new_with_image(_("Overwrite file"), text,
836                                                    fds->dest, fds->source,
837                                                    file_util_rename_single_cancel_cb, fds);
838                 confirm_dialog_add(cd, _("Overwrite"), file_util_rename_single_ok_cb);
839                 g_free(text);
840                 return;
841                 }
842         else
843                 {
844                 if (rename (fds->source, fds->dest) < 0)
845                         {
846                         gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), filename_from_path(fds->source), filename_from_path(fds->dest));
847                         warning_dialog(_("Error renaming file"), text);
848                         g_free(text);
849                         }
850                 else
851                         {
852                         file_is_renamed(fds->source, fds->dest);
853                         }
854                 }
855         file_data_single_free(fds);
856 }
857
858 static void file_util_rename_single_cb(GtkWidget *w, gpointer data)
859 {
860         FileDialog *fd = data;
861         gchar *name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
862         gchar *buf = g_strconcat(fd->dest_path, "/", name, NULL);
863
864         if (strlen(name) == 0 || strcmp(fd->source_path, buf) == 0)
865                 {
866                 g_free(buf);
867                 return;
868                 }
869
870         g_free(fd->dest_path);
871         fd->dest_path = buf;
872
873         file_util_rename_single(file_data_single_new(fd->source_path, fd->dest_path, fd->type));
874
875         generic_dialog_close(NULL, fd);
876 }
877
878 static void file_util_rename_single_do(gchar *source_path)
879 {
880         FileDialog *fd;
881         gchar *text;
882         gchar *name = filename_from_path(source_path);
883
884         text = g_strdup_printf(_("Rename file:\n%s\nto:"), name);
885         fd = generic_dialog_new(_("GQview - rename"), text, _("Rename"), _("Cancel"),
886                 file_util_rename_single_cb, generic_dialog_close);
887         g_free(text);
888
889         fd->source_path = g_strdup(source_path);
890         fd->dest_path = remove_level_from_path(source_path);
891
892         fd->entry = gtk_entry_new();
893         gtk_entry_set_text(GTK_ENTRY(fd->entry), name);
894         gtk_box_pack_start(GTK_BOX(fd->vbox), fd->entry, FALSE, FALSE, 0);
895         gtk_widget_grab_focus(fd->entry);
896         gtk_widget_show(fd->entry);
897 }
898
899 void file_util_rename(gchar *source_path, GList *source_list)
900 {
901         if (!source_path && !source_list) return;
902
903         if (source_path)
904                 {
905                 file_util_rename_single_do(source_path);
906                 }
907         else if (!source_list->next)
908                 {
909                 file_util_rename_single_do(source_list->data);
910                 free_selected_list(source_list);
911                 }
912         else
913                 {
914                 file_util_rename_multiple_do(source_list);
915                 }
916 }
917
918 /*
919  *--------------------------------------------------------------------------
920  * Create directory routines
921  *--------------------------------------------------------------------------
922  */
923
924 static void file_util_create_dir_do(gchar *source, gchar *path)
925 {
926         if (isfile(path))
927                 {
928                 gchar *text = g_strdup_printf(_("The path:\n%s\nalready exists as a file."), filename_from_path(path));
929                 warning_dialog(_("Could not create directory"), text);
930                 g_free(text);
931                 }
932         else if (isdir(path))
933                 {
934                 gchar *text = g_strdup_printf(_("The directory:\n%s\nalready exists."), filename_from_path(path));
935                 warning_dialog(_("Directory exists"), text);
936                 g_free(text);
937                 }
938         else
939                 {
940                 if (mkdir (path, 0755) < 0)
941                         {
942                         gchar *text = g_strdup_printf(_("Unable to create directory:\n%s"), filename_from_path(path));
943                         warning_dialog(_("Error creating directory"), text);
944                         g_free(text);
945                         }
946                 else
947                         {
948                         if (strcmp(source, current_path) == 0)
949                                 {
950                                 gchar *buf = g_strdup(current_path);
951                                 filelist_change_to(buf);
952                                 g_free(buf);
953                                 }
954                         }
955                 }
956 }
957
958 static void file_util_create_dir_cb(GtkWidget *w, gpointer data)
959 {
960         FileDialog *fd = data;
961         gchar *name = gtk_entry_get_text(GTK_ENTRY(fd->entry));
962
963         if (strlen(name) == 0) return;
964
965         g_free(fd->dest_path);
966         fd->dest_path = g_strconcat(fd->source_path, "/", name, NULL);
967
968         file_util_create_dir_do(fd->source_path, fd->dest_path);
969
970         generic_dialog_close(NULL, fd);
971 }
972
973 void file_util_create_dir(gchar *path)
974 {
975         FileDialog *fd;
976         gchar *text;
977         gchar *name;
978
979         if (!isdir(path)) return;
980         name = filename_from_path(path);
981
982         text = g_strdup_printf(_("Create directory in:\n%s\nnamed:"), path);
983         fd = generic_dialog_new(_("GQview - new directory"), text, _("Create"), _("Cancel"),
984                 file_util_create_dir_cb, generic_dialog_close);
985         g_free(text);
986
987         fd->source_path = g_strdup(path);
988         fd->dest_path = NULL;
989
990         fd->entry = gtk_entry_new();
991         gtk_box_pack_start(GTK_BOX(fd->vbox), fd->entry, FALSE, FALSE, 0);
992         gtk_widget_grab_focus(fd->entry);
993         gtk_widget_show(fd->entry);
994 }
995