mirror of https://github.com/FreeCol/freecol.git
599 lines
22 KiB
Java
599 lines
22 KiB
Java
/**
|
|
* Copyright (C) 2002-2007 The FreeCol Team
|
|
*
|
|
* This file is part of FreeCol.
|
|
*
|
|
* FreeCol 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* FreeCol 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 FreeCol. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.sf.freecol.client.gui.panel;
|
|
|
|
import java.awt.AWTEvent;
|
|
import java.awt.ActiveEvent;
|
|
import java.awt.BorderLayout;
|
|
import java.awt.Component;
|
|
import java.awt.Dimension;
|
|
import java.awt.EventQueue;
|
|
import java.awt.GridLayout;
|
|
import java.awt.MenuComponent;
|
|
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.logging.Logger;
|
|
|
|
import javax.swing.ImageIcon;
|
|
import javax.swing.JButton;
|
|
import javax.swing.JFileChooser;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JScrollPane;
|
|
import javax.swing.JTextArea;
|
|
import javax.swing.JTextField;
|
|
import javax.swing.SwingUtilities;
|
|
import javax.swing.border.CompoundBorder;
|
|
import javax.swing.border.EmptyBorder;
|
|
import javax.swing.filechooser.FileFilter;
|
|
|
|
import net.miginfocom.swing.MigLayout;
|
|
import net.sf.freecol.FreeCol;
|
|
import net.sf.freecol.client.gui.Canvas;
|
|
import net.sf.freecol.client.gui.i18n.Messages;
|
|
import net.sf.freecol.server.generator.MapGeneratorOptions;
|
|
|
|
|
|
/**
|
|
* Superclass for all dialogs in FreeCol. This class also contains
|
|
* methods to create simple dialogs.
|
|
*/
|
|
public class FreeColDialog<T> extends FreeColPanel {
|
|
|
|
private static final Logger logger = Logger.getLogger(FreeColDialog.class.getName());
|
|
|
|
protected static final String CANCEL = "CANCEL";
|
|
|
|
// Stores the response from the user:
|
|
private T response = null;
|
|
|
|
// Whether or not the user have made the choice.
|
|
private boolean responseGiven = false;
|
|
|
|
protected JButton cancelButton = new JButton(Messages.message("cancel"));
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public FreeColDialog(Canvas parent) {
|
|
super(parent);
|
|
|
|
cancelButton.setActionCommand(CANCEL);
|
|
cancelButton.addActionListener(this);
|
|
enterPressesWhenFocused(cancelButton);
|
|
setCancelComponent(cancelButton);
|
|
}
|
|
|
|
/**
|
|
* Sets the <code>response</code> and wakes up any thread waiting for this information.
|
|
*
|
|
* @param response The object that should be returned by {@link #getResponse}.
|
|
*/
|
|
public synchronized void setResponse(T response) {
|
|
this.response = response;
|
|
responseGiven = true;
|
|
logger.info("Response has been set to " + response);
|
|
notifyAll();
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the <code>response</code> when set by <code>setResponse(Object response)</code>.
|
|
* Waits the thread until then.
|
|
*
|
|
* @return The object as set by {@link #setResponse}.
|
|
*/
|
|
public synchronized T getResponse() {
|
|
// Wait the thread until 'response' is available. Notice that we have to process
|
|
// the events manually if the current thread is the Event Dispatch Thread (EDT).
|
|
|
|
try {
|
|
if (SwingUtilities.isEventDispatchThread()) {
|
|
EventQueue theQueue = getToolkit().getSystemEventQueue();
|
|
|
|
while (!responseGiven) {
|
|
// This is essentially the body of EventDispatchThread
|
|
AWTEvent event = theQueue.getNextEvent();
|
|
Object src = event.getSource();
|
|
|
|
// We cannot call theQueue.dispatchEvent, so I pasted its body here:
|
|
if (event instanceof ActiveEvent) {
|
|
((ActiveEvent) event).dispatch();
|
|
} else if (src instanceof Component) {
|
|
((Component) src).dispatchEvent(event);
|
|
} else if (src instanceof MenuComponent) {
|
|
((MenuComponent) src).dispatchEvent(event);
|
|
} else {
|
|
logger.warning("unable to dispatch event: " + event);
|
|
}
|
|
}
|
|
} else {
|
|
while (!responseGiven) {
|
|
wait();
|
|
}
|
|
}
|
|
} catch(InterruptedException e){}
|
|
|
|
T tempResponse = response;
|
|
response = null;
|
|
responseGiven = false;
|
|
|
|
return tempResponse;
|
|
}
|
|
|
|
/**
|
|
* Sets that no response has been given.
|
|
*/
|
|
public void resetResponse() {
|
|
response = null;
|
|
responseGiven = false;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>FreeColDialog</code> with a text and a cancel-button,
|
|
* in addition to buttons for each of the objects in the given array.
|
|
*
|
|
* @param text The text that explains the choice for the user.
|
|
* @param cancelText The text displayed on the "cancel"-button.
|
|
* @param choices The ChoiceItems.
|
|
* @return The <code>FreeColDialog</code>.
|
|
* @see ChoiceItem
|
|
*/
|
|
public static <T> FreeColDialog<ChoiceItem<T>> createChoiceDialog(String text, String cancelText,
|
|
List<ChoiceItem<T>> choices) {
|
|
|
|
if (choices.isEmpty()) {
|
|
throw new IllegalArgumentException("Can not create choice dialog with 0 choices!");
|
|
}
|
|
|
|
|
|
final List<JButton> choiceBtnLst = new ArrayList<JButton>();
|
|
|
|
final FreeColDialog<ChoiceItem<T>> choiceDialog =
|
|
new FreeColDialog<ChoiceItem<T>>(FreeCol.getFreeColClient().getCanvas()) {
|
|
public void requestFocus() {
|
|
for(JButton b : choiceBtnLst){
|
|
if(b.isEnabled()){
|
|
b.requestFocus();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
choiceDialog.setLayout(new MigLayout("fillx, wrap 1", "[align center]", ""));
|
|
JTextArea textArea = getDefaultTextArea(text);
|
|
|
|
choiceDialog.add(textArea);
|
|
|
|
int columns = 1;
|
|
if ((choices.size() % 4) == 0 && choices.size() > 12) columns = 4;
|
|
else if ((choices.size() % 3) == 0 && choices.size() > 6) columns = 3;
|
|
else if ((choices.size() % 2) == 0 && choices.size() > 4) columns = 2;
|
|
|
|
else if (choices.size() > 21) columns = 4;
|
|
else if (choices.size() > 10) columns = 2;
|
|
|
|
JPanel choicesPanel = new JPanel(new GridLayout(0, columns, 10, 10));
|
|
choicesPanel.setBorder(new CompoundBorder(choicesPanel.getBorder(),
|
|
new EmptyBorder(10, 20, 10, 20)));
|
|
|
|
/*
|
|
final ChoiceItem<T> firstObject = choices.get(0);
|
|
if(firstObject.isEnabled()){
|
|
firstButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
choiceDialog.setResponse(firstObject);
|
|
}
|
|
});
|
|
}
|
|
firstButton.setEnabled(firstObject.isEnabled());
|
|
choicesPanel.add(firstButton);
|
|
choices.remove(0);
|
|
*/
|
|
|
|
for (final ChoiceItem<T> object : choices) {
|
|
final JButton objectButton = new JButton(object.toString());
|
|
if(object.isEnabled()){
|
|
objectButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
choiceDialog.setResponse(object);
|
|
}
|
|
});
|
|
enterPressesWhenFocused(objectButton);
|
|
}
|
|
objectButton.setEnabled(object.isEnabled());
|
|
choiceBtnLst.add(objectButton);
|
|
choicesPanel.add(objectButton);
|
|
}
|
|
if (choices.size() > 20) {
|
|
JScrollPane scrollPane = new JScrollPane(choicesPanel,
|
|
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
|
choiceDialog.add(scrollPane, "newline 20");
|
|
} else {
|
|
choicesPanel.setOpaque(false);
|
|
choiceDialog.add(choicesPanel, "newline 20");
|
|
}
|
|
|
|
if (cancelText != null) {
|
|
choiceDialog.cancelButton.setText(cancelText);
|
|
choiceDialog.add(choiceDialog.cancelButton, "newline 20, tag cancel");
|
|
}
|
|
|
|
choiceDialog.setSize(choiceDialog.getPreferredSize());
|
|
|
|
return choiceDialog;
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new <code>FreeColDialog</code> with a text and a ok/cancel option.
|
|
* The "ok"-option calls {@link #setResponse setResponse(new Boolean(true))}
|
|
* and the "cancel"-option calls {@link #setResponse setResponse(new Boolean(false))}.
|
|
*
|
|
* @param text The text that explains the choice for the user.
|
|
* @param okText The text displayed on the "ok"-button.
|
|
* @param cancelText The text displayed on the "cancel"-button.
|
|
* @return The <code>FreeColDialog</code>.
|
|
*/
|
|
public static FreeColDialog<Boolean> createConfirmDialog(String text, String okText, String cancelText) {
|
|
return createConfirmDialog(new String[] {text}, null, okText, cancelText);
|
|
}
|
|
|
|
public static FreeColDialog<Boolean> createConfirmDialog(String[] texts, ImageIcon[] icons,
|
|
String okText, String cancelText) {
|
|
// create the dialog
|
|
final FreeColDialog<Boolean> confirmDialog =
|
|
new FreeColDialog<Boolean>(FreeCol.getFreeColClient().getCanvas());
|
|
|
|
confirmDialog.setLayout(new MigLayout("wrap 2", "[][fill]", ""));
|
|
|
|
confirmDialog.okButton.setText(okText);
|
|
confirmDialog.okButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed( ActionEvent event ) {
|
|
confirmDialog.setResponse(Boolean.TRUE);
|
|
}
|
|
});
|
|
|
|
confirmDialog.cancelButton.setText(cancelText);
|
|
confirmDialog.cancelButton.removeActionListener(confirmDialog);
|
|
confirmDialog.cancelButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed( ActionEvent event ) {
|
|
confirmDialog.setResponse(Boolean.FALSE);
|
|
}
|
|
});
|
|
|
|
for (int i = 0; i < texts.length; i++) {
|
|
if (icons != null && icons[i] != null) {
|
|
confirmDialog.add(new JLabel(icons[i]));
|
|
confirmDialog.add(getDefaultTextArea(texts[i]));
|
|
} else {
|
|
confirmDialog.add(getDefaultTextArea(texts[i]), "skip");
|
|
}
|
|
}
|
|
|
|
confirmDialog.add(confirmDialog.okButton, "newline 20, span, split 2, tag ok");
|
|
confirmDialog.add(confirmDialog.cancelButton, "tag cancel");
|
|
|
|
return confirmDialog;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>FreeColDialog</code> with a text field and a ok/cancel option.
|
|
* The "ok"-option calls {@link #setResponse setResponse(textField.getText())}
|
|
* and the "cancel"-option calls {@link #setResponse setResponse(null)}.
|
|
*
|
|
* @param text The text that explains the action to the user.
|
|
* @param defaultValue The default value appearing in the text field.
|
|
* @param okText The text displayed on the "ok"-button.
|
|
* @param cancelText The text displayed on the "cancel"-button.
|
|
* @return The <code>FreeColDialog</code>.
|
|
*/
|
|
public static FreeColDialog<String> createInputDialog(String text, String defaultValue,
|
|
String okText, String cancelText) {
|
|
|
|
final JTextField input = new JTextField(defaultValue);
|
|
|
|
final FreeColDialog<String> inputDialog =
|
|
new FreeColDialog<String>(FreeCol.getFreeColClient().getCanvas()) {
|
|
public void requestFocus() {
|
|
input.requestFocus();
|
|
}
|
|
};
|
|
|
|
inputDialog.setLayout(new MigLayout("wrap 1, gapy 20", "", ""));
|
|
|
|
inputDialog.okButton.setText(okText);
|
|
inputDialog.okButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
inputDialog.setResponse(input.getText());
|
|
}
|
|
});
|
|
|
|
input.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
inputDialog.setResponse(input.getText());
|
|
}
|
|
});
|
|
|
|
input.selectAll();
|
|
|
|
inputDialog.add(getDefaultTextArea(text));
|
|
inputDialog.add(input, "width 180:, growx");
|
|
|
|
if (cancelText == null) {
|
|
inputDialog.add(inputDialog.okButton, "tag ok");
|
|
inputDialog.setCancelComponent(inputDialog.okButton);
|
|
} else {
|
|
inputDialog.cancelButton.setText(cancelText);
|
|
inputDialog.add(inputDialog.okButton, "split 2, tag ok");
|
|
inputDialog.add(inputDialog.cancelButton, "tag cancel");
|
|
}
|
|
|
|
inputDialog.setSize(inputDialog.getPreferredSize());
|
|
|
|
return inputDialog;
|
|
}
|
|
|
|
|
|
public static FreeColDialog<Dimension> createMapSizeDialog(final Canvas canvas) {
|
|
|
|
final int defaultSize = canvas.getSpecification().getRangeOption("model.option.mapSize")
|
|
.getValue();
|
|
final int defaultHeight = MapGeneratorOptions.getHeight(defaultSize);
|
|
final int defaultWidth = MapGeneratorOptions.getWidth(defaultSize);
|
|
final int COLUMNS = 5;
|
|
|
|
final String widthText = Messages.message("width");
|
|
final String heightText = Messages.message("height");
|
|
|
|
final JTextField inputWidth = new JTextField(Integer.toString(defaultWidth), COLUMNS);
|
|
final JTextField inputHeight = new JTextField(Integer.toString(defaultHeight), COLUMNS);
|
|
|
|
final FreeColDialog<Dimension> mapSizeDialog = new FreeColDialog<Dimension>(canvas);
|
|
|
|
mapSizeDialog.setLayout(new MigLayout("wrap 2"));
|
|
|
|
mapSizeDialog.okButton.setText(Messages.message("ok"));
|
|
mapSizeDialog.okButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
try {
|
|
int width = Integer.parseInt(inputWidth.getText());
|
|
int height = Integer.parseInt(inputHeight.getText());
|
|
if (width <= 0 || height <= 0) {
|
|
throw new NumberFormatException();
|
|
}
|
|
mapSizeDialog.setResponse(new Dimension(width, height));
|
|
} catch (NumberFormatException nfe) {
|
|
canvas.errorMessage("integerAboveZero");
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
JLabel widthLabel = new JLabel(widthText);
|
|
widthLabel.setLabelFor(inputWidth);
|
|
JLabel heightLabel = new JLabel(heightText);
|
|
heightLabel.setLabelFor(inputHeight);
|
|
|
|
mapSizeDialog.add(new JLabel(Messages.message("editor.mapSize")), "span, align center");
|
|
mapSizeDialog.add(widthLabel, "newline 20");
|
|
mapSizeDialog.add(inputWidth);
|
|
mapSizeDialog.add(heightLabel);
|
|
mapSizeDialog.add(inputHeight);
|
|
|
|
mapSizeDialog.add(mapSizeDialog.okButton, "newline 20, span, split2, tag ok");
|
|
mapSizeDialog.add(mapSizeDialog.cancelButton, "tag cancel");
|
|
|
|
mapSizeDialog.setSize(mapSizeDialog.getPreferredSize());
|
|
|
|
return mapSizeDialog;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>FreeColDialog</code> in which the user
|
|
* may choose a savegame to load.
|
|
*
|
|
* @param directory The directory to display when choosing the file.
|
|
* @param fileFilters The available file filters in the
|
|
* dialog.
|
|
* @return The <code>FreeColDialog</code>.
|
|
*/
|
|
public static FreeColDialog<File> createLoadDialog(File directory, FileFilter[] fileFilters) {
|
|
final FreeColDialog<File> loadDialog =
|
|
new FreeColDialog<File>(FreeCol.getFreeColClient().getCanvas());
|
|
final JFileChooser fileChooser = new JFileChooser(directory);
|
|
|
|
loadDialog.okButton.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
File selectedFile = fileChooser.getSelectedFile();
|
|
if (selectedFile != null) {
|
|
loadDialog.setResponse(selectedFile);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (fileFilters.length > 0) {
|
|
for (FileFilter fileFilter : fileFilters) {
|
|
fileChooser.addChoosableFileFilter(fileFilter);
|
|
}
|
|
fileChooser.setFileFilter(fileFilters[0]);
|
|
fileChooser.setAcceptAllFileFilterUsed(false);
|
|
}
|
|
fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
|
|
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
|
fileChooser.setFileHidingEnabled(false);
|
|
fileChooser.setControlButtonsAreShown(false);
|
|
loadDialog.setLayout(new MigLayout("fill", "", ""));
|
|
loadDialog.add(fileChooser, "grow");
|
|
loadDialog.add(loadDialog.okButton, "newline 20, split 2, tag ok");
|
|
loadDialog.add(loadDialog.cancelButton, "tag cancel");
|
|
loadDialog.setSize(480, 320);
|
|
|
|
return loadDialog;
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new <code>FreeColDialog</code> in which the user
|
|
* may choose the destination of the savegame.
|
|
*
|
|
* @param directory The directory to display when choosing the name.
|
|
* @param standardName This extension will be added to the
|
|
* specified filename (if not added by the user).
|
|
* @param fileFilters The available file filters in the
|
|
* dialog.
|
|
* @param defaultName Default filename for the savegame.
|
|
* @return The <code>FreeColDialog</code>.
|
|
*/
|
|
public static FreeColDialog<File> createSaveDialog(File directory, final String standardName,
|
|
FileFilter[] fileFilters, String defaultName) {
|
|
final FreeColDialog<File> saveDialog =
|
|
new FreeColDialog<File>(FreeCol.getFreeColClient().getCanvas());
|
|
final JFileChooser fileChooser = new JFileChooser(directory);
|
|
final File defaultFile = new File(defaultName);
|
|
|
|
fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
|
|
if (fileFilters.length > 0) {
|
|
for (int i=0; i<fileFilters.length; i++) {
|
|
fileChooser.addChoosableFileFilter(fileFilters[i]);
|
|
}
|
|
fileChooser.setFileFilter(fileFilters[0]);
|
|
fileChooser.setAcceptAllFileFilterUsed(false);
|
|
}
|
|
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
|
fileChooser.addActionListener(new ActionListener() {
|
|
public void actionPerformed(ActionEvent event) {
|
|
String actionCommand = event.getActionCommand();
|
|
if (actionCommand.equals(JFileChooser.APPROVE_SELECTION)) {
|
|
File file = fileChooser.getSelectedFile();
|
|
if (standardName != null && !file.getName().endsWith(standardName)) {
|
|
file = new File(file.getAbsolutePath() + standardName);
|
|
}
|
|
saveDialog.setResponse(file);
|
|
}
|
|
else if (actionCommand.equals(JFileChooser.CANCEL_SELECTION)) {
|
|
saveDialog.setResponse(null);
|
|
}
|
|
}
|
|
});
|
|
fileChooser.setFileHidingEnabled(false);
|
|
fileChooser.setSelectedFile(defaultFile);
|
|
saveDialog.setLayout(new BorderLayout());
|
|
saveDialog.add(fileChooser);
|
|
saveDialog.setSize(480, 320);
|
|
|
|
return saveDialog;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a filter accepting "*.fsg".
|
|
* @return The filter.
|
|
*/
|
|
public static FileFilter getFSGFileFilter() {
|
|
|
|
return new FreeColFileFilter( ".fsg", "filter.savedGames" );
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a filter accepting "*.fgo".
|
|
* @return The filter.
|
|
*/
|
|
public static FileFilter getFGOFileFilter() {
|
|
|
|
return new FreeColFileFilter( ".fgo", "filter.gameOptions" );
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a filter accepting all files containing a
|
|
* {@link net.sf.freecol.common.model.GameOptions}.
|
|
* That is; both "*.fgo" and "*.fsg".
|
|
*
|
|
* @return The filter.
|
|
*/
|
|
public static FileFilter getGameOptionsFileFilter() {
|
|
|
|
return new FreeColFileFilter( ".fgo", ".fsg", "filter.gameOptionsAndSavedGames" );
|
|
}
|
|
|
|
|
|
static final class FreeColFileFilter extends FileFilter {
|
|
|
|
private final String extension1;
|
|
private final String extension2;
|
|
private final String description;
|
|
|
|
FreeColFileFilter( String extension1,
|
|
String extension2,
|
|
String descriptionMessage ) {
|
|
|
|
this.extension1 = extension1;
|
|
this.extension2 = extension2;
|
|
description = Messages.message(descriptionMessage);
|
|
}
|
|
|
|
FreeColFileFilter( String extension, String descriptionMessage ) {
|
|
|
|
this.extension1 = extension;
|
|
this.extension2 = "....";
|
|
description = Messages.message(descriptionMessage);
|
|
}
|
|
|
|
public boolean accept(File f) {
|
|
|
|
return f.isDirectory() || f.getName().endsWith(extension1)
|
|
|| f.getName().endsWith(extension2);
|
|
}
|
|
|
|
public String getDescription() {
|
|
|
|
return description;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used for Polymorphism in Recruit, Purchase, Train Dialogs
|
|
*/
|
|
public void initialize() {}
|
|
|
|
/**
|
|
* This function analyses an event and calls the right methods to take care
|
|
* of the user's requests.
|
|
*
|
|
* @param event The incoming ActionEvent.
|
|
*/
|
|
public void actionPerformed(ActionEvent event) {
|
|
String command = event.getActionCommand();
|
|
if (CANCEL.equals(command)) {
|
|
setResponse(null);
|
|
} else {
|
|
super.actionPerformed(event);
|
|
}
|
|
}
|
|
|
|
}
|