/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.plotter;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.util.Iterator;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import com.rapidminer.gui.new_plotter.gui.popup.PopupAction;
import com.rapidminer.gui.tools.ListHoverHelper;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.tools.LogService;
/**
* Selection field for the available {@link Plotter} which shows preview images for all plotters.
* The look and feel is similar to a Combobox. A selection informs all listeners at
* {@link PlotterControlPanel}.
*
* @author David Arnu, Michael Knopf
*
*/
public class PlotterChooser extends JButton {
private static final long serialVersionUID = 1L;
/**
* The gap of the drop down arrow button
*/
private static final int GAP_RIGHT = 11;
private static final int SMALL_ICON_SIZE = 50;
private static final int ICON_SIZE = 100;
private static final double MIN_RESOLUTION_HEIGHT = 800;
private boolean smallIcons = false;
private final class PlotterListCellRenderer<E> extends JPanel implements ListCellRenderer<E> {
private static final long serialVersionUID = 1L;
// cell components
private JLabel label;
// styling
private final Color defaultBackground; // see constructor
private final Color hoverBackground = Color.WHITE;
private final int borderWidth = 3;
private final Border defaultrBorder = BorderFactory.createEmptyBorder(this.borderWidth, this.borderWidth,
this.borderWidth, this.borderWidth);
private final Border hoverBorder = BorderFactory.createLineBorder(this.hoverBorderColor, this.borderWidth);
private final Color hoverBorderColor = Color.DARK_GRAY;
private final Font defaultFont; // see constructor
private final Font hoverFont; // see constructor
/**
* Defines the layout of the panel once for all cells. Instances are reused for cell
* rendering.
*/
public PlotterListCellRenderer() {
super();
// setup label
this.label = new JLabel();
this.label.setHorizontalAlignment(SwingConstants.CENTER);
this.label.setVerticalAlignment(SwingConstants.TOP);
this.label.setHorizontalTextPosition(SwingConstants.CENTER);
this.label.setVerticalTextPosition(SwingConstants.BOTTOM);
this.add(this.label);
// setup colors / fonts
this.defaultBackground = this.getBackground();
this.defaultFont = this.label.getFont();
this.hoverFont = this.defaultFont.deriveFont(Font.BOLD);
}
/**
* Updates panel and label for the given cell.
*/
@Override
public Component getListCellRendererComponent(JList<? extends E> list, E value, int index, boolean isSelected,
boolean cellHasFocus) {
String plotterName = (String) value;
// display selected variant of the icon if the cell is selected
// or in a hover state
Icon icon;
if (ListHoverHelper.index(list) == index) {
icon = getIcon(plotterName, true);
} else {
icon = getIcon(plotterName, isSelected);
}
// update panel and label depending on cell state
label.setIcon(icon);
label.setText(plotterName);
if (isSelected) {
this.setBorder(this.hoverBorder);
this.setBackground(this.hoverBackground);
this.label.setFont(this.hoverFont);
} else {
this.setBorder(this.defaultrBorder);
// since the same panel and label is used for all cells, we
// have to 'reset' the styles to their default values
this.setBackground(this.defaultBackground);
this.label.setFont(this.defaultFont);
}
this.requestFocusInWindow();
return this;
}
private Icon getIcon(String plotterName, boolean selected) {
// check to decide which icon size should be loaded
if (!isSmallIconsUsed()) {
if (selected) {
return SwingTools
.createImage("icons/chartPreview/" + ICON_SIZE + "/" + plotterName.replace(' ', '_') + ".png");
} else {
return SwingTools.createImage(
"icons/chartPreview/" + ICON_SIZE + "/" + plotterName.replace(' ', '_') + "-grey.png");
}
} else {
if (selected) {
return SwingTools.createImage(
"icons/chartPreview/" + SMALL_ICON_SIZE + "/" + plotterName.replace(' ', '_') + ".png");
} else {
return SwingTools.createImage(
"icons/chartPreview/" + SMALL_ICON_SIZE + "/" + plotterName.replace(' ', '_') + "-grey.png");
}
}
}
}
private JList<String> plotterList = new JList<>(new DefaultListModel<>());
public PlotterChooser() {
super();
ListHoverHelper.install(plotterList);
smallIcons = isResolutionTooSmall();
plotterList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
plotterList.setVisibleRowCount(5);
plotterList.setCellRenderer(new PlotterListCellRenderer<>());
plotterList.setBackground(UIManager.getColor("Panel.background"));
plotterList.setSelectionForeground(Color.BLACK);
final PopupAction popupAction = new PopupAction("choose_plotter", plotterList);
setAction(popupAction);
this.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (smallIcons != isResolutionTooSmall()) {
smallIcons = !smallIcons; // toggle the type of icons
// if the resolution has changed, create a new panel to support this resolution
plotterList.setCellRenderer(new PlotterListCellRenderer<>());
}
}
});
plotterList.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent arg0) {
popupAction.focusLost();
fireSelectionEvent();
}
});
plotterList.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if (key == KeyEvent.VK_ENTER) {
popupAction.focusLost();
fireSelectionEvent();
} else if (key == KeyEvent.VK_ESCAPE) {
// close popup without doing anything
popupAction.focusLost();
}
}
});
}
public void setSettings(PlotterConfigurationModel plotterSettings) {
populateList(plotterSettings);
}
private void populateList(PlotterConfigurationModel plotterSettings) {
((DefaultListModel<?>) plotterList.getModel()).clear();
Iterator<String> n = plotterSettings.getAvailablePlotters().keySet().iterator();
while (n.hasNext()) {
String plotterName = n.next();
try {
Class<? extends Plotter> plotterClass = plotterSettings.getAvailablePlotters().get(plotterName);
if (plotterClass != null) {
((DefaultListModel<String>) plotterList.getModel()).addElement(plotterName);
}
} catch (IllegalArgumentException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.plotter.PlotterControlPanel.instatiating_plotter_error", plotterName);
} catch (SecurityException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.plotter.PlotterControlPanel.instatiating_plotter_error", plotterName);
}
}
}
public void removeAllItems() {
((DefaultListModel<String>) plotterList.getModel()).clear();
}
public void addItem(String item) {
((DefaultListModel<String>) plotterList.getModel()).addElement(item);
if (plotterList.getModel().getSize() == 1) {
plotterList.setSelectedIndex(0);
updateButtonText();
}
}
public void setSelectedItem(String plotterName) {
plotterList.setSelectedValue(plotterName, true);
updateButtonText();
}
private void fireSelectionEvent() {
updateButtonText();
fireItemStateChanged(new ItemEvent(this, plotterList.getSelectedIndex(), plotterList.getSelectedIndex(),
ItemEvent.SELECTED));
}
private void updateButtonText() {
setText((String) getSelectedItem());
}
public Object getSelectedItem() {
return plotterList.getSelectedValue();
}
/**
* Query if the small plot preview icons are used
*
* @return true, if the small icons are used
*/
public boolean isSmallIconsUsed() {
return smallIcons;
}
/**
* Set to true if the small plot preview icons should be used
*
* @param smallIcons
* true, for the small icons to be used
*/
public void setUseSmallIcons(boolean smallIcons) {
this.smallIcons = smallIcons;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
GeneralPath arrow = new GeneralPath();
int h = 2;
int w = 4;
arrow.moveTo(getWidth() - GAP_RIGHT - w, getHeight() / 2);
arrow.lineTo(getWidth() - GAP_RIGHT + w, getHeight() / 2);
arrow.lineTo(getWidth() - GAP_RIGHT, getHeight() / 2 + 2 * h);
arrow.closePath();
Graphics2D g2 = (Graphics2D) g.create();
if (isEnabled()) {
g2.setColor(Color.BLACK);
} else {
g2.setColor(Color.GRAY);
}
g2.fill(arrow);
g2.dispose();
}
/**
*
* @return Returns true if a display has a too small resolution
*/
private boolean isResolutionTooSmall() {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] allScreens = env.getScreenDevices();
// check for all screen devices their display height
for (GraphicsDevice screen : allScreens) {
if (screen.getDefaultConfiguration().getBounds().getHeight() < MIN_RESOLUTION_HEIGHT) {
return true;
}
}
return false; // big icons are supported
}
}