package org.freeplane.view.swing.map;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.text.View;
import org.freeplane.core.ui.components.html.ScaledHTML;
import org.freeplane.core.util.TextUtils;
/*
* Freeplane - mind map editor
* Copyright (C) 2009 Dimitry
*
* This file author is Dimitry
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Dimitry Polivaev
* 23.08.2009
*/
public class ZoomableLabelUI extends BasicLabelUI {
private boolean isPainting = false;
static ZoomableLabelUI labelUI = new ZoomableLabelUI();
private Rectangle iconR = new Rectangle();
private Rectangle textR = new Rectangle();
private Rectangle viewR = new Rectangle();
private int maximumWidth = Integer.MAX_VALUE;
public Dimension getPreferredSize(final ZoomableLabel c, int maximumWidth) {
try{
this.maximumWidth = maximumWidth;
final Dimension preferredSize = getPreferredSize(c);
return preferredSize;
}
finally{
this.maximumWidth = Integer.MAX_VALUE;
}
}
@Override
public Dimension getPreferredSize(final JComponent c) {
final Dimension preferredSize = super.getPreferredSize(c);
final int fontHeight = ((ZoomableLabel) c).getFontMetrics().getHeight();
final Insets insets = c.getInsets();
preferredSize.width = Math.max(preferredSize.width, fontHeight/2 + insets.left + insets.right);
preferredSize.height = Math.max(preferredSize.height, fontHeight + insets.top + insets.bottom);
final float zoom = ((ZoomableLabel) c).getZoom();
if (zoom != 1f) {
preferredSize.width = (int) (Math.ceil(zoom * preferredSize.width));
preferredSize.height = (int) (Math.ceil(zoom * preferredSize.height));
}
return preferredSize;
}
public static ComponentUI createUI(final JComponent c) {
return labelUI;
}
@Override
protected String layoutCL(final JLabel label, final FontMetrics fontMetrics, final String text, final Icon icon,
final Rectangle viewR, final Rectangle iconR, final Rectangle textR) {
final ZoomableLabel zLabel = (ZoomableLabel) label;
ScaledHTML.Renderer v = null;
if (isPainting) {
final Insets insets = zLabel.getInsets();
final int width = zLabel.getWidth();
final int height = zLabel.getHeight();
final float zoom = zLabel.getZoom();
viewR.x = insets.left;
viewR.y = insets.top;
viewR.width = (int) (width / zoom) - (insets.left + insets.right);
viewR.height = (int)(height / zoom) - (insets.top + insets.bottom);
if(viewR.width < 0)
viewR.width = 0;
v = (ScaledHTML.Renderer) label.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
float preferredWidth = v.getPreferredSpan(View.X_AXIS);
int textWidth = viewR.width;
if(icon != null)
textWidth -= icon.getIconWidth() + label.getIconTextGap();
if(preferredWidth < textWidth){
v.setSize(textWidth, 1);
super.layoutCL(zLabel, zLabel.getFontMetrics(), text, icon, viewR, iconR, textR);
v.setSize(textR.width, textR.height);
return text;
}
}
}
else if(maximumWidth != Integer.MAX_VALUE){
final Insets insets = label.getInsets();
viewR.width = maximumWidth - insets.left - insets.right;
if(viewR.width < 0)
viewR.width = 0;
v = (ScaledHTML.Renderer) label.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.resetSize();
float preferredWidth = v.getPreferredSpan(View.X_AXIS);
float minimumWidth = v.getMinimumSpan(View.X_AXIS);
int textWidth = viewR.width;
if(icon != null)
textWidth -= icon.getIconWidth() + label.getIconTextGap();
if(preferredWidth > textWidth){
if(minimumWidth > textWidth){
viewR.width += minimumWidth - textWidth;
textWidth = (int) minimumWidth;
}
v.setSize(textWidth, 1);
super.layoutCL(zLabel, zLabel.getFontMetrics(), text, icon, viewR, iconR, textR);
v.setSize(textR.width, textR.height);
return text;
}
}
}
super.layoutCL(zLabel, zLabel.getFontMetrics(), text, icon, viewR, iconR, textR);
return text;
}
@Override
public void paint(final Graphics g, final JComponent label) {
final ZoomableLabel mainView = (ZoomableLabel) label;
if (!mainView.useFractionalMetrics()) {
try {
isPainting = true;
superPaintSafe(g, mainView);
}
finally {
isPainting = false;
}
return;
}
final Graphics2D g2 = (Graphics2D) g;
final Object oldRenderingHintFM = g2.getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS);
final Object newRenderingHintFM = RenderingHints.VALUE_FRACTIONALMETRICS_ON;
if (oldRenderingHintFM != newRenderingHintFM) {
g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, newRenderingHintFM);
}
final AffineTransform transform = g2.getTransform();
final float zoom = mainView.getZoom() * 0.97f;
g2.scale(zoom, zoom);
final boolean htmlViewSet = null != label.getClientProperty(BasicHTML.propertyKey);
try {
isPainting = true;
if(htmlViewSet){
GlyphPainterMetricResetter.resetPainter();
}
superPaintSafe(g, mainView);
}
finally {
isPainting = false;
if(htmlViewSet){
GlyphPainterMetricResetter.resetPainter();
}
}
g2.setTransform(transform);
if (oldRenderingHintFM != newRenderingHintFM) {
g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, oldRenderingHintFM != null ? oldRenderingHintFM
: RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
}
}
// Workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7126361
private void superPaintSafe(final Graphics g, final JLabel label) {
try {
super.paint(g, label);
} catch (ClassCastException e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText(TextUtils.format("html_problem", label.getText()));
}
});
}
}
@Override
public void propertyChange(PropertyChangeEvent e) {
GlyphPainterMetricResetter.resetPainter();
try {
String name = e.getPropertyName();
if (name == "text" || "font" == name || "foreground" == name) {
JLabel lbl = ((JLabel) e.getSource());
String text = lbl.getText();
ScaledHTML.updateRenderer(lbl, text);
View v = (View) lbl.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
lbl.putClientProperty("preferredWidth", v.getPreferredSpan(View.X_AXIS));
}
}
else
super.propertyChange(e);
}
finally{
GlyphPainterMetricResetter.resetPainter();
}
}
@Override
protected void installComponents(JLabel c) {
ScaledHTML.updateRenderer(c, c.getText());
c.setInheritsPopupMenu(true);
}
public Rectangle getIconR(ZoomableLabel label) {
layout(label);
return iconR;
}
public Rectangle getTextR(ZoomableLabel label) {
layout(label);
return textR;
}
private void layout(ZoomableLabel label) {
String text = label.getText();
if(text == null || text.equals(""))
text = "!";
Icon icon = (label.isEnabled()) ? label.getIcon() :
label.getDisabledIcon();
boolean wasPainting = isPainting;
try{
isPainting = true;
iconR.x = iconR.y = iconR.width = iconR.height = 0;
textR.x = textR.y = textR.width = textR.height = 0;
layoutCL(label, label.getFontMetrics(), text, icon, viewR, iconR,textR);
final float zoom = label.getZoom();
iconR.x = (int)(iconR.x * zoom);
iconR.y = (int)(iconR.y * zoom);
iconR.width = (int)(iconR.width * zoom);
iconR.height = (int)(iconR.height * zoom);
textR.x = (int)(textR.x * zoom);
textR.y = (int)(textR.y * zoom);
textR.width = (int)(textR.width * zoom);
textR.height = (int)(textR.height * zoom);
}
finally{
isPainting = wasPainting;
}
}
}