/**
* 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.tools;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.text.MessageFormat;
import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import com.rapidminer.gui.ConditionalAction;
import com.rapidminer.gui.RapidMinerGUI;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.SystemInfoUtilities;
import com.rapidminer.tools.SystemInfoUtilities.OperatingSystem;
/**
* This will create an action, whose settings are take from a .properties file being part of the GUI
* Resource bundles of RapidMiner. These might be accessed using the I18N class.
*
* A resource action needs a key specifier, which will be used to build the complete keys of the
* form: gui.action.<specifier>.label = Which will be the caption gui.action.<specifier>.icon = The
* icon of this action. For examples used in menus or buttons gui.action.<specifier>.acc = The
* accelerator key used for menu entries gui.action.<specifier>.tip = Which will be the tool tip
* gui.action.<specifier>.mne = Which will give you access to the mnemonics key. Please make it the
* same case as in the label
*
* @author Simon Fischer, Sebastian Land
*/
public abstract class ResourceAction extends ConditionalAction {
private static final long serialVersionUID = -3699425760142415331L;
private final String key;
private final String iconName;
private final IconType iconType;
/**
* Specifies the style of the icon.
*/
public enum IconType {
/** standard, multi-colored icons for 24x24 and higher, grey for 16x16 */
NORMAL,
/** flat, single-colored icons */
FLAT,
/** monochrome icons for 16x16 */
MONO
};
/**
* Creates a new {@link ResourceAction} with the standard {@link IconType}.
*
* @param i18nKey
* @param i18nArgs
*/
public ResourceAction(String i18nKey, Object... i18nArgs) {
this(false, i18nKey, i18nArgs);
setCondition(EDIT_IN_PROGRESS, DONT_CARE);
}
/**
* Creates a new {@link ResourceAction} with the specified {@link IconType}.
*
* @param i18nKey
* @param iconType
* @param i18nArgs
*/
public ResourceAction(String i18nKey, IconType iconType, Object... i18nArgs) {
this(false, i18nKey, iconType, i18nArgs);
setCondition(EDIT_IN_PROGRESS, DONT_CARE);
}
/**
* Creates a new {@link ResourceAction} with the standard {@link IconType}.
*
* @param smallIcon
* @param i18nKey
* @param i18nArgs
*/
public ResourceAction(boolean smallIcon, String i18nKey, Object... i18nArgs) {
this(smallIcon ? 16 : 24, i18nKey, i18nArgs);
}
/**
* Creates a new {@link ResourceAction} with the standard {@link IconType}.
*
* @param iconSize
* @param i18nKey
* @param i18nArgs
*/
public ResourceAction(int iconSize, String i18nKey, Object... i18nArgs) {
this(iconSize, i18nKey, IconType.NORMAL, i18nArgs);
}
/**
* Creates a new {@link ResourceAction} with the specified {@link IconType}.
*
* @param smallIcon
* @param i18nKey
* @param iconType
* @param i18nArgs
*/
public ResourceAction(boolean smallIcon, String i18nKey, IconType iconType, Object... i18nArgs) {
this(smallIcon ? 16 : 24, i18nKey, iconType, i18nArgs);
}
/**
* Creates a new {@link ResourceAction} with the specified {@link IconType}.
*
* @param iconSize
* @param i18nKey
* @param iconType
* @param i18nArgs
*/
public ResourceAction(int iconSize, String i18nKey, IconType iconType, Object... i18nArgs) {
super(i18nArgs == null || i18nArgs.length == 0 ? getMessage(i18nKey + ".label")
: MessageFormat.format(getMessage(i18nKey + ".label"), i18nArgs));
putValue(ACTION_COMMAND_KEY, i18nKey);
this.key = i18nKey;
this.iconType = iconType;
String mne = getMessageOrNull(i18nKey + ".mne");
if (mne != null && mne.length() > 0) {
String name = (String) getValue(NAME);
if (name != null && name.length() > 0 && name.indexOf(mne.charAt(0)) == -1
&& name.indexOf(Character.toLowerCase(mne.charAt(0))) == -1) {
LogService.getRoot().log(Level.FINE, "com.rapidminer.gui.tools.ResourceAction.key_not_found",
new Object[] { mne, i18nKey, name });
}
mne = mne.toUpperCase();
putValue(MNEMONIC_KEY, (int) mne.charAt(0));
}
String acc = getMessageOrNull(i18nKey + ".acc");
KeyStroke accStroke = null;
if (acc != null) {
if (SystemInfoUtilities.getOperatingSystem() == OperatingSystem.OSX) {
acc = acc.replace("ctrl", "meta");
acc = acc.replace("control", "meta");
}
accStroke = KeyStroke.getKeyStroke(acc);
putValue(ACCELERATOR_KEY, accStroke);
}
String tip = getMessageOrNull(i18nKey + ".tip");
if (tip != null) {
if (accStroke != null) {
StringBuilder tipBuilder = new StringBuilder();
tipBuilder.append(tip);
tipBuilder.append(" (");
tipBuilder.append(SwingTools.formatKeyStroke(accStroke));
tipBuilder.append(")");
tip = tipBuilder.toString();
}
putValue(SHORT_DESCRIPTION,
i18nArgs == null || i18nArgs.length == 0 ? tip : MessageFormat.format(tip, i18nArgs));
}
this.iconName = getMessageOrNull(i18nKey + ".icon");
if (getIconName() != null && !getIconName().trim().isEmpty()) {
ImageIcon small = null;
ImageIcon large = null;
if (iconType == IconType.FLAT) {
small = SwingTools.createIcon("flat_icons/16/" + getIconName());
large = SwingTools.createIcon("flat_icons/" + iconSize + "/" + getIconName());
}
if (small == null) {
small = SwingTools.createIcon("16/" + getIconName(), iconType == IconType.MONO);
}
if (large == null) {
large = SwingTools.createIcon(iconSize + "/" + getIconName(), iconType == IconType.MONO);
}
putValue(LARGE_ICON_KEY, iconSize == 16 ? small != null ? small : large : large);
putValue(SMALL_ICON, small != null ? small : large);
}
putValue("rm_id", i18nKey);
}
/**
* Adds the action to the input and action map of the components.
*
* @param condition
* one out of {@link JComponent#WHEN_FOCUSED}, ...
* @param disableOnFocusLost
* if <code>true</code>, will disable the action on FocusLost event and enable it
* again on FocusGained (if conditions of superclass are met).
* @param components
* the {@link JComponent}s to register this action to
*/
public void addToActionMap(int condition, boolean disableOnFocusLost, boolean initiallyDisabled, String actionKey,
JComponent... components) {
for (JComponent comp : components) {
if (comp == null) {
throw new IllegalArgumentException("components must not be null!");
}
KeyStroke keyStroke = (KeyStroke) getValue(ACCELERATOR_KEY);
if (keyStroke != null) {
actionKey = actionKey == null ? key : actionKey;
comp.getInputMap(condition).put(keyStroke, actionKey);
comp.getActionMap().put(actionKey, this);
} else {
LogService.getRoot().log(Level.FINE, "com.rapidminer.gui.tools.ResourceAction.add_action_key_error", key);
}
if (disableOnFocusLost) {
comp.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
if (!e.isTemporary()) {
// focus lost here means disable it no matter the conditions
ResourceAction.this.setEnabled(false);
ResourceAction.super.setDisabledDueToFocusLost(true);
}
}
@Override
public void focusGained(FocusEvent e) {
if (!e.isTemporary()) {
// focus gained here means enable it if conditions are fulfilled
ResourceAction.super.setDisabledDueToFocusLost(false);
RapidMinerGUI.getMainFrame().getActions().enableActions();
}
}
});
if (initiallyDisabled) {
if (SwingUtilities.isEventDispatchThread()) {
super.setDisabledDueToFocusLost(true);
setEnabled(false);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ResourceAction.super.setDisabledDueToFocusLost(true);
ResourceAction.this.setEnabled(false);
}
});
}
}
}
}
}
/**
* Adds the action to the input and action map of the component.
*
* @param condition
* one out of WHEN_IN_FOCUES, ...
*/
public void addToActionMap(JComponent component, int condition) {
addToActionMap(component, null, condition);
}
/**
* Adds the action to the input and action map of the component.
*
* @param condition
* one out of WHEN_IN_FOCUES, ...
*/
public void addToActionMap(JComponent component, String actionKey, int condition) {
addToActionMap(condition, false, false, actionKey, component);
}
/**
* This returns the i18n key of this action.
*/
public String getKey() {
return key;
}
public String getIconName() {
return iconName;
}
public IconType getIconType() {
return iconType;
}
private static String getMessage(String key) {
return I18N.getMessage(I18N.getGUIBundle(), "gui.action." + key);
}
private static String getMessageOrNull(String key) {
return I18N.getMessageOrNull(I18N.getGUIBundle(), "gui.action." + key);
}
}