TreeWrapper updated: arrange your OPML documents!
After some short holidays, I have been doing some little improvements in TreeWrapper, an easy to use wrapper that allows for drag and drop on JTrees. Improvements include support for JDK 1.4, better handling of popup menus, better performance, support for disabled trees and some other minor things.
To verify that everything is working I built a simple to use OPML editor, that you can use to arrange your RSS feeds into categories.
JDK 1.4 support
As I explained earlier on the first post about TreeWrapper, JDK 5 includes support for examining what the contents of the object being transferred are during the drag operation. JDK 1.4 does not. Well, not directly. Kirill Grouchnikov found that JDK 1.4 includes a protected method exactly for that. He suggested using reflection to access that method and, well, there we go. The code looks like this:
507 private Transferable getTransferable( DropTargetDragEvent dtde ) 508 { 509 try 510 { 511 DropTargetContext context = dtde.getDropTargetContext(); 512 if ( getTransferableMethod == null ) 513 { 514 getTransferableMethod = context.getClass().getDeclaredMethod( "getTransferable", EMPTY_CLASS_ARRAY ); 515 getTransferableMethod.setAccessible( true ); 516 } 517 return (Transferable) getTransferableMethod.invoke( context, EMPTY_OBJECT_ARRAY ); 518 } 519 catch( Exception e ) 520 { 521 e.printStackTrace( System.err ); 522 return null; 523 } 524 }
Thanks, Kirill, for this. As a consequence TreeWrapper runs on JDK 1.4 now.
Flooded with events!!
The fact is that "dragOver( DropTargetEvent )" is invoked several times per second during a drag and drop operation, even if the user does not move the mouse. As a consequence, the previous version of TreeWrapper invoked the set of listeners a lot of times per second, even for the same node!!
This, of course, is a waste of CPU resources. And it slows things down a lot. The new version of TreeWrapper handles gracefully this behaviour by keeping a reference to the last "dragged over" node, and refusing to ask the listeners twice for the same node. As a consequence, the new version of TreeWrapper is much faster and consumes less CPU resources.
The code looks like this:
526 /** This node to avoid too many invocations to dragOver */ 527 private TreeNode lastDragOverNode = null; 528 529 public void dragOver(DropTargetDragEvent dtde) 530 { 531 if ( ! tree.isEnabled() ) 532 { 533 dtde.rejectDrag(); 534 return; 535 } 536 537 // Is this a valid node for dropping? 538 TreePath dropPath = tree.getClosestPathForLocation( dtde.getLocation().x, 539 dtde.getLocation().y ); 540 541 TreeNode currentDropNode = (TreeNode) dropPath.getLastPathComponent(); 542 543 if ( dropPath == null || currentDropNode == null || currentDropNode.equals( lastDragOverNode ) ) 544 { 545 return; 546 } 547 else 548 { 549 lastDragOverNode = currentDropNode; 550 }
Better popup menu handling
I'm running a recent install of Kubuntu 6.06. I like KDE and feel comfortable with it. I admit I didn't test the previous release of TreeWrapper on Windows (I just did some small tests on Windows), so I didn't notice that Windows and Linux handle popup menu triggers in different ways.
The fact is that a MouseEvent.isPopupTrigger() behaves differently in Windows and Linux. In Linux you detect popup events (usually right-clicks on the mouse) by overriding the "MouseListener.mouseReleased( MouseEvent me )" method. But Windows prefers the "MouseListener.mousePressed( MouseEvent me )" instead.
The bug is easy to solve. Basically you have a MouseAdapter and override both methods, and then for each one you verify that MouseEvent.isPopupTrigger(), like this:
139 class PopupChooserMouseListener 140 extends MouseAdapter 141 { 142 143 // @Override 144 public void mousePressed(MouseEvent e) 145 { 146 verifyPopupTrigger( e ); 147 } 148 149 // @Override 150 public void mouseReleased(MouseEvent e) 151 { 152 verifyPopupTrigger( e ); 153 } 154 155 private void verifyPopupTrigger( MouseEvent e ) 156 { 157 if ( customPopupHandler != null && e.isPopupTrigger() ) 158 { 159 Point point = e.getPoint(); 160 TreePath path = tree.getClosestPathForLocation( point.x, point.y ); 161 if ( path != null ) 162 { 163 tree.getSelectionModel().setSelectionPath( path ); 164 TreeNode node = (TreeNode) path.getLastPathComponent(); 165 JPopupMenu menu = customPopupHandler.getMenuAt( tree, node ); 166 if ( menu != null ) 167 { 168 menu.show( tree, point.x, point.y ); 169 } 170 } 171 } 172 } 173 }
An OPML Editor
To verify that everything was working correctly I decided to build a simple OPML editor. You know, the Outline Processor Markup Language is a spec commonly used to export collections of RSS feeds. You can, for instance, import your list of feeds (as an OPML document) from your bloglines account. This OPML editor allows me to load/save/import and arrange my list of feeds and categories. This OPML editor is a step forward towards the construction of a more complete RSS feed reader.
You can run the OPML Editor using JavaWebStart 1.4+.
More info on the TreeWrapper can be found at the attic.
I would appreciate feedback about he TreeWrapper. If you find a bug or see an interesting new feature then please let me know.
Happy Swinging, Antonio
blog comments powered by Disqus