steSize(w,h) doesn't change displayed sizes

After setSize(w,h) both getWidth() and setHeight() return the proper numbers but visibly on the screen, the dimensions do not change. Code, extracted from the live ap, is compilable.

Using the code below, and clicking "Rotate" button demonstrates this. System.out.println() shows that the sizes have changed internally, but visibly, they do not change. (also nothing shows up at all until the first time "Rotate" is clicked. That, the wrongly clipped border, and the messed up rotation clipping and text placement are separate issues.)

What's wrong here? FWIW: Nearly identical code applied to a JLabel with an image works perfectly. If it works correctly on a JLabel why does it mess up so badly on a JTextPane?

Java is a cool language, but as an old C++ programmer I also find it deeply mysterious (and mystifying) at times.

Thanks in advance for any clues or hints,

--gary

import javax.swing.*;

import javax.swing.text.*;

import javax.swing.border.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

publicclass Rotateextends 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.white);

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(){

publicvoid actionPerformed( ActionEvent e ){

textPane.setRotation(textPane.getRotation()+1);

}

});

toolBar.add( rotateButton );

return toolBar;

}

privatestaticvoid 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);

}

publicstaticvoid main(String[] args){

//Schedule a job for the event-dispatching thread:

//creating and showing this application's GUI.

javax.swing.SwingUtilities.invokeLater(new Runnable(){

publicvoid run(){

createAndShowGUI();

}

});

}

}

class TextPanelextends JTextPane{

// implements rotation for a JTextPane

privateint rotation;

privateint tx, ty;

privateint wide, high;

// valid rotation values are:

// 0 = no rotation

// 1 = rotation 90 degree clockwise

// 2 = rotation 180 degrees

// 3 = rotation 90 degrees counterclockwise

TextPanel(){

rotation = 0;

tx = 0;

ty = 0;

}

publicvoid setDefaultBounds(int x,int y,int width,int height){

high = height;

wide = width;

super.setBounds(x,y,width,height);

}

publicvoid 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();

System.out.println("Rotation="+rotation+" Width="+getWidth()+" Height="+getHeight());

}

publicint getRotation(){return rotation;}

publicvoid 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(h*tx,w*ty);

tr.rotate(angle);

g2.setTransform(tr);

super.paintComponent(g);

}

}

[9501 byte] By [fiziwiga] at [2007-10-3 2:20:08]
# 1

> After setSize(w,h) both getWidth() and setHeight()

> return the proper numbers but visibly on the screen,

> the dimensions do not change. Code, extracted from

> the live ap, is compilable.

>

> Using the code below, and clicking "Rotate" button

> demonstrates this. System.out.println() shows that

> the sizes have changed internally, but visibly, they

> do not change. (also nothing shows up at all until

> the first time "Rotate" is clicked. That, the wrongly

> clipped border, and the messed up rotation clipping

> and text placement are separate issues.)

>

> What's wrong here? FWIW: Nearly identical code

> applied to a JLabel with an image works perfectly. If

> it works correctly on a JLabel why does it mess up so

> badly on a JTextPane?

Because a JTextPane is at least an order of magnitude more complex than a JLabel.

Even you you got it to paint itself rotated, things such as mouse events, highlighting and selection wouldn't work properly.

The paintComponent method does exactly what it says: paint the component. It does not participate in any of the behavior of what it paints.

Niceguy1a at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 2

Yes, I understand that there are additional issues with mouse events, text selection, etc. Those are a separate issues which can be addressed by making the text area non-editable except in the upright position. I simply need to be able to display it rotated, with all the proper fonts and size, bold, italic, etc. attributes.

My question is why does the JTextPane not rotate and clip properly. That question is quite independant of the other issues.

Granted, the JTextPane is orders of magnitude more complex than the JLabel, but does that imply that it can't be rotated with Affine Transforms? Complex or not, it should be rotatable.

Yes I understand that paintComponent does not participate in the behavior of what is painted. But painting, in a rotated position, is ALL I want it to do, so that's fine.

--gary

fiziwiga at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 3

Heres a tutorial that has an example of rotating text. Maybe it will give you some ideas. [url http://java.sun.com/docs/books/tutorial/2d/display/transforming.html]Transforming Text, Shapes and Images[/url].

You could always create an image of the text pane using code like:

BufferedImage image = new BufferedImage(?width, ?height, BufferedImage.TYPE_INT_RGB);

Graphics2D g2d = image.createGraphics();

textComponent.paint( g2d );

g2d.dispose();

camickra at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 4

Thanks for the suggestion of creating an image.

Thanks for the tutorial link, I covered that tutorial a few weeks ago. I am successfully rotating text and images in JLabels, and the text in the JTextPane IS rotating, it's just that the clipping and borders are all messed up, even though the same code works for JLabels.

Is there a way to get from a JTextPane to a TextLayout object? It appears I could rotate the TextLayout object instead without too much trouble, I just don't see how to get the contents of my JTextPane, with all it's internal formatting information, into a TextLayout object.

Thanks,

--gary

fiziwiga at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 5

> I am successfully rotating text and images in JLabels

Which is why I suggested creating an image of the text pane. The next step would be to add the image to a JLabel.

> It appears I could rotate the TextLayout object instead ...

I'm assuming you have different fonts, bolding and colors, so I agree, I don't know how to easily get all that information to build your own TextLayout. The information is contained in an Element object. From the Document you can use the getDefaultRootElement and start iterating through each element. Each element contains an AttributeSet. Don't know if you can use this directly with the TextLayout or not.

camickra at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 6

I spent a few mintues playing with it. I tore up some of the code but I think it works better. It will still need some tweaking:

import java.awt.Color;

import java.awt.Dimension;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.BorderFactory;

import javax.swing.JButton;

import javax.swing.JComponent;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JTextPane;

import javax.swing.JToolBar;

import javax.swing.text.BadLocationException;

import javax.swing.text.StyledDocument;

public class Rotate2 extends JPanel{

private TextPanel textPane;

public Rotate2() {

JToolBar toolBar = buildToolbar();

add(toolBar);

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.");

}

textPane.setBorder(BorderFactory.createLineBorder( Color.red ));

add(textPane);

textPane.setPreferredSize(new Dimension(100, 100));

}

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);

revalidate();

}

});

toolBar.add( rotateButton );

return toolBar;

}

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();

}

});

}

private static void createAndShowGUI() {

JFrame frame = new JFrame("Rotate");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Create and set up the content pane.

JComponent newContentPane = new Rotate2();

newContentPane.setOpaque(true); //content panes must be opaque

frame.setContentPane(newContentPane);

//Display the window.

frame.pack();

frame.setVisible(true);

}

class TextPanel extends JTextPane {

// implements rotation for a JTextPane

private int rotation;

private int tx, ty;

// valid rotation values are:

// 0 = no rotation

// 1 = rotation 90 degree clockwise

// 2 = rotation 180 degrees

// 3 = rotation 90 degrees counterclockwise

TextPanel() {

rotation = 0;

tx = 0;

ty = 0;

}

public void setRotation( int newRotation ) {

rotation = newRotation % 4;

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;

g2.rotate(angle);

g2.translate(ty * getWidth(), tx* getHeight());

super.paintComponent(g);

}

}

}

zadoka at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 7

Thanks.

As far as I can tell this looks just the same when the text pane is wider than it is tall, rather than square as you've made it.

Try replacing

textPane.setPreferredSize(new Dimension(100, 100));

with

textPane.setPreferredSize(new Dimension(200, 100));

and you'll see what I mean. The clipping and orientation of the pane is wrong, even though the text is rotated. The stament to swap width for height on odd rotations that you removed needs to be there, even though it just doesn't work the way it's supposed to. You can only get away with removing that with a square component, and in my ap the components are rarely square.

Also, in the real ap the whole thing needs to be a JLayeredPane, which I noticed you removed. The JLayeredPane has a different default Layout, and I do need the layers in the actual ap.

--gary

fiziwiga at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 8
I didn't know the JLayeredPane was a requirement, I just thought it would make things easier if it wasn't there.What type of app is this that things need to be rotated 90 or 180 degrees and the textpane's can't be square? Is everything rotating or specific pieces?
zadoka at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...
# 9

Hi,

It's a multi-page sheet layout program for a client who is a printer. There are possibly dozens of separate JLabels with images and JTextPanes that can each be individually sized (with resizing handles) and moved by drag-and-drop to anywhere on the master sheet. Some might be story headlines, text body, clip art, title banners, etc. etc., that go to make up a full newsletter page. Then as many as 16 pages might be arranged on a single very large sheet of paper, represented by the JLayeredPane which is scrollable and expandable to just about any sheet size you could imagine..

For multi-page layout it is often required that some of the pages be placed upside down on the large sheet of paper, and for certain newsletter layouts postal addresses are laid out sideways on one edge so that when the newsletter sheet is cut and folded they are all oriented properly.

Anyway, I did finally solve the problem. This works perfectly, as long as the text does not need to be editable while in the rotated position. Basically, I take a screen shot of the rendered text and just rotate the BufferedImage containing that screen shot. When it's unrotated then it again becomes a normal JTextPane and can be edited.

Thanks for your help and suggestions.

--gary

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);

}

}

fiziwiga at 2007-7-14 19:19:02 > top of Java-index,Java Essentials,Java Programming...