/*
* MegaMek - Copyright (C) 2004 Ben Mazur (bmazur@sev.org)
*
* 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 2 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.
*/
package megamek.client.ui.AWT;
import java.awt.BorderLayout;
import java.awt.Choice;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.ItemSelectable;
import java.awt.Label;
import java.awt.List;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.Iterator;
import java.util.Vector;
import megamek.client.ui.Messages;
import megamek.client.ui.AWT.util.ImageFileFactory;
import megamek.client.ui.AWT.util.PlayerColors;
import megamek.client.ui.AWT.widget.AdvancedLabel;
import megamek.client.ui.AWT.widget.ImageButton;
import megamek.common.Player;
import megamek.common.util.DirectoryItems;
/**
* This dialog allows players to select the camo pattern (or color) used by
* their units during the game. It automatically fills itself with all the color
* choices in <code>Settings</code> and all the camo patterns in the
* "data/iamges/camo" directory tree. Created on January 19, 2004
*
* @author James Damour
* @version 1
*/
public class CamoChoiceDialog extends Dialog implements ActionListener,
ItemListener, ItemSelectable {
/**
*
*/
private static final long serialVersionUID = -7942369852180659605L;
/**
* The parent <code>Frame</code> of this dialog.
*/
private Frame frame;
/**
* The categorized camo patterns.
*/
private DirectoryItems camos;
/**
* The menu containing the category names.
*/
private Choice categories;
/**
* The list containing the item names.
*/
private List items;
/**
* The "keep old camo" button.
*/
private ImageButton keep;
/**
* The "select new camo" button.
*/
private ImageButton select;
/**
* The previously selected category.
*/
private String prevCat;
/**
* The previously selected item.
*/
private String prevItem;
/**
* The registered camo selection listeners.
*/
private Vector<ItemListener> listeners = new Vector<ItemListener>();
/**
* A helper function to close the dialog.
*/
/* package */void close() {
// Make sure the previous selection is the current selection.
setCategory(prevCat);
setItemName(prevItem);
// And hide the dialog.
setVisible(false);
}
/**
* A helper function to fill the list with items in the selected category.
*
* @param category - the <code>String</code> name of the category whose
* items should be displayed.
*/
/* package */void fillList(String category) {
// Clear the list of items.
items.removeAll();
// If this is the "no camos" category, then
// fill the item list with the colors.
if (Player.NO_CAMO.equals(category)) {
for (int color = 0; color < Player.colorNames.length; color++) {
items.add(Player.colorNames[color]);
}
}
// Otherwise, fill the list with the camo names.
else {
// Translate the "root camo" category name.
Iterator<String> camoNames;
if (Player.ROOT_CAMO.equals(category)) {
camoNames = camos.getItemNames(""); //$NON-NLS-1$
} else {
camoNames = camos.getItemNames(category);
}
// Get the camo names for this category.
while (camoNames.hasNext()) {
items.add(camoNames.next());
}
}
// If this is the previous selection, then
// select the previous item in the category.
// Otherwise, select the first item in the list.
if (prevCat.equals(category)) {
setItemName(prevItem);
} else {
setItemName(items.getItem(0));
}
}
/**
* A helper function to assign values for the previously selected camo. This
* function will also set the "keep old camo" button's image. Please note,
* if the specified selection does not exist, or if there is an error when
* generating the selection's image, the values won't change.
*
* @param category - the <code>String</code> category name. This value
* must be one of the categories from the
* <code>DirectoryItems</code>.
* @param item - the <code>String</code> name of the item. This value must
* be one of the items in the named category from
* <code>DirectoryItems</code>.
*/
/* package */void setPrevSelection(String category, String item) {
// If a "no camo" item is selected, clear the image.
if (Player.NO_CAMO.equals(category)) {
keep.setImage(null);
// Find the correct background color.
for (int color = 0; color < Player.colorNames.length; color++) {
if (Player.colorNames[color].equals(item)) {
keep.setBackground(PlayerColors.getColor(color));
prevCat = category;
prevItem = item;
break;
}
}
}
// Otherwise, clear the background color and try to
// set the camo image for the "keep old camo" button.
else {
try {
// Don't forget to translate the ROOT_CAMO.
String curCat = category;
if (Player.ROOT_CAMO.equals(curCat)) {
curCat = ""; //$NON-NLS-1$
}
// We need to copy the image to make it appear.
Image image = (Image) camos.getItem(curCat, item);
// Now, we're ready.
keep.setBackground(categories.getBackground());
keep.setImage(image);
prevCat = category;
prevItem = item;
} catch (Exception err) {
// Print the stack trace and display the message.
err.printStackTrace();
AlertDialog dlg = new AlertDialog(
frame,
Messages
.getString("CamoChoiceDialog.error_getting_camo"), err.getMessage()); //$NON-NLS-1$
dlg.setVisible(true);
dlg.dispose();
}
}
}
/**
* Create a dialog that allows players to choose a camo pattern.
*
* @param parent - the <code>Frame</code> that displays this dialog.
*/
public CamoChoiceDialog(Frame parent) {
// Initialize our superclass and record our parent frame.
super(parent, Messages
.getString("CamoChoiceDialog.select_camo_pattern"), true); //$NON-NLS-1$
frame = parent;
// Declare local variables.
Iterator<String> names;
String name;
// Parse the camo directory.
try {
camos = new DirectoryItems(new File("data/images/camo"), "", //$NON-NLS-1$ //$NON-NLS-2$
ImageFileFactory.getInstance());
} catch (Exception e) {
camos = null;
}
// Close the window, when the WM says to.
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
close();
}
});
// Use a border layout.
this.setLayout(new BorderLayout());
// Create a pulldown menu for the categories.
Panel panel = new Panel();
this.add(panel, BorderLayout.NORTH);
panel.setLayout(new GridBagLayout());
GridBagConstraints layout = new GridBagConstraints();
layout.anchor = GridBagConstraints.CENTER;
categories = new Choice();
panel.add(categories, layout);
// Fill the pulldown. Include the "no camo" category.
// Make sure the "no camo" and "root camo" are at top.
// Only add the "root camo" category if it contains items.
categories.addItem(Player.NO_CAMO);
if (camos != null) {
if (camos.getItemNames("").hasNext()) { //$NON-NLS-1$
categories.addItem(Player.ROOT_CAMO);
}
names = camos.getCategoryNames();
while (names.hasNext()) {
name = names.next();
if (!name.equals("")) { //$NON-NLS-1$
categories.addItem(name);
}
}
}
// Refill the item list when a new category is selected.
// Make sure that the "select new camo" button is updated.
categories.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) {
fillList((String) event.getItem());
CamoChoiceDialog.this.itemStateChanged(new ItemEvent(items,
event.getID(), items.getSelectedItem(),
ItemEvent.SELECTED));
}
});
// Create a list to hold the items in the category.
items = new List(15);
this.add(items, BorderLayout.CENTER);
// Update the "select new camo" when an item is selected.
items.addItemListener(this);
// Create a panel to hold our buttons.
// Use a grid bag layout.
panel = new Panel();
panel.setLayout(new GridBagLayout());
this.add(panel, BorderLayout.EAST);
layout = new GridBagConstraints();
layout.anchor = GridBagConstraints.EAST;
layout.gridx = 0;
layout.gridy = 0;
layout.gridwidth = 1;
layout.gridheight = 1;
layout.fill = GridBagConstraints.NONE;
layout.ipadx = 4;
layout.ipady = 4;
layout.weightx = 0;
layout.weighty = 0;
// Add a "spacer" label to push everything else to the bottom.
layout.weighty = 1;
panel.add(new Label(), layout);
layout.weighty = 0;
layout.gridy++;
// Add a label for the "keep old camo" button.
panel.add(new AdvancedLabel(Messages
.getString("CamoChoiceDialog.keep_old_camo")), layout); //$NON-NLS-1$
layout.gridy++;
// Create the "keep old camo" button.
keep = new ImageButton();
keep.setLabel(Messages.getString("CamoChoiceDialog.no_camo")); //$NON-NLS-1$
keep.setPreferredSize(84, 72);
keep.addActionListener(new ActionListener() {
// Pressing this button closes without saving.
public void actionPerformed(ActionEvent event) {
close();
}
});
keep.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
// Pressing enter on this button closes without saving.
if (KeyEvent.VK_ENTER == event.getKeyCode()) {
close();
}
}
});
panel.add(keep, layout);
layout.gridy++;
// Add a label for the "select new camo" button.
panel.add(new AdvancedLabel(Messages
.getString("CamoChoiceDialog.select_new_camo")), layout); //$NON-NLS-1$
layout.gridy++;
// Create the "select new camo" button.
select = new ImageButton();
select.setLabel(Messages.getString("CamoChoiceDialog.no_camo")); //$NON-NLS-1$
select.setPreferredSize(84, 72);
select.addActionListener(this);
panel.add(select, layout);
// Fire the "select new camo" action when the enter key is pressed
// on either the list or the "select new camo" button.
KeyAdapter enterAdapter = new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if (KeyEvent.VK_ENTER == event.getKeyCode()) {
actionPerformed(new ActionEvent(select, event.getID(),
select.getActionCommand()));
}
}
};
items.addKeyListener(enterAdapter);
select.addKeyListener(enterAdapter);
// Set the "previously selected" values to the defaults.
setPrevSelection(Player.NO_CAMO, Player.colorNames[0]);
// Fill the item list with the colors.
fillList(Player.NO_CAMO);
// Perform the initial layout.
this.pack();
}
/**
* Handle the "select new camo" button's action. <p/> Implements
* <code>ActionListener</code>.
*
* @param event - the <code>ActionEvent</code> that was invoked.
*/
public void actionPerformed(ActionEvent event) {
// Did the worker change their selection?
String curCat = categories.getSelectedItem();
String curItem = items.getSelectedItem();
if (!curCat.equals(prevCat) || !curItem.equals(prevItem)) {
// Save the new values.
setPrevSelection(curCat, curItem);
// Are there any listeners?
if (!listeners.isEmpty()) {
// Get the Image.
Image image = null;
Image[] array = (Image[]) this.getSelectedObjects();
if (null != array)
image = array[0];
// Create an ItemEvent for the change.
ItemEvent alert = new ItemEvent(this, event.getID(), image,
ItemEvent.ITEM_STATE_CHANGED);
// Alert the listeners.
for (ItemListener l : listeners) {
l.itemStateChanged(alert);
}
} // End have-listeners
} // End selection-changed
// Now exit.
close();
}
/**
* Update the "select new camo" button whenever a list item is selected.
* <p/> Implements <code>ItemListener</code>.
*
* @param event - the <code>ItemEvent</code> for the list selection.
*/
public void itemStateChanged(ItemEvent event) {
// Get the category and the index of the selected item.
String curCat = categories.getSelectedItem();
// If a "no camo" item is selected, clear the image.
if (Player.NO_CAMO.equals(curCat)) {
select.setImage(null);
select.setBackground(PlayerColors
.getColor(items.getSelectedIndex()));
return;
}
// Replace the ROOT_CAMO string with "".
if (Player.ROOT_CAMO.equals(curCat)) {
curCat = ""; //$NON-NLS-1$
}
// Clear the background and try to set the camo image.
try {
select.setBackground(categories.getBackground());
select.setImage((Image) camos.getItem(curCat, items
.getSelectedItem()));
} catch (Exception err) {
// Print the stack trace and display the message.
err.printStackTrace();
AlertDialog dlg = new AlertDialog(
frame,
Messages.getString("CamoChoiceDialog.error_getting_camo"), err.getMessage()); //$NON-NLS-1$
dlg.setVisible(true);
dlg.dispose();
}
}
/**
* Get the most recently selected (and confirmed) image. The player must
* click the "select new camo" button to change this value. <p/> Implements
* <code>ItemSelectable</code>.
*
* @return If the player has selected from the "no camo" category, or if an
* error occurs in getting the selected image, a <code>null</code>
* is returned. Otherwise, the array will contain a single
* <code>Image</code>.
*/
public Object[] getSelectedObjects() {
// Update the prev selection.
setPrevSelection(categories.getSelectedItem(), items.getSelectedItem());
// Return a null if the "no camo" category is selected.
if (Player.NO_CAMO.equals(prevCat))
return null;
// Try to get the selected camo's Image.
// Don't forget to translate the ROOT_CAMO.
Image image = null;
try {
String curCat = prevCat;
if (Player.ROOT_CAMO.equals(curCat)) {
curCat = ""; //$NON-NLS-1$
}
image = (Image) camos.getItem(curCat, prevItem);
} catch (Exception err) {
// Print the stack trace and display the message.
err.printStackTrace();
AlertDialog dlg = new AlertDialog(
frame,
Messages.getString("CamoChoiceDialog.error_getting_camo"), err.getMessage()); //$NON-NLS-1$
dlg.setVisible(true);
dlg.dispose();
}
if (null == image)
return null;
// Return an array containing the image.
Image[] ret = new Image[1];
ret[0] = image;
return ret;
}
/**
* Add an <code>ItemListener</code> that wants to be alerted when a new
* camo is selected. <p/> Implements <code>ItemSelectable</code>.
*
* @param listener - the <code>ItemListener</code> to be alerted.
*/
public void addItemListener(ItemListener listener) {
// Don't add a listener multiple times.
if (!listeners.contains(listener)) {
listeners.addElement(listener);
}
}
/**
* Remove an <code>ItemListener</code> that wants to stop be alerted when
* a new camo is selected. <p/> Implements <code>ItemSelectable</code>.
*
* @param listener - the <code>ItemListener</code> to be alerted.
*/
public void removeItemListener(ItemListener listener) {
listeners.removeElement(listener);
}
/**
* Get the selected category.
*
* @return the <code>String</code> name of the most recently selected
* category. This value will not be <code>null</code>.
*/
public String getCategory() {
return prevCat;
}
/**
* Get the selected item's name. If the most recently selected category is
* <code>Player.NO_CAMO</code>, then the item named is a color from
* <code>Player.colorNames</code>.
*
* @return the <code>String</code> name of the most recently selected
* item. This value will not be <code>null</code>.
*/
public String getItemName() {
return prevItem;
}
/**
* Set the selected category.
*
* @param category - the <code>String</code> name of the desired category.
* This value may be <code>null</code>. If no match is found,
* the category will not change.
*/
public void setCategory(String category) {
// Get the current selection.
String cur = categories.getSelectedItem();
// Do nothing, if the request is for the selected item.
if (!cur.equals(category)) {
// Try to find the requested item.
for (int loop = 0; loop < categories.getItemCount(); loop++) {
// Did we find it?
if (categories.getItem(loop).equals(category)) {
// Select this position.
categories.select(loop);
// Fill the list.
fillList(category);
// Stop looking for the category.
break;
} // End found-requested-category
} // Check the next category
} // End new-selection
}
/**
* Set the selected item in the currently-selected category.
*
* @param item - the <code>String</code> name of the desired item. This
* value may be <code>null</code>. If no match is found, the
* item selection will not change.
*/
public void setItemName(String item) {
// Do nothing is we're passed a null.
if (null != item) {
// Get the current selection.
String cur = items.getSelectedItem();
// Do nothing, if the request is for the selected item.
if (!item.equals(cur)) {
// Try to find the requested item.
String[] contents = items.getItems();
for (int loop = 0; loop < items.getItemCount(); loop++) {
// Did we find it?
if (contents[loop].equals(item)) {
// Select this position.
items.select(loop);
// Stop looking for the item.
break;
} // End found-requested-item
} // Check the next item
} // End new-selection
} // End not-passed-null
}
/**
* Show the dialog. Make sure that all selections have been applied. <p/>
* Overrides <code>Component#setVisible()</code>.
*/
public void setVisible(boolean show) {
if (show) {
// Make sure the "keep" button is set correctly.
setPrevSelection(categories.getSelectedItem(), items
.getSelectedItem());
// Make sure the "select" button is set correctly.
itemStateChanged(new ItemEvent(items, 0, items.getSelectedItem(),
ItemEvent.SELECTED));
// Now show the dialog.
}
super.setVisible(show);
}
}