updated the .TP cleanup for coherency in the key description pages.
[midnight-commander.git] / src / filegui.c
blob120811a129fce17e46648a877260f083a6d83194
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 "tty.h"
76 #include "eregex.h"
77 #include "setup.h"
78 #include "dialog.h"
79 /* Needed by query_replace */
80 #include "color.h"
81 #include "win.h"
82 #include "dlg.h"
83 #define WANT_WIDGETS
84 #include "widget.h" /* Note the sequence */
85 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
86 #include "layout.h"
87 #include "wtools.h"
89 /* Needed for current_panel, other_panel and WTree */
90 #include "dir.h"
91 #include "panel.h"
92 #include "file.h"
93 #include "filegui.h"
94 #include "fileopctx.h"
95 #include "tree.h"
96 #include "key.h"
97 #include "../vfs/vfs.h"
99 /* }}} */
101 /* This structure describes the UI and internal data required by a file
102 * operation context.
104 typedef struct {
105 /* ETA and bps */
107 int showing_eta;
108 int showing_bps;
109 int eta_extra;
111 /* Dialog and widgets for the operation progress window */
113 Dlg_head *op_dlg;
115 WLabel *file_label[2];
116 WLabel *file_string[2];
117 WLabel *progress_label[3];
118 WGauge *progress_gauge[3];
119 WLabel *eta_label;
120 WLabel *bps_label;
121 WLabel *stalled_label;
123 /* Query replace dialog */
125 Dlg_head *replace_dlg;
126 char *replace_filename;
127 int replace_result;
128 struct stat *s_stat, *d_stat;
129 } FileOpContextUI;
132 /* Used to save the hint line */
133 static int last_hint_line;
135 /* File operate window sizes */
136 #define WX 62
137 #define WY 10
138 #define BY 10
139 #define WX_ETA_EXTRA 12
141 #define FCOPY_GAUGE_X 14
142 #define FCOPY_LABEL_X 5
144 /* Used for button result values */
145 enum {
146 REPLACE_YES = B_USER,
147 REPLACE_NO,
148 REPLACE_APPEND,
149 REPLACE_ALWAYS,
150 REPLACE_UPDATE,
151 REPLACE_NEVER,
152 REPLACE_ABORT,
153 REPLACE_SIZE,
154 REPLACE_REGET
155 } FileReplaceCode;
157 static FileProgressStatus
158 check_progress_buttons (FileOpContext *ctx)
160 int c;
161 Gpm_Event event;
162 FileOpContextUI *ui;
164 if (ctx->ui == NULL)
165 return FILE_CONT;
167 ui = ctx->ui;
169 event.x = -1; /* Don't show the GPM cursor */
170 c = get_event (&event, 0, 0);
171 if (c == EV_NONE)
172 return FILE_CONT;
174 /* Reinitialize to avoid old values after events other than
175 selecting a button */
176 ui->op_dlg->ret_value = FILE_CONT;
178 dlg_process_event (ui->op_dlg, c, &event);
179 switch (ui->op_dlg->ret_value) {
180 case FILE_SKIP:
181 return FILE_SKIP;
182 break;
183 case B_CANCEL:
184 case FILE_ABORT:
185 return FILE_ABORT;
186 break;
187 default:
188 return FILE_CONT;
192 /* {{{ File progress display routines */
194 void
195 file_op_context_create_ui (FileOpContext *ctx, FileOperation op,
196 int with_eta)
198 FileOpContextUI *ui;
199 int x_size;
200 int minus;
201 int eta_offset;
202 char *sixty;
203 char *fifteen;
205 g_return_if_fail (ctx != NULL);
206 g_return_if_fail (ctx->ui == NULL);
208 ui = g_new0 (FileOpContextUI, 1);
209 ctx->ui = ui;
211 minus = verbose ? 0 : 3;
212 eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
214 sixty = "";
215 fifteen = "";
217 ctx->recursive_result = 0;
219 ui->replace_result = 0;
220 ui->showing_eta = with_eta;
221 ui->showing_bps = with_eta;
222 ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
223 x_size = (WX + 4) + ui->eta_extra;
225 ui->op_dlg = create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors,
226 NULL, NULL, op_names[op], DLG_CENTER);
228 last_hint_line = the_hint->widget.y;
229 if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
230 the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
232 add_widget (ui->op_dlg,
233 button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
234 NORMAL_BUTTON, _("&Abort"), 0, 0, "abort"));
235 add_widget (ui->op_dlg,
236 button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
237 NORMAL_BUTTON, _("&Skip"), 0, 0, "skip"));
239 add_widget (ui->op_dlg, ui->progress_gauge[2] =
240 gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0, "g-1"));
241 add_widget (ui->op_dlg, ui->progress_label[2] =
242 label_new (7, FCOPY_LABEL_X, fifteen, "l-1"));
243 add_widget (ui->op_dlg, ui->bps_label =
244 label_new (7, WX, "", "bps-label"));
246 add_widget (ui->op_dlg, ui->progress_gauge[1] =
247 gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0, "g-2"));
248 add_widget (ui->op_dlg, ui->progress_label[1] =
249 label_new (8, FCOPY_LABEL_X, fifteen, "l-2"));
250 add_widget (ui->op_dlg, ui->stalled_label =
251 label_new (8, WX, "", "stalled"));
253 add_widget (ui->op_dlg, ui->progress_gauge[0] =
254 gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0, "g-3"));
255 add_widget (ui->op_dlg, ui->progress_label[0] =
256 label_new (6, FCOPY_LABEL_X, fifteen, "l-3"));
257 add_widget (ui->op_dlg, ui->eta_label =
258 label_new (6, WX, "", "eta_label"));
260 add_widget (ui->op_dlg, ui->file_string[1] =
261 label_new (4, FCOPY_GAUGE_X, sixty, "fs-l-1"));
262 add_widget (ui->op_dlg, ui->file_label[1] =
263 label_new (4, FCOPY_LABEL_X, fifteen, "fs-l-2"));
264 add_widget (ui->op_dlg, ui->file_string[0] =
265 label_new (3, FCOPY_GAUGE_X, sixty, "fs-x-1"));
266 add_widget (ui->op_dlg, ui->file_label[0] =
267 label_new (3, FCOPY_LABEL_X, fifteen, "fs-x-2"));
269 /* We will manage the dialog without any help, that's why
270 we have to call init_dlg */
271 init_dlg (ui->op_dlg);
272 ui->op_dlg->running = 1;
275 void
276 file_op_context_destroy_ui (FileOpContext *ctx)
278 FileOpContextUI *ui;
280 g_return_if_fail (ctx != NULL);
282 if (ctx->ui) {
283 ui = ctx->ui;
285 dlg_run_done (ui->op_dlg);
286 destroy_dlg (ui->op_dlg);
287 g_free (ui);
290 the_hint->widget.y = last_hint_line;
292 ctx->ui = NULL;
295 static FileProgressStatus
296 show_no_bar (FileOpContext *ctx, int n)
298 FileOpContextUI *ui;
300 if (ctx->ui == NULL)
301 return FILE_CONT;
303 ui = ctx->ui;
305 if (n >= 0) {
306 label_set_text (ui->progress_label[n], "");
307 gauge_show (ui->progress_gauge[n], 0);
309 return check_progress_buttons (ctx);
312 static FileProgressStatus
313 show_bar (FileOpContext *ctx, int n, double done, double total)
315 FileOpContextUI *ui;
317 if (ctx->ui == NULL)
318 return FILE_CONT;
320 ui = ctx->ui;
323 * Gauge needs integers, so give it with integers between 0 and 1023.
324 * This precision should be quite reasonable.
326 gauge_set_value (ui->progress_gauge[n], 1024,
327 (int) (1024 * done / total));
328 gauge_show (ui->progress_gauge[n], 1);
329 return check_progress_buttons (ctx);
332 static void
333 file_eta_show (FileOpContext *ctx)
335 int eta_hours, eta_mins, eta_s;
336 char eta_buffer[BUF_TINY];
337 FileOpContextUI *ui;
339 if (ctx->ui == NULL)
340 return;
342 ui = ctx->ui;
344 if (!ui->showing_eta)
345 return;
347 if (ctx->eta_secs > 0.5) {
348 eta_hours = ctx->eta_secs / (60 * 60);
349 eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
350 eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
351 g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
352 eta_hours, eta_mins, eta_s);
353 } else
354 *eta_buffer = 0;
356 label_set_text (ui->eta_label, eta_buffer);
359 static void
360 file_bps_show (FileOpContext *ctx)
362 char bps_buffer[BUF_TINY];
363 FileOpContextUI *ui;
365 if (ctx->ui == NULL)
366 return;
368 ui = ctx->ui;
370 if (!ui->showing_bps)
371 return;
373 if (ctx->bps > 1024 * 1024) {
374 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
375 ctx->bps / (1024 * 1024.0));
376 } else if (ctx->bps > 1024) {
377 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
378 ctx->bps / 1024.0);
379 } else if (ctx->bps > 1) {
380 g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
381 ctx->bps);
382 } else
383 *bps_buffer = 0;
385 label_set_text (ui->bps_label, bps_buffer);
388 FileProgressStatus
389 file_progress_show (FileOpContext *ctx, off_t done, off_t total)
391 FileOpContextUI *ui;
393 g_return_val_if_fail (ctx != NULL, FILE_CONT);
395 if (ctx->ui == NULL)
396 return FILE_CONT;
398 ui = ctx->ui;
400 if (!verbose)
401 return check_progress_buttons (ctx);
402 if (total > 0) {
403 label_set_text (ui->progress_label[0], _("File"));
404 file_eta_show (ctx);
405 file_bps_show (ctx);
406 return show_bar (ctx, 0, done, total);
407 } else
408 return show_no_bar (ctx, 0);
411 FileProgressStatus
412 file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
414 FileOpContextUI *ui;
416 g_return_val_if_fail (ctx != NULL, FILE_CONT);
418 if (ctx->ui == NULL)
419 return FILE_CONT;
421 ui = ctx->ui;
423 if (!verbose)
424 return check_progress_buttons (ctx);
425 if (total > 0) {
426 label_set_text (ui->progress_label[1], _("Count"));
427 return show_bar (ctx, 1, done, total);
428 } else
429 return show_no_bar (ctx, 1);
432 FileProgressStatus
433 file_progress_show_bytes (FileOpContext *ctx, double done, double total)
435 FileOpContextUI *ui;
437 g_return_val_if_fail (ctx != NULL, FILE_CONT);
439 if (ctx->ui == NULL)
440 return FILE_CONT;
442 ui = ctx->ui;
444 if (!verbose)
445 return check_progress_buttons (ctx);
446 if (total > 0) {
447 label_set_text (ui->progress_label[2], _("Bytes"));
448 return show_bar (ctx, 2, done, total);
449 } else
450 return show_no_bar (ctx, 2);
453 /* }}} */
455 #define truncFileString(ui, s) name_trunc (s, ui->eta_extra + 47)
457 FileProgressStatus
458 file_progress_show_source (FileOpContext *ctx, char *s)
460 FileOpContextUI *ui;
462 g_return_val_if_fail (ctx != NULL, FILE_CONT);
464 if (ctx->ui == NULL)
465 return FILE_CONT;
467 ui = ctx->ui;
469 if (s != NULL) {
470 #ifdef WITH_FULL_PATHS
471 int i = strlen (cpanel->cwd);
473 /* We remove the full path we have added before */
474 if (!strncmp (s, cpanel->cwd, i)) {
475 if (s[i] == PATH_SEP)
476 s += i + 1;
478 #endif /* WITH_FULL_PATHS */
480 label_set_text (ui->file_label[0], _("Source"));
481 label_set_text (ui->file_string[0], truncFileString (ui, s));
482 return check_progress_buttons (ctx);
483 } else {
484 label_set_text (ui->file_label[0], "");
485 label_set_text (ui->file_string[0], "");
486 return check_progress_buttons (ctx);
490 FileProgressStatus
491 file_progress_show_target (FileOpContext *ctx, char *s)
493 FileOpContextUI *ui;
495 g_return_val_if_fail (ctx != NULL, FILE_CONT);
497 if (ctx->ui == NULL)
498 return FILE_CONT;
500 ui = ctx->ui;
502 if (s != NULL) {
503 label_set_text (ui->file_label[1], _("Target"));
504 label_set_text (ui->file_string[1], truncFileString (ui, s));
505 return check_progress_buttons (ctx);
506 } else {
507 label_set_text (ui->file_label[1], "");
508 label_set_text (ui->file_string[1], "");
509 return check_progress_buttons (ctx);
513 FileProgressStatus
514 file_progress_show_deleting (FileOpContext *ctx, char *s)
516 FileOpContextUI *ui;
518 g_return_val_if_fail (ctx != NULL, FILE_CONT);
520 if (ctx->ui == NULL)
521 return FILE_CONT;
523 ui = ctx->ui;
525 label_set_text (ui->file_label[0], _("Deleting"));
526 label_set_text (ui->file_label[0], truncFileString (ui, s));
527 return check_progress_buttons (ctx);
530 #define X_TRUNC 52
533 * FIXME: probably it is better to replace this with quick dialog machinery,
534 * but actually I'm not familiar with it and have not much time :(
535 * alex
537 static struct {
538 char *text;
539 int ypos, xpos;
540 int value; /* 0 for labels */
541 char *tkname;
542 } rd_widgets[] = {
544 N_("Target file \"%s\" already exists!"), 3, 4, 0, "target-e"}, {
545 N_("&Abort"), BY + 3, 25, REPLACE_ABORT, "abort"}, {
546 N_("If &size differs"), BY + 1, 28, REPLACE_SIZE, "if-size"}, {
547 N_("Non&e"), BY, 47, REPLACE_NEVER, "none"}, {
548 N_("&Update"), BY, 36, REPLACE_UPDATE, "update"}, {
549 N_("A&ll"), BY, 28, REPLACE_ALWAYS, "all"}, {
550 N_("Overwrite all targets?"), BY, 4, 0, "over-label"}, {
551 N_("&Reget"), BY - 1, 28, REPLACE_REGET, "reget"}, {
552 N_("A&ppend"), BY - 2, 45, REPLACE_APPEND, "append"}, {
553 N_("&No"), BY - 2, 37, REPLACE_NO, "no"}, {
554 N_("&Yes"), BY - 2, 28, REPLACE_YES, "yes"}, {
555 N_("Overwrite this target?"), BY - 2, 4, 0, "overlab"}, {
556 N_("Target date: %s, size %d"), 6, 4, 0, "target-date"}, {
557 N_("Source date: %s, size %d"), 5, 4, 0, "source-date"}
560 #define ADD_RD_BUTTON(i)\
561 add_widget (ui->replace_dlg,\
562 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
563 NORMAL_BUTTON, rd_widgets [i].text, 0, 0, rd_widgets [i].tkname))
565 #define ADD_RD_LABEL(ui,i,p1,p2)\
566 g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
567 add_widget (ui->replace_dlg,\
568 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer, rd_widgets [i].tkname))
570 static void
571 init_replace (FileOpContext *ctx, enum OperationMode mode)
573 FileOpContextUI *ui;
574 char buffer[BUF_SMALL];
575 char *title;
576 static int rd_xlen = 60, rd_trunc = X_TRUNC;
578 #ifdef ENABLE_NLS
579 static int i18n_flag;
580 if (!i18n_flag) {
581 int l1, l2, l, row;
582 register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
583 while (i--)
584 rd_widgets[i].text = _(rd_widgets[i].text);
587 * longest of "Overwrite..." labels
588 * (assume "Target date..." are short enough)
590 l1 = max (strlen (rd_widgets[6].text),
591 strlen (rd_widgets[11].text));
593 /* longest of button rows */
594 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
595 for (row = l = l2 = 0; i--;) {
596 if (rd_widgets[i].value != 0) {
597 if (row != rd_widgets[i].ypos) {
598 row = rd_widgets[i].ypos;
599 l2 = max (l2, l);
600 l = 0;
602 l += strlen (rd_widgets[i].text) + 4;
605 l2 = max (l2, l); /* last row */
606 rd_xlen = max (rd_xlen, l1 + l2 + 8);
607 rd_trunc = rd_xlen - 6;
609 /* Now place buttons */
610 l1 += 5; /* start of first button in the row */
611 i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
613 for (l = l1, row = 0; --i > 1;) {
614 if (rd_widgets[i].value != 0) {
615 if (row != rd_widgets[i].ypos) {
616 row = rd_widgets[i].ypos;
617 l = l1;
619 rd_widgets[i].xpos = l;
620 l += strlen (rd_widgets[i].text) + 4;
623 /* Abort button is centered */
624 rd_widgets[1].xpos =
625 (rd_xlen - strlen (rd_widgets[1].text) - 3) / 2;
627 #endif /* ENABLE_NLS */
629 ui = ctx->ui;
631 if (mode == Foreground)
632 title = _(" File exists ");
633 else
634 title = _(" Background process: File exists ");
636 /* FIXME - missing help node */
637 ui->replace_dlg =
638 create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
639 title, DLG_CENTER);
642 ADD_RD_LABEL (ui, 0,
643 name_trunc (ui->replace_filename,
644 rd_trunc - strlen (rd_widgets[0].text)), 0);
645 ADD_RD_BUTTON (1);
647 ADD_RD_BUTTON (2);
648 ADD_RD_BUTTON (3);
649 ADD_RD_BUTTON (4);
650 ADD_RD_BUTTON (5);
651 ADD_RD_LABEL (ui, 6, 0, 0);
653 /* "this target..." widgets */
654 if (!S_ISDIR (ui->d_stat->st_mode)) {
655 if ((ui->d_stat->st_size
656 && ui->s_stat->st_size > ui->d_stat->st_size))
657 ADD_RD_BUTTON (7);
659 ADD_RD_BUTTON (8);
661 ADD_RD_BUTTON (9);
662 ADD_RD_BUTTON (10);
663 ADD_RD_LABEL (ui, 11, 0, 0);
665 ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
666 (int) ui->d_stat->st_size);
667 ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
668 (int) ui->s_stat->st_size);
671 void
672 file_progress_set_stalled_label (FileOpContext *ctx, char *stalled_msg)
674 FileOpContextUI *ui;
676 g_return_if_fail (ctx != NULL);
678 if (ctx->ui == NULL)
679 return;
681 ui = ctx->ui;
683 label_set_text (ui->stalled_label, stalled_msg);
686 FileProgressStatus
687 file_progress_real_query_replace (FileOpContext *ctx,
688 enum OperationMode mode, char *destname,
689 struct stat *_s_stat,
690 struct stat *_d_stat)
692 FileOpContextUI *ui;
694 g_return_val_if_fail (ctx != NULL, FILE_CONT);
695 g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
697 ui = ctx->ui;
699 if (ui->replace_result < REPLACE_ALWAYS) {
700 ui->replace_filename = destname;
701 ui->s_stat = _s_stat;
702 ui->d_stat = _d_stat;
703 init_replace (ctx, mode);
704 run_dlg (ui->replace_dlg);
705 ui->replace_result = ui->replace_dlg->ret_value;
706 if (ui->replace_result == B_CANCEL)
707 ui->replace_result = REPLACE_ABORT;
708 destroy_dlg (ui->replace_dlg);
711 switch (ui->replace_result) {
712 case REPLACE_UPDATE:
713 do_refresh ();
714 if (_s_stat->st_mtime > _d_stat->st_mtime)
715 return FILE_CONT;
716 else
717 return FILE_SKIP;
719 case REPLACE_SIZE:
720 do_refresh ();
721 if (_s_stat->st_size == _d_stat->st_size)
722 return FILE_SKIP;
723 else
724 return FILE_CONT;
726 case REPLACE_REGET:
727 /* Carefull: we fall through and set do_append */
728 ctx->do_reget = _d_stat->st_size;
730 case REPLACE_APPEND:
731 ctx->do_append = 1;
733 case REPLACE_YES:
734 case REPLACE_ALWAYS:
735 do_refresh ();
736 return FILE_CONT;
737 case REPLACE_NO:
738 case REPLACE_NEVER:
739 do_refresh ();
740 return FILE_SKIP;
741 case REPLACE_ABORT:
742 default:
743 return FILE_ABORT;
747 #define FMDY 13
748 #define FMD_XLEN 64
749 extern int fmd_xlen;
750 static QuickWidget fmd_widgets[] = {
752 #define FMCB0 FMDC
753 #define FMCB12 0
754 #define FMCB11 1
755 /* follow symlinks and preserve Attributes must be the first */
756 {quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
757 0 /* &op_preserve */ , 0, "preserve"},
758 {quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
759 0 /* &file_mask_op_follow_links */ , 0, "follow"},
760 {quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, "to"},
761 {quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
762 0 /* &source_easy_patterns */ , 0, "using-shell"},
763 {quick_input, 3, 64, 3, FMDY, "", 58,
764 0, 0, 0, "input-def"},
765 #define FMDI1 4
766 #define FMDI2 5
767 #define FMDC 3
768 {quick_input, 3, 64, 6, FMDY, "", 58, 0,
769 0, 0, "input2"},
770 #define FMDI0 6
771 {quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, "ql"},
772 #define FMBRGT 7
773 {quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
774 "cancel"},
775 #undef SKIP
776 #ifdef WITH_BACKGROUND
777 # define SKIP 5
778 # define FMCB21 11
779 # define FMCB22 10
780 # define FMBLFT 9
781 # define FMBMID 8
782 {quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
783 "back"},
784 #else /* WITH_BACKGROUND */
785 # define SKIP 4
786 # define FMCB21 10
787 # define FMCB22 9
788 # define FMBLFT 8
789 # undef FMBMID
790 #endif
791 {quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, "ok"},
792 {quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
793 0 /* &file_mask_stable_symlinks */ , 0, "stab-sym"},
794 {quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
796 0 /* &dive_into_subdirs */ , 0, "dive"},
800 void
801 fmd_init_i18n (int force)
803 #ifdef ENABLE_NLS
804 static int initialized = FALSE;
805 register int i;
806 int len;
808 if (initialized && !force)
809 return;
811 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
812 op_names[i] = _(op_names[i]);
814 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
815 while (i--)
816 if (fmd_widgets[i].text[0] != '\0')
817 fmd_widgets[i].text = _(fmd_widgets[i].text);
819 len = strlen (fmd_widgets[FMCB11].text)
820 + strlen (fmd_widgets[FMCB21].text) + 15;
821 fmd_xlen = max (fmd_xlen, len);
823 len = strlen (fmd_widgets[FMCB12].text)
824 + strlen (fmd_widgets[FMCB22].text) + 15;
825 fmd_xlen = max (fmd_xlen, len);
827 len = strlen (fmd_widgets[FMBRGT].text)
828 + strlen (fmd_widgets[FMBLFT].text) + 11;
830 #ifdef FMBMID
831 len += strlen (fmd_widgets[FMBMID].text) + 6;
832 #endif
834 fmd_xlen = max (fmd_xlen, len + 4);
836 len = (fmd_xlen - (len + 6)) / 2;
837 i = fmd_widgets[FMBLFT].relative_x = len + 3;
838 i += strlen (fmd_widgets[FMBLFT].text) + 8;
840 #ifdef FMBMID
841 fmd_widgets[FMBMID].relative_x = i;
842 i += strlen (fmd_widgets[FMBMID].text) + 6;
843 #endif
845 fmd_widgets[FMBRGT].relative_x = i;
847 #define chkbox_xpos(i) \
848 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
850 chkbox_xpos (FMCB0);
851 chkbox_xpos (FMCB21);
852 chkbox_xpos (FMCB22);
854 if (fmd_xlen != FMD_XLEN) {
855 i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
856 while (i--)
857 fmd_widgets[i].x_divisions = fmd_xlen;
859 fmd_widgets[FMDI1].hotkey_pos =
860 fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
862 #undef chkbox_xpos
864 initialized = TRUE;
865 #endif /* !ENABLE_NLS */
868 char *
869 file_mask_dialog (FileOpContext *ctx, FileOperation operation, char *text,
870 char *def_text, int only_one, int *do_background)
872 int source_easy_patterns = easy_patterns;
873 char *source_mask, *orig_mask, *dest_dir;
874 const char *error;
875 struct stat buf;
876 int val;
877 QuickDialog Quick_input;
879 g_return_val_if_fail (ctx != NULL, NULL);
880 #if 0
881 message_3s (1, __FUNCTION__, "text = `%s' \n def_text = `%s'", text,
882 def_text);
883 #endif
884 fmd_init_i18n (FALSE);
886 /* Set up the result pointers */
888 fmd_widgets[FMCB12].result = &ctx->op_preserve;
889 fmd_widgets[FMCB11].result = &ctx->follow_links;
890 fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
891 fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
893 /* Create the dialog */
895 ctx->stable_symlinks = 0;
896 fmd_widgets[FMDC].result = &source_easy_patterns;
897 fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
898 Quick_input.xlen = fmd_xlen;
899 Quick_input.xpos = -1;
900 Quick_input.title = op_names[operation];
901 Quick_input.help = "[Mask Copy/Rename]";
902 Quick_input.ylen = FMDY;
903 Quick_input.i18n = 1;
904 Quick_input.widgets = fmd_widgets;
905 fmd_widgets[FMDI0].text = text;
906 fmd_widgets[FMDI2].text = def_text;
907 fmd_widgets[FMDI2].str_result = &dest_dir;
908 fmd_widgets[FMDI1].str_result = &source_mask;
910 *do_background = 0;
911 ask_file_mask:
913 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
914 return 0;
916 if (ctx->follow_links)
917 ctx->stat_func = (mc_stat_fn) mc_stat;
918 else
919 ctx->stat_func = (mc_stat_fn) mc_lstat;
921 if (ctx->op_preserve) {
922 ctx->preserve = 1;
923 ctx->umask_kill = 0777777;
924 ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
925 } else {
926 int i;
927 ctx->preserve = ctx->preserve_uidgid = 0;
928 i = umask (0);
929 umask (i);
930 ctx->umask_kill = i ^ 0777777;
933 orig_mask = source_mask;
934 if (!dest_dir || !*dest_dir) {
935 if (source_mask)
936 g_free (source_mask);
937 return dest_dir;
939 if (source_easy_patterns) {
940 source_easy_patterns = easy_patterns;
941 easy_patterns = 1;
942 source_mask = convert_pattern (source_mask, match_file, 1);
943 easy_patterns = source_easy_patterns;
944 error =
945 re_compile_pattern (source_mask, strlen (source_mask),
946 &ctx->rx);
947 g_free (source_mask);
948 } else
949 error =
950 re_compile_pattern (source_mask, strlen (source_mask),
951 &ctx->rx);
953 if (error) {
954 message_3s (1, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
955 orig_mask, error);
956 if (orig_mask)
957 g_free (orig_mask);
958 goto ask_file_mask;
960 if (orig_mask)
961 g_free (orig_mask);
962 ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
963 if (ctx->dest_mask == NULL)
964 ctx->dest_mask = dest_dir;
965 else
966 ctx->dest_mask++;
967 orig_mask = ctx->dest_mask;
968 if (!*ctx->dest_mask
969 || (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
970 && (!only_one
971 || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
972 || (ctx->dive_into_subdirs
973 && ((!only_one && !is_wildcarded (ctx->dest_mask))
974 || (only_one && !mc_stat (dest_dir, &buf)
975 && S_ISDIR (buf.st_mode)))))
976 ctx->dest_mask = g_strdup ("*");
977 else {
978 ctx->dest_mask = g_strdup (ctx->dest_mask);
979 *orig_mask = 0;
981 if (!*dest_dir) {
982 g_free (dest_dir);
983 dest_dir = g_strdup ("./");
985 if (val == B_USER)
986 *do_background = 1;
987 return dest_dir;