Source for java.awt.dnd.DropTarget

   1: /* DropTarget.java -- 
   2:    Copyright (C) 2002, 2003, 2004  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.awt.dnd;
  40: 
  41: import java.awt.Component;
  42: import java.awt.GraphicsEnvironment;
  43: import java.awt.HeadlessException;
  44: import java.awt.Insets;
  45: import java.awt.Point;
  46: import java.awt.Rectangle;
  47: import java.awt.datatransfer.FlavorMap;
  48: import java.awt.datatransfer.SystemFlavorMap;
  49: import java.awt.dnd.peer.DropTargetPeer;
  50: import java.awt.event.ActionEvent;
  51: import java.awt.event.ActionListener;
  52: import java.awt.peer.ComponentPeer;
  53: import java.awt.peer.LightweightPeer;
  54: import java.io.Serializable;
  55: import java.util.EventListener;
  56: import java.util.TooManyListenersException;
  57: 
  58: import javax.swing.Timer;
  59: 
  60: /**
  61:  * @author Michael Koch
  62:  * @since 1.2
  63:  */
  64: public class DropTarget
  65:   implements DropTargetListener, EventListener, Serializable
  66: {
  67:   /**
  68:    * Compatible with JDK 1.2+
  69:    */
  70:   private static final long serialVersionUID = -6283860791671019047L;
  71: 
  72:   protected static class DropTargetAutoScroller
  73:     implements ActionListener
  74:   {
  75:     /**
  76:      * The threshold that keeps the autoscroller running.
  77:      */
  78:     private static final int HYSTERESIS = 10;
  79: 
  80:     /**
  81:      * The initial timer delay.
  82:      */
  83:     private static final int DELAY = 100;
  84: 
  85:     private Component component;
  86:     private Point point;
  87: 
  88:     /**
  89:      * The timer that triggers autoscrolling.
  90:      */
  91:     private Timer timer;
  92: 
  93:     /**
  94:      * The outer region of the scroller. This is the component's size.
  95:      */
  96:     private Rectangle outer;
  97: 
  98:     /**
  99:      * The inner region of the scroller. This is the component size without
 100:      * the autoscroll insets.
 101:      */
 102:     private Rectangle inner;
 103: 
 104:     protected DropTargetAutoScroller (Component c, Point p)
 105:     {
 106:       component = c;
 107:       point = p;
 108:       timer = new Timer(DELAY, this);
 109:       timer.setCoalesce(true);
 110:       timer.start();
 111:     }
 112: 
 113:     protected void updateLocation (Point newLocn)
 114:     {
 115:       Point previous = point;
 116:       point = newLocn;
 117:       if (Math.abs(point.x - previous.x) > HYSTERESIS
 118:           || Math.abs(point.y - previous.y) > HYSTERESIS)
 119:         {
 120:           if (timer.isRunning())
 121:             timer.stop();
 122:         }
 123:       else
 124:         {
 125:           if (! timer.isRunning())
 126:             timer.start();
 127:         }
 128:     }
 129: 
 130:     protected void stop ()
 131:     {
 132:       timer.start();
 133:     }
 134: 
 135:     public void actionPerformed (ActionEvent e)
 136:     {
 137:       Autoscroll autoScroll = (Autoscroll) component;
 138: 
 139:       // First synchronize the inner and outer rectangles.
 140:       Insets i = autoScroll.getAutoscrollInsets();
 141:       int width = component.getWidth();
 142:       int height = component.getHeight();
 143:       if (width != outer.width || height != outer.height)
 144:         outer.setBounds(0, 0, width, height);
 145:       if (inner.x != i.left || inner.y != i.top)
 146:         inner.setLocation(i.left, i.top);
 147:       int inWidth = width - i.left - i.right;
 148:       int inHeight = height - i.top - i.bottom;
 149:       if (inWidth != inner.width || inHeight != inner.height)
 150:         inner.setSize(inWidth, inHeight);
 151: 
 152:       // Scroll if the outer rectangle contains the location, but the
 153:       // inner doesn't.
 154:       if (outer.contains(point) && ! inner.contains(point))
 155:         autoScroll.autoscroll(point);
 156:     }
 157:   }
 158: 
 159:   private Component component;
 160:   private FlavorMap flavorMap;
 161:   private int actions;
 162:   private DropTargetPeer peer;
 163:   private DropTargetContext dropTargetContext;
 164:   private DropTargetListener dropTargetListener;
 165:   private DropTarget.DropTargetAutoScroller autoscroller;
 166:   private boolean active = true;
 167:     
 168:   /**
 169:    * Creates a <code>DropTarget</code> object.
 170:    *
 171:    * @exception HeadlessException If GraphicsEnvironment.isHeadless()
 172:    * returns true.
 173:    */
 174:   public DropTarget ()
 175:   {
 176:     this (null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null);
 177:   }
 178:   
 179:   /**
 180:    * Creates a <code>DropTarget</code> object.
 181:    *
 182:    * @exception HeadlessException If GraphicsEnvironment.isHeadless()
 183:    * returns true.
 184:    */
 185:   public DropTarget (Component c, DropTargetListener dtl)
 186:   {
 187:     this (c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null);
 188:   }
 189:   
 190:   /**
 191:    * Creates a <code>DropTarget</code> object.
 192:    *
 193:    * @exception HeadlessException If GraphicsEnvironment.isHeadless()
 194:    * returns true.
 195:    */
 196:   public DropTarget (Component c, int i, DropTargetListener dtl)
 197:   {
 198:     this (c, i, dtl, true, null);
 199:   }
 200:   
 201:   /**
 202:    * Creates a <code>DropTarget</code> object.
 203:    *
 204:    * @exception HeadlessException If GraphicsEnvironment.isHeadless()
 205:    * returns true.
 206:    */
 207:   public DropTarget (Component c, int i, DropTargetListener dtl, boolean b)
 208:   {
 209:     this (c, i, dtl, b, null);
 210:   }
 211:   
 212:   /**
 213:    * Creates a <code>DropTarget</code> object.
 214:    *
 215:    * @exception HeadlessException If GraphicsEnvironment.isHeadless()
 216:    * returns true.
 217:    */
 218:   public DropTarget (Component c, int i, DropTargetListener dtl, boolean b,
 219:                      FlavorMap fm)
 220:   {
 221:     if (GraphicsEnvironment.isHeadless ())
 222:       throw new HeadlessException ();
 223: 
 224:     setComponent(c);
 225:     setDefaultActions(i);
 226:     dropTargetListener = dtl;
 227:     
 228:     if (fm == null)
 229:       flavorMap = SystemFlavorMap.getDefaultFlavorMap();
 230:     else
 231:       flavorMap = fm;
 232:     
 233:     setActive (b);
 234:     
 235:     if (c != null)
 236:       c.setDropTarget(this);
 237:   }
 238: 
 239:   /**
 240:    * Sets the component associated with this drop target object.
 241:    */
 242:   public void setComponent (Component c)
 243:   {
 244:     if (component != null)
 245:       clearAutoscroll();
 246:     component = c;
 247:   }
 248: 
 249:   /**
 250:    * Returns the component associated with this drop target object.
 251:    */
 252:   public Component getComponent ()
 253:   {
 254:     return component;
 255:   }
 256: 
 257:   /**
 258:    * Sets the default actions.
 259:    */
 260:   public void setDefaultActions (int ops)
 261:   {
 262:     actions = ops;
 263:   }
 264: 
 265:   /**
 266:    * Returns the default actions.
 267:    */
 268:   public int getDefaultActions ()
 269:   {
 270:     return actions;
 271:   }
 272: 
 273:   public void setActive (boolean active)
 274:   {
 275:     this.active = active;
 276:     if (! active)
 277:       clearAutoscroll();
 278:   }
 279: 
 280:   public boolean isActive()
 281:   {
 282:     return active;
 283:   }
 284: 
 285:   /**
 286:    * Adds a new <code>DropTargetListener</code>.
 287:    * 
 288:    * @exception TooManyListenersException Sun's JDK does not, despite
 289:    * documentation, throw this exception here when you install an additional
 290:    * <code>DropTargetListener</code>.  So to be compatible, we do the same
 291:    * thing.
 292:    */
 293:   public void addDropTargetListener (DropTargetListener dtl)
 294:     throws TooManyListenersException
 295:   {
 296:     if (dtl == null)
 297:       return;
 298:     
 299:     if (dtl.equals(this))
 300:       throw new IllegalArgumentException();
 301:     
 302:     if (dropTargetListener != null)
 303:       throw new TooManyListenersException();
 304:     
 305:     dropTargetListener = dtl;
 306:   }
 307: 
 308:   public void removeDropTargetListener(DropTargetListener dtl)
 309:   {
 310:     if (dropTargetListener != null)
 311:       dropTargetListener = null;
 312:   }
 313: 
 314:   public void dragEnter(DropTargetDragEvent dtde)
 315:   {
 316:     if (active)
 317:       {
 318:         if (dropTargetListener != null)
 319:           dropTargetListener.dragEnter(dtde);
 320:         initializeAutoscrolling(dtde.getLocation());
 321:       }
 322:   }
 323: 
 324:   public void dragOver(DropTargetDragEvent dtde)
 325:   {
 326:     if (active)
 327:       {
 328:         if (dropTargetListener != null)
 329:           dropTargetListener.dragOver(dtde);
 330:         updateAutoscroll(dtde.getLocation());
 331:       }
 332:   }
 333: 
 334:   public void dropActionChanged(DropTargetDragEvent dtde)
 335:   {
 336:     if (active)
 337:       {
 338:         if (dropTargetListener != null)
 339:           dropTargetListener.dropActionChanged(dtde);
 340:         updateAutoscroll(dtde.getLocation());
 341:       }
 342:   }
 343: 
 344:   public void dragExit(DropTargetEvent dte)
 345:   {
 346:     if (active)
 347:       {
 348:         if (dropTargetListener != null)
 349:           dropTargetListener.dragExit(dte);
 350:         clearAutoscroll();
 351:       }
 352:   }
 353: 
 354:   public void drop(DropTargetDropEvent dtde)
 355:   {
 356:     clearAutoscroll();
 357:     if (dropTargetListener != null)
 358:       dropTargetListener.drop(dtde);
 359:   }
 360: 
 361:   public FlavorMap getFlavorMap()
 362:   {
 363:     return flavorMap;
 364:   }
 365: 
 366:   public void setFlavorMap(FlavorMap fm)
 367:   {
 368:     flavorMap = fm;
 369:   }
 370: 
 371:   public void addNotify(ComponentPeer p)
 372:   {
 373:     Component c = component;
 374:     while (c != null && p instanceof LightweightPeer)
 375:       {
 376:         p = c.getPeer();
 377:         c = c.getParent();
 378:       }
 379: 
 380:     if (p instanceof DropTargetPeer)
 381:       {
 382:         peer = ((DropTargetPeer) p);
 383:         peer.addDropTarget(this);
 384:       }
 385:     else
 386:       peer = null;
 387:   }
 388: 
 389:   public void removeNotify(ComponentPeer p)
 390:   {
 391:     ((DropTargetPeer) peer).removeDropTarget(this);
 392:     peer = null;
 393:     p = null;
 394:   }
 395: 
 396:   public DropTargetContext getDropTargetContext()
 397:   {
 398:     if (dropTargetContext == null)
 399:       dropTargetContext = createDropTargetContext ();
 400:     
 401:     return dropTargetContext;
 402:   }
 403: 
 404:   protected DropTargetContext createDropTargetContext()
 405:   {
 406:     if (dropTargetContext == null)
 407:       dropTargetContext = new DropTargetContext (this);
 408:     
 409:     return dropTargetContext;
 410:   }
 411: 
 412:   protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller
 413:                                                        (Component c, Point p)
 414:   {
 415:     return new DropTarget.DropTargetAutoScroller (c, p);
 416:   }
 417: 
 418:   protected void initializeAutoscrolling(Point p)
 419:   {
 420:     if (component instanceof Autoscroll) // Checks for null too.
 421:       autoscroller = createDropTargetAutoScroller (component, p);
 422:   }
 423: 
 424:   protected void updateAutoscroll(Point dragCursorLocn)
 425:   {
 426:     if (autoscroller != null)
 427:       autoscroller.updateLocation(dragCursorLocn);
 428:   }
 429: 
 430:   protected void clearAutoscroll()
 431:   {
 432:     if (autoscroller != null)
 433:       {
 434:         autoscroller.stop();
 435:         autoscroller = null;
 436:       }
 437:   }
 438: } // class DropTarget