/*
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.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.border.Border;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JRadioButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import de.haukerehfeld.quakeinjector.gui.ErrorEvent;
import de.haukerehfeld.quakeinjector.gui.ErrorListener;
import de.haukerehfeld.quakeinjector.gui.JPathPanel;
import de.haukerehfeld.quakeinjector.gui.LookAndFeelDefaults;
import de.haukerehfeld.quakeinjector.gui.OkayCancelApplyPanel;
public class EngineConfigDialog extends JDialog {
private final static String windowTitle = "Engine Configuration";
private final ChangeListenerList listeners = new ChangeListenerList();
private final JPanel configPanel;
private final JPathPanel enginePath;
private final JPathPanel engineExecutable;
private final JTextField engineCommandline;
private final JPathPanel downloadPath;
private final JCheckBox rogue;
private final JCheckBox hipnotic;
private final WorkingDirOpts workingDirOpts;
public EngineConfigDialog(final JFrame frame,
Configuration.EnginePath enginePathDefault,
Configuration.EngineExecutable engineExeDefault,
Configuration.WorkingDirAtExecutable workingDirAtExecutable,
Configuration.DownloadPath downloadPathDefault,
Configuration.EngineCommandLine cmdlineDefault,
boolean rogueInstalled,
boolean hipnoticInstalled) {
super(frame, windowTitle, true);
configPanel = new JPanel();
configPanel.setBorder(LookAndFeelDefaults.PADDINGBORDER);
configPanel.setLayout(new GridBagLayout());
JLabel description = new JLabel("Configure engine specific settings");
description.setLabelFor(this);
description.setBorder(LookAndFeelDefaults.DIALOGDESCRIPTIONBORDER);
configPanel.add(description, new GridBagConstraints());
final JButton okay = new JButton("Okay");
final JButton cancel = new JButton("Cancel");
final JButton apply = new JButton("Apply");
class LabelConstraints extends GridBagConstraints {{
anchor = LINE_START;
fill = NONE;
gridx = 0;
gridwidth = 1;
gridheight = 1;
weightx = 0;
weighty = 0;
}};
class InputConstraints extends GridBagConstraints {{
anchor = LINE_END;
fill = HORIZONTAL;
gridx = 1;
gridwidth = 2;
gridheight = 1;
weightx = 1;
weighty = 0;
}};
int row = 1;
Border leftBorder = BorderFactory
.createEmptyBorder(0, 0, 0, LookAndFeelDefaults.FRAMEPADDING);
{
JLabel cmdlineLabel = new JLabel("Quake commandline options");
cmdlineLabel.setBorder(leftBorder);
this.engineCommandline = new JTextField(cmdlineDefault.get(), 40);
final int row_ = row;
configPanel.add(cmdlineLabel, new LabelConstraints() {{ gridy = row_; }});
configPanel.add(engineCommandline, new InputConstraints() {{ gridy = row_; }});
}
++row;
{
//"Path to quake directory",
JLabel enginePathLabel = new JLabel("Quake Directory");
enginePathLabel.setBorder(leftBorder);
enginePath = new JPathPanel(new JPathPanel.WritableDirectoryVerifier(),
enginePathDefault.get(),
javax.swing.JFileChooser.DIRECTORIES_ONLY);
final int row_ = row;
configPanel.add(enginePathLabel, new LabelConstraints() {{ gridy = row_; }});
configPanel.add(enginePath, new InputConstraints() {{ gridy = row_; }});
}
++row;
JLabel engineExeLabel = new JLabel("Quake Executable");
engineExeLabel.setBorder(leftBorder);
engineExecutable = new JPathPanel(
new JPathPanel.Verifier() {
public boolean verify(File exe) {
return EngineStarter.isValidApplication(exe);
}
public String errorMessage(File f) {
return EngineStarter.errorMessageForApplication(f);
}
},
engineExeDefault.get(),
enginePathDefault.get(),
javax.swing.JFileChooser.FILES_ONLY);
{
final int row_ = row;
configPanel.add(engineExeLabel, new LabelConstraints() {{ gridy = row_; }});
configPanel.add(engineExecutable, new InputConstraints() {{ gridy = row_; }});
}
{
}
{
}
enginePath.verify();
engineExecutable.verify();
++row;
{
//"Path to quake directory",
JLabel downloadLabel = new JLabel("Download Directory");
downloadLabel.setBorder(leftBorder);
downloadPath = new JPathPanel(new JPathPanel.WritableDirectoryVerifier(),
downloadPathDefault.get(),
javax.swing.JFileChooser.DIRECTORIES_ONLY);
downloadPath.verify();
final int row_ = row;
configPanel.add(downloadLabel, new LabelConstraints() {{ gridy = row_; }});
configPanel.add(downloadPath, new InputConstraints() {{ gridy = row_; }});
}
++row;
{
JLabel expansionsInstalled = new JLabel("Expansion packs installed");
expansionsInstalled.setBorder(leftBorder);
rogue = new JCheckBox("rogue");
rogue.setMnemonic(KeyEvent.VK_R);
rogue.setSelected(rogueInstalled);
hipnotic = new JCheckBox("hipnotic");
hipnotic.setMnemonic(KeyEvent.VK_H);
hipnotic.setSelected(hipnoticInstalled);
final int row_ = row;
configPanel.add(expansionsInstalled, new LabelConstraints() {{ gridy = row_; }});
configPanel.add(rogue, new InputConstraints() {{ gridy = row_; gridwidth = 1; }});
configPanel.add(hipnotic, new InputConstraints() {{
gridy = row_;
gridx = 2;
gridwidth = 1;
}});
}
++row;
workingDirOpts = new WorkingDirOpts(row, workingDirAtExecutable.get());
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.setBorder(LookAndFeelDefaults.PADDINGBORDER);
tabbedPane.addTab("Engine Specifics", null, configPanel, "Configure Engine Specifics");
tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
add(tabbedPane, BorderLayout.CENTER);
class EnableOkay implements ChangeListener, DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
check();
}
@Override
public void insertUpdate(DocumentEvent e) {
check();
}
@Override
public void removeUpdate(DocumentEvent e) {
check();
}
@Override
public void stateChanged(ChangeEvent e) {
check();
}
private void check() {
if (enginePath.verifies() && engineExecutable.verifies()) {
okay.setEnabled(true);
apply.setEnabled(true);
}
}
};
final EnableOkay enableOkay = new EnableOkay() ;
engineCommandline.getDocument().addDocumentListener(enableOkay);
enginePath.addErrorListener(new ErrorListener() {
public void errorOccured(ErrorEvent e) {
okay.setEnabled(false);
apply.setEnabled(false);
}
});
//change basepath of the exe when quakedir changes
enginePath.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
engineExecutable.setBasePath(enginePath.getPath());
}
});
//(un)set working dir opt visibility as appropriate
enginePath.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
workingDirOpts.checkDisplay();
}
});
enginePath.addChangeListener(enableOkay);
engineExecutable.addErrorListener(new ErrorListener() {
public void errorOccured(ErrorEvent e) {
okay.setEnabled(false);
apply.setEnabled(false);
}
});
//(un)set working dir opt visibility as appropriate
engineExecutable.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
workingDirOpts.checkDisplay();
}
});
engineExecutable.addChangeListener(enableOkay);
downloadPath.addChangeListener(enableOkay);
rogue.addChangeListener(enableOkay);
hipnotic.addChangeListener(enableOkay);
ActionListener save = new ActionListener() {
public void actionPerformed(ActionEvent e) {
listeners.notifyChangeListeners(this);
apply.setEnabled(false);
}
};
okay.addActionListener(save);
apply.addActionListener(save);
ActionListener close = new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
dispose();
}
};
okay.addActionListener(close);
cancel.addActionListener(close);
{
JPanel okayCancelPanel = new OkayCancelApplyPanel(okay, cancel, apply, true);
add(okayCancelPanel, BorderLayout.PAGE_END);
}
}
class WorkingDirOpts {
private final JSeparator workingDirBoxSep;
private final GridBagConstraints sepConstraints;
private final JLabel workingDirTitle;
private final GridBagConstraints titleConstraints;
private final Box workingDirChoices;
private final GridBagConstraints choicesConstraints;
private final JRadioButton workAtExe;
private boolean visible;
public WorkingDirOpts(int configPanelRow, boolean workAtExeDefault) {
// Separator
workingDirBoxSep = new JSeparator(JSeparator.HORIZONTAL);
sepConstraints = new GridBagConstraints();
sepConstraints.gridx = 0;
sepConstraints.gridy = configPanelRow;
sepConstraints.gridwidth = GridBagConstraints.REMAINDER;
sepConstraints.fill = GridBagConstraints.HORIZONTAL;
sepConstraints.insets = new Insets(10, 0, 10, 0);
sepConstraints.weightx = 1;
// Explanatory text
String workingDirBlurb =
"<html><body><nobr>" +
"<b>Notice:</b> your Quake Executable is not located in " +
"your Quake Directory.<br/>Choose where the runtime " +
"\"working directory\" for Quake should be located:" +
"</nobr></body></html>";
workingDirTitle = new JLabel(workingDirBlurb);
titleConstraints = new GridBagConstraints();
titleConstraints.gridx = 0;
titleConstraints.gridy = configPanelRow + 1;
titleConstraints.gridwidth = GridBagConstraints.REMAINDER;
// Container for the choices
workingDirChoices = new Box(BoxLayout.Y_AXIS);
// Radio buttons for the choices
JRadioButton workInBase = new JRadioButton("in Quake Directory");
String baseBlurb =
"Using the Quake Directory as the working directory may " +
"fail if necessary libraries or other resources are " +
"located with the engine.";
workInBase.setToolTipText(baseBlurb);
workInBase.setSelected(!workAtExeDefault);
workAtExe = new JRadioButton("at Quake Executable");
String exeBlurb =
"Using the Quake Engine's location as the working directory " +
"may require adding a -basedir argument to the command line.";
workAtExe.setToolTipText(exeBlurb);
workAtExe.setSelected(workAtExeDefault);
ButtonGroup workingDirGroup = new ButtonGroup();
workingDirGroup.add(workInBase);
workingDirGroup.add(workAtExe);
workingDirChoices.add(workInBase);
workingDirChoices.add(workAtExe);
choicesConstraints = new GridBagConstraints();
choicesConstraints.gridx = 0;
choicesConstraints.gridy = configPanelRow + 2;
choicesConstraints.gridwidth = GridBagConstraints.REMAINDER;
choicesConstraints.insets = new Insets(5, 0, 5, 0);
// Start visible if appropriate
visible = false;
checkDisplay();
}
public void checkDisplay() {
File exeDir = null;
if (null != engineExecutable) {
exeDir = engineExecutable.getPath().getParentFile();
}
boolean validPaths =
null != exeDir &&
null != enginePath &&
!(exeDir.getPath().equals("")) &&
!(enginePath.getPath().getPath().equals("")) &&
!(engineExecutable.getPath().equals(enginePath.getPath()));
boolean showOpt =
validPaths && !(enginePath.getPath().equals(exeDir));
if (showOpt) {
if (!visible) {
configPanel.add(workingDirBoxSep, sepConstraints);
configPanel.add(workingDirTitle, titleConstraints);
configPanel.add(workingDirChoices, choicesConstraints);
verticalRepack();
visible = true;
}
}
else {
if (visible) {
configPanel.remove(workingDirBoxSep);
configPanel.remove(workingDirTitle);
configPanel.remove(workingDirChoices);
verticalRepack();
visible = false;
}
}
}
private void verticalRepack() {
EngineConfigDialog dialog = EngineConfigDialog.this;
Rectangle bounds = dialog.getBounds();
dialog.setMinimumSize(new Dimension(bounds.width, 0));
dialog.pack();
dialog.setMinimumSize(null);
}
public boolean getWorkingDirAtExecutable() {
return workAtExe.isSelected();
}
}
public File getEnginePath() {
return enginePath.getPath();
}
public File getEngineExecutable() {
return engineExecutable.getPath();
}
public boolean getWorkingDirAtExecutable() {
return workingDirOpts.getWorkingDirAtExecutable();
}
public String getCommandline() {
return engineCommandline.getText();
}
/**
* get hipnoticInstalled
*/
public boolean getHipnoticInstalled() { return hipnotic.isSelected(); }
/**
* get rogueInstalled
*/
public boolean getRogueInstalled() { return rogue.isSelected(); }
public File getDownloadPath() {
return downloadPath.getPath();
}
public void addChangeListener(ChangeListener l) {
listeners.addChangeListener(l);
}
}