JJ_MKVMaker/lib/ui/explorer/rename_flow.dart

87 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import '../../core/batch_rename.dart';
import '../../core/file_ops.dart';
import '../../core/vfs.dart';
import '../../l10n/tr.dart';
import '../batch_rename_dialog.dart';
import '../notice.dart';
import 'explorer_controller.dart';
import 'explorer_pane.dart';
import 'name_dialog.dart';
import 'write_guard.dart';
/// 약속 (E5d-6): 이름 바꾸기 - 하나 ([rename], 이름 창) · 여럿 ([batch], 78: 번호는 목록에 보이는 차례대로, 끝나면 [되돌리기]).
/// 바꾼 뒤 목록을 다시 읽고 표시 · 초점 · 숨긴 목록을 새 이름으로. 읽기 전용인 곳은 [ExplorerGuard] 가 막고, 못 하면 까닭을 알림
class ExplorerRenames {
ExplorerRenames(this.ctl, this.guard);
final ExplorerController ctl;
final ExplorerGuard guard;
Future<void> rename(BuildContext context, ExplorerPane pane, String path) async {
if (guard.blockArchive([path])) return;
final name = await askName(context, tr('이름 변경'), vBasename(path));
if (name == null || name == vBasename(path)) return;
try {
final now = await FileOps.rename(path, name);
await ctl.followHidden(moved: [(path, now)]);
await ctl.refreshAll([vDirname(path)]);
for (final x in ctl.panes) {
x.sel.rename(path, now);
}
pane.focused = now;
pane.changed();
} catch (e) {
ctl.host.snack(trf('이름을 바꿀 수 없습니다: {0}', [e]));
}
}
/// 78: 일괄 이름 바꾸기 - 번호는 목록에 보이는 차례대로. 끝나면 [되돌리기]
Future<void> batch(BuildContext context, ExplorerPane pane, List<String> paths) async {
if (guard.blockArchive(paths)) return;
int at(String x) {
final i = (pane.cache[vDirname(x)] ?? const <FileEntry>[]).indexWhere((e) => samePath(e.path, x));
return i < 0 ? 1 << 30 : i;
}
final ordered = [...paths]..sort((a, b) {
final d = vDirname(a).compareTo(vDirname(b));
return d != 0 ? d : at(a).compareTo(at(b));
});
final done = await showBatchRename(context, ordered,
isDir: vIsDirSync,
initial: RenameRule.fromJson(ctl.c.settings.explorer.batchRenameLast),
onRun: (r) => ctl.c.updateSettings((s) => s.explorer.batchRenameLast = r.toJson()));
if (done == null || done.isEmpty || !context.mounted) return;
await _after(pane, done);
if (!context.mounted) return;
showNotice(ScaffoldMessenger.of(context),
text: trf('{0}개의 이름을 바꿨습니다.', [done.length]),
action: (
tr('되돌리기'),
() async {
try {
final back = await runBatchRename([for (final (orig, now) in done) (now, vBasename(orig))]);
if (context.mounted) await _after(pane, back);
} on BatchRenameException catch (e) {
ctl.host.snack(trf('되돌리지 못했습니다: {0}', [e]));
}
}
));
}
/// 바꾼 뒤: 목록을 다시 읽고, 표시 · 초점을 새 이름으로
Future<void> _after(ExplorerPane pane, List<(String, String)> moved) async {
await ctl.followHidden(moved: moved);
await ctl.refreshAll({for (final (a, _) in moved) vDirname(a)});
for (final x in ctl.panes) {
for (final (from, to) in moved) {
x.sel.rename(from, to);
if (x.focused != null && samePath(x.focused!, from)) x.focused = to;
}
x.changed();
}
}
}