*** empty log message ***
[midnight-commander.git] / src / filegui.c
blob88039069a20fe747e1f1342a8ae00e908c88247f
1 /* {{{ Copyright */
3 /* File managing. Important notes on this file:
4 *
5 * About the use of dialogs in this file:
6 * If you want to add a new dialog box (or call a routine that pops
7 * up a dialog box), you have to provide a wrapper for background
8 * operations (ie, background operations have to up-call to the parent
9 * process).
11 * For example, instead of using the message() routine, in this
12 * file, you should use one of the stubs that call message with the
13 * proper number of arguments (ie, message_1s, message_2s and so on).
15 * Actually, that is a rule that should be followed by any routines
16 * that may be called from this module.
20 /* File managing GUI for the text mode edition
22 * Copyright (C) 1994, 1995, 1996 The Free Software Foundation
24 * Written by: 1994, 1995 Janne Kukonlehto
25 * 1994, 1995 Fred Leeflang
26 * 1994, 1995, 1996 Miguel de Icaza
27 * 1995, 1996 Jakub Jelinek
28 * 1997 Norbert Warmuth
29 * 1998 Pavel Machek
31 * The copy code was based in GNU's cp, and was written by:
32 * Torbjorn Granlund, David MacKenzie, and Jim Meyering.
34 * The move code was based in GNU's mv, and was written by:
35 * Mike Parker and David MacKenzie.
37 * Janne Kukonlehto added much error recovery to them for being used
38 * in an interactive program.
40 * This program is free software; you can redistribute it and/or modify
41 * it under the terms of the GNU General Public License as published by
42 * the Free Software Foundation; either version 2 of the License, or
43 * (at your option) any later version.
45 * This program is distributed in the hope that it will be useful,
46 * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48 * GNU General Public License for more details.
50 * You should have received a copy of the GNU General Public License
51 * along with this program; if not, write to the Free Software
52 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
55 /* }}} */
57 /* {{{ Include files */
59 #include <config.h>
60 /* Hack: the vfs code should not rely on this */
61 #define WITH_FULL_PATHS 1
63 #include <sys/types.h>
64 #include <stdio.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <string.h>
69 #ifdef HAVE_UNISTD_H
70 # include <unistd.h>
71 #endif
72 #include <sys/stat.h>
73 #include <fcntl.h>
75 #include "global.h"
76 #include "tty.h"
77 #include "eregex.h"
78 #include "setup.h"
79 #include "dialog.h"
80 /* Needed by query_replace */
81 #include "color.h"
82 #include "win.h"
83 #include "dlg.h"
84 #define WANT_WIDGETS
85 #include "widget.h" /* Note the sequence */
86 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
87 #include "layout.h"
88 #include "wtools.h"
90 /* Needed for current_panel, other_panel and WTree */
91 #include "dir.h"
92 #include "panel.h"
93 #include "file.h"
94 #include "filegui.h"
95 #include "fileopctx.h"
96 #include "tree.h"
97 #include "key.h"
98 #include "../vfs/vfs.h"
100 /* }}} */
102 /* This structure describes the UI and internal data required by a file
103 * operation context.
105 typedef struct {
106 /* ETA and bps */
108 int showing_eta;
109 int showing_bps;
110 int eta_extra;
112 /* Dialog and widgets for the operation progress window */
114 Dlg_head *op_dlg;
116 WLabel *file_label[2];
117 WLabel *file_string[2];
118 WLabel *progress_label[3];
119 WGauge *progress_gauge[3];
120 WLabel *eta_label;
121 WLabel *bps_label;
122 WLabel *stalled_label;
124 /* Query replace dialog */
126 Dlg_head *replace_dlg;
127 char *replace_filename;
128 int replace_result;
129 struct stat *s_stat, *d_stat;
130 } FileOpContextUI;
133 /* Replace dialog: color set */
134 static int replace_colors [4];
136 /* Used to save the hint line */
137 static int last_hint_line;
139 /* File operate window sizes */
140 #define WX 62
141 #define WY 10
142 #define BY 10
143 #define WX_ETA_EXTRA 12
145 #define FCOPY_GAUGE_X 14
146 #define FCOPY_LABEL_X 5
148 /* Used for button result values */
149 enum {
150 REPLACE_YES = B_USER,
151 REPLACE_NO,
152 REPLACE_APPEND,
153 REPLACE_ALWAYS,
154 REPLACE_UPDATE,
155 REPLACE_NEVER,
156 REPLACE_ABORT,
157 REPLACE_SIZE,
158 REPLACE_REGET
159 } FileReplaceCode;
161 static FileProgressStatus
162 check_progress_buttons (FileOpContext *ctx)
164 int c;
165 Gpm_Event event;
166 FileOpContextUI *ui;
168 if (ctx->ui == NULL)
169 return FILE_CONT;
171 ui = ctx->ui;
173 event.x = -1; /* Don't show the GPM cursor */
174 c = get_event (&event, 0, 0);
175 if (c == EV_NONE)
176 return FILE_CONT;
177 dlg_process_event (ui->op_dlg, c, &event);
178 switch (ui->op_dlg->ret_value) {
179 case FILE_SKIP:
180 return FILE_SKIP;
181 break;
182 case B_CANCEL:
183 case FILE_ABORT:
184 return FILE_ABORT;
185 break;
186 default:
187 return FILE_CONT;
191 /* {{{ File progress display routines */
193 static int
194 op_win_callback (struct Dlg_head *h, int id, int msg)
196 switch (msg){
197 case DLG_DRAW:
198 attrset (COLOR_NORMAL);
199 dlg_erase (h);
200 draw_box (h, 1, 2, h->lines-2, h->cols-4);
201 return 1;
203 return 0;
206 void
207 file_op_context_create_ui (FileOpContext *ctx, FileOperation op, int with_eta)
209 FileOpContextUI *ui;
210 int x_size;
211 int minus;
212 int eta_offset;
213 char *sixty;
214 char *fifteen;
216 g_return_if_fail (ctx != NULL);
217 g_return_if_fail (ctx->ui == NULL);
219 ui = g_new0 (FileOpContextUI, 1);
220 ctx->ui = ui;
222 minus = verbose ? 0 : 3;
223 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
225 sixty = "";
226 fifteen = "";
228 ctx->recursive_result = 0;
230 ui->replace_result = 0;
231 ui->showing_eta = with_eta;
232 ui->showing_bps = with_eta;
233 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
234 x_size = (WX + 4) + ui->eta_extra;
236 ui->op_dlg = create_dlg (0, 0, WY-minus+4, x_size, dialog_colors,
237 op_win_callback, "", "opwin", DLG_CENTER);
239 last_hint_line = the_hint->widget.y;
240 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
241 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines+1;
243 x_set_dialog_title (ui->op_dlg, "");
245 add_widget (ui->op_dlg, button_new (BY-minus, WX - 19 + eta_offset, FILE_ABORT,
246 NORMAL_BUTTON, _("&Abort"), 0, 0, "abort"));
247 add_widget (ui->op_dlg, button_new (BY-minus, 14 + eta_offset, FILE_SKIP,
248 NORMAL_BUTTON, _("&Skip"), 0, 0, "skip"));
250 add_widget (ui->op_dlg, ui->progress_gauge[2] = gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0, "g-1"));
251 add_widget (ui->op_dlg, ui->progress_label[2] = label_new (7, FCOPY_LABEL_X, fifteen, "l-1"));
252 add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, "", "bps-label"));
254 add_widget (ui->op_dlg, ui->progress_gauge[1] = gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0, "g-2"));
255 add_widget (ui->op_dlg, ui->progress_label[1] = label_new (8, FCOPY_LABEL_X, fifteen, "l-2"));
256 add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, "", "stalled"));
258 add_widget (ui->op_dlg, ui->progress_gauge[0] = gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0, "g-3"));
259 add_widget (ui->op_dlg, ui->progress_label[0] = label_new (6, FCOPY_LABEL_X, fifteen, "l-3"));
260 add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, "", "eta_label"));
262 add_widget (ui->op_dlg, ui->file_string[1] = label_new (4, FCOPY_GAUGE_X, sixty, "fs-l-1"));
263 add_widget (ui->op_dlg, ui->file_label[1] = label_new (4, FCOPY_LABEL_X, fifteen, "fs-l-2"));
264 add_widget (ui->op_dlg, ui->file_string[0] = label_new (3, FCOPY_GAUGE_X, sixty, "fs-x-1"));
265 add_widget (ui->op_dlg, ui->file_label[0] = label_new (3, FCOPY_LABEL_X, fifteen, "fs-x-2"));
267 /* We will manage the dialog without any help, that's why
268 we have to call init_dlg */
269 init_dlg (ui->op_dlg);
270 ui->op_dlg->running = 1;
273 void
274 file_op_context_destroy_ui (FileOpContext *ctx)
276 FileOpContextUI *ui;
278 g_return_if_fail (ctx != NULL);
280 if (ctx->ui){
281 ui = ctx->ui;
283 dlg_run_done (ui->op_dlg);
284 destroy_dlg (ui->op_dlg);
285 g_free (ui);
288 the_hint->widget.y = last_hint_line;
290 ctx->ui = NULL;
293 static FileProgressStatus
294 show_no_bar (FileOpContext *ctx, int n)
296 FileOpContextUI *ui;
298 if (ctx->ui == NULL)
299 return FILE_CONT;
301 ui = ctx->ui;
303 if (n >= 0) {
304 label_set_text (ui->progress_label[n], "");
305 gauge_show (ui->progress_gauge[n], 0);
307 return check_progress_buttons (ctx);
310 static FileProgressStatus
311 show_bar (FileOpContext *ctx, int n, double done, double total)
313 FileOpContextUI *ui;
315 if (ctx->ui == NULL)
316 return FILE_CONT;
318 ui = ctx->ui;
321 * Gauge needs integers, so give it with integers between 0 and 1023.
322 * This precision should be quite reasonable.
324 gauge_set_value (ui->progress_gauge[n], 1024, (int) (1024 * done / total));
325 gauge_show (ui->progress_gauge[n], 1);
326 return check_progress_buttons (ctx);
329 static void
330 file_eta_show (FileOpContext *ctx)
332 int eta_hours, eta_mins, eta_s;
333 char eta_buffer [BUF_TINY];
334 FileOpContextUI *ui;
336 if (ctx->ui == NULL)
337 return;
339 ui = ctx->ui;
341 if (!ui->showing_eta)
342 return;
344 if (ctx->eta_secs > 0.5) {
345 eta_hours = ctx->eta_secs / (60 * 60);
346 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
347 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
348 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"), eta_hours, eta_mins, eta_s);
349 } else
350 *eta_buffer = 0;
352 label_set_text (ui->eta_label, eta_buffer);
355 static void
356 file_bps_show (FileOpContext *ctx)
358 char bps_buffer [BUF_TINY];
359 FileOpContextUI *ui;
361 if (ctx->ui == NULL)
362 return;
364 ui = ctx->ui;
366 if (!ui->showing_bps)
367 return;
369 if (ctx->bps > 1024*1024) {
370 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"), ctx->bps / (1024*1024.0));
371 } else if (ctx->bps > 1024){
372 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"), ctx->bps / 1024.0);
373 } else if (ctx->bps > 1){
374 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"), ctx->bps);
375 } else
376 *bps_buffer = 0;
378 label_set_text (ui->bps_label, bps_buffer);
381 FileProgressStatus
382 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
384 FileOpContextUI *ui;
386 g_return_val_if_fail (ctx != NULL, FILE_CONT);
388 if (ctx->ui == NULL)
389 return FILE_CONT;
391 ui = ctx->ui;
393 if (!verbose)
394 return check_progress_buttons (ctx);
395 if (total > 0){
396 label_set_text (ui->progress_label[0], _("File"));
397 file_eta_show (ctx);
398 file_bps_show (ctx);
399 return show_bar (ctx, 0, done, total);
400 } else
401 return show_no_bar (ctx, 0);
404 FileProgressStatus
405 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
407 FileOpContextUI *ui;
409 g_return_val_if_fail (ctx != NULL, FILE_CONT);
411 if (ctx->ui == NULL)
412 return FILE_CONT;
414 ui = ctx->ui;
416 if (!verbose)
417 return check_progress_buttons (ctx);
418 if (total > 0){
419 label_set_text (ui->progress_label[1], _("Count"));
420 return show_bar (ctx, 1, done, total);
421 } else
422 return show_no_bar (ctx, 1);
425 FileProgressStatus
426 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
428 FileOpContextUI *ui;
430 g_return_val_if_fail (ctx != NULL, FILE_CONT);
432 if (ctx->ui == NULL)
433 return FILE_CONT;
435 ui = ctx->ui;
437 if (!verbose)
438 return check_progress_buttons (ctx);
439 if (total > 0){
440 label_set_text (ui->progress_label[2], _("Bytes"));
441 return show_bar (ctx, 2, done, total);
442 } else
443 return show_no_bar (ctx, 2);
446 /* }}} */
448 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
450 FileProgressStatus
451 file_progress_show_source (FileOpContext *ctx, char *s)
453 FileOpContextUI *ui;
455 g_return_val_if_fail (ctx != NULL, FILE_CONT);
457 if (ctx->ui == NULL)
458 return FILE_CONT;
460 ui = ctx->ui;
462 if (s != NULL) {
463 #ifdef WITH_FULL_PATHS
464 int i = strlen (cpanel->cwd);
466 /* We remove the full path we have added before */
467 if (!strncmp (s, cpanel->cwd, i)){
468 if (s[i] == PATH_SEP)
469 s += i + 1;
471 #endif /* WITH_FULL_PATHS */
473 label_set_text (ui->file_label[0], _("Source"));
474 label_set_text (ui->file_string[0], truncFileString (ui, s));
475 return check_progress_buttons (ctx);
476 } else {
477 label_set_text (ui->file_label[0], "");
478 label_set_text (ui->file_string[0], "");
479 return check_progress_buttons (ctx);
483 FileProgressStatus
484 file_progress_show_target (FileOpContext *ctx, char *s)
486 FileOpContextUI *ui;
488 g_return_val_if_fail (ctx != NULL, FILE_CONT);
490 if (ctx->ui == NULL)
491 return FILE_CONT;
493 ui = ctx->ui;
495 if (s != NULL) {
496 label_set_text (ui->file_label[1], _("Target"));
497 label_set_text (ui->file_string[1], truncFileString (ui, s));
498 return check_progress_buttons (ctx);
499 } else {
500 label_set_text (ui->file_label[1], "");
501 label_set_text (ui->file_string[1], "");
502 return check_progress_buttons (ctx);
506 FileProgressStatus
507 file_progress_show_deleting (FileOpContext *ctx, char *s)
509 FileOpContextUI *ui;
511 g_return_val_if_fail (ctx != NULL, FILE_CONT);
513 if (ctx->ui == NULL)
514 return FILE_CONT;
516 ui = ctx->ui;
518 label_set_text (ui->file_label[0], _("Deleting"));
519 label_set_text (ui->file_label[0], truncFileString (ui, s));
520 return check_progress_buttons (ctx);
523 static int
524 replace_callback (struct Dlg_head *h, int Id, int Msg)
526 switch (Msg){
527 case DLG_DRAW:
528 dialog_repaint (h, ERROR_COLOR, ERROR_COLOR);
529 break;
531 return 0;
534 #define X_TRUNC 52
537 * FIXME: probably it is better to replace this with quick dialog machinery,
538 * but actually I'm not familiar with it and have not much time :(
539 * alex
541 static struct
543 char* text;
544 int ypos, xpos;
545 int value; /* 0 for labels */
546 char* tkname;
548 rd_widgets [] =
550 {N_("Target file \"%s\" already exists!"),
551 3, 4, 0, "target-e" },
552 {N_("&Abort"), BY + 3, 25, REPLACE_ABORT, "abort" },
553 {N_("if &Size differs"),
554 BY + 1, 28, REPLACE_SIZE, "if-size" },
555 {N_("non&E"), BY, 47, REPLACE_NEVER, "none" },
556 {N_("&Update"), BY, 36, REPLACE_UPDATE, "update" },
557 {N_("al&L"), BY, 28, REPLACE_ALWAYS, "all" },
558 {N_("Overwrite all targets?"),
559 BY, 4, 0, "over-label" },
560 {N_("&Reget"), BY - 1, 28, REPLACE_REGET, "reget" },
561 {N_("ap&Pend"), BY - 2, 45, REPLACE_APPEND, "append" },
562 {N_("&No"), BY - 2, 37, REPLACE_NO, "no" },
563 {N_("&Yes"), BY - 2, 28, REPLACE_YES, "yes" },
564 {N_("Overwrite this target?"),
565 BY - 2, 4, 0, "overlab" },
566 {N_("Target date: %s, size %d"),
567 6, 4, 0, "target-date" },
568 {N_("Source date: %s, size %d"),
569 5, 4, 0, "source-date" }
572 #define ADD_RD_BUTTON(i)\
573 add_widget (ui->replace_dlg,\
574 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
575 NORMAL_BUTTON, rd_widgets [i].text, 0, 0, rd_widgets [i].tkname))
577 #define ADD_RD_LABEL(ui,i,p1,p2)\
578 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
579 add_widget (ui->replace_dlg,\
580 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer, rd_widgets [i].tkname))
582 static void
583 init_replace (FileOpContext *ctx, enum OperationMode mode)
585 FileOpContextUI *ui;
586 char buffer [BUF_SMALL];
587 static int rd_xlen = 60, rd_trunc = X_TRUNC;
589 #ifdef ENABLE_NLS
590 static int i18n_flag;
591 if (!i18n_flag) {
592 int l1, l2, l, row;
593 register int i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
594 while (i--)
595 rd_widgets [i].text = _(rd_widgets [i].text);
598 *longest of "Overwrite..." labels
599 * (assume "Target date..." are short enough)
601 l1 = max (strlen (rd_widgets [6].text), strlen (rd_widgets [11].text));
603 /* longest of button rows */
604 i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
605 for (row = l = l2 = 0; i--;) {
606 if (rd_widgets [i].value != 0) {
607 if (row != rd_widgets [i].ypos) {
608 row = rd_widgets [i].ypos;
609 l2 = max (l2, l);
610 l = 0;
612 l += strlen (rd_widgets [i].text) + 4;
615 l2 = max (l2, l); /* last row */
616 rd_xlen = max (rd_xlen, l1 + l2 + 8);
617 rd_trunc = rd_xlen - 6;
619 /* Now place buttons */
620 l1 += 5; /* start of first button in the row */
621 i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
623 for (l = l1, row = 0; --i > 1;) {
624 if (rd_widgets [i].value != 0) {
625 if (row != rd_widgets [i].ypos) {
626 row = rd_widgets [i].ypos;
627 l = l1;
629 rd_widgets [i].xpos = l;
630 l += strlen (rd_widgets [i].text) + 4;
633 /* Abort button is centered */
634 rd_widgets [1].xpos = (rd_xlen - strlen (rd_widgets [1].text) - 3) / 2;
636 #endif /* ENABLE_NLS */
638 ui = ctx->ui;
640 replace_colors [0] = ERROR_COLOR;
641 replace_colors [1] = COLOR_NORMAL;
642 replace_colors [2] = ERROR_COLOR;
643 replace_colors [3] = COLOR_NORMAL;
645 ui->replace_dlg = create_dlg (0, 0, 16, rd_xlen, replace_colors, replace_callback,
646 "[ Replace ]", "replace", DLG_CENTER);
648 x_set_dialog_title (ui->replace_dlg,
649 (mode == Foreground
650 ? _(" File exists ")
651 : _(" Background process: File exists ")));
654 ADD_RD_LABEL(ui, 0,
655 name_trunc (ui->replace_filename, rd_trunc - strlen (rd_widgets [0].text)), 0);
656 ADD_RD_BUTTON(1);
658 ADD_RD_BUTTON(2);
659 ADD_RD_BUTTON(3);
660 ADD_RD_BUTTON(4);
661 ADD_RD_BUTTON(5);
662 ADD_RD_LABEL(ui, 6, 0, 0);
664 /* "this target..." widgets */
665 if (!S_ISDIR (ui->d_stat->st_mode)){
666 if ((ui->d_stat->st_size && ui->s_stat->st_size > ui->d_stat->st_size))
667 ADD_RD_BUTTON(7);
669 ADD_RD_BUTTON(8);
671 ADD_RD_BUTTON(9);
672 ADD_RD_BUTTON(10);
673 ADD_RD_LABEL(ui, 11,0,0);
675 ADD_RD_LABEL(ui, 12, file_date (ui->d_stat->st_mtime), (int) ui->d_stat->st_size);
676 ADD_RD_LABEL(ui, 13, file_date (ui->s_stat->st_mtime), (int) ui->s_stat->st_size);
679 void
680 file_progress_set_stalled_label (FileOpContext *ctx, char *stalled_msg)
682 FileOpContextUI *ui;
684 g_return_if_fail (ctx != NULL);
686 if (ctx->ui == NULL)
687 return;
689 ui = ctx->ui;
691 label_set_text (ui->stalled_label, stalled_msg);
694 FileProgressStatus
695 file_progress_real_query_replace (FileOpContext *ctx, enum OperationMode mode,
696 char *destname, struct stat *_s_stat,
697 struct stat *_d_stat)
699 FileOpContextUI *ui;
701 g_return_val_if_fail (ctx != NULL, FILE_CONT);
702 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
704 ui = ctx->ui;
706 if (ui->replace_result < REPLACE_ALWAYS){
707 ui->replace_filename = destname;
708 ui->s_stat = _s_stat;
709 ui->d_stat = _d_stat;
710 init_replace (ctx, mode);
711 run_dlg (ui->replace_dlg);
712 ui->replace_result = ui->replace_dlg->ret_value;
713 if (ui->replace_result == B_CANCEL)
714 ui->replace_result = REPLACE_ABORT;
715 destroy_dlg (ui->replace_dlg);
718 switch (ui->replace_result){
719 case REPLACE_UPDATE:
720 do_refresh ();
721 if (_s_stat->st_mtime > _d_stat->st_mtime)
722 return FILE_CONT;
723 else
724 return FILE_SKIP;
726 case REPLACE_SIZE:
727 do_refresh ();
728 if (_s_stat->st_size == _d_stat->st_size)
729 return FILE_SKIP;
730 else
731 return FILE_CONT;
733 case REPLACE_REGET:
734 /* Carefull: we fall through and set do_append */
735 ctx->do_reget = _d_stat->st_size;
737 case REPLACE_APPEND:
738 ctx->do_append = 1;
740 case REPLACE_YES:
741 case REPLACE_ALWAYS:
742 do_refresh ();
743 return FILE_CONT;
744 case REPLACE_NO:
745 case REPLACE_NEVER:
746 do_refresh ();
747 return FILE_SKIP;
748 case REPLACE_ABORT:
749 default:
750 return FILE_ABORT;
754 #define FMDY 13
755 #define FMD_XLEN 64
756 extern int fmd_xlen;
757 static QuickWidget fmd_widgets [] = {
759 #define FMCB0 FMDC
760 #define FMCB12 0
761 #define FMCB11 1
762 /* follow symlinks and preserve Attributes must be the first */
763 { quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
764 0 /* &op_preserve */, 0, "preserve" },
765 { quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
766 0 /* &file_mask_op_follow_links */, 0, "follow" },
767 { quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0,"to"},
768 { quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
769 0 /* &source_easy_patterns */, 0, "using-shell" },
770 { quick_input, 3, 64, 3, FMDY, "", 58,
771 0, 0, 0, "input-def" },
772 #define FMDI1 4
773 #define FMDI2 5
774 #define FMDC 3
775 { quick_input, 3, 64, 6, FMDY, "", 58, 0,
776 0, 0, "input2" },
777 #define FMDI0 6
778 { quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, "ql" },
779 #define FMBRGT 7
780 { quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
781 "cancel" },
782 #undef SKIP
783 #ifdef WITH_BACKGROUND
784 # define SKIP 5
785 # define FMCB21 11
786 # define FMCB22 10
787 # define FMBLFT 9
788 # define FMBMID 8
789 { quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0, "back" },
790 #else /* WITH_BACKGROUND */
791 # define SKIP 4
792 # define FMCB21 10
793 # define FMCB22 9
794 # define FMBLFT 8
795 # undef FMBMID
796 #endif
797 { quick_button, 14, 64, 9, FMDY, N_("&Ok"), 0, B_ENTER, 0, 0, "ok" },
798 { quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
799 0 /* &file_mask_stable_symlinks */, 0, "stab-sym" },
800 { quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0, 0,
801 0 /* &dive_into_subdirs */, 0, "dive" },
802 { 0 } };
804 void
805 fmd_init_i18n (int force)
807 #ifdef ENABLE_NLS
808 static int initialized = FALSE;
809 register int i;
810 int len;
812 if (initialized && !force)
813 return;
815 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
816 op_names [i] = _(op_names [i]);
818 i = sizeof (fmd_widgets) / sizeof (fmd_widgets [0]) - 1;
819 while (i--)
820 if (fmd_widgets [i].text[0] != '\0')
821 fmd_widgets [i].text = _(fmd_widgets [i].text);
823 len = strlen (fmd_widgets [FMCB11].text)
824 + strlen (fmd_widgets [FMCB21].text) + 15;
825 fmd_xlen = max (fmd_xlen, len);
827 len = strlen (fmd_widgets [FMCB12].text)
828 + strlen (fmd_widgets [FMCB22].text) + 15;
829 fmd_xlen = max (fmd_xlen, len);
831 len = strlen (fmd_widgets [FMBRGT].text)
832 + strlen (fmd_widgets [FMBLFT].text) + 11;
834 #ifdef FMBMID
835 len += strlen (fmd_widgets [FMBMID].text) + 6;
836 #endif
838 fmd_xlen = max (fmd_xlen, len + 4);
840 len = (fmd_xlen - (len + 6)) / 2;
841 i = fmd_widgets [FMBLFT].relative_x = len + 3;
842 i += strlen (fmd_widgets [FMBLFT].text) + 8;
844 #ifdef FMBMID
845 fmd_widgets [FMBMID].relative_x = i;
846 i += strlen (fmd_widgets [FMBMID].text) + 6;
847 #endif
849 fmd_widgets [FMBRGT].relative_x = i;
851 #define chkbox_xpos(i) \
852 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
854 chkbox_xpos(FMCB0);
855 chkbox_xpos(FMCB21);
856 chkbox_xpos(FMCB22);
858 if (fmd_xlen != FMD_XLEN)
860 i = sizeof (fmd_widgets) / sizeof (fmd_widgets [0]) - 1;
861 while (i--)
862 fmd_widgets [i].x_divisions = fmd_xlen;
864 fmd_widgets [FMDI1].hotkey_pos =
865 fmd_widgets [FMDI2].hotkey_pos = fmd_xlen - 6;
867 #undef chkbox_xpos
869 initialized = TRUE;
870 #endif /* !ENABLE_NLS */
873 char *
874 file_mask_dialog (FileOpContext *ctx, FileOperation operation, char *text,
875 char *def_text, int only_one, int *do_background)
877 int source_easy_patterns = easy_patterns;
878 char *source_mask, *orig_mask, *dest_dir;
879 const char *error;
880 struct stat buf;
881 int val;
882 QuickDialog Quick_input;
884 g_return_val_if_fail (ctx != NULL, NULL);
885 #if 0
886 message_3s (1, __FUNCTION__, "text = `%s' \n def_text = `%s'", text, def_text);
887 #endif
888 fmd_init_i18n (FALSE);
890 /* Set up the result pointers */
892 fmd_widgets[FMCB12].result = &ctx->op_preserve;
893 fmd_widgets[FMCB11].result = &ctx->follow_links;
894 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
895 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
897 /* Create the dialog */
899 ctx->stable_symlinks = 0;
900 fmd_widgets [FMDC].result = &source_easy_patterns;
901 fmd_widgets [FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
902 Quick_input.xlen = fmd_xlen;
903 Quick_input.xpos = -1;
904 Quick_input.title = op_names [operation];
905 Quick_input.help = "[Mask Copy/Rename]";
906 Quick_input.ylen = FMDY;
907 Quick_input.i18n = 1;
909 if (operation == OP_COPY) {
910 Quick_input.class = "quick_file_mask_copy";
911 } else { /* operation == OP_MOVE */
912 Quick_input.class = "quick_file_mask_move";
914 Quick_input.widgets = fmd_widgets;
915 fmd_widgets [FMDI0].text = text;
916 fmd_widgets [FMDI2].text = def_text;
917 fmd_widgets [FMDI2].str_result = &dest_dir;
918 fmd_widgets [FMDI1].str_result = &source_mask;
920 *do_background = 0;
921 ask_file_mask:
923 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
924 return 0;
926 if (ctx->follow_links)
927 ctx->stat_func = (mc_stat_fn) mc_stat;
928 else
929 ctx->stat_func = (mc_stat_fn) mc_lstat;
931 if (ctx->op_preserve) {
932 ctx->preserve = 1;
933 ctx->umask_kill = 0777777;
934 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
935 } else {
936 int i;
937 ctx->preserve = ctx->preserve_uidgid = 0;
938 i = umask (0);
939 umask (i);
940 ctx->umask_kill = i ^ 0777777;
943 orig_mask = source_mask;
944 if (!dest_dir || !*dest_dir){
945 if (source_mask)
946 g_free (source_mask);
947 return dest_dir;
949 if (source_easy_patterns){
950 source_easy_patterns = easy_patterns;
951 easy_patterns = 1;
952 source_mask = convert_pattern (source_mask, match_file, 1);
953 easy_patterns = source_easy_patterns;
954 error = re_compile_pattern (source_mask, strlen (source_mask), &ctx->rx);
955 g_free (source_mask);
956 } else
957 error = re_compile_pattern (source_mask, strlen (source_mask), &ctx->rx);
959 if (error){
960 message_3s (1, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
961 orig_mask, error);
962 if (orig_mask)
963 g_free (orig_mask);
964 goto ask_file_mask;
966 if (orig_mask)
967 g_free (orig_mask);
968 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
969 if (ctx->dest_mask == NULL)
970 ctx->dest_mask = dest_dir;
971 else
972 ctx->dest_mask++;
973 orig_mask = ctx->dest_mask;
974 if (!*ctx->dest_mask || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask) &&
975 (!only_one || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))) ||
976 (ctx->dive_into_subdirs && ((!only_one && !is_wildcarded (ctx->dest_mask)) ||
977 (only_one
978 && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
979 ctx->dest_mask = g_strdup ("*");
980 else {
981 ctx->dest_mask = g_strdup (ctx->dest_mask);
982 *orig_mask = 0;
984 if (!*dest_dir){
985 g_free (dest_dir);
986 dest_dir = g_strdup ("./");
988 if (val == B_USER)
989 *do_background = 1;
990 return dest_dir;