/*
* org.openmicroscopy.shoola.agents.util.ui.ChannelButton
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 University of Dundee. All rights reserved.
*
*
* This program 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 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.util.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import org.openmicroscopy.shoola.util.CommonsLangUtils;
import org.openmicroscopy.shoola.util.ui.ColouredButton;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
/**
* Customized button used to select the rendered channel.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since 3.0-Beta4
*/
public class ChannelButton
extends ColouredButton
implements ActionListener
{
/** The default size of the component. */
public static final Dimension DEFAULT_MIN_SIZE = new Dimension(30, 30);
/** The default size of the component. */
public static final Dimension DEFAULT_MAX_SIZE = new Dimension(60, 30);
/**
* Bound property name indicating that the channel is or is not selected.
*/
public static final String CHANNEL_SELECTED_PROPERTY = "channelSelected";
/**
* Bound property name indicating that the channel is or is not selected.
*/
public static final String CHANNEL_OVERLAY_PROPERTY = "channelOverlay";
/**
* Bound property name indicating that the channel is mapped to a new color.
*/
public static final String CHANNEL_COLOUR_PROPERTY = "channelColour";
/** The minimum size of the font. */
public static final int MIN_FONT_SIZE = 10;
/** The description associated to this channel. */
private static final String DESCRIPTION = "Toggle channel on/off.";
/** The index of the channel. */
protected final int index;
/** The pop up menu associated to this component. */
private ChannelButtonPopupMenu popupMenu;
/** Flag indicating if right-click are supported. */
private boolean rightClickSupported;
/** Flag indicating that the button is an overlay. */
private boolean overlay;
/** The default font.*/
private Font originalFont;
/** Flat indicating that the text should be trimmed at the end of the String */
private boolean trimEnd = true;
/** Fires an event to select the channel. */
private final void setChannelSelected()
{
if (!isEnabled()) return;
Boolean value = Boolean.valueOf(true);
if (isSelected()) value = Boolean.valueOf(false);
Map<Integer, Boolean> map = new HashMap<Integer, Boolean>(1);
map.put(Integer.valueOf(index), value);
if (overlay)
firePropertyChange(CHANNEL_OVERLAY_PROPERTY, null, map);
else
firePropertyChange(CHANNEL_SELECTED_PROPERTY, null, map);
}
/**
* Selects the channel or displays the pop up menu.
*
* @param e The mouse event to handle.
*/
private void onClick(MouseEvent e)
{
boolean mask = (e.isControlDown() || e.isMetaDown());
if (e.getButton() == MouseEvent.BUTTON1 && !(mask) )
setChannelSelected();
else if ((e.getButton() == MouseEvent.BUTTON2 || mask))
onReleased(e);
}
/**
* Handles the mouse released event because pop-up menus are triggered
* differently depending on the platform.
*
* @param e The The mouse event to handle.
*/
private void onReleased(MouseEvent e)
{
if (e.isPopupTrigger() && rightClickSupported) {
if (popupMenu == null)
popupMenu = new ChannelButtonPopupMenu(this);
popupMenu.show(this, e.getX(), e.getY());
}
}
/**
* Returns the preferred dimension of the component.
*
* @param dimWidth The width of the component.
*/
private Dimension setComponentSize(int dimWidth)
{
Font f = getFont();
String text = getText();
FontMetrics fm = getFontMetrics(f);
int width = fm.stringWidth(text);
Dimension d = DEFAULT_MIN_SIZE;
if (width > DEFAULT_MIN_SIZE.width && width <= dimWidth) {
d = new Dimension(width+10, DEFAULT_MIN_SIZE.height);
} else if (width > dimWidth) {
int size = fm.stringWidth(UIUtilities.DOTS);
width += size;
String s = "";
int n = trimEnd ? 0 : text.length()-1;
List l = new ArrayList();
char ch;
while (fm.stringWidth(s)+size < dimWidth-size) {
ch = text.charAt(n);
s += ch;
l.add(ch);
n = trimEnd ? n+1 : n-1;
}
if(!trimEnd)
Collections.reverse(l);
Iterator i = l.iterator();
s = trimEnd ? "" : UIUtilities.DOTS;
while (i.hasNext())
s += i.next();
if(trimEnd)
s += UIUtilities.DOTS;
super.setText(s);
//reset text
width = fm.stringWidth(s);
d = new Dimension(width+10, DEFAULT_MIN_SIZE.height);
}
return d;
}
/**
* Parses the text.
*
* @param text The text to parse.
* @return See above.
*/
private String parseText(String text)
{
if (CommonsLangUtils.isBlank(text)) return "";
String[] values = text.split("\\(");
if (values == null || values.length == 0) return text;
return values[0].trim();
}
/**
* Sets the text associated to the component.
*
* @param text The value to set.
*/
private void setTextValue(String text)
{
super.setText(parseText(text));
List<String> l = new ArrayList<String>(2);
if (CommonsLangUtils.isNotBlank(text)) l.add(text);
l.add(DESCRIPTION);
setToolTipText(UIUtilities.formatToolTipText(l));
}
/**
* Creates a new instance.
*
* @param text The text of the button. The text should correspond to
* the label of the channel.
* @param color The background color of the button. Corresponds to the
* color associated to the channel.
* @param index The channel index.
* @param selected Pass <code>true</code> to select the channel (i.e.
* the channel is rendered), <code>false</code> otherwise
* (i.e. the channel is not rendered.)
* @param trimEnd Pass <code>true</code> if the end of the text should be trimmed,
* if it is too large to display; <code>false</code> to trim at
* the beginning.
*/
public ChannelButton(String text, Color color, int index, boolean selected, boolean trimEnd)
{
super(text, color);
originalFont = getFont();
setTextValue(text);
//Need to parse the String.
this.index = index;
rightClickSupported = true;
this.trimEnd = trimEnd;
setSelected(selected);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) { onClick(e); }
public void mouseReleased(MouseEvent e) { onReleased(e); }
});
setPreferredSize(setComponentSize(DEFAULT_MAX_SIZE.width));
}
/**
* Creates a new instance.
*
* @param text The text of the button. The text should correspond to
* the label of the channel.
* @param color The background color of the button. Corresponds to the
* color associated to the channel.
* @param index The channel index.
* @param selected Pass <code>true</code> to select the channel (i.e.
* the channel is rendered), <code>false</code> otherwise
* (i.e. the channel is not rendered.)
*/
public ChannelButton(String text, Color color, int index, boolean selected)
{
this(text, color, index, selected, true);
}
/**
* Creates a deselected button.
*
* @param text The text of the button. The text should correspond to
* the label of the channel.
* @param color The background color of the button. Corresponds to the
* color associated to the channel.
* @param index The channel index.
*/
public ChannelButton(String text, Color color, int index)
{
this(text, color, index, false);
}
/**
* Sets the overlay flag.
*
* @param overlay Pass <code>true</code> to indicate that it is a button
* for overlay, <code>false</code> otherwise.
*/
public void setOverlay(boolean overlay) { this.overlay = overlay; }
/** Fires a property change to bring up on screen the color picker. */
void showColorPicker()
{
firePropertyChange(CHANNEL_COLOUR_PROPERTY, null,
Integer.valueOf(index));
}
/**
* Returns the index of the channel.
*
* @return See above.
*/
public int getChannelIndex() { return index; }
/**
* Sets to <code>true</code> if right clicks are supported,
* <code>false</code> otherwise.
*
* @param value The value to set.
*/
public void setRightClickSupported(boolean value)
{
rightClickSupported = value;
}
/**
* Overridden to set the border of the button.
* @see ColouredButton#setSelected(boolean)
*/
public void setSelected(boolean selected)
{
super.setSelected(selected);
if (selected) setBorder(BorderFactory.createLoweredBevelBorder());
else setBorder(BorderFactory.createRaisedBevelBorder());
}
/**
* Overridden so the text can be parsed and the tool tip set.
* @see ColouredButton#setText(String)
*/
public void setText(String text)
{
setTextValue(text);
if (originalFont != null) {
setFont(originalFont);
if (CommonsLangUtils.isNotBlank(text)) {
int width = getFontMetrics(getFont()).stringWidth(text);
if (width > DEFAULT_MAX_SIZE.width)
width = DEFAULT_MAX_SIZE.width;
if (width < getPreferredSize().width)
width = getPreferredSize().width;
Dimension d = setComponentSize(width);
setSize(d);
setPreferredSize(d);
}
}
repaint();
}
/**
* Handles click performed on the channel button.
* @see ActionListener#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent e) { setChannelSelected(); }
}