The Source for Java Technology Collaboration
User: Password:



Shannon Hickey

Shannon Hickey's Blog

First Class Drag and Drop Support in Mustang

Posted by shan_man on January 02, 2006 at 03:41 PM | Comments (32)

Many Thanks!

An expression that my friend and Swing Team colleague in Russia, Michael Knyazev, offers to me; and one I'd like to extend to you now. It's been seven months since my first blog, with it's goofy play on words, discussion of Swing's improved drag gesture, and my promise that I'd write about more exciting Drag and Drop enhancements shortly. Seven months is slightly longer than I intended, and I sincerely apologize for the delay in fulfilling my promise. Even more importantly, I'd like to offer Many Thanks, for both your patience and your visit here today.

In this blog entry I'll introduce some major enhancements that make Swing Drag and Drop much more powerful, flexible, and easy to use: a first class citizen in Mustang! The discussion will include a Java Web Start demo displaying this power in the context of JTree, and source code showing how, with a single method call, you can finally configure JTree to accept drops between nodes. But first, to help excuse the seven month delay with some finesse (and yet delay a little longer), I'll quickly tell you what I've been up to for the last little while.

There's been lots of bug fixing and code reviewing, as we work the release into top shape for beta. I've also been working hard along with my team on implementing and refining major features for the Mustang release. I travelled to California in September to engage in early Dolphin planning sessions with my team. And most incredible, I had the opportunity to visit our team members in Saint Petersburg, Russia. Wow - what an amazing experience! Our colleagues were outstanding hosts, and their city is very beautiful. I've included a few thumbnail pictures below, that you can mouse-over for descriptions, and click for full versions:

Buildings along the Moika canal Buildings along the Moika canal at night Griboedov channel and "Savior on Spilled Blood" Cathedral Griboedov channel and "Savior on Spilled Blood" Cathedral at night Saint Isaac's Cathedral View from the top of Saint Isaac's Cathedral. Peter and Paul fortress in the background, across the Neva River. Hermitage Museum is the green building to the right. A bridge over the Fontanka river

The things I'll remember most about my trip are the wonderful people, the beautiful architecture, Russian pancakes (Blinis) with strawberry jam, refining humor with Chet Haase, and the fact that there's smoking everywhere - which was just fine, since I enjoy the occasional cigarette myself. It was an experience I'll remember forever, and I give Many Thanks to our Russian colleagues for making it so.

With that, let's continue on to the fun technical stuff. There are two themes addressed by our Mustang Drag and Drop feature. The first is the ability to tell a component how to determine drop locations, and to easily and consistently query said locations. The second is for Swing to provide all relevant information on a transfer when asking the TransferHandler if it is acceptable, and when telling it to import data. Together, the enhancements made under these themes open up many doors for Swing Drag and Drop users.

The first theme has a single associated bug number, 4468566, with a synopsis (Swing DnD should not use selection to show drop location) that very clearly states the issue to be solved. Since the initial implementation of Swing Drag and Drop, multiple components (JTree, JTable, JList, JTextComponents) have been capable of visually displaying a drop location during a Drag and Drop operation. Historically this has been done by determining where the mouse is in the component, and then simply changing the component's selection temporarily to be the item under the mouse. Likewise, for text components, the caret is moved to the location under the mouse, temporarily clearing any selection. There is a fundamental problem with this approach in that clearing the selection affects the user experience. After initiating a drag, the user immediately loses the context of what data they are dragging.

Secondarily, this approach can lead to problems for developers, as they are potentially forced to ignore selection events that are fired during Drag and Drop. They also have to deal with the fact that there's no consistent API for fetching the drop location at drop time; and must learn that they have to ask the component directly for it's selected item or index. Finally, and possibly most important, the use of selection to show drop location has a very serious limitation: One cannot drop between items. And so...drum roll please...

Introducing setDropMode(DropMode)! This is a new property on the components mentioned above, allowing you to decide how drop locations are determined. You want to drop on top of items - sure; you want to drop between items - no problem; you want a combination of both - excellent, you can do it. For each of these cases there is a drop mode that can be set on the component, and the location will be automatically calculated and displayed, without affecting the component's selection! The following table lists each drop mode constant, defined in class DropMode, the components that support each mode, and what each one means:

Mode Supported By Description
ON JList, JTree, JTable The drop location should be tracked in terms of the position of existing items. Useful for dropping on top of items.
INSERT JList, JTree, JTable, JTextComponent The drop location should be tracked in terms of the position where new data should be inserted. Useful for inserting or re-arranging content.
ON_OR_INSERT JList, JTree, JTable A combination of ON and INSERT, specifying that data can be dropped on top of existing items, or in insert locations as specified by INSERT.
[ON_OR_]INSERT_ROWS  
[ON_OR_]INSERT_COLS  
JTable Drop modes specific to JTable, indicating that drop locations should be calculated in terms of rows or columns only.
USE_SELECTION JList, JTree, JTable, JTextComponent The default, for backward compatibility. A component's own internal selection mechanism (or caret for text components) should be used to track the drop location.

At this point, let's take a little break from the technical talk to play. Because it's one of the more interesting examples, I've included a Java Web Start enabled demo allowing me to illustrate how you can finally configure a JTree to accept drops between nodes. Of course, the demo will also let you try each of the other drop modes supported by JTree. Click here to launch the demo (requires build 76 or greater of Mustang to work correctly, which you can download now if you don't already have it).

Screen Shot 1 below shows what the demo looks like when it's launched. At the very top is a drag source, with the label "Drag from here:". You'll use this component to initiate drags when playing with the demo. The middle component is a JTree that's been configured to accept drops, and will allow you to try out the different drop modes in the context of trees. Notice that by default, the "blue" node is selected. This allows me to demonstrate how the new drop modes provide drag-over feedback without affecting selection. Finally, at the bottom is a JComboBox used for setting different drop modes on the JTree. By default it is set to INSERT, the subject of this discussion.

Screen Shot 1

The INSERT mode was designed by considering exactly what you need when you want to Drag and Drop to insert new nodes into a tree. That is, the ability to drop on top of existing non-leaf (folder) nodes to insert a child, and the ability to drop between existing nodes to insert between them. This is exactly what you'll see in the demo. Initiate a drag by pressing on top of the drag source and dragging the mouse a short distance. Drag into the tree and start moving down on top of the nodes. As you drag over folder nodes they become highlighted to indicate that the drop can be accepted into that folder. This can be seen in Screen Shot 2 below, which illustrates the effect of dragging over the "names" folder node. Screen Shot 3 shows the effect of releasing the mouse and dropping on top of that node: a new item is inserted as the last child of the "names" node.

Screen Shot 2 Screen Shot 3
 

Similarly, as you drag between nodes a horizontal line is displayed to indicate that the drop can be accepted at that location. The line shows the position and level of the tree where the new node is to be inserted. Screen Shot 4 illustrates this drag-over effect and Screen Shot 5 displays the effect of releasing the mouse at the position indicated: a new node is inserted as a child of "colors" between "red" and "yellow".

Screen Shot 4 Screen Shot 5
 

Notice that throughout this demonstration, selection of the "blue" node is not affected at all! As mentioned previously, all of the new drop modes show the drop location without affecting selection. And speaking of other drop modes, you may have noticed something that you were unable to do with the INSERT mode: namely, dropping on top of a leaf node to add a child to it, effectively converting it to a folder node. This is certainly possible, but a job for ON_OR_INSERT. The difference is that ON_OR_INSERT allows dropping on top of any node, whereas INSERT only allows dropping on top of existing folders. Finally, I'd like to draw your attention to one additional ease of use feature added for JTree Drag and Drop in Mustang: during a drag operation, hovering the mouse over a collapsed folder node for two seconds causes the folder to be expanded, making it possible to drop within its list of children.

Now that you've taken the demo for a spin and had a chance to see the power of setDropMode, it's time to dive into the second theme to see how developers have been empowered to write robust drop handling code, by making the drop location and other important information available to TransferHandler. As you know, the responsibility for deciding the acceptability of drops, and for performing data import, lies with the TransferHandler class. Prior to Mustang, however, some common tasks could not be accomplished due to needed information not being available to TransferHandler's drop related methods. Multiple bugs complained about these problems:

  • 4942851: canImport should carry Transferable in TransferHandler
  • 5029427: Location sensitive dropping
  • 5029432: Provide a way of getting the action in TransferHandler.importData(...)

As described in these bug reports, it wasn't possible to customize drop handling based on the Transferable, drop location, or drop action associated with a particular drop. These limitations have finally been removed in Mustang with the addition of a new TransferHandler.TransferInfo inner class to encapsulate the details of every transfer. An instance of this class is provided to new overloaded versions of TransferHandler's import related methods, canImport and importData, so that developers can now accept or reject transfers, and customize data import, based on all information that can be provided about a transfer. As one example of what this enables, developers can now write a TransferHandler to accepts drops only to particular locations in a component!

The following table outlines the API of TransferInfo:

Method Description
Component getComponent() Returns the target component of the transfer.
DataFlavor[] getDataFlavors() Returns the data flavors for the transfer.
boolean isDataFlavorSupported(DataFlavor) Returns whether or not the given data flavor is supported.
Transferable getTransferable() Returns the Transferable associated with the transfer.
boolean isDrop() Returns whether or not the transfer represents a drop operation.
int getDropAction() If the transfer is a drop, returns the action chosen for the drop, otherwise returns -1.
TransferHandler.DropLocation getDropLocation() If the transfer is a drop, returns the current drop location for the component, otherwise returns null.


Important Note: This blog was published before finalization of the Swing drag and drop API for Java SE 6. As such, there have since been minor modifications. In particular, TransferInfo has been renamed to TransferSupport and now benefits from additional methods. For full details on these changes, please see my later blog entitled Choosing the Drop Action, and Further Changes to Swing Drag and Drop.

The last entry is particularly exciting in terms of the first Mustang theme that I discussed earlier. getDropLocation allows you to query the drop location determined by the component in a consistent manner, regardless of the component you're working with. And the really neat thing is that the return value from this method, which is declared to be of the new type TransferHandler.DropLocation, is actually a more informative subtype when TransferHandler is used with those components having built in drop support. Specifically, the default TransferHandler.DropLocation contains the single method getDropPoint, which returns the point in the component over which the operation is occurring. But when used with JTree, JList, JTable or JTextComponent, a subclass of DropLocation (JTree.DropLocation, JList.DropLocation, JTable.DropLocation or JTextComponent.DropLocation respectively) is returned, giving more information about the drop location in terms of the particular component type; for example, the index for lists or text components, or the tree path for trees.

As we've been doing throughout this blog, let's again take a closer look at this aspect of the new Drag and Drop support in the context of JTree. For Drag and Drop with JTree, the class JTree.DropLocation provides details on the drop location by way of the two methods getPath and getChildIndex. The former dictates the path over which dropped data should be inserted. The latter indicates the index where dropped data should be inserted with respect to the path given. Together these methods make explicit the exact location of the drop in terms that make sense for JTree!

Pulling together the ideas I've discussed in this portion of the blog, I'd like to show things in practice by sharing the source code of the TransferHandler I wrote for the earlier tree demo. To accomplish the import behavior shown by the demo, only two methods require overriding. First, the new version of canImport which takes a TransferInfo, to return the acceptability of transfers:

public boolean canImport(TransferHandler.TransferInfo info) {
    // for demo purposes, we'll only support drops and not clipboard paste
    if (!info.isDrop()) {
        return false;
    }

    // we only support importing Strings
    if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
        return false;
    }

    // fetch the drop location (it's a JTree.DropLocation for JTree)
    JTree.DropLocation dl = (JTree.DropLocation)info.getDropLocation();

    // we only support drops for valid paths in the tree
    return dl.getPath() != null;
}

Extremely simple! We accept imports of Strings, only for valid drop locations, and that's it. Note the use of isDataFlavorSupported for determining whether or not the String flavor is available. This is much simpler than looping through all of the available flavors to look for the String flavor - the only available approach prior to Mustang.

The second method of interest, with an implementation almost as simple as canImport, is the new version of importData, responsible for handling data import:

public boolean importData(TransferHandler.TransferInfo info) {
    // if we can't handle the import, return so
    if (!canImport(info)) {
        return false;
    }

    // fetch the drop location (it's a JTree.DropLocation for JTree)
    JTree.DropLocation dl = (JTree.DropLocation)info.getDropLocation();

    // fetch the path and child index from the drop location
    TreePath path = dl.getPath();
    int childIndex = dl.getChildIndex();

    // fetch the Transferable
    Transferable trans = info.getTransferable();

    // fetch the data, and bail if it fails
    String data;
    try {
        data = (String)trans.getTransferData(DataFlavor.stringFlavor);
    } catch (UnsupportedFlavorException e) {
        return false;
    } catch (IOException e) {
        return false;
    }

    // if the child index is -1, the drop was directly on top of the path,
    // which we'll treat as inserting at the end of the path's child list
    if (childIndex == -1) {
        childIndex = treeModel.getChildCount(path.getLastPathComponent());
    }

    // create a new node to represent the data and insert it into the model
    DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(data);
    DefaultMutableTreeNode parentNode =
        (DefaultMutableTreeNode)path.getLastPathComponent();
    treeModel.insertNodeInto(newNode, parentNode, childIndex);

    // expand and scroll so that the new node is visible
    TreePath newPath = path.pathByAddingChild(newNode);
    tree.makeVisible(newPath);
    tree.scrollRectToVisible(tree.getPathBounds(newPath));

    // return success
    return true;
}

That's it - the full TransferHandler source for accepting Drag and Drop inserts into a JTree. Again, it's extremely simple with the additions made in Mustang. I believe you'll find this to be the case with Mustang Drag and Drop in general - it's been made easier to use, barriers have been eliminated, and new functionality has been made possible. As you can tell, I'm rather excited about this and have lots of information to share on the subject. In fact, I have enough for at least three more blog entries. Which is to say that, although I'm about to conclude this particular lengthy discussion, I think you'll be hearing from me again "shortly", and this time a lot sooner than seven months.

Until then, all the best to you. Oh, and one last time - Many Thanks!


Bookmark blog post:
del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • You should show what can happen when you also change the way the insert mode paints the component (like in the chat client you demoed at JavaOne 05).

    Posted by: gfx on January 02, 2006 at 05:08 PM

  • Most definitely gfx! In fact, customizing the painting of drop locations is one of the topics I already have planned for an upcoming blog entry. Thanks for confirming it's a worthwhile topic :)

    Posted by: shan_man on January 02, 2006 at 05:17 PM

  • Thanks so much for fixing this drag-n-drop stuff. It's been causing me no end of headaches.

    Posted by: billgroppe on January 03, 2006 at 03:10 AM

  • Are drag images going to be supported in the Win32 implementation? This works great in 1.4.2 w/ OS X but I had to implement it by hand for Win32.

    Also, is the general expectation that pevious DnD code written for 1.4.2 will still work in 1.6 assuming the DnD APIs were properly leveraged? Or is there a good chance many of us will have to re-work our code?

    FYI, I know I've skipped 1.5 but there wasn't enough client side stuff in the release to justify breaking compatibility with 1.4.2 deployment. However, subpixel antialiasing alone will probably be enough to move me to 1.6.

    Posted by: bwy on January 03, 2006 at 08:34 AM

  • hi shannon,

    I have a query for you:
    how much of this work do you think intersects with:
    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4816922

    Im seeing some very interesting visuals here on this blog and the bug is about visuals.

    Im curious because Im interested in in attacking the problem via the collaborator project. But if the problem has been resolved then hey, thanks! :)

    leouser

    Posted by: leouser on January 03, 2006 at 03:39 PM

  • billgroppe, thank you for your comment. It's been a pleasure working on this for Mustang. I hope your headaches have finally ended!

    Posted by: shan_man on January 09, 2006 at 12:53 PM


  • bwy, I'm thrilled to hear that you plan to move to 1.6 and am very pleased we've given you a reason to move!

    In regards to drag images being supported in the Win32 implementation, I assume you're referring to API that allows you to attach a visual representation of what you're dragging to the mouse cursor. Unfortunately, I have to report that implementing this for Windows did not make Mustang, as there was much more investigation involved than we had expected. However, it's at the top of our list for Dolphin (under bug number 4816922) and since the formal API already exists, I wouldn't rule out the possibility of seeing it in a Mustang update release if there's enough demand.


    As to your question about Drag and Drop code written for 1.4.2 continuing to work in 1.6, it most certainly will! As is the case whenever we introduce new API, we strive to implement it in such a way as to not affect backward compatibility. This is true even when we wish (in our idealistic hearts) that it didn't have to be. For example, we made the default drop mode USE_SELECTION to match existing behavior, even though we know that mode is no longer desirable and would have loved to remove it altogether. Likewise, the new versions of canImport and importData that we added to TransferHandler overload the existing versions, when in an ideal world we'd have preferred to remove the old versions. But existing code must continue to work, and it will continue to work. And of course the new API is there for future development, or for existing applications that want to take advantage of more powerful API.

    Posted by: shan_man on January 09, 2006 at 01:34 PM

  • thanks for the reply Shannon. I don't believe it is 4816922 is Windows specific but is a Swing wide problem. If I remember right the code just never gets called! :D Its also very possible that getVisualIcon(TransferHandler handler) is too simple of a method to do anythin useful. We shall see. :) leouser

    Posted by: leouser on January 09, 2006 at 06:13 PM


  • Hi leouser! I was actually planning on responding directly to your question last night, but ran out of time. So I'm back this morning. As you've ascertained from my response to bwy, the work done for Mustang Swing Drag and Drop did not include resolving 4816922.


    It was on the list of things to address for Mustang but, as I mentioned, there was more investigation required than we expected. Initially it was believed that the fix might be as simple as calling getVisualRepresentation and then passing an image to the AWT Drag and Drop subsystem. Unfortunately, it turns out that DragSource.isDragImageSupported returns false on Windows, as support for a drag image hasn't been implemented. As such, fixing this also requires coordination with the AWT Team.


    Once we determined this, the AWT Team investigated and, surprisingly, could not determine a native API to use in providing this support on Windows. At first I was surprised by this, since I've seen drag images on Windows when dragging file lists from explorer or an icon from the desktop. But when we looked at this closer we realized that these drag images are only shown when dragging over Explorer or IE. To see this yourself, try dragging a file list from Explorer to a different application - the translucent image of the file list you're dragging dissapears when entering the window.


    As you can see, there's more investigation to be done. We'd like to figure out what exactly is happening on Windows and whether or not there is an API we can take advantage of. Failing that, another option is to create our own support - perhaps by using a transparent window with the drag image. If you have any ideas, insight or proposals, please share. Thanks! :)

    Posted by: shan_man on January 10, 2006 at 08:23 AM

  • Thanks Shannon, for these awesome news!
    I noticed, that the TransferHandler still contains boilerplate code: method importData needs to extract the drop location path from the info object, then invoke methods on the model, and then expand and scroll the tree to the newly inserted object. Wouldn't it be possible to get rid of all that with the introduction of mutable model interfaces? e.g. MutableTreeModel, MutableTableModel, MutableListModel?
    When I wrote DnD extensions for JTree, JList and JTable, I always based them on mutable model Interfaces. These model Interfaces have the add/remove/set methods like the default models that are provided by swing, plus methods to export and import elements to/from a Transferable.
    This allowed to entirely isolate application developers from drag and drop code. They only thing they needed to do, was to implement the mutable model interface, and use a DnDJTree instead of a JTree as the view component. In addition, with the same models, it is possible to support cut- and-paste functionality as well.
    For me, having mutable models, seems to be a logical way to go, when the components provide editing functionality.Maybe there is a good reason, why the Swing team wants to avoid introducing such model interfaces?

    Posted by: wrandelshofer on January 15, 2006 at 08:05 AM

  • Sorry, its me again. :)
    I have a question about the drop mode. Is this a global option (for the entire component) or is it individual to each element?
    For example, if the user has permission to drop an element into some folders on a JTree, but not into others, is it possible to visualize this while the user is dragging the element over the tree? So that the drop indicator is only shown when the mouse hovers over locations for which dropping is allowed?

    Posted by: wrandelshofer on January 15, 2006 at 08:24 AM

  • Hi Shannon,

    I would really give up my leg,arms,ears and whatever in order to make you guys fix 4816922.

    The problem regarding the visual representation of the cursor in Windows is indeed true. I tried dragging a text file from Windows Explorer onto Wordpad, and soon as the mouse enters Wordpad, the translucent image of the text file,beneath the cursor, suddenly vanishes!

    So the problem is really due to Microsoft not having probably an unified drag and drop API, as the AWT team has rightly found out.

    So not depending on Windows, and going ahead with a native Java impelmentation, may be the best path forward.

    I would like to draw your attention to Romain Guy's demo here. PhotoCollage.
    He uses a GlassPane for the setting the visual representation of the item being dragged.

    Posted by: swapnonil on January 16, 2006 at 08:57 AM


  • Hi wrandelshofer, and thank you for your two comments! In regards to the introduction of mutable model interfaces to reduce boilerplate code, we'll certainly investigate the concept. We're very interested in any ideas that make using Swing easier for developers. If you happen to have the time, I would love it if you could submit an RFE on the idea, and perhaps include some details of what methods you'd like to see in the implementation. Additionally, if you'd like to jump-start this, I encourage you to contribute code and ideas to the SwingLabs project. If you're unfamiliar with SwingLabs, it acts as a testbed for ideas related to client side techologies, and successful experiments will be considered for inclusion into future vesions of the JDK!


    Now, as to your question regarding accepting drops onto some elements and not on others, it's a fantastic question that I'm glad you've asked! You see, prior to Mustang it was not possible to do what you've described, because canImport was only called once for a component, when the drag entered. Being a very inhibiting problem, this was another major thing that we resolved as part of these Drag and Drop enhancements. I'll explain and clarify:


    The drop mode itself is a setting on the component that simply tells the component how to determine drop locations; it's unrelated to the question you've asked. What's important is that canImport is now additionally called every time the mouse is moved while dragging over a component. As I've described, the data that is passed to the method includes the drop location. As such, you can easily query this and accept or reject the drag based on the element you're dragging over. And of course, Swing's Drag and Drop implementation will show a valid drop cursor and indicate the drop location when you accept, and a no-drag cursor and no drop indicator when you reject. Exactly as you've requested :)

    Posted by: shan_man on January 16, 2006 at 09:27 AM


  • Hello swapnonil, and thank you for the offer of extra body parts. But I'll ask you to hold on to them a little longer, as we're still planning to fix this problem under our own will. ;-)


    If we cannot find an underlying API on native platforms, we will still do something to resolve this problem. Romain's demo is quite beautiful, and shows that there is definitely potential to use a GlassPane. The one drawback to the approach, though, is that it limits the visual representation to within the Swing window. Obviously we'd like to have something that crosses window boundaries, but it's comforting to know that there are multiple potential implementations. Thanks again!

    Posted by: shan_man on January 16, 2006 at 09:36 AM

  • Shannon, Thank you for your very positive and kind reply.I really like canImport being called on every mouse move. I couldn't have requested anything better than that. :)About my idea of mutable models: I fear, I haven't enough time to actively and reliably drive a SwingLabs project. I quickly made a draft page along with sample code and javadoc here. It would be nice if you could glance it over, and tell me whether something like this could serve as a starting point for an RFE. Maybe there is lots in my draft page which wont work for such use, which you can spot immediately.I am looking forward to see more exciting blogs about DnD enhancements by you.

    Posted by: wrandelshofer on January 16, 2006 at 11:43 AM


  • Prompted by wrandelshofer's recent question about accepting or rejecting drags based on the element in question, I've posted a full blog entry on the subject. Please see Location-Sensitive Drag and Drop in Mustang if you'd like to learn more. Thanks!

    Posted by: shan_man on January 17, 2006 at 12:48 PM

  • Hi Shannon, I currently migrated to Mustang to get the new DND support. It's working fine but we are missing additional modes for JTable like ON_ROW and ON_ROW_OR_INSERTS_ROWS to get the complete row highlighted instead of a single cell. is this feasible and reasonable?
    Thanks, Tobias


    Posted by: tobias_schlegl on January 31, 2006 at 01:38 AM


  • Wow Tobias! Like wrandelshofer, you've just asked an excellent question that has prompted me to start writing another blog entry on the subject. Please watch for it to be posted soon. For now, I'll give you a short version of the answer without too many of the technical details.


    What you've asked for is most definitely feasible and reasonable. However, it doesn't require any additions to the JTable API at all, and you can accomplish it now with relative ease and just a small amount of code. You see, setting the DropMode simply tells the table how to determine drop locations; and the code responsible for actually displaying those locations is separate, contained in the pluggable rendering code of the component. As such, the existing ON modes are all you need.


    When in any of the ON modes, an ON location is represented in terms of the row and column of the item that the drag is over. The default cell renderer for tables checks these indices when rendering each cell and if they match, highlights it with the drop color. It's quite simple for you to plug in a replacement renderer that highlights the cell if the row matches (regardless of the column), hence highlighting all cells in the drop location row. I'll discuss exactly how to do this, and include code samples, in my upcoming entry.


    I look forward to it!

    Posted by: shan_man on February 15, 2006 at 06:34 PM

  • Hi Shannon,

    It's really great that you guys are doing this! It will really make a difference to having nice, usable DnD apps written in Swing.

    I'm working on a project that really needs this stuff, but for a while yet, can't really switch to 1.6. Do you have, or know of a general implementation that takes the same approach that could be hacked in over 1.5? ... like substantial subclasses of JTable, TableUI and TransferHandler that generate useful events. I would be most appreciative

    thanks,
    Artyom Ash

    Posted by: artyom on June 12, 2006 at 12:33 PM

  • The demo used to work with earlier version of Mustang; It seems to be broken with beta 2 release.

    Posted by: amirk on June 28, 2006 at 02:27 PM

  • The webstart demo is still broken in build 102

    Posted by: os2baba on October 27, 2006 at 10:58 PM


  • artyom: Thanks for your question! Unfortunately I don't know of any implementation that provides for 5.0 what we've implemented in JDK 6.

    Posted by: shan_man on November 21, 2006 at 01:36 PM


  • amirk and os2baba: Thanks for pointing out that the demos stopped working! This blog was published before the Swing drag and drop API for Java SE 6 was finalized. There have since been minor modifications which have affected the working of these demos.


    I have now updated the demos for the newer API, and added notes in the blog content where appropriate. For more details on these later API changes, please see my later blog entitled Choosing the Drop Action, and Further Changes to Swing Drag and Drop. Thanks!

    Posted by: shan_man on November 22, 2006 at 12:12 PM

  • I am writing an example wih drag-n-drop in JTree. I've wrote TransferHandler for my JTree and inserting new nodes by dragging from another frame in my application. And when I try to insert a node after the last position of JTree structure I always got the same exception:

    2007-02-14 17:18:03,375 ERROR Unexpected error in thread AWT-EventQueue-0
    - - - - - - - -
    [AWT-EventQueue-0] com.tibbo.platform.util.PlatformUtils.showError (PlatformUtils.java:39)
    java.lang.NullPointerException
    at javax.swing.plaf.basic.BasicTreeUI.getDropLineRect(BasicTreeUI.java:1313)
    at javax.swing.plaf.basic.BasicTreeUI.access$1700(BasicTreeUI.java:45)
    at javax.swing.plaf.basic.BasicTreeUI$Handler.repaintDropLocation(BasicTreeUI.java:3482)
    at javax.swing.plaf.basic.BasicTreeUI$Handler.propertyChange(BasicTreeUI.java:3468)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:339)
    at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:276)
    at java.awt.Component.firePropertyChange(Component.java:7865)
    at javax.swing.JTree.setDropLocation(JTree.java:1425)
    at javax.swing.TransferHandler$DropHandler.setComponentDropLocation(TransferHandler.java:1376)
    at javax.swing.TransferHandler$DropHandler.cleanup(TransferHandler.java:1494)
    at javax.swing.TransferHandler$DropHandler.drop(TransferHandler.java:1474)
    at java.awt.dnd.DropTarget.drop(DropTarget.java:430)
    at javax.swing.TransferHandler$SwingDropTarget.drop(TransferHandler.java:1193)
    at sun.awt.dnd.SunDropTargetContextPeer.processDropMessage(SunDropTargetContextPeer.java:500)
    at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchDropEvent(SunDropTargetContextPeer.java:812)
    at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:736)
    at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:30)
    at java.awt.Component.dispatchEventImpl(Component.java:4267)
    at java.awt.Container.dispatchEventImpl(Container.java:2116)
    at java.awt.Component.dispatchEvent(Component.java:4240)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4322)
    at java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:4057)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3911)
    at java.awt.Container.dispatchEventImpl(Container.java:2102)
    at java.awt.Window.dispatchEventImpl(Window.java:2429)
    at java.awt.Component.dispatchEvent(Component.java:4240)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

    I think there is some problems in class BasicTreeUI.
    Maybe this was fixed in next Mustang distributions. I have jdk6 version: "1.6.0-b105".
    By the way. There is an unused variable row at BasicTreeUI.1282:
    " int row = tree.getRowForPath(path);"

    Is it really a bug? Or what is that?

    Posted by: bronto on February 14, 2007 at 07:04 AM

  • I've solved the problem. The cause was that during TransferHandler.importData() I called DefaultTreeModel.nodesWereInserted(); inside of
    SwingUtilities.invokeLater(). As I understand after that EventDispatchThread tryed to execute getDopLocation for node that was not visible yet in TreeModel (invokeLater() draw it really later:)). I removed invokeLater wrapper and the problem disappeared. drag-n-drop is being executed a little longer when TransferHandler.importData() is done?? May be I am wrong. But I think that new drag-n-drop engine is not safety enough. Shannon! Make review for BasicTreeUI.java What do you think about "int row = tree.getRowForPath(path);" and NullPointerException in "if (rect.y != 0) {
    rect.y--;
    }"

    Posted by: bronto on February 15, 2007 at 04:16 AM


  • Hi bronto. Glad to hear you've solved the problem. In response to your post, I've reviewed the code of BasicTreeUI. I agree that the variable "row" is not being used and I've removed it. As for the "rect" variable, it seems to me that it should never be null if the tree is in an consistent state, which it always should be during drag and drop.


    Your use of invokeLater seems like it may leave the tree temporarily in an inconsistent state. Did your code add the nodes immediately to the model, but not fire nodesWereInserted until later? If this is the case, I can imagine exceptions occurring in random places such as what you've seen here.


    I'd be happy to look at the problem some more, and even take another look at BasicTreeUI, if you could provide me more details or perhaps a test case.

    Thanks!

    Posted by: shan_man on February 20, 2007 at 09:52 AM

  • Hallo, Shannon! I'm glad to help you improve the Mustang. I continue my work with dnd and understand much more about it. And now I see that my "bug" report was abit hurried. Excuse me for my incompetent reports! You are right! The problem was with inconsistent state of jtree during my manipulations with it. More understanding help me to work with dnd correctly in present.
    By the way. In present I have another question. If I set DropMode.ON_OR_INSERT for my jtree and examine it's behavior during dnd I notice that it stops to auto-expand group-nodes when I try to drag on it. Any ideas about it? Did you face this defect or received any reports about it?

    Posted by: bronto on March 02, 2007 at 01:39 AM

  • JTree is not able to initate a drag once you provide your own TransferHandler implementation. I don't know what the problem is but I was happy about the news about new drag and drop support, but once I started to look into incorporating the new drag and drop in our application, I ran into this issue on my first steps ... why can't a JTree initiate the drag when you set your own transfer handler?

    Posted by: seane on August 31, 2007 at 04:07 PM

  • seane: JTree definitely can initiate drags once you replace the default TransferHandler. Just remember that replacing the default TransferHandler means that you're now responsible for providing the list of allowed source actions, and for creating the Transferable in response to a drag gesture. Your Transferable will need to override getSourceActions and createTransferable. Also, don't forget to call setDragEnabled on the JTree. Thanks!

    Posted by: shan_man on August 31, 2007 at 06:24 PM

  • Shanon, I may be doing something wrong but I have done all of that, but the thing is when click on a node to drag, there is no gesture indicating a drag started. I have posted the simple code on java.net forum (http://forums.java.net/jive/thread.jspa?threadID=30347&tstart=0) of course the actual code is a more complicated but this code that can be compiled, shows that there is no gesture shown, and I even have tried to add a mouse motion listener to use it to do a exportAsDrag, but again it seemed that mouse motion listener was completley ignored too ...

    Posted by: seane on August 31, 2007 at 06:45 PM

  • seane: But the code that you've posted in the forum doesn't do what I've suggested. It doesn't override getSourceActions and createTransferable. And since it doesn't claim to support any source actions, and it doesn't know how to create a transferable, dragging does nothing. I'll post a bit of sample code in the forum for you...

    Posted by: shan_man on August 31, 2007 at 08:44 PM

  • Hi Shanon, I have implemented DnD of wrapped images in our application. Using the TransferHandler and the createTransferable method and other methods as outlined above.

    I can transfer my images between JTables and JLists etc and all works just fine. However, I now need to drag these transferables (images) on to the native OS such as WinXP.

    How would you go about doing this using a TransferHandler?
    Thanks

    Posted by: paulgain on December 11, 2007 at 08:57 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds