/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package gov.nasa.arc.mct.fastplot.view;
import gov.nasa.arc.mct.fastplot.bridge.LegendEntry;
import gov.nasa.arc.mct.fastplot.bridge.PlotConstants;
import gov.nasa.arc.mct.fastplot.bridge.PlotLineColorPalette;
import gov.nasa.arc.mct.fastplot.bridge.PlotLineShapePalette;
import gov.nasa.arc.mct.fastplot.bridge.PlotMarkerIcon;
import gov.nasa.arc.mct.fastplot.settings.LineSettings;
import gov.nasa.arc.mct.fastplot.utils.MenuItemSpinner;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.util.ResourceBundle;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
/**
* Provides popup menus to legend entries upon request.
* @author vwoeltje
*/
public class LegendEntryPopupMenuFactory {
private static final ResourceBundle BUNDLE =
ResourceBundle.getBundle(LegendEntryPopupMenuFactory.class.getName().substring(0,
LegendEntryPopupMenuFactory.class.getName().lastIndexOf("."))+".Bundle");
private PlotViewManifestation manifestation;
public LegendEntryPopupMenuFactory(PlotViewManifestation targetPlotManifestation) {
manifestation = targetPlotManifestation;
}
/**
* Get a popup menu for a specified legend entry
* @param entry the legend entry to produce a popup menu
* @return a popup menu with options appropriate to the specified legend entry
*/
public JPopupMenu getPopup(LegendEntry entry) {
LegendEntryPopup popup = new LegendEntryPopup(manifestation, entry);
return popup;
}
private class LegendEntryPopup extends JPopupMenu {
private static final long serialVersionUID = -4846098785335776279L;
private int spinnerValue;
public LegendEntryPopup(final PlotViewManifestation manifestation, final LegendEntry legendEntry) {
super();
if (!manifestation.isLocked()) {
String name = legendEntry.getComputedBaseDisplayName();
spinnerValue = legendEntry.getNumberRegressionPoints();
if (name.isEmpty()) name = legendEntry.getFullBaseDisplayName();
final LineSettings settings = legendEntry.getLineSettings();
// Color submenu
String subMenuText = String.format(BUNDLE.getString("SelectColor.label"), name);
JMenu subMenu = new JMenu(subMenuText);
Color currentColor = legendEntry.getForeground();
for (int i = 0; i < PlotConstants.MAX_NUMBER_OF_DATA_ITEMS_ON_A_PLOT; i++) {
JMenuItem item = new JRadioButtonMenuItem("",
new SolidColorIcon(PlotLineColorPalette.getColor(i)),
(currentColor == PlotLineColorPalette.getColor(i))
);
final int colorIndex = i;
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
legendEntry.setForeground(PlotLineColorPalette.getColor(colorIndex));
manifestation.persistPlotLineSettings();
}
});
subMenu.add(item);
}
add(subMenu);
// Thickness submenu
subMenuText = String.format(BUNDLE.getString("SelectThickness.label"), name);
subMenu = new JMenu(subMenuText);
for (int i = 1; i <= PlotConstants.MAX_LINE_THICKNESS; i++) {
JMenuItem item = new JRadioButtonMenuItem("" + i,
(settings.getThickness() == i));
final int thickness = i;
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
settings.setThickness(thickness);
legendEntry.setLineSettings(settings);
manifestation.persistPlotLineSettings();
}
});
subMenu.add(item);
}
add(subMenu);
// Marker submenu
if (manifestation.getPlot() != null &&
manifestation.getPlot().getPlotLineDraw().drawMarkers()) {
subMenuText = String.format(BUNDLE.getString("SelectMarker.label"), name);
subMenu = new JMenu(subMenuText);
for (int i = 0; i < PlotConstants.MAX_NUMBER_OF_DATA_ITEMS_ON_A_PLOT; i++) {
JMenuItem item = new JRadioButtonMenuItem("",
new PlotMarkerIcon(PlotLineShapePalette.getShape(i), false),
(settings.getMarker() == i && !settings.getUseCharacter()));
item.setForeground(legendEntry.getForeground());
final int marker = i;
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
settings.setMarker(marker);
settings.setUseCharacter(false);
legendEntry.setLineSettings(settings);
manifestation.persistPlotLineSettings();
}
});
subMenu.add(item);
}
JMenuItem other = new JRadioButtonMenuItem(BUNDLE.getString("SelectCharacter.label"),
settings.getUseCharacter());
if (!settings.getCharacter().isEmpty()) {
FontRenderContext frc = ((Graphics2D) manifestation.getGraphics()).getFontRenderContext();
other.setIcon(new PlotMarkerIcon(
PlotLineShapePalette.getShape(settings.getCharacter(), frc),
PlotLineColorPalette.getColor(settings.getColorIndex()),
false));
}
other.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
final CharacterDialog dialog = new CharacterDialog();
dialog.setInitialString(settings.getCharacter());
dialog.ok.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
settings.setCharacter(dialog.field.getText().trim());
settings.setUseCharacter(true);
legendEntry.setLineSettings(settings);
manifestation.persistPlotLineSettings();
}
});
dialog.setVisible(true);
}
});
subMenu.add(other);
add(subMenu);
}
// Regression line
addSeparator();
String subMenuText2 = String.format(BUNDLE.getString("RegressionPointsLabel"),
name);
final JMenuItem regressionLineCheckBox = new JCheckBoxMenuItem(BUNDLE.getString("RegressionLineLabel"),false);
final JMenu regressionMenu = new JMenu(subMenuText2);
SpinnerModel pointsModel = new SpinnerNumberModel(legendEntry.getNumberRegressionPoints(), 2,
PlotConstants.MAX_NUMBER_REGRESSION_POINTS, 1);
final JSpinner spinner = new MenuItemSpinner(pointsModel, regressionMenu);
spinner.setPreferredSize(new Dimension(50, 20));
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
legendEntry.setNumberRegressionPoints(Integer.parseInt(((JSpinner)e.getSource()).getValue().toString()));
}
});
addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// TODO Auto-generated method stub
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
if (spinnerValue != Integer.parseInt(spinner.getValue().toString())) {
manifestation.persistPlotLineSettings();
}
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// TODO Auto-generated method stub
}
});
regressionLineCheckBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
AbstractButton abstractButton = (AbstractButton) e.getSource();
if (abstractButton.getModel().isSelected()) {
legendEntry.setHasRegressionLine(true);
} else {
legendEntry.setHasRegressionLine(false);
}
manifestation.persistPlotLineSettings();
}
});
if (legendEntry.hasRegressionLine()) {
regressionLineCheckBox.setSelected(true);
} else {
regressionLineCheckBox.setSelected(false);
}
add(regressionLineCheckBox);
regressionMenu.add(spinner);
add(regressionMenu);
}
}
private class SolidColorIcon implements Icon {
private Color iconColor;
public SolidColorIcon (Color c) {
iconColor = c;
}
@Override
public int getIconHeight() {
return 12;
}
@Override
public int getIconWidth() {
return 48;
}
@Override
public void paintIcon(Component arg0, Graphics g, int x,
int y) {
g.setColor(iconColor);
g.fillRect(x, y, getIconWidth(), getIconHeight() - 1);
g.setColor(iconColor.darker());
g.drawRect(x, y, getIconWidth(), getIconHeight() - 1);
}
}
private class CharacterDialog extends JDialog {
private static final long serialVersionUID = 1644116578638815212L;
private JTextField field = new JTextField(1);
private JButton ok, cancel;
public CharacterDialog() {
super((Frame) SwingUtilities.windowForComponent(manifestation));
Frame f = (Frame) SwingUtilities.windowForComponent(manifestation);
setTitle(BUNDLE.getString("SelectCharacter.title") + " - " +
f.getTitle() + " - " +
manifestation.getInfo().getViewName());
setLocationRelativeTo((Frame) SwingUtilities.windowForComponent(manifestation));
ActionListener closer = new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
setVisible(false);
dispose();
}
};
ok = new JButton("OK");
cancel = new JButton("Cancel");
ok .addActionListener(closer);
cancel.addActionListener(closer);
getRootPane().setDefaultButton(ok);
final Document doc = field.getDocument();
if (doc instanceof AbstractDocument) {
AbstractDocument ad = (AbstractDocument) doc;
ad.setDocumentFilter(new DocumentFilter() {
@Override
public void insertString(FilterBypass bypass, int offset,
String str, AttributeSet attr)
throws BadLocationException {
if (doc.getLength() + str.length() < 2 && !str.trim().isEmpty()) {
super.insertString(bypass, offset, str, attr);
}
}
@Override
public void replace(FilterBypass bypass, int start,
int length, String str, AttributeSet attr)
throws BadLocationException {
if (doc.getLength() + str.length() < 2 && !str.trim().isEmpty()) {
super.replace(bypass, start, length, str, attr);
}
}
});
}
doc.addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent arg0) {
ok.setEnabled(doc.getLength() == 1);
}
@Override
public void insertUpdate(DocumentEvent arg0) {
ok.setEnabled(doc.getLength() == 1);
}
@Override
public void removeUpdate(DocumentEvent arg0) {
ok.setEnabled(doc.getLength() == 1);
}
});
JPanel panel = new JPanel();
JLabel label = new JLabel(BUNDLE.getString("SelectCharacterDescription.label"));
SpringLayout layout = new SpringLayout();
panel.setLayout(layout);
panel .add(label);
panel .add(field);
panel .add(ok);
panel .add(cancel);
layout.putConstraint(SpringLayout.WEST , label , 0, SpringLayout.WEST , panel );
layout.putConstraint(SpringLayout.WEST , field , 12, SpringLayout.EAST , label );
layout.putConstraint(SpringLayout.EAST , panel , 0, SpringLayout.EAST , field );
layout.putConstraint(SpringLayout.EAST , cancel, 0, SpringLayout.EAST , panel );
layout.putConstraint(SpringLayout.EAST , ok , -5, SpringLayout.WEST , cancel);
layout.putConstraint(SpringLayout.NORTH, label , 0, SpringLayout.NORTH, panel );
layout.putConstraint(SpringLayout.NORTH, field , 0, SpringLayout.NORTH, panel );
layout.putConstraint(SpringLayout.NORTH, cancel, 17, SpringLayout.SOUTH, field );
layout.putConstraint(SpringLayout.NORTH, ok , 17, SpringLayout.SOUTH, field );
layout.putConstraint(SpringLayout.SOUTH, panel, 0, SpringLayout.SOUTH, cancel);
layout.putConstraint(SpringLayout.SOUTH, panel , 0, SpringLayout.SOUTH, ok );
panel.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11));
getContentPane().add(panel);
pack();
}
private void setInitialString (String s) {
field.setText(s.trim());
ok.setEnabled(!s.trim().isEmpty());
}
}
}
}