Just a little correction at the it.po file.
[midnight-commander.git] / src / filegui.c
blob4d36808c38659cac8fa1544fc6d9511b270a9041
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>
74 #include "global.h"
75 #include "setup.h" /* verbose */
76 #include "dialog.h" /* do_refresh */
77 #include "color.h" /* dialog_colors */
78 #include "dlg.h" /* B_ENTER */
79 #include "background.h" /* message_3s */
80 #include "widget.h" /* WLabel */
81 #define WANT_WIDGETS
82 #include "main.h" /* the_hint */
83 #include "wtools.h" /* QuickDialog */
84 #include "panel.h" /* cpanel */
85 #include "fileopctx.h" /* FILE_CONT */
86 #include "filegui.h"
87 #include "key.h" /* get_event */
89 /* }}} */
91 /* This structure describes the UI and internal data required by a file
92 * operation context.
94 typedef struct {
95 /* ETA and bps */
97 int showing_eta;
98 int showing_bps;
99 int eta_extra;
101 /* Dialog and widgets for the operation progress window */
103 Dlg_head *op_dlg;
105 WLabel *file_label[2];
106 WLabel *file_string[2];
107 WLabel *progress_label[3];
108 WGauge *progress_gauge[3];
109 WLabel *eta_label;
110 WLabel *bps_label;
111 WLabel *stalled_label;
113 /* Query replace dialog */
115 Dlg_head *replace_dlg;
116 char *replace_filename;
117 int replace_result;
118 struct stat *s_stat, *d_stat;
119 } FileOpContextUI;
122 /* Used to save the hint line */
123 static int last_hint_line;
125 /* File operate window sizes */
126 #define WX 62
127 #define WY 10
128 #define BY 10
129 #define WX_ETA_EXTRA 12
131 #define FCOPY_GAUGE_X 14
132 #define FCOPY_LABEL_X 5
134 /* Used for button result values */
135 enum {
136 REPLACE_YES = B_USER,
137 REPLACE_NO,
138 REPLACE_APPEND,
139 REPLACE_ALWAYS,
140 REPLACE_UPDATE,
141 REPLACE_NEVER,
142 REPLACE_ABORT,
143 REPLACE_SIZE,
144 REPLACE_REGET
145 } FileReplaceCode;
147 static FileProgressStatus
148 check_progress_buttons (FileOpContext *ctx)
150 int c;
151 Gpm_Event event;
152 FileOpContextUI *ui;
154 if (ctx->ui == NULL)
155 return FILE_CONT;
157 ui = ctx->ui;
159 event.x = -1; /* Don't show the GPM cursor */
160 c = get_event (&event, 0, 0);
161 if (c == EV_NONE)
162 return FILE_CONT;
164 /* Reinitialize to avoid old values after events other than
165 selecting a button */
166 ui->op_dlg->ret_value = FILE_CONT;
168 dlg_process_event (ui->op_dlg, c, &event);
169 switch (ui->op_dlg->ret_value) {
170 case FILE_SKIP:
171 return FILE_SKIP;
172 break;
173 case B_CANCEL:
174 case FILE_ABORT:
175 return FILE_ABORT;
176 break;
177 default:
178 return FILE_CONT;
182 /* {{{ File progress display routines */
184 void
185 file_op_context_create_ui (FileOpContext *ctx, FileOperation op,
186 int with_eta)
188 FileOpContextUI *ui;
189 int x_size;
190 int minus;
191 int eta_offset;
192 char *sixty;
193 char *fifteen;
195 g_return_if_fail (ctx != NULL);
196 g_return_if_fail (ctx->ui == NULL);
198 ui = g_new0 (FileOpContextUI, 1);
199 ctx->ui = ui;
201 minus = verbose ? 0 : 3;
202 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
204 sixty = "";
205 fifteen = "";
207 ctx->recursive_result = 0;
209 ui->replace_result = 0;
210 ui->showing_eta = with_eta;
211 ui->showing_bps = with_eta;
212 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
213 x_size = (WX + 4) + ui->eta_extra;
215 ui->op_dlg = create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors,
216 NULL, NULL, op_names[op], DLG_CENTER);
218 last_hint_line = the_hint->widget.y;
219 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
220 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
222 add_widget (ui->op_dlg,
223 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
224 NORMAL_BUTTON, _("&Abort"), 0, 0, "abort"));
225 add_widget (ui->op_dlg,
226 button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
227 NORMAL_BUTTON, _("&Skip"), 0, 0, "skip"));
229 add_widget (ui->op_dlg, ui->progress_gauge[2] =
230 gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0, "g-1"));
231 add_widget (ui->op_dlg, ui->progress_label[2] =
232 label_new (7, FCOPY_LABEL_X, fifteen, "l-1"));
233 add_widget (ui->op_dlg, ui->bps_label =
234 label_new (7, WX, "", "bps-label"));
236 add_widget (ui->op_dlg, ui->progress_gauge[1] =
237 gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0, "g-2"));
238 add_widget (ui->op_dlg, ui->progress_label[1] =
239 label_new (8, FCOPY_LABEL_X, fifteen, "l-2"));
240 add_widget (ui->op_dlg, ui->stalled_label =
241 label_new (8, WX, "", "stalled"));
243 add_widget (ui->op_dlg, ui->progress_gauge[0] =
244 gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0, "g-3"));
245 add_widget (ui->op_dlg, ui->progress_label[0] =
246 label_new (6, FCOPY_LABEL_X, fifteen, "l-3"));
247 add_widget (ui->op_dlg, ui->eta_label =
248 label_new (6, WX, "", "eta_label"));
250 add_widget (ui->op_dlg, ui->file_string[1] =
251 label_new (4, FCOPY_GAUGE_X, sixty, "fs-l-1"));
252 add_widget (ui->op_dlg, ui->file_label[1] =
253 label_new (4, FCOPY_LABEL_X, fifteen, "fs-l-2"));
254 add_widget (ui->op_dlg, ui->file_string[0] =
255 label_new (3, FCOPY_GAUGE_X, sixty, "fs-x-1"));
256 add_widget (ui->op_dlg, ui->file_label[0] =
257 label_new (3, FCOPY_LABEL_X, fifteen, "fs-x-2"));
259 /* We will manage the dialog without any help, that's why
260 we have to call init_dlg */
261 init_dlg (ui->op_dlg);
262 ui->op_dlg->running = 1;
265 void
266 file_op_context_destroy_ui (FileOpContext *ctx)
268 FileOpContextUI *ui;
270 g_return_if_fail (ctx != NULL);
272 if (ctx->ui) {
273 ui = ctx->ui;
275 dlg_run_done (ui->op_dlg);
276 destroy_dlg (ui->op_dlg);
277 g_free (ui);
280 the_hint->widget.y = last_hint_line;
282 ctx->ui = NULL;
285 static FileProgressStatus
286 show_no_bar (FileOpContext *ctx, int n)
288 FileOpContextUI *ui;
290 if (ctx->ui == NULL)
291 return FILE_CONT;
293 ui = ctx->ui;
295 if (n >= 0) {
296 label_set_text (ui->progress_label[n], "");
297 gauge_show (ui->progress_gauge[n], 0);
299 return check_progress_buttons (ctx);
302 static FileProgressStatus
303 show_bar (FileOpContext *ctx, int n, double done, double total)
305 FileOpContextUI *ui;
307 if (ctx->ui == NULL)
308 return FILE_CONT;
310 ui = ctx->ui;
313 * Gauge needs integers, so give it with integers between 0 and 1023.
314 * This precision should be quite reasonable.
316 gauge_set_value (ui->progress_gauge[n], 1024,
317 (int) (1024 * done / total));
318 gauge_show (ui->progress_gauge[n], 1);
319 return check_progress_buttons (ctx);
322 static void
323 file_eta_show (FileOpContext *ctx)
325 int eta_hours, eta_mins, eta_s;
326 char eta_buffer[BUF_TINY];
327 FileOpContextUI *ui;
329 if (ctx->ui == NULL)
330 return;
332 ui = ctx->ui;
334 if (!ui->showing_eta)
335 return;
337 if (ctx->eta_secs > 0.5) {
338 eta_hours = ctx->eta_secs / (60 * 60);
339 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
340 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
341 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
342 eta_hours, eta_mins, eta_s);
343 } else
344 *eta_buffer = 0;
346 label_set_text (ui->eta_label, eta_buffer);
349 static void
350 file_bps_show (FileOpContext *ctx)
352 char bps_buffer[BUF_TINY];
353 FileOpContextUI *ui;
355 if (ctx->ui == NULL)
356 return;
358 ui = ctx->ui;
360 if (!ui->showing_bps)
361 return;
363 if (ctx->bps > 1024 * 1024) {
364 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
365 ctx->bps / (1024 * 1024.0));
366 } else if (ctx->bps > 1024) {
367 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
368 ctx->bps / 1024.0);
369 } else if (ctx->bps > 1) {
370 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
371 ctx->bps);
372 } else
373 *bps_buffer = 0;
375 label_set_text (ui->bps_label, bps_buffer);
378 FileProgressStatus
379 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
381 FileOpContextUI *ui;
383 g_return_val_if_fail (ctx != NULL, FILE_CONT);
385 if (ctx->ui == NULL)
386 return FILE_CONT;
388 ui = ctx->ui;
390 if (!verbose)
391 return check_progress_buttons (ctx);
392 if (total > 0) {
393 label_set_text (ui->progress_label[0], _("File"));
394 file_eta_show (ctx);
395 file_bps_show (ctx);
396 return show_bar (ctx, 0, done, total);
397 } else
398 return show_no_bar (ctx, 0);
401 FileProgressStatus
402 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
404 FileOpContextUI *ui;
406 g_return_val_if_fail (ctx != NULL, FILE_CONT);
408 if (ctx->ui == NULL)
409 return FILE_CONT;
411 ui = ctx->ui;
413 if (!verbose)
414 return check_progress_buttons (ctx);
415 if (total > 0) {
416 label_set_text (ui->progress_label[1], _("Count"));
417 return show_bar (ctx, 1, done, total);
418 } else
419 return show_no_bar (ctx, 1);
422 FileProgressStatus
423 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
425 FileOpContextUI *ui;
427 g_return_val_if_fail (ctx != NULL, FILE_CONT);
429 if (ctx->ui == NULL)
430 return FILE_CONT;
432 ui = ctx->ui;
434 if (!verbose)
435 return check_progress_buttons (ctx);
436 if (total > 0) {
437 label_set_text (ui->progress_label[2], _("Bytes"));
438 return show_bar (ctx, 2, done, total);
439 } else
440 return show_no_bar (ctx, 2);
443 /* }}} */
445 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
447 FileProgressStatus
448 file_progress_show_source (FileOpContext *ctx, char *s)
450 FileOpContextUI *ui;
452 g_return_val_if_fail (ctx != NULL, FILE_CONT);
454 if (ctx->ui == NULL)
455 return FILE_CONT;
457 ui = ctx->ui;
459 if (s != NULL) {
460 #ifdef WITH_FULL_PATHS
461 int i = strlen (cpanel->cwd);
463 /* We remove the full path we have added before */
464 if (!strncmp (s, cpanel->cwd, i)) {
465 if (s[i] == PATH_SEP)
466 s += i + 1;
468 #endif /* WITH_FULL_PATHS */
470 label_set_text (ui->file_label[0], _("Source"));
471 label_set_text (ui->file_string[0], truncFileString (ui, s));
472 return check_progress_buttons (ctx);
473 } else {
474 label_set_text (ui->file_label[0], "");
475 label_set_text (ui->file_string[0], "");
476 return check_progress_buttons (ctx);
480 FileProgressStatus
481 file_progress_show_target (FileOpContext *ctx, char *s)
483 FileOpContextUI *ui;
485 g_return_val_if_fail (ctx != NULL, FILE_CONT);
487 if (ctx->ui == NULL)
488 return FILE_CONT;
490 ui = ctx->ui;
492 if (s != NULL) {
493 label_set_text (ui->file_label[1], _("Target"));
494 label_set_text (ui->file_string[1], truncFileString (ui, s));
495 return check_progress_buttons (ctx);
496 } else {
497 label_set_text (ui->file_label[1], "");
498 label_set_text (ui->file_string[1], "");
499 return check_progress_buttons (ctx);
503 FileProgressStatus
504 file_progress_show_deleting (FileOpContext *ctx, char *s)
506 FileOpContextUI *ui;
508 g_return_val_if_fail (ctx != NULL, FILE_CONT);
510 if (ctx->ui == NULL)
511 return FILE_CONT;
513 ui = ctx->ui;
515 label_set_text (ui->file_label[0], _("Deleting"));
516 label_set_text (ui->file_label[0], truncFileString (ui, s));
517 return check_progress_buttons (ctx);
520 #define X_TRUNC 52
523 * FIXME: probably it is better to replace this with quick dialog machinery,
524 * but actually I'm not familiar with it and have not much time :(
525 * alex
527 static struct {
528 char *text;
529 int ypos, xpos;
530 int value; /* 0 for labels */
531 char *tkname;
532 } rd_widgets[] = {
534 N_("Target file \"%s\" already exists!"), 3, 4, 0, "target-e"}, {
535 N_("&Abort"), BY + 3, 25, REPLACE_ABORT, "abort"}, {
536 N_("If &size differs"), BY + 1, 28, REPLACE_SIZE, "if-size"}, {
537 N_("Non&e"), BY, 47, REPLACE_NEVER, "none"}, {
538 N_("&Update"), BY, 36, REPLACE_UPDATE, "update"}, {
539 N_("A&ll"), BY, 28, REPLACE_ALWAYS, "all"}, {
540 N_("Overwrite all targets?"), BY, 4, 0, "over-label"}, {
541 N_("&Reget"), BY - 1, 28, REPLACE_REGET, "reget"}, {
542 N_("A&ppend"), BY - 2, 45, REPLACE_APPEND, "append"}, {
543 N_("&No"), BY - 2, 37, REPLACE_NO, "no"}, {
544 N_("&Yes"), BY - 2, 28, REPLACE_YES, "yes"}, {
545 N_("Overwrite this target?"), BY - 2, 4, 0, "overlab"}, {
546 N_("Target date: %s, size %d"), 6, 4, 0, "target-date"}, {
547 N_("Source date: %s, size %d"), 5, 4, 0, "source-date"}
550 #define ADD_RD_BUTTON(i)\
551 add_widget (ui->replace_dlg,\
552 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
553 NORMAL_BUTTON, rd_widgets [i].text, 0, 0, rd_widgets [i].tkname))
555 #define ADD_RD_LABEL(ui,i,p1,p2)\
556 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
557 add_widget (ui->replace_dlg,\
558 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer, rd_widgets [i].tkname))
560 static void
561 init_replace (FileOpContext *ctx, enum OperationMode mode)
563 FileOpContextUI *ui;
564 char buffer[BUF_SMALL];
565 char *title;
566 static int rd_xlen = 60, rd_trunc = X_TRUNC;
568 #ifdef ENABLE_NLS
569 static int i18n_flag;
570 if (!i18n_flag) {
571 int l1, l2, l, row;
572 register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
573 while (i--)
574 rd_widgets[i].text = _(rd_widgets[i].text);
577 * longest of "Overwrite..." labels
578 * (assume "Target date..." are short enough)
580 l1 = max (strlen (rd_widgets[6].text),
581 strlen (rd_widgets[11].text));
583 /* longest of button rows */
584 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
585 for (row = l = l2 = 0; i--;) {
586 if (rd_widgets[i].value != 0) {
587 if (row != rd_widgets[i].ypos) {
588 row = rd_widgets[i].ypos;
589 l2 = max (l2, l);
590 l = 0;
592 l += strlen (rd_widgets[i].text) + 4;
595 l2 = max (l2, l); /* last row */
596 rd_xlen = max (rd_xlen, l1 + l2 + 8);
597 rd_trunc = rd_xlen - 6;
599 /* Now place buttons */
600 l1 += 5; /* start of first button in the row */
601 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
603 for (l = l1, row = 0; --i > 1;) {
604 if (rd_widgets[i].value != 0) {
605 if (row != rd_widgets[i].ypos) {
606 row = rd_widgets[i].ypos;
607 l = l1;
609 rd_widgets[i].xpos = l;
610 l += strlen (rd_widgets[i].text) + 4;
613 /* Abort button is centered */
614 rd_widgets[1].xpos =
615 (rd_xlen - strlen (rd_widgets[1].text) - 3) / 2;
617 #endif /* ENABLE_NLS */
619 ui = ctx->ui;
621 if (mode == Foreground)
622 title = _(" File exists ");
623 else
624 title = _(" Background process: File exists ");
626 /* FIXME - missing help node */
627 ui->replace_dlg =
628 create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
629 title, DLG_CENTER);
632 ADD_RD_LABEL (ui, 0,
633 name_trunc (ui->replace_filename,
634 rd_trunc - strlen (rd_widgets[0].text)), 0);
635 ADD_RD_BUTTON (1);
637 ADD_RD_BUTTON (2);
638 ADD_RD_BUTTON (3);
639 ADD_RD_BUTTON (4);
640 ADD_RD_BUTTON (5);
641 ADD_RD_LABEL (ui, 6, 0, 0);
643 /* "this target..." widgets */
644 if (!S_ISDIR (ui->d_stat->st_mode)) {
645 if ((ui->d_stat->st_size
646 && ui->s_stat->st_size > ui->d_stat->st_size))
647 ADD_RD_BUTTON (7);
649 ADD_RD_BUTTON (8);
651 ADD_RD_BUTTON (9);
652 ADD_RD_BUTTON (10);
653 ADD_RD_LABEL (ui, 11, 0, 0);
655 ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
656 (int) ui->d_stat->st_size);
657 ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
658 (int) ui->s_stat->st_size);
661 void
662 file_progress_set_stalled_label (FileOpContext *ctx, char *stalled_msg)
664 FileOpContextUI *ui;
666 g_return_if_fail (ctx != NULL);
668 if (ctx->ui == NULL)
669 return;
671 ui = ctx->ui;
673 label_set_text (ui->stalled_label, stalled_msg);
676 FileProgressStatus
677 file_progress_real_query_replace (FileOpContext *ctx,
678 enum OperationMode mode, char *destname,
679 struct stat *_s_stat,
680 struct stat *_d_stat)
682 FileOpContextUI *ui;
684 g_return_val_if_fail (ctx != NULL, FILE_CONT);
685 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
687 ui = ctx->ui;
689 if (ui->replace_result < REPLACE_ALWAYS) {
690 ui->replace_filename = destname;
691 ui->s_stat = _s_stat;
692 ui->d_stat = _d_stat;
693 init_replace (ctx, mode);
694 run_dlg (ui->replace_dlg);
695 ui->replace_result = ui->replace_dlg->ret_value;
696 if (ui->replace_result == B_CANCEL)
697 ui->replace_result = REPLACE_ABORT;
698 destroy_dlg (ui->replace_dlg);
701 switch (ui->replace_result) {
702 case REPLACE_UPDATE:
703 do_refresh ();
704 if (_s_stat->st_mtime > _d_stat->st_mtime)
705 return FILE_CONT;
706 else
707 return FILE_SKIP;
709 case REPLACE_SIZE:
710 do_refresh ();
711 if (_s_stat->st_size == _d_stat->st_size)
712 return FILE_SKIP;
713 else
714 return FILE_CONT;
716 case REPLACE_REGET:
717 /* Carefull: we fall through and set do_append */
718 ctx->do_reget = _d_stat->st_size;
720 case REPLACE_APPEND:
721 ctx->do_append = 1;
723 case REPLACE_YES:
724 case REPLACE_ALWAYS:
725 do_refresh ();
726 return FILE_CONT;
727 case REPLACE_NO:
728 case REPLACE_NEVER:
729 do_refresh ();
730 return FILE_SKIP;
731 case REPLACE_ABORT:
732 default:
733 return FILE_ABORT;
737 #define FMDY 13
738 #define FMD_XLEN 64
739 extern int fmd_xlen;
740 static QuickWidget fmd_widgets[] = {
742 #define FMCB0 FMDC
743 #define FMCB12 0
744 #define FMCB11 1
745 /* follow symlinks and preserve Attributes must be the first */
746 {quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
747 0 /* &op_preserve */ , 0, "preserve"},
748 {quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
749 0 /* &file_mask_op_follow_links */ , 0, "follow"},
750 {quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, "to"},
751 {quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
752 0 /* &source_easy_patterns */ , 0, "using-shell"},
753 {quick_input, 3, 64, 3, FMDY, "", 58,
754 0, 0, 0, "input-def"},
755 #define FMDI1 4
756 #define FMDI2 5
757 #define FMDC 3
758 {quick_input, 3, 64, 6, FMDY, "", 58, 0,
759 0, 0, "input2"},
760 #define FMDI0 6
761 {quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, "ql"},
762 #define FMBRGT 7
763 {quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
764 "cancel"},
765 #undef SKIP
766 #ifdef WITH_BACKGROUND
767 # define SKIP 5
768 # define FMCB21 11
769 # define FMCB22 10
770 # define FMBLFT 9
771 # define FMBMID 8
772 {quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
773 "back"},
774 #else /* WITH_BACKGROUND */
775 # define SKIP 4
776 # define FMCB21 10
777 # define FMCB22 9
778 # define FMBLFT 8
779 # undef FMBMID
780 #endif
781 {quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, "ok"},
782 {quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
783 0 /* &file_mask_stable_symlinks */ , 0, "stab-sym"},
784 {quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
786 0 /* &dive_into_subdirs */ , 0, "dive"},
790 static int
791 is_wildcarded (char *p)
793 for (; *p; p++) {
794 if (*p == '*')
795 return 1;
796 else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
797 return 1;
799 return 0;
802 void
803 fmd_init_i18n (int force)
805 #ifdef ENABLE_NLS
806 static int initialized = FALSE;
807 register int i;
808 int len;
810 if (initialized && !force)
811 return;
813 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
814 op_names[i] = _(op_names[i]);
816 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
817 while (i--)
818 if (fmd_widgets[i].text[0] != '\0')
819 fmd_widgets[i].text = _(fmd_widgets[i].text);
821 len = strlen (fmd_widgets[FMCB11].text)
822 + strlen (fmd_widgets[FMCB21].text) + 15;
823 fmd_xlen = max (fmd_xlen, len);
825 len = strlen (fmd_widgets[FMCB12].text)
826 + strlen (fmd_widgets[FMCB22].text) + 15;
827 fmd_xlen = max (fmd_xlen, len);
829 len = strlen (fmd_widgets[FMBRGT].text)
830 + strlen (fmd_widgets[FMBLFT].text) + 11;
832 #ifdef FMBMID
833 len += strlen (fmd_widgets[FMBMID].text) + 6;
834 #endif
836 fmd_xlen = max (fmd_xlen, len + 4);
838 len = (fmd_xlen - (len + 6)) / 2;
839 i = fmd_widgets[FMBLFT].relative_x = len + 3;
840 i += strlen (fmd_widgets[FMBLFT].text) + 8;
842 #ifdef FMBMID
843 fmd_widgets[FMBMID].relative_x = i;
844 i += strlen (fmd_widgets[FMBMID].text) + 6;
845 #endif
847 fmd_widgets[FMBRGT].relative_x = i;
849 #define chkbox_xpos(i) \
850 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
852 chkbox_xpos (FMCB0);
853 chkbox_xpos (FMCB21);
854 chkbox_xpos (FMCB22);
856 if (fmd_xlen != FMD_XLEN) {
857 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
858 while (i--)
859 fmd_widgets[i].x_divisions = fmd_xlen;
861 fmd_widgets[FMDI1].hotkey_pos =
862 fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
864 #undef chkbox_xpos
866 initialized = TRUE;
867 #endif /* !ENABLE_NLS */
870 char *
871 file_mask_dialog (FileOpContext *ctx, FileOperation operation, char *text,
872 char *def_text, int only_one, int *do_background)
874 int source_easy_patterns = easy_patterns;
875 char *source_mask, *orig_mask, *dest_dir;
876 const char *error;
877 struct stat buf;
878 int val;
879 QuickDialog Quick_input;
881 g_return_val_if_fail (ctx != NULL, NULL);
882 #if 0
883 message_3s (1, __FUNCTION__, "text = `%s' \n def_text = `%s'", text,
884 def_text);
885 #endif
886 fmd_init_i18n (FALSE);
888 /* Set up the result pointers */
890 fmd_widgets[FMCB12].result = &ctx->op_preserve;
891 fmd_widgets[FMCB11].result = &ctx->follow_links;
892 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
893 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
895 /* Create the dialog */
897 ctx->stable_symlinks = 0;
898 fmd_widgets[FMDC].result = &source_easy_patterns;
899 fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
900 Quick_input.xlen = fmd_xlen;
901 Quick_input.xpos = -1;
902 Quick_input.title = op_names[operation];
903 Quick_input.help = "[Mask Copy/Rename]";
904 Quick_input.ylen = FMDY;
905 Quick_input.i18n = 1;
906 Quick_input.widgets = fmd_widgets;
907 fmd_widgets[FMDI0].text = text;
908 fmd_widgets[FMDI2].text = def_text;
909 fmd_widgets[FMDI2].str_result = &dest_dir;
910 fmd_widgets[FMDI1].str_result = &source_mask;
912 *do_background = 0;
913 ask_file_mask:
915 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
916 return 0;
918 if (ctx->follow_links)
919 ctx->stat_func = (mc_stat_fn) mc_stat;
920 else
921 ctx->stat_func = (mc_stat_fn) mc_lstat;
923 if (ctx->op_preserve) {
924 ctx->preserve = 1;
925 ctx->umask_kill = 0777777;
926 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
927 } else {
928 int i;
929 ctx->preserve = ctx->preserve_uidgid = 0;
930 i = umask (0);
931 umask (i);
932 ctx->umask_kill = i ^ 0777777;
935 orig_mask = source_mask;
936 if (!dest_dir || !*dest_dir) {
937 if (source_mask)
938 g_free (source_mask);
939 return dest_dir;
941 if (source_easy_patterns) {
942 source_easy_patterns = easy_patterns;
943 easy_patterns = 1;
944 source_mask = convert_pattern (source_mask, match_file, 1);
945 easy_patterns = source_easy_patterns;
946 error =
947 re_compile_pattern (source_mask, strlen (source_mask),
948 &ctx->rx);
949 g_free (source_mask);
950 } else
951 error =
952 re_compile_pattern (source_mask, strlen (source_mask),
953 &ctx->rx);
955 if (error) {
956 message_3s (1, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
957 orig_mask, error);
958 if (orig_mask)
959 g_free (orig_mask);
960 goto ask_file_mask;
962 if (orig_mask)
963 g_free (orig_mask);
964 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
965 if (ctx->dest_mask == NULL)
966 ctx->dest_mask = dest_dir;
967 else
968 ctx->dest_mask++;
969 orig_mask = ctx->dest_mask;
970 if (!*ctx->dest_mask
971 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
972 && (!only_one
973 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
974 || (ctx->dive_into_subdirs
975 && ((!only_one && !is_wildcarded (ctx->dest_mask))
976 || (only_one && !mc_stat (dest_dir, &buf)
977 && S_ISDIR (buf.st_mode)))))
978 ctx->dest_mask = g_strdup ("*");
979 else {
980 ctx->dest_mask = g_strdup (ctx->dest_mask);
981 *orig_mask = 0;
983 if (!*dest_dir) {
984 g_free (dest_dir);
985 dest_dir = g_strdup ("./");
987 if (val == B_USER)
988 *do_background = 1;
989 return dest_dir;