/*
Copyright 2009 Hauke Rehfeld
This file is part of QuakeInjector.
QuakeInjector 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.
QuakeInjector 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 QuakeInjector. If not, see <http://www.gnu.org/licenses/>.
*/
package de.haukerehfeld.quakeinjector;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* the panel that shows Info about the selected map
*/
class PackageInteractionPanel extends JPanel implements ChangeListener,
PackageListSelectionHandler.SelectionListener {
private static final String uninstallText = "Uninstall";
private static final String installText = "Install";
private static final String playText = "Play";
private QuakeInjector main;
private EngineStarter starter;
private Configuration.RepositoryBasePath paths;
private RequirementList requirements;
private InstallQueuePanel installQueue;
private JButton uninstallButton;
private JButton installButton;
private JButton playButton;
private JComboBox startmaps;
private boolean ready = false;
/**
* Currently selected map
*/
private Package selectedMap = null;
private Installer installer;
private SaveInstalled installedMaps;
public PackageInteractionPanel(QuakeInjector main, InstallQueuePanel installQueue) {
super(new GridBagLayout());
this.main = main;
this.installQueue = installQueue;
uninstallButton = new JButton(uninstallText);
uninstallButton.setEnabled(false);
uninstallButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
uninstall();
}
});
add(uninstallButton, new GridBagConstraints() {{
fill = BOTH;
}});
installButton = new JButton(installText);
installButton.setEnabled(false);
// int preferredHeight = (int) installButton.getPreferredSize().getHeight();
// {
// Dimension maxSize = new Dimension(150, preferredHeight);
// installButton.setMinimumSize(maxSize);
// installButton.setPreferredSize(maxSize);
// }
installButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
install();
}
});
add(installButton, new GridBagConstraints() {{
gridx = 1;
gridy = 0;
fill = BOTH;
}});
playButton = new JButton(playText);
playButton.setEnabled(false);
playButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
start();
}
});
add(playButton, new GridBagConstraints() {{
gridx = 0;
gridy = 1;
fill = BOTH;
}});
startmaps = new JComboBox();
// {
// Dimension maxSize = new Dimension(100, preferredHeight);
// startmaps.setPreferredSize(maxSize);
// startmaps.setMinimumSize(maxSize);
// }
add(startmaps, new GridBagConstraints() {{
gridx = 1;
gridy = 1;
fill = BOTH;
weightx = 1;
}});
disableUI();
refreshUi();
}
public void init(Installer installer,
Configuration.RepositoryBasePath paths,
RequirementList requirements,
EngineStarter starter,
SaveInstalled installedMaps) {
this.paths = paths;
this.requirements = requirements;
this.starter = starter;
this.installedMaps = installedMaps;
this.installer = installer;
ready = true;
refreshUi();
}
public void installRequirements(Package map) {
for (Package requirement: map.getAvailableRequirements()) {
String id = requirement.getId();
if (requirement.isInstalled()) {
System.out.print("Required package " + id + " already installed.");
}
else {
System.out.print("Required package " + id + " not installed. Installing...");
install(requirement, true);
}
}
}
private boolean hasCurrentPackage() {
return (selectedMap != null);
}
public void install() {
if (!hasCurrentPackage()) { return; }
install(selectedMap, false);
}
private boolean checkInstallRequirements(Package selectedMap) {
List<Requirement> unmet = selectedMap.getUnavailableRequirements();
if (!unmet.isEmpty()) {
String msg = "The following prerequisites to play "
+ selectedMap.getId()
+ " can't be installed automatically:\n"
+ Utils.join(unmet, ",\n")
+ ".\n";
Object[] options = {"Install anyways",
"Cancel Install"};
int install =
JOptionPane.showOptionDialog(this,
msg,
"Prerequisites not available for automatic install",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options,
options[1]);
if (install != 0) {
return false;
}
}
return true;
}
private boolean checkPlayRequirements(Package selectedMap) {
//in theory this should never happen ;)
if (!selectedMap.isInstalled()) {
String msg = selectedMap.getId()
+ " doesn't seem to be installed.";
Object[] options = {"Install",
"Cancel Start"};
int install =
JOptionPane.showOptionDialog(this,
msg,
"Map not installed",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options,
options[1]);
if (install == 0) {
install(selectedMap, false);
}
return false;
}
List<Requirement> unmet = selectedMap.getUnmetRequirements();
if (!unmet.isEmpty()) {
String msg = "The following prerequisites to play "
+ selectedMap.getId()
+ " don't seem to be installed: \n"
+ Utils.join(unmet, ",\n ")
+ ".\nYou probably can't play this package.";
Object[] options = {"Start anyways",
"Cancel Start"};
int install =
JOptionPane.showOptionDialog(this,
msg,
"Prerequisites not installed",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options,
options[1]);
if (install != 0) {
return false;
}
}
return true;
}
private boolean checkInstallDirectory() {
while (!installer.checkInstallDirectory()) {
if (!main.enginePathNotSetDialogue()) {
return false;
}
}
return true;
}
public void install(final Package selectedMap, boolean becauseRequired) {
if (!checkInstallDirectory()
|| installer.alreadyQueued(selectedMap)
|| !checkInstallRequirements(selectedMap)) {
return;
}
installRequirements(selectedMap);
String description = "Installing ";
if (becauseRequired) {
description += "prerequisite ";
}
description += selectedMap.getId();
final InstallQueuePanel.Job progressListener
= installQueue.addJob(description,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
installer.cancel(selectedMap);
}
});
installer.install(selectedMap,
paths.getRepositoryUrl(selectedMap.getId()),
new Installer.InstallErrorHandler() {
public void handle(OnlineFileNotFoundException error) {
installQueue.finished(progressListener,
"File not found");
refreshUi();
String msg = "The file couldn't be found in the online"
+ " repository";
JOptionPane.showMessageDialog(PackageInteractionPanel.this,
msg,
"File not found (404)",
JOptionPane.WARNING_MESSAGE);
}
public List<File> overwrite(Map<String,File> files) {
PackageOverwriteDialog overwrite = new PackageOverwriteDialog(main);
for (Map.Entry<String,File> e: files.entrySet()) {
String name = e.getKey();
File f = e.getValue();
overwrite.addFile(name, f.exists());
}
overwrite.packAndShow();
List<File> overwriteFiles = new ArrayList<File>();
if (overwrite.isCanceled()) {
return overwriteFiles;
}
for (String name: overwrite.getOverwritten()) {
overwriteFiles.add(files.get(name));
}
return overwriteFiles;
}
public void success(PackageFileList installedFiles) {
Requirement r = requirements.get(installedFiles.getId());
r.setInstalled(true);
if (!(r instanceof Package)) {
System.err.println(r + " isn't a Package!");
}
else {
((Package) r).setFileList(installedFiles);
}
try {
installedMaps.write(requirements);
}
catch (java.io.IOException e) {
System.out.println("Couldn't write installed Maps file!"
+ e.getMessage());
}
progressListener.setProgress(100);
installQueue.finished(progressListener, "Success");
refreshUi();
}
public void handle(FileNotWritableException error,
PackageFileList alreadyInstalledFiles) {
cleanup(alreadyInstalledFiles,
"Couldn't write");
String msg = "Couldn't write to harddisk! "
+ error.getMessage();
JOptionPane.showMessageDialog(PackageInteractionPanel.this,
msg,
"Couldn't write to harddisk",
JOptionPane.ERROR_MESSAGE);
}
public void handle(java.io.IOException error,
PackageFileList alreadyInstalledFiles) {
cleanup(alreadyInstalledFiles, "File Error");
String msg = "Couldn't open file! "
+ error.getMessage();
JOptionPane.showMessageDialog(PackageInteractionPanel.this,
msg,
"Couldn't open file!",
JOptionPane.ERROR_MESSAGE);
}
public void handle(java.net.SocketException error,
PackageFileList alreadyInstalledFiles) {
cleanup(alreadyInstalledFiles, "Network Error");
String msg = "Download failed! " + error.getMessage();
JOptionPane.showMessageDialog(PackageInteractionPanel.this,
msg,
"Download failed!",
JOptionPane.ERROR_MESSAGE);
}
public void handle(Installer.CancelledException error,
PackageFileList alreadyInstalledFiles) {
cleanup(alreadyInstalledFiles, "Canceled");
}
private void cleanup(PackageFileList alreadyInstalledFiles,
String message) {
System.out.println("Cleaning up...");
uninstall(selectedMap, alreadyInstalledFiles);
installQueue.finished(progressListener, message);
refreshUi();
}
},
progressListener);
installButton.setEnabled(false);
}
public void uninstall() {
if (!checkInstallDirectory()) {
return;
}
if (!hasCurrentPackage()) { return; }
uninstall(selectedMap, selectedMap.getFileList());
uninstallButton.setEnabled(false);
}
private void uninstall(final Package map, PackageFileList files) {
String description = "Uninstalling " + files.getId();
final InstallQueuePanel.Job progressListener
= installQueue.addJob(description,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
//cancel button action
}
});
installer.uninstall(files,
new Installer.UninstallErrorHandler() {
@Override
public void success() {
installQueue.finished(progressListener, "success");
synchronized (map) {
map.setInstalled(false);
}
SwingWorker<Void,Void> saveInstalled
= new SwingWorker<Void,Void>() {
@Override
public Void doInBackground() {
try {
installedMaps.write(requirements);
}
catch (java.io.IOException e) {
System.out.println("Couldn't write installed Maps file!" + e.getMessage());
}
return null;
}
};
saveInstalled.execute();
refreshUi();
}
@Override
public void error(Exception e) {
refreshUi();
installQueue.finished(progressListener, "fail");
System.out.println(e.getMessage());
e.printStackTrace();
}
},
progressListener
);
}
public void start() {
if (!hasCurrentPackage()) { return; }
if (!starter.checkPaths()) {
JOptionPane.showMessageDialog(main,
"Quake engine paths aren't set correctly, can't start.",
"Quake engine paths not configured",
JOptionPane.ERROR_MESSAGE);
return;
}
if (!checkPlayRequirements(selectedMap)) {
return;
}
String startmap = (String) startmaps.getSelectedItem();
//System.out.println("startmap: " + startmap);
try {
Process p = starter.start(selectedMap.getCommandline(), startmap);
EngineOutputDialog eod = new EngineOutputDialog(main, p.getInputStream());
eod.pack();
eod.setLocationRelativeTo(main);
eod.setVisible(true);
}
catch (java.io.IOException e) {
/** @todo 2009-05-04 14:28 hrehfeld pop up dialogue */
System.out.println("Couldn't start quake engine: " + e.getMessage());
}
}
public void setSelection(Package map) {
this.selectedMap = map;
refreshUi();
}
private void refreshUi() {
if (!ready || !hasCurrentPackage()) {
installButton.setText(installText);
disableUI();
return;
}
installButton.setText(installText + " " + selectedMap.getId());
//we do this regardless of displaying the list, because we can
//then simply get the selection from the list even if there's
//only one option
java.util.List<String> maps = selectedMap.getStartmaps();
startmaps.removeAllItems();
for (String startmap: maps) {
startmaps.addItem(startmap);
}
if (selectedMap.isInstalled()) {
installButton.setEnabled(false);
uninstallButton.setEnabled(true);
playButton.setEnabled(true);
boolean enableList = false;
if (maps.size() > 1) {
enableList = true;
}
startmaps.setEnabled(enableList);
}
else {
if (installer.alreadyQueued(selectedMap)) {
installButton.setEnabled(false);
}
else {
installButton.setEnabled(true);
}
playButton.setEnabled(false);
uninstallButton.setEnabled(false);
startmaps.setEnabled(false);
}
revalidate();
repaint();
}
private void disableUI() {
uninstallButton.setEnabled(false);
playButton.setEnabled(false);
installButton.setEnabled(false);
startmaps.setEnabled(false);
}
@Override
public void stateChanged(ChangeEvent e) {
refreshUi();
}
@Override
public void selectionChanged(Package s) {
setSelection(s);
}
}