Summary
There are several different things about desktop drag and drop that are interesting. I've been using several things in my JiniDesktop class in my http://startnow.dev.java.net project. Here's an extracted example of all the pieces.
Advertisement
When creating a drag and drop desktop like you'd find on a desktop computing environment, there are several interesting things to tend to. I've been working through these issues in my JiniDesktop application. This application looks for all ServiceUI enabled Jini services, and then enumerates them into a tree structure for access.
You can drag the service names out of the tree structure and place them on a desktop which you can then arrange by dragging the icons around. I used a JLayeredPane to make the drag operations be layered on top of the static operations more easily.
My JDesktopItem class buried inside of the JiniDesktop class has much more too it then shown here, including some different class structure. I extracted the parts that I thought would be the most interesting and have included them below.
The DeskItem class
I used JLabel as the parent class so that I could also associate an Icon with the text of the service name. I haven't included support or use of Icons here to simplify the visible code.
I'm just going to include all of the code here for now, and my follow up with some point by point discussion if anyone is interested. There are two classes here, if you put them into the appropriately named files, adding the necessary imports, you should be able to compile them. Run the DeskExample class and you'll see how the draging on the desktop works including hover control and selection highlighting.
I used gradient drawing to make everything look fancy. You can, of course do some different things with the drawing.
public class DeskItem extends JLabel {
/**
* The original UI that we revert to when not selected, hovered or otherwise active.
*/
private ComponentUI oldUI;
/**
* Is this DeskItem in a selected state?
*/
protected boolean selected;
/**
* The JLayeredPane we are on, if any.
*/
private JLayeredPane pane;
/**
* The MouseMotionListener created to manage our drag state.
*/
private MyMouseMotionListener mml;
/**
* The ComponentUI used for normal rendering.
*/
protected static MyUI myui = new MyUI();
/**
* The ComponentUI used for active rendering (hover).
*/
protected static MyUI actui = new MyUI( new Color( 255, 205, 215) );
/**
* The ComponentUI used for selected state.
*/
protected static MyUI selui = new MyUI( new Color( 230, 230, 255 ) );
/**
* The ComponentUI used for selected and active rendering (hover).
*/
protected static MyUI selactui = new MyUI( new Color( 180, 180, 255 ) );
/**
* Should gradient rendering be down at all?
*/
protected static boolean gradients = true;
/**
* The last DeskItem that was selected.
*/
private static DeskItem lastItem;
/**
* The current, active DeskItem.
*/
private static DeskItem curItem;
/**
* Logger for this class
*/
private static Logger log = Logger.getLogger( DeskItem.class.getName() );
/**
* Activates all the mouse listening activities
*/
private void mouseEntry() {
addMouseListener( new MouseAdapter() {
public void mouseEntered( MouseEvent ev ) {
if( gradients ) {
setUI( selected ? selactui : actui );
setOpaque(true);
}
repaint();
}
public void mouseExited( MouseEvent ev ) {
if( gradients ) {
setUI( selected ? selui : myui );
setOpaque(selected);
}
repaint();
}
});
addMouseListener( new MyMouseAdapter() );
}
/**
* Used to deselect the last selected DeskItem so that none are active. This can
* be called when the background of the desk is clicked on, or some other focus
* change event.
*/
public static synchronized void clearSelection() {
if( lastItem != null ) {
lastItem.exit();
}
lastItem = null;
}
/**
* This is called when the item is double clicked on. A subclass can provide an
* action here.
*/
public void invoke() {
}
/**
* Called to report exception encountered during processing of events.
* @param ex The Exception to report.
*/
protected void reportException( Exception ex ) {
log.log( Level.SEVERE, ex.toString(), ex );
}
/**
* Make the selectable object bigger to surround the text
* with coloring.
*/
protected void setBorder() {
setBorder( BorderFactory.createEmptyBorder( 5,5,5,5));
}
/**
* Creates a DeskItem that is on a JLayeredPane with the passed String identifier.
* @param pane The JLayeredPane that the item will be placed on.
* @param label The Label string to use.
*/
public DeskItem( JLayeredPane pane, String label ) {
super(label);
this.pane = pane;
oldUI = getUI();
if( gradients )
setUI( myui );
mouseEntry();
}
/**
* Constructs a DeskItem that won't be placed on a JLayeredPane.
* @param label The String label for this item
*/
public DeskItem( String label ) {
super(label);
oldUI = getUI();
if( gradients )
setUI( myui );
mouseEntry();
}
/**
* Called when the mouse enters this DeskItem.
*/
protected void enter() {
selected = true;
if( gradients ) {
setUI( selui );
}
setOpaque( true );
repaint();
curItem = this;
}
/**
* Called when the mouse exits this DeskItem.
*/
protected void exit() {
curItem = null;
selected = false;
setOpaque( false );
repaint();
}
/**
* Called when the user performs a context menu click using the popup trigger button/operation
* associated with the component.
*
* Subclasses can override this method to provide an operation.
*
* @param ev The associated mouse event to get the context from.
*/
protected void popup( MouseEvent ev ) {
}
/**
* The MouseAdapter that handles click events to establish the MouseMotionListener.
*/
private class MyMouseAdapter extends MouseAdapter {
/**
* The item this listener is associated with.
*/
DeskItem item;
/**
* Creates an instance.
*/
public MyMouseAdapter() {
this.item = DeskItem.this;
}
/**
* Called when the user clicks on the DeskItem.
* @param ev The event associated with the click operation.
*/
public void mouseClicked( MouseEvent ev ) {
if( ev.getClickCount() == 2 && ev.getButton() == 1 ) {
log.finer("Invoking: "+DeskItem.this);
try {
// Invoke the item to process any action.
item.invoke();
} catch( Exception ex ) {
reportException(ex);
}
}
}
/**
* Called when the mouse is pressed down.
* @param ev The associated event.
*/
public void mousePressed( MouseEvent ev ) {
// Check if popup
if( ev.isPopupTrigger() ) {
// switch selected to this item
if( lastItem != DeskItem.this ) {
lastItem.exit();
}
lastItem = DeskItem.this;
lastItem.enter();
// Show the menu if any
popup(ev);
return;
}
// Not popup, so clear last selected
if( lastItem != null ) {
lastItem.exit();
}
// If not button 1, just ignore
if( ev.getButton() != 1 )
return;
// Activate motion listener and what for drag.
createMotionListener(ev);
// Add the mouse listener created, or already existing.
DeskItem.this.addMouseMotionListener(mml);
}
/**
* called to create the MouseMotionListener when the drag operation starts.
* @param ev The associated mouse event.
*/
private void createMotionListener(MouseEvent ev) {
lastItem = DeskItem.this;
lastItem.enter();
Point p = lastItem.getLocation();
final int offx = ev.getX();
final int offy = ev.getY();
// Create new listener as needed
if( mml == null ) {
mml = new MyMouseMotionListener();
}
// Set drag offsets into object
mml.setOffsets( offx, offy );
}
/**
* Called when the mouse button is released.
* @param ev The associated event.
*/
public void mouseReleased( MouseEvent ev ) {
if( ev.isPopupTrigger() ) {
// Make sure the correct last item is identified.
if( lastItem != null && lastItem != DeskItem.this ) {
lastItem.exit();
}
lastItem = DeskItem.this;
lastItem.enter();
popup(ev);
// Stop listening to mouse motion events.
if( mml != null )
DeskItem.this.removeMouseMotionListener(mml);
return;
}
// Not popup, remove motion listener
DeskItem.this.removeMouseMotionListener(mml);
// When dropped, move back to the default layer.
if( pane != null ) {
pane.setLayer( DeskItem.this,
JLayeredPane.DEFAULT_LAYER.intValue(), 0 );
}
}
}
/**
* The MouseMotionAdapter used to track the drag operation.
*/
private class MyMouseMotionListener extends MouseMotionAdapter {
/**
* The x offset of the initial mouse click from the left edge of the DeskItem.
*/
int offx;
/**
* The y offset of the mouse from the top of the DeskItem
*/
int offy;
/**
* updates the current offsets for each successive drag operation to the click point that the
* mouse was out when the mouse was pressed.
* @param x The X location of the initial mouse down event.
* @param y The Y location of the initial mouse down event.
*/
public void setOffsets(int x, int y ) {
offx = x;
offy = y;
}
/**
* Called when the mouse is moved without a button down.
* @param ev The associated event for this operation.
*/
public void mouseMoved( MouseEvent ev ) {
mouseDragged(ev);
}
/**
* Called when the mouse is moved with button one down.
* @param ev The associated mouse event.
*/
public void mouseDragged( MouseEvent ev ) {
Point pt = getLocation();
Point p = new Point( ev.getX()+pt.x-offx,
ev.getY()+pt.y-offy);
// Positioning is every 5 pixels to make it easier to line things up.
int xoff = p.x % 5;
int yoff = p.y % 5;
p = new Point( p.x-xoff+5, p.y-yoff+5);
// On a JDesktopPane, change the layer so that
// we pass over everything on the desktop
if( pane != null ) {
pane.setLayer( DeskItem.this,
JLayeredPane.DRAG_LAYER.intValue() );
}
setLocation( p.x, p.y );
}
}
/**
* The UI used to control the drawing of the DeskItem without having to conditionalize
* the paint operations.
*/
private static class MyUI extends com.sun.java.swing.plaf.windows.WindowsLabelUI {
/**
* The color of the background.
*/
Color col;
/**
* Constructs an instance with the default coloring using the default background color
* associated with the look and feel.
*/
public MyUI() {
col = new JLabel().getBackground();
}
/**
* Constructs and instance using the passed color for the background.
* @param c The color to use for the background painting operations.
*/
public MyUI( Color c ) {
col = c;
}
/**
* Called to perform the paint operation on the passed component.
* @param g The graphics context for the paint operation.
* @param c The component to paint into the graphics environment.
*/
public void update(Graphics g, JComponent c) {
if( !gradients ) {
super.update( g, c );
return;
}
if (c.isOpaque()) {
Graphics2D g2 = (Graphics2D)g;
Paint pt = g2.getPaint();
g2.setPaint( new GradientPaint( c.getWidth(), 0, col.darker(),
c.getWidth(), c.getHeight(), col ) );
g.fillRoundRect(0, 0, c.getWidth(),c.getHeight(), 5, 5 );
g2.setPaint( pt );
}
super.paint(g, c);
}
}
}
The DeskExample class
This is the main class which provides the example desktop with some DeskItems on it.
Drag them around and see how it all works.
public class DeskExample {
Logger log = Logger.getLogger( getClass().getName() );
public static void main( String args[] ) {
new DeskExample();
}
/** Creates a new instance of DeskExample */
public DeskExample() {
JFrame f = new JFrame("Example desktop dragging");
JDesktopPane pane = new JDesktopPane() {
// Use the background color of a known component.
Color col = new Color( 200, 235, 210 );
public void paintComponent( Graphics g ) {
Graphics2D g2 = (Graphics2D)g;
Paint pt = g2.getPaint();
int w = getSize().width;
int h = getSize().height;
g2.setPaint( new GradientPaint( w/4, h/2, col.brighter(),
w, h, col.darker() ) );
g2.fillRect( 0, 0, w, h );
g2.setPaint( pt );
}
};
pane.setOpaque( true );
Packer pk = new Packer( f.getContentPane() );
pk.pack( pane ).fillboth();
for( int i = 0; i < 10; ++i ) {
DeskItem item = new DeskItem( pane, "Item #"+i );
log.info("adding item: "+item);
pane.add( item );
pane.setLayer( item, JLayeredPane.DEFAULT_LAYER.intValue() );
item.setLocation( 10, i*30 );
item.setVisible(true);
item.setSize( item.getPreferredSize() );
item.setForeground( Color.black );
}
f.pack();
f.setSize( 400, 300 );
f.setLocationRelativeTo( null );
f.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent ev ) {
System.exit(1);
}
});
f.setVisible( true );
}
}
this is interesting source code. What you here on the theme "DnD" with the Swing library, distribute. How do you approach it makes sense to use a professional Swing JavaBeans? The application is based on two JSplitPane. On the left page is on the button of the first to the second. Top with a JTree, down with a JList. In the right button is the last component JLayeredPane swing. A apply added, technical JavaBean JTree is visible. By DnD, this professional JavaBean on the so-called bench. Then in JList the properties for the configuration visible. My horizon ends at DnD for a JavaBean. How do I DnD for a JavaBean. Capsules in a JLabel perhaps?
Kind regards, FolkertM
[DE] Das ist interessanter Quellkode. Welchen Du hier, zu dem Thema "DnD" mit der Swing Bibliothek, veröffentlichst. Wie, meinst Du ist es sinnvoll vorzugehen um in einer Swing Anwendung fachliche JavaBeans hinzufügen? Die Anwendung basiert auf zwei JSplitPane. Auf der linken Seite ist im Button des Ersten, das Zweite. Oben mit einem JTree, unten mit einem JList. In dem rechten Button ist als letzte Swing Komponente ein JLayeredPane. Eine zur Anwendung, hinzugefügte, fachliche JavaBean ist im JTree sichtbar. Per DnD kommt diese fachliche JavaBean auf die sogenannte Werkbank. Dann sind im JList die Eigenschaften für die Konfiguration sichtbar. Mein Horizont endet beim DnD für eine JavaBean. Wie verwende ich DnD für eine JavaBean. Kapseln in einem JLabel vielleicht?
Googles language conversions leave something to be desired... I think I understand part of what you are saying. But, I'm not sure if the question is about making DnD work in a JavaBean, or about the particulars of your screen layout, and which items you want to drag to which other components.