/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Icy 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.action;
import icy.gui.frame.progress.ProgressFrame;
import icy.main.Icy;
import icy.resource.icon.IcyIcon;
import icy.system.thread.ThreadUtil;
import icy.util.StringUtil;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.KeyStroke;
import org.pushingpixels.flamingo.api.common.RichTooltip;
/**
* Icy basic AbstractAction class.
*
* @author Stephane
*/
public abstract class IcyAbstractAction extends AbstractAction
{
/**
* Sets the tooltip text of a component from an Action.
*
* @param c
* the Component to set the tooltip text on
* @param a
* the Action to set the tooltip text from, may be null
*/
public static void setToolTipTextFromAction(JComponent c, Action a)
{
if (a != null)
{
final String longDesc = (String) a.getValue(Action.LONG_DESCRIPTION);
final String shortDesc = (String) a.getValue(Action.SHORT_DESCRIPTION);
if (StringUtil.isEmpty(longDesc))
c.setToolTipText(shortDesc);
else
c.setToolTipText(longDesc);
}
}
/**
*
*/
private static final long serialVersionUID = -8544059445777661407L;
private static final int DEFAULT_ICON_SIZE = 20;
/**
* The "enabled" property key.
*/
public static final String ENABLED_KEY = "enabled";
/**
* internals
*/
protected boolean bgProcess;
protected boolean processing;
protected String processMessage;
protected ProgressFrame progressFrame;
public IcyAbstractAction(String name, IcyIcon icon, String description, String longDescription, int keyCode,
int modifiers, boolean bgProcess, String processMessage)
{
super(name, icon);
// by default we use the name as Action Command
putValue(ACTION_COMMAND_KEY, name);
if (keyCode != 0)
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(keyCode, modifiers));
if (!StringUtil.isEmpty(description))
putValue(SHORT_DESCRIPTION, description);
if (!StringUtil.isEmpty(longDescription))
putValue(LONG_DESCRIPTION, longDescription);
this.bgProcess = bgProcess;
this.processMessage = processMessage;
progressFrame = null;
processing = false;
}
public IcyAbstractAction(String name, IcyIcon icon, String description, String longDescription, int keyCode,
int modifiers)
{
this(name, icon, description, longDescription, keyCode, modifiers, false, null);
}
public IcyAbstractAction(String name, IcyIcon icon, String description, String longDescription, boolean bgProcess,
String processMessage)
{
this(name, icon, description, longDescription, 0, 0, bgProcess, processMessage);
}
public IcyAbstractAction(String name, IcyIcon icon, String description, boolean bgProcess, String processMessage)
{
this(name, icon, description, null, 0, 0, bgProcess, processMessage);
}
public IcyAbstractAction(String name, IcyIcon icon, String description, int keyCode, int modifiers)
{
this(name, icon, description, null, keyCode, modifiers, false, null);
}
public IcyAbstractAction(String name, IcyIcon icon, String description, int keyCode)
{
this(name, icon, description, null, keyCode, 0, false, null);
}
public IcyAbstractAction(String name, IcyIcon icon, String description, String longDescription)
{
this(name, icon, description, longDescription, 0, 0, false, null);
}
public IcyAbstractAction(String name, IcyIcon icon, String description)
{
this(name, icon, description, null, 0, 0, false, null);
}
public IcyAbstractAction(String name, IcyIcon icon)
{
this(name, icon, null, null, 0, 0, false, null);
}
public IcyAbstractAction(String name)
{
this(name, null, null, null, 0, 0, false, null);
}
/**
* @deprecated Use {@link #IcyAbstractAction(String, IcyIcon, String, int, int)} instead.
*/
@Deprecated
public IcyAbstractAction(String name, String iconName, String description, int keyCode, int modifiers)
{
this(name, new IcyIcon(iconName, DEFAULT_ICON_SIZE), description, null, keyCode, modifiers, false, null);
}
/**
* @deprecated Use {@link #IcyAbstractAction(String, IcyIcon, String, int)} instead.
*/
@Deprecated
public IcyAbstractAction(String name, String iconName, String description, int keyCode)
{
this(name, new IcyIcon(iconName, DEFAULT_ICON_SIZE), description, null, keyCode, 0, false, null);
}
/**
* @deprecated Use {@link #IcyAbstractAction(String, IcyIcon, String)} instead.
*/
@Deprecated
public IcyAbstractAction(String name, String iconName, String description)
{
this(name, new IcyIcon(iconName, DEFAULT_ICON_SIZE), description, null, 0, 0, false, null);
}
/**
* @deprecated Use {@link #IcyAbstractAction(String, IcyIcon)} instead.
*/
@Deprecated
public IcyAbstractAction(String name, String iconName)
{
this(name, new IcyIcon(iconName, DEFAULT_ICON_SIZE), null, null, 0, 0, false, null);
}
/**
* @return true if this action process is done in a background thread.
*/
public boolean isBgProcess()
{
return bgProcess;
}
/**
* Set to true if you want to action to be processed in a background thread.
*
* @see #isBgProcess()
* @see #setProcessMessage(String)
*/
public void setBgProcess(boolean bgProcess)
{
this.bgProcess = bgProcess;
}
/**
* @return the process message to display for background action process.
* @see #setProcessMessage(String)
* @see #isBgProcess()
*/
public String getProcessMessage()
{
return processMessage;
}
public RichTooltip getRichToolTip()
{
final String desc = getDescription();
final String longDesc = getLongDescription();
final IcyIcon icon = getIcon();
if (StringUtil.isEmpty(desc) && StringUtil.isEmpty(longDesc))
return null;
final RichTooltip result = new RichTooltip();
if (!StringUtil.isEmpty(desc))
result.setTitle(desc);
if (!StringUtil.isEmpty(longDesc))
{
for (String ld : longDesc.split("\n"))
result.addDescriptionSection(ld);
}
if (icon != null)
result.setMainImage(icon.getImage());
return result;
}
/**
* Set the process message to display for background action process.<br>
* If set to null then no message is displayed (default).
*
* @see #setBgProcess(boolean)
*/
public void setProcessMessage(String processMessage)
{
this.processMessage = processMessage;
}
public void setName(String value)
{
putValue(Action.NAME, value);
}
public String getName()
{
return (String) getValue(Action.NAME);
}
public void setIcon(IcyIcon value)
{
putValue(Action.SMALL_ICON, value);
}
public IcyIcon getIcon()
{
return (IcyIcon) getValue(Action.SMALL_ICON);
}
public void setDescription(String value)
{
putValue(Action.SHORT_DESCRIPTION, value);
}
public String getDescription()
{
return (String) getValue(Action.SHORT_DESCRIPTION);
}
public void setLongDescription(String value)
{
putValue(Action.LONG_DESCRIPTION, value);
}
public String getLongDescription()
{
return (String) getValue(Action.LONG_DESCRIPTION);
}
public void setAccelerator(int keyCode, int modifiers)
{
if (keyCode != 0)
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(keyCode, modifiers));
else
putValue(ACCELERATOR_KEY, null);
}
public void setAccelerator(int keyCode)
{
setAccelerator(keyCode, 0);
}
/**
* Returns the selected state (for toggle button type).
*/
public boolean isSelected()
{
return Boolean.TRUE.equals(getValue(SELECTED_KEY));
}
/**
* Sets the selected state (for toggle button type).
*/
public void setSelected(boolean value)
{
putValue(SELECTED_KEY, Boolean.valueOf(value));
}
/**
* Returns the {@link KeyStroke} for this action (can be null).
*/
public KeyStroke getKeyStroke()
{
return (KeyStroke) getValue(ACCELERATOR_KEY);
}
/**
* @return true if action is currently processing.<br>
* Meaningful only when {@link #setBgProcess(boolean)} is set to true)
*/
public boolean isProcessing()
{
return processing;
}
@Override
public boolean isEnabled()
{
return enabled && !processing;
}
@Override
public void setEnabled(boolean value)
{
if (enabled != value)
{
final boolean wasEnabled = isEnabled();
enabled = value;
final boolean isEnabled = isEnabled();
// notify enabled change
if (wasEnabled != isEnabled)
firePropertyChange(ENABLED_KEY, Boolean.valueOf(wasEnabled), Boolean.valueOf(isEnabled));
}
}
protected void setProcessing(boolean value)
{
if (processing != value)
{
final boolean wasEnabled = isEnabled();
processing = value;
final boolean isEnabled = isEnabled();
// notify enabled change
if (wasEnabled != isEnabled)
firePropertyChange(ENABLED_KEY, Boolean.valueOf(wasEnabled), Boolean.valueOf(isEnabled));
}
}
/**
* Helper method to fire enabled changed event (this force component refresh)
*/
public void enabledChanged()
{
final boolean enabledState = isEnabled();
// notify enabled change
firePropertyChange(ENABLED_KEY, Boolean.valueOf(!enabledState), Boolean.valueOf(enabledState));
}
/**
* Returns a {@link JLabel} component representing the action.
*/
public JLabel getLabelComponent(boolean wantIcon, boolean wantText)
{
final JLabel result = new JLabel();
if (wantIcon)
result.setIcon(getIcon());
if (wantText)
result.setText(getName());
final String desc = getDescription();
if (StringUtil.isEmpty(desc))
result.setToolTipText(getLongDescription());
else
result.setToolTipText(getDescription());
return result;
}
/**
* Returns a {@link JLabel} component representing the action.
*/
public JLabel getLabelComponent()
{
return getLabelComponent(true, true);
}
@Override
public void actionPerformed(ActionEvent e)
{
setProcessing(true);
// background execution ?
if (isBgProcess())
{
final ActionEvent event = e;
ThreadUtil.bgRun(new Runnable()
{
@Override
public void run()
{
final String mess = getProcessMessage();
if (!StringUtil.isEmpty(mess) && !Icy.getMainInterface().isHeadLess())
progressFrame = new ProgressFrame(mess);
else
progressFrame = null;
try
{
doAction(event);
}
finally
{
if (progressFrame != null)
progressFrame.close();
// need to be done on the EDT (can change the enabled state)
ThreadUtil.invokeLater(new Runnable()
{
@Override
public void run()
{
setProcessing(false);
}
});
}
}
});
}
else
{
try
{
doAction(e);
}
finally
{
setProcessing(false);
}
}
}
/**
* Execute action (delayed execution if action requires it)
*/
public void execute()
{
actionPerformed(new ActionEvent(this, 0, ""));
}
/**
* @deprecated Use {@link #executeNow()} instead
*/
@Deprecated
public boolean doAction()
{
return doAction(new ActionEvent(this, 0, ""));
}
/**
* Execute action now (wait for execution to complete)
*/
public boolean executeNow()
{
return doAction(new ActionEvent(this, 0, ""));
}
/**
* Action implementation
*/
protected abstract boolean doAction(ActionEvent e);
}