Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.gui > #4107 > unrolled thread

JTree within JScrollPane:

Started by"JSchneider" <jschneider@THRWHITE.remove-dii-this>
First post2011-04-27 15:48 +0000
Last post2011-04-27 15:48 +0000
Articles 4 — 2 participants

Back to article view | Back to comp.lang.java.gui


Contents

  JTree within JScrollPane: "JSchneider" <jschneider@THRWHITE.remove-dii-this> - 2011-04-27 15:48 +0000
    Re: JTree within JScrollP "tomaszewski.p" <tomaszewski.p@THRWHITE.remove-dii-this> - 2011-04-27 15:48 +0000
      Re: JTree within JScrollP "JSchneider" <jschneider@THRWHITE.remove-dii-this> - 2011-04-27 15:48 +0000
        Re: JTree within JScrollP "tomaszewski.p" <tomaszewski.p@THRWHITE.remove-dii-this> - 2011-04-27 15:48 +0000

#4107 — JTree within JScrollPane:

From"JSchneider" <jschneider@THRWHITE.remove-dii-this>
Date2011-04-27 15:48 +0000
SubjectJTree within JScrollPane:
Message-ID<0a56d7bd-4911-480e-82ea-413d2adf8764@c58g2000hsc.googlegroups.com>
  To: comp.lang.java.gui
Hi,

I have the following requirement:

I have a JTree placed within a JScrollPane. The model of the JTree is
now updated every other second. But the additional entries are added
at the top of the Model (index is nearly always 0).
This is a requirement and can't be changed...
(Think of a mail client that shows the youngest mails at the bottom
and loads the youngest mails first - filling the tree from the bottom
to the top).

Now the problem:
If the user has scrolled to the bottom (he wants to see the youngest
entries) the visible entries change every time an additional row is
added at the top (old mail has been added).

My approach:
I calculate the delta between the bottom of the visible rect and the
size of the tree when an entry has been added (within a
TreeModelListener).
Then I have overridden "validate" and "invalidate" and call
"scrollRectToVisible" with an updated rect, calculated based on the
delta.

That works well - just with one exception. ScrollRectToVisble causes a
repaint with the old visible rect. Therefore the tree is
"jumping" (paints one time with the "wrong" visible rect).
When the scroll mode of the JScrollPane is changed to
"JViewport.BACKINGSTORE_SCROLL_MODE" everything works fine.
Unfortunately this is not an option due to performance issues....

I hope the problem is understandable - any hints or suggestions?
Thanks in advance...

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [next] | [standalone]


#4108 — Re: JTree within JScrollP

From"tomaszewski.p" <tomaszewski.p@THRWHITE.remove-dii-this>
Date2011-04-27 15:48 +0000
SubjectRe: JTree within JScrollP
Message-ID<g96633$b4h$1@news.interia.pl>
In reply to#4107
  To: comp.lang.java.gui


"JSchneider" <johannes@familieschneider.info> wrote in message 
news:0a56d7bd-4911-480e-82ea-413d2adf8764@c58g2000hsc.googlegroups.com...
> Hi,
>
> I have the following requirement:
>
> I have a JTree placed within a JScrollPane. The model of the JTree is
> now updated every other second. But the additional entries are added
> at the top of the Model (index is nearly always 0).
> This is a requirement and can't be changed...
[...]
> Now the problem:
> If the user has scrolled to the bottom (he wants to see the youngest
> entries) the visible entries change every time an additional row is
> added at the top (old mail has been added).
[...]
> ScrollRectToVisble causes a
> repaint with the old visible rect. Therefore the tree is
> "jumping" (paints one time with the "wrong" visible rect).
[...]

Try this:

import java.util.Enumeration;
import java.util.List;
import java.util.LinkedList;

import java.awt.BorderLayout;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;

public class JTreeRefreshTest {

    static class NodeElement {
        private String message;

        public NodeElement(String message) {
            this.message = message;
        }

        @Override
        public String toString() {
            return message;
        }
    }

    static class TestTreeModel extends DefaultTreeModel {
           public TestTreeModel(TreeNode root) {
               super(root);
           }

           public void addNode(DefaultMutableTreeNode parent, 
DefaultMutableTreeNode child)
           {
               parent.insert(child, 0);
               final int childIndex = parent.getIndex(child);
               fireTreeNodesInserted(parent, parent.getPath(), new int[] 
{childIndex}, new Object[] {child});
           }
    }

    static class TestPanel extends JPanel {
        private DefaultMutableTreeNode rootNode;
        private TestTreeModel treeModel;

        public TestPanel() {
            super();
            rootNode = new DefaultMutableTreeNode(new NodeElement("Root"));
            for (int idx = 0; idx < 100; idx++) {
                rootNode.add(new DefaultMutableTreeNode(new 
NodeElement("Child " + idx)));
            }
            treeModel = new TestTreeModel(rootNode);

            final JTree tree = new JTree(treeModel);
            final JScrollPane scroll = new JScrollPane();
            scroll.getViewport().add(tree);

            setLayout(new BorderLayout());
            add(scroll, BorderLayout.CENTER);

            tree.addKeyListener(new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent event) {
                    if (event.getKeyCode() == KeyEvent.VK_1) {
                        final DefaultMutableTreeNode newNode = new 
DefaultMutableTreeNode(new NodeElement("New Node"));
                        treeModel.addNode(rootNode, newNode);
                    }
                }
            });
            treeModel.addTreeModelListener(new TreeModelListener() {
                @Override
                public void treeNodesChanged(TreeModelEvent event) {
                }

                @Override
                public void treeNodesInserted(TreeModelEvent event) {
                    tree.scrollPathToVisible(event.getTreePath());
                }

                @Override
                public void treeNodesRemoved(TreeModelEvent event) {
                }

                @Override
                public void treeStructureChanged(TreeModelEvent event) {
                }
            });
        }
    }

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(new TestPanel(), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

}

Przemek

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#4109 — Re: JTree within JScrollP

From"JSchneider" <jschneider@THRWHITE.remove-dii-this>
Date2011-04-27 15:48 +0000
SubjectRe: JTree within JScrollP
Message-ID<69535338-c5a3-4946-bf2d-a00a247e38bf@25g2000hsx.googlegroups.com>
In reply to#4108
  To: comp.lang.java.gui
Thanks for your reply, but I think I didn't explain my problem enough:


In your example the tree is scrolled to the newly added entry:

  public void treeNodesInserted(TreeModelEvent event) {
     tree.scrollPathToVisible(event.getTreePath());
  }

but that behaviour interrupts the user. Instead it is necessary that
the same entries are shown --> the user doesn't even recognize any
changes...

If you try this code, you will see the flickering (scrollPathToVisible
is trickering a repaint...):



import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeModel;
import java.awt.BorderLayout;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class JTreeRefreshTest {
  static class NodeElement {
    private String message;

    public NodeElement( String message ) {
      this.message = message;
    }

    @Override
    public String toString() {
      return message;
    }
  }

  static class TestTreeModel extends DefaultTreeModel {
    public TestTreeModel( TreeNode root ) {
      super( root );
    }

    public void addNode( DefaultMutableTreeNode parent,
                         DefaultMutableTreeNode child ) {
      parent.insert( child, 0 );
      final int childIndex = parent.getIndex( child );
      fireTreeNodesInserted( parent, parent.getPath(), new int[]
          {childIndex}, new Object[]{child} );
    }
  }

  static class TestPanel extends JPanel {
    private DefaultMutableTreeNode rootNode;
    private TestTreeModel treeModel;

    public TestPanel() {
      rootNode = new DefaultMutableTreeNode( new
NodeElement( "Root" ) );
      for ( int idx = 0; idx < 100; idx++ ) {
        rootNode.add( new DefaultMutableTreeNode( new
            NodeElement( "Child " + idx ) ) );
      }
      treeModel = new TestTreeModel( rootNode );

      final MyJTree tree = new MyJTree( treeModel );
      final JScrollPane scroll = new JScrollPane( tree );
//      scroll.getViewport().add( tree );

      setLayout( new BorderLayout() );
      add( scroll, BorderLayout.CENTER );

      tree.addKeyListener( new KeyAdapter() {
        @Override
        public void keyReleased( KeyEvent event ) {
          if ( event.getKeyCode() == KeyEvent.VK_1 ) {
            final DefaultMutableTreeNode newNode = new
                DefaultMutableTreeNode( new NodeElement( "New
Node" ) );
            treeModel.addNode( rootNode, newNode );
          }
        }
      } );
      treeModel.addTreeModelListener( new TreeModelListener() {
        public void treeNodesChanged( TreeModelEvent event ) {
        }

        public void treeNodesInserted( TreeModelEvent event ) {
          Rectangle visibleRect = tree.getVisibleRect();
          tree.oldDeltaY = tree.getSize().height - visibleRect.y -
visibleRect.height;
        }

        public void treeNodesRemoved( TreeModelEvent event ) {
        }

        public void treeStructureChanged( TreeModelEvent event ) {
        }
      } );
    }

    private static class MyJTree extends JTree {
      public MyJTree( TreeModel model) {
        super( model );
      }

      @Override
        public void validate() {
        super.validate();
        fixVisibleRect();
      }

      @Override
        public void invalidate() {
        super.invalidate();
        fixVisibleRect();
      }

      @Override
        public void paint( Graphics g ) {
        oldDeltaY = -1;
        super.paint( g );
      }

      /**
         * Fixes the visible rect
       */
      private void fixVisibleRect() {
        if ( oldDeltaY > -1 ) {
          Rectangle visibleRect = getVisibleRect();
          int actualDelta = getSize().height - visibleRect.y -
visibleRect.height;

          if ( actualDelta != oldDeltaY ) {
            int delta = actualDelta - oldDeltaY;
            visibleRect.y += delta;

            scrollRectToVisible( visibleRect );
            oldDeltaY = -1;
          }
        }
      }

      int oldDeltaY = -1;
    }
  }

  public static void main( String[] args ) {
    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.setLayout( new BorderLayout() );
    frame.add( new TestPanel(), BorderLayout.CENTER );
    frame.pack();
    frame.setVisible( true );
  }

}

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [next] | [standalone]


#4112 — Re: JTree within JScrollP

From"tomaszewski.p" <tomaszewski.p@THRWHITE.remove-dii-this>
Date2011-04-27 15:48 +0000
SubjectRe: JTree within JScrollP
Message-ID<g9d7m1$6oh$1@inews.gazeta.pl>
In reply to#4109
  To: comp.lang.java.gui


Uzytkownik "JSchneider" <johannes@familieschneider.info> napisal w
wiadomosci
news:69535338-c5a3-4946-bf2d-a00a247e38bf@25g2000hsx.googlegroups.com...
> Thanks for your reply, but I think I didn't explain my problem enough:
>
>
> In your example the tree is scrolled to the newly added entry:
>
>  public void treeNodesInserted(TreeModelEvent event) {
>     tree.scrollPathToVisible(event.getTreePath());
>  }
>
> but that behaviour interrupts the user. Instead it is necessary that
> the same entries are shown --> the user doesn't even recognize any
> changes...
>
> If you try this code, you will see the flickering (scrollPathToVisible
> is trickering a repaint...):
>
[...]

Hi,

  Everything looks fine in Your solution. The only thing with scrollbar is
resizing and I assume that is correct behaviour (JRE 1.6.0_7, Vista, Nvidia
GF 8400).

Przemek

---
 * Synchronet * The Whitehouse BBS --- whitehouse.hulds.com --- check it out free usenet!
--- Synchronet 3.15a-Win32 NewsLink 1.92
Time Warp of the Future BBS - telnet://time.synchro.net:24

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.gui


csiph-web