87 lines
3.4 KiB
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();
|
|
}
|
|
}
|
|
}
|