TransferHandler.exportDone will nicht so richtig

Status
Nicht offen für weitere Antworten.

max60watt

Mitglied
Hallo zusammen,
nachdem ich einige Zeit gegoogelt und auch in diesem Forum geblättert habe, ist es mir gelungen ein Drag und Drop mit eigenem TransferHandler aufzubauen.
Das Transfer-Objekt ist kein Standard Objekt, sondern ein selbstgebautes JPanel mit JTextField, JLabel+Icon und einer JCheckBox.

Der Transfer klappt auch wunderbar, das MovePanel wird im DragFrame aufgenommen und im DropFrame abgelegt. Allerdings möchte ich beim Ablegen prüfen, ob ein COPY oder MOVE stattgefunden hat.
Laut API kann ich das in der Methode exportDone(JComponent source, Transferable data, int action) der Klasse TransferHandler. Diese Methode habe ich eingebaut (Zeile 68 im DragFrame). Allerdings bekomme ich beim droppen im DropFrame immer eine 0 (TransferHandler.NONE) zurück.
Merkwürdigerweise bekomme ich bei folgenden Konstellationen den richtigen Wert:

Beim droppen in KWrite: KWrite fügt das Objekt nicht ein, aber die action von exportDone führt den richtigen Wert an.
Bein droppen in das eigene MovePanel: Wenn ich den Dropvorgang starte (z.B. MovePanel #2) und ich die Maus in der Position zwischem dem TextField und CheckBox des MovePanel #2 loslasse, wird auch der richtige action Wert angezeigt.

Hat jemand eine Idee, wieso beim DropPanel nur 0 zurückgegeben wird?


Grüss Max


Das Panel das per DnD verschoben werden soll:
Code:
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;

public class MovePanel extends JPanel{
  JTextField textField = new JTextField();
  JLabel imageLabel = new JLabel();
  JCheckBox checkbox = new JCheckBox("Check Box" ,true);
  int id;
  String string;
  Color color;
  private DragSource dragSource;
//------------------------------------------------------------------------------------------------
  public MovePanel(String s, Color c, int x, int y, int i){
    string = s;
    color = c;
    id = i;
    setLayout(null);
    setLocation(x, y);
    setSize(180,160);
    setBackground(Color.WHITE);
    setBorder(new LineBorder(Color.black));

    imageLabel.setBounds(10,10,160,120);
    imageLabel.setIcon(new ImageIcon(createImage(string, color)));
    textField.setText(string);
    textField.setBounds(10,137,100,19);
    checkbox.setBounds(142,137,18,19);

    add(imageLabel);
    add(textField);
    add(checkbox);

  }
//------------------------------------------------------------------------------------------------
  public void setMovePanelLocation(Point p){
    setLocation(p);
  }
//------------------------------------------------------------------------------------------------
  public void setMovePanelLocation(int x,int y){
    setLocation(x,y);
  }
//------------------------------------------------------------------------------------------------
  public int getId(){
    return id;
  }
//------------------------------------------------------------------------------------------------
  public String toString(){
    return "MovePanel";
  }
//------------------------------------------------------------------------------------------------
  public BufferedImage createImage(String s, Color c){
    BufferedImage bufferedImage = new BufferedImage(162, 120, BufferedImage.TYPE_3BYTE_BGR);
    Graphics g = bufferedImage.createGraphics();

    g.setColor(c);
    g.fillRect(0,0,160,120);
    g.setColor(Color.BLUE);
    g.drawString(s, 10,20);
    return bufferedImage;
  }
//------------------------------------------------------------------------------------------------
}



Das DragFrame incl. TransferHandler
Code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.*;


public class DragFrame extends JFrame{
MovePanel myPanel01 = new MovePanel("Eins", Color.red,10,10,0);
MovePanel myPanel02 = new MovePanel("Zwei", Color.green,200,10,1);
//------------------------------------------------------------------------------------------------
  public DragFrame(){
    super("Drag Frame");
    setLayout(null);

    myPanel01.setTransferHandler(new PanelTransfer());
    myPanel02.setTransferHandler(new PanelTransfer());

    MouseListener listener = new MouseAdapter() {
      public void mousePressed(MouseEvent me) {
        JComponent comp = (JComponent) me.getSource();
        TransferHandler handler = comp.getTransferHandler();
        handler.exportAsDrag(comp, me, TransferHandler.COPY);
      }
    };

    myPanel01.addMouseListener(listener);
    myPanel02.addMouseListener(listener);

    add(myPanel01);
    add(myPanel02);
  }
//------------------------------------------------------------------------------------------------
  public void rmPanel(int i){
    System.out.println("DragFrame#rmPanel(int i) " + i);
  }
//------------------------------------------------------------------------------------------------
  public static void main(String args[]){
  DragFrame app = new DragFrame();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.setBounds(10,10,400,260);
    app.setVisible(true);
  }
//------------------------------------------------------------------------------------------------
}
//================================================================================================
class PanelTransfer extends TransferHandler implements Transferable {

  MovePanel movePanel;
  public static final DataFlavor panelFlavor = new DataFlavor(MovePanel.class, "Move Panel");
  private static final DataFlavor[] flavors = new DataFlavor[] {
    panelFlavor
  };
//------------------------------------------------------------------------------------------------
  public int getSourceActions(JComponent c) {
    return TransferHandler.COPY_OR_MOVE;
  }
//------------------------------------------------------------------------------------------------
  public boolean canImport(JComponent comp, DataFlavor flavor[]) {
      for (int j = 0, m = flavors.length; j < m; j++) {
        if (panelFlavor.equals(flavors[j])) {
          return true;
        }
      }
    return false;
  }
//------------------------------------------------------------------------------------------------
  protected void exportDone(JComponent comp, Transferable data, int action) {
    System.out.println("exportDone=" + action);  // <--- Action ist 0 bei drop auf DropFrame
  }
//------------------------------------------------------------------------------------------------
  public Transferable createTransferable(JComponent comp) {
    if (comp instanceof MovePanel) {
      movePanel  = (MovePanel)comp;
      return this;
    }
    return null;
  }
//------------------------------------------------------------------------------------------------
  public boolean importData(JComponent comp, Transferable t) {
    if (t.isDataFlavorSupported(flavors[0])) {
      try {
        movePanel = (MovePanel) t.getTransferData(flavors[0]);
        return true;
      }
      catch (UnsupportedFlavorException ignored) {System.out.println(ignored.getMessage());}
      catch (IOException ignored) {System.out.println(ignored.getMessage());}
    }
  return false;
  }
//------------------------------------------------------------------------------------------------
  public Object getTransferData(DataFlavor flavor) {
    if (isDataFlavorSupported(flavor)) {
      return movePanel;
    }
    return null;
  }
//------------------------------------------------------------------------------------------------
  public DataFlavor[] getTransferDataFlavors() {
    return flavors;
  }
//------------------------------------------------------------------------------------------------
  public boolean isDataFlavorSupported(DataFlavor flavor) {
    return flavors[0].equals(flavor);
  }
//------------------------------------------------------------------------------------------------
}



Das DropFrame:
Code:
import java.awt.*;
import java.awt.dnd.*;
import javax.swing.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;

public class DropFrame extends JFrame implements DropTargetListener{
  DropTarget target = new DropTarget();
  public static final DataFlavor panelFlavor = new DataFlavor(MovePanel.class, "Move Panel");
//------------------------------------------------------------------------------------------------
  public DropFrame(){
    super("Drop Frame");
    this.setLayout(null);
    setDropTarget(target);
    try{
      target.addDropTargetListener(this);
    }
    catch (TooManyListenersException e) {
      JOptionPane.showMessageDialog(null,e.getMessage(),"TooManyListenersException",JOptionPane.INFORMATION_MESSAGE);
    }
  }
//------------------------------------------------------------------------------------------------
  public void setPanel(MovePanel m, Point p){
    MovePanel movePanel = m;
      movePanel.setLocation(p);
      add(movePanel);
    repaint();
  }
//------------------------------------------------------------------------------------------------
  public void drop(DropTargetDropEvent dtde) {
    Transferable transferable = dtde.getTransferable();
    dtde.acceptDrop(DnDConstants.ACTION_COPY);

    if(dtde.isDataFlavorSupported(panelFlavor)){
      try {
        MovePanel movePanel = (MovePanel)transferable.getTransferData(panelFlavor);
        setPanel(movePanel, dtde.getLocation());
      }
      catch (UnsupportedFlavorException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"UnsupportedFlavorException",JOptionPane.INFORMATION_MESSAGE);
      }
      catch (IOException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"IOException",JOptionPane.INFORMATION_MESSAGE);
      }
    }
  }
//------------------------------------------------------------------------------------------------
  public void dragExit(DropTargetEvent dte){}
//------------------------------------------------------------------------------------------------
  public void dropActionChanged(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
  public void dragOver(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
  public void dragEnter(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
  public static void main(String args[]){
  DropFrame app = new DropFrame();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.setBounds(420,10,400,260);
    app.setVisible(true);
  }
//------------------------------------------------------------------------------------------------
}
 
Hachja - das Drag&Drop ist.... teilweise... hm - könnte besser dokumentiert sein ... und ist etwas unübersichtlich (Wenn du mal irgendwann eine Auflistung findest, wo steht, welcher der verf***ten scheiß-drecks "int action", die überall rumfliegen, sich auf die DnDConstants beziehen, und welche auf die TransferHandler-Actions, sag' bescheid :roll: )

Ich habe mich auch noch nicht viel mit DnD beschäftigt, darum sind alle Angaben ohne Gewähr!

Du solltest das PanelTransfer aufteilen in ein
PanelTransferHandler extends TransferHandler
und
PanelTransferable implements Transferable
TransferHandler und Transferable sind "orthogonal" zueinander - haben also "nichts" miteinander zu tun. Der eine KÜMMERT sich um den Transfer, der andere WIRD transferiert. Man will nicht jedes mal, wenn man ein Objekt transferieren will, einen TransferHandler erstellen müssen. Und wenn man einen TransferHandler für eine Component setzt, dann sollte dieser TransferHanlder kein transferierbares Objekt "enthalten", wie es im Moment bei dir mit dem MovePanel aber ist....

Zum eigentlichen Problem: Soweit ich das sehe, muss man in der drop-Methode des DropTargetListeners noch explizit die dropComplete-Methode des DropTargetDropEvents aufrufen. Ich hab' das im unten stehenden Quellcode mal reingehackt (suche nach "XXX"), und noch einen kleinen Kommentar dazugeschrieben: Die importData-Methode deines TransferHandlers wird im Moment nicht wirklich verwendet, weil der TransferHandler ja NUR für die Quall- aber nicht für die Zielkomponente gesetzt wird.

Wie auch immer .... kurze Antwort:
Da wo du
dtde.acceptDrop(DnDConstants.ACTION_COPY);
aufrufst, musst du auch noch
dtde.dropComplete(true);
aufrufen.

Lange Antwort: Hier mal der zusammengeschusterte Code.... :wink:
Code:
// Von [url]http://www.java-forum.org/de/posting.php?mode=reply&t=74499[/url]

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;


public class DragFrame2 extends JFrame{
MovePanel myPanel01 = new MovePanel("Eins", Color.red,10,10,0);
MovePanel myPanel02 = new MovePanel("Zwei", Color.green,200,10,1);
//------------------------------------------------------------------------------------------------
  public DragFrame2(){
    super("Drag Frame");
    setLayout(null);

    myPanel01.setTransferHandler(new PanelTransferHandler());
    myPanel02.setTransferHandler(new PanelTransferHandler());

    MouseListener listener = new MouseAdapter() {
      public void mousePressed(MouseEvent me) {
        JComponent comp = (JComponent) me.getSource();
        TransferHandler handler = comp.getTransferHandler();
        handler.exportAsDrag(comp, me, TransferHandler.COPY);
      }
    };

    myPanel01.addMouseListener(listener);
    myPanel02.addMouseListener(listener);

    add(myPanel01);
    add(myPanel02);
  }
//------------------------------------------------------------------------------------------------
  public void rmPanel(int i){
    System.out.println("DragFrame2#rmPanel(int i) " + i);
  }
//------------------------------------------------------------------------------------------------
  public static void main(String args[]){
  DragFrame2 app = new DragFrame2();
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.setBounds(10,510,400,260);
    app.setVisible(true);

  DropFrame app2 = new DropFrame();
    app2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app2.setBounds(420,510,400,260);
    app2.setVisible(true);

  }
//------------------------------------------------------------------------------------------------
}




//================================================================================================
class PanelTransferHandler extends TransferHandler {

  public static final DataFlavor panelFlavor = new DataFlavor(MovePanel.class, "MovePanel");
  private static final DataFlavor[] flavors = new DataFlavor[] {
    panelFlavor
  };
//------------------------------------------------------------------------------------------------
  public int getSourceActions(JComponent c) {
    return TransferHandler.COPY_OR_MOVE;
  }
//------------------------------------------------------------------------------------------------
  public boolean canImport(JComponent comp, DataFlavor flavor[]) {
      for (int j = 0, m = flavors.length; j < m; j++) {
        if (panelFlavor.equals(flavors[j])) {
          return true;
        }
      }
    return false;
  }
//------------------------------------------------------------------------------------------------
  protected void exportDone(JComponent comp, Transferable data, int action) {
    System.out.println("exportDone=" + action);  // <--- Action ist 0 bei drop auf DropFrame
  }

//------------------------------------------------------------------------------------------------
  public Transferable createTransferable(JComponent comp) {
    if (comp instanceof MovePanel) {
      MovePanel movePanel  = (MovePanel)comp;
      return new PanelTransferable(movePanel);
    }
    return null;
  }
//------------------------------------------------------------------------------------------------
  public boolean importData(JComponent comp, Transferable t) {
    System.out.println("importData "+t);
    if (t.isDataFlavorSupported(flavors[0])) {
      try {
        MovePanel movePanel = (MovePanel) t.getTransferData(flavors[0]);
        System.out.println("importing "+movePanel);
        return true;
      }
      catch (UnsupportedFlavorException ignored) {System.out.println(ignored.getMessage());}
      catch (IOException ignored) {System.out.println(ignored.getMessage());}
    }
  return false;
  }
}



//================================================================================================
class PanelTransferable implements Transferable {

  MovePanel movePanel;
  public static final DataFlavor panelFlavor = new DataFlavor(MovePanel.class, "MovePanel");
  private static final DataFlavor[] flavors = new DataFlavor[] {
    panelFlavor
  };


  public PanelTransferable(MovePanel movePanel)
  {
      this.movePanel = movePanel;
  }

//------------------------------------------------------------------------------------------------
  public Object getTransferData(DataFlavor flavor) {
    if (isDataFlavorSupported(flavor)) {
      return movePanel;
    }
    return null;
  }
//------------------------------------------------------------------------------------------------
  public DataFlavor[] getTransferDataFlavors() {
    return flavors;
  }
//------------------------------------------------------------------------------------------------
  public boolean isDataFlavorSupported(DataFlavor flavor) {
    return flavors[0].equals(flavor);
  }
//------------------------------------------------------------------------------------------------
}







class DropFrame extends JFrame implements DropTargetListener{
  DropTarget target = new DropTarget();
  public static final DataFlavor panelFlavor = new DataFlavor(MovePanel.class, "MovePanel");
//------------------------------------------------------------------------------------------------
  public DropFrame(){
    this.setLayout(null);
    setDropTarget(target);
    try{
      target.addDropTargetListener(this);
    }
    catch (TooManyListenersException e) {
      JOptionPane.showMessageDialog(null,e.getMessage(),"TooManyListenersException",JOptionPane.INFORMATION_MESSAGE);
    }
  }
//------------------------------------------------------------------------------------------------
  public void setPanel(MovePanel m, Point p){
    MovePanel movePanel = m;
      movePanel.setLocation(p);
      add(movePanel);
    repaint();
  }
//------------------------------------------------------------------------------------------------
  public void drop(DropTargetDropEvent dtde) {
    Transferable transferable = dtde.getTransferable();

    if(dtde.isDataFlavorSupported(panelFlavor)){
      try {
        MovePanel movePanel = (MovePanel)transferable.getTransferData(panelFlavor);
        setPanel(movePanel, dtde.getLocation());

        // XXX
        dtde.dropComplete(true);
        dtde.acceptDrop(DnDConstants.ACTION_COPY);

        // Wenn man auf eine JComponent droppen würde, würde man für diese
        // JCompnent einen passenden TransferHandler setzen, und hier jetzt
        // sowas machen wie
        /*
        JComponent c = (JComponent)dtde.getDropTargetContext().getComponent();
        TransferHandler transferHandler = c.getTransferHandler();
        Transferable trans = dtde.getTransferable();
        boolean success = transferHandler.importData(c, trans);
        dtde.dropComplete(success);
        dtde.acceptDrop(DnDConstants.ACTION_COPY);
        */


      }
      catch (UnsupportedFlavorException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"UnsupportedFlavorException",JOptionPane.INFORMATION_MESSAGE);
        dtde.rejectDrop();
      }
      catch (IOException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"IOException",JOptionPane.INFORMATION_MESSAGE);
        dtde.rejectDrop();
      }
    }

  }

//------------------------------------------------------------------------------------------------
  public void dragExit(DropTargetEvent dte){}
//------------------------------------------------------------------------------------------------
  public void dropActionChanged(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
  public void dragOver(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
  public void dragEnter(DropTargetDragEvent dtde){}
//------------------------------------------------------------------------------------------------
}



class MovePanel extends JPanel{
  JTextField textField = new JTextField();
  JLabel imageLabel = new JLabel();
  JCheckBox checkbox = new JCheckBox("Check Box" ,true);
  int id;
  String string;
  Color color;
  private DragSource dragSource;
//------------------------------------------------------------------------------------------------
  public MovePanel(String s, Color c, int x, int y, int i){
    string = s;
    color = c;
    id = i;
    setLayout(null);
    setLocation(x, y);
    setSize(180,160);
    setBackground(Color.WHITE);
    setBorder(new LineBorder(Color.black));

    imageLabel.setBounds(10,10,160,120);
    imageLabel.setIcon(new ImageIcon(createImage(string, color)));
    textField.setText(string);
    textField.setBounds(10,137,100,19);
    checkbox.setBounds(142,137,18,19);

    add(imageLabel);
    add(textField);
    add(checkbox);

  }
//------------------------------------------------------------------------------------------------
  public void setMovePanelLocation(Point p){
    setLocation(p);
  }
//------------------------------------------------------------------------------------------------
  public void setMovePanelLocation(int x,int y){
    setLocation(x,y);
  }
//------------------------------------------------------------------------------------------------
  public int getId(){
    return id;
  }
//------------------------------------------------------------------------------------------------
  public String toString(){
    return "MovePanel";
  }
//------------------------------------------------------------------------------------------------
  public BufferedImage createImage(String s, Color c){
    BufferedImage bufferedImage = new BufferedImage(162, 120, BufferedImage.TYPE_3BYTE_BGR);
    Graphics g = bufferedImage.createGraphics();

    g.setColor(c);
    g.fillRect(0,0,160,120);
    g.setColor(Color.BLUE);
    g.drawString(s, 10,20);
    return bufferedImage;
  }
//------------------------------------------------------------------------------------------------
}
 
Hallo Marco13,

Marco13 hat gesagt.:
Du solltest das PanelTransfer aufteilen in ein
PanelTransferHandler extends TransferHandler
und
PanelTransferable implements Transferable
Habe ich gemacht. Wollte eigentlich nur eine Klasse sparen. 😀

Der Hinweis auf dtde.dropComplete(true); war goldrichtig.
Allerdings hat exportDone(...) den Wert zurückgegeben, welchen ich in dtde.acceptDrop(...) (MOVE/COPY) im DropFrame.drop(...) angegeben habe. Gelöst habe ich es mit dtde.acceptDrop(dtde.getDropAction()).

Die Methode drop im DropFrame sieht nun so aus:
Code:
  public void drop(DropTargetDropEvent dtde) {
    Transferable transferable = dtde.getTransferable();
    if(dtde.isDataFlavorSupported(panelFlavor)){
      try {
        dtde.acceptDrop(dtde.getDropAction());
        MovePanel movePanel = (MovePanel)transferable.getTransferData(panelFlavor);
        setPanel(movePanel, dtde.getLocation());
        dtde.dropComplete(true);
      }
      catch (UnsupportedFlavorException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"UnsupportedFlavorException",JOptionPane.INFORMATION_MESSAGE);
      }
      catch (IOException e) {
        JOptionPane.showMessageDialog(null,e.getMessage(),"IOException",JOptionPane.INFORMATION_MESSAGE);
      }
    }
  }
Das funktioniert wie gewollt.

Bei deinem Hack:

Code:
JComponent c = (JComponent)dtde.getDropTargetContext().getComponent();
TransferHandler transferHandler = c.getTransferHandler();
Transferable trans = dtde.getTransferable();
boolean success = transferHandler.importData(c, trans);
dtde.dropComplete(success);
gehe ich davon aus, das der wahre Wert für dropComplete ermittelt werden soll.
Leider kommt bei JComponent c = (JComponent)dtde.getDropTargetContext().getComponent(); die Exception:

java hat gesagt.:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: DropFrame cannot be cast to javax.swing.JComponent
at DropFrame.drop(DropFrame.java:42)
at java.awt.dnd.DropTarget.drop(DropTarget.java:430)
at sun.awt.dnd.SunDropTargetContextPeer.processDropMessage(SunDropTargetContextPeer.java:500)
...

Alles im allen ist mir hier wieder geholfen worden.

Besten Dank an Marco13 und das Board.

Grüss Max
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben