/**
* 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.flow.processrendering.view.components;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import com.rapidminer.gui.flow.processrendering.model.ProcessRendererModel;
import com.rapidminer.gui.flow.processrendering.view.ProcessEventDecorator;
import com.rapidminer.gui.tools.ProcessGUITools;
import com.rapidminer.gui.tools.bubble.BubbleWindow;
import com.rapidminer.operator.ExecutionUnit;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.ProcessSetupError;
import com.rapidminer.operator.ports.Port;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.tools.ProcessTools;
import com.rapidminer.tools.container.Pair;
/**
* Decorator that checks if the warning icon of an operator was clicked and displays operator
* warnings in that case. Handles display of warning bubbles and events that show or hide them.
*
* @author Gisa Schaefer
* @since 7.1.1
*/
public class OperatorWarningHandler implements ProcessEventDecorator {
/** the bubble which is shown when the user clicks on the warning icon on an operator */
private BubbleWindow operatorWarningBubble;
/** the backing model */
private final ProcessRendererModel model;
/** the size of a warning icon */
private static final int WARNING_ICON_SIZE = 16;
/**
* the missing input port that caused the last warning bubble or {@code null} if it had another
* cause
*/
private Port lastMissingInputPort;
/**
* the missing parameter that caused the last warning bubble or {@code null} if it had another
* cause
*/
private Pair<Operator, ParameterType> lastMissingParamPair;
/**
* the setup error that caused the last warning bubble or {@code null} if it had another cause
*/
private ProcessSetupError lastProcessSetupError;
/**
* Creates decorator that checks if the warning icon of an operator was clicked and displays
* operator warnings in that case.
*/
public OperatorWarningHandler(ProcessRendererModel model) {
this.model = model;
}
@Override
public void processMouseEvent(ExecutionUnit process, MouseEventType type, MouseEvent e) {
if (type == MouseEventType.MOUSE_CLICKED) {
// get the operator that was clicked on
Operator operator = model.getHoveringOperator();
// check if there is a warning icon for this operator
if (operator != null && !operator.getErrorList().isEmpty()) {
// calculate the bounding box of the warning icon as it is drawn by {@link
// ProcessDrawer#renderOperator()}
Rectangle2D frame = model.getOperatorRect(operator);
int iconX = (int) (frame.getX() + 3 + WARNING_ICON_SIZE);
int iconY = (int) (frame.getY() + frame.getHeight() - WARNING_ICON_SIZE - 2);
int size = (int) Math.ceil(WARNING_ICON_SIZE);
Rectangle2D boundingBox = new Rectangle2D.Float(iconX, iconY, size, size);
// check if the user clicked into the bounding box of the warning icon
if (model.getMousePositionRelativeToProcess() != null
&& boundingBox.contains(model.getMousePositionRelativeToProcess())) {
showOperatorWarning(operator);
}
}
}
}
@Override
public void processKeyEvent(ExecutionUnit process, KeyEventType type, KeyEvent e) {
// not needed
}
/**
* Kills the operator warning bubble.
*/
public void killWarningBubble() {
if (operatorWarningBubble != null) {
operatorWarningBubble.killBubble(true);
}
}
/**
* Shows the first setup warning for the operator via an error bubble. Checks missing mandatory
* parameters and missing port connections for the operator and its sub-operators first, then
* displays the first {@link ProcessSetupError} from the operator error list. Hides the error
* bubble instead of showing the same bubble again.
*
* @param operator
* the operator for which to show the warnings
*/
public void showOperatorWarning(Operator operator) {
Pair<Operator, ParameterType> missingParamPair = ProcessTools.getOperatorWithoutMandatoryParameter(operator);
if (missingParamPair != null) {
if (operatorWarningBubble != null) {
// if the required bubble is already shown kill it for toggle effect
if (!operatorWarningBubble.isKilled() && missingParamPair.equals(lastMissingParamPair)) {
operatorWarningBubble.killBubble(true);
return;
}
// kill wrong bubble and go on showing new bubble
operatorWarningBubble.killBubble(true);
}
operatorWarningBubble = ProcessGUITools.displayPrecheckMissingMandatoryParameterWarning(
missingParamPair.getFirst(), missingParamPair.getSecond(), false);
// set last values
lastMissingParamPair = missingParamPair;
lastMissingInputPort = null;
lastProcessSetupError = null;
return;
}
lastMissingParamPair = null;
Port missingInputPort = ProcessTools.getMissingPortConnection(operator);
if (missingInputPort != null) {
if (operatorWarningBubble != null) {
// if the required bubble is already shown kill it for toggle effect
if (!operatorWarningBubble.isKilled() && missingInputPort.equals(lastMissingInputPort)) {
operatorWarningBubble.killBubble(true);
return;
}
// kill wrong bubble and go on showing new bubble
operatorWarningBubble.killBubble(true);
}
operatorWarningBubble = ProcessGUITools.displayPrecheckInputPortDisconnectedWarning(missingInputPort, false);
// set last values
lastMissingInputPort = missingInputPort;
lastProcessSetupError = null;
return;
}
lastMissingInputPort = null;
if (!operator.getErrorList().isEmpty()) {
ProcessSetupError processSetupError = operator.getErrorList().get(0);
if (processSetupError != null) {
if (operatorWarningBubble != null) {
// if the required bubble is already shown kill it for toggle effect
if (!operatorWarningBubble.isKilled() && processSetupError.equals(lastProcessSetupError)) {
operatorWarningBubble.killBubble(true);
return;
}
// kill wrong bubble and go on showing new bubble
operatorWarningBubble.killBubble(true);
}
operatorWarningBubble = ProcessGUITools.displayProcessSetupError(processSetupError);
lastProcessSetupError = processSetupError;
return;
}
}
lastProcessSetupError = null;
}
}