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}