/**
* 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.processeditor.results;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.text.DateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import com.rapidminer.Process;
import com.rapidminer.ProcessLocation;
import com.rapidminer.gui.RapidMinerGUI;
import com.rapidminer.gui.actions.ExtendedMouseClickedAdapter;
import com.rapidminer.gui.look.Colors;
import com.rapidminer.gui.look.RapidLookAndFeel;
import com.rapidminer.gui.look.borders.TextFieldBorder;
import com.rapidminer.gui.look.ui.ExtensionButtonUI;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.operator.IOObject;
import com.rapidminer.tools.Tools;
/**
* Summarizes the result of a single process execution.
*
* @author Simon Fischer, Marco Boeck
*
*/
public class ProcessExecutionResultOverview extends JPanel {
private static final int MAX_PROCESS_NAME_LENGTH = 60;
/** arrow icon with an arrow pointing up */
private static final ImageIcon ICON_ARROW_UP = SwingTools.createIcon("16/" + "navigate_up.png");
/** arrow icon with an arrow pointing down */
private static final ImageIcon ICON_ARROW_DOWN = SwingTools.createIcon("16/" + "navigate_down.png");
private static final long serialVersionUID = 1L;
private final Action RESTORE_PROCESS = new ResourceAction(true, "resulthistory.restore_process") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
try {
if (RapidMinerGUI.getMainFrame().close()) {
Process process = new Process(ProcessExecutionResultOverview.this.process);
process.setProcessLocation(processLocation);
RapidMinerGUI.getMainFrame().setProcess(process, true);
}
} catch (Exception e1) {
SwingTools.showSimpleErrorMessage("cannot_restore_history_process", e1);
}
}
};
private final Action REMOVE_FROM_HISTORY = new ResourceAction(true, "resulthistory.remove") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
parent.removeProcessOverview(ProcessExecutionResultOverview.this);
}
};
private final JButton removeButton = new JButton(REMOVE_FROM_HISTORY);
private final JButton restoreButton = new JButton(RESTORE_PROCESS);
private final String process;
private final ProcessLocation processLocation;
private final Date terminationTime;
private long executionTime;
private boolean expanded = false;
private final List<SingleResultOverview> results = new LinkedList<>();
private final ResultOverview parent;
private final JLabel labelExp;
private final JLabel headerLabel;
private JPanel resultPanel;
public ProcessExecutionResultOverview(ResultOverview parent, Process process, List<IOObject> results,
String statusMessage) {
this.parent = parent;
this.process = process.getRootOperator().getXML(false);
this.processLocation = process.getProcessLocation();
setOpaque(true);
setBackground(Colors.WHITE);
setBorder(new TextFieldBorder());
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
setLayout(new BorderLayout());
JPanel mainPanel = new JPanel(new GridBagLayout());
mainPanel.setOpaque(true);
mainPanel.setBackground(Colors.WHITE);
labelExp = new JLabel(ICON_ARROW_DOWN, SwingConstants.RIGHT);
labelExp.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
add(labelExp, BorderLayout.WEST);
add(mainPanel, BorderLayout.CENTER);
try {
executionTime = Long.parseLong(String.valueOf(process.getRootOperator().getValue("execution-time").getValue()));
} catch (NumberFormatException e) {
executionTime = System.currentTimeMillis() - process.getRootOperator().getStartTime();
}
this.terminationTime = new Date(process.getRootOperator().getStartTime() + executionTime);
String processName;
if (process.getProcessLocation() != null) {
processName = process.getProcessLocation().getShortName();
} else {
processName = process.getRootOperator().getName();
}
StringBuilder b = new StringBuilder();
b.append("<html><strong>");
b.append(SwingTools.getShortenedDisplayName(processName, MAX_PROCESS_NAME_LENGTH));
b.append("</strong> (");
b.append(results.size());
b.append(" results. <small>");
b.append(statusMessage);
b.append("</small>)<br/>Completed: ");
b.append(DateFormat.getDateTimeInstance().format(terminationTime));
b.append(" (execution time: ").append(Tools.formatDuration(executionTime)).append(")");
b.append("</html>");
headerLabel = new JLabel(b.toString());
headerLabel.setFont(headerLabel.getFont().deriveFont(14f));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainPanel.add(headerLabel, gbc);
restoreButton.setText(null);
restoreButton.setContentAreaFilled(false);
restoreButton.setUI(new ExtensionButtonUI());
gbc.gridx += 1;
gbc.weightx = 0.0;
gbc.fill = GridBagConstraints.NONE;
mainPanel.add(restoreButton, gbc);
removeButton.setText(null);
removeButton.setContentAreaFilled(false);
removeButton.setUI(new ExtensionButtonUI());
gbc.gridx += 1;
mainPanel.add(removeButton, gbc);
resultPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
resultPanel.setOpaque(true);
resultPanel.setBackground(Colors.WHITE);
resultPanel.setCursor(Cursor.getDefaultCursor());
gbc.gridx = 0;
gbc.gridy += 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = 3;
mainPanel.add(resultPanel, gbc);
int i = 0;
for (IOObject result : results) {
SingleResultOverview singleOverview = new SingleResultOverview(result, process, i);
this.results.add(singleOverview);
i++;
}
addMouseListener(new ExtendedMouseClickedAdapter() {
@Override
public void click(MouseEvent e) {
setExpanded(!expanded);
}
@Override
public void showContextMenu(Point point) {
JPopupMenu menu = new JPopupMenu();
menu.add(RESTORE_PROCESS);
menu.add(REMOVE_FROM_HISTORY);
menu.addSeparator();
menu.add(ProcessExecutionResultOverview.this.parent.CLEAR_HISTORY_ACTION);
menu.show(ProcessExecutionResultOverview.this, (int) point.getX(), (int) point.getY());
}
});
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(Colors.WHITE);
g2.fillRoundRect(0, 0, getWidth() - 1, getHeight() - 1, RapidLookAndFeel.CORNER_DEFAULT_RADIUS,
RapidLookAndFeel.CORNER_DEFAULT_RADIUS);
g2.dispose();
}
/**
* Removes and adds the single result blocks according to the current width.
*/
private void redoLayout() {
resultPanel.removeAll();
if (expanded) {
int curWidth = parent.getSize().width;
int relevantWidth = SingleResultOverview.MIN_WIDTH + 25;
int xCount = curWidth / relevantWidth;
int yCount = (int) Math.ceil((double) results.size() / xCount);
resultPanel.setLayout(new GridLayout(yCount, xCount));
for (SingleResultOverview overview : results) {
resultPanel.add(overview);
}
}
revalidate();
}
/**
* Toggle the expansion state.
*
* @param expanded
*/
private void setExpanded(boolean expanded) {
if (expanded != this.expanded) {
this.expanded = expanded;
if (expanded) {
labelExp.setIcon(ICON_ARROW_UP);
} else {
labelExp.setIcon(ICON_ARROW_DOWN);
}
redoLayout();
}
}
}