Java Forum / General / August 2006
Why does AffineTransform not work on JTextPane?
fiziwig - 10 Aug 2006 18:20 GMT I can rotate every other type of component I've tried, but when I rotate a JTextPane by 90 or 180 degrees (which I REALLY need for my customer's application) The clipping and bordering get all messed up and I'm at my wit's end trying to make it work.
Am I doing something wrong, or is there a Java bug in applying a transform to a JTextPane? The same basic code works on a JLabel, why won't it work on a JTextPane?
Any ideas or suggestions would be deeply appreciated.
OR any ideas for a different way to display STYLED text (font, size, color, bold, italic on a character-by-character basis) at a 90 or 180 degree rotation. It doesn't have to be editable when rotated, but it does need to be editable when upright, otherwise I'd just use HTML in a JLabel.
Thanks,
--gary
Compilable example:
import javax.swing.*; import javax.swing.text.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*;
public class Rotate extends JPanel {
private TextPanel textPane; private JLayeredPane parent;
public Rotate() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); JToolBar toolBar = buildToolbar(); add(toolBar);
parent = new JLayeredPane(); add(parent); parent.setBackground( Color.gray); parent.setPreferredSize(new Dimension(640, 480));
// Create a text pane.
textPane = new TextPanel(); StyledDocument doc = textPane.getStyledDocument(); try { doc.insertString(doc.getLength(), "This is some sample text.\nIt can be Rotated.", null); } catch (BadLocationException ble) { System.err.println("Couldn't insert initial text into text pane."); } Border myBorder = BorderFactory.createLineBorder( Color.red ); textPane.setBorder(myBorder); parent.setOpaque(true); parent.add(textPane); textPane.setDefaultBounds(120, 120, 240, 120); } private JToolBar buildToolbar() {
JToolBar toolBar = new JToolBar(); toolBar.setRollover( true ); toolBar.setFloatable( false ); JButton rotateButton = new JButton("Rotate"); rotateButton.setToolTipText( "Rotate text editing pane" ); rotateButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { textPane.setRotation(textPane.getRotation()+1); } }); toolBar.add( rotateButton ); return toolBar; } private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true);
//Create and set up the window. JFrame frame = new JFrame("Rotate"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane. JComponent newContentPane = new Rotate(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane);
//Display the window. frame.pack(); frame.setVisible(true); }
public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); }
}
class TextPanel extends JTextPane {
// implements rotation for a JTextPane
private int rotation; private int tx, ty; private int wide, high;
// valid rotation values are: // 0 = no rotation // 1 = rotation 90 degree clockwise // 2 = rotation 180 degrees // 3 = rotation 90 degrees counterclockwise
TextPanel() { super(); rotation = 0; tx = 0; ty = 0; } public void setDefaultBounds( int x, int y, int width, int height) { high = height; wide = width; super.setBounds(x,y,width,height); } public void setRotation( int newRotation ) { rotation = newRotation % 4;
if ((rotation%2)==0) { setSize(wide,high); } else { setSize(high,wide); } switch (rotation) { case 0 : tx = 0; ty = 0; break; case 1 : tx = 1; ty = 0; break; case 2 : tx = 1; ty = 1; break; case 3 : tx = 0; ty = 1; break; } repaint(); } public int getRotation() { return rotation; }
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g; double angle = rotation * Math.PI/2; AffineTransform tr = g2.getTransform(); int h,w; if ((rotation%2) == 0) { w = wide; h = high; } else { h = wide; w = high; }
tr.setToTranslation(getWidth()*tx,getHeight()*ty); tr.rotate(angle); g2.setTransform(tr); // <----- comment this line out to see correct borders super.paintComponent(g2); } }
Oliver Wong - 10 Aug 2006 19:29 GMT >I can rotate every other type of component I've tried, but when I > rotate a JTextPane by 90 or 180 degrees (which I REALLY need for my [quoted text clipped - 12 lines] > does need to be editable when upright, otherwise I'd just use HTML in a > JLabel. How about using a JTextPane for editing, and when editing is done, copy the contents and put it into a JLabel, and rotate the JLabel?
- Oliver
fiziwig - 10 Aug 2006 19:44 GMT > "fiziwig" <fiziwig@yahoo.com> wrote in message <snip>
> > OR any ideas for a different way to display STYLED text (font, size, > > color, bold, italic on a character-by-character basis) at a 90 or 180 [quoted text clipped - 6 lines] > > - Oliver That might work. The problem is then I have to go through the text character by character pulling out the attributes (font, size, color, bold, italic,...) and turning those into HTML tags so that they will display properly in the JLabel. Then when the user selects the text for editing again I have to turn around and parse out all the HTML tags and build it back into a styled document.
This would be soooo easy in C++/Windows, but Java makes the hard things easy by making the easy things hard, by keeping some very important things hidden and inaccessable. That's great most of the time, but it's a major pain in the neck at other times. It's like trying to build a geodesic dome with Lincoln logs. The Java Lincoln logs are great as long as you're building their kind of rectalinear log-cabin-type stuff, but to build anything unconventional, like round or octagonal, you're flat out of luck.
--gary
Oliver Wong - 10 Aug 2006 19:54 GMT >> "fiziwig" <fiziwig@yahoo.com> wrote in message > [quoted text clipped - 16 lines] > editing again I have to turn around and parse out all the HTML tags and > build it back into a styled document. Or you could have a central "model" object which semantically preserves all the information you need in your document, and generate HTML like views for the Label, and the character-style like view and editor for the JTextPane.
Another thing you could try is, once the user has finished editing everything, taking a screen capture of the JTextPane, and then rotating that image, and displaying it.
- Oliver
fiziwig - 10 Aug 2006 20:04 GMT > >> "fiziwig" <fiziwig@yahoo.com> wrote in message <snip>
> Another thing you could try is, once the user has finished editing > everything, taking a screen capture of the JTextPane, and then rotating that > image, and displaying it. > > - Oliver The screen cap idea is a good one. That would be perfectly suitable. This is a page layout program for a customer who is a printer and he needs to be able to turn blocks of text on their side or upside down in doing multi-page layouts on a single very large sheet of paper. There can be literally dozens of separate JTextPanes in a single document.
I'll look into the screen cap approach. If I can figure out how to do a screen cap, that is. ;-)
Thanks,
--gary
Oliver Wong - 10 Aug 2006 20:58 GMT > I'll look into the screen cap approach. If I can figure out how to do a > screen cap, that is. ;-) See http://java.sun.com/products/java-media/jai/forDevelopers/jaifaq.html#displaybmp
- Oliver
fiziwig - 10 Aug 2006 21:52 GMT > > I'll look into the screen cap approach. If I can figure out how to do a > > screen cap, that is. ;-) [quoted text clipped - 3 lines] > > - Oliver Thanks for all your help. After days and days of pulling my hair out, the rotation finally works perfectly. Here's how I ended up doing it:
class TextPanel extends JTextPane {
// implements rotation for a JTextPane
private int rotation; private int tx, ty; private int wide, high; private BufferedImage renderedText = null;
// valid rotation values are: // 0 = no rotation // 1 = rotation 90 degree clockwise // 2 = rotation 180 degrees // 3 = rotation 90 degrees counterclockwise
TextPanel() { super(); rotation = 0; tx = 0; ty = 0; } public void setDefaultBounds( int x, int y, int width, int height) { high = height; wide = width; super.setBounds(x,y,width,height); } public void setRotation( int newRotation ) {
newRotation = newRotation % 4; if ( rotation != newRotation ) { switch (newRotation) { case 0 : tx = 0; ty = 0; break; case 1 : tx = 1; ty = 0; break; case 2 : tx = 1; ty = 1; break; case 3 : tx = 0; ty = 1; break; } if ( newRotation != 0 ) { if ( renderedText==null) { rotation = 0; // so that text is actually rendered renderedText = new BufferedImage(wide, high, BufferedImage.TYPE_INT_RGB); Graphics2D g2D = renderedText.createGraphics(); paint( g2D ); } } rotation = newRotation; // so the repaint will paint the rendered image if ((rotation%2)==0) { setSize(wide,high); } else { setSize(high,wide); }
repaint(); } } public int getRotation() { return rotation; }
public void paintComponent(Graphics g) {
if ( rotation == 0 ) { super.paintComponent(g); return; } Graphics2D g2 = (Graphics2D) g; double angle = rotation * Math.PI/2; AffineTransform tr = g2.getTransform(); if (rotation==2) { tr.setToTranslation(wide*tx,high*ty); } else { tr.setToTranslation(high*tx,wide*ty); } tr.rotate(angle); g2.drawImage(renderedText, tr, this); } }
--gary
Thomas Weidenfeller - 11 Aug 2006 09:42 GMT > public void paintComponent(Graphics g) { Override paint(), not paintComponent() but before you do so, familiarize yourself with the way paint() in Swing works. There is a TSC painting article about this.
/Thomas
 Signature The comp.lang.java.gui FAQ: ftp://ftp.cs.uu.nl/pub/NEWS.ANSWERS/computer-lang/java/gui/faq http://www.uni-giessen.de/faq/archiv/computer-lang.java.gui.faq/
Alex Hunsley - 12 Aug 2006 03:14 GMT >> public void paintComponent(Graphics g) { > > Override paint(), not paintComponent() but before you do so, familiarize > yourself with the way paint() in Swing works. There is a TSC painting > article about this. Why do you suggest doing this?
jmcgill - 12 Aug 2006 15:25 GMT >>> public void paintComponent(Graphics g) { >> [quoted text clipped - 3 lines] > > Why do you suggest doing this? I'm not convinced that's exactly what this article is saying. Maybe the OP has another TSC painting article in mind, or maybe I'm not reading this well.
http://java.sun.com/products/jfc/tsc/articles/painting/
Thomas Weidenfeller - 14 Aug 2006 09:51 GMT >>> public void paintComponent(Graphics g) { >> [quoted text clipped - 3 lines] > > Why do you suggest doing this? Reading the documentation? Because it helps to know how things work.
Overriding paint()? Because it makes sense in this particular case.
paint() calls paintComponent(), paintBorder() and paintChildren(). If you want to rotate a component it makes sense to also rotate its border and potential children. Manipulating the Graphics context in paint() would mean you can affect all three activities in a controlled way: Change it and then call super.paint(g).
On the other hand, manipulating the Graphics context in paintComponent(), and not reseting it at the end of the method, means you are relying on a particular sequence of method invocations in paint(). You are betting on an implementation detail.
/Thomas
 Signature The comp.lang.java.gui FAQ: ftp://ftp.cs.uu.nl/pub/NEWS.ANSWERS/computer-lang/java/gui/faq http://www.uni-giessen.de/faq/archiv/computer-lang.java.gui.faq/
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|