/** * 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.properties; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import com.rapidminer.gui.tools.AttributeGuiTools; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.tools.AbstractObservable; import com.rapidminer.tools.Observer; import com.rapidminer.tools.Ontology; import com.rapidminer.tools.expression.ExampleResolver; import com.rapidminer.tools.expression.FunctionDescription; import com.rapidminer.tools.expression.FunctionInput; import com.rapidminer.tools.expression.FunctionInput.Category; /** * Panel which displays an input of a {@link FunctionDescription}. * * @author Sabrina Kirstein * @since 6.5.0 * */ public class FunctionInputPanel extends JPanel { /** functions that should be highlighted in the description of date function constants */ public static final String[] HIGHLIGHT_FUNCTIONS_DESCRIPTIONS = { "date_str_loc", "date_str", "date_add", "date_set", "date_get", "eval" }; /** * As the FunctionDescriptionPanel is a Panel, it cannot be an observable. It owns an * {@link Observable}, which informs the observers about click changes. * * @author Sabrina Kirstein */ private class PrivateObservable extends AbstractObservable<FunctionInputPanel> { @Override public void fireUpdate() { fireUpdate(getInstance()); } } /** * Defines different input types of {@link FunctionDescription}s. * * @author Sabrina Kirstein */ private static final long serialVersionUID = -1394721896496797249L; /** label showing the name of the input */ private JLabel lblInputName; /** the function input represented by this panel */ private FunctionInput input; /** the default background (used for highlighting) */ private Color defaultBackground; /** mouse listener to react on hover events (highlight the panel) */ private MouseListener hoverMouseListener; /** mouse listener to send mouse events to the parent */ private MouseListener dispatchMouseListener; /** value of the function input */ private String inputValue; /** observable to update the parent on click events */ private PrivateObservable observable = new PrivateObservable(); /** defines when the width of function input names or description is cropped */ private static final int MAX_WIDTH_OF_TEXT = 350; private static final Color COLOR_HIGHLIGHT = new Color(225, 225, 225); private static final int FIRST_ROW_HEIGHT = 35; private static final int ROW_HEIGHT = 20; private static final String HTML_TAB = " "; private static final ImageIcon ICON_ATTRIBUTE_VALUE = SwingTools.createIcon("16/question.png"); private static final ImageIcon ICON_CUSTOM_MACRO = SwingTools.createIcon("16/keyboard_key_a_edit.png"); private static final ImageIcon ICON_PREDEFINED_MACRO = SwingTools.createIcon("16/keyboard_key_a.png"); private static final String NAME_CUSTOM_MACRO = "Custom Macro"; private static final String NAME_PREDEFINED_MACRO = "Predefined Macro"; /** * Creates a panel for a given function input without a value. * * @param input * the related function input */ public FunctionInputPanel(FunctionInput input) { this(input, null); } /** * Creates a panel for a given function input with a specified value. * * @param input * the related function input * @param inputValue * value of the function input */ public FunctionInputPanel(FunctionInput input, String inputValue) { this.inputValue = inputValue; this.input = input; // initialize the UI initGUI(); addMouseListener(createOrGetHoverMouseListener()); } /** * Gives the input name * * @return input name */ public String getInputName() { return input.getName(); } public Category getCategory() { return input.getCategory(); } /** * Register an observer to react on click events */ public void registerObserver(Observer<FunctionInputPanel> observer) { observable.addObserver(observer, false); } private FunctionInputPanel getInstance() { return this; } /** * initializes the graphical user interface */ private void initGUI() { defaultBackground = getBackground(); int height = FIRST_ROW_HEIGHT; setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.weighty = 1; gbc.insets = new Insets(0, 5, 0, 5); gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; /** label showing the type of the input */ JLabel lblTypeIcon = new JLabel(getTypeIcon()); lblTypeIcon.setToolTipText(getIconToolTip()); lblTypeIcon.addMouseListener(createOrGetDispatchMouseListener()); add(lblTypeIcon, gbc); gbc.gridx += 1; gbc.insets = new Insets(0, 0, 0, 0); String text = input.getName(); lblInputName = new JLabel(); lblInputName.addMouseListener(createOrGetDispatchMouseListener()); lblInputName.setToolTipText(text); String croppedText = "<html>" + SwingTools.getStrippedJComponentText(this, HTML_TAB + HTML_TAB + HTML_TAB + text, MAX_WIDTH_OF_TEXT, 0) + "</html>"; lblInputName.setText(croppedText); lblInputName.setAlignmentX(LEFT_ALIGNMENT); setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); add(lblInputName, gbc); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.gridx += 1; JPanel gapPanel = new JPanel(); gapPanel.addMouseListener(createOrGetDispatchMouseListener()); gapPanel.setOpaque(false); add(gapPanel, gbc); // add a row to show the input value, if it is defined if (inputValue != null) { gbc.gridy += 1; gbc.gridx = 1; height += ROW_HEIGHT; /** label showing the value of the input, if given */ JLabel lblInputValue = new JLabel(); if (input.getCategoryName() == ExampleResolver.KEY_SPECIAL_ATTRIBUTES) { lblInputValue.setToolTipText("<html><b>Role:</b> " + inputValue + "</html>"); } else { lblInputValue.setToolTipText("<html>" + inputValue + "</html>"); } croppedText = SwingTools.getStrippedJComponentText(this, HTML_TAB + HTML_TAB + HTML_TAB + inputValue, MAX_WIDTH_OF_TEXT, 0); for (String highlightFunction : HIGHLIGHT_FUNCTIONS_DESCRIPTIONS) { croppedText = croppedText.replaceAll(highlightFunction, "<i>" + highlightFunction + "</i>"); } lblInputValue.setText("<html>" + croppedText + "</html>"); lblInputValue.setForeground(Color.GRAY); lblInputValue.setAlignmentX(LEFT_ALIGNMENT); lblInputValue.addMouseListener(createOrGetDispatchMouseListener()); add(lblInputValue, gbc); } addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { // add the input name to the expression observable.fireUpdate(); } @Override public void mouseExited(MouseEvent e) { highlightInputName(false); } @Override public void mouseEntered(MouseEvent e) { highlightInputName(true); } }); setMinimumSize(new Dimension(getMinimumSize().width, height)); setPreferredSize(new Dimension(getPreferredSize().width, height)); setMaximumSize(new Dimension(getMaximumSize().width, getPreferredSize().height)); } /** * Creates the {@link MouseListener} which highlights the panel. If it is already created this * will return the current instance. * * @return */ private MouseListener createOrGetHoverMouseListener() { if (hoverMouseListener == null) { hoverMouseListener = new MouseAdapter() { @Override public void mouseExited(MouseEvent e) { highlight(false); } @Override public void mouseEntered(MouseEvent e) { highlight(true); } }; } return hoverMouseListener; } /** * Creates the {@link MouseListener} which delivers {@link MouseEvent}s to the * {@link FunctionInputPanel}. Some GUI elements, like {@link JLabel} with Tooltips, may consume * all events and does not inform the parent component. * * @return */ private MouseListener createOrGetDispatchMouseListener() { if (dispatchMouseListener == null) { dispatchMouseListener = new MouseListener() { @Override public void mouseClicked(MouseEvent e) { FunctionInputPanel.this.dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, FunctionInputPanel.this)); } @Override public void mousePressed(MouseEvent e) { FunctionInputPanel.this.dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, FunctionInputPanel.this)); } @Override public void mouseReleased(MouseEvent e) { FunctionInputPanel.this.dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, FunctionInputPanel.this)); } @Override public void mouseEntered(MouseEvent e) { FunctionInputPanel.this.dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, FunctionInputPanel.this)); } @Override public void mouseExited(MouseEvent e) { FunctionInputPanel.this.dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, FunctionInputPanel.this)); } }; } return dispatchMouseListener; } /** * Highlight the {@link #lblInputName}. * * @param highlight */ private void highlightInputName(boolean highlight) { if (highlight) { lblInputName.setForeground(SwingTools.RAPIDMINER_ORANGE); } else { lblInputName.setForeground(Color.BLACK); } highlight(highlight); } /** * Highlight the {@link FunctionInputPanel}. * * @param highlight */ private void highlight(boolean highlight) { if (highlight) { setBackground(COLOR_HIGHLIGHT); } else { if (defaultBackground != null) { setBackground(defaultBackground); } } } /** * @return tool tip containing the written type of the {@link FunctionInput} */ private String getIconToolTip() { switch (input.getCategory()) { case DYNAMIC: case CONSTANT: return Ontology.ATTRIBUTE_VALUE_TYPE.mapIndexToDisplayName(input.getType()); case SCOPE: if (input.useCustomIcon()) { return NAME_CUSTOM_MACRO; } else { return NAME_PREDEFINED_MACRO; } default: return Ontology.ATTRIBUTE_VALUE_TYPE.mapIndexToDisplayName(Ontology.ATTRIBUTE_VALUE); } } /** * @return icon representing the type of the {@link FunctionInput} */ private ImageIcon getTypeIcon() { switch (input.getCategory()) { case DYNAMIC: // select the icon for the related attribute type if (input.getType() == Ontology.ATTRIBUTE_VALUE) { return ICON_ATTRIBUTE_VALUE; } else { return AttributeGuiTools.getIconForValueType(input.getType(), true); } case CONSTANT: // select the icon for the related attribute type return AttributeGuiTools.getIconForValueType(input.getType(), true); case SCOPE: if (input.useCustomIcon()) { return ICON_CUSTOM_MACRO; } else { return ICON_PREDEFINED_MACRO; } default: return ICON_ATTRIBUTE_VALUE; } } }