/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.ui; import javax.swing.plaf.FontUIResource; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import static java.util.Locale.ENGLISH; /** * This class is intended to update a component's font automatically, * if the default font is changed on L&F update. * * @author Sergey.Malenkov */ public final class RelativeFont implements PropertyChangeListener { private static final float MULTIPLIER = 1.09f; // based on the default sizes: 10, 11, 12, 13, 14 public static final RelativeFont NORMAL = new RelativeFont(null, null, null); public static final RelativeFont PLAIN = NORMAL.style(Font.PLAIN); public static final RelativeFont BOLD = NORMAL.style(Font.BOLD); public static final RelativeFont ITALIC = NORMAL.style(Font.ITALIC); public static final RelativeFont LARGE = NORMAL.large(); public static final RelativeFont SMALL = NORMAL.small(); public static final RelativeFont HUGE = LARGE.large(); public static final RelativeFont TINY = SMALL.small(); private static final String PROPERTY = "font"; private final String myFamily; private final Integer myStyle; private final Float mySize; private RelativeFont(String family, Integer style, Float size) { myFamily = family; myStyle = style; mySize = size; } /** * @param family a new family to derive font * @return a new instance with the specified family */ public RelativeFont family(String family) { return family.equals(myFamily) ? this : new RelativeFont(family, myStyle, mySize); } /** * @param style a new style to derive font * @return a new instance with the specified style */ public RelativeFont style(int style) { return null != myStyle && myStyle == style ? this : new RelativeFont(myFamily, style, mySize); } /** * @return a new instance with increased font size */ public RelativeFont large() { float size = mySize == null ? 1f : mySize; return new RelativeFont(myFamily, myStyle, size * MULTIPLIER); } /** * @return a new instance with decreased font size */ public RelativeFont small() { float size = mySize == null ? 1f : mySize; return new RelativeFont(myFamily, myStyle, size / MULTIPLIER); } /** * Installs this instance on the specified component. * It adds the "font" property change listener * that replaces a component's font with the relative one. * * @param component the component to install on * @return the same component */ public <T extends Component> T install(T component) { Font font = derive(component.getFont()); if (font != null) { component.setFont(new MyFont(font)); } uninstallFrom(component); component.addPropertyChangeListener(PROPERTY, this); return component; } /** * Uninstalls all instances from the specified component. * It just removes all the "font" property change listeners * without any font modification. * * @param component the component to uninstall from * @return the same component */ public static <T extends Component> T uninstallFrom(T component) { for (PropertyChangeListener listener : component.getPropertyChangeListeners(PROPERTY)) { if (listener instanceof RelativeFont) { component.removePropertyChangeListener(PROPERTY, listener); } } return component; } /** * Creates a new font by replicating the specified one * and applying a new family, style, and/or size. * * @param font the font to modify * @return a new font, or the specified one if a change is not needed */ public Font derive(Font font) { if (font != null) { if (null != myFamily && !myFamily.equals(font.getFamily(ENGLISH))) { int style = null != myStyle ? myStyle : font.getStyle(); font = new Font(myFamily, style, font.getSize()); } else if (null != myStyle && myStyle != font.getStyle()) { return mySize != null ? font.deriveFont(myStyle, mySize * font.getSize2D()) : font.deriveFont(myStyle); } if (mySize != null) { return font.deriveFont(mySize * font.getSize2D()); } } return font; } @Override public void propertyChange(PropertyChangeEvent event) { if (!(event.getNewValue() instanceof MyFont) && (event.getSource() instanceof Component) && PROPERTY.equals(event.getPropertyName())) { Component component = (Component)event.getSource(); Font font = derive(event.getNewValue() instanceof Font ? (Font)event.getNewValue() : component.getFont()); if (font != null) { component.setFont(new MyFont(font)); } } } private static final class MyFont extends FontUIResource { private MyFont(Font font) { super(font); } } }