Dokumentation zum Drag & Drop und Verschieben von Steuerelementen
=================================================================

Das Verbinden von Steuerelementen mit Pfaden/Pfeilen (und auch das Erstellen von neuen
Steuerelementen mit Drag aus der Liste und Drop aufs Ablaufdiagramm) ist mit einem anderen
System realisiert (nämlich Drag & Drop) als das Verschieben von Steuerelementen
(MouseMotionListener).

1.) Drag & Drop
---------------

Voraus: Hier wird NICHT der TransferHandler verwendet. Den habe ich irgendwie nicht zum
laufen gebracht. Stattdessen wird DragSource/DragGestureListener und DropTarget verwendet.

Beim Drag & Drop ist es grundsätzlich so, dass beim Start des Drags festgelegt wird, welche Daten
denn transferiert werden sollen. Dafür werden Diese daten in ein Transferable gespeichert. Beim
Drop auf dem Target kann das Target auf dieses Transferable zugreifen und was sinnvolles damit
machen.

Wenn ich aber hier eine ganze Klasse transferieren will, geht das nicht ohne weiteres: Ich kann
zwar natürlich Transferable implementieren, aber dann gibt es z.B. Exceptions beim Serialisieren der
Listener. Es hat auch keinen Sinn, wenn ich Listener transient mache, weil sie dann weg sind.
Die Lösung dafür ist, dass ich relevanten Objekte beim Start des Drags in globalen Variablen
referenziere, die ich beim Drop auslesen kann.

DragSource
----------
Es wird einfach eine Instanz davon erstellt, ohne Argumente im Konstruktor. Dann wird die
Instanz-Methode createDefaultDragGestureRecognizer(). Dieser übergibt man erstens die Komponente,
bei der das Drag funktionieren soll, also das Steuerelement. Zweitens übergibt man die Art des Drags,
z.B. "verlinken" und drittens den DragGestureListener! Dieser muss in der Methode dragGestureRecognized()
den Drag-Vorgang initiieren. Außerdem wird der DragSource noch ein DragSourceListener registriert, der
bei mir lediglich das Cursor-Symbol richtig setzt, wenn beim Drag der Mauszeiger auf der selben
Komponente ist, die den Drag auch begonnen hat.

DropTarget
----------
Auch hier wird einfach eine Instanz erstellt, auch ohne Argumente im Konstruktor. Mit der Methode
setComponent() wird das DropTarget mit der GUI-Komponente verbunden. Die Standard Drop-Art könnte z.B.
"verlinken" sein, das geht mit der Methode setDefaultActions(). Zuletzt muss noch der
DropTargetListener registriert werden mit addDropTargetListener(). Die wichtigste Methode des Listeners
ist drop(). Darin wird die Aktion ausgeführt, die diese ganze Drag & Drop-Geschichte eben bewirken
möchte, z.B. die Steuerelementen mit Pfaden verbinden!

Drag & Drop-Vorgang
-------------------
a) Drag & Drop zum Erstellen neuer Steuerelemente
Der DragGestureListener der Auswahlliste mit den Steuerelementen, startet in seiner Methode ja den
Drag-Vorgang und übergibt startDrag() ein Transferable. Normalerweise würde dieses Transferable das
ControlElement enthalten, das dem FlowDiagam hinzugefügt werden soll. Aber das geht ja nicht, weil
das ControlElement nicht serialisierbar ist. Deswegen wird das ControlElement in einer static-Variable
des FlowDiagrams, nämlich transferDataForControlElementCreation, zwischengespeichert. Das Transferable
enthält dafür nur das DataFlavor, das beschreibt um welchen Typ von Übertragung es sich bei diesem
Drag & Drop handelt.

b) Drag & Drop zum Erstellen von Pfaden zwischen Steuerelementen
Der DragGestureListener des ControlElementComponent startet in seiner Methode den Drag - wenn es sich
um einen Pfad-Startpunkt handelt - und übergibt der Methode startDrag() ein Transferable. Auch dieses
Transferable enthält aber nur das DataFlavor, die wichtigen Daten sind dafür wieder in einer private-
static-Variable in ControlElementComponent abgelegt, nämlich transferDataForControlElementPathLinking.
Dieses Objekt hat zwei Methoden: getPath() und getControlElementComponent(). Die erste gibt den Pfad-
Index an, die zweite das Source-Steuerelement, von dem aus der Drag gestartet hat. Beim Drop stellt
das Target daraus den Pfad zwischen dem Source-Steuerelement (Pfad-Index wie angegeben) und sich selbst
her.

2.) Verschieben von Steuerelementen
-----------------------------------

Das Verschieben ist auch nicht einfacher, weil der Code für optimales Verhalten - auch in Bezug auf
Mehrfachauswahl, Autoscroll, ... - "gut" verteilt ist. Es werden MouseListener und MouseMotionListener
verwendet und es gibt zwei globale Behelfsvariablen: movedSinceLastMousePressed und
lastMousePressedPoint.

In MouseListener.mousePressed() wird der Punkt auf den geklickt wurde (relativ zum Steuerelement)
gemerkt. Das ist nötig, weil er später, wenn die Position (linke obere Ecke des Steuerelements)
neu gesetzt wird, addiert werden muss! Außerdem wird ein Flag "moved since last mouse pressed" auf
false gesetzt. Das Flag wird abgefragt, wenn die Maustaste losgelassen wird, weil dann - wenn true -
das zuletzt ausgewählte Steuerelement in den sichtbaren Bereich des Ablaufdiagramms gescrollt wird.
Ansonnsten wird das Flag nirgends abgefragt.

Das Verschieben selbst geschieht in MouseMotionListener.mouseDragged(). Die Methode wird aufgerufen,
wenn die Maus bewegt wird, und zwar pixel-genau. Der Code dort wird im Großen und Ganzen nur
ausgeführt, wenn überhaupt die Maustaste gedrückt ist. Es wird zum ersten das Flag "moved since last
mouse pressed" auf true gesetzt, zum zweiten das Cursor-Symbol auf "verschieben" gesetzt und zuletzt
alle ausgewählten Steuerelemente simultan um die "Mausdifferenz" verschoben.
