diff --git a/src/com/romraider/maps/Table3D.java b/src/com/romraider/maps/Table3D.java index fa2a7c65..a87b4b58 100644 --- a/src/com/romraider/maps/Table3D.java +++ b/src/com/romraider/maps/Table3D.java @@ -23,7 +23,7 @@ package com.romraider.maps; import com.romraider.Settings; import com.romraider.swing.TableFrame; -import com.romraider.swing.VTextIcon; +import com.romraider.swing.VerticalLabelUI; import com.romraider.util.AxisRange; import static com.romraider.util.ColorScaler.getScaledColor; import static com.romraider.util.ParamChecker.isNullOrEmpty; @@ -205,9 +205,8 @@ public class Table3D extends Table { topPanel.add(new JLabel(name, JLabel.CENTER), BorderLayout.NORTH); topPanel.add(new JLabel(xAxis.getName() + " (" + xAxis.getScale().getUnit() + ")", JLabel.CENTER), BorderLayout.NORTH); - JLabel yLabel = new JLabel(); - VTextIcon icon = new VTextIcon(yLabel, yAxis.getName() + " (" + yAxis.getScale().getUnit() + ")", VTextIcon.ROTATE_LEFT); - yLabel.setIcon(icon); + JLabel yLabel = new JLabel(yAxis.getName() + " (" + yAxis.getScale().getUnit() + ")"); + yLabel.setUI(new VerticalLabelUI(false)); add(yLabel, BorderLayout.WEST); add(new JLabel(getScale().getUnit(), JLabel.CENTER), BorderLayout.SOUTH); diff --git a/src/com/romraider/swing/VTextIcon.java b/src/com/romraider/swing/VTextIcon.java deleted file mode 100644 index fe8e8fb8..00000000 --- a/src/com/romraider/swing/VTextIcon.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * - * RomRaider Open-Source Tuning, Logging and Reflashing - * Copyright (C) 2006-2008 RomRaider.com - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -package com.romraider.swing; - -import javax.swing.Icon; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -/** - * VTextIcon is an Icon implementation which draws a short string vertically. - * It's useful for JTabbedPanes with LEFT or RIGHT tabs but can be used in any - * component which supports Icons, such as JLabel or JButton - *
- * You can provide a hint to indicate whether to rotate the string - * to the left or right, or not at all, and it checks to make sure - * that the rotation is legal for the given string - * (for example, Chinese/Japanese/Korean scripts have special rules when - * drawn vertically and should never be rotated) - */ -public class VTextIcon extends Component implements Icon, PropertyChangeListener { - String fLabel; - String[] fCharStrings; // for efficiency, break the fLabel into one-char strings to be passed to drawString - int[] fCharWidths; // Roman characters should be centered when not rotated (Japanese fonts are monospaced) - int[] fPosition; // Japanese half-height characters need to be shifted when drawn vertically - int fWidth, fHeight, fCharHeight, fDescent; // Cached for speed - int fRotation; - //Component fComponent; - - static final int POSITION_NORMAL = 0; - static final int POSITION_TOP_RIGHT = 1; - static final int POSITION_FAR_TOP_RIGHT = 2; - - public static final int ROTATE_DEFAULT = 0x00; - public static final int ROTATE_NONE = 0x01; - public static final int ROTATE_LEFT = 0x02; - public static final int ROTATE_RIGHT = 0x04; - - /** - * Creates aVTextIcon
for the specified component
- * with the specified label
.
- * It sets the orientation to the default for the string
- *
- * @see #verifyRotation
- */
- public VTextIcon(Component component, String label) {
- this(component, label, ROTATE_DEFAULT);
- }
-
- /**
- * Creates a VTextIcon
for the specified component
- * with the specified label
.
- * It sets the orientation to the provided value if it's legal for the string
- *
- * @see #verifyRotation
- */
- public VTextIcon(Component component, String label, int rotateHint) {
- //fComponent = component;
- fLabel = label;
- fRotation = verifyRotation(label, rotateHint);
- calcDimensions();
- //fComponent.addPropertyChangeListener(this);
- }
-
- /**
- * sets the label to the given string, updating the orientation as needed
- * and invalidating the layout if the size changes
- *
- * @see #verifyRotation
- */
- public void setLabel(String label) {
- fLabel = label;
- fRotation = verifyRotation(label, fRotation); // Make sure the current rotation is still legal
- recalcDimensions();
- }
-
- /**
- * Checks for changes to the font on the fComponent
- * so that it can invalidate the layout if the size changes
- */
- public void propertyChange(PropertyChangeEvent e) {
- String prop = e.getPropertyName();
- if ("font".equals(prop)) {
- recalcDimensions();
- }
- }
-
- /**
- * Calculates the dimensions. If they've changed,
- * invalidates the component
- */
- void recalcDimensions() {
- int wOld = getIconWidth();
- int hOld = getIconHeight();
- calcDimensions();
- if (wOld != getIconWidth() || hOld != getIconHeight()) {
- //fComponent.invalidate();
- }
- }
-
- void calcDimensions() {
- //FontMetrics fm = fComponent.getFontMetrics(fComponent.getFont());
- // fCharHeight = fm.getAscent() + fm.getDescent();
- // fDescent = fm.getDescent();
- if (fRotation == ROTATE_NONE) {
- int len = fLabel.length();
- char data[] = new char[len];
- fLabel.getChars(0, len, data, 0);
- // if not rotated, width is that of the widest char in the string
- fWidth = 0;
- // we need an array of one-char strings for drawString
- fCharStrings = new String[len];
- fCharWidths = new int[len];
- fPosition = new int[len];
- char ch;
- for (int i = 0; i < len; i++) {
- ch = data[i];
- // fCharWidths[i] = fm.charWidth(ch);
- if (fCharWidths[i] > fWidth) {
- fWidth = fCharWidths[i];
- }
- fCharStrings[i] = new String(data, i, 1);
- // small kana and punctuation
- if (sDrawsInTopRight.indexOf(ch) >= 0) // if ch is in sDrawsInTopRight
- {
- fPosition[i] = POSITION_TOP_RIGHT;
- } else if (sDrawsInFarTopRight.indexOf(ch) >= 0) {
- fPosition[i] = POSITION_FAR_TOP_RIGHT;
- } else {
- fPosition[i] = POSITION_NORMAL;
- }
- }
- // and height is the font height * the char count, + one extra leading at the bottom
- fHeight = fCharHeight * len + fDescent;
- } else {
- // if rotated, width is the height of the string
- fWidth = fCharHeight;
- // and height is the width, plus some buffer space
- //fHeight = fm.stringWidth(fLabel) + 2 * kBufferSpace;
- }
- }
-
- /**
- * Draw the icon at the specified location. Icon implementations
- * may use the Component argument to get properties useful for
- * painting, e.g. the foreground or background color.
- */
- public void paintIcon(Component c, Graphics g, int x, int y) {
- // We don't insist that it be on the same Component
- g.setColor(c.getForeground());
- g.setFont(c.getFont());
- if (fRotation == ROTATE_NONE) {
- int yPos = y + fCharHeight;
- for (int i = 0; i < fCharStrings.length; i++) {
- // Special rules for Japanese - "half-height" characters (like ya, yu, yo in combinations)
- // should draw in the top-right quadrant when drawn vertically
- // - they draw in the bottom-left normally
- int tweak;
- switch (fPosition[i]) {
- case POSITION_NORMAL:
- // Roman fonts should be centered. Japanese fonts are always monospaced.
- g.drawString(fCharStrings[i], x + ((fWidth - fCharWidths[i]) / 2), yPos);
- break;
- case POSITION_TOP_RIGHT:
- tweak = fCharHeight / 3; // Should be 2, but they aren't actually half-height
- g.drawString(fCharStrings[i], x + (tweak / 2), yPos - tweak);
- break;
- case POSITION_FAR_TOP_RIGHT:
- tweak = fCharHeight - fCharHeight / 3;
- g.drawString(fCharStrings[i], x + (tweak / 2), yPos - tweak);
- break;
- }
- yPos += fCharHeight;
- }
- } else if (fRotation == ROTATE_LEFT) {
- g.translate(x + fWidth, y + fHeight);
- ((Graphics2D) g).rotate(-NINETY_DEGREES);
- g.drawString(fLabel, kBufferSpace, -fDescent);
- ((Graphics2D) g).rotate(NINETY_DEGREES);
- g.translate(-(x + fWidth), -(y + fHeight));
- } else if (fRotation == ROTATE_RIGHT) {
- g.translate(x, y);
- ((Graphics2D) g).rotate(NINETY_DEGREES);
- g.drawString(fLabel, kBufferSpace, -fDescent);
- ((Graphics2D) g).rotate(-NINETY_DEGREES);
- g.translate(-x, -y);
- }
-
- }
-
- /**
- * Returns the icon's width.
- *
- * @return an int specifying the fixed width of the icon.
- */
- public int getIconWidth() {
- return fWidth;
- }
-
- /**
- * Returns the icon's height.
- *
- * @return an int specifying the fixed height of the icon.
- */
- public int getIconHeight() {
- return fHeight;
- }
-
- /**
- * verifyRotation
- *
- * returns the best rotation for the string (ROTATE_NONE, ROTATE_LEFT, ROTATE_RIGHT)
- *
- * This is public static so you can use it to test a string without creating a VTextIcon
- *
- * from http://www.unicode.org/unicode/reports/tr9/tr9-3.html
- * When setting text using the Arabic script in vertical lines,
- * it is more common to employ a horizontal baseline that
- * is rotated by 90deg counterclockwise so that the characters
- * are ordered from top to bottom. Latin text and numbers
- * may be rotated 90deg clockwise so that the characters
- * are also ordered from top to bottom.
- *
- * Rotation rules
- * - Roman can rotate left, right, or none - default right (counterclockwise)
- * - CJK can't rotate
- * - Arabic must rotate - default left (clockwise)
- *
- * from the online edition of _The Unicode Standard, Version 3.0_, file ch10.pdf page 4
- * Ideographs are found in three blocks of the Unicode Standard...
- * U+4E00-U+9FFF, U+3400-U+4DFF, U+F900-U+FAFF
- *
- * Hiragana is U+3040-U+309F, katakana is U+30A0-U+30FF
- *
- * from http://www.unicode.org/unicode/faq/writingdirections.html
- * East Asian scripts are frequently written in vertical lines
- * which run from top-to-bottom and are arrange columns either
- * from left-to-right (Mongolian) or right-to-left (other scripts).
- * Most characters use the same shape and orientation when displayed
- * horizontally or vertically, but many punctuation characters
- * will change their shape when displayed vertically.
- *
- * Letters and words from other scripts are generally rotated through
- * ninety degree angles so that they, too, will read from top to bottom.
- * That is, letters from left-to-right scripts will be rotated clockwise
- * and letters from right-to-left scripts counterclockwise, both
- * through ninety degree angles.
- *
- * Unlike the bidirectional case, the choice of vertical layout
- * is usually treated as a formatting style; therefore,
- * the Unicode Standard does not define default rendering behavior
- * for vertical text nor provide directionality controls designed to override such behavior
- */
- public static int verifyRotation(String label, int rotateHint) {
- boolean hasCJK = false;
- boolean hasMustRotate = false; // Arabic, etc
-
- int len = label.length();
- char data[] = new char[len];
- char ch;
- label.getChars(0, len, data, 0);
- for (int i = 0; i < len; i++) {
- ch = data[i];
- if ((ch >= '\u4E00' && ch <= '\u9FFF') ||
- (ch >= '\u3400' && ch <= '\u4DFF') ||
- (ch >= '\uF900' && ch <= '\uFAFF') ||
- (ch >= '\u3040' && ch <= '\u309F') ||
- (ch >= '\u30A0' && ch <= '\u30FF')) {
- hasCJK = true;
- }
- if ((ch >= '\u0590' && ch <= '\u05FF') || // Hebrew
- (ch >= '\u0600' && ch <= '\u06FF') || // Arabic
- (ch >= '\u0700' && ch <= '\u074F')) // Syriac
- {
- hasMustRotate = true;
- }
- }
- // If you mix Arabic with Chinese, you're on your own
- if (hasCJK) {
- return DEFAULT_CJK;
- }
-
- int legal = hasMustRotate ? LEGAL_MUST_ROTATE : LEGAL_ROMAN;
- if ((rotateHint & legal) > 0) {
- return rotateHint;
- }
-
- // The hint wasn't legal, or it was zero
- return hasMustRotate ? DEFAULT_MUST_ROTATE : DEFAULT_ROMAN;
- }
-
- // The small kana characters and Japanese punctuation that draw in the top right quadrant:
- // small a, i, u, e, o, tsu, ya, yu, yo, wa (katakana only) ka ke
- static final String sDrawsInTopRight =
- "\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308E" + // hiragana
- "\u30A1\u30A3\u30A5\u30A7\u30A9\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6"; // katakana
- static final String sDrawsInFarTopRight = "\u3001\u3002"; // comma, full stop
-
- static final int DEFAULT_CJK = ROTATE_NONE;
- static final int LEGAL_ROMAN = ROTATE_NONE | ROTATE_LEFT | ROTATE_RIGHT;
- static final int DEFAULT_ROMAN = ROTATE_RIGHT;
- static final int LEGAL_MUST_ROTATE = ROTATE_LEFT | ROTATE_RIGHT;
- static final int DEFAULT_MUST_ROTATE = ROTATE_LEFT;
-
- static final double NINETY_DEGREES = Math.toRadians(90.0);
- static final int kBufferSpace = 5;
-}
diff --git a/src/com/romraider/swing/VerticalLabelUI.java b/src/com/romraider/swing/VerticalLabelUI.java
new file mode 100644
index 00000000..02173225
--- /dev/null
+++ b/src/com/romraider/swing/VerticalLabelUI.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * RomRaider Open-Source Tuning, Logging and Reflashing
+ * Copyright (C) 2006-2008 RomRaider.com
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+package com.romraider.swing;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.plaf.basic.BasicLabelUI;
+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.geom.AffineTransform;
+
+public final class VerticalLabelUI extends BasicLabelUI {
+ private static Rectangle PAINT_ICON_R = new Rectangle();
+ private static Rectangle PAINT_TEXT_R = new Rectangle();
+ private static Rectangle PAINT_VIEW_R = new Rectangle();
+ private static Insets PAINT_VIEW_INSETS = new Insets(0, 0, 0, 0);
+ protected boolean clockwise;
+
+ static {
+ labelUI = new VerticalLabelUI(false);
+ }
+
+ public VerticalLabelUI(boolean clockwise) {
+ super();
+ this.clockwise = clockwise;
+ }
+
+ public Dimension getPreferredSize(JComponent c) {
+ Dimension dim = super.getPreferredSize(c);
+ return new Dimension(dim.height, dim.width);
+ }
+
+ public void paint(Graphics graphics, JComponent component) {
+ JLabel label = (JLabel) component;
+ String text = label.getText();
+ Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
+
+ if ((icon == null) && (text == null)) return;
+
+ FontMetrics fm = graphics.getFontMetrics();
+ PAINT_VIEW_INSETS = component.getInsets(PAINT_VIEW_INSETS);
+ PAINT_VIEW_R.x = PAINT_VIEW_INSETS.left;
+ PAINT_VIEW_R.y = PAINT_VIEW_INSETS.top;
+
+ // Use inverted height & width
+ PAINT_VIEW_R.height = component.getWidth() - (PAINT_VIEW_INSETS.left + PAINT_VIEW_INSETS.right);
+ PAINT_VIEW_R.width = component.getHeight() - (PAINT_VIEW_INSETS.top + PAINT_VIEW_INSETS.bottom);
+
+ PAINT_ICON_R.x = PAINT_ICON_R.y = PAINT_ICON_R.width = PAINT_ICON_R.height = 0;
+ PAINT_TEXT_R.x = PAINT_TEXT_R.y = PAINT_TEXT_R.width = PAINT_TEXT_R.height = 0;
+
+ String clippedText = layoutCL(label, fm, text, icon, PAINT_VIEW_R, PAINT_ICON_R, PAINT_TEXT_R);
+ int textWidth = fm.stringWidth(clippedText);
+
+ Graphics2D g2 = (Graphics2D) graphics;
+ AffineTransform tr = g2.getTransform();
+ if (clockwise) {
+ g2.rotate(Math.PI / 2);
+ g2.translate(component.getHeight() / 2 - textWidth / 2, -component.getWidth());
+ } else {
+ g2.rotate(-Math.PI / 2);
+ g2.translate(-component.getHeight() / 2 - textWidth / 2, 0);
+ }
+
+ if (icon != null) icon.paintIcon(component, graphics, PAINT_ICON_R.x, PAINT_ICON_R.y);
+
+ if (text != null) {
+ int textX = PAINT_TEXT_R.x;
+ int textY = PAINT_TEXT_R.y + fm.getAscent();
+
+ if (label.isEnabled()) paintEnabledText(label, graphics, clippedText, textX, textY);
+ else paintDisabledText(label, graphics, clippedText, textX, textY);
+ }
+
+ g2.setTransform(tr);
+ }
+}