diff --git a/lib/util.c b/lib/util.c index 012feebd2..42e8a0418 100644 --- a/lib/util.c +++ b/lib/util.c @@ -49,6 +49,7 @@ #include "lib/vfs/mc-vfs/vfs.h" #include "lib/strutil.h" +#include "src/filegui.h" #include "src/file.h" /* copy_file_file() */ #ifndef HAVE_CHARSET #include "src/main.h" /* eight_bit_clean */ @@ -539,15 +540,16 @@ check_for_default (const char *default_file, const char *file) { if (!exist_file (file)) { FileOpContext *ctx; - off_t count = 0; - double bytes = 0.0; + FileOpTotalContext *tctx; if (!exist_file (default_file)) return -1; ctx = file_op_context_new (OP_COPY); - file_op_context_create_ui (ctx, 0); - copy_file_file (ctx, default_file, file, 1, &count, &bytes, 1); + tctx = file_op_total_context_new (); + file_op_context_create_ui (ctx, 0, FALSE); + copy_file_file (tctx, ctx, default_file, file); + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); } @@ -1444,7 +1446,7 @@ save_file_position (const char *filename, long line, long column, off_t offset) /* put the new record */ if (line != 1 || column != 0) { - if (fprintf (f, "%s %ld;%ld;%lli\n", filename, line, column, offset) < 0) + if (fprintf (f, "%s %ld;%ld;%llu\n", filename, line, column, offset) < 0) goto write_position_error; } diff --git a/src/cmd.c b/src/cmd.c index a79a797d7..343677809 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -345,7 +345,7 @@ void copy_cmd (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_COPY, 0)) { + if (panel_operate (current_panel, OP_COPY, FALSE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } @@ -355,7 +355,7 @@ copy_cmd (void) void rename_cmd (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_MOVE, 0)){ + if (panel_operate (current_panel, OP_MOVE, FALSE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } @@ -365,7 +365,7 @@ void rename_cmd (void) void copy_cmd_local (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_COPY, 1)){ + if (panel_operate (current_panel, OP_COPY, TRUE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } @@ -375,7 +375,7 @@ void copy_cmd_local (void) void rename_cmd_local (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_MOVE, 1)){ + if (panel_operate (current_panel, OP_MOVE, TRUE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } @@ -422,7 +422,7 @@ void delete_cmd (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_DELETE, 0)){ + if (panel_operate (current_panel, OP_DELETE, FALSE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } @@ -433,7 +433,7 @@ void delete_cmd_local (void) { save_cwds_stat (); - if (panel_operate (current_panel, OP_DELETE, 1)){ + if (panel_operate (current_panel, OP_DELETE, TRUE)) { update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } diff --git a/src/file.c b/src/file.c index e7293e528..68a94496e 100644 --- a/src/file.c +++ b/src/file.c @@ -54,7 +54,6 @@ #include #include #include -#include #include "lib/global.h" #include "lib/tty/tty.h" @@ -86,6 +85,9 @@ /* Hack: the vfs code should not rely on this */ #define WITH_FULL_PATHS 1 +#define FILEOP_UPDATE_INTERVAL 2 +#define FILEOP_STALLING_INTERVAL 4 + int verbose = 1; /* @@ -138,9 +140,8 @@ static FileProgressStatus query_replace (FileOpContext * ctx, const char *destna static FileProgressStatus query_recursive (FileOpContext * ctx, const char *s); static FileProgressStatus do_file_error (const char *str); static FileProgressStatus erase_dir_iff_empty (FileOpContext *ctx, const char *s); -static FileProgressStatus erase_file (FileOpContext *ctx, const char *s, - off_t *progress_count, double *progress_bytes, - int is_toplevel_file); +static FileProgressStatus erase_file (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *s, gboolean is_toplevel_file); static FileProgressStatus files_error (const char *format, const char *file1, const char *file2); @@ -271,12 +272,9 @@ make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path) int len; FileProgressStatus return_status; struct stat sb; - int dst_is_symlink; + gboolean dst_is_symlink; - if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode)) - dst_is_symlink = 1; - else - dst_is_symlink = 0; + dst_is_symlink = (mc_lstat (dst_path, &sb) == 0) && S_ISLNK (sb.st_mode); retry_src_readlink: len = mc_readlink (src_path, link_target, MC_MAXPATHLEN - 1); @@ -296,17 +294,17 @@ make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path) _(" Cannot make stable symlinks across " "non-local filesystems: \n\n" " Option Stable Symlinks will be disabled ")); - ctx->stable_symlinks = 0; + ctx->stable_symlinks = FALSE; } - if (ctx->stable_symlinks && *link_target != PATH_SEP) { + if (ctx->stable_symlinks && !g_path_is_absolute (link_target)) { char *p, *q, *s; const char *r = strrchr (src_path, PATH_SEP); if (r) { p = g_strndup (src_path, r - src_path + 1); - if (*dst_path == PATH_SEP) + if (g_path_is_absolute (dst_path)) q = g_strdup (dst_path); else q = g_strconcat (p, dst_path, (char *) NULL); @@ -349,37 +347,36 @@ make_symlink (FileOpContext *ctx, const char *src_path, const char *dst_path) return return_status; } -static int -progress_update_one (FileOpContext *ctx, - off_t *progress_count, - double *progress_bytes, off_t add, int is_toplevel_file) +static FileProgressStatus +progress_update_one (FileOpTotalContext *tctx, FileOpContext *ctx, off_t add, gboolean is_toplevel_file) { - int ret; + struct timeval tv_current; + static struct timeval tv_start = {}; if (is_toplevel_file || ctx->progress_totals_computed) { - (*progress_count)++; - (*progress_bytes) += add; + tctx->progress_count++; + tctx->progress_bytes += add; + } + if (tv_start.tv_sec == 0) { + gettimeofday (&tv_start, (struct timezone *) NULL); + } + gettimeofday (&tv_current, (struct timezone *) NULL); + if ((tv_current.tv_sec - tv_start.tv_sec) > FILEOP_UPDATE_INTERVAL) + { + file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count); + file_progress_show_total (tctx, ctx, tctx->progress_bytes, TRUE); + tv_start.tv_sec = tv_current.tv_sec; } - /* Apply some heuristic here to not call the update stuff very often */ - ret = - file_progress_show_count (ctx, *progress_count, - ctx->progress_count); - if (ret != FILE_CONT) - return ret; - ret = - file_progress_show_bytes (ctx, *progress_bytes, - ctx->progress_bytes); - - return ret; + return check_progress_buttons (ctx); } /* Status of the destination file */ -enum { - DEST_NONE, /* Not created */ - DEST_SHORT, /* Created, not fully copied */ - DEST_FULL /* Created, fully copied */ -}; +typedef enum { + DEST_NONE = 0, /* Not created */ + DEST_SHORT = 1, /* Created, not fully copied */ + DEST_FULL = 2 /* Created, fully copied */ +} dest_status_t; static FileProgressStatus real_warn_same_file (enum OperationMode mode, const char *fmt, @@ -428,34 +425,81 @@ warn_same_file (const char *fmt, const char *a, const char *b) } #endif -FileProgressStatus -copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, - int ask_overwrite, off_t *progress_count, - double *progress_bytes, int is_toplevel_file) +static void +copy_file_file_display_progress (FileOpTotalContext *tctx, FileOpContext *ctx, + struct timeval tv_current, struct timeval tv_transfer_start, + off_t file_size, off_t n_read_total) { - uid_t src_uid = (uid_t) - 1; - gid_t src_gid = (gid_t) - 1; + long dt; + + /* 1. Update rotating dash after some time */ + rotate_dash (); + + /* 3. Compute ETA */ + dt = (tv_current.tv_sec - tv_transfer_start.tv_sec); + + if (n_read_total) { + ctx->eta_secs = ((dt / (double) n_read_total) * file_size) - dt; + ctx->bps = n_read_total / ((dt < 1) ? 1 : dt); + } else + ctx->eta_secs = 0.0; + + /* 4. Compute BPS rate */ + ctx->bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec); + if (ctx->bps_time < 1) + ctx->bps_time = 1; + ctx->bps = n_read_total / ctx->bps_time; + + /* 5. Compute total ETA and BPS*/ + if (ctx->progress_bytes != 0) { + double remain_bytes; + tctx->copyed_bytes = tctx->progress_bytes + n_read_total + ctx->do_reget; + remain_bytes = ctx->progress_bytes - tctx->copyed_bytes; +#if 1 + { + int total_secs = tv_current.tv_sec - tctx->transfer_start.tv_sec; + + if (total_secs < 1) + total_secs = 1; + tctx->bps = tctx->copyed_bytes / total_secs; + tctx->eta_secs = remain_bytes / tctx->bps; + } +#else + /* broken on lot of little files */ + tctx->bps_count++; + tctx->bps = ( tctx->bps * (tctx->bps_count - 1) + ctx->bps) / tctx->bps_count; + tctx->eta_secs = remain_bytes / tctx->bps; +#endif + } +} + +FileProgressStatus +copy_file_file (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *src_path, const char *dst_path) +{ + uid_t src_uid = (uid_t) -1; + gid_t src_gid = (gid_t) -1; - char *buf = NULL; - int buf_size = BUF_8K; int src_desc, dest_desc = -1; int n_read, n_written; mode_t src_mode = 0; /* The mode of the source file */ struct stat sb, sb2; struct utimbuf utb; - int dst_exists = 0, appending = 0; + gboolean dst_exists = FALSE, appending = FALSE; off_t n_read_total = 0, file_size = -1; FileProgressStatus return_status, temp_status; struct timeval tv_transfer_start; - int dst_status = DEST_NONE; /* 1 if the file is not fully copied */ + dest_status_t dst_status = DEST_NONE; int open_flags; + gboolean is_first_time=TRUE; /* FIXME: We should not be using global variables! */ ctx->do_reget = 0; return_status = FILE_RETRY; - if (file_progress_show_source (ctx, src_path) == FILE_ABORT || - file_progress_show_target (ctx, dst_path) == FILE_ABORT) + file_progress_show_source (ctx, src_path); + file_progress_show_target (ctx, dst_path); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -469,7 +513,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, continue; return return_status; } - dst_exists = 1; + dst_exists = TRUE; break; } @@ -487,7 +531,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), src_path, dst_path); /* Should we replace destination? */ - if (ask_overwrite) { + if (tctx->ask_overwrite) { ctx->do_reget = 0; return_status = query_replace (ctx, dst_path, &sb, &sb2); if (return_status != FILE_CONT) @@ -550,11 +594,12 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, return return_status; } - if (ctx->do_reget) { + if (ctx->do_reget != 0) { if (mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget) { message (D_ERROR, _("Warning"), _(" Reget failed, about to overwrite file ")); - ctx->do_reget = ctx->do_append = 0; + ctx->do_reget = 0; + ctx->do_append = FALSE; } } @@ -563,7 +608,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, _(" Cannot fstat source file \"%s\" \n %s "), src_path); if (return_status == FILE_RETRY) continue; - ctx->do_append = 0; + ctx->do_append = FALSE; goto ret; } src_mode = sb.st_mode; @@ -574,7 +619,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, file_size = sb.st_size; open_flags = O_WRONLY; - if (dst_exists != 0) { + if (dst_exists) { if (ctx->do_append != 0) open_flags |= O_APPEND; else @@ -582,6 +627,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, } else { open_flags |= O_CREAT | O_EXCL; } + while ((dest_desc = mc_open (dst_path, open_flags, src_mode)) < 0) { if (errno == EEXIST) { goto ret; @@ -590,13 +636,13 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, _(" Cannot create target file \"%s\" \n %s "), dst_path); if (return_status == FILE_RETRY) continue; - ctx->do_append = 0; + ctx->do_append = FALSE; goto ret; } dst_status = DEST_SHORT; /* file opened, but not fully copied */ appending = ctx->do_append; - ctx->do_append = 0; + ctx->do_append = FALSE; /* Find out the optimal buffer size. */ while (mc_fstat (dest_desc, &sb)) { @@ -606,13 +652,16 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, continue; goto ret; } - buf = g_malloc (buf_size); ctx->eta_secs = 0.0; ctx->bps = 0; - return_status = file_progress_show (ctx, 0, file_size); - + if (tctx->bps == 0 || (file_size/(tctx->bps)) > FILEOP_UPDATE_INTERVAL) { + file_progress_show (ctx, 0, file_size, "", TRUE); + } else { + file_progress_show (ctx, 1, 1, "", TRUE); + } + return_status = check_progress_buttons (ctx); mc_refresh (); if (return_status != FILE_CONT) @@ -621,17 +670,18 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, { struct timeval tv_current, tv_last_update, tv_last_input; int secs, update_secs; - long dt; - const char *stalled_msg; + const char *stalled_msg=""; tv_last_update = tv_transfer_start; for (;;) { + char buf[BUF_8K]; + /* src_read */ if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0)) n_read = -1; else - while ((n_read = mc_read (src_desc, buf, buf_size)) < 0) { + while ((n_read = mc_read (src_desc, buf, sizeof (buf))) < 0) { return_status = file_error ( _(" Cannot read source file \"%s\" \n %s "), src_path); if (return_status == FILE_RETRY) @@ -651,7 +701,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, * permissions: -------, so if we happen to have actually * read something, we should fix the permissions. */ - if (!(src_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) + if ((src_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == 0) src_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; gettimeofday (&tv_last_input, NULL); @@ -670,60 +720,48 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, goto ret; } } - - /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */ secs = (tv_current.tv_sec - tv_last_update.tv_sec); - if (secs > 2) { - rotate_dash (); + update_secs = (tv_current.tv_sec - tv_last_input.tv_sec); + + if (is_first_time || secs > FILEOP_UPDATE_INTERVAL ) + { + copy_file_file_display_progress(tctx, ctx, + tv_current, + tv_transfer_start, + file_size, + n_read_total); tv_last_update = tv_current; } + is_first_time = FALSE; - /* 2. Check for a stalled condition */ - update_secs = (tv_current.tv_sec - tv_last_input.tv_sec); - stalled_msg = ""; - if (update_secs > 4) { - stalled_msg = _("(stalled)"); - } + if (update_secs > FILEOP_STALLING_INTERVAL) { + stalled_msg = _("(stalled)"); + } + { + gboolean force_update = + (tv_current.tv_sec - tctx->transfer_start.tv_sec) > FILEOP_UPDATE_INTERVAL; + file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count); + file_progress_show_total (tctx, ctx, tctx->progress_bytes + n_read_total + ctx->do_reget, + force_update); - /* 3. Compute ETA */ - if (secs > 2) { - dt = (tv_current.tv_sec - tv_transfer_start.tv_sec); - if (n_read_total) { - ctx->eta_secs = - ((dt / (double) n_read_total) * file_size) - dt; - ctx->bps = n_read_total / ((dt < 1) ? 1 : dt); - } else - ctx->eta_secs = 0.0; - } + file_progress_show (ctx, n_read_total + ctx->do_reget, file_size, stalled_msg, + force_update); + } + mc_refresh (); - /* 4. Compute BPS rate */ - if (secs > 2) { - ctx->bps_time = - (tv_current.tv_sec - tv_transfer_start.tv_sec); - if (ctx->bps_time < 1) - ctx->bps_time = 1; - ctx->bps = n_read_total / ctx->bps_time; - } + return_status = check_progress_buttons (ctx); - file_progress_set_stalled_label (ctx, stalled_msg); - return_status = file_progress_show_bytes (ctx, *progress_bytes + - n_read_total + ctx->do_reget, ctx->progress_bytes); - if (return_status == FILE_CONT) { - return_status = - file_progress_show (ctx, n_read_total + ctx->do_reget, file_size); - } - mc_refresh (); - if (return_status != FILE_CONT) + if (return_status != FILE_CONT) { + mc_refresh (); goto ret; + } } } dst_status = DEST_FULL; /* copy successful, don't remove target file */ ret: - g_free (buf); - while (src_desc != -1 && mc_close (src_desc) < 0) { temp_status = file_error ( _(" Cannot close source file \"%s\" \n %s "), src_path); @@ -749,7 +787,7 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, result = query_dialog (Q_("DialogTitle|Copy"), _("Incomplete file was retrieved. Keep it?"), D_ERROR, 2, _("&Delete"), _("&Keep")); - if (!result) + if (result == 0) mc_unlink (dst_path); } else if (dst_status == DEST_FULL) { /* Copy has succeeded */ @@ -785,13 +823,10 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, } if (return_status == FILE_CONT) - return_status = - progress_update_one (ctx, progress_count, progress_bytes, - file_size, is_toplevel_file); + return_status = progress_update_one (tctx, ctx, file_size, tctx->is_toplevel_file); return return_status; } - /* * I think these copy_*_* functions should have a return type. * anyway, this function *must* have two directories as arguments. @@ -799,9 +834,9 @@ copy_file_file (FileOpContext *ctx, const char *src_path, const char *dst_path, /* FIXME: This function needs to check the return values of the function calls */ FileProgressStatus -copy_dir_dir (FileOpContext *ctx, const char *s, const char *_d, int toplevel, - int move_over, int delete, struct link *parent_dirs, - off_t *progress_count, double *progress_bytes) +copy_dir_dir (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s, const char *_d, + gboolean toplevel, gboolean move_over, gboolean do_delete, + struct link *parent_dirs) { struct dirent *next; struct stat buf, cbuf; @@ -956,18 +991,16 @@ copy_dir_dir (FileOpContext *ctx, const char *s, const char *_d, int toplevel, * dir already exists. So, we give the recursive call the flag 0 * meaning no toplevel. */ - return_status = copy_dir_dir (ctx, path, mdpath, 0, 0, delete, - parent_dirs, progress_count, progress_bytes); + return_status = copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs); g_free (mdpath); } else { char *dest_file; dest_file = concat_dir_and_file (dest_dir, x_basename (path)); - return_status = copy_file_file (ctx, path, dest_file, 1, - progress_count, progress_bytes, 0); + return_status = copy_file_file (tctx, ctx, path, dest_file); g_free (dest_file); } - if (delete && return_status == FILE_CONT) { + if (do_delete && return_status == FILE_CONT) { if (ctx->erase_at_end) { static struct link *tail; size_t len = strlen (path); @@ -984,7 +1017,7 @@ copy_dir_dir (FileOpContext *ctx, const char *s, const char *_d, int toplevel, if (S_ISDIR (buf.st_mode)) { return_status = erase_dir_iff_empty (ctx, path); } else - return_status = erase_file (ctx, path, 0, 0, 0); + return_status = erase_file (tctx, ctx, path, FALSE); } } g_free (path); @@ -1016,15 +1049,15 @@ copy_dir_dir (FileOpContext *ctx, const char *s, const char *_d, int toplevel, /* {{{ Move routines */ static FileProgressStatus -move_file_file (FileOpContext *ctx, const char *s, const char *d, - off_t *progress_count, double *progress_bytes) +move_file_file (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s, const char *d) { struct stat src_stats, dst_stats; FileProgressStatus return_status = FILE_CONT; gboolean copy_done = FALSE; - if (file_progress_show_source (ctx, s) == FILE_ABORT - || file_progress_show_target (ctx, d) == FILE_ABORT) + file_progress_show_source (ctx, s); + file_progress_show_target (ctx, d); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -1066,9 +1099,7 @@ move_file_file (FileOpContext *ctx, const char *s, const char *d, } if (mc_rename (s, d) == 0) { - return progress_update_one (ctx, progress_count, - progress_bytes, - src_stats.st_size, 1); + return progress_update_one (tctx, ctx, src_stats.st_size, TRUE); } } #if 0 @@ -1090,16 +1121,17 @@ move_file_file (FileOpContext *ctx, const char *s, const char *d, #endif /* Failed because filesystem boundary -> copy the file instead */ - return_status = - copy_file_file (ctx, s, d, 0, progress_count, progress_bytes, 1); + return_status = copy_file_file (tctx, ctx, s, d); if (return_status != FILE_CONT) return return_status; copy_done = TRUE; - if ((return_status = - file_progress_show_source (ctx, NULL)) != FILE_CONT - || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT) + file_progress_show_source (ctx, NULL); + file_progress_show (ctx, 0, 0, "", FALSE); + + return_status = check_progress_buttons (ctx); + if (return_status != FILE_CONT) return return_status; mc_refresh (); @@ -1114,18 +1146,14 @@ move_file_file (FileOpContext *ctx, const char *s, const char *d, } if (!copy_done) { - return_status = progress_update_one (ctx, - progress_count, - progress_bytes, - src_stats.st_size, 1); + return_status = progress_update_one (tctx, ctx, src_stats.st_size, TRUE); } return return_status; } FileProgressStatus -move_dir_dir (FileOpContext *ctx, const char *s, const char *d, - off_t *progress_count, double *progress_bytes) +move_dir_dir (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s, const char *d) { struct stat sbuf, dbuf, destbuf; struct link *lp; @@ -1134,8 +1162,9 @@ move_dir_dir (FileOpContext *ctx, const char *s, const char *d, gboolean move_over = FALSE; gboolean dstat_ok; - if (file_progress_show_source (ctx, s) == FILE_ABORT || - file_progress_show_target (ctx, d) == FILE_ABORT) + file_progress_show_source (ctx, s); + file_progress_show_target (ctx, d); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -1158,8 +1187,7 @@ move_dir_dir (FileOpContext *ctx, const char *s, const char *d, retry_dst_stat: if (!mc_stat (destdir, &destbuf)) { if (move_over) { - return_status = copy_dir_dir (ctx, s, destdir, 0, 1, 1, 0, - progress_count, progress_bytes); + return_status = copy_dir_dir (tctx, ctx, s, destdir, FALSE, TRUE, TRUE, NULL); if (return_status != FILE_CONT) goto ret; @@ -1198,15 +1226,16 @@ move_dir_dir (FileOpContext *ctx, const char *s, const char *d, } /* Failed because of filesystem boundary -> copy dir instead */ return_status = - copy_dir_dir (ctx, s, destdir, 0, 0, 1, 0, progress_count, - progress_bytes); + copy_dir_dir (tctx, ctx, s, destdir, FALSE, FALSE, TRUE, NULL); if (return_status != FILE_CONT) goto ret; oktoret: - if ((return_status = - file_progress_show_source (ctx, NULL)) != FILE_CONT - || (return_status = file_progress_show (ctx, 0, 0)) != FILE_CONT) + file_progress_show_source (ctx, NULL); + file_progress_show (ctx, 0, 0, "", FALSE); + + return_status = check_progress_buttons (ctx); + if (return_status != FILE_CONT) goto ret; mc_refresh (); @@ -1217,7 +1246,7 @@ move_dir_dir (FileOpContext *ctx, const char *s, const char *d, erase_dir_iff_empty (ctx, erase_list->name); } else return_status = - erase_file (ctx, erase_list->name, 0, 0, 0); + erase_file (tctx, ctx, erase_list->name, FALSE); lp = erase_list; erase_list = erase_list->next; g_free (lp); @@ -1240,17 +1269,17 @@ move_dir_dir (FileOpContext *ctx, const char *s, const char *d, /* {{{ Erase routines */ /* Don't update progress status if progress_count==NULL */ static FileProgressStatus -erase_file (FileOpContext *ctx, const char *s, off_t *progress_count, - double *progress_bytes, int is_toplevel_file) +erase_file (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s, gboolean is_toplevel_file) { int return_status; struct stat buf; - if (file_progress_show_deleting (ctx, s) == FILE_ABORT) + file_progress_show_deleting (ctx, s); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); - if (progress_count && mc_lstat (s, &buf)) { + if (tctx->progress_count && mc_lstat (s, &buf)) { /* ignore, most likely the mc_unlink fails, too */ buf.st_size = 0; } @@ -1262,16 +1291,14 @@ erase_file (FileOpContext *ctx, const char *s, off_t *progress_count, return return_status; } - if (progress_count) - return progress_update_one (ctx, progress_count, progress_bytes, - buf.st_size, is_toplevel_file); + if (tctx->progress_count) + return progress_update_one (tctx, ctx, buf.st_size, is_toplevel_file); else return FILE_CONT; } static FileProgressStatus -recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count, - double *progress_bytes) +recursive_erase (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s) { struct dirent *next; struct stat buf; @@ -1300,18 +1327,17 @@ recursive_erase (FileOpContext *ctx, const char *s, off_t *progress_count, } if (S_ISDIR (buf.st_mode)) return_status = - (recursive_erase - (ctx, path, progress_count, progress_bytes) - != FILE_CONT) ? FILE_RETRY : FILE_CONT; + (recursive_erase (tctx, ctx, path) != FILE_CONT) ? FILE_RETRY : FILE_CONT; else return_status = - erase_file (ctx, path, progress_count, progress_bytes, 0); + erase_file (tctx, ctx, path, 0); g_free (path); } mc_closedir (reading); if (return_status != FILE_CONT) return return_status; - if (file_progress_show_deleting (ctx, s) == FILE_ABORT) + file_progress_show_deleting (ctx, s); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -1352,8 +1378,7 @@ check_dir_is_empty (const char *path) } FileProgressStatus -erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count, - double *progress_bytes) +erase_dir (FileOpTotalContext *tctx, FileOpContext *ctx, const char *s) { FileProgressStatus error; @@ -1363,7 +1388,8 @@ erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count, if (strcmp (s, ".") == 0) return FILE_SKIP; - if (file_progress_show_deleting (ctx, s) == FILE_ABORT) + file_progress_show_deleting (ctx, s); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -1378,8 +1404,7 @@ erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count, if (error == 0) { /* not empty */ error = query_recursive (ctx, s); if (error == FILE_CONT) - return recursive_erase (ctx, s, progress_count, - progress_bytes); + return recursive_erase (tctx, ctx, s); else return error; } @@ -1405,7 +1430,8 @@ erase_dir_iff_empty (FileOpContext *ctx, const char *s) if (strcmp (s, ".") == 0) return FILE_SKIP; - if (file_progress_show_deleting (ctx, s) == FILE_ABORT) + file_progress_show_deleting (ctx, s); + if (check_progress_buttons (ctx) == FILE_ABORT) return FILE_ABORT; mc_refresh (); @@ -1610,7 +1636,7 @@ compute_dir_size (const char *dirname, const void *ui, * overwrite any files by doing the copy. */ static FileProgressStatus -panel_compute_totals (WPanel *panel, const void *ui, +panel_compute_totals (const WPanel *panel, const void *ui, compute_dir_size_callback cback, off_t *ret_marked, double *ret_total) { @@ -1636,7 +1662,7 @@ panel_compute_totals (WPanel *panel, const void *ui, dir_name = concat_dir_and_file (panel->cwd, panel->dir.list[i].fname); - status = compute_dir_size (dir_name, ui, cback, + status = compute_dir_size (dir_name, ui, cback, &subdir_count, &subdir_bytes); g_free (dir_name); @@ -1654,6 +1680,39 @@ panel_compute_totals (WPanel *panel, const void *ui, return FILE_CONT; } +/* Initialize variables for progress bars */ +static FileProgressStatus +panel_operate_init_totals (FileOperation operation, + const WPanel *panel, const char *source, + FileOpContext *ctx) +{ + FileProgressStatus status; + + if (operation != OP_MOVE && verbose && file_op_compute_totals) { + ComputeDirSizeUI *ui; + + ui = compute_dir_size_create_ui (); + + if (source != NULL) + status = compute_dir_size (source, ui, compute_dir_size_update_ui, + &ctx->progress_count, &ctx->progress_bytes); + else + status = panel_compute_totals (panel, ui, compute_dir_size_update_ui, + &ctx->progress_count, &ctx->progress_bytes); + + compute_dir_size_destroy_ui (ui); + + ctx->progress_totals_computed = (status == FILE_CONT); + } else { + status = FILE_CONT; + ctx->progress_count = panel->marked; + ctx->progress_bytes = panel->total; + ctx->progress_totals_computed = FALSE; + } + + return status; +} + /* * This array introduced to avoid translation problems. The former (op_names) * is assumed to be nouns, suitable in dialog box titles; this one should @@ -1702,7 +1761,7 @@ static const char *question_format = N_("%s?"); * src_stat is only used when single_source is not NULL. */ static char * -panel_operate_generate_prompt (const WPanel *panel, const int operation, +panel_operate_generate_prompt (const WPanel *panel, FileOperation operation, gboolean single_source, const struct stat *src_stat) { @@ -1809,14 +1868,14 @@ end_bg_process (FileOpContext *ctx, enum OperationMode mode) { * Performs one of the operations on the selection on the source_panel * (copy, delete, move). * - * Returns 1 if did change the directory - * structure, Returns 0 if user aborted + * Returns TRUE if did change the directory + * structure, Returns FALSE if user aborted * * force_single forces operation on the current entry and affects * default destination. Current filename is used as default. */ -int -panel_operate (void *source_panel, FileOperation operation, int force_single) +gboolean +panel_operate (void *source_panel, FileOperation operation, gboolean force_single) { WPanel *panel = (WPanel *) source_panel; const gboolean single_entry = force_single || (panel->marked <= 1) @@ -1831,16 +1890,13 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) char *dest = NULL; char *temp = NULL; char *save_cwd = NULL, *save_dest = NULL; - struct stat src_stat, dst_stat; + struct stat src_stat; int i; FileProgressStatus value; FileOpContext *ctx; + FileOpTotalContext *tctx; - off_t count = 0; - double bytes = 0; - - int dst_result; - int do_bg = 0; /* do background operation? */ + gboolean do_bg = FALSE; /* do background operation? */ #ifdef ENABLE_NLS static gboolean i18n_flag = FALSE; @@ -1870,11 +1926,13 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) if (!strcmp (source, "..")) { message (D_ERROR, MSG_ERROR, _(" Cannot operate on \"..\"! ")); - return 0; + return FALSE; } } ctx = file_op_context_new (operation); + tctx = file_op_total_context_new (); + gettimeofday (&(tctx->transfer_start), (struct timezone *) NULL); /* Show confirmation dialog */ if (operation != OP_DELETE) { @@ -1904,8 +1962,9 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) dest_dir_ = g_strdup (dest_dir); } if (dest_dir_ == NULL) { + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); - return 0; + return FALSE; } /* Generate confirmation prompt */ @@ -1921,9 +1980,10 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) g_free (dest_dir_); if (dest == NULL || dest[0] == '\0') { + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); g_free (dest); - return 0; + return FALSE; } } else if (confirm_delete) { char *format; @@ -1951,16 +2011,32 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) _("&Yes"), _("&No")); if (i != 0) { + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); - return 0; + return FALSE; } } + { + filegui_dialog_type_t dialog_type; + + if (operation == OP_DELETE) { + dialog_type = FILEGUI_DIALOG_DELETE_ITEM; + } else { + dialog_type = !((operation != OP_COPY) || (single_entry) || (force_single)) + ? FILEGUI_DIALOG_MULTI_ITEM + : FILEGUI_DIALOG_ONE_ITEM; + + if ((single_entry) && (operation == OP_COPY) && S_ISDIR (selection (panel)->st.st_mode)) + dialog_type = FILEGUI_DIALOG_MULTI_ITEM; + } + /* Background also need ctx->ui, but not full */ if (do_bg) - file_op_context_create_ui_without_init (ctx, 1); + file_op_context_create_ui_without_init (ctx, 1, dialog_type); else - file_op_context_create_ui (ctx, 1); + file_op_context_create_ui (ctx, 1, dialog_type); + } #ifdef WITH_BACKGROUND /* Did the user select to do a background operation? */ @@ -1980,7 +2056,7 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) mc_setctl (panel->cwd, VFS_SETCTL_FORGET, NULL); mc_setctl (dest, VFS_SETCTL_FORGET, NULL); /* file_op_context_destroy (ctx); */ - return 0; + return FALSE; } } #endif /* WITH_BACKGROUND */ @@ -1989,14 +2065,13 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) /* We do not want to trash cache every time file is created/touched. However, this will make our cache contain invalid data. */ - if (dest) { - if (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1)) - save_dest = g_strdup (dest); - } - if (panel->cwd) { - if (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1)) - save_cwd = g_strdup (panel->cwd); - } + if ((dest != NULL) + && (mc_setctl (dest, VFS_SETCTL_STALE_DATA, (void *) 1))) + save_dest = g_strdup (dest); + + if ((panel->cwd[0] != '\0') + && (mc_setctl (panel->cwd, VFS_SETCTL_STALE_DATA, (void *) 1))) + save_cwd = g_strdup (panel->cwd); /* Now, let's do the job */ @@ -2013,205 +2088,171 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) source_with_path = concat_dir_and_file (panel->cwd, source); #endif /* WITH_FULL_PATHS */ - if (operation == OP_DELETE) { - if (S_ISDIR (src_stat.st_mode)) - value = erase_dir (ctx, source_with_path, &count, &bytes); - else - value = - erase_file (ctx, source_with_path, &count, &bytes, 1); - } else { - temp = transform_source (ctx, source_with_path); - if (temp == NULL) { - value = transform_error; - } else { - char *repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest); - char *temp2 = concat_dir_and_file (repl_dest, temp); - g_free (repl_dest); - g_free (temp); - g_free(dest); - dest = temp2; - - switch (operation) { - case OP_COPY: - /* - * we use file_mask_op_follow_links only with OP_COPY, - */ - (*ctx->stat_func) (source_with_path, &src_stat); - - if (S_ISDIR (src_stat.st_mode)) { - value = - copy_dir_dir (ctx, source_with_path, dest, 1, - 0, 0, 0, &count, &bytes); - } else { - value = - copy_file_file (ctx, source_with_path, dest, 1, - &count, &bytes, 1); - } - break; - - case OP_MOVE: - if (S_ISDIR (src_stat.st_mode)) - value = - move_dir_dir (ctx, source_with_path, dest, - &count, &bytes); - else - value = - move_file_file (ctx, source_with_path, dest, - &count, &bytes); - break; - - default: - /* Unknown file operation */ - abort (); - } - } - } /* Copy or move operation */ - - if ((value == FILE_CONT) && !force_single) - unmark_files (panel); - } else { - /* Many files */ - /* Check destination for copy or move operation */ - if (operation != OP_DELETE) { - retry_many_dst_stat: - dst_result = mc_stat (dest, &dst_stat); - if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)) { - if (file_error - (_(" Destination \"%s\" must be a directory \n %s "), - dest) == FILE_RETRY) - goto retry_many_dst_stat; - goto clean_up; - } - } - - /* Initialize variables for progress bars */ - if (operation != OP_MOVE && verbose && file_op_compute_totals) { - ComputeDirSizeUI *ui; - FileProgressStatus status; - - ui = compute_dir_size_create_ui (); - status = panel_compute_totals (panel, - ui, compute_dir_size_update_ui, - &ctx->progress_count, &ctx->progress_bytes); - compute_dir_size_destroy_ui (ui); - - if (status != FILE_CONT) - goto clean_up; - - ctx->progress_totals_computed = 1; - } else { - ctx->progress_totals_computed = 0; - ctx->progress_count = panel->marked; - ctx->progress_bytes = panel->total; - } - - /* Loop for every file, perform the actual copy operation */ - for (i = 0; i < panel->count; i++) { - if (!panel->dir.list[i].f.marked) - continue; /* Skip the unmarked ones */ - - source = panel->dir.list[i].fname; - src_stat = panel->dir.list[i].st; - -#ifdef WITH_FULL_PATHS - g_free (source_with_path); - source_with_path = concat_dir_and_file (panel->cwd, source); -#endif /* WITH_FULL_PATHS */ - + if (panel_operate_init_totals (operation, panel, + source_with_path, ctx) == FILE_CONT) { if (operation == OP_DELETE) { if (S_ISDIR (src_stat.st_mode)) - value = - erase_dir (ctx, source_with_path, &count, &bytes); + value = erase_dir (tctx, ctx, source_with_path); else - value = - erase_file (ctx, source_with_path, &count, &bytes, - 1); + value = erase_file (tctx, ctx, source_with_path, 1); } else { temp = transform_source (ctx, source_with_path); if (temp == NULL) value = transform_error; else { - char *temp3; - char *repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest); - char *temp2 = concat_dir_and_file (repl_dest, temp); - g_free(repl_dest); - - g_free(temp); - temp3 = source_with_path; - source_with_path = strutils_shell_unescape(source_with_path); - g_free(temp3); - temp3 = temp2; - temp2 = strutils_shell_unescape(temp2); - g_free(temp3); + char *repl_dest, *temp2; + repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest); + temp2 = concat_dir_and_file (repl_dest, temp); + g_free (temp); + g_free (repl_dest); + g_free (dest); + dest = temp2; + switch (operation) { case OP_COPY: - /* - * we use file_mask_op_follow_links only with OP_COPY - */ - (*ctx->stat_func) (source_with_path, &src_stat); + /* we use file_mask_op_follow_links only with OP_COPY */ + ctx->stat_func (source_with_path, &src_stat); + if (S_ISDIR (src_stat.st_mode)) - value = - copy_dir_dir (ctx, source_with_path, temp2, - 1, 0, 0, 0, &count, &bytes); + value = copy_dir_dir (tctx, ctx, source_with_path, dest, + TRUE, FALSE, FALSE, NULL); else - value = - copy_file_file (ctx, source_with_path, - temp2, 1, &count, &bytes, - 1); - free_linklist (&dest_dirs); + value = copy_file_file (tctx, ctx, source_with_path, dest); break; case OP_MOVE: if (S_ISDIR (src_stat.st_mode)) - value = - move_dir_dir (ctx, source_with_path, temp2, - &count, &bytes); + value = move_dir_dir (tctx, ctx, source_with_path, dest); else - value = - move_file_file (ctx, source_with_path, - temp2, &count, &bytes); + value = move_file_file (tctx, ctx, source_with_path, dest); break; default: /* Unknown file operation */ abort (); } - g_free (temp2); } } /* Copy or move operation */ - if (value == FILE_ABORT) + if ((value == FILE_CONT) && !force_single) + unmark_files (panel); + } + } else { + /* Many files */ + + /* Check destination for copy or move operation */ + while (operation != OP_DELETE) { + int dst_result; + struct stat dst_stat; + + dst_result = mc_stat (dest, &dst_stat); + + if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode)) + break; + + if (file_error (_(" Destination \"%s\" must be a directory \n %s "), + dest) != FILE_RETRY) goto clean_up; + } - if (value == FILE_CONT) - do_file_mark (panel, i, 0); + if (panel_operate_init_totals (operation, panel, NULL, ctx) == FILE_CONT) { + /* Loop for every file, perform the actual copy operation */ + for (i = 0; i < panel->count; i++) { + if (!panel->dir.list[i].f.marked) + continue; /* Skip the unmarked ones */ - if (file_progress_show_count (ctx, count, ctx->progress_count) - == FILE_ABORT) - goto clean_up; + source = panel->dir.list[i].fname; + src_stat = panel->dir.list[i].st; - if (verbose - && file_progress_show_bytes (ctx, bytes, - ctx->progress_bytes) == - FILE_ABORT) - goto clean_up; +#ifdef WITH_FULL_PATHS + g_free (source_with_path); + source_with_path = concat_dir_and_file (panel->cwd, source); +#endif /* WITH_FULL_PATHS */ - if (operation != OP_DELETE && verbose - && file_progress_show (ctx, 0, 0) == FILE_ABORT) - goto clean_up; + if (operation == OP_DELETE) { + if (S_ISDIR (src_stat.st_mode)) + value = erase_dir (tctx, ctx, source_with_path); + else + value = erase_file (tctx, ctx, source_with_path, 1); + } else { + temp = transform_source (ctx, source_with_path); - mc_refresh (); - } /* Loop for every file */ + if (temp == NULL) + value = transform_error; + else { + char *temp2, *temp3, *repl_dest; + + repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest); + temp2 = concat_dir_and_file (repl_dest, temp); + g_free (temp); + g_free (repl_dest); + temp3 = source_with_path; + source_with_path = strutils_shell_unescape (source_with_path); + g_free (temp3); + temp3 = temp2; + temp2 = strutils_shell_unescape (temp2); + g_free (temp3); + + switch (operation) { + case OP_COPY: + /* we use file_mask_op_follow_links only with OP_COPY */ + ctx->stat_func (source_with_path, &src_stat); + if (S_ISDIR (src_stat.st_mode)) + value = copy_dir_dir (tctx, ctx, source_with_path, temp2, + TRUE, FALSE, FALSE, NULL); + else + value = copy_file_file (tctx, ctx, source_with_path, temp2); + free_linklist (&dest_dirs); + break; + + case OP_MOVE: + if (S_ISDIR (src_stat.st_mode)) + value = move_dir_dir (tctx, ctx, source_with_path, temp2); + else + value = move_file_file (tctx, ctx, source_with_path, temp2); + break; + + default: + /* Unknown file operation */ + abort (); + } + + g_free (temp2); + } + } /* Copy or move operation */ + + if (value == FILE_ABORT) + break; + + if (value == FILE_CONT) + do_file_mark (panel, i, 0); + + file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count); + + if (verbose) { + file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE); + + if (operation != OP_DELETE) + file_progress_show (ctx, 0, 0, "", FALSE); + } + + if (check_progress_buttons (ctx) == FILE_ABORT) + break; + + mc_refresh (); + } /* Loop for every file */ + } } /* Many entries */ + clean_up: /* Clean up */ - - if (save_cwd) { + if (save_cwd != NULL) { mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL); g_free (save_cwd); } - if (save_dest) { + + if (save_dest != NULL) { mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL); g_free (save_dest); } @@ -2224,6 +2265,7 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) g_free (dest); g_free (ctx->dest_mask); ctx->dest_mask = NULL; + #ifdef WITH_BACKGROUND /* Let our parent know we are saying bye bye */ if (we_are_background) { @@ -2239,7 +2281,7 @@ panel_operate (void *source_panel, FileOperation operation, int force_single) #endif /* WITH_BACKGROUND */ file_op_context_destroy (ctx); - return 1; + return TRUE; } /* }}} */ @@ -2307,21 +2349,21 @@ real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s gchar *text; if (ctx->recursive_result < RECURSIVE_ALWAYS) { - const char *msg = - mode == - Foreground ? - _("\n Directory not empty. \n" - " Delete it recursively? ") + const char *msg = mode == Foreground + ? _("\n Directory not empty. \n" + " Delete it recursively? ") : _("\n Background process: Directory not empty \n" " Delete it recursively? "); text = g_strconcat (_(" Delete: "), path_trunc (s, 30), " ", (char *) NULL); if (safe_delete) query_set_sel (1); - ctx->recursive_result = query_dialog (text, msg, D_ERROR, 5, - _("&Yes"), _("&No"), - _("A&ll"), _("Non&e"), - _("&Abort")); + + ctx->recursive_result = + (FileCopyMode) query_dialog (text, msg, D_ERROR, 5, + _("&Yes"), _("&No"), + _("A&ll"), _("Non&e"), + _("&Abort")); if (ctx->recursive_result != RECURSIVE_ABORT) do_refresh (); @@ -2338,7 +2380,6 @@ real_query_recursive (FileOpContext *ctx, enum OperationMode mode, const char *s return FILE_SKIP; case RECURSIVE_ABORT: - default: return FILE_ABORT; } diff --git a/src/file.h b/src/file.h index a25dad822..86b1c8f66 100644 --- a/src/file.h +++ b/src/file.h @@ -7,6 +7,7 @@ #define MC_FILE_H #include /* off_t */ +#include #include "lib/global.h" #include "dialog.h" /* Dlg_head */ @@ -15,18 +16,18 @@ struct link; -FileProgressStatus copy_file_file (FileOpContext *ctx, const char *s, const char *d, - int ask_overwrite, off_t *progress_count, - double *progress_bytes, int is_toplevel_file); -FileProgressStatus move_dir_dir (FileOpContext *ctx, const char *s, const char *d, - off_t *progress_count, double *progress_bytes); -FileProgressStatus copy_dir_dir (FileOpContext *ctx, const char *s, const char *d, int toplevel, - int move_over, int delete, struct link *parent_dirs, - off_t *progress_count, double *progress_bytes); -FileProgressStatus erase_dir (FileOpContext *ctx, const char *s, off_t *progress_count, - double *progress_bytes); +FileProgressStatus copy_file_file (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *src_path, const char *dst_path); +FileProgressStatus move_dir_dir (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *s, const char *d); +FileProgressStatus copy_dir_dir (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *s, const char *d, + gboolean toplevel, gboolean move_over, gboolean do_delete, + struct link *parent_dirs); +FileProgressStatus erase_dir (FileOpTotalContext *tctx, FileOpContext *ctx, + const char *s); -int panel_operate (void *source_panel, FileOperation op, int force_single); +gboolean panel_operate (void *source_panel, FileOperation op, gboolean force_single); extern int file_op_compute_totals; diff --git a/src/filegui.c b/src/filegui.c index b88af3b16..6d894ca20 100644 --- a/src/filegui.c +++ b/src/filegui.c @@ -79,6 +79,7 @@ #include "lib/global.h" #include "lib/tty/key.h" /* tty_get_event */ +#include "lib/mcconfig.h" #include "lib/search.h" #include "lib/vfs/mc-vfs/vfs.h" #include "lib/strescape.h" @@ -108,51 +109,8 @@ typedef enum { /* 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 { +typedef enum { REPLACE_YES = B_USER, REPLACE_NO, REPLACE_APPEND, @@ -162,19 +120,57 @@ enum { REPLACE_ABORT, REPLACE_SIZE, REPLACE_REGET -}; +} replace_action_t; -static int -filegui__check_attrs_on_fs(const char *fs_path) +/* This structure describes the UI and internal data required by a file + * operation context. + */ +typedef struct { + /* ETA and bps */ + gboolean showing_eta; + gboolean showing_bps; + + /* Dialog and widgets for the operation progress window */ + Dlg_head *op_dlg; + WLabel *file_string[2]; + WLabel *file_label[2]; + WGauge *progress_file_gauge; + WLabel *progress_file_label; + + WGauge *progress_total_gauge; + + WLabel *total_files_processed_label; + WLabel *time_label; + WLabel *total_bytes_label; + + /* Query replace dialog */ + Dlg_head *replace_dlg; + const char *replace_filename; + replace_action_t 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 58 +#define WY 11 +#define FCOPY_LABEL_X 3 + +static gboolean +filegui__check_attrs_on_fs (const char *fs_path) { #ifdef STATFS STRUCT_STATFS stfs; if (!setup_copymove_persistent_attr) - return 0; + return FALSE; - if (STATFS(fs_path, &stfs)!=0) - return 1; + if (STATFS (fs_path, &stfs) != 0) + return TRUE; # ifdef __linux__ switch ((filegui_nonattrs_fs_t) stfs.f_type) @@ -186,8 +182,7 @@ filegui__check_attrs_on_fs(const char *fs_path) case SMB_SUPER_MAGIC: case NCP_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: - return 0; - break; + return FALSE; } # elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) \ || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) @@ -197,21 +192,21 @@ filegui__check_attrs_on_fs(const char *fs_path) || !strcmp(stfs.f_fstypename, "procfs") || !strcmp(stfs.f_fstypename, "smbfs") || strstr(stfs.f_fstypename, "fusefs")) - return 0; + return FALSE; # elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE) if (!strcmp(stfs.f_basetype, "pcfs") || !strcmp(stfs.f_basetype, "ntfs") || !strcmp(stfs.f_basetype, "proc") || !strcmp(stfs.f_basetype, "smbfs") || !strcmp(stfs.f_basetype, "fuse")) - return 0; + return FALSE; # endif #endif /* STATFS */ - return 1; + return TRUE; } -static FileProgressStatus +FileProgressStatus check_progress_buttons (FileOpContext *ctx) { int c; @@ -236,11 +231,9 @@ check_progress_buttons (FileOpContext *ctx) 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; } @@ -249,38 +242,56 @@ check_progress_buttons (FileOpContext *ctx) /* {{{ File progress display routines */ void -file_op_context_create_ui_without_init (FileOpContext *ctx, int with_eta) +file_op_context_create_ui_without_init (FileOpContext *ctx, gboolean with_eta, filegui_dialog_type_t dialog_type) { FileOpContextUI *ui; - int x_size; - int minus; - int eta_offset; - const char *sixty; - const char *fifteen; + int minus, total_reserve=0; + const char *abort_button_label = N_("&Abort"); + const char *skip_button_label = N_("&Skip"); + int abort_button_width, skip_button_width, buttons_width; + int dlg_width; g_return_if_fail (ctx != NULL); g_return_if_fail (ctx->ui == NULL); +#ifdef ENABLE_NLS + abort_button_label = _(abort_button_label); + skip_button_label = _(skip_button_label); +#endif + + abort_button_width = str_term_width1 (abort_button_label) + 3; + skip_button_width = str_term_width1 (skip_button_label) + 3; + buttons_width = abort_button_width + skip_button_width + 2; + + dlg_width = max (WX, buttons_width + 6); + ui = g_new0 (FileOpContextUI, 1); ctx->ui = ui; + ctx->dialog_type = dialog_type; minus = verbose ? 0 : 3; - eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0; - sixty = ""; - fifteen = ""; + switch (dialog_type) { + case FILEGUI_DIALOG_ONE_ITEM: + total_reserve = 0; + break; + case FILEGUI_DIALOG_MULTI_ITEM: + total_reserve = 5; + break; + case FILEGUI_DIALOG_DELETE_ITEM: + total_reserve = -5; + break; + } - ctx->recursive_result = 0; + ctx->recursive_result = RECURSIVE_YES; - ui->replace_result = 0; + ui->replace_result = REPLACE_YES; 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], + create_dlg (0, 0, WY - minus + 1 + total_reserve, dlg_width, + dialog_colors, NULL, NULL, op_names[ctx->operation], DLG_CENTER | DLG_REVERSE); last_hint_line = the_hint->widget.y; @@ -288,49 +299,65 @@ file_op_context_create_ui_without_init (FileOpContext *ctx, int with_eta) 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)); + button_new (WY - minus - 2 + total_reserve, + dlg_width/2 + 1, FILE_ABORT, + NORMAL_BUTTON, abort_button_label, NULL)); add_widget (ui->op_dlg, - button_new (BY - minus, 14 + eta_offset, FILE_SKIP, - NORMAL_BUTTON, _("&Skip"), 0)); + button_new (WY - minus - 2 + total_reserve, + dlg_width/2 - 1 - skip_button_width, FILE_SKIP, + NORMAL_BUTTON, skip_button_label, NULL)); - 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, "")); + if (dialog_type == FILEGUI_DIALOG_MULTI_ITEM) { + add_widget (ui->op_dlg, hline_new (8, 1, dlg_width - 2)); - 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->total_bytes_label = + label_new (8, FCOPY_LABEL_X + 15, "")); + + add_widget (ui->op_dlg, ui->progress_total_gauge = + gauge_new (9, FCOPY_LABEL_X + 3, 0, 100, 0)); + + add_widget (ui->op_dlg, ui->total_files_processed_label = + label_new (11, FCOPY_LABEL_X, "")); + + add_widget (ui->op_dlg, ui->time_label = + label_new (12, FCOPY_LABEL_X, "")); + } + + add_widget (ui->op_dlg, ui->progress_file_label = + label_new (7, FCOPY_LABEL_X, "")); + + add_widget (ui->op_dlg, ui->progress_file_gauge = + gauge_new (6, FCOPY_LABEL_X + 3, 0, 100, 0)); add_widget (ui->op_dlg, ui->file_string[1] = - label_new (4, FCOPY_GAUGE_X, sixty)); + label_new (5, FCOPY_LABEL_X, "")); + add_widget (ui->op_dlg, ui->file_label[1] = - label_new (4, FCOPY_LABEL_X, fifteen)); + label_new (4, FCOPY_LABEL_X, "")); add_widget (ui->op_dlg, ui->file_string[0] = - label_new (3, FCOPY_GAUGE_X, sixty)); + label_new (3, FCOPY_LABEL_X, "")); add_widget (ui->op_dlg, ui->file_label[0] = - label_new (3, FCOPY_LABEL_X, fifteen)); + label_new (2, FCOPY_LABEL_X, "")); + + if ((right_panel == current_panel) + && !mc_config_get_bool (mc_main_config,"Layout", + "classic_progressbar", TRUE)) { + ui->progress_file_gauge->from_left_to_right = FALSE; + if (dialog_type == FILEGUI_DIALOG_MULTI_ITEM) + ui->progress_total_gauge->from_left_to_right = FALSE; + } } void -file_op_context_create_ui (FileOpContext *ctx, int with_eta) +file_op_context_create_ui (FileOpContext *ctx, gboolean with_eta, filegui_dialog_type_t dialog_type) { FileOpContextUI *ui; g_return_if_fail (ctx != NULL); g_return_if_fail (ctx->ui == NULL); - file_op_context_create_ui_without_init(ctx, with_eta); + file_op_context_create_ui_without_init (ctx, with_eta, dialog_type); ui = ctx->ui; /* We will manage the dialog without any help, that's why @@ -359,178 +386,170 @@ file_op_context_destroy_ui (FileOpContext *ctx) 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) +file_frmt_time (char *buffer, double eta_secs) { 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); + eta_hours = eta_secs / (60 * 60); + eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60; + eta_s = eta_secs - (eta_hours * 60 * 60 + eta_mins * 60); + g_snprintf (buffer, BUF_TINY, _("%d:%02d.%02d"), eta_hours, eta_mins, eta_s); } static void -file_bps_show (FileOpContext *ctx) +file_eta_prepare_for_show (char *buffer, double eta_secs, gboolean always_show) { - char bps_buffer[BUF_TINY]; - FileOpContextUI *ui; - - if (ctx->ui == NULL) + char _fmt_buff[BUF_TINY]; + if (eta_secs <= 0.5 && !always_show) { + *buffer = '\0'; 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); + } + if (eta_secs <= 0.5) + eta_secs = 1; + file_frmt_time (_fmt_buff, eta_secs); + g_snprintf (buffer, BUF_TINY, _("ETA %s"), _fmt_buff); } -FileProgressStatus -file_progress_show (FileOpContext *ctx, off_t done, off_t total) +static void +file_bps_prepare_for_show (char *buffer, long bps) +{ + if (bps > 1024 * 1024) { + g_snprintf (buffer, BUF_TINY, _("%.2f MB/s"), + bps / (1024 * 1024.0)); + } else if (bps > 1024) { + g_snprintf (buffer, BUF_TINY, _("%.2f KB/s"), + bps / 1024.0); + } else if (bps > 1) { + g_snprintf (buffer, BUF_TINY, _("%ld B/s"), + bps); + } else + *buffer = 0; +} + +/* + show progressbar for file +*/ +void +file_progress_show (FileOpContext *ctx, off_t done, off_t total, + const char *stalled_msg, gboolean force_update) { FileOpContextUI *ui; + char buffer[BUF_TINY]; + char buffer2[BUF_TINY]; + char buffer3[BUF_TINY]; - g_return_val_if_fail (ctx != NULL, FILE_CONT); + g_return_if_fail (ctx != NULL); if (ctx->ui == NULL) - return FILE_CONT; + return; 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); + return; + + if (total == 0) { + gauge_show (ui->progress_file_gauge, 0); + return; + } + + gauge_set_value (ui->progress_file_gauge, 1024, (int) (1024 * done / total)); + gauge_show (ui->progress_file_gauge, 1); + + if (!force_update) + return; + + if (ui->showing_eta && ctx->eta_secs > 0.5) { + file_eta_prepare_for_show (buffer2, ctx->eta_secs, FALSE); + file_bps_prepare_for_show (buffer3, ctx->bps); + g_snprintf (buffer, BUF_TINY, "%s (%s) %s", buffer2, buffer3, stalled_msg); + } else { + g_snprintf (buffer, BUF_TINY, "%s",stalled_msg); + } + + label_set_text (ui->progress_file_label, buffer); } -FileProgressStatus +void file_progress_show_count (FileOpContext *ctx, off_t done, off_t total) { + char buffer[BUF_TINY]; FileOpContextUI *ui; - g_return_val_if_fail (ctx != NULL, FILE_CONT); + g_return_if_fail (ctx != NULL); - if (ctx->ui == NULL) - return FILE_CONT; + if (ctx->dialog_type != FILEGUI_DIALOG_MULTI_ITEM || ctx->ui == NULL) + return; 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); + return; + + g_snprintf (buffer, BUF_TINY, _("Files processed: %llu of %llu"), done, total); + + label_set_text (ui->total_files_processed_label, buffer); } -FileProgressStatus -file_progress_show_bytes (FileOpContext *ctx, double done, double total) +void +file_progress_show_total (FileOpTotalContext *tctx, FileOpContext *ctx, double copyed_bytes, + gboolean need_show_total_summary) { + char buffer[BUF_TINY]; + char buffer2[BUF_TINY]; + char buffer3[BUF_TINY]; + char buffer4[BUF_TINY]; + struct timeval tv_current; FileOpContextUI *ui; - g_return_val_if_fail (ctx != NULL, FILE_CONT); - - if (ctx->ui == NULL) - return FILE_CONT; + if (ctx->dialog_type != FILEGUI_DIALOG_MULTI_ITEM || ctx->ui == NULL) + return; 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); + if (ctx->progress_bytes > 0 ){ + gauge_set_value (ui->progress_total_gauge, 1024, (int) (1024 * copyed_bytes/ ctx->progress_bytes)); + gauge_show (ui->progress_total_gauge, 1); } else - return show_no_bar (ctx, 2); + gauge_show (ui->progress_total_gauge, 0); + + + if (!need_show_total_summary && tctx->bps == 0) + return; + + gettimeofday (&tv_current, NULL); + file_frmt_time (buffer2, tv_current.tv_sec - tctx->transfer_start.tv_sec); + file_eta_prepare_for_show (buffer3, tctx->eta_secs, TRUE); + file_bps_prepare_for_show (buffer4, (long) tctx->bps); + + + + g_snprintf (buffer, BUF_TINY, _("Time: %s %s (%s)"), buffer2, buffer3, buffer4); + label_set_text (ui->time_label, buffer); + + size_trunc_len (buffer2, 5, tctx->copyed_bytes, 0); + size_trunc_len (buffer3, 5, ctx->progress_bytes, 0); + + g_snprintf (buffer, BUF_TINY, _(" Total: %s of %s "), buffer2, buffer3); + + label_set_text (ui->total_bytes_label, buffer); + } /* }}} */ -#define truncFileString(ui, s) str_trunc (s, ui->eta_extra + 47) -#define truncFileStringSecure(ui, s) path_trunc (s, ui->eta_extra + 47) +#define truncFileString(ui, s) str_trunc (s, 52) +#define truncFileStringSecure(ui, s) path_trunc (s, 52) -FileProgressStatus +void file_progress_show_source (FileOpContext *ctx, const char *s) { FileOpContextUI *ui; - g_return_val_if_fail (ctx != NULL, FILE_CONT); + g_return_if_fail (ctx != NULL); if (ctx->ui == NULL) - return FILE_CONT; + return; ui = ctx->ui; @@ -547,52 +566,46 @@ file_progress_show_source (FileOpContext *ctx, const char *s) 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 +void file_progress_show_target (FileOpContext *ctx, const char *s) { FileOpContextUI *ui; - g_return_val_if_fail (ctx != NULL, FILE_CONT); + g_return_if_fail (ctx != NULL); if (ctx->ui == NULL) - return FILE_CONT; + return; 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 +void file_progress_show_deleting (FileOpContext *ctx, const char *s) { FileOpContextUI *ui; - g_return_val_if_fail (ctx != NULL, FILE_CONT); + g_return_if_fail (ctx != NULL); if (ctx->ui == NULL) - return FILE_CONT; + return; 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); } /* @@ -600,9 +613,7 @@ file_progress_show_deleting (FileOpContext *ctx, const char *s) * but actually I'm not familiar with it and have not much time :( * alex */ - - -static int +static replace_action_t overwrite_query_dialog (FileOpContext *ctx, enum OperationMode mode) { #define ADD_RD_BUTTON(i)\ @@ -767,23 +778,11 @@ overwrite_query_dialog (FileOpContext *ctx, enum OperationMode mode) g_free (widgets_len); - return result; + return (result == B_CANCEL) ? REPLACE_ABORT : (replace_action_t) result; #undef ADD_RD_LABEL #undef ADD_RD_BUTTON } -void -file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg) -{ - FileOpContextUI *ui; - - g_return_if_fail (ctx != NULL); - g_return_if_fail (ctx->ui != NULL); - - 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, @@ -802,8 +801,6 @@ file_progress_real_query_replace (FileOpContext *ctx, ui->s_stat = _s_stat; ui->d_stat = _d_stat; ui->replace_result = overwrite_query_dialog (ctx, mode); - if (ui->replace_result == B_CANCEL) - ui->replace_result = REPLACE_ABORT; } switch (ui->replace_result) { @@ -826,7 +823,7 @@ file_progress_real_query_replace (FileOpContext *ctx, ctx->do_reget = _d_stat->st_size; case REPLACE_APPEND: - ctx->do_append = 1; + ctx->do_append = TRUE; case REPLACE_YES: case REPLACE_ALWAYS: @@ -992,7 +989,7 @@ file_mask_dialog (FileOpContext *ctx, FileOperation operation, /* destination */ fmd_widgets[7 - OFFSET].u.input.text = def_text_secure; - ctx->stable_symlinks = 0; + ctx->stable_symlinks = FALSE; *do_background = FALSE; { @@ -1018,18 +1015,18 @@ file_mask_dialog (FileOpContext *ctx, FileOperation operation, ctx->stat_func = mc_lstat; if (ctx->op_preserve) { - ctx->preserve = 1; + ctx->preserve = TRUE; ctx->umask_kill = 0777777; - ctx->preserve_uidgid = (geteuid () == 0) ? 1 : 0; + ctx->preserve_uidgid = (geteuid () == 0); } else { int i2; - ctx->preserve = ctx->preserve_uidgid = 0; + ctx->preserve = ctx->preserve_uidgid = FALSE; i2 = umask (0); umask (i2); ctx->umask_kill = i2 ^ 0777777; } - if (!dest_dir || !*dest_dir) { + if ((dest_dir == NULL) || (*dest_dir == '\0')) { g_free (def_text_secure); g_free (source_mask); return dest_dir; diff --git a/src/filegui.h b/src/filegui.h index f61fb88b3..1cb372699 100644 --- a/src/filegui.h +++ b/src/filegui.h @@ -9,9 +9,31 @@ #include "lib/global.h" #include "fileopctx.h" +typedef enum { + FILEGUI_DIALOG_ONE_ITEM, + FILEGUI_DIALOG_MULTI_ITEM, + FILEGUI_DIALOG_DELETE_ITEM +} filegui_dialog_type_t; + +void file_op_context_create_ui (FileOpContext *ctx, gboolean with_eta, filegui_dialog_type_t dialog_type); +void file_op_context_create_ui_without_init (FileOpContext *ctx, gboolean with_eta, filegui_dialog_type_t dialog_type); +void file_op_context_destroy_ui (FileOpContext *ctx); + + char *file_mask_dialog (FileOpContext *ctx, FileOperation operation, gboolean only_one, const char *format, const void *text, const char *def_text, gboolean *do_background); +FileProgressStatus check_progress_buttons (FileOpContext *ctx); + +void file_progress_show (FileOpContext *ctx, off_t done, off_t total, + const char *stalled_msg, gboolean force_update); +void file_progress_show_count (FileOpContext *ctx, off_t done, off_t total); +void file_progress_show_total (FileOpTotalContext *tctx, FileOpContext *ctx, + double copyed_bytes, gboolean need_show_total_summary); +void file_progress_show_source (FileOpContext *ctx, const char *path); +void file_progress_show_target (FileOpContext *ctx, const char *path); +void file_progress_show_deleting (FileOpContext *ctx, const char *path); + #endif /* MC_FILEGUI_H */ diff --git a/src/fileopctx.c b/src/fileopctx.c index 589b708aa..604796150 100644 --- a/src/fileopctx.c +++ b/src/fileopctx.c @@ -55,10 +55,10 @@ file_op_context_new (FileOperation op) ctx->eta_secs = 0.0; ctx->progress_bytes = 0.0; ctx->op_preserve = TRUE; - ctx->do_reget = TRUE; + ctx->do_reget = 1; ctx->stat_func = mc_lstat; ctx->preserve = TRUE; - ctx->preserve_uidgid = (geteuid () == 0) ? TRUE : FALSE; + ctx->preserve_uidgid = (geteuid () == 0); ctx->umask_kill = 0777777; ctx->erase_at_end = TRUE; @@ -87,3 +87,20 @@ file_op_context_destroy (FileOpContext *ctx) g_free (ctx); } + +FileOpTotalContext * +file_op_total_context_new (void) +{ + FileOpTotalContext *tctx; + tctx = g_new0 (FileOpTotalContext, 1); + tctx->ask_overwrite = TRUE; + tctx->is_toplevel_file = TRUE; + return tctx; +} + +void +file_op_total_context_destroy (FileOpTotalContext *tctx) +{ + g_return_if_fail (tctx != NULL); + g_free (tctx); +} diff --git a/src/fileopctx.h b/src/fileopctx.h index 305fe6389..30119c184 100644 --- a/src/fileopctx.h +++ b/src/fileopctx.h @@ -17,14 +17,26 @@ #include #include +#include + +#include "lib/global.h" struct mc_search_struct; + typedef enum { - OP_COPY, - OP_MOVE, - OP_DELETE + OP_COPY = 0, + OP_MOVE = 1, + OP_DELETE = 2 } FileOperation; +typedef enum { + RECURSIVE_YES = 0, + RECURSIVE_NO = 1, + RECURSIVE_ALWAYS = 2, + RECURSIVE_NEVER = 3, + RECURSIVE_ABORT = 4 +} FileCopyMode; + typedef int (*mc_stat_fn) (const char *filename, struct stat *buf); /* This structure describes a context for file operations. It is used to update @@ -44,7 +56,8 @@ typedef struct FileOpContext { long bps_time; /* Whether the panel total has been computed */ - int progress_totals_computed; + gboolean progress_totals_computed; + int dialog_type; /* Counters for progress indicators */ off_t progress_count; @@ -55,25 +68,25 @@ typedef struct FileOpContext { * to preserve file attributs when moving files across filesystem boundaries * (we want to keep the value of the checkbox between copy operations). */ - int op_preserve; + gboolean op_preserve; /* Result from the recursive query */ - int recursive_result; + FileCopyMode recursive_result; /* Whether to do a reget */ off_t do_reget; /* Controls appending to files */ - int do_append; + gboolean do_append; /* Whether to stat or lstat */ - int follow_links; + gboolean follow_links; /* Pointer to the stat function we will use */ mc_stat_fn stat_func; /* Whether to recompute symlinks */ - int stable_symlinks; + gboolean stable_symlinks; /* Preserve the original files' owner, group, permissions, and * timestamps (owner, group only as root). @@ -83,7 +96,7 @@ typedef struct FileOpContext { /* If running as root, preserve the original uid/gid (we don't want to * try chown for non root) preserve_uidgid = preserve && uid == 0 */ - int preserve_uidgid; + gboolean preserve_uidgid; /* The bits to preserve in created files' modes on file copy */ int umask_kill; @@ -91,7 +104,9 @@ typedef struct FileOpContext { /* The mask of files to actually operate on */ char *dest_mask; - struct mc_search_struct *search_handle; + /* search handler */ + struct mc_search_struct *search_handle; + /* Whether to dive into subdirectories for recursive operations */ int dive_into_subdirs; @@ -99,24 +114,40 @@ typedef struct FileOpContext { * successfully copied files when all files below the directory and its * subdirectories were processed. * - * If erase_at_end is zero files will be deleted immediately after their + * If erase_at_end is FALSE files will be deleted immediately after their * successful copy (Note: this behavior is not tested and at the moment * it can't be changed at runtime). */ - int erase_at_end; + gboolean erase_at_end; /* PID of the child for background operations */ pid_t pid; /* User interface data goes here */ - void *ui; } FileOpContext; +typedef struct { + off_t progress_count; + double progress_bytes; + double copyed_bytes; + size_t bps; + size_t bps_count; + struct timeval transfer_start; + double eta_secs; + + gboolean ask_overwrite; + gboolean is_toplevel_file; + +} FileOpTotalContext; + FileOpContext *file_op_context_new (FileOperation op); void file_op_context_destroy (FileOpContext *ctx); +FileOpTotalContext *file_op_total_context_new (void); +void file_op_total_context_destroy (FileOpTotalContext *tctx); + extern const char *op_names [3]; @@ -127,14 +158,6 @@ typedef enum { FILE_ABORT = 3 } FileProgressStatus; -typedef enum { - RECURSIVE_YES, - RECURSIVE_NO, - RECURSIVE_ALWAYS, - RECURSIVE_NEVER, - RECURSIVE_ABORT -} FileCopyMode; - /* First argument passed to real functions */ enum OperationMode { Foreground, @@ -143,19 +166,6 @@ enum OperationMode { /* The following functions are implemented separately by each port */ -void file_op_context_create_ui (FileOpContext *ctx, int with_eta); -void file_op_context_create_ui_without_init (FileOpContext *ctx, int with_eta); -void file_op_context_destroy_ui (FileOpContext *ctx); - -FileProgressStatus file_progress_show (FileOpContext *ctx, off_t done, off_t total); -FileProgressStatus file_progress_show_count (FileOpContext *ctx, off_t done, off_t total); -FileProgressStatus file_progress_show_bytes (FileOpContext *ctx, double done, double total); -FileProgressStatus file_progress_show_source (FileOpContext *ctx, const char *path); -FileProgressStatus file_progress_show_target (FileOpContext *ctx, const char *path); -FileProgressStatus file_progress_show_deleting (FileOpContext *ctx, const char *path); - -void file_progress_set_stalled_label (FileOpContext *ctx, const char *stalled_msg); - FileProgressStatus file_progress_real_query_replace (FileOpContext *ctx, enum OperationMode mode, const char *destname, diff --git a/src/tree.c b/src/tree.c index 8ab1d1403..109eeb1f5 100644 --- a/src/tree.c +++ b/src/tree.c @@ -66,6 +66,7 @@ #include "keybind.h" #include "history.h" #include "tree.h" +#include "filegui.h" const global_keymap_t *tree_map; @@ -634,21 +635,26 @@ tree_copy (WTree *tree, const char *default_dest) { char msg [BUF_MEDIUM]; char *dest; - off_t count = 0; - double bytes = 0; - FileOpContext *ctx; if (tree->selected_ptr == NULL) return; g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"), str_trunc (tree->selected_ptr->name, 50)); - dest = input_expand_dialog (Q_("DialogTitle|Copy"), msg, MC_HISTORY_FM_TREE_COPY, default_dest); + dest = input_expand_dialog (Q_("DialogTitle|Copy"), + msg, MC_HISTORY_FM_TREE_COPY, default_dest); if (dest != NULL && *dest != '\0') { + FileOpContext *ctx; + FileOpTotalContext *tctx; + ctx = file_op_context_new (OP_COPY); - file_op_context_create_ui (ctx, FALSE); - copy_dir_dir (ctx, tree->selected_ptr->name, dest, 1, 0, 0, 0, &count, &bytes); + tctx = file_op_total_context_new (); + file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM); + tctx->ask_overwrite = FALSE; + tctx->is_toplevel_file = FALSE; + copy_dir_dir (tctx, ctx, tree->selected_ptr->name, dest, TRUE, FALSE, FALSE, NULL); + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); } @@ -661,9 +667,8 @@ tree_move (WTree *tree, const char *default_dest) char msg [BUF_MEDIUM]; char *dest; struct stat buf; - double bytes = 0; - off_t count = 0; FileOpContext *ctx; + FileOpTotalContext *tctx; if (tree->selected_ptr == NULL) return; @@ -692,8 +697,10 @@ tree_move (WTree *tree, const char *default_dest) } ctx = file_op_context_new (OP_MOVE); - file_op_context_create_ui (ctx, FALSE); - move_dir_dir (ctx, tree->selected_ptr->name, dest, &count, &bytes); + tctx = file_op_total_context_new (); + file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM); + move_dir_dir (tctx, ctx, tree->selected_ptr->name, dest); + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); g_free (dest); @@ -723,9 +730,8 @@ static void tree_rmdir (void *data) { WTree *tree = data; - off_t count = 0; - double bytes = 0; FileOpContext *ctx; + FileOpTotalContext *tctx; if (!tree->selected_ptr) return; @@ -745,9 +751,12 @@ tree_rmdir (void *data) } ctx = file_op_context_new (OP_DELETE); - file_op_context_create_ui (ctx, FALSE); - if (erase_dir (ctx, tree->selected_ptr->name, &count, &bytes) == FILE_CONT) + tctx = file_op_total_context_new (); + + file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM); + if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT) tree_forget (tree); + file_op_total_context_destroy (tctx); file_op_context_destroy (ctx); } diff --git a/src/widget.c b/src/widget.c index cfa485f6d..4df7a5a66 100644 --- a/src/widget.c +++ b/src/widget.c @@ -785,6 +785,66 @@ label_new (int y, int x, const char *text) return l; } +static cb_ret_t +hline_callback (Widget *w, widget_msg_t msg, int parm) +{ + WHLine *l = (WHLine *) w; + Dlg_head *h = l->widget.parent; + + switch (msg) { + case WIDGET_INIT: + case WIDGET_RESIZED: + if (l->auto_adjust_cols) { + if (((w->parent->flags & DLG_COMPACT) != 0)) { + w->x = w->parent->x; + w->cols = w->parent->cols; + } else { + w->x = w->parent->x + 1; + w->cols = w->parent->cols - 2; + } + } + + case WIDGET_FOCUS: + /* We don't want to get the focus */ + return MSG_NOT_HANDLED; + + case WIDGET_DRAW: + if (l->transparent) + tty_setcolor (DEFAULT_COLOR); + else + tty_setcolor (DLG_NORMALC (h)); + + tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2); + + if (l->auto_adjust_cols) { + widget_move (w, 0, 0); + tty_print_alt_char (ACS_LTEE); + widget_move (w, 0, w->cols - 1); + tty_print_alt_char (ACS_RTEE); + } + return MSG_HANDLED; + + default: + return default_proc (msg, parm); + } +} + + +WHLine * +hline_new (int y, int x, int width) +{ + WHLine *l; + int cols = width; + int lines = 1; + + l = g_new (WHLine, 1); + init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL); + l->auto_adjust_cols = (width < 0); + l->transparent = FALSE; + widget_want_cursor (l->widget, 0); + return l; +} + /* Gauge widget (progress indicator) */ /* Currently width is hardcoded here for text mode */ @@ -825,10 +885,19 @@ gauge_callback (Widget *w, widget_msg_t msg, int parm) percentage = (200 * done / total + 1) / 2; columns = (2 * (gauge_len - 7) * done / total + 1) / 2; tty_print_char ('['); - tty_setcolor (GAUGE_COLOR); - tty_printf ("%*s", (int) columns, ""); - tty_setcolor (DLG_NORMALC (h)); - tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage); + if (g->from_left_to_right) { + tty_setcolor (GAUGE_COLOR); + tty_printf ("%*s", (int) columns, ""); + tty_setcolor (DLG_NORMALC (h)); + tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage); + } else { + tty_setcolor (DLG_NORMALC (h)); + tty_printf ("%*s", gauge_len - columns - 7, ""); + tty_setcolor (GAUGE_COLOR); + tty_printf ("%*s", columns, ""); + tty_setcolor (DLG_NORMALC (h)); + tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage); + } } return MSG_HANDLED; } @@ -869,6 +938,7 @@ gauge_new (int y, int x, int shown, int max, int current) max = 1; /* I do not like division by zero :) */ g->max = max; g->current = current; + g->from_left_to_right = TRUE; widget_want_cursor (g->widget, 0); return g; } diff --git a/src/widget.h b/src/widget.h index a7590935f..00c9269b8 100644 --- a/src/widget.h +++ b/src/widget.h @@ -89,6 +89,7 @@ typedef struct WGauge { int shown; int max; int current; + gboolean from_left_to_right; } WGauge; GList *history_get (const char *input_name); @@ -128,6 +129,12 @@ typedef struct { int transparent; /* Paint in the default color fg/bg */ } WLabel; +typedef struct { + Widget widget; + gboolean auto_adjust_cols; /* Compute widget.cols from parent width? */ + gboolean transparent; /* Paint in the default color fg/bg */ +} WHLine; + typedef struct WLEntry { char *text; /* Text to display */ int hotkey; @@ -187,6 +194,8 @@ WRadio *radio_new (int y, int x, int count, const char **text); WCheck *check_new (int y, int x, int state, const char *text); WInput *input_new (int y, int x, int color, int len, const char *text, const char *histname, INPUT_COMPLETE_FLAGS completion_flags); WLabel *label_new (int y, int x, const char *text); +WHLine *hline_new (int y, int x, int width); + WGauge *gauge_new (int y, int x, int shown, int max, int current); WListbox *listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback); WButtonBar *buttonbar_new (gboolean visible);