mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-23 04:46:55 +03:00
3b5e2c052f
resolve conflicts: configure.ac src/complete.c src/file.c src/layout.c src/main.c src/menu.c src/screen.c src/util.c src/widget.c src/widget.h
987 lines
25 KiB
C
987 lines
25 KiB
C
/* File management GUI for the text mode edition
|
|
*
|
|
* Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
|
|
* 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
|
*
|
|
* Written by: 1994, 1995 Janne Kukonlehto
|
|
* 1994, 1995 Fred Leeflang
|
|
* 1994, 1995, 1996 Miguel de Icaza
|
|
* 1995, 1996 Jakub Jelinek
|
|
* 1997 Norbert Warmuth
|
|
* 1998 Pavel Machek
|
|
*
|
|
* The copy code was based in GNU's cp, and was written by:
|
|
* Torbjorn Granlund, David MacKenzie, and Jim Meyering.
|
|
*
|
|
* The move code was based in GNU's mv, and was written by:
|
|
* Mike Parker and David MacKenzie.
|
|
*
|
|
* Janne Kukonlehto added much error recovery to them for being used
|
|
* in an interactive program.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* Please note that all dialogs used here must be safe for background
|
|
* operations.
|
|
*/
|
|
|
|
|
|
/* {{{ Include files */
|
|
|
|
#include <config.h>
|
|
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "global.h"
|
|
#include "setup.h" /* verbose */
|
|
#include "dialog.h" /* do_refresh() */
|
|
#include "color.h" /* dialog_colors */
|
|
#include "widget.h" /* WLabel */
|
|
#include "main-widgets.h"
|
|
#include "main.h" /* the_hint */
|
|
#include "wtools.h" /* QuickDialog */
|
|
#include "panel.h" /* current_panel */
|
|
#include "fileopctx.h" /* FILE_CONT */
|
|
#include "filegui.h"
|
|
#include "key.h" /* get_event */
|
|
#include "util.h" /* strip_password() */
|
|
#include "strutil.h"
|
|
|
|
/* }}} */
|
|
|
|
/* Hack: the vfs code should not rely on this */
|
|
#define WITH_FULL_PATHS 1
|
|
|
|
/* This structure describes the UI and internal data required by a file
|
|
* operation context.
|
|
*/
|
|
typedef struct {
|
|
/* ETA and bps */
|
|
|
|
int showing_eta;
|
|
int showing_bps;
|
|
int eta_extra;
|
|
|
|
/* Dialog and widgets for the operation progress window */
|
|
|
|
Dlg_head *op_dlg;
|
|
|
|
WLabel *file_label[2];
|
|
WLabel *file_string[2];
|
|
WLabel *progress_label[3];
|
|
WGauge *progress_gauge[3];
|
|
WLabel *eta_label;
|
|
WLabel *bps_label;
|
|
WLabel *stalled_label;
|
|
|
|
/* Query replace dialog */
|
|
|
|
Dlg_head *replace_dlg;
|
|
const char *replace_filename;
|
|
int replace_result;
|
|
struct stat *s_stat, *d_stat;
|
|
} FileOpContextUI;
|
|
|
|
|
|
/* Used to save the hint line */
|
|
static int last_hint_line;
|
|
|
|
/* File operate window sizes */
|
|
#define WX 62
|
|
#define WY 10
|
|
#define BY 10
|
|
#define WX_ETA_EXTRA 12
|
|
|
|
#define FCOPY_GAUGE_X 14
|
|
#define FCOPY_LABEL_X 5
|
|
|
|
/* Used for button result values */
|
|
enum {
|
|
REPLACE_YES = B_USER,
|
|
REPLACE_NO,
|
|
REPLACE_APPEND,
|
|
REPLACE_ALWAYS,
|
|
REPLACE_UPDATE,
|
|
REPLACE_NEVER,
|
|
REPLACE_ABORT,
|
|
REPLACE_SIZE,
|
|
REPLACE_REGET
|
|
};
|
|
|
|
static FileProgressStatus
|
|
check_progress_buttons (FileOpContext *ctx)
|
|
{
|
|
int c;
|
|
Gpm_Event event;
|
|
FileOpContextUI *ui;
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
event.x = -1; /* Don't show the GPM cursor */
|
|
c = get_event (&event, 0, 0);
|
|
if (c == EV_NONE)
|
|
return FILE_CONT;
|
|
|
|
/* Reinitialize to avoid old values after events other than
|
|
selecting a button */
|
|
ui->op_dlg->ret_value = FILE_CONT;
|
|
|
|
dlg_process_event (ui->op_dlg, c, &event);
|
|
switch (ui->op_dlg->ret_value) {
|
|
case FILE_SKIP:
|
|
return FILE_SKIP;
|
|
break;
|
|
case B_CANCEL:
|
|
case FILE_ABORT:
|
|
return FILE_ABORT;
|
|
break;
|
|
default:
|
|
return FILE_CONT;
|
|
}
|
|
}
|
|
|
|
/* {{{ File progress display routines */
|
|
|
|
void
|
|
file_op_context_create_ui (FileOpContext *ctx, int with_eta)
|
|
{
|
|
FileOpContextUI *ui;
|
|
int x_size;
|
|
int minus;
|
|
int eta_offset;
|
|
const char *sixty;
|
|
const char *fifteen;
|
|
|
|
g_return_if_fail (ctx != NULL);
|
|
g_return_if_fail (ctx->ui == NULL);
|
|
|
|
ui = g_new0 (FileOpContextUI, 1);
|
|
ctx->ui = ui;
|
|
|
|
minus = verbose ? 0 : 3;
|
|
eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
|
|
|
|
sixty = "";
|
|
fifteen = "";
|
|
|
|
ctx->recursive_result = 0;
|
|
|
|
ui->replace_result = 0;
|
|
ui->showing_eta = with_eta;
|
|
ui->showing_bps = with_eta;
|
|
ui->eta_extra = with_eta ? WX_ETA_EXTRA : 0;
|
|
x_size = (WX + 4) + ui->eta_extra;
|
|
|
|
ui->op_dlg =
|
|
create_dlg (0, 0, WY - minus + 4, x_size, dialog_colors, NULL,
|
|
NULL, op_names[ctx->operation],
|
|
DLG_CENTER | DLG_REVERSE);
|
|
|
|
last_hint_line = the_hint->widget.y;
|
|
if ((ui->op_dlg->y + ui->op_dlg->lines) > last_hint_line)
|
|
the_hint->widget.y = ui->op_dlg->y + ui->op_dlg->lines + 1;
|
|
|
|
add_widget (ui->op_dlg,
|
|
button_new (BY - minus, WX - 19 + eta_offset, FILE_ABORT,
|
|
NORMAL_BUTTON, _("&Abort"), 0));
|
|
add_widget (ui->op_dlg,
|
|
button_new (BY - minus, 14 + eta_offset, FILE_SKIP,
|
|
NORMAL_BUTTON, _("&Skip"), 0));
|
|
|
|
add_widget (ui->op_dlg, ui->progress_gauge[2] =
|
|
gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0));
|
|
add_widget (ui->op_dlg, ui->progress_label[2] =
|
|
label_new (7, FCOPY_LABEL_X, fifteen));
|
|
add_widget (ui->op_dlg, ui->bps_label = label_new (7, WX, ""));
|
|
|
|
add_widget (ui->op_dlg, ui->progress_gauge[1] =
|
|
gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0));
|
|
add_widget (ui->op_dlg, ui->progress_label[1] =
|
|
label_new (8, FCOPY_LABEL_X, fifteen));
|
|
add_widget (ui->op_dlg, ui->stalled_label = label_new (8, WX, ""));
|
|
|
|
add_widget (ui->op_dlg, ui->progress_gauge[0] =
|
|
gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0));
|
|
add_widget (ui->op_dlg, ui->progress_label[0] =
|
|
label_new (6, FCOPY_LABEL_X, fifteen));
|
|
add_widget (ui->op_dlg, ui->eta_label = label_new (6, WX, ""));
|
|
|
|
add_widget (ui->op_dlg, ui->file_string[1] =
|
|
label_new (4, FCOPY_GAUGE_X, sixty));
|
|
add_widget (ui->op_dlg, ui->file_label[1] =
|
|
label_new (4, FCOPY_LABEL_X, fifteen));
|
|
add_widget (ui->op_dlg, ui->file_string[0] =
|
|
label_new (3, FCOPY_GAUGE_X, sixty));
|
|
add_widget (ui->op_dlg, ui->file_label[0] =
|
|
label_new (3, FCOPY_LABEL_X, fifteen));
|
|
|
|
/* We will manage the dialog without any help, that's why
|
|
we have to call init_dlg */
|
|
init_dlg (ui->op_dlg);
|
|
ui->op_dlg->running = 1;
|
|
}
|
|
|
|
void
|
|
file_op_context_destroy_ui (FileOpContext *ctx)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_if_fail (ctx != NULL);
|
|
|
|
if (ctx->ui) {
|
|
ui = ctx->ui;
|
|
|
|
dlg_run_done (ui->op_dlg);
|
|
destroy_dlg (ui->op_dlg);
|
|
g_free (ui);
|
|
}
|
|
|
|
the_hint->widget.y = last_hint_line;
|
|
|
|
ctx->ui = NULL;
|
|
}
|
|
|
|
static FileProgressStatus
|
|
show_no_bar (FileOpContext *ctx, int n)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (n >= 0) {
|
|
label_set_text (ui->progress_label[n], "");
|
|
gauge_show (ui->progress_gauge[n], 0);
|
|
}
|
|
return check_progress_buttons (ctx);
|
|
}
|
|
|
|
static FileProgressStatus
|
|
show_bar (FileOpContext *ctx, int n, double done, double total)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
/*
|
|
* Gauge needs integers, so give it with integers between 0 and 1023.
|
|
* This precision should be quite reasonable.
|
|
*/
|
|
gauge_set_value (ui->progress_gauge[n], 1024,
|
|
(int) (1024 * done / total));
|
|
gauge_show (ui->progress_gauge[n], 1);
|
|
return check_progress_buttons (ctx);
|
|
}
|
|
|
|
static void
|
|
file_eta_show (FileOpContext *ctx)
|
|
{
|
|
int eta_hours, eta_mins, eta_s;
|
|
char eta_buffer[BUF_TINY];
|
|
FileOpContextUI *ui;
|
|
|
|
if (ctx->ui == NULL)
|
|
return;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (!ui->showing_eta)
|
|
return;
|
|
|
|
if (ctx->eta_secs > 0.5) {
|
|
eta_hours = ctx->eta_secs / (60 * 60);
|
|
eta_mins = (ctx->eta_secs - (eta_hours * 60 * 60)) / 60;
|
|
eta_s = ctx->eta_secs - (eta_hours * 60 * 60 + eta_mins * 60);
|
|
g_snprintf (eta_buffer, sizeof (eta_buffer), _("ETA %d:%02d.%02d"),
|
|
eta_hours, eta_mins, eta_s);
|
|
} else
|
|
*eta_buffer = 0;
|
|
|
|
label_set_text (ui->eta_label, eta_buffer);
|
|
}
|
|
|
|
static void
|
|
file_bps_show (FileOpContext *ctx)
|
|
{
|
|
char bps_buffer[BUF_TINY];
|
|
FileOpContextUI *ui;
|
|
|
|
if (ctx->ui == NULL)
|
|
return;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (!ui->showing_bps)
|
|
return;
|
|
|
|
if (ctx->bps > 1024 * 1024) {
|
|
g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f MB/s"),
|
|
ctx->bps / (1024 * 1024.0));
|
|
} else if (ctx->bps > 1024) {
|
|
g_snprintf (bps_buffer, sizeof (bps_buffer), _("%.2f KB/s"),
|
|
ctx->bps / 1024.0);
|
|
} else if (ctx->bps > 1) {
|
|
g_snprintf (bps_buffer, sizeof (bps_buffer), _("%ld B/s"),
|
|
ctx->bps);
|
|
} else
|
|
*bps_buffer = 0;
|
|
|
|
label_set_text (ui->bps_label, bps_buffer);
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_show (FileOpContext *ctx, off_t done, off_t total)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (!verbose)
|
|
return check_progress_buttons (ctx);
|
|
if (total > 0) {
|
|
label_set_text (ui->progress_label[0], _("File"));
|
|
file_eta_show (ctx);
|
|
file_bps_show (ctx);
|
|
return show_bar (ctx, 0, done, total);
|
|
} else
|
|
return show_no_bar (ctx, 0);
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_show_count (FileOpContext *ctx, off_t done, off_t total)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (!verbose)
|
|
return check_progress_buttons (ctx);
|
|
if (total > 0) {
|
|
label_set_text (ui->progress_label[1], _("Count"));
|
|
return show_bar (ctx, 1, done, total);
|
|
} else
|
|
return show_no_bar (ctx, 1);
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_show_bytes (FileOpContext *ctx, double done, double total)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (!verbose)
|
|
return check_progress_buttons (ctx);
|
|
if (total > 0) {
|
|
label_set_text (ui->progress_label[2], _("Bytes"));
|
|
return show_bar (ctx, 2, done, total);
|
|
} else
|
|
return show_no_bar (ctx, 2);
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
#define truncFileString(ui, s) str_trunc (s, ui->eta_extra + 47)
|
|
#define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47)
|
|
|
|
FileProgressStatus
|
|
file_progress_show_source (FileOpContext *ctx, const char *s)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (s != NULL) {
|
|
#ifdef WITH_FULL_PATHS
|
|
int i = strlen (current_panel->cwd);
|
|
|
|
/* We remove the full path we have added before */
|
|
if (!strncmp (s, current_panel->cwd, i)) {
|
|
if (s[i] == PATH_SEP)
|
|
s += i + 1;
|
|
}
|
|
#endif /* WITH_FULL_PATHS */
|
|
|
|
label_set_text (ui->file_label[0], _("Source"));
|
|
label_set_text (ui->file_string[0], truncFileString (ui, s));
|
|
return check_progress_buttons (ctx);
|
|
} else {
|
|
label_set_text (ui->file_label[0], "");
|
|
label_set_text (ui->file_string[0], "");
|
|
return check_progress_buttons (ctx);
|
|
}
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_show_target (FileOpContext *ctx, const char *s)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (s != NULL) {
|
|
label_set_text (ui->file_label[1], _("Target"));
|
|
label_set_text (ui->file_string[1], truncFileStringSecure (ui, s));
|
|
return check_progress_buttons (ctx);
|
|
} else {
|
|
label_set_text (ui->file_label[1], "");
|
|
label_set_text (ui->file_string[1], "");
|
|
return check_progress_buttons (ctx);
|
|
}
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_show_deleting (FileOpContext *ctx, const char *s)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
|
|
if (ctx->ui == NULL)
|
|
return FILE_CONT;
|
|
|
|
ui = ctx->ui;
|
|
|
|
label_set_text (ui->file_label[0], _("Deleting"));
|
|
label_set_text (ui->file_label[0], truncFileStringSecure (ui, s));
|
|
return check_progress_buttons (ctx);
|
|
}
|
|
|
|
#define X_TRUNC 52
|
|
|
|
/*
|
|
* FIXME: probably it is better to replace this with quick dialog machinery,
|
|
* but actually I'm not familiar with it and have not much time :(
|
|
* alex
|
|
*/
|
|
static struct {
|
|
const char *text;
|
|
int ypos, xpos;
|
|
int value; /* 0 for labels */
|
|
} rd_widgets[] = {
|
|
{
|
|
N_("Target file \"%s\" already exists!"), 3, 4, 0}, {
|
|
N_("&Abort"), BY + 3, 25, REPLACE_ABORT}, {
|
|
N_("If &size differs"), BY + 1, 28, REPLACE_SIZE}, {
|
|
N_("Non&e"), BY, 47, REPLACE_NEVER}, {
|
|
N_("&Update"), BY, 36, REPLACE_UPDATE}, {
|
|
N_("A&ll"), BY, 28, REPLACE_ALWAYS}, {
|
|
N_("Overwrite all targets?"), BY, 4, 0}, {
|
|
N_("&Reget"), BY - 1, 28, REPLACE_REGET}, {
|
|
N_("A&ppend"), BY - 2, 45, REPLACE_APPEND}, {
|
|
N_("&No"), BY - 2, 37, REPLACE_NO}, {
|
|
N_("&Yes"), BY - 2, 28, REPLACE_YES}, {
|
|
N_("Overwrite this target?"), BY - 2, 4, 0}, {
|
|
#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || (defined _LARGE_FILES && _LARGE_FILES)
|
|
N_("Target date: %s, size %llu"), 6, 4, 0}, {
|
|
N_("Source date: %s, size %llu"), 5, 4, 0}
|
|
#else
|
|
N_("Target date: %s, size %u"), 6, 4, 0}, {
|
|
N_("Source date: %s, size %u"), 5, 4, 0}
|
|
#endif
|
|
};
|
|
|
|
#define ADD_RD_BUTTON(i)\
|
|
add_widget (ui->replace_dlg,\
|
|
button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
|
|
NORMAL_BUTTON, rd_widgets [i].text, 0))
|
|
|
|
#define ADD_RD_LABEL(ui,i,p1,p2)\
|
|
g_snprintf (buffer, sizeof (buffer), rd_widgets [i].text, p1, p2);\
|
|
add_widget (ui->replace_dlg,\
|
|
label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer))
|
|
|
|
static void
|
|
init_replace (FileOpContext *ctx, enum OperationMode mode)
|
|
{
|
|
FileOpContextUI *ui;
|
|
char buffer[BUF_SMALL];
|
|
const char *title;
|
|
static int rd_xlen = 60, rd_trunc = X_TRUNC;
|
|
|
|
#ifdef ENABLE_NLS
|
|
static int i18n_flag;
|
|
if (!i18n_flag) {
|
|
int l1, l2, l, row;
|
|
register int i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
|
|
while (i--)
|
|
rd_widgets[i].text = _(rd_widgets[i].text);
|
|
|
|
/*
|
|
* longest of "Overwrite..." labels
|
|
* (assume "Target date..." are short enough)
|
|
*/
|
|
l1 = max (str_term_width1 (rd_widgets[6].text),
|
|
str_term_width1 (rd_widgets[11].text));
|
|
|
|
/* longest of button rows */
|
|
i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
|
|
for (row = l = l2 = 0; i--;) {
|
|
if (rd_widgets[i].value != 0) {
|
|
if (row != rd_widgets[i].ypos) {
|
|
row = rd_widgets[i].ypos;
|
|
l2 = max (l2, l);
|
|
l = 0;
|
|
}
|
|
l+= str_term_width1 (rd_widgets[i].text) + 4;
|
|
}
|
|
}
|
|
l2 = max (l2, l); /* last row */
|
|
rd_xlen = max (rd_xlen, l1 + l2 + 8);
|
|
rd_trunc = rd_xlen - 6;
|
|
|
|
/* Now place buttons */
|
|
l1 += 5; /* start of first button in the row */
|
|
i = sizeof (rd_widgets) / sizeof (rd_widgets[0]);
|
|
|
|
for (l = l1, row = 0; --i > 1;) {
|
|
if (rd_widgets[i].value != 0) {
|
|
if (row != rd_widgets[i].ypos) {
|
|
row = rd_widgets[i].ypos;
|
|
l = l1;
|
|
}
|
|
rd_widgets[i].xpos = l;
|
|
l+= str_term_width1 (rd_widgets[i].text) + 4;
|
|
}
|
|
}
|
|
/* Abort button is centered */
|
|
rd_widgets[1].xpos =
|
|
(rd_xlen - str_term_width1 (rd_widgets[1].text) - 3) / 2;
|
|
}
|
|
#endif /* ENABLE_NLS */
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (mode == Foreground)
|
|
title = _(" File exists ");
|
|
else
|
|
title = _(" Background process: File exists ");
|
|
|
|
/* FIXME - missing help node */
|
|
ui->replace_dlg =
|
|
create_dlg (0, 0, 16, rd_xlen, alarm_colors, NULL, "[Replace]",
|
|
title, DLG_CENTER | DLG_REVERSE);
|
|
|
|
|
|
ADD_RD_LABEL (ui, 0,
|
|
str_trunc (ui->replace_filename,
|
|
rd_trunc - str_term_width1 (rd_widgets[0].text)), 0);
|
|
ADD_RD_BUTTON (1);
|
|
|
|
ADD_RD_BUTTON (2);
|
|
ADD_RD_BUTTON (3);
|
|
ADD_RD_BUTTON (4);
|
|
ADD_RD_BUTTON (5);
|
|
ADD_RD_LABEL (ui, 6, 0, 0);
|
|
|
|
/* "this target..." widgets */
|
|
if (!S_ISDIR (ui->d_stat->st_mode)) {
|
|
if ((ctx->operation == OP_COPY) && (ui->d_stat->st_size != 0)
|
|
&& (ui->s_stat->st_size > ui->d_stat->st_size))
|
|
ADD_RD_BUTTON (7); /* reget */
|
|
|
|
ADD_RD_BUTTON (8); /* Overwrite all targets? */
|
|
}
|
|
ADD_RD_BUTTON (9);
|
|
ADD_RD_BUTTON (10);
|
|
ADD_RD_LABEL (ui, 11, 0, 0);
|
|
|
|
ADD_RD_LABEL (ui, 12, file_date (ui->d_stat->st_mtime),
|
|
(off_t) ui->d_stat->st_size);
|
|
ADD_RD_LABEL (ui, 13, file_date (ui->s_stat->st_mtime),
|
|
(off_t) ui->s_stat->st_size);
|
|
}
|
|
|
|
void
|
|
file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_if_fail (ctx != NULL);
|
|
|
|
if (ctx->ui == NULL)
|
|
return;
|
|
|
|
ui = ctx->ui;
|
|
|
|
label_set_text (ui->stalled_label, stalled_msg);
|
|
}
|
|
|
|
FileProgressStatus
|
|
file_progress_real_query_replace (FileOpContext *ctx,
|
|
enum OperationMode mode, const char *destname,
|
|
struct stat *_s_stat,
|
|
struct stat *_d_stat)
|
|
{
|
|
FileOpContextUI *ui;
|
|
|
|
g_return_val_if_fail (ctx != NULL, FILE_CONT);
|
|
g_return_val_if_fail (ctx->ui != NULL, FILE_CONT);
|
|
|
|
ui = ctx->ui;
|
|
|
|
if (ui->replace_result < REPLACE_ALWAYS) {
|
|
ui->replace_filename = destname;
|
|
ui->s_stat = _s_stat;
|
|
ui->d_stat = _d_stat;
|
|
init_replace (ctx, mode);
|
|
run_dlg (ui->replace_dlg);
|
|
ui->replace_result = ui->replace_dlg->ret_value;
|
|
if (ui->replace_result == B_CANCEL)
|
|
ui->replace_result = REPLACE_ABORT;
|
|
destroy_dlg (ui->replace_dlg);
|
|
}
|
|
|
|
switch (ui->replace_result) {
|
|
case REPLACE_UPDATE:
|
|
do_refresh ();
|
|
if (_s_stat->st_mtime > _d_stat->st_mtime)
|
|
return FILE_CONT;
|
|
else
|
|
return FILE_SKIP;
|
|
|
|
case REPLACE_SIZE:
|
|
do_refresh ();
|
|
if (_s_stat->st_size == _d_stat->st_size)
|
|
return FILE_SKIP;
|
|
else
|
|
return FILE_CONT;
|
|
|
|
case REPLACE_REGET:
|
|
/* Careful: we fall through and set do_append */
|
|
ctx->do_reget = _d_stat->st_size;
|
|
|
|
case REPLACE_APPEND:
|
|
ctx->do_append = 1;
|
|
|
|
case REPLACE_YES:
|
|
case REPLACE_ALWAYS:
|
|
do_refresh ();
|
|
return FILE_CONT;
|
|
case REPLACE_NO:
|
|
case REPLACE_NEVER:
|
|
do_refresh ();
|
|
return FILE_SKIP;
|
|
case REPLACE_ABORT:
|
|
default:
|
|
return FILE_ABORT;
|
|
}
|
|
}
|
|
|
|
#define FMDY 13
|
|
#define FMD_XLEN 64
|
|
extern int fmd_xlen;
|
|
static QuickWidget fmd_widgets[] = {
|
|
|
|
#define FMCB0 FMDC
|
|
#define FMCB12 0
|
|
#define FMCB11 1
|
|
/* follow symlinks and preserve Attributes must be the first */
|
|
{quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
|
|
0 /* &op_preserve */ , 0, NULL},
|
|
{quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
|
|
0 /* &file_mask_op_follow_links */ , 0, NULL},
|
|
{quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, NULL},
|
|
{quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
|
|
0 /* &source_easy_patterns */ , 0, NULL},
|
|
{quick_input, 3, 64, 3, FMDY, "", 58,
|
|
0, 0, 0, "input-def"},
|
|
#define FMDI1 4
|
|
#define FMDI2 5
|
|
#define FMDC 3
|
|
{quick_input, 3, 64, 6, FMDY, "", 58, 0,
|
|
0, 0, "input2"},
|
|
#define FMDI0 6
|
|
{quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, NULL},
|
|
#define FMBRGT 7
|
|
{quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0,
|
|
NULL},
|
|
#undef SKIP
|
|
#ifdef WITH_BACKGROUND
|
|
# define SKIP 5
|
|
# define FMCB21 11
|
|
# define FMCB22 10
|
|
# define FMBLFT 9
|
|
# define FMBMID 8
|
|
{quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0,
|
|
NULL},
|
|
#else /* WITH_BACKGROUND */
|
|
# define SKIP 4
|
|
# define FMCB21 10
|
|
# define FMCB22 9
|
|
# define FMBLFT 8
|
|
# undef FMBMID
|
|
#endif
|
|
{quick_button, 14, 64, 9, FMDY, N_("&OK"), 0, B_ENTER, 0, 0, NULL},
|
|
{quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
|
|
0 /* &file_mask_stable_symlinks */ , 0, NULL},
|
|
{quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0,
|
|
0,
|
|
0 /* &dive_into_subdirs */ , 0, NULL},
|
|
NULL_QuickWidget
|
|
};
|
|
|
|
static int
|
|
is_wildcarded (char *p)
|
|
{
|
|
for (; *p; p++) {
|
|
if (*p == '*')
|
|
return 1;
|
|
else if (*p == '\\' && p[1] >= '1' && p[1] <= '9')
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
fmd_init_i18n (int force)
|
|
{
|
|
#ifdef ENABLE_NLS
|
|
static int initialized = FALSE;
|
|
register int i;
|
|
int len;
|
|
|
|
if (initialized && !force)
|
|
return;
|
|
|
|
for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
|
|
op_names[i] = _(op_names[i]);
|
|
|
|
i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
|
|
while (i--)
|
|
if (fmd_widgets[i].text[0] != '\0')
|
|
fmd_widgets[i].text = _(fmd_widgets[i].text);
|
|
|
|
len = str_term_width1 (fmd_widgets[FMCB11].text)
|
|
+ str_term_width1 (fmd_widgets[FMCB21].text) + 15;
|
|
fmd_xlen = max (fmd_xlen, len);
|
|
|
|
len = str_term_width1 (fmd_widgets[FMCB12].text)
|
|
+ str_term_width1 (fmd_widgets[FMCB22].text) + 15;
|
|
fmd_xlen = max (fmd_xlen, len);
|
|
|
|
len = str_term_width1 (fmd_widgets[FMBRGT].text)
|
|
+ str_term_width1 (fmd_widgets[FMBLFT].text) + 11;
|
|
|
|
#ifdef FMBMID
|
|
len+= str_term_width1 (fmd_widgets[FMBMID].text) + 6;
|
|
#endif
|
|
|
|
fmd_xlen = max (fmd_xlen, len + 4);
|
|
|
|
len = (fmd_xlen - (len + 6)) / 2;
|
|
i = fmd_widgets[FMBLFT].relative_x = len + 3;
|
|
i+= str_term_width1 (fmd_widgets[FMBLFT].text) + 8;
|
|
|
|
#ifdef FMBMID
|
|
fmd_widgets[FMBMID].relative_x = i;
|
|
i+= str_term_width1 (fmd_widgets[FMBMID].text) + 6;
|
|
#endif
|
|
|
|
fmd_widgets[FMBRGT].relative_x = i;
|
|
|
|
#define chkbox_xpos(i) \
|
|
fmd_widgets [i].relative_x = fmd_xlen - str_term_width1 (fmd_widgets [i].text) - 6
|
|
|
|
chkbox_xpos (FMCB0);
|
|
chkbox_xpos (FMCB21);
|
|
chkbox_xpos (FMCB22);
|
|
|
|
if (fmd_xlen != FMD_XLEN) {
|
|
i = sizeof (fmd_widgets) / sizeof (fmd_widgets[0]) - 1;
|
|
while (i--)
|
|
fmd_widgets[i].x_divisions = fmd_xlen;
|
|
|
|
fmd_widgets[FMDI1].hotkey_pos =
|
|
fmd_widgets[FMDI2].hotkey_pos = fmd_xlen - 6;
|
|
}
|
|
#undef chkbox_xpos
|
|
|
|
initialized = TRUE;
|
|
#endif /* !ENABLE_NLS */
|
|
}
|
|
|
|
char *
|
|
file_mask_dialog (FileOpContext *ctx, FileOperation operation, const char *text,
|
|
const char *def_text, int only_one, int *do_background)
|
|
{
|
|
int source_easy_patterns = easy_patterns;
|
|
char *source_mask, *orig_mask, *dest_dir, *tmpdest;
|
|
const char *error;
|
|
char *def_text_secure;
|
|
struct stat buf;
|
|
int val;
|
|
QuickDialog Quick_input;
|
|
|
|
g_return_val_if_fail (ctx != NULL, NULL);
|
|
#if 0
|
|
message (D_ERROR, __FUNCTION__, "text = `%s' \n def_text = `%s'", text,
|
|
def_text);
|
|
#endif
|
|
fmd_init_i18n (FALSE);
|
|
|
|
/* Set up the result pointers */
|
|
|
|
fmd_widgets[FMCB12].result = &ctx->op_preserve;
|
|
fmd_widgets[FMCB11].result = &ctx->follow_links;
|
|
fmd_widgets[FMCB22].result = &ctx->stable_symlinks;
|
|
fmd_widgets[FMCB21].result = &ctx->dive_into_subdirs;
|
|
|
|
/* filter out a possible password from def_text */
|
|
def_text_secure = strip_password (g_strdup (def_text), 1);
|
|
|
|
/* Create the dialog */
|
|
|
|
ctx->stable_symlinks = 0;
|
|
fmd_widgets[FMDC].result = &source_easy_patterns;
|
|
fmd_widgets[FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
|
|
Quick_input.xlen = fmd_xlen;
|
|
Quick_input.xpos = -1;
|
|
Quick_input.title = op_names[operation];
|
|
Quick_input.help = "[Mask Copy/Rename]";
|
|
Quick_input.ylen = FMDY;
|
|
Quick_input.i18n = 1;
|
|
Quick_input.widgets = fmd_widgets;
|
|
fmd_widgets[FMDI0].text = text;
|
|
fmd_widgets[FMDI2].text = def_text_secure;
|
|
fmd_widgets[FMDI2].str_result = &dest_dir;
|
|
fmd_widgets[FMDI1].str_result = &source_mask;
|
|
|
|
*do_background = 0;
|
|
ask_file_mask:
|
|
|
|
if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL) {
|
|
g_free (def_text_secure);
|
|
return 0;
|
|
}
|
|
g_free (def_text_secure);
|
|
|
|
if (ctx->follow_links)
|
|
ctx->stat_func = mc_stat;
|
|
else
|
|
ctx->stat_func = mc_lstat;
|
|
|
|
if (ctx->op_preserve) {
|
|
ctx->preserve = 1;
|
|
ctx->umask_kill = 0777777;
|
|
ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0;
|
|
} else {
|
|
int i;
|
|
ctx->preserve = ctx->preserve_uidgid = 0;
|
|
i = umask (0);
|
|
umask (i);
|
|
ctx->umask_kill = i ^ 0777777;
|
|
}
|
|
|
|
orig_mask = source_mask;
|
|
if (!dest_dir || !*dest_dir) {
|
|
g_free (source_mask);
|
|
return dest_dir;
|
|
}
|
|
if (source_easy_patterns) {
|
|
source_easy_patterns = easy_patterns;
|
|
easy_patterns = 1;
|
|
source_mask = convert_pattern (source_mask, match_file, 1);
|
|
easy_patterns = source_easy_patterns;
|
|
error =
|
|
re_compile_pattern (source_mask, strlen (source_mask),
|
|
&ctx->rx);
|
|
g_free (source_mask);
|
|
} else
|
|
error =
|
|
re_compile_pattern (source_mask, strlen (source_mask),
|
|
&ctx->rx);
|
|
|
|
if (error) {
|
|
message (D_ERROR, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
|
|
orig_mask, error);
|
|
g_free (orig_mask);
|
|
goto ask_file_mask;
|
|
}
|
|
g_free (orig_mask);
|
|
|
|
tmpdest = dest_dir;
|
|
dest_dir = tilde_expand(tmpdest);
|
|
g_free(tmpdest);
|
|
|
|
ctx->dest_mask = strrchr (dest_dir, PATH_SEP);
|
|
if (ctx->dest_mask == NULL)
|
|
ctx->dest_mask = dest_dir;
|
|
else
|
|
ctx->dest_mask++;
|
|
orig_mask = ctx->dest_mask;
|
|
if (!*ctx->dest_mask
|
|
|| (!ctx->dive_into_subdirs && !is_wildcarded (ctx->dest_mask)
|
|
&& (!only_one
|
|
|| (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode))))
|
|
|| (ctx->dive_into_subdirs
|
|
&& ((!only_one && !is_wildcarded (ctx->dest_mask))
|
|
|| (only_one && !mc_stat (dest_dir, &buf)
|
|
&& S_ISDIR (buf.st_mode)))))
|
|
ctx->dest_mask = g_strdup ("*");
|
|
else {
|
|
ctx->dest_mask = g_strdup (ctx->dest_mask);
|
|
*orig_mask = 0;
|
|
}
|
|
if (!*dest_dir) {
|
|
g_free (dest_dir);
|
|
dest_dir = g_strdup ("./");
|
|
}
|
|
if (val == B_USER)
|
|
*do_background = 1;
|
|
return dest_dir;
|
|
}
|