Drag and Drop und eigener TransferType ja oder nein?

Nightshifter

Grünschnabel
Hallo allerseits,

es gibt zwar schon hunderte DND Threads aber keiner beantwortet mein folgendes Problem:
Ich habe eine GUI mit einem Graphen (Jung 2.0.1 mit Swing) auf der rechten und einem Tree (SWT) auf der linken Seite.

Der Tree enthält mehrere Items mit "Element" als Data Objekt. Die Element Objekte werden mit Hibernate aus einer Datenbank ausgelesen und haben dementsprechend (weil Tree) "parent" und "child" Attribute. Außerdem stehen sie untereinander auch noch in einer Beziehung, die ich hier mal Abhängigkeit(en) nennen möchte.

Der Graph ist vorerst leer und ich möchte per DND Elemente aus dem Tree hinein ziehen. Wenn ein Element A dem Graphen hinzugefügt wird, werden alle anderen Elemente, die mit diesem Element A in Verbingung stehen ebenfalls hinzugefügt und durch Kanten dementsprechend verbunden.

Meine aktuelle Lösung sieht in etwa wie folgt aus:
Beim Drag wird die Id bzw. die Ids der slektieren Items als String mit Semikolons getrennt übergeben (ID = Primärschlüssel). Beim Drop wird der String dann wieder gesplittet und jedes Element anhand der ID aus der Datenbank ausgelesen und dem Graphen hinzugefügt.

Frage: gibt es eine bessere / elegantere Lösung? Wie ist das mit einem eigenen TransferType bei komplexen Objekten wie meinem "Element" Objekt, welches n Kinder und n Abhängige Objekte etc. haben kann? Natürlich könnte man auch beim Drag die selektierten Elemente in ein Singleton schreiben und beim Drop dann raus holen (dann spart man sich den Datenbankzugriff), aber das ist ja auch irgendwie halbherzig.

Mir ist bewusst, dass ich keinen eigenen TransferType benötige, wenn ich Objekte nicht Anwendungsübergreifend Draggen will, aber die Lösung mit dem String kommt mir nicht sonderlich schön vor.

Wen es interessiert hier mein SourceAdapter (Tree) und DropTarget(Graph):
Java:
public class TreeSourceAdapter extends DragSourceAdapter
{
    TreeItem[] dragSourceItems = null;
    Tree       tree;

    public TreeSourceAdapter(Tree tree)
    {
        super();
        this.tree = tree;
    }

    public void dragStart(DragSourceEvent event)
    {
        TreeItem[] selection = tree.getSelection();
        if (selection.length > 0)
        {
            event.doit = true;
            dragSourceItems = selection;
        }
        else
        {
            event.doit = false;
        }
    }

    public void dragSetData(DragSourceEvent event)
    {
        String ids = "";
        for (TreeItem treeItem : dragSourceItems)
        {
            Element e = (Element) treeItem.getData();
            String id = String.valueOf(e.getId());
            if (ids.equals(""))
            {
                ids += id;
            }
            else
            {
                ids += ";" + id;
            }
        }
        event.data = ids;
    }

    public void dragFinished(DragSourceEvent event)
    {
        dragSourceItems = null;
    }
}
Java:
public void drop(DropTargetDropEvent dtde)
{
    dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
    String o = null;
    try
    {
        o = (String)dtde.getTransferable().getTransferData(DataFlavor.stringFlavor);
    }
    catch (Exception ex)
    {
    }
    String[] ids = o.split(";");
    DaoFactory daoFactory = new DaoFactory();
    ElementDao dao = daoFactory.getElementDao();
    for (String idString : ids)
    {
        Long id = Long.valueOf(idString);
        Element e = dao.getById(id);
        graph.addElement(e);
    }
}
 
Es wäre erst mal ganz nett gewesen, wenn du uns mitgeteilt hättest, dass der DragSourceAdapter den du verwendest nicht in der Standardbibliothek vorhanden ist, sondern in org.eclipse.swt.dnd, das hätte einiges schonmal um ein vielfaches einfacher gemacht.

Was die eigentliche Frage betrifft, ich würde an deiner Stelle einen eigenen DataFlavor anlegen.
Würde für deinen Fall dann so aussehen:

Java:
DataFlavor treeItemFlavor = new DataFlavor(TreeItem.class, "TreeItem");

Das wäre die saubere Methode denke ich.
 
Zuletzt bearbeitet von einem Moderator:
Es wäre erst mal ganz nett gewesen, wenn du uns mitgeteilt hättest, dass der DragSourceAdapter den du verwendest nicht in der Standardbibliothek vorhanden ist, sondern in org.eclipse.swt.dnd, das hätte einiges schonmal um ein vielfaches einfacher gemacht.

Sorry, ich hatte extra dazu geschrieben, dass es ein SWT Tree ist. Ich bin davon ausgegangen, dass man dann den swt Listener nehmen muss. Ich hab das mit dem DataFlavor auch schon öfter mit JTrees etc. gemacht, wusste aber bis jetzt nichts, dass das auch mit swt widgets so funktioniert und bin ich auch nicht auf die Idee gekommen. Ich werds heute abend mal ausprobieren.

Vielen Dank!
 
Also ich komme so ehrlich gesagt nicht weiter. Ich habe mir jetzt mal ein minimal Bespiel gebastelt, mit einem SWT Tree als Quelle und einer JTextArea als Ziel. Ich poste es mal hier kommentarlos (vielleicht hilft es ja anderen bei ähnlichen Problemen):

Java:
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;

import javax.swing.JTextArea;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class DnDTest
{

    public static void main(String[] args)
    {
        Display display = new Display();
        Shell shell = new Shell(display);
        FillLayout fillLayout = new FillLayout();
        shell.setLayout(fillLayout);

        final Tree tree = new Tree(shell, SWT.BORDER | SWT.MULTI);
        for (int i = 0; i < 5; i++)
        {
            TreeItem treeItem = new TreeItem(tree, SWT.NONE);
            treeItem.setText("Item " + (i + 1));
        }

        Composite composite = new Composite(shell, SWT.EMBEDDED | SWT.NO_BACKGROUND);
        Frame frame = SWT_AWT.new_Frame(composite);
        Panel panel = new Panel(new BorderLayout());
        frame.add(panel);
        final JTextArea jTxtArea = new JTextArea();
        panel.add(jTxtArea, BorderLayout.CENTER);

        DragSource ds = new DragSource(tree, DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_MOVE);
        ds.setTransfer(new Transfer[] { TextTransfer.getInstance() });
        ds.addDragListener(new DragSourceAdapter() {
            
            private TreeItem[] selection = null;

            @Override
            public void dragStart(DragSourceEvent event)
            {
                selection = tree.getSelection();
            }

            @Override
            public void dragSetData(DragSourceEvent event)
            {
                String s = "";
                for (TreeItem item : selection)
                {
                    s += (String) item.getText() + "\n";
                }
                event.data = s;
            }

        });

        DropTarget jTxtAreaDropTarget = new DropTarget(jTxtArea, new DropTargetAdapter() {
            @Override
            public void drop(DropTargetDropEvent dtde)
            {
                dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
                String o = "nope";
                try
                {
                    o = (String) dtde.getTransferable().getTransferData(DataFlavor.stringFlavor);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
                jTxtArea.append(o);
            }
        });
        jTxtArea.setDropTarget(jTxtAreaDropTarget);

        shell.pack();
        shell.open();

        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

}

Auch hier habe ich wieder den DragSource aus dem SWT package genommen. Hier wird auch wieder nur ein String (der Text des TreeItems) übergeben. Ich habe den Tree absichtlich mit SWT.MULTI instanziert, da das draggen mehrerer Items möglich sein soll. Mein Problem verstärkt sich zudem noch dadurch, dass ein TreeItem verschiedene Objekte enthalten kann. Damit meine ich, dass zwar klar ist welche das sein können, aber nicht in jeden TreeItem der gleiche Objekttyp drinnen ist. Daher reicht es auch nicht mehr, wie in meinem Ursprungspost beschrieben, nur IDs zu übergeben, da zwei verschiedene Objekte die gleiche ID haben können, wenn sie nicht den gleichen Typ haben.

Das Problem was ich habe ist an sich nicht, wie ich beim DropTarget ein TreeItem in empfang nehme, sondern eher wie ich es beim DragSource rein bekomme. Bei SWT muss man ja dazu org.eclipse.swt.dnd.ByteArrayTransfer erweitern und die beiden Methoden javaToNative und nativeToJava überschreiben. Bei dem Veruch habe ich schon Probleme mit einem TreeItem, welches ich nicht einfach in ein Byte Array "umwandeln" kann. An andere Stelle habe ich auch gelesen, dass man ein TreeItem nicht draggen kann und auch nicht soll, sondern nur die Elemente darin (also Tree.data).

Wenn ich nun veruche das AWT DragSource zu verwenden, funtioniert das auch nicht, da ein SWT Tree keine Component ist, wie sie java.awt.dnd.DragSource.createDefaultDragGestureRecognizer() erwartet. Ich denke dass was ich vorhabe funktioniert so einfach nicht. Ich werde, dann eben doch einfach den TextTransfer auf der Drag Seite und das DataFlavor.stringFlavor auf der Drop Seite verwenden und mit die Strings so zusammen basteln, wie ich sie brauche. Ich poste mal eine Quick & Dirty Lösung wenn ich fertig bin.
 

Neue Beiträge

Zurück