001package jmri.util.swing;
002
003import java.awt.Dimension;
004import java.awt.FontMetrics;
005import java.awt.Graphics;
006import java.awt.Graphics2D;
007import java.awt.Insets;
008import java.awt.Rectangle;
009import java.awt.geom.AffineTransform;
010import javax.swing.Icon;
011import javax.swing.JComponent;
012import javax.swing.JLabel;
013import javax.swing.plaf.basic.BasicLabelUI;
014
015/**
016 * Allows a JLabel to be displayed vertically, with a defined orientation. Usage
017 * (for a vertical label with anti-clockwise orientation):
018 * <code>
019 * <br>JLabel label = new JLabel("Vertical Label");
020 * <br>label.setUI(new VerticalLabelUI(VerticalLabelUI.ANTICLOCKWISE));
021 * </code>
022 * <hr>
023 * This file is part of JMRI.
024 * <p>
025 * JMRI is free software; you can redistribute it and/or modify it under the
026 * terms of version 2 of the GNU General Public License as published by the Free
027 * Software Foundation. See the "COPYING" file for a copy of this license.
028 * <p>
029 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
030 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
031 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
032 *
033 * @author Matthew Harris copyright (c) 2010
034 */
035// commented out in AudioBufferFrame and used in scripts, so do not deprecate
036// without checking those sources first even though IDE find usages tools show
037// no use outside unit tests
038public class VerticalLabelUI extends BasicLabelUI {
039
040    /**
041     * Define Clockwise rotation (+90 degrees from horizontal)
042     */
043    public static final int CLOCKWISE = 1;
044
045    /**
046     * Define Anti-Clockwise rotation (-90 degrees from horizontal)
047     */
048    public static final int ANTICLOCKWISE = 2;
049
050    /**
051     * Variable to determine rotation direction
052     */
053    private int rotation;
054
055    /**
056     * Static variables used to compute bounding rectangles for each constituent
057     * part of the VerticalLabel
058     */
059    private final Rectangle iconRectangle = new Rectangle();
060    private final Rectangle textRectangle = new Rectangle();
061    private final Rectangle viewRectangle = new Rectangle();
062    private Insets viewInsets = new Insets(0, 0, 0, 0);
063
064    /**
065     * Default constructor which provides a vertical label with anti-clockwise
066     * orientation
067     */
068    public VerticalLabelUI() {
069        this(ANTICLOCKWISE);
070    }
071
072    /**
073     * Constructor used to provide a vertical label of the specified orientation
074     *
075     * @param rotation defines the rotation:
076     * <br>{@link #CLOCKWISE} or
077     * <br>{@link #ANTICLOCKWISE}
078     */
079    public VerticalLabelUI(int rotation) {
080        super();
081        this.rotation = rotation;
082    }
083
084    @Override
085    public Dimension getPreferredSize(JComponent component) {
086        Dimension dimension = super.getPreferredSize(component);
087        return new Dimension(dimension.height, dimension.width);
088    }
089
090    @Override
091    public void paint(Graphics graphics, JComponent component) {
092
093        // Retrieve the text and icon of the label being rotated
094        if (component instanceof JLabel) {
095            JLabel label = (JLabel) component;
096            String text = label.getText();
097            Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
098
099            // If both the icon and text are empty, nothing to be done
100            if ((icon == null) && (text == null)) {
101                return;
102            }
103
104            // Retrieve the font redering informaton
105            FontMetrics fontMetrics = graphics.getFontMetrics();
106
107            // Determine required insets for the view
108            viewInsets = component.getInsets(viewInsets);
109
110            // Initialise view origin
111            viewRectangle.x = viewInsets.left;
112            viewRectangle.y = viewInsets.top;
113
114            // Determine view height and width
115            // (inverting width and height as rotation not yet performed)
116            viewRectangle.height = component.getWidth() - (viewInsets.left + viewInsets.right);
117            viewRectangle.width = component.getHeight() - (viewInsets.top + viewInsets.bottom);
118
119            // Initialise the icon and text bounding boxes
120            iconRectangle.x = 0;
121            iconRectangle.y = 0;
122            iconRectangle.width = 0;
123            iconRectangle.height = 0;
124            textRectangle.x = 0;
125            textRectangle.y = 0;
126            textRectangle.width = 0;
127            textRectangle.height = 0;
128
129            // Grab the string to display
130            String clippedText
131                    = layoutCL(label, fontMetrics, text, icon, viewRectangle, iconRectangle, textRectangle);
132
133            // Store the current transform prior to rotation
134            AffineTransform transform = null;
135            if (graphics instanceof Graphics2D) {
136                transform = ((Graphics2D) graphics).getTransform();
137
138                // Perform the rotation
139                ((Graphics2D) graphics).rotate((this.rotation == CLOCKWISE ? 1 : -1) * (Math.PI / 2));
140                ((Graphics2D) graphics).translate(
141                        this.rotation == CLOCKWISE ? 0 : -component.getHeight(),
142                        this.rotation == CLOCKWISE ? -component.getWidth() : 0);
143            }
144
145            // If necessary, paint the icon
146            if (icon != null) {
147                icon.paintIcon(component, graphics, iconRectangle.x, iconRectangle.y);
148            }
149
150            // If necessary, paint the text
151            if (text != null) {
152                int textX = textRectangle.x;
153                int textY = textRectangle.y + fontMetrics.getAscent();
154
155                if (label.isEnabled()) {
156                    paintEnabledText(label, graphics, clippedText, textX, textY);
157                } else {
158                    paintDisabledText(label, graphics, clippedText, textX, textY);
159                }
160            }
161
162            if (graphics instanceof Graphics2D && transform != null) {
163                // Finally, restore the original transform
164                ((Graphics2D) graphics).setTransform(transform);
165            }
166        }
167    }
168
169    //private static final Logger log = LoggerFactory.getLogger(VerticalLabelUI.class);
170}