Updated italian translation
[midnight-commander.git] / src / filegui.c
blobb3535f2ef97e4a06bdf4dce9252dda6b437976a2
1 /* File management GUI for the text mode edition
3 * Copyright (C) 1994, 1995, 1996 The Free Software Foundation
4 *
5 * Written by: 1994, 1995 Janne Kukonlehto
6 * 1994, 1995 Fred Leeflang
7 * 1994, 1995, 1996 Miguel de Icaza
8 * 1995, 1996 Jakub Jelinek
9 * 1997 Norbert Warmuth
10 * 1998 Pavel Machek
12 * The copy code was based in GNU's cp, and was written by:
13 * Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 * The move code was based in GNU's mv, and was written by:
16 * Mike Parker and David MacKenzie.
18 * Janne Kukonlehto added much error recovery to them for being used
19 * in an interactive program.
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
37 * Please note that all dialogs used here must be safe for background
38 * operations.
42 /* {{{ Include files */
44 #include <config.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <string.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <unistd.h>
55 #include "global.h"
56 #include "setup.h" /* verbose */
57 #include "dialog.h" /* do_refresh() */
58 #include "color.h" /* dialog_colors */
59 #include "widget.h" /* WLabel */
60 #define WANT_WIDGETS
61 #include "main.h" /* the_hint */
62 #include "wtools.h" /* QuickDialog */
63 #include "panel.h" /* current_panel */
64 #include "fileopctx.h" /* FILE_CONT */
65 #include "filegui.h"
66 #include "key.h" /* get_event */
67 #include "util.h" /* strip_password() */
69 /* }}} */
71 /* Hack: the vfs code should not rely on this */
72 #define WITH_FULL_PATHS 1
74 /* This structure describes the UI and internal data required by a file
75 * operation context.
77 typedef struct {
78 /* ETA and bps */
80 int showing_eta;
81 int showing_bps;
82 int eta_extra;
84 /* Dialog and widgets for the operation progress window */
86 Dlg_head *op_dlg;
88 WLabel *file_label[2];
89 WLabel *file_string[2];
90 WLabel *progress_label[3];
91 WGauge *progress_gauge[3];
92 WLabel *eta_label;
93 WLabel *bps_label;
94 WLabel *stalled_label;
96 /* Query replace dialog */
98 Dlg_head *replace_dlg;
99 const char *replace_filename;
100 int replace_result;
101 struct stat *s_stat, *d_stat;
102 } FileOpContextUI;
105 /* Used to save the hint line */
106 static int last_hint_line;
108 /* File operate window sizes */
109 #define WX 62
110 #define WY 10
111 #define BY 10
112 #define WX_ETA_EXTRA 12
114 #define FCOPY_GAUGE_X 14
115 #define FCOPY_LABEL_X 5
117 /* Used for button result values */
118 enum {
119 REPLACE_YES = B_USER,
120 REPLACE_NO,
121 REPLACE_APPEND,
122 REPLACE_ALWAYS,
123 REPLACE_UPDATE,
124 REPLACE_NEVER,
125 REPLACE_ABORT,
126 REPLACE_SIZE,
127 REPLACE_REGET
130 static FileProgressStatus
131 check_progress_buttons (FileOpContext *ctx)
133 int c;
134 Gpm_Event event;
135 FileOpContextUI *ui;
137 if (ctx->ui == NULL)
138 return FILE_CONT;
140 ui = ctx->ui;
142 event.x = -1; /* Don't show the GPM cursor */
143 c = get_event (&event, 0, 0);
144 if (c == EV_NONE)
145 return FILE_CONT;
147 /* Reinitialize to avoid old values after events other than
148 selecting a button */
149 ui->op_dlg->ret_value = FILE_CONT;
151 dlg_process_event (ui->op_dlg, c, &event);
152 switch (ui->op_dlg->ret_value) {
153 case FILE_SKIP:
154 return FILE_SKIP;
155 break;
156 case B_CANCEL:
157 case FILE_ABORT:
158 return FILE_ABORT;
159 break;
160 default:
161 return FILE_CONT;
165 /* {{{ File progress display routines */
167 void
168 file_op_context_create_ui (FileOpContext *ctx, int with_eta)
170 FileOpContextUI *ui;
171 int x_size;
172 int minus;
173 int eta_offset;
174 const char *sixty;
175 const char *fifteen;
177 g_return_if_fail (ctx != NULL);
178 g_return_if_fail (ctx->ui == NULL);
180 ui = g_new0 (FileOpContextUI, 1);
181 ctx->ui = ui;
183 minus = verbose ? 0 : 3;
184 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
186 sixty = "";
187 fifteen = "";
189 ctx->recursive_result = 0;
191 ui->replace_result = 0;
192 ui->showing_eta = with_eta;
193 ui->showing_bps = with_eta;
194 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
195 x_size = (WX + 4) + ui->eta_extra;
197 ui->op_dlg =
198 create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors, NULL,
199 NULL, op_names[ctx->operation],
200 DLG_CENTER | DLG_REVERSE);
202 last_hint_line = the_hint->widget.y;
203 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
204 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
206 add_widget (ui->op_dlg,
207 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
208 NORMAL_BUTTON, _("&Abort"), 0));
209 add_widget (ui->op_dlg,
210 button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
211 NORMAL_BUTTON, _("&Skip"), 0));
213 add_widget (ui->op_dlg, ui->progress_gauge[2] =
214 gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0));
215 add_widget (ui->op_dlg, ui->progress_label[2] =
216 label_new (7, FCOPY_LABEL_X, fifteen));
217 add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, ""));
219 add_widget (ui->op_dlg, ui->progress_gauge[1] =
220 gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0));
221 add_widget (ui->op_dlg, ui->progress_label[1] =
222 label_new (8, FCOPY_LABEL_X, fifteen));
223 add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, ""));
225 add_widget (ui->op_dlg, ui->progress_gauge[0] =
226 gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0));
227 add_widget (ui->op_dlg, ui->progress_label[0] =
228 label_new (6, FCOPY_LABEL_X, fifteen));
229 add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, ""));
231 add_widget (ui->op_dlg, ui->file_string[1] =
232 label_new (4, FCOPY_GAUGE_X, sixty));
233 add_widget (ui->op_dlg, ui->file_label[1] =
234 label_new (4, FCOPY_LABEL_X, fifteen));
235 add_widget (ui->op_dlg, ui->file_string[0] =
236 label_new (3, FCOPY_GAUGE_X, sixty));
237 add_widget (ui->op_dlg, ui->file_label[0] =
238 label_new (3, FCOPY_LABEL_X, fifteen));
240 /* We will manage the dialog without any help, that's why
241 we have to call init_dlg */
242 init_dlg (ui->op_dlg);
243 ui->op_dlg->running = 1;
246 void
247 file_op_context_destroy_ui (FileOpContext *ctx)
249 FileOpContextUI *ui;
251 g_return_if_fail (ctx != NULL);
253 if (ctx->ui) {
254 ui = ctx->ui;
256 dlg_run_done (ui->op_dlg);
257 destroy_dlg (ui->op_dlg);
258 g_free (ui);
261 the_hint->widget.y = last_hint_line;
263 ctx->ui = NULL;
266 static FileProgressStatus
267 show_no_bar (FileOpContext *ctx, int n)
269 FileOpContextUI *ui;
271 if (ctx->ui == NULL)
272 return FILE_CONT;
274 ui = ctx->ui;
276 if (n >= 0) {
277 label_set_text (ui->progress_label[n], "");
278 gauge_show (ui->progress_gauge[n], 0);
280 return check_progress_buttons (ctx);
283 static FileProgressStatus
284 show_bar (FileOpContext *ctx, int n, double done, double total)
286 FileOpContextUI *ui;
288 if (ctx->ui == NULL)
289 return FILE_CONT;
291 ui = ctx->ui;
294 * Gauge needs integers, so give it with integers between 0 and 1023.
295 * This precision should be quite reasonable.
297 gauge_set_value (ui->progress_gauge[n], 1024,
298 (int) (1024 * done / total));
299 gauge_show (ui->progress_gauge[n], 1);
300 return check_progress_buttons (ctx);
303 static void
304 file_eta_show (FileOpContext *ctx)
306 int eta_hours, eta_mins, eta_s;
307 char eta_buffer[BUF_TINY];
308 FileOpContextUI *ui;
310 if (ctx->ui == NULL)
311 return;
313 ui = ctx->ui;
315 if (!ui->showing_eta)
316 return;
318 if (ctx->eta_secs > 0.5) {
319 eta_hours = ctx->eta_secs / (60 * 60);
320 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
321 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
322 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
323 eta_hours, eta_mins, eta_s);
324 } else
325 *eta_buffer = 0;
327 label_set_text (ui->eta_label, eta_buffer);
330 static void
331 file_bps_show (FileOpContext *ctx)
333 char bps_buffer[BUF_TINY];
334 FileOpContextUI *ui;
336 if (ctx->ui == NULL)
337 return;
339 ui = ctx->ui;
341 if (!ui->showing_bps)
342 return;
344 if (ctx->bps > 1024 * 1024) {
345 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
346 ctx->bps / (1024 * 1024.0));
347 } else if (ctx->bps > 1024) {
348 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
349 ctx->bps / 1024.0);
350 } else if (ctx->bps > 1) {
351 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
352 ctx->bps);
353 } else
354 *bps_buffer = 0;
356 label_set_text (ui->bps_label, bps_buffer);
359 FileProgressStatus
360 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
362 FileOpContextUI *ui;
364 g_return_val_if_fail (ctx != NULL, FILE_CONT);
366 if (ctx->ui == NULL)
367 return FILE_CONT;
369 ui = ctx->ui;
371 if (!verbose)
372 return check_progress_buttons (ctx);
373 if (total > 0) {
374 label_set_text (ui->progress_label[0], _("File"));
375 file_eta_show (ctx);
376 file_bps_show (ctx);
377 return show_bar (ctx, 0, done, total);
378 } else
379 return show_no_bar (ctx, 0);
382 FileProgressStatus
383 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
385 FileOpContextUI *ui;
387 g_return_val_if_fail (ctx != NULL, FILE_CONT);
389 if (ctx->ui == NULL)
390 return FILE_CONT;
392 ui = ctx->ui;
394 if (!verbose)
395 return check_progress_buttons (ctx);
396 if (total > 0) {
397 label_set_text (ui->progress_label[1], _("Count"));
398 return show_bar (ctx, 1, done, total);
399 } else
400 return show_no_bar (ctx, 1);
403 FileProgressStatus
404 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
406 FileOpContextUI *ui;
408 g_return_val_if_fail (ctx != NULL, FILE_CONT);
410 if (ctx->ui == NULL)
411 return FILE_CONT;
413 ui = ctx->ui;
415 if (!verbose)
416 return check_progress_buttons (ctx);
417 if (total > 0) {
418 label_set_text (ui->progress_label[2], _("Bytes"));
419 return show_bar (ctx, 2, done, total);
420 } else
421 return show_no_bar (ctx, 2);
424 /* }}} */
426 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
427 #define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
429 FileProgressStatus
430 file_progress_show_source (FileOpContext *ctx, const char *s)
432 FileOpContextUI *ui;
434 g_return_val_if_fail (ctx != NULL, FILE_CONT);
436 if (ctx->ui == NULL)
437 return FILE_CONT;
439 ui = ctx->ui;
441 if (s != NULL) {
442 #ifdef WITH_FULL_PATHS
443 int i = strlen (current_panel->cwd);
445 /* We remove the full path we have added before */
446 if (!strncmp (s, current_panel->cwd, i)) {
447 if (s[i] == PATH_SEP)
448 s += i + 1;
450 #endif /* WITH_FULL_PATHS */
452 label_set_text (ui->file_label[0], _("Source"));
453 label_set_text (ui->file_string[0], truncFileString (ui, s));
454 return check_progress_buttons (ctx);
455 } else {
456 label_set_text (ui->file_label[0], "");
457 label_set_text (ui->file_string[0], "");
458 return check_progress_buttons (ctx);
462 FileProgressStatus
463 file_progress_show_target (FileOpContext *ctx, const char *s)
465 FileOpContextUI *ui;
467 g_return_val_if_fail (ctx != NULL, FILE_CONT);
469 if (ctx->ui == NULL)
470 return FILE_CONT;
472 ui = ctx->ui;
474 if (s != NULL) {
475 label_set_text (ui->file_label[1], _("Target"));
476 label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
477 return check_progress_buttons (ctx);
478 } else {
479 label_set_text (ui->file_label[1], "");
480 label_set_text (ui->file_string[1], "");
481 return check_progress_buttons (ctx);
485 FileProgressStatus
486 file_progress_show_deleting (FileOpContext *ctx, const char *s)
488 FileOpContextUI *ui;
490 g_return_val_if_fail (ctx != NULL, FILE_CONT);
492 if (ctx->ui == NULL)
493 return FILE_CONT;
495 ui = ctx->ui;
497 label_set_text (ui->file_label[0], _("Deleting"));
498 label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
499 return check_progress_buttons (ctx);
502 #define X_TRUNC 52
505 * FIXME: probably it is better to replace this with quick dialog machinery,
506 * but actually I'm not familiar with it and have not much time :(
507 * alex
509 static struct {
510 const char *text;
511 int ypos, xpos;
512 int value; /* 0 for labels */
513 } rd_widgets[] = {
515 N_("Target file \"%s\" already exists!"), 3, 4, 0}, {
516 N_("&Abort"), BY + 3, 25, REPLACE_ABORT}, {
517 N_("If &size differs"), BY + 1, 28, REPLACE_SIZE}, {
518 N_("Non&e"), BY, 47, REPLACE_NEVER}, {
519 N_("&Update"), BY, 36, REPLACE_UPDATE}, {
520 N_("A&ll"), BY, 28, REPLACE_ALWAYS}, {
521 N_("Overwrite all targets?"), BY, 4, 0}, {
522 N_("&Reget"), BY - 1, 28, REPLACE_REGET}, {
523 N_("A&ppend"), BY - 2, 45, REPLACE_APPEND}, {
524 N_("&No"), BY - 2, 37, REPLACE_NO}, {
525 N_("&Yes"), BY - 2, 28, REPLACE_YES}, {
526 N_("Overwrite this target?"), BY - 2, 4, 0}, {
527 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
528 N_("Target date: %s, size %llu"), 6, 4, 0}, {
529 N_("Source date: %s, size %llu"), 5, 4, 0}
530 #else
531 N_("Target date: %s, size %u"), 6, 4, 0}, {
532 N_("Source date: %s, size %u"), 5, 4, 0}
533 #endif
536 #define ADD_RD_BUTTON(i)\
537 add_widget (ui->replace_dlg,\
538 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
539 NORMAL_BUTTON, rd_widgets [i].text, 0))
541 #define ADD_RD_LABEL(ui,i,p1,p2)\
542 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
543 add_widget (ui->replace_dlg,\
544 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
546 static void
547 init_replace (FileOpContext *ctx, enum OperationMode mode)
549 FileOpContextUI *ui;
550 char buffer[BUF_SMALL];
551 const char *title;
552 static int rd_xlen = 60, rd_trunc = X_TRUNC;
554 #ifdef ENABLE_NLS
555 static int i18n_flag;
556 if (!i18n_flag) {
557 int l1, l2, l, row;
558 register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
559 while (i--)
560 rd_widgets[i].text = _(rd_widgets[i].text);
563 * longest of "Overwrite..." labels
564 * (assume "Target date..." are short enough)
566 l1 = max (strlen (rd_widgets[6].text),
567 strlen (rd_widgets[11].text));
569 /* longest of button rows */
570 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
571 for (row = l = l2 = 0; i--;) {
572 if (rd_widgets[i].value != 0) {
573 if (row != rd_widgets[i].ypos) {
574 row = rd_widgets[i].ypos;
575 l2 = max (l2, l);
576 l = 0;
578 l += strlen (rd_widgets[i].text) + 4;
581 l2 = max (l2, l); /* last row */
582 rd_xlen = max (rd_xlen, l1 + l2 + 8);
583 rd_trunc = rd_xlen - 6;
585 /* Now place buttons */
586 l1 += 5; /* start of first button in the row */
587 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
589 for (l = l1, row = 0; --i > 1;) {
590 if (rd_widgets[i].value != 0) {
591 if (row != rd_widgets[i].ypos) {
592 row = rd_widgets[i].ypos;
593 l = l1;
595 rd_widgets[i].xpos = l;
596 l += strlen (rd_widgets[i].text) + 4;
599 /* Abort button is centered */
600 rd_widgets[1].xpos =
601 (rd_xlen - strlen (rd_widgets[1].text) - 3) / 2;
603 #endif /* ENABLE_NLS */
605 ui = ctx->ui;
607 if (mode == Foreground)
608 title = _(" File exists ");
609 else
610 title = _(" Background process: File exists ");
612 /* FIXME - missing help node */
613 ui->replace_dlg =
614 create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
615 title, DLG_CENTER | DLG_REVERSE);
618 ADD_RD_LABEL (ui, 0,
619 name_trunc (ui->replace_filename,
620 rd_trunc - strlen (rd_widgets[0].text)), 0);
621 ADD_RD_BUTTON (1);
623 ADD_RD_BUTTON (2);
624 ADD_RD_BUTTON (3);
625 ADD_RD_BUTTON (4);
626 ADD_RD_BUTTON (5);
627 ADD_RD_LABEL (ui, 6, 0, 0);
629 /* "this target..." widgets */
630 if (!S_ISDIR (ui->d_stat->st_mode)) {
631 if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
632 && (ui->s_stat->st_size > ui->d_stat->st_size))
633 ADD_RD_BUTTON (7); /* reget */
635 ADD_RD_BUTTON (8); /* Overwrite all targets? */
637 ADD_RD_BUTTON (9);
638 ADD_RD_BUTTON (10);
639 ADD_RD_LABEL (ui, 11, 0, 0);
641 ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
642 (off_t) ui->d_stat->st_size);
643 ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
644 (off_t) ui->s_stat->st_size);
647 void
648 file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg)
650 FileOpContextUI *ui;
652 g_return_if_fail (ctx != NULL);
654 if (ctx->ui == NULL)
655 return;
657 ui = ctx->ui;
659 label_set_text (ui->stalled_label, stalled_msg);
662 FileProgressStatus
663 file_progress_real_query_replace (FileOpContext *ctx,
664 enum OperationMode mode, const char *destname,
665 struct stat *_s_stat,
666 struct stat *_d_stat)
668 FileOpContextUI *ui;
670 g_return_val_if_fail (ctx != NULL, FILE_CONT);
671 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
673 ui = ctx->ui;
675 if (ui->replace_result < REPLACE_ALWAYS) {
676 ui->replace_filename = destname;
677 ui->s_stat = _s_stat;
678 ui->d_stat = _d_stat;
679 init_replace (ctx, mode);
680 run_dlg (ui->replace_dlg);
681 ui->replace_result = ui->replace_dlg->ret_value;
682 if (ui->replace_result == B_CANCEL)
683 ui->replace_result = REPLACE_ABORT;
684 destroy_dlg (ui->replace_dlg);
687 switch (ui->replace_result) {
688 case REPLACE_UPDATE:
689 do_refresh ();
690 if (_s_stat->st_mtime > _d_stat->st_mtime)
691 return FILE_CONT;
692 else
693 return FILE_SKIP;
695 case REPLACE_SIZE:
696 do_refresh ();
697 if (_s_stat->st_size == _d_stat->st_size)
698 return FILE_SKIP;
699 else
700 return FILE_CONT;
702 case REPLACE_REGET:
703 /* Careful: we fall through and set do_append */
704 ctx->do_reget = _d_stat->st_size;
706 case REPLACE_APPEND:
707 ctx->do_append = 1;
709 case REPLACE_YES:
710 case REPLACE_ALWAYS:
711 do_refresh ();
712 return FILE_CONT;
713 case REPLACE_NO:
714 case REPLACE_NEVER:
715 do_refresh ();
716 return FILE_SKIP;
717 case REPLACE_ABORT:
718 default:
719 return FILE_ABORT;
723 #define FMDY 13
724 #define FMD_XLEN 64
725 extern int fmd_xlen;
726 static QuickWidget fmd_widgets[] = {
728 #define FMCB0 FMDC
729 #define FMCB12 0
730 #define FMCB11 1
731 /* follow symlinks and preserve Attributes must be the first */
732 {quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
733 0 /* &op_preserve */ , 0, "preserve"},
734 {quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
735 0 /* &file_mask_op_follow_links */ , 0, "follow"},
736 {quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, "to"},
737 {quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
738 0 /* &source_easy_patterns */ , 0, "using-shell"},
739 {quick_input, 3, 64, 3, FMDY, "", 58,
740 0, 0, 0, "input-def"},
741 #define FMDI1 4
742 #define FMDI2 5
743 #define FMDC 3
744 {quick_input, 3, 64, 6, FMDY, "", 58, 0,
745 0, 0, "input2"},
746 #define FMDI0 6
747 {quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, "ql"},
748 #define FMBRGT 7
749 {quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
750 "cancel"},
751 #undef SKIP
752 #ifdef WITH_BACKGROUND
753 # define SKIP 5
754 # define FMCB21 11
755 # define FMCB22 10
756 # define FMBLFT 9
757 # define FMBMID 8
758 {quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
759 "back"},
760 #else /* WITH_BACKGROUND */
761 # define SKIP 4
762 # define FMCB21 10
763 # define FMCB22 9
764 # define FMBLFT 8
765 # undef FMBMID
766 #endif
767 {quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, "ok"},
768 {quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
769 0 /* &file_mask_stable_symlinks */ , 0, "stab-sym"},
770 {quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
772 0 /* &dive_into_subdirs */ , 0, "dive"},
773 NULL_QuickWidget
776 static int
777 is_wildcarded (char *p)
779 for (; *p; p++) {
780 if (*p == '*')
781 return 1;
782 else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
783 return 1;
785 return 0;
788 void
789 fmd_init_i18n (int force)
791 #ifdef ENABLE_NLS
792 static int initialized = FALSE;
793 register int i;
794 int len;
796 if (initialized && !force)
797 return;
799 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
800 op_names[i] = _(op_names[i]);
802 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
803 while (i--)
804 if (fmd_widgets[i].text[0] != '\0')
805 fmd_widgets[i].text = _(fmd_widgets[i].text);
807 len = strlen (fmd_widgets[FMCB11].text)
808 + strlen (fmd_widgets[FMCB21].text) + 15;
809 fmd_xlen = max (fmd_xlen, len);
811 len = strlen (fmd_widgets[FMCB12].text)
812 + strlen (fmd_widgets[FMCB22].text) + 15;
813 fmd_xlen = max (fmd_xlen, len);
815 len = strlen (fmd_widgets[FMBRGT].text)
816 + strlen (fmd_widgets[FMBLFT].text) + 11;
818 #ifdef FMBMID
819 len += strlen (fmd_widgets[FMBMID].text) + 6;
820 #endif
822 fmd_xlen = max (fmd_xlen, len + 4);
824 len = (fmd_xlen - (len + 6)) / 2;
825 i = fmd_widgets[FMBLFT].relative_x = len + 3;
826 i += strlen (fmd_widgets[FMBLFT].text) + 8;
828 #ifdef FMBMID
829 fmd_widgets[FMBMID].relative_x = i;
830 i += strlen (fmd_widgets[FMBMID].text) + 6;
831 #endif
833 fmd_widgets[FMBRGT].relative_x = i;
835 #define chkbox_xpos(i) \
836 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
838 chkbox_xpos (FMCB0);
839 chkbox_xpos (FMCB21);
840 chkbox_xpos (FMCB22);
842 if (fmd_xlen != FMD_XLEN) {
843 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
844 while (i--)
845 fmd_widgets[i].x_divisions = fmd_xlen;
847 fmd_widgets[FMDI1].hotkey_pos =
848 fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
850 #undef chkbox_xpos
852 initialized = TRUE;
853 #endif /* !ENABLE_NLS */
856 char *
857 file_mask_dialog (FileOpContext *ctx, FileOperation operation, const char *text,
858 const char *def_text, int only_one, int *do_background)
860 int source_easy_patterns = easy_patterns;
861 char *source_mask, *orig_mask, *dest_dir, *tmpdest;
862 const char *error;
863 char *def_text_secure;
864 struct stat buf;
865 int val;
866 QuickDialog Quick_input;
868 g_return_val_if_fail (ctx != NULL, NULL);
869 #if 0
870 message (1, __FUNCTION__, "text = `%s' \n def_text = `%s'", text,
871 def_text);
872 #endif
873 fmd_init_i18n (FALSE);
875 /* Set up the result pointers */
877 fmd_widgets[FMCB12].result = &ctx->op_preserve;
878 fmd_widgets[FMCB11].result = &ctx->follow_links;
879 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
880 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
882 /* filter out a possible password from def_text */
883 def_text_secure = strip_password (g_strdup (def_text), 1);
885 /* Create the dialog */
887 ctx->stable_symlinks = 0;
888 fmd_widgets[FMDC].result = &source_easy_patterns;
889 fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
890 Quick_input.xlen = fmd_xlen;
891 Quick_input.xpos = -1;
892 Quick_input.title = op_names[operation];
893 Quick_input.help = "[Mask Copy/Rename]";
894 Quick_input.ylen = FMDY;
895 Quick_input.i18n = 1;
896 Quick_input.widgets = fmd_widgets;
897 fmd_widgets[FMDI0].text = text;
898 fmd_widgets[FMDI2].text = def_text_secure;
899 fmd_widgets[FMDI2].str_result = &dest_dir;
900 fmd_widgets[FMDI1].str_result = &source_mask;
902 *do_background = 0;
903 ask_file_mask:
905 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL) {
906 g_free (def_text_secure);
907 return 0;
909 g_free (def_text_secure);
911 if (ctx->follow_links)
912 ctx->stat_func = mc_stat;
913 else
914 ctx->stat_func = mc_lstat;
916 if (ctx->op_preserve) {
917 ctx->preserve = 1;
918 ctx->umask_kill = 0777777;
919 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
920 } else {
921 int i;
922 ctx->preserve = ctx->preserve_uidgid = 0;
923 i = umask (0);
924 umask (i);
925 ctx->umask_kill = i ^ 0777777;
928 orig_mask = source_mask;
929 if (!dest_dir || !*dest_dir) {
930 g_free (source_mask);
931 return dest_dir;
933 if (source_easy_patterns) {
934 source_easy_patterns = easy_patterns;
935 easy_patterns = 1;
936 source_mask = convert_pattern (source_mask, match_file, 1);
937 easy_patterns = source_easy_patterns;
938 error =
939 re_compile_pattern (source_mask, strlen (source_mask),
940 &ctx->rx);
941 g_free (source_mask);
942 } else
943 error =
944 re_compile_pattern (source_mask, strlen (source_mask),
945 &ctx->rx);
947 if (error) {
948 message (1, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
949 orig_mask, error);
950 g_free (orig_mask);
951 goto ask_file_mask;
953 g_free (orig_mask);
955 tmpdest = dest_dir;
956 dest_dir = tilde_expand(tmpdest);
957 g_free(tmpdest);
959 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
960 if (ctx->dest_mask == NULL)
961 ctx->dest_mask = dest_dir;
962 else
963 ctx->dest_mask++;
964 orig_mask = ctx->dest_mask;
965 if (!*ctx->dest_mask
966 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
967 && (!only_one
968 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
969 || (ctx->dive_into_subdirs
970 && ((!only_one && !is_wildcarded (ctx->dest_mask))
971 || (only_one && !mc_stat (dest_dir, &buf)
972 && S_ISDIR (buf.st_mode)))))
973 ctx->dest_mask = g_strdup ("*");
974 else {
975 ctx->dest_mask = g_strdup (ctx->dest_mask);
976 *orig_mask = 0;
978 if (!*dest_dir) {
979 g_free (dest_dir);
980 dest_dir = g_strdup ("./");
982 if (val == B_USER)
983 *do_background = 1;
984 return dest_dir;