-
Bug
-
Resolution: Unresolved
-
P4
-
6u16
-
x86
-
windows_xp
FULL PRODUCT VERSION :
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
Consider the class in the source code section below.
If you run it in the form that it initially is, everything works great: the JTextArea expands to fill all of the screen except the bottom, leaving precisely enough room for the row of buttons. Cool. Click on the Refresh text button and see how even tho the JTextArea now has way more text data, no worries, as a vertical scroll bar appears and the GUI overall remains the same.
Now comment out the line
private boolean makeShortText = true;
and uncomment the line
private boolean makeShortText = false;
This change will cause the first amount of text to be many lines, instead of a few lines, as before (but note that each time that you click on the button, the number of lines is toggled).
Now when you run it, the JTextArea has ballooned in size to the point where you no longer see the buttons in the GUI (they have been pushed down below the edge of the screen). Major uncool.
This bad behavior can be fixed by uncommenting the line
jScrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
You might also be able to fix the bad behavior by setting the preferred size of the JTextArea.
Note that there appears to be a bug somewhere, most likely in GroupLayout. If leave that line
// jScrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
commented out, but comment out the line
int numLines = makeShortText ? 10 : 10*1000;
and uncomment the line
int numLines = makeShortText ? 10 : 1000;
then there is no need at all the set preferred sizes, the GUI always works.
So why does the GUI suddenly fail when the JTextArea gets really big? Note that either number of lines, 1000 or 10000, is way bigger than what my--and yours--screen can actually hold. BUT FOR SOME REASON IT IS THE REALLY BIG NUMBER, 10000, THAT CAUSES PROBLEMS.
It seems like when the pref size of the JTextArea/JScrollPane exceeds the value of a short is when the bad behavior starts. See also
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6871357
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the source code below, as described above.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// See:
// http://forums.sun.com/thread.jspa?messageID=10845836#10845836
//package xxx;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* Demonstrates issues with {@link JScrollPane} (or is it {@link GroupLayout}?).
* <p>
* Like typical Java GUI code, this class is not multithread safe:
* it expects to only be called by {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread}.
* Note, however, that the sole public API method, {@link #main main}, may be safely called by any thread.
*/
public class JScrollPaneDemo extends JFrame {
// -------------------- constants --------------------
private static final String frameTitle = "JScrollPaneDemo";
private static final long serialVersionUID = 1;
// -------------------- instance fields --------------------
private JButton refreshButton;
private JTextArea jtextArea;
// private boolean makeShortText = true;
private boolean makeShortText = false;
// -------------------- main --------------------
/**
* Creates a new {@link JScrollPaneDemo} instance.
* <p>
* This method may be called by any thread because it internally submits all of its work to {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread}.
*/
public static void main(String[] args) {
EventQueue.invokeLater( new Runnable() { public void run() {
new JScrollPaneDemo();
} } );
}
// -------------------- constructor --------------------
public JScrollPaneDemo() {
super(frameTitle);
buildGui();
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setVisible(true);
}
// -------------------- gui methods --------------------
private void buildGui() {
getContentPane().removeAll();
getContentPane().add( buildPanel() );
maximizeWindow(this);
}
private JComponent buildPanel() {
JPanel jpanel = new JPanel();
JComponent textScrollPane = buildTextScrollPane();
JComponent buttons = buildButtons();
GroupLayout layout = new GroupLayout(jpanel); // see http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html
jpanel.setLayout(layout);
layout.setAutoCreateGaps(true); // automatically add gaps between components
layout.setAutoCreateContainerGaps(true); // automatically create gaps between components that touch the edge of the container and the container.
layout.setHorizontalGroup(
layout.createParallelGroup()
.addComponent(textScrollPane)
.addComponent(buttons)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(textScrollPane)
.addComponent(buttons)
);
return jpanel;
}
private JComponent buildTextScrollPane() {
JScrollPane jscrollPane = new JScrollPane();
jscrollPane.getViewport().setView( buildTextArea() );
// jscrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
return jscrollPane;
}
private JComponent buildTextArea() {
jtextArea = new JTextArea( makeText() );
jtextArea.setEditable(false);
jtextArea.setLineWrap(false);
jtextArea.setTabSize(4);
return jtextArea;
}
private JComponent buildButtons() {
JPanel jpanel = new JPanel();
refreshButton = new JButton("Refresh text");
refreshButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
jtextArea.setText( makeText() );
jtextArea.revalidate();
}
} );
getRootPane().setDefaultButton(refreshButton);
JButton exitButton = new JButton("Exit");
exitButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JScrollPaneDemo.this.dispose();
}
} );
GroupLayout layout = new GroupLayout(jpanel); // see http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html
jpanel.setLayout(layout);
layout.setAutoCreateGaps(true); // automatically add gaps between components
layout.setAutoCreateContainerGaps(true); // automatically create gaps between components that touch the edge of the container and the container.
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addComponent(refreshButton)
.addComponent(exitButton)
);
layout.setVerticalGroup(
layout.createParallelGroup()
.addComponent(refreshButton)
.addComponent(exitButton)
);
return jpanel;
}
// -------------------- maximizeWindow --------------------
/**
* Sets the location and size of window so as to maximize its area.
* <p>
* The fundamental upper bound is the {@link Toolkit#getScreenSize size of the screen}.
* On systems with multiple displays, <i>the size of the primary display is used</i>.
* <p>
* A secondary constraint is the presence of permanent desktop items like the <a href="http://en.wikipedia.org/wiki/Taskbar">Windows Taskbar</a>.
* This method assumes that you do not want the window to cover these items, so their presence makes the effective screen size smaller.
* <p>
* Only {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread} may call this method.
* <p>
* @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
*/
public static void maximizeWindow(Window window) throws HeadlessException {
//window.setExtendedState( Frame.MAXIMIZED_BOTH ); // would love to simply call this, unfortunately, it only works for Frames, and it does not takes insets into account...
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize(); // see also: GraphicsConfiguration.getBounds() and GraphicsConfiguration.getMaximumWindowBounds()
GraphicsConfiguration gc = window.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(gc);
int windowX = insets.left;
int windowY = insets.top;
window.setLocation(windowX, windowY);
int screenWidthFree = screenSize.width - (insets.left + insets.right);
int screenHeightFree = screenSize.height - (insets.top + insets.bottom);
window.setSize(screenWidthFree, screenHeightFree);
}
// -------------------- makeText --------------------
private String makeText() {
int numLines = makeShortText ? 10 : 10*1000;
makeShortText = !makeShortText; // toggle this field so that do the opposite next time this method is called
StringBuilder sb = new StringBuilder();
for (int i = 0; i < numLines; i++) {
sb.append("Line #").append(i);
sb.append("\n");
}
return sb.toString();
}
}
---------- END SOURCE ----------
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
Consider the class in the source code section below.
If you run it in the form that it initially is, everything works great: the JTextArea expands to fill all of the screen except the bottom, leaving precisely enough room for the row of buttons. Cool. Click on the Refresh text button and see how even tho the JTextArea now has way more text data, no worries, as a vertical scroll bar appears and the GUI overall remains the same.
Now comment out the line
private boolean makeShortText = true;
and uncomment the line
private boolean makeShortText = false;
This change will cause the first amount of text to be many lines, instead of a few lines, as before (but note that each time that you click on the button, the number of lines is toggled).
Now when you run it, the JTextArea has ballooned in size to the point where you no longer see the buttons in the GUI (they have been pushed down below the edge of the screen). Major uncool.
This bad behavior can be fixed by uncommenting the line
jScrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
You might also be able to fix the bad behavior by setting the preferred size of the JTextArea.
Note that there appears to be a bug somewhere, most likely in GroupLayout. If leave that line
// jScrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
commented out, but comment out the line
int numLines = makeShortText ? 10 : 10*1000;
and uncomment the line
int numLines = makeShortText ? 10 : 1000;
then there is no need at all the set preferred sizes, the GUI always works.
So why does the GUI suddenly fail when the JTextArea gets really big? Note that either number of lines, 1000 or 10000, is way bigger than what my--and yours--screen can actually hold. BUT FOR SOME REASON IT IS THE REALLY BIG NUMBER, 10000, THAT CAUSES PROBLEMS.
It seems like when the pref size of the JTextArea/JScrollPane exceeds the value of a short is when the bad behavior starts. See also
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6871357
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the source code below, as described above.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// See:
// http://forums.sun.com/thread.jspa?messageID=10845836#10845836
//package xxx;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
* Demonstrates issues with {@link JScrollPane} (or is it {@link GroupLayout}?).
* <p>
* Like typical Java GUI code, this class is not multithread safe:
* it expects to only be called by {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread}.
* Note, however, that the sole public API method, {@link #main main}, may be safely called by any thread.
*/
public class JScrollPaneDemo extends JFrame {
// -------------------- constants --------------------
private static final String frameTitle = "JScrollPaneDemo";
private static final long serialVersionUID = 1;
// -------------------- instance fields --------------------
private JButton refreshButton;
private JTextArea jtextArea;
// private boolean makeShortText = true;
private boolean makeShortText = false;
// -------------------- main --------------------
/**
* Creates a new {@link JScrollPaneDemo} instance.
* <p>
* This method may be called by any thread because it internally submits all of its work to {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread}.
*/
public static void main(String[] args) {
EventQueue.invokeLater( new Runnable() { public void run() {
new JScrollPaneDemo();
} } );
}
// -------------------- constructor --------------------
public JScrollPaneDemo() {
super(frameTitle);
buildGui();
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
setVisible(true);
}
// -------------------- gui methods --------------------
private void buildGui() {
getContentPane().removeAll();
getContentPane().add( buildPanel() );
maximizeWindow(this);
}
private JComponent buildPanel() {
JPanel jpanel = new JPanel();
JComponent textScrollPane = buildTextScrollPane();
JComponent buttons = buildButtons();
GroupLayout layout = new GroupLayout(jpanel); // see http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html
jpanel.setLayout(layout);
layout.setAutoCreateGaps(true); // automatically add gaps between components
layout.setAutoCreateContainerGaps(true); // automatically create gaps between components that touch the edge of the container and the container.
layout.setHorizontalGroup(
layout.createParallelGroup()
.addComponent(textScrollPane)
.addComponent(buttons)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(textScrollPane)
.addComponent(buttons)
);
return jpanel;
}
private JComponent buildTextScrollPane() {
JScrollPane jscrollPane = new JScrollPane();
jscrollPane.getViewport().setView( buildTextArea() );
// jscrollPane.setPreferredSize( Toolkit.getDefaultToolkit().getScreenSize() );
return jscrollPane;
}
private JComponent buildTextArea() {
jtextArea = new JTextArea( makeText() );
jtextArea.setEditable(false);
jtextArea.setLineWrap(false);
jtextArea.setTabSize(4);
return jtextArea;
}
private JComponent buildButtons() {
JPanel jpanel = new JPanel();
refreshButton = new JButton("Refresh text");
refreshButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
jtextArea.setText( makeText() );
jtextArea.revalidate();
}
} );
getRootPane().setDefaultButton(refreshButton);
JButton exitButton = new JButton("Exit");
exitButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JScrollPaneDemo.this.dispose();
}
} );
GroupLayout layout = new GroupLayout(jpanel); // see http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html
jpanel.setLayout(layout);
layout.setAutoCreateGaps(true); // automatically add gaps between components
layout.setAutoCreateContainerGaps(true); // automatically create gaps between components that touch the edge of the container and the container.
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addComponent(refreshButton)
.addComponent(exitButton)
);
layout.setVerticalGroup(
layout.createParallelGroup()
.addComponent(refreshButton)
.addComponent(exitButton)
);
return jpanel;
}
// -------------------- maximizeWindow --------------------
/**
* Sets the location and size of window so as to maximize its area.
* <p>
* The fundamental upper bound is the {@link Toolkit#getScreenSize size of the screen}.
* On systems with multiple displays, <i>the size of the primary display is used</i>.
* <p>
* A secondary constraint is the presence of permanent desktop items like the <a href="http://en.wikipedia.org/wiki/Taskbar">Windows Taskbar</a>.
* This method assumes that you do not want the window to cover these items, so their presence makes the effective screen size smaller.
* <p>
* Only {@link EventQueue}'s {@link EventQueue#isDispatchThread dispatch thread} may call this method.
* <p>
* @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true
*/
public static void maximizeWindow(Window window) throws HeadlessException {
//window.setExtendedState( Frame.MAXIMIZED_BOTH ); // would love to simply call this, unfortunately, it only works for Frames, and it does not takes insets into account...
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize(); // see also: GraphicsConfiguration.getBounds() and GraphicsConfiguration.getMaximumWindowBounds()
GraphicsConfiguration gc = window.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(gc);
int windowX = insets.left;
int windowY = insets.top;
window.setLocation(windowX, windowY);
int screenWidthFree = screenSize.width - (insets.left + insets.right);
int screenHeightFree = screenSize.height - (insets.top + insets.bottom);
window.setSize(screenWidthFree, screenHeightFree);
}
// -------------------- makeText --------------------
private String makeText() {
int numLines = makeShortText ? 10 : 10*1000;
makeShortText = !makeShortText; // toggle this field so that do the opposite next time this method is called
StringBuilder sb = new StringBuilder();
for (int i = 0; i < numLines; i++) {
sb.append("Line #").append(i);
sb.append("\n");
}
return sb.toString();
}
}
---------- END SOURCE ----------