/*
* 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.gui.util;
import icy.common.listener.weak.WeakListener;
import icy.image.ImageUtil;
import icy.preferences.GeneralPreferences;
import icy.system.IcyExceptionHandler;
import icy.system.SystemUtil;
import icy.system.thread.ThreadUtil;
import icy.util.ClassUtil;
import icy.util.ReflectionUtil;
import icy.util.StringUtil;
import ij.util.Java2;
import java.awt.Color;
import java.awt.Component;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Map;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.PopupFactory;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.InternalFrameUI;
import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup;
import org.pushingpixels.flamingo.api.common.JCommandToggleMenuButton;
import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu;
import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.DecorationAreaType;
import org.pushingpixels.substance.api.SubstanceColorScheme;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.substance.api.SubstanceSkin;
import org.pushingpixels.substance.api.colorscheme.LightAquaColorScheme;
import org.pushingpixels.substance.api.fonts.FontPolicy;
import org.pushingpixels.substance.api.fonts.FontSet;
import org.pushingpixels.substance.api.fonts.SubstanceFontUtilities;
import org.pushingpixels.substance.api.skin.SkinChangeListener;
import org.pushingpixels.substance.api.skin.SkinInfo;
import org.pushingpixels.substance.internal.ui.SubstanceInternalFrameUI;
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceInternalFrameTitlePane;
import org.pushingpixels.substance.internal.utils.SubstanceTitlePane;
/**
* @author Stephane
*/
public class LookAndFeelUtil
{
/**
* Weak listener wrapper for SkinChangeListener.
*
* @author Stephane
*/
public static class WeakSkinChangeListener extends WeakListener<SkinChangeListener> implements SkinChangeListener
{
public WeakSkinChangeListener(SkinChangeListener listener)
{
super(listener);
}
@Override
public void removeListener(Object source)
{
removeSkinChangeListener(this);
}
@Override
public void skinChanged()
{
final SkinChangeListener listener = getListener();
if (listener != null)
listener.skinChanged();
}
}
static int defaultFontSize;
static Map<String, SkinInfo> map;
private static ArrayList<SkinInfo> skins;
static int currentFontSize;
public static void init()
{
try
{
// so ImageJ won't change look and feel later
ReflectionUtil.getField(Java2.class, "lookAndFeelSet", true).set(null, Boolean.valueOf(true));
}
catch (Exception e)
{
// do it in another way (slower)
Java2.setSystemLookAndFeel();
}
// enable or not EDT checking in substance
SystemUtil.setProperty("insubstantial.checkEDT", "false");
SystemUtil.setProperty("insubstantial.logEDT", "true");
// funny features of substance
// AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.FOCUS_LOOP_ANIMATION);
// AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.GHOSTING_BUTTON_PRESS);
// AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.GHOSTING_ICON_ROLLOVER);
// AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.ICON_GLOW);
// UIManager.put(SubstanceLookAndFeel.USE_THEMED_DEFAULT_ICONS, Boolean.TRUE);
// UIManager.put(SubstanceLookAndFeel.SHOW_EXTRA_WIDGETS, Boolean.TRUE);
// UIManager.put(SubstanceLookAndFeel.WATERMARK_VISIBLE, Boolean.TRUE);
// SubstanceLookAndFeel.setWidgetVisible(null, true, SubstanceWidgetType.MENU_SEARCH);
// SubstanceLookAndFeel.setWidgetVisible(null, false,
// SubstanceWidgetType.TITLE_PANE_HEAP_STATUS);
// enabled LAF decoration instead of native ones
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
map = SubstanceLookAndFeel.getAllSkins();
skins = new ArrayList<SkinInfo>(map.values());
final LookAndFeelInfo[] lafInfos = new LookAndFeelInfo[skins.size()];
// install Substance look and feel
for (int i = 0; i < skins.size(); i++)
{
final SkinInfo skin = skins.get(i);
final String className = skin.getClassName();
final String simpleName = ClassUtil.getSimpleClassName(className);
final int len = simpleName.length();
// remove the "skin" suffix from simple name
final String lafClassName = ClassUtil.getPackageName(className) + ".Substance"
+ simpleName.substring(0, len - 4) + "LookAndFeel";
lafInfos[i] = new LookAndFeelInfo(skin.getDisplayName(), lafClassName);
}
// replace system LAF by substance LAF
UIManager.setInstalledLookAndFeels(lafInfos);
// set default skin
setSkin(GeneralPreferences.getGuiSkin());
// get default font size
final FontPolicy fontPolicy = SubstanceFontUtilities.getDefaultFontPolicy();
final FontSet fontSet = fontPolicy.getFontSet("Substance", null);
defaultFontSize = fontSet.getMessageFont().getSize();
currentFontSize = defaultFontSize;
// set default font size
setFontSize(GeneralPreferences.getGuiFontSize());
// Define custom PopupFactory :
// That fix Ribbon repaint bugs on Medium Weight components for OSX
PopupFactory.setSharedInstance(new CustomPopupFactory());
}
/**
* Add skin change listener (automatically add weak listener)
*/
public static void addSkinChangeListener(SkinChangeListener listener)
{
SubstanceLookAndFeel.registerSkinChangeListener(listener);
}
/**
* Remove skin change listener
*/
public static void removeSkinChangeListener(SkinChangeListener listener)
{
SubstanceLookAndFeel.unregisterSkinChangeListener(listener);
}
/**
* get current Substance skin
*/
public static SubstanceSkin getCurrentSkin()
{
return SubstanceLookAndFeel.getCurrentSkin();
}
/**
* get current Substance skin display name
*/
public static String getCurrentSkinName()
{
final SubstanceSkin skin = getCurrentSkin();
if (skin != null)
return skin.getDisplayName();
return null;
}
// /**
// * get current Look And Feel
// */
// public static LookAndFeel getLookAndFeel()
// {
// return UIManager.getLookAndFeel();
// }
//
// /**
// * get current Look And Feel name
// */
// public static String getLookAndFeelName()
// {
// final LookAndFeel laf = getLookAndFeel();
//
// if (laf instanceof SubstanceLookAndFeel)
// return SubstanceLookAndFeel.getCurrentSkin().getDisplayName();
//
// return UIManager.getLookAndFeel().getName();
// }
public static JCommandPopupMenu getLookAndFeelMenu()
{
final JCommandPopupMenu result = new JCommandPopupMenu();
final CommandToggleButtonGroup buttonGroup = new CommandToggleButtonGroup();
final String currentSkinName = getCurrentSkinName();
for (SkinInfo skin : skins)
{
final String skinName = skin.getDisplayName();
final JCommandToggleMenuButton button = new JCommandToggleMenuButton(skinName, null);
button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
// set LAF
LookAndFeelUtil.setSkin(skinName);
// and save to preferences
GeneralPreferences.setGuiSkin(skinName);
}
});
result.addMenuButton(button);
// TODO: why this is needed ? ribbon bug ?
button.getUI().installUI(button);
buttonGroup.add(button);
buttonGroup.setSelected(button, button.getText().equals(currentSkinName));
}
return result;
}
/**
* get default Look And Feel font size
*/
public static int getDefaultFontSize()
{
return defaultFontSize;
}
/**
* get default Substance skin
*/
public static String getDefaultSkin()
{
// should be Cerulean
return skins.get(4).getDisplayName();
}
// /**
// * get default Look And Feel
// */
// public static String getDefaultLookAndFeel()
// {
// return "org.pushingpixels.substance.api.skin.SubstanceCremeCoffeeLookAndFeel";
// // return UIManager.getSystemLookAndFeelClassName();
// }
/**
* Get current LookAndFeel font size
*/
public static int getFontSize()
{
return currentFontSize;
}
/**
* Set LookAndFeel font size
*/
public static void setFontSize(final int size)
{
if (size != currentFontSize)
{
ThreadUtil.invokeLater(new Runnable()
{
@Override
public void run()
{
final float scaleFactor = (float) size / (float) defaultFontSize;
try
{
// will fail here if look and feel is not a substance one
SubstanceLookAndFeel.setFontPolicy(SubstanceFontUtilities.getScaledFontPolicy(scaleFactor));
currentFontSize = size;
}
catch (Exception e)
{
System.err.println("LookAndFeelUtil.setFontSize(...) error :");
IcyExceptionHandler.showErrorMessage(e, false);
}
}
});
}
}
/**
* Set the specified LookAndFeel skin (skin display name)
*/
public static void setSkin(final String skinName)
{
if (!StringUtil.equals(skinName, getCurrentSkinName()))
{
ThreadUtil.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
SubstanceLookAndFeel.setSkin(map.get(skinName).getClassName());
}
catch (Exception e)
{
System.err.println("LookAndFeelUtil.setSkin(...) error :");
IcyExceptionHandler.showErrorMessage(e, false);
}
}
});
}
}
/**
* Return a foreground component color image depending original alpha intensity image
*/
public static Image getForegroundImageFromAlphaImage(Component c, Image alphaImage)
{
return paintForegroundImageFromAlphaImage(c, alphaImage, null);
}
/**
* Return a background component color image depending original alpha intensity image
*/
public static Image getBackgroundImageFromAlphaImage(Component c, Image alphaImage)
{
return paintBackgroundImageFromAlphaImage(c, alphaImage, null);
}
/**
* Paint foreground component color in 'out' image<br>
* depending original alpha intensity from 'alphaImage'
*/
public static Image paintForegroundImageFromAlphaImage(Component c, Image alphaImage, Image out)
{
return ImageUtil.paintColorImageFromAlphaImage(alphaImage, out, getForeground(c));
}
/**
* Paint background component color in 'out' image<br>
* depending original alpha intensity from 'alphaImage'
*/
public static Image paintBackgroundImageFromAlphaImage(Component c, Image alphaImage, Image out)
{
return ImageUtil.paintColorImageFromAlphaImage(alphaImage, out, getBackground(c));
}
public static SubstanceColorScheme getActiveColorScheme(DecorationAreaType d)
{
return getCurrentSkin().getActiveColorScheme(d);
}
/**
* @deprecated Use {@link #getActiveColorScheme(DecorationAreaType)} instead.
*/
@Deprecated
public static SubstanceColorScheme getActiveColorSheme(DecorationAreaType d)
{
return getActiveColorScheme(d);
}
public static SubstanceColorScheme getBackgroundColorScheme(DecorationAreaType d)
{
final SubstanceSkin skin = getCurrentSkin();
if (skin != null) return skin.getBackgroundColorScheme(d);
// Arrive only when using designer --> use a random color theme, we don't care
return new LightAquaColorScheme();
}
public static SubstanceColorScheme getDisabledColorScheme(DecorationAreaType d)
{
final SubstanceSkin skin = getCurrentSkin();
if (skin != null) return skin.getDisabledColorScheme(d);
// Arrive only when using designer --> use a random color theme, we don't care
return new LightAquaColorScheme();
}
public static SubstanceColorScheme getEnabledColorScheme(DecorationAreaType d)
{
final SubstanceSkin skin = getCurrentSkin();
if (skin != null) return skin.getEnabledColorScheme(d);
// Arrive only when using designer --> use a random color theme, we don't care
return new LightAquaColorScheme();
}
public static SubstanceSkin getSkin()
{
return SubstanceLookAndFeel.getCurrentSkin();
}
public static SubstanceSkin getSkin(Component c)
{
return SubstanceLookAndFeel.getCurrentSkin(c);
}
public static DecorationAreaType getDecoration(Component c)
{
return SubstanceLookAndFeel.getDecorationType(c);
}
public static SubstanceColorScheme getColorScheme(Component c, ComponentState state)
{
return getSkin().getColorScheme(c, state);
}
public static SubstanceColorScheme getColorScheme(Component c, ColorSchemeAssociationKind kind, ComponentState state)
{
return getSkin().getColorScheme(c, kind, state);
}
public static SubstanceColorScheme getActiveColorScheme(Component c)
{
final SubstanceSkin skin = getSkin(c);
final DecorationAreaType decoration = getDecoration(c);
if ((skin != null) && (decoration != null))
return skin.getActiveColorScheme(decoration);
return null;
}
public static SubstanceColorScheme getActiveColorScheme(Component c, ComponentState state)
{
return SubstanceColorSchemeUtilities.getActiveColorScheme(c, state);
}
/**
* @deprecated Use {@link #getActiveColorScheme(Component)} instead.
*/
@Deprecated
public static SubstanceColorScheme getActiveColorSheme(Component c)
{
return getActiveColorScheme(c);
}
/**
* @deprecated Use {@link #getActiveColorScheme(Component, ComponentState)} instead.
*/
@Deprecated
public static SubstanceColorScheme getActiveColorSheme(Component c, ComponentState state)
{
return getActiveColorScheme(c, state);
}
public static SubstanceColorScheme getBackgroundColorScheme(Component c)
{
final SubstanceSkin skin = getSkin(c);
final DecorationAreaType decoration = getDecoration(c);
if ((skin != null) && (decoration != null))
return skin.getBackgroundColorScheme(decoration);
return null;
}
public static SubstanceColorScheme getDisabledColorScheme(Component c)
{
final SubstanceSkin skin = getSkin(c);
final DecorationAreaType decoration = getDecoration(c);
if ((skin != null) && (decoration != null))
return skin.getDisabledColorScheme(decoration);
return null;
}
public static SubstanceColorScheme getEnabledColorScheme(Component c)
{
final SubstanceSkin skin = getSkin(c);
final DecorationAreaType decoration = getDecoration(c);
if ((skin != null) && (decoration != null))
return skin.getEnabledColorScheme(decoration);
return null;
}
/**
* Return the foreground color for the specified component
*/
public static Color getForeground(Component c)
{
SubstanceColorScheme colorScheme;
if (c == null)
colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL);
else if (c.isEnabled())
colorScheme = getEnabledColorScheme(c);
else
colorScheme = getDisabledColorScheme(c);
if (colorScheme == null)
getEnabledColorScheme(DecorationAreaType.GENERAL);
if (colorScheme != null)
return new ColorUIResource(colorScheme.getForegroundColor());
if (c != null)
return c.getForeground();
return Color.white;
}
/**
* Return the selected foreground color for the specified component
*/
public static Color getSelectedForeground(Component c)
{
SubstanceColorScheme colorScheme;
if (c == null)
colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL);
else if (c.isEnabled())
colorScheme = getEnabledColorScheme(c);
else
colorScheme = getDisabledColorScheme(c);
if (colorScheme == null)
getEnabledColorScheme(DecorationAreaType.GENERAL);
if (colorScheme != null)
return new ColorUIResource(colorScheme.getSelectionForegroundColor());
if (c != null)
return c.getForeground();
return Color.gray;
}
/**
* Return the background color for the specified component
*/
public static Color getBackground(Component c)
{
SubstanceColorScheme colorScheme;
if (c == null)
colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL);
else if (c.isEnabled())
colorScheme = getEnabledColorScheme(c);
else
colorScheme = getDisabledColorScheme(c);
if (colorScheme == null)
getEnabledColorScheme(DecorationAreaType.GENERAL);
if (colorScheme != null)
return new ColorUIResource(colorScheme.getBackgroundFillColor());
if (c != null)
return c.getBackground();
return Color.lightGray;
}
/**
* Return the selected background color for the specified component
*/
public static Color getSelectedBackground(Component c)
{
SubstanceColorScheme colorScheme;
if (c == null)
colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL);
else if (c.isEnabled())
colorScheme = getEnabledColorScheme(c);
else
colorScheme = getDisabledColorScheme(c);
if (colorScheme == null)
getEnabledColorScheme(DecorationAreaType.GENERAL);
if (colorScheme != null)
return new ColorUIResource(colorScheme.getSelectionBackgroundColor());
if (c != null)
return c.getBackground();
return Color.darkGray;
}
public static SubstanceTitlePane getTitlePane(Window window)
{
return (SubstanceTitlePane) SubstanceLookAndFeel.getTitlePaneComponent(window);
}
public static SubstanceInternalFrameTitlePane getTitlePane(JInternalFrame frame)
{
final InternalFrameUI ui = frame.getUI();
if (ui instanceof SubstanceInternalFrameUI)
return ((SubstanceInternalFrameUI) ui).getTitlePane();
return null;
}
public static void setTitlePane(JInternalFrame frame, SubstanceInternalFrameTitlePane titlePane)
{
final InternalFrameUI ui = frame.getUI();
if (ui instanceof SubstanceInternalFrameUI)
((SubstanceInternalFrameUI) ui).setNorthPane(titlePane);
}
}