-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
1.2.0, 1.2.1
-
generic, x86
-
generic, windows_nt
tered(MouseEvent e) {
//OLD: removeFromSource();
// These events are generated a lot. This removeFromSource() call
// had to be removed because it came ahead of the mouse released
// and thus the mouse release would not be passed to the
// editor Component.
}
public void mouseExited(MouseEvent e) {
//OLD: removeFromSource();
// same as in mouseEntered()
}
public void mouseDragged(MouseEvent e) {
// This new code is needed to forward the drag events.
if(destination != null) //NEW
destination.dispatchEvent(SwingUtilities.convertMouseEvent //NEW
(source, e, destination)); //NEW
//OLD: removeFromSource();
// Similar to as in mouseEntered(), but we want to keep getting these
// so we forward all drags until the mouse is released.
}
public void mouseMoved(MouseEvent e) {
//OLD: removeFromSource();
// same as in mouseEntered() but we probably never
// want these events anyway
}
protected void removeFromSource() {
if(source != null) {
source.removeMouseListener(this);
source.removeMouseMotionListener(this);
}
source = destination = null;
}
} // End of class MouseInputHandler
} // End of class FixedTreeUI
} // end of class ScrollBarBug
(Review ID: 57711)
======================================================================
Name: skT88420 Date: 06/21/99
Under several (possibly unrelated) circumstances the arrow buttons
on the JScrollbar can get stuck, and stay stuck till the thumb of the
JScrollbar reaches the min or max position, thus
preventing the user
from taking any action until the
scrollbar gets unstuck.
1) The first case is easy to reproduce. Simply create two
JInternalFrames in the SwingSet demo, and arrange them so
that
you can click the arrow button on the inactive window.
When
you click the arrow button on the inactive window the
button will become stuck, and the scrollbar will scroll to the
accross it's whole length.
2) The second case is more important and more difficult to
reproduce.
In our application when we open a model or two large text
files if you press the arrow button for a second or two it will
get stuck for a long time, often enough time to get to the min
or max, but not always, especially with a very large text file.
Unfortunately I haven't been able to duplicate this in a test
case. The test case below only has the scroll bars scrolling
a few extra steps if you hold the scroll arrow button down
for a few seconds.
The factors that we thought might contribute are:
a) paint() time
b) amount of event activity in the event thread
c) whether the Timer is actually coalescing the events
but haven't been able to find what is the contributing factor
in our application. This is a very serious bug for us, and we will
certainly cooperate with you in any way that will get this
resolved in a timely manner.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.event.*;
public class Bug extends JFrame
implements ActionListener
{
DesktopManager fDesktopManager;
JDesktopPane fDesktop;
public Bug(String argv[])
{
setLookAndFeel(null); //null gives default for os
validate();
this.setTitle("Bug");
//Desktop manager
fDesktopManager = new DefaultDesktopManager();
//Desktop pane
fDesktop = new JDesktopPane();
fDesktop.setDesktopManager(fDesktopManager);
this.getContentPane().add (fDesktop, BorderLayout.CENTER);
// fDesktop.setOpaque (true);
//Menu bar
JMenuBar mainMenu = new JMenuBar();
JMenu fileMenu = new JMenu("New Frame");
JMenuItem mainMenuItem = new JMenuItem();
JMenuItem mainMenuItem2 = new JMenuItem();
mainMenuItem.setText("JInternalFrame");
mainMenuItem.setActionCommand("newJInternalFrame");
mainMenuItem.addActionListener(this);
mainMenuItem2.setText("JFrame");
mainMenuItem2.setActionCommand("newJFrame");
mainMenuItem2.addActionListener(this);
fileMenu.add(mainMenuItem);
fileMenu.add(mainMenuItem2);
mainMenu.add(fileMenu);
this.setJMenuBar(mainMenu);
this.setSize(600,480);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
// respond to menu items being selected
String s = e.getActionCommand();
if(s.equals("newJFrame"))
{
Frame frame = new Frame();
frame.setBounds (0,0,400,400);
frame.show();
} //if
else if(s.equals("newJInternalFrame")){
InternalFrame iframe = new InternalFrame();
iframe.pack();
iframe.setBounds (0,0,400,400);
fDesktop.add(iframe, JLayeredPane.DEFAULT_LAYER);
try {
iframe.setSelected (true);
} catch (java.beans.PropertyVetoException exeption) {}
} // else if
else
{
System.out.println("Yippee! " + s + " performed.");
} //else
}//actionPerformed
private static void setLookAndFeel(String pOsName)
{
String osName = pOsName;
if(null == osName){
osName = System.getProperty ("os.name");
}//if
LookAndFeel appLookAndFeel;
if (osName.indexOf ("Windows") >= 0) {
appLookAndFeel= new com.sun.java.swing.plaf.windows.WindowsLookAndFeel();
System.out.println("Windows look 'n feel selected");
}
else if (osName.indexOf ("Solaris") >= 0){
appLookAndFeel= new com.sun.java.swing.plaf.motif.MotifLookAndFeel();
System.out.println("Solaris look 'n feel selected");
}
else {
appLookAndFeel= new javax.swing.plaf.metal.MetalLookAndFeel();
System.out.println("Metal look 'n feel selected");
}
try {
UIManager.setLookAndFeel(appLookAndFeel);
} catch (UnsupportedLookAndFeelException e) {}
} // setLookAndFeel
public static void main(String argv[])
{
Bug app = new Bug(argv);
}//main
}
class Frame extends JFrame
{
Focusable fFocusable;
JScrollPane fPane;
public Frame()
{
fFocusable = new Focusable();
fFocusable.setBackground(Color.blue);
fPane = new JScrollPane (fFocusable);
this.getContentPane().add(fPane);
}
}
class InternalFrame extends JInternalFrame
{
Focusable fFocusable;
JScrollPane fPane;
public InternalFrame()
{
super("An Internal Frame",true,true,true,true);
fFocusable = new Focusable();
fFocusable.setBackground(Color.red);
fPane = new JScrollPane(fFocusable);
this.getContentPane().add(fPane);
pack();
}
}
class Thing
{
public Color fColor;
public Point fPoint;
public Thing(Point p, Color c)
{
fPoint = p;
fColor = c;
}
}
class Focusable
extends JComponent
implements Scrollable, ActionListener
{
private Vector fTimer;
private Random fRandom;
private Vector fThings;
static private int RECT = 10;
public Focusable ()
{
fTimer = new Vector();
for(int i = 0; i < 50; ++i){
Timer t = new Timer(100, this);
t.start();
fTimer.add(t);
}
fRandom = new Random();
fThings = new Vector();
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
setPreferredSize(new Dimension(900,900));
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e)
{
Color color = new Color(100, 0, 255);
fThings.add(new Thing(e.getPoint(),color));
repaint();
}
});
}
public void paint(Graphics g)
{
System.out.println("paint");
for(int i = 0; i < 20; ++i){
System.out.println("i = " + i);
g.setColor(Color.black);
Rectangle r = getVisibleRect();
g.fillRect(r.x,r.y,r.width,r.height);
for(int j = 0; j < fThings.size(); ++j){
Thing t = (Thing)fThings.get(j);
Point p = t.fPoint;
g.setColor(t.fColor);
g.fillRect(p.x,p.y,RECT,RECT);
}
}
}
public void actionPerformed(ActionEvent e)
{
System.out.println("Action performed by " + e.getSource());
}
//////////////////////////////////////////////////////////////////
// i m p l e m e n t s S c r o l l a b l e
//////////////////////////////////////////////////////////////////
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
} // getPreferredScrollableViewportSize
// Scroll 1/10 of the visible area.
//
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction)
{
return (int) Math.rint (((orientation == SwingConstants.VERTICAL)
? visibleRect.getHeight() : visibleRect.getWidth()) / 10);
} // getScrollableUnitIncrement
// Scroll 9/10 of the visible area.
//
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction)
{
return (int) Math.rint (9* ((orientation == SwingConstants.VERTICAL)
? visibleRect.getHeight() : visibleRect.getWidth()) / 10);
} // getScrollableBlockIncrement
public boolean getScrollableTracksViewportHeight()
{
return false;
} // getScrollableTracksViewportHeight
public boolean getScrollableTracksViewportWidth()
{
return false;
} // getScrollableTracksViewportWidth
}
(Review ID: 54375)
======================================================================
Name: dbT83986 Date: 05/01/99
/*
Specifically the problems that can be seen when a tree cell
editor involves a scrollbar are as follows (a simple javac
and java will compile and run this demo):
1. initial click on a scroll arrow causes it to scroll
non-stop to end of scrollbar when it should make
one small increment
2. initial click in the scroller area beside thumb
control keeps block scrolling up to the point
of the click when it should jump only one block
3. initial click on thumb control is lost (not forwarded
to the scrollbar) such that it cannot be grabbed on
that first click and dragged as should be possible
Note, this is all on the very FIRST click that brings in the
editor component where there was none before. Once the
component is on the screen, all behaves as it should. The point
of MouseInputHandler is to pass all mouse manipulation to
the editor component as expected to fulfill the illusion of
the editor being on the screen at all times even though it isn't.
THE BOTTOM TREE CELL IS THE BEST ILLUSTRATION OF
BEHAVIORS 1 AND 2 IN THE JTextArea VERSION.
The cause of behaviors 1 and 2 is that the MouseInputHandler in
BasicTreeUI ends its forwarding too soon such that the
mouse released event is not being forwarded. Thus it behaves
as though the user never lets go of the mouse button. This
explains why the arrow stays "depressed" looking.
Behavior 3 is from not forwarding drag events at all
when it should.
TO SEE THE BAD BEHAVIOR: set the useFixedTreeUI variable to false
TO SEE THE FIX IN ACTION: set useFixedTreeUI to true
OPTIONAL: set useJustScrollBars to true to see a scroll
bar only verison of the demo, set it to false
to use a JTextArea editor in a scroll pane
FIX NOTES: The fix is kind of big because overriding here is hard.
Ultimately javax.swing needs repair. See the comments
at completeEditing() and in MouseInputHandler for details
of why the fix is so large and what exactly was fixed.
*/
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
public class ScrollBarBug extends JFrame {
static final boolean useFixedTreeUI = false;
static final boolean useJustScrollBars = false;
public ScrollBarBug() {
super("ScrollBar Bug");
String shortString = "short string";
String longString = "long string to illustrate what a long line of test looks like in the tree in case it matters but this has no newlines";
String multiNoScrollString = "NO SCROLL\nshort strings, mulitline\nshort strings, mulitline";
String multiHorizScrollString = "HORIZ SCROLL ONLY\nlong strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\nlong strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)";
String multiVertScrollString = "VERT SCROLL ONLY\n2short strings, mulitline\n3short strings, mulitline\n4short strings, mulitline\n5short strings, mulitline\n6short strings, mulitline\n7short strings, mulitline\n8end";
String multiBothScrollString = "BOTH SCROLL\n2long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n3long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n4long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n5long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n6long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n7long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n8end long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n";
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode(multiBothScrollString);
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode(multiHorizScrollString);
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode(multiVertScrollString);
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode(multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString);
node1.add(node2);
node1.add(node3);
node1.add(node4);
JTree tree = new Tree(node1);
tree.setEditable(true);
tree.setShowsRootHandles(true);
tree.setCellRenderer(new RendererEditor());
tree.setCellEditor(new RendererEditor());
getContentPane().add(tree, BorderLayout.CENTER);
}
public static void main(String args[]) {
ScrollBarBug frame = new ScrollBarBug();
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} });
frame.setSize(800, 500);
frame.setVisible(true);
}
class RendererEditor implements TreeCellRenderer, TreeCellEditor {
JScrollPane sp;
JTextArea ta;
JScrollBar sb;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (useJustScrollBars)
return getNewJScrollBar();
else
return getNewJScrollPane(value.toString());
}
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
if (useJustScrollBars)
return getNewJScrollBar();
else
return getNewJScrollPane(value.toString());
}
public Object getCellEditorValue() { return "we don't really edit the text of the cell"; }
public boolean isCellEditable(EventObject anEvent) { return true; }
public boolean shouldSelectCell(EventObject anEvent) { return true; }
public boolean stopCellEditing() { return true; }
public void cancelCellEditing() { }
public void addCellEditorListener(CellEditorListener l) { }
public void removeCellEditorListener(CellEditorListener l) { }
private JScrollPane getNewJScrollPane(final String text) {
sp = new JScrollPane();
ta = new JTextArea() ;
ta.setEditable(false);
ta.setRows(5);
ta.setColumns(40);
ta.setLineWrap(false);
// Making the textarea uneditable or non-enabled doesn't work
// Only setting the caret to the text area start
// results in NOT jerking the area to the lower-right (the end).
// Here we assume the user is going to edit it.
//
// ta.setText(text);
// ta.setCaretPosition(0);
//
// But wait. After fixing the forwarding of mouse events
// to the scrollbar (broken in BasicTreeUI ), it became
// clear that setText() and/or setCaretPosition() in any
// combination results in all kinds of caret adjustment "safely"
// done later. But, the mouse events are done NOW so the later
// caret adjustments negate the initial mouse click on the
// editor even though it is forwarded correctly.
//
// So, fix up a document with the text FIRST before it knows
// its JTextArea to notify. So, the caret adjustments never
// happen and are not needed. Then, add the document and all is well.
//
// Comment this out to see the bad behavior. Comment this
// out and uncomment out the setText() and setCaretPosition()
// to see the click negation effect of doing things "later."
PlainDocument doc = new PlainDocument();
try { doc.insertString(0, text, null); } catch (BadLocationException e) { }
ta.setDocument(doc);
sp.getViewport().add(ta);
return sp;
} // end of getNewJScrollPane()
private JScrollBar getNewJScrollBar() {
sb = new JScrollBar(JScrollBar.HORIZONTAL, 50, 10, 0, 200);
sb.setPreferredSize(new Dimension(150, 30));
sb.setUnitIncrement(5);
sb.setBlockIncrement(10);
return sb;
}
} // end of class RendererEditor
class Tree extends JTree {
public Tree(TreeNode root) {
//super(root, false);
super(new DefaultTreeModel(root, false));
}
public void updateUI() {
if (useFixedTreeUI)
setUI(new FixedTreeUI());
else
setUI((TreeUI)UIManager.getUI(this));
invalidate();
}
}
// This is the fixed UI for the tree that contains
// the fixed MouseInputHandler.
class FixedTreeUI extends MetalTreeUI {
// This is completeEditing() verbatum from BasicTreeUI.
// It had to be done that way because usually there
// is a way to override some sort of getMouseInputHandler()
// function to slip in one's own handler. But, in this
// case it is hardcoded and the only way to repalce it
// is to in turn replace this function. This function is
// ok as is and otherwise would not be overridden.
protected void completeEditing(boolean messageStop,
boolean messageCancel,
boolean messageTree) {
if(stopEditingInCompleteEditing && editingComponent != null) {
Component oldComponent = editingComponent;
TreePath oldPath = editingPath;
TreeCellEditor oldEditor = cellEditor;
Object newValue = oldEditor.getCellEditorValue();
Rectangle editingBounds = getPathBounds(tree,
editingPath);
boolean requestFocus = (tree != null &&
(tree.hasFocus() || SwingUtilities.
findFocusOwner(editingComponent) != null));
editingComponent = null;
editingPath = null;
if(messageStop)
oldEditor.stopCellEditing();
else if(messageCancel)
oldEditor.cancelCellEditing();
tree.remove(oldComponent);
if(editorHasDifferentSize) {
treeState.invalidatePathBounds(oldPath);
updateSize();
}
else {
editingBounds.x = 0;
editingBounds.width = tree.getSize().width;
tree.repaint(editingBounds);
}
if(requestFocus)
tree.requestFocus();
if(messageTree)
treeModel.valueForPathChanged(oldPath, newValue);
}
}
/**
* Will start editing for node if there is a cellEditor and
* shouldSelectCell returns true.<p>
* This assumes that path is valid and visible.
*/
protected boolean startEditing(TreePath path, MouseEvent event) {
completeEditing();
if(cellEditor != null && tree.isPathEditable(path)) {
int row = getRowForPath(tree, path);
editingComponent = cellEditor.getTreeCellEditorComponent
(tree, path.getLastPathComponent(),
tree.isPathSelected(path), tree.isExpanded(path),
treeModel.isLeaf(path.getLastPathComponent()), row);
if(cellEditor.isCellEditable(event)) {
Rectangle nodeBounds = getPathBounds(tree, path);
editingRow = row;
Dimension editorSize = editingComponent.getPreferredSize();
// Only allow odd heights if explicitly set.
if(editorSize.height != nodeBounds.height &&
getRowHeight() > 0)
editorSize.height = getRowHeight();
if(editorSize.width != nodeBounds.width ||
editorSize.height != nodeBounds.height) {
// Editor wants different width or height, invalidate
// treeState and relayout.
editorHasDifferentSize = true;
treeState.invalidatePathBounds(path);
updateSize();
}
else
editorHasDifferentSize = false;
tree.add(editingComponent);
editingComponent.setBounds(nodeBounds.x, nodeBounds.y,
editorSize.width,
editorSize.height);
editingPath = path;
editingComponent.validate();
Rectangle visRect = tree.getVisibleRect();
tree.paintImmediately(nodeBounds.x, nodeBounds.y,
visRect.width + visRect.x - nodeBounds.x,
editorSize.height);
if(cellEditor.shouldSelectCell(event)) {
stopEditingInCompleteEditing = false;
try {
tree.setSelectionRow(row);
} catch (Exception e) {
System.err.println("Editing exception: " + e);
}
stopEditingInCompleteEditing = true;
}
if(event != null && event instanceof MouseEvent) {
/* Find the component that will get forwarded all the
mouse events until mouseReleased. */
Point componentPoint = SwingUtilities.convertPoint
(tree, new Point(event.getX(), event.getY()),
editingComponent);
/* Create an instance of BasicTreeMouseListener to handle
passing the mouse/motion events to the necessary
component. */
new MouseInputHandler(tree, SwingUtilities
.getDeepestComponentAt(editingComponent,
componentPoint.x, componentPoint.y),
event);
}
return true;
}
else
editingComponent = null;
}
return false;
} // end of completeEditing() taken from BasicTreeUI
// This is where the bugs are fixed. This inner class
// is from BasicTreeUI. But the one there seems to simply
// be unfinished mostly, but incorrect a little bit.
// The old code is commented out with //OLD:
// and the new code lines added to fix it are tagged
// with //NEW at the end of each line.
public class MouseInputHandler extends Object implements
MouseInputListener
{
/** Source that events are coming from. */
protected Component source;
/** Destination that recieves all events. */
protected Component destination;
public MouseInputHandler(Component source, Component destination,
MouseEvent event){
this.source = source;
this.destination = destination;
this.source.addMouseListener(this);
this.source.addMouseMotionListener(this);
/* Dispatch the editing event! */
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, event, destination));
}
public void mouseClicked(MouseEvent e) {
if(destination != null)
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, e, destination));
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
if(destination != null)
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, e, destination));
removeFromSource();
}
public void mouseEn
//OLD: removeFromSource();
// These events are generated a lot. This removeFromSource() call
// had to be removed because it came ahead of the mouse released
// and thus the mouse release would not be passed to the
// editor Component.
}
public void mouseExited(MouseEvent e) {
//OLD: removeFromSource();
// same as in mouseEntered()
}
public void mouseDragged(MouseEvent e) {
// This new code is needed to forward the drag events.
if(destination != null) //NEW
destination.dispatchEvent(SwingUtilities.convertMouseEvent //NEW
(source, e, destination)); //NEW
//OLD: removeFromSource();
// Similar to as in mouseEntered(), but we want to keep getting these
// so we forward all drags until the mouse is released.
}
public void mouseMoved(MouseEvent e) {
//OLD: removeFromSource();
// same as in mouseEntered() but we probably never
// want these events anyway
}
protected void removeFromSource() {
if(source != null) {
source.removeMouseListener(this);
source.removeMouseMotionListener(this);
}
source = destination = null;
}
} // End of class MouseInputHandler
} // End of class FixedTreeUI
} // end of class ScrollBarBug
(Review ID: 57711)
======================================================================
Name: skT88420 Date: 06/21/99
Under several (possibly unrelated) circumstances the arrow buttons
on the JScrollbar can get stuck, and stay stuck till the thumb of the
JScrollbar reaches the min or max position, thus
preventing the user
from taking any action until the
scrollbar gets unstuck.
1) The first case is easy to reproduce. Simply create two
JInternalFrames in the SwingSet demo, and arrange them so
that
you can click the arrow button on the inactive window.
When
you click the arrow button on the inactive window the
button will become stuck, and the scrollbar will scroll to the
accross it's whole length.
2) The second case is more important and more difficult to
reproduce.
In our application when we open a model or two large text
files if you press the arrow button for a second or two it will
get stuck for a long time, often enough time to get to the min
or max, but not always, especially with a very large text file.
Unfortunately I haven't been able to duplicate this in a test
case. The test case below only has the scroll bars scrolling
a few extra steps if you hold the scroll arrow button down
for a few seconds.
The factors that we thought might contribute are:
a) paint() time
b) amount of event activity in the event thread
c) whether the Timer is actually coalescing the events
but haven't been able to find what is the contributing factor
in our application. This is a very serious bug for us, and we will
certainly cooperate with you in any way that will get this
resolved in a timely manner.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.event.*;
public class Bug extends JFrame
implements ActionListener
{
DesktopManager fDesktopManager;
JDesktopPane fDesktop;
public Bug(String argv[])
{
setLookAndFeel(null); //null gives default for os
validate();
this.setTitle("Bug");
//Desktop manager
fDesktopManager = new DefaultDesktopManager();
//Desktop pane
fDesktop = new JDesktopPane();
fDesktop.setDesktopManager(fDesktopManager);
this.getContentPane().add (fDesktop, BorderLayout.CENTER);
// fDesktop.setOpaque (true);
//Menu bar
JMenuBar mainMenu = new JMenuBar();
JMenu fileMenu = new JMenu("New Frame");
JMenuItem mainMenuItem = new JMenuItem();
JMenuItem mainMenuItem2 = new JMenuItem();
mainMenuItem.setText("JInternalFrame");
mainMenuItem.setActionCommand("newJInternalFrame");
mainMenuItem.addActionListener(this);
mainMenuItem2.setText("JFrame");
mainMenuItem2.setActionCommand("newJFrame");
mainMenuItem2.addActionListener(this);
fileMenu.add(mainMenuItem);
fileMenu.add(mainMenuItem2);
mainMenu.add(fileMenu);
this.setJMenuBar(mainMenu);
this.setSize(600,480);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
// respond to menu items being selected
String s = e.getActionCommand();
if(s.equals("newJFrame"))
{
Frame frame = new Frame();
frame.setBounds (0,0,400,400);
frame.show();
} //if
else if(s.equals("newJInternalFrame")){
InternalFrame iframe = new InternalFrame();
iframe.pack();
iframe.setBounds (0,0,400,400);
fDesktop.add(iframe, JLayeredPane.DEFAULT_LAYER);
try {
iframe.setSelected (true);
} catch (java.beans.PropertyVetoException exeption) {}
} // else if
else
{
System.out.println("Yippee! " + s + " performed.");
} //else
}//actionPerformed
private static void setLookAndFeel(String pOsName)
{
String osName = pOsName;
if(null == osName){
osName = System.getProperty ("os.name");
}//if
LookAndFeel appLookAndFeel;
if (osName.indexOf ("Windows") >= 0) {
appLookAndFeel= new com.sun.java.swing.plaf.windows.WindowsLookAndFeel();
System.out.println("Windows look 'n feel selected");
}
else if (osName.indexOf ("Solaris") >= 0){
appLookAndFeel= new com.sun.java.swing.plaf.motif.MotifLookAndFeel();
System.out.println("Solaris look 'n feel selected");
}
else {
appLookAndFeel= new javax.swing.plaf.metal.MetalLookAndFeel();
System.out.println("Metal look 'n feel selected");
}
try {
UIManager.setLookAndFeel(appLookAndFeel);
} catch (UnsupportedLookAndFeelException e) {}
} // setLookAndFeel
public static void main(String argv[])
{
Bug app = new Bug(argv);
}//main
}
class Frame extends JFrame
{
Focusable fFocusable;
JScrollPane fPane;
public Frame()
{
fFocusable = new Focusable();
fFocusable.setBackground(Color.blue);
fPane = new JScrollPane (fFocusable);
this.getContentPane().add(fPane);
}
}
class InternalFrame extends JInternalFrame
{
Focusable fFocusable;
JScrollPane fPane;
public InternalFrame()
{
super("An Internal Frame",true,true,true,true);
fFocusable = new Focusable();
fFocusable.setBackground(Color.red);
fPane = new JScrollPane(fFocusable);
this.getContentPane().add(fPane);
pack();
}
}
class Thing
{
public Color fColor;
public Point fPoint;
public Thing(Point p, Color c)
{
fPoint = p;
fColor = c;
}
}
class Focusable
extends JComponent
implements Scrollable, ActionListener
{
private Vector fTimer;
private Random fRandom;
private Vector fThings;
static private int RECT = 10;
public Focusable ()
{
fTimer = new Vector();
for(int i = 0; i < 50; ++i){
Timer t = new Timer(100, this);
t.start();
fTimer.add(t);
}
fRandom = new Random();
fThings = new Vector();
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
setPreferredSize(new Dimension(900,900));
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e)
{
Color color = new Color(100, 0, 255);
fThings.add(new Thing(e.getPoint(),color));
repaint();
}
});
}
public void paint(Graphics g)
{
System.out.println("paint");
for(int i = 0; i < 20; ++i){
System.out.println("i = " + i);
g.setColor(Color.black);
Rectangle r = getVisibleRect();
g.fillRect(r.x,r.y,r.width,r.height);
for(int j = 0; j < fThings.size(); ++j){
Thing t = (Thing)fThings.get(j);
Point p = t.fPoint;
g.setColor(t.fColor);
g.fillRect(p.x,p.y,RECT,RECT);
}
}
}
public void actionPerformed(ActionEvent e)
{
System.out.println("Action performed by " + e.getSource());
}
//////////////////////////////////////////////////////////////////
// i m p l e m e n t s S c r o l l a b l e
//////////////////////////////////////////////////////////////////
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
} // getPreferredScrollableViewportSize
// Scroll 1/10 of the visible area.
//
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction)
{
return (int) Math.rint (((orientation == SwingConstants.VERTICAL)
? visibleRect.getHeight() : visibleRect.getWidth()) / 10);
} // getScrollableUnitIncrement
// Scroll 9/10 of the visible area.
//
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction)
{
return (int) Math.rint (9* ((orientation == SwingConstants.VERTICAL)
? visibleRect.getHeight() : visibleRect.getWidth()) / 10);
} // getScrollableBlockIncrement
public boolean getScrollableTracksViewportHeight()
{
return false;
} // getScrollableTracksViewportHeight
public boolean getScrollableTracksViewportWidth()
{
return false;
} // getScrollableTracksViewportWidth
}
(Review ID: 54375)
======================================================================
Name: dbT83986 Date: 05/01/99
/*
Specifically the problems that can be seen when a tree cell
editor involves a scrollbar are as follows (a simple javac
and java will compile and run this demo):
1. initial click on a scroll arrow causes it to scroll
non-stop to end of scrollbar when it should make
one small increment
2. initial click in the scroller area beside thumb
control keeps block scrolling up to the point
of the click when it should jump only one block
3. initial click on thumb control is lost (not forwarded
to the scrollbar) such that it cannot be grabbed on
that first click and dragged as should be possible
Note, this is all on the very FIRST click that brings in the
editor component where there was none before. Once the
component is on the screen, all behaves as it should. The point
of MouseInputHandler is to pass all mouse manipulation to
the editor component as expected to fulfill the illusion of
the editor being on the screen at all times even though it isn't.
THE BOTTOM TREE CELL IS THE BEST ILLUSTRATION OF
BEHAVIORS 1 AND 2 IN THE JTextArea VERSION.
The cause of behaviors 1 and 2 is that the MouseInputHandler in
BasicTreeUI ends its forwarding too soon such that the
mouse released event is not being forwarded. Thus it behaves
as though the user never lets go of the mouse button. This
explains why the arrow stays "depressed" looking.
Behavior 3 is from not forwarding drag events at all
when it should.
TO SEE THE BAD BEHAVIOR: set the useFixedTreeUI variable to false
TO SEE THE FIX IN ACTION: set useFixedTreeUI to true
OPTIONAL: set useJustScrollBars to true to see a scroll
bar only verison of the demo, set it to false
to use a JTextArea editor in a scroll pane
FIX NOTES: The fix is kind of big because overriding here is hard.
Ultimately javax.swing needs repair. See the comments
at completeEditing() and in MouseInputHandler for details
of why the fix is so large and what exactly was fixed.
*/
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;
public class ScrollBarBug extends JFrame {
static final boolean useFixedTreeUI = false;
static final boolean useJustScrollBars = false;
public ScrollBarBug() {
super("ScrollBar Bug");
String shortString = "short string";
String longString = "long string to illustrate what a long line of test looks like in the tree in case it matters but this has no newlines";
String multiNoScrollString = "NO SCROLL\nshort strings, mulitline\nshort strings, mulitline";
String multiHorizScrollString = "HORIZ SCROLL ONLY\nlong strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\nlong strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)";
String multiVertScrollString = "VERT SCROLL ONLY\n2short strings, mulitline\n3short strings, mulitline\n4short strings, mulitline\n5short strings, mulitline\n6short strings, mulitline\n7short strings, mulitline\n8end";
String multiBothScrollString = "BOTH SCROLL\n2long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n3long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n4long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n5long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n6long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n7long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n8end long strings that require horizontal scrolling for sure but also mulitline (does the text area get wider than necessary)\n";
DefaultMutableTreeNode node1 = new DefaultMutableTreeNode(multiBothScrollString);
DefaultMutableTreeNode node2 = new DefaultMutableTreeNode(multiHorizScrollString);
DefaultMutableTreeNode node3 = new DefaultMutableTreeNode(multiVertScrollString);
DefaultMutableTreeNode node4 = new DefaultMutableTreeNode(multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString + multiBothScrollString);
node1.add(node2);
node1.add(node3);
node1.add(node4);
JTree tree = new Tree(node1);
tree.setEditable(true);
tree.setShowsRootHandles(true);
tree.setCellRenderer(new RendererEditor());
tree.setCellEditor(new RendererEditor());
getContentPane().add(tree, BorderLayout.CENTER);
}
public static void main(String args[]) {
ScrollBarBug frame = new ScrollBarBug();
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} });
frame.setSize(800, 500);
frame.setVisible(true);
}
class RendererEditor implements TreeCellRenderer, TreeCellEditor {
JScrollPane sp;
JTextArea ta;
JScrollBar sb;
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (useJustScrollBars)
return getNewJScrollBar();
else
return getNewJScrollPane(value.toString());
}
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
if (useJustScrollBars)
return getNewJScrollBar();
else
return getNewJScrollPane(value.toString());
}
public Object getCellEditorValue() { return "we don't really edit the text of the cell"; }
public boolean isCellEditable(EventObject anEvent) { return true; }
public boolean shouldSelectCell(EventObject anEvent) { return true; }
public boolean stopCellEditing() { return true; }
public void cancelCellEditing() { }
public void addCellEditorListener(CellEditorListener l) { }
public void removeCellEditorListener(CellEditorListener l) { }
private JScrollPane getNewJScrollPane(final String text) {
sp = new JScrollPane();
ta = new JTextArea() ;
ta.setEditable(false);
ta.setRows(5);
ta.setColumns(40);
ta.setLineWrap(false);
// Making the textarea uneditable or non-enabled doesn't work
// Only setting the caret to the text area start
// results in NOT jerking the area to the lower-right (the end).
// Here we assume the user is going to edit it.
//
// ta.setText(text);
// ta.setCaretPosition(0);
//
// But wait. After fixing the forwarding of mouse events
// to the scrollbar (broken in BasicTreeUI ), it became
// clear that setText() and/or setCaretPosition() in any
// combination results in all kinds of caret adjustment "safely"
// done later. But, the mouse events are done NOW so the later
// caret adjustments negate the initial mouse click on the
// editor even though it is forwarded correctly.
//
// So, fix up a document with the text FIRST before it knows
// its JTextArea to notify. So, the caret adjustments never
// happen and are not needed. Then, add the document and all is well.
//
// Comment this out to see the bad behavior. Comment this
// out and uncomment out the setText() and setCaretPosition()
// to see the click negation effect of doing things "later."
PlainDocument doc = new PlainDocument();
try { doc.insertString(0, text, null); } catch (BadLocationException e) { }
ta.setDocument(doc);
sp.getViewport().add(ta);
return sp;
} // end of getNewJScrollPane()
private JScrollBar getNewJScrollBar() {
sb = new JScrollBar(JScrollBar.HORIZONTAL, 50, 10, 0, 200);
sb.setPreferredSize(new Dimension(150, 30));
sb.setUnitIncrement(5);
sb.setBlockIncrement(10);
return sb;
}
} // end of class RendererEditor
class Tree extends JTree {
public Tree(TreeNode root) {
//super(root, false);
super(new DefaultTreeModel(root, false));
}
public void updateUI() {
if (useFixedTreeUI)
setUI(new FixedTreeUI());
else
setUI((TreeUI)UIManager.getUI(this));
invalidate();
}
}
// This is the fixed UI for the tree that contains
// the fixed MouseInputHandler.
class FixedTreeUI extends MetalTreeUI {
// This is completeEditing() verbatum from BasicTreeUI.
// It had to be done that way because usually there
// is a way to override some sort of getMouseInputHandler()
// function to slip in one's own handler. But, in this
// case it is hardcoded and the only way to repalce it
// is to in turn replace this function. This function is
// ok as is and otherwise would not be overridden.
protected void completeEditing(boolean messageStop,
boolean messageCancel,
boolean messageTree) {
if(stopEditingInCompleteEditing && editingComponent != null) {
Component oldComponent = editingComponent;
TreePath oldPath = editingPath;
TreeCellEditor oldEditor = cellEditor;
Object newValue = oldEditor.getCellEditorValue();
Rectangle editingBounds = getPathBounds(tree,
editingPath);
boolean requestFocus = (tree != null &&
(tree.hasFocus() || SwingUtilities.
findFocusOwner(editingComponent) != null));
editingComponent = null;
editingPath = null;
if(messageStop)
oldEditor.stopCellEditing();
else if(messageCancel)
oldEditor.cancelCellEditing();
tree.remove(oldComponent);
if(editorHasDifferentSize) {
treeState.invalidatePathBounds(oldPath);
updateSize();
}
else {
editingBounds.x = 0;
editingBounds.width = tree.getSize().width;
tree.repaint(editingBounds);
}
if(requestFocus)
tree.requestFocus();
if(messageTree)
treeModel.valueForPathChanged(oldPath, newValue);
}
}
/**
* Will start editing for node if there is a cellEditor and
* shouldSelectCell returns true.<p>
* This assumes that path is valid and visible.
*/
protected boolean startEditing(TreePath path, MouseEvent event) {
completeEditing();
if(cellEditor != null && tree.isPathEditable(path)) {
int row = getRowForPath(tree, path);
editingComponent = cellEditor.getTreeCellEditorComponent
(tree, path.getLastPathComponent(),
tree.isPathSelected(path), tree.isExpanded(path),
treeModel.isLeaf(path.getLastPathComponent()), row);
if(cellEditor.isCellEditable(event)) {
Rectangle nodeBounds = getPathBounds(tree, path);
editingRow = row;
Dimension editorSize = editingComponent.getPreferredSize();
// Only allow odd heights if explicitly set.
if(editorSize.height != nodeBounds.height &&
getRowHeight() > 0)
editorSize.height = getRowHeight();
if(editorSize.width != nodeBounds.width ||
editorSize.height != nodeBounds.height) {
// Editor wants different width or height, invalidate
// treeState and relayout.
editorHasDifferentSize = true;
treeState.invalidatePathBounds(path);
updateSize();
}
else
editorHasDifferentSize = false;
tree.add(editingComponent);
editingComponent.setBounds(nodeBounds.x, nodeBounds.y,
editorSize.width,
editorSize.height);
editingPath = path;
editingComponent.validate();
Rectangle visRect = tree.getVisibleRect();
tree.paintImmediately(nodeBounds.x, nodeBounds.y,
visRect.width + visRect.x - nodeBounds.x,
editorSize.height);
if(cellEditor.shouldSelectCell(event)) {
stopEditingInCompleteEditing = false;
try {
tree.setSelectionRow(row);
} catch (Exception e) {
System.err.println("Editing exception: " + e);
}
stopEditingInCompleteEditing = true;
}
if(event != null && event instanceof MouseEvent) {
/* Find the component that will get forwarded all the
mouse events until mouseReleased. */
Point componentPoint = SwingUtilities.convertPoint
(tree, new Point(event.getX(), event.getY()),
editingComponent);
/* Create an instance of BasicTreeMouseListener to handle
passing the mouse/motion events to the necessary
component. */
new MouseInputHandler(tree, SwingUtilities
.getDeepestComponentAt(editingComponent,
componentPoint.x, componentPoint.y),
event);
}
return true;
}
else
editingComponent = null;
}
return false;
} // end of completeEditing() taken from BasicTreeUI
// This is where the bugs are fixed. This inner class
// is from BasicTreeUI. But the one there seems to simply
// be unfinished mostly, but incorrect a little bit.
// The old code is commented out with //OLD:
// and the new code lines added to fix it are tagged
// with //NEW at the end of each line.
public class MouseInputHandler extends Object implements
MouseInputListener
{
/** Source that events are coming from. */
protected Component source;
/** Destination that recieves all events. */
protected Component destination;
public MouseInputHandler(Component source, Component destination,
MouseEvent event){
this.source = source;
this.destination = destination;
this.source.addMouseListener(this);
this.source.addMouseMotionListener(this);
/* Dispatch the editing event! */
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, event, destination));
}
public void mouseClicked(MouseEvent e) {
if(destination != null)
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, e, destination));
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
if(destination != null)
destination.dispatchEvent(SwingUtilities.convertMouseEvent
(source, e, destination));
removeFromSource();
}
public void mouseEn
- duplicates
-
JDK-4188924 NullPointerException when editing in JTree
-
- Resolved
-