/*******************************************************************************
* Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://robocode.sourceforge.net/license/epl-v10.html
*
* Contributors:
* Mathew A. Nelson
* - Initial API and implementation
* Flemming N. Larsen
* - Code cleanup
* - Ported to Java 5
* - Replaced FileSpecificationVector with plain Vector
* - Replaced synchronizedList on lists for availableRobots, robotList, and
* availablePackages with a CopyOnWriteArrayList in order to prevent
* ConcurrentModificationExceptions when accessing these list via
* Iterators using public methods to this class
* - Changed the F5 key press for refreshing the list of available robots
* into 'shortcut key' + R to comply with other OSes like e.g. Mac OS
* Robert D. Maupin
* - Replaced old collection types like Vector and Hashtable with
* synchronized List and HashMap
*******************************************************************************/
package net.sf.robocode.ui.dialog;
import net.sf.robocode.repository.IRepositoryItem;
import net.sf.robocode.ui.util.ShortcutUtil;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Mathew A. Nelson (original)
* @author Flemming N. Larsen (contributor)
* @author Robert D. Maupin (contributor)
*/
@SuppressWarnings("serial")
public class AvailableRobotsPanel extends JPanel {
private final List<ItemWrapper> availableRobots = new CopyOnWriteArrayList<ItemWrapper>();
private List<IRepositoryItem> robotList = new CopyOnWriteArrayList<IRepositoryItem>();
private final List<String> availablePackages = new CopyOnWriteArrayList<String>();
private JScrollPane availableRobotsScrollPane;
private JList availableRobotsList;
private final JButton actionButton;
private final JList actionList;
private JList availablePackagesList;
private JScrollPane availablePackagesScrollPane;
private RobotNameCellRenderer robotNameCellRenderer;
private final RobotSelectionPanel robotSelectionPanel;
private final String title;
private final EventHandler eventHandler = new EventHandler();
public AvailableRobotsPanel(JButton actionButton, String title, JList actionList,
RobotSelectionPanel robotSelectionPanel) {
super();
this.title = title;
this.actionButton = actionButton;
this.actionList = actionList;
this.robotSelectionPanel = robotSelectionPanel;
initialize();
}
private void initialize() {
setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title));
setLayout(new BorderLayout());
JPanel top = new JPanel();
top.setLayout(new GridLayout(1, 2));
JPanel a = new JPanel();
a.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), "Packages"));
a.setLayout(new BorderLayout());
a.add(getAvailablePackagesScrollPane());
a.setPreferredSize(new Dimension(120, 100));
top.add(a);
JPanel b = new JPanel();
b.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), "Robots"));
b.setLayout(new BorderLayout());
b.add(getAvailableRobotsScrollPane());
b.setPreferredSize(new Dimension(120, 100));
top.add(b);
add(top, BorderLayout.CENTER);
JLabel refreshLabel = new JLabel("Press " + ShortcutUtil.getModifierKeyText() + "+R to refresh");
refreshLabel.setHorizontalAlignment(SwingConstants.CENTER);
add(refreshLabel, BorderLayout.SOUTH);
}
public List<ItemWrapper> getAvailableRobots() {
return availableRobots;
}
public List<IRepositoryItem> getRobotList() {
return robotList;
}
public List<ItemWrapper> getSelectedRobots() {
List<ItemWrapper> selected = new ArrayList<ItemWrapper>();
for (int i : getAvailableRobotsList().getSelectedIndices()) {
selected.add(availableRobots.get(i));
}
return selected;
}
/**
* Return the availableRobotsList.
*
* @return JList
*/
public JList getAvailableRobotsList() {
if (availableRobotsList == null) {
availableRobotsList = new JList();
availableRobotsList.setModel(new AvailableRobotsModel());
availableRobotsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
availableRobotsList.setCellRenderer(getRobotNameCellRenderer());
MouseListener mouseListener = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// This does not work in Linux under IBM JRE 1.3.0...
if (e.getClickCount() >= 2) {
if (e.getClickCount() % 2 == 0) {
if (actionButton != null) {
actionButton.doClick();
}
}
}
}
};
availableRobotsList.addMouseListener(mouseListener);
availableRobotsList.addListSelectionListener(eventHandler);
}
return availableRobotsList;
}
/**
* Return the JScrollPane1 property value.
*
* @return JScrollPane
*/
private JScrollPane getAvailableRobotsScrollPane() {
if (availableRobotsScrollPane == null) {
availableRobotsScrollPane = new JScrollPane();
availableRobotsScrollPane.setViewportView(getAvailableRobotsList());
// Bug fix [2975871] - Minor visual bug - Currently selected robot gets covered
availableRobotsScrollPane.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
getAvailableRobotsList().ensureIndexIsVisible(getAvailableRobotsList().getSelectedIndex());
}
});
}
return availableRobotsScrollPane;
}
public void setRobotList(List<IRepositoryItem> robotListList) {
robotList = robotListList;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
availablePackages.clear();
availableRobots.clear();
if (robotList == null) {
robotList = new CopyOnWriteArrayList<IRepositoryItem>();
availablePackages.add("One moment please...");
((AvailablePackagesModel) getAvailablePackagesList().getModel()).changed();
getAvailablePackagesList().clearSelection();
((AvailableRobotsModel) getAvailableRobotsList().getModel()).changed();
} else {
availablePackages.add("(All)");
String packageName;
for (IRepositoryItem robotSpec : robotList) {
packageName = robotSpec.getFullPackage();
if (packageName == null) {
continue;
}
if (!availablePackages.contains(packageName)) {
availablePackages.add(packageName);
}
}
availablePackages.add("(No package)");
for (IRepositoryItem robotSpec : robotList) {
availableRobots.add(new ItemWrapper(robotSpec));
}
((AvailablePackagesModel) getAvailablePackagesList().getModel()).changed();
getAvailablePackagesList().setSelectedIndex(0);
((AvailableRobotsModel) getAvailableRobotsList().getModel()).changed();
getAvailablePackagesList().requestFocus();
}
}
});
}
private void availablePackagesListSelectionChanged() {
int sel[] = getAvailablePackagesList().getSelectedIndices();
boolean useShortName = false;
availableRobots.clear();
if (sel.length == 1) {
useShortName = true;
getAvailablePackagesList().scrollRectToVisible(getAvailablePackagesList().getCellBounds(sel[0], sel[0]));
}
for (int element : sel) {
String selectedPackage = availablePackages.get(element);
if (selectedPackage.equals("(All)")) {
useShortName = false;
availableRobots.clear();
for (IRepositoryItem robotItem : robotList) {
getRobotNameCellRenderer().setUseShortName(useShortName);
availableRobots.add(new ItemWrapper(robotItem));
}
break;
}
// Single package.
for (IRepositoryItem robotItem : robotList) {
getRobotNameCellRenderer().setUseShortName(useShortName);
if ((robotItem.getFullPackage() == null && selectedPackage.equals("(No package)"))
|| selectedPackage.equals(robotItem.getFullPackage())) {
availableRobots.add(new ItemWrapper(robotItem));
}
}
}
((AvailableRobotsModel) getAvailableRobotsList().getModel()).changed();
if (availableRobots.size() > 0) {
availableRobotsList.setSelectedIndex(0);
availableRobotsListSelectionChanged();
}
}
private void availableRobotsListSelectionChanged() {
int sel[] = getAvailableRobotsList().getSelectedIndices();
if (sel.length == 1) {
if (actionList != null) {
actionList.clearSelection();
}
IRepositoryItem robotSpecification = ((ItemWrapper) getAvailableRobotsList().getModel().getElementAt(sel[0])).getItem();
if (robotSelectionPanel != null) {
robotSelectionPanel.showDescription(robotSpecification);
}
} else {
if (robotSelectionPanel != null) {
robotSelectionPanel.showDescription(null);
}
}
}
public void clearSelection() {
getAvailableRobotsList().clearSelection();
((AvailableRobotsModel) getAvailableRobotsList().getModel()).changed();
}
/**
* Return the availableRobotsList.
*
* @return JList
*/
private JList getAvailablePackagesList() {
if (availablePackagesList == null) {
availablePackagesList = new JList();
availablePackagesList.setModel(new AvailablePackagesModel());
availablePackagesList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
availablePackagesList.addListSelectionListener(eventHandler);
}
return availablePackagesList;
}
/**
* Return the availablePackagesScrollPane
*
* @return JScrollPane
*/
private JScrollPane getAvailablePackagesScrollPane() {
if (availablePackagesScrollPane == null) {
availablePackagesScrollPane = new JScrollPane();
availablePackagesScrollPane.setViewportView(getAvailablePackagesList());
}
return availablePackagesScrollPane;
}
private RobotNameCellRenderer getRobotNameCellRenderer() {
if (robotNameCellRenderer == null) {
robotNameCellRenderer = new RobotNameCellRenderer();
}
return robotNameCellRenderer;
}
private class EventHandler implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
if (e.getSource() == getAvailableRobotsList()) {
availableRobotsListSelectionChanged();
} else if (e.getSource() == getAvailablePackagesList()) {
availablePackagesListSelectionChanged();
}
}
}
private class AvailablePackagesModel extends AbstractListModel {
public void changed() {
fireContentsChanged(this, 0, getSize());
}
public int getSize() {
return availablePackages.size();
}
public String getElementAt(int which) {
return availablePackages.get(which);
}
}
private class AvailableRobotsModel extends AbstractListModel {
public void changed() {
fireContentsChanged(this, 0, getSize());
}
public int getSize() {
return availableRobots.size();
}
public ItemWrapper getElementAt(int which) {
return availableRobots.get(which);
}
}
/**
* Is there because of keyboard navigation is tied to toString() method
*/
public static class ItemWrapper {
private final IRepositoryItem item;
public ItemWrapper(IRepositoryItem item) {
this.item = item;
}
public IRepositoryItem getItem() {
return item;
}
// Used writing the robot name in JList. Is used for keyboard typing in JList to find robot. Bugfix for [2658090]
public String toString() {
return item.getUniqueShortClassNameWithVersion();
}
}
/**
* RobotNameCellRender, which renders the list cells with "Team: " prefix, and with or without package names.
* This is cheating, as the ItemWrapper.toString() delivers the string used for keyboard navigation etc.
*/
private static class RobotNameCellRenderer extends JLabel implements ListCellRenderer {
private boolean useShortName = false;
public RobotNameCellRenderer() {
setOpaque(true);
}
public void setUseShortName(boolean useShortNames) {
this.useShortName = useShortNames;
}
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setComponentOrientation(list.getComponentOrientation());
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
String text;
if (value instanceof ItemWrapper) {
IRepositoryItem item = ((ItemWrapper) value).getItem();
text = (item.isTeam() ? "Team: " : "");
text += useShortName
? item.getUniqueShortClassNameWithVersion()
: item.getUniqueFullClassNameWithVersion();
} else {
text = value.toString();
}
setText(text);
setEnabled(list.isEnabled());
setFont(list.getFont());
return this;
}
}
}