/*
* SK's Minecraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors
* Please see LICENSE.txt for license information.
*/
package com.skcraft.launcher.creator.controller;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.skcraft.concurrency.Deferred;
import com.skcraft.concurrency.Deferreds;
import com.skcraft.concurrency.SettableProgress;
import com.skcraft.launcher.creator.dialog.GenerateListingDialog;
import com.skcraft.launcher.creator.dialog.ManifestEntryDialog;
import com.skcraft.launcher.creator.model.creator.ManifestEntry;
import com.skcraft.launcher.creator.model.creator.Workspace;
import com.skcraft.launcher.creator.model.swing.ListingType;
import com.skcraft.launcher.creator.model.swing.ManifestEntryTableModel;
import com.skcraft.launcher.dialog.ProgressDialog;
import com.skcraft.launcher.persistence.Persistence;
import com.skcraft.launcher.swing.SwingHelper;
import com.skcraft.launcher.util.SwingExecutor;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.stream.Collectors;
public class GenerateListingController {
private final GenerateListingDialog dialog;
private final Workspace workspace;
private final List<ManifestEntry> manifestEntries;
private final ListeningExecutorService executor;
private final ManifestEntryTableModel manifestTableModel;
public GenerateListingController(GenerateListingDialog dialog, Workspace workspace, List<ManifestEntry> manifestEntries, ListeningExecutorService executor) {
this.dialog = dialog;
this.workspace = workspace;
this.manifestEntries = manifestEntries;
this.executor = executor;
this.manifestTableModel = new ManifestEntryTableModel(manifestEntries);
dialog.getManifestsTable().setModel(manifestTableModel);
dialog.getManifestsTableAdjuster().adjustColumns();
initListeners();
setListingType(workspace.getPackageListingType());
}
public void setOutputDir(File dir) {
dialog.getDestDirField().setPath(dir.getAbsolutePath());
}
public void setListingType(ListingType type) {
dialog.getListingTypeCombo().setSelectedItem(type);
}
public void show() {
dialog.setVisible(true);
}
public Optional<ManifestEntry> getManifestFromIndex(int selectedIndex) {
if (selectedIndex >= 0) {
ManifestEntry manifest = manifestEntries.get(selectedIndex);
if (manifest != null) {
return Optional.fromNullable(manifest);
}
}
return Optional.absent();
}
public Optional<ManifestEntry> getSelectedManifest() {
JTable table = dialog.getManifestsTable();
int selectedIndex = table.getSelectedRow();
if (selectedIndex >= 0) {
selectedIndex = table.convertRowIndexToModel(selectedIndex);
ManifestEntry manifest = manifestEntries.get(selectedIndex);
if (manifest != null) {
return Optional.fromNullable(manifest);
}
}
SwingHelper.showErrorDialog(dialog, "Please select a modpack from the list.", "Error");
return Optional.absent();
}
private void updateManifestEntryInTable(ManifestEntry manifestEntry) {
int index = manifestEntries.indexOf(manifestEntry);
if (index >= 0) {
manifestTableModel.fireTableRowsUpdated(index, index);
}
}
private void initListeners() {
dialog.getManifestsTable().addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
JTable table = (JTable) e.getSource();
Point point = e.getPoint();
int selectedIndex = table.rowAtPoint(point);
if (selectedIndex >= 0) {
selectedIndex = table.convertRowIndexToModel(selectedIndex);
Optional<ManifestEntry> optional = getManifestFromIndex(selectedIndex);
if (optional.isPresent()) {
if (showModifyDialog(optional.get())) {
updateManifestEntryInTable(optional.get());
}
}
}
}
}
});
dialog.getListingTypeCombo().addItemListener(e -> {
ListingType type = (ListingType) e.getItem();
dialog.getGameKeyWarning().setVisible(!type.isGameKeyCompatible());
});
dialog.getEditManifestButton().addActionListener(e -> {
Optional<ManifestEntry> optional = getSelectedManifest();
if (optional.isPresent()) {
if (showModifyDialog(optional.get())) {
updateManifestEntryInTable(optional.get());
}
}
});
dialog.getGenerateButton().addActionListener(e -> tryGenerate());
dialog.getCancelButton().addActionListener(e -> dialog.dispose());
}
private boolean showModifyDialog(ManifestEntry manifestEntry) {
ManifestEntryDialog modifyDialog = new ManifestEntryDialog(dialog);
modifyDialog.setTitle("Modify " + manifestEntry.getManifestInfo().getLocation());
ManifestEntryController controller = new ManifestEntryController(modifyDialog, manifestEntry);
return controller.show();
}
private boolean tryGenerate() {
String path = dialog.getDestDirField().getPath().trim();
if (path.isEmpty()) {
SwingHelper.showErrorDialog(dialog, "A directory must be entered.", "Error");
return false;
}
List<ManifestEntry> selected = manifestEntries.stream()
.filter(ManifestEntry::isSelected)
.sorted()
.collect(Collectors.toCollection(Lists::newArrayList));
if (selected.isEmpty()) {
SwingHelper.showErrorDialog(dialog, "At least one modpack must be selected to appear in the package list.", "Error");
return false;
}
ListingType listingType = (ListingType) dialog.getListingTypeCombo().getSelectedItem();
File destDir = new File(path);
destDir.mkdirs();
File file = new File(destDir, listingType.getFilename());
workspace.setPackageListingEntries(selected);
workspace.setPackageListingType(listingType);
Persistence.commitAndForget(workspace);
SettableProgress progress = new SettableProgress("Generating package listing...", -1);
Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(() -> listingType.generate(selected)))
.thenTap(() -> progress.set("Deleting older package listing files...", -1))
.thenApply(input -> {
for (ListingType otherListingType : ListingType.values()) {
File f = new File(destDir, otherListingType.getFilename());
if (f.exists()) {
f.delete();
}
}
return input;
})
.thenTap(() -> progress.set("Writing package listing to disk...", -1))
.thenApply(input -> {
try {
Files.write(input, file, Charset.forName("UTF-8"));
return file;
} catch (IOException e) {
throw new RuntimeException("Failed to write package listing file to disk", e);
}
})
.handleAsync(v -> {
if (listingType.isGameKeyCompatible()) {
SwingHelper.showMessageDialog(dialog, "Successfully generated package listing.", "Success", null, JOptionPane.INFORMATION_MESSAGE);
} else {
SwingHelper.showMessageDialog(dialog, "Successfully generated package listing.\n\n" +
"Note that any modpacks with game keys set were not added.",
"Success", null, JOptionPane.INFORMATION_MESSAGE);
}
dialog.dispose();
SwingHelper.browseDir(destDir, dialog);
}, ex -> {}, SwingExecutor.INSTANCE);
ProgressDialog.showProgress(dialog, deferred, progress, "Writing package listing...", "Writing package listing...");
SwingHelper.addErrorDialogCallback(dialog, deferred);
return true;
}
}