Support #2010 一键导出待更新模组列表 (#2354)

* Fix #2010

* Improve behaviour

* Add License
This commit is contained in:
Burning_TNT
2023-07-07 14:58:14 +08:00
committed by GitHub
parent 008fe17035
commit 2414db1c61
3 changed files with 239 additions and 1 deletions

View File

@@ -42,8 +42,14 @@ import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.CSVTable;
import java.net.URL; import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -100,6 +106,9 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
actions.setPadding(new Insets(8)); actions.setPadding(new Insets(8));
actions.setAlignment(Pos.CENTER_RIGHT); actions.setAlignment(Pos.CENTER_RIGHT);
JFXButton exportListButton = FXUtils.newRaisedButton(i18n("button.export"));
exportListButton.setOnAction(e -> exportList());
JFXButton nextButton = FXUtils.newRaisedButton(i18n("mods.check_updates.update")); JFXButton nextButton = FXUtils.newRaisedButton(i18n("mods.check_updates.update"));
nextButton.setOnAction(e -> updateMods()); nextButton.setOnAction(e -> updateMods());
@@ -107,7 +116,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent())); cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
onEscPressed(this, cancelButton::fire); onEscPressed(this, cancelButton::fire);
actions.getChildren().setAll(nextButton, cancelButton); actions.getChildren().setAll(exportListButton, nextButton, cancelButton);
setBottom(actions); setBottom(actions);
} }
@@ -140,6 +149,36 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
TaskCancellationAction.NORMAL); TaskCancellationAction.NORMAL);
} }
private void exportList() {
Path path = Paths.get("hmcl-mod-update-list-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".csv").toAbsolutePath();
Controllers.taskDialog(Task.runAsync(() -> {
CSVTable csvTable = CSVTable.createEmpty();
csvTable.set(0, 0, "Source File Name");
csvTable.set(1, 0, "Current Version");
csvTable.set(2, 0, "Target Version");
csvTable.set(3, 0, "Update Source");
for (int i = 0; i < objects.size(); i++) {
csvTable.set(0, i + 1, objects.get(i).fileName.get());
csvTable.set(1, i + 1, objects.get(i).currentVersion.get());
csvTable.set(2, i + 1, objects.get(i).targetVersion.get());
csvTable.set(3, i + 1, objects.get(i).source.get());
}
csvTable.write(Files.newOutputStream(path));
FXUtils.showFileInExplorer(path);
}).whenComplete(exception -> {
if (exception == null) {
Controllers.dialog(path.toString(), i18n("message.success"));
} else {
Controllers.dialog("", i18n("message.error"), MessageDialogPane.MessageType.ERROR);
}
}), i18n("button.export"), TaskCancellationAction.NORMAL);
}
@Override @Override
public ReadOnlyObjectWrapper<State> stateProperty() { public ReadOnlyObjectWrapper<State> stateProperty() {
return state; return state;

View File

@@ -0,0 +1,115 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
public final class InfiniteSizeList<T> extends ArrayList<T> {
private int actualSize;
public InfiniteSizeList(int initialCapacity) {
super(initialCapacity);
this.actualSize = 0;
}
public InfiniteSizeList() {
this.actualSize = 0;
}
public InfiniteSizeList(@NotNull Collection<? extends T> c) {
super(c);
this.actualSize = 0;
for (int i = super.size() - 1; i >= 0; i--) {
if (super.get(i) != null) {
actualSize = i + 1;
break;
}
}
}
@Override
public T get(int index) {
if (index >= super.size()) {
return null;
}
return super.get(index);
}
@Override
public T set(int index, T element) {
if (element == null) { // The element is null.
if (index >= super.size()) {
return null; // The element (actually null) is out of the allocated size.
}
T previous = super.get(index);
if (previous != null) { // !null -> null
super.set(index, null);
if (index == this.actualSize - 1) { // Recalculate the actualSize.
this.actualSize = 0;
for (int i = index - 1; i >= 0; i--) {
if (super.get(i) != null) {
this.actualSize = i + 1;
break;
}
}
}
return previous;
} else { // null -> null
return null;
}
} else {
// The element isn't null.
if (index >= super.size()) {
allocate0(index);
}
T previous = super.get(index);
if (previous != null) { // !null -> !null
super.set(index, element);
return previous;
} else { // null -> !null
super.set(index, element);
if (index >= this.actualSize) {
this.actualSize = index + 1;
}
return null;
}
}
}
private void allocate0(int index) {
super.ensureCapacity(index + 1);
}
@Override
public int size() {
return actualSize;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util.io;
import org.jackhuang.hmcl.util.InfiniteSizeList;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
public final class CSVTable {
private final List<List<String>> table = new InfiniteSizeList<>();
private CSVTable() {
}
public static CSVTable createEmpty() {
return new CSVTable();
}
public String get(int x, int y) {
List<String> row = table.get(y);
if (row == null) {
return null;
}
return row.get(x);
}
public void set(int x, int y, String txt) {
List<String> row = table.get(y);
if (row == null) {
row = new InfiniteSizeList<>(x);
table.set(y, row);
}
row.set(x, txt);
}
public void write(OutputStream outputStream) throws IOException {
try (PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
outputStream, StandardCharsets.UTF_8)), false
)) {
for (List<String> row : this.table) {
if (row != null) {
for (int j = 0; j < row.size(); j++) {
String txt = row.get(j);
if (txt != null) {
printWriter.write(this.escape(txt));
}
if (j != row.size() - 1) {
printWriter.write(',');
}
}
}
printWriter.write('\n');
}
}
}
private String escape(String txt) {
if (!txt.contains("\"") && !txt.contains(",")) {
return txt;
} else {
return "\"" + txt.replace("\"", "\"\"") + "\"";
}
}
}