KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > it > businesslogic > ireport > gui > docking > VTextIcon


1 /*
2  * Copyright (C) 2005 - 2006 JasperSoft Corporation. All rights reserved.
3  * http://www.jaspersoft.com.
4  *
5  * Unless you have purchased a commercial license agreement from JasperSoft,
6  * the following license terms apply:
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as published by
10  * the Free Software Foundation.
11  *
12  * This program is distributed WITHOUT ANY WARRANTY; and without the
13  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see http://www.gnu.org/licenses/gpl.txt
18  * or write to:
19  *
20  * Free Software Foundation, Inc.,
21  * 59 Temple Place - Suite 330,
22  * Boston, MA USA 02111-1307
23  *
24  *
25  *
26  *
27  * VTextIcon.java
28  *
29  * Created on March 18, 2006, 10:57 PM
30  *
31  */

32
33 package it.businesslogic.ireport.gui.docking;
34
35 import java.awt.*;
36 import javax.swing.*;
37 import java.beans.*;
38
39 /**
40  VTextIcon is an Icon implementation which draws a short string vertically.
41  It's useful for JTabbedPanes with LEFT or RIGHT tabs but can be used in any
42  component which supports Icons, such as JLabel or JButton
43  
44  You can provide a hint to indicate whether to rotate the string
45  to the left or right, or not at all, and it checks to make sure
46  that the rotation is legal for the given string
47  (for example, Chinese/Japanese/Korean scripts have special rules when
48  drawn vertically and should never be rotated)
49  */

50 public class VTextIcon implements Icon, PropertyChangeListener {
51     String JavaDoc fLabel;
52     String JavaDoc[] fCharStrings; // for efficiency, break the fLabel into one-char strings to be passed to drawString
53
int[] fCharWidths; // Roman characters should be centered when not rotated (Japanese fonts are monospaced)
54
int[] fPosition; // Japanese half-height characters need to be shifted when drawn vertically
55
int fWidth, fHeight, fCharHeight, fDescent; // Cached for speed
56
int fRotation;
57     Component fComponent;
58     
59     static final int POSITION_NORMAL = 0;
60     static final int POSITION_TOP_RIGHT = 1;
61     static final int POSITION_FAR_TOP_RIGHT = 2;
62     
63     public static final int ROTATE_DEFAULT = 0x00;
64     public static final int ROTATE_NONE = 0x01;
65     public static final int ROTATE_LEFT = 0x02;
66     public static final int ROTATE_RIGHT = 0x04;
67     
68     /**
69      * Creates a <code>VTextIcon</code> for the specified <code>component</code>
70      * with the specified <code>label</code>.
71      * It sets the orientation to the default for the string
72      * @see #verifyRotation
73      */

74     public VTextIcon(Component component, String JavaDoc label) {
75         this(component, label, ROTATE_DEFAULT);
76     }
77     
78     /**
79      * Creates a <code>VTextIcon</code> for the specified <code>component</code>
80      * with the specified <code>label</code>.
81      * It sets the orientation to the provided value if it's legal for the string
82      * @see #verifyRotation
83      */

84     public VTextIcon(Component component, String JavaDoc label, int rotateHint) {
85         fComponent = component;
86         fLabel = label;
87         fRotation = verifyRotation(label, rotateHint);
88         calcDimensions();
89         fComponent.addPropertyChangeListener(this);
90     }
91     
92     /**
93      * sets the label to the given string, updating the orientation as needed
94      * and invalidating the layout if the size changes
95      * @see #verifyRotation
96      */

97     public void setLabel(String JavaDoc label) {
98         fLabel = label;
99         fRotation = verifyRotation(label, fRotation); // Make sure the current rotation is still legal
100
recalcDimensions();
101     }
102     
103     /**
104      * Checks for changes to the font on the fComponent
105      * so that it can invalidate the layout if the size changes
106      */

107     public void propertyChange(PropertyChangeEvent e) {
108         String JavaDoc prop = e.getPropertyName();
109         if("font".equals(prop)) {
110             recalcDimensions();
111         }
112     }
113     
114     /**
115      * Calculates the dimensions. If they've changed,
116      * invalidates the component
117      */

118     void recalcDimensions() {
119         int wOld = getIconWidth();
120         int hOld = getIconHeight();
121         calcDimensions();
122         if (wOld != getIconWidth() || hOld != getIconHeight())
123             fComponent.invalidate();
124     }
125     
126     void calcDimensions() {
127         FontMetrics fm = fComponent.getFontMetrics(fComponent.getFont());
128         fCharHeight = fm.getAscent() + fm.getDescent();
129         fDescent = fm.getDescent();
130         if (fRotation == ROTATE_NONE) {
131             int len = fLabel.length();
132             char data[] = new char[len];
133             fLabel.getChars(0, len, data, 0);
134             // if not rotated, width is that of the widest char in the string
135
fWidth = 0;
136             // we need an array of one-char strings for drawString
137
fCharStrings = new String JavaDoc[len];
138             fCharWidths = new int[len];
139             fPosition = new int[len];
140             char ch;
141             for (int i = 0; i < len; i++) {
142                 ch = data[i];
143                 fCharWidths[i] = fm.charWidth(ch);
144                 if (fCharWidths[i] > fWidth)
145                     fWidth = fCharWidths[i];
146                 fCharStrings[i] = new String JavaDoc(data, i, 1);
147                 // small kana and punctuation
148
if (sDrawsInTopRight.indexOf(ch) >= 0) // if ch is in sDrawsInTopRight
149
fPosition[i] = POSITION_TOP_RIGHT;
150                 else if (sDrawsInFarTopRight.indexOf(ch) >= 0)
151                     fPosition[i] = POSITION_FAR_TOP_RIGHT;
152                 else
153                     fPosition[i] = POSITION_NORMAL;
154             }
155             // and height is the font height * the char count, + one extra leading at the bottom
156
fHeight = fCharHeight * len + fDescent;
157         }
158         else {
159             // if rotated, width is the height of the string
160
fWidth = fCharHeight;
161             // and height is the width, plus some buffer space
162
fHeight = fm.stringWidth(fLabel) + 2*kBufferSpace;
163         }
164     }
165
166    /**
167      * Draw the icon at the specified location. Icon implementations
168      * may use the Component argument to get properties useful for
169      * painting, e.g. the foreground or background color.
170      */

171     public void paintIcon(Component c, Graphics g, int x, int y) {
172         // We don't insist that it be on the same Component
173
g.setColor(c.getForeground());
174         g.setFont(c.getFont());
175                 //
176
Graphics2D g2 = (Graphics2D)g;
177                 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
178                     RenderingHints.VALUE_ANTIALIAS_ON);
179                 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
180                     RenderingHints.VALUE_STROKE_DEFAULT);
181                 g2.setRenderingHint(RenderingHints.KEY_DITHERING,
182                     RenderingHints.VALUE_DITHER_ENABLE);
183                 
184         if (fRotation == ROTATE_NONE) {
185             int yPos = y + fCharHeight;
186             for (int i = 0; i < fCharStrings.length; i++) {
187                 // Special rules for Japanese - "half-height" characters (like ya, yu, yo in combinations)
188
// should draw in the top-right quadrant when drawn vertically
189
// - they draw in the bottom-left normally
190
int tweak;
191                 switch (fPosition[i]) {
192                     case POSITION_NORMAL:
193                         // Roman fonts should be centered. Japanese fonts are always monospaced.
194
g.drawString(fCharStrings[i], x+((fWidth-fCharWidths[i])/2), yPos);
195                         break;
196                     case POSITION_TOP_RIGHT:
197                         tweak = fCharHeight/3; // Should be 2, but they aren't actually half-height
198
g.drawString(fCharStrings[i], x+(tweak/2), yPos-tweak);
199                         break;
200                     case POSITION_FAR_TOP_RIGHT:
201                         tweak = fCharHeight - fCharHeight/3;
202                         g.drawString(fCharStrings[i], x+(tweak/2), yPos-tweak);
203                         break;
204                 }
205                 yPos += fCharHeight;
206             }
207         }
208         else if (fRotation == ROTATE_LEFT) {
209             g.translate(x+fWidth,y+fHeight);
210             ((Graphics2D)g).rotate(-NINETY_DEGREES);
211             g.drawString(fLabel, kBufferSpace, -fDescent);
212             ((Graphics2D)g).rotate(NINETY_DEGREES);
213             g.translate(-(x+fWidth),-(y+fHeight));
214         }
215         else if (fRotation == ROTATE_RIGHT) {
216             g.translate(x,y);
217             ((Graphics2D)g).rotate(NINETY_DEGREES);
218             g.drawString(fLabel, kBufferSpace, -fDescent);
219             ((Graphics2D)g).rotate(-NINETY_DEGREES);
220             g.translate(-x,-y);
221         }
222     
223     }
224     
225     /**
226      * Returns the icon's width.
227      *
228      * @return an int specifying the fixed width of the icon.
229      */

230     public int getIconWidth() {
231         return fWidth;
232     }
233     
234     /**
235      * Returns the icon's height.
236      *
237      * @return an int specifying the fixed height of the icon.
238      */

239     public int getIconHeight() {
240         return fHeight;
241     }
242     
243     /**
244          verifyRotation
245         
246         returns the best rotation for the string (ROTATE_NONE, ROTATE_LEFT, ROTATE_RIGHT)
247         
248         This is public static so you can use it to test a string without creating a VTextIcon
249         
250      from http://www.unicode.org/unicode/reports/tr9/tr9-3.html
251      When setting text using the Arabic script in vertical lines,
252      it is more common to employ a horizontal baseline that
253      is rotated by 90 counterclockwise so that the characters
254      are ordered from top to bottom. Latin text and numbers
255      may be rotated 90 clockwise so that the characters
256      are also ordered from top to bottom.
257      
258         Rotation rules
259         - Roman can rotate left, right, or none - default right (counterclockwise)
260         - CJK can't rotate
261         - Arabic must rotate - default left (clockwise)
262      
263      from the online edition of _The Unicode Standard, Version 3.0_, file ch10.pdf page 4
264      Ideographs are found in three blocks of the Unicode Standard...
265      U+4E00-U+9FFF, U+3400-U+4DFF, U+F900-U+FAFF
266      
267      Hiragana is U+3040-U+309F, katakana is U+30A0-U+30FF
268      
269      from http://www.unicode.org/unicode/faq/writingdirections.html
270      East Asian scripts are frequently written in vertical lines
271      which run from top-to-bottom and are arrange columns either
272      from left-to-right (Mongolian) or right-to-left (other scripts).
273      Most characters use the same shape and orientation when displayed
274      horizontally or vertically, but many punctuation characters
275      will change their shape when displayed vertically.
276
277      Letters and words from other scripts are generally rotated through
278      ninety degree angles so that they, too, will read from top to bottom.
279      That is, letters from left-to-right scripts will be rotated clockwise
280      and letters from right-to-left scripts counterclockwise, both
281      through ninety degree angles.
282
283     Unlike the bidirectional case, the choice of vertical layout
284     is usually treated as a formatting style; therefore,
285     the Unicode Standard does not define default rendering behavior
286     for vertical text nor provide directionality controls designed to override such behavior
287
288      */

289     public static int verifyRotation(String JavaDoc label, int rotateHint) {
290         boolean hasCJK = false;
291         boolean hasMustRotate = false; // Arabic, etc
292

293         int len = label.length();
294         char data[] = new char[len];
295         char ch;
296         label.getChars(0, len, data, 0);
297         for (int i = 0; i < len; i++) {
298             ch = data[i];
299             if ((ch >= '\u4E00' && ch <= '\u9FFF') ||
300                 (ch >= '\u3400' && ch <= '\u4DFF') ||
301                 (ch >= '\uF900' && ch <= '\uFAFF') ||
302                 (ch >= '\u3040' && ch <= '\u309F') ||
303                 (ch >= '\u30A0' && ch <= '\u30FF') )
304                hasCJK = true;
305             if ((ch >= '\u0590' && ch <= '\u05FF') || // Hebrew
306
(ch >= '\u0600' && ch <= '\u06FF') || // Arabic
307
(ch >= '\u0700' && ch <= '\u074F') ) // Syriac
308
hasMustRotate = true;
309         }
310         // If you mix Arabic with Chinese, you're on your own
311
if (hasCJK)
312             return DEFAULT_CJK;
313         
314         int legal = hasMustRotate ? LEGAL_MUST_ROTATE : LEGAL_ROMAN;
315         if ((rotateHint & legal) > 0)
316             return rotateHint;
317         
318         // The hint wasn't legal, or it was zero
319
return hasMustRotate ? DEFAULT_MUST_ROTATE : DEFAULT_ROMAN;
320     }
321     
322     // The small kana characters and Japanese punctuation that draw in the top right quadrant:
323
// small a, i, u, e, o, tsu, ya, yu, yo, wa (katakana only) ka ke
324
static final String JavaDoc sDrawsInTopRight =
325         "\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308E" + // hiragana
326
"\u30A1\u30A3\u30A5\u30A7\u30A9\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6"; // katakana
327
static final String JavaDoc sDrawsInFarTopRight = "\u3001\u3002"; // comma, full stop
328

329     static final int DEFAULT_CJK = ROTATE_NONE;
330     static final int LEGAL_ROMAN = ROTATE_NONE | ROTATE_LEFT | ROTATE_RIGHT;
331     static final int DEFAULT_ROMAN = ROTATE_RIGHT;
332     static final int LEGAL_MUST_ROTATE = ROTATE_LEFT | ROTATE_RIGHT;
333     static final int DEFAULT_MUST_ROTATE = ROTATE_LEFT;
334
335     static final double NINETY_DEGREES = Math.toRadians(90.0);
336     static final int kBufferSpace = 5;
337 }
338
Popular Tags