Input/Output MouseDrag

HimBromBeere

Top Contributor
Malzzeit,

nachdem ich mein Problem mit der AffineTransformation endlich mal in den Griff bekommen hab, eröffnet sich mir nun endlich die Welt der navigierbaren Kartenanwendungen... denkste.

Baue gerade ein Werkzeug zum Verschieben der Karte mittels drag&drop. Dafür hab ich mir ´ne Funktion pan(dx, dy) geschrieben. Das Problem ist nur, wie bekomme ich dx und dy raus? Dafür dacht ich mir, ich speichere mir alle x Millisekunden die letzte Mausposition ab und ziehe von ihr die aktuelle Position ab. In meiner mouseMove-Funktion hab ich bereits in mouseX und mouseY, aber wie ich grade feststellen musste, wird während eines MouseDrag-Ereignisses MouseMove nicht ausgelöst.

So wirklich glücklich bin ich mit der andauernden Speicherung der letzten Koordinaten nicht, hat jemand ´ne bessere Idee?

EDIT: Gleich mal noch ´ne andere Geschichte.

Wäre es ratsam, die doch etwas rechenintensiveren Transformationen mal in einen eigenen Thread zu bauen? Aber das Problem wäre dann ja sicher, dass ich dann andauernd in eine CurrentModificationException laufen würde, weil ich die zu zeichnenden Shapes andanerund neu berechne.
 
Zuletzt bearbeitet:

xehpuk

Top Contributor
Jetzt mal nur zur ursprünglichen Geschichte …

So ganz habe ich das Problem nicht verstanden. Bevor der erste MouseDrag erfolgt, kommt erst ein MousePress. Darüber kommst du an die Startposition ran. Während der MouseDrags erhältst du immer den aktuellsten Punkt und kannst damit die Differenz zum alten Punkt berechnen. Bist du mit den Berechnungen fertig, setzt du den aktuellen Punkt als "neuen" ehemaligen Punkt.
Du musst also nicht in bestimmten Intervallen die Position abfragen, sondern reagierst einfach auf die MouseEvents.
 

HimBromBeere

Top Contributor
Startposition krieg ich auch über den letzten mouseMove, aber das ist erstmal Latte. Was ich wollte, ist aber, dass bereits während des Drags gezeichnet wird, also brauche ich nicht die Position des letzten Klicks sondern des letzten "Zeicheneregnisses". Da jedoch, wenn ich wirklich sämtliche dieser Eregnisse auswerte, die Bewegung unnatürlich langsam erscheinen dürfte, hatte ich halt die Idee mit der Momentaufnahme alle paar ms für die Mauspositionen.
 

Marco13

Top Contributor
Java:
private Point previousMousePosition = new Point();
public void mouseMoved(MouseEvent e)
{
    previousMousePosition = e.getPoint();
}
public void mouseDragged(MouseEvent e)
{
    int dx = previousMousePosition.x - e.getX();
    int dy = previousMousePosition.y - e.getY();
    verarbeite(dx,dy);
    previousMousePosition = e.getPoint();
}
:bahnhof:
 

xehpuk

Top Contributor
Wenn die Transformation wirklich so rechenintensiv ist, kannst du sie in einem Hintergrundthread auslagern.

Mit dem EDT könnte man dann an einem einelementigen Puffer synchronisieren, in dem vom EDT immer die letzte Mausposition gespeichert wird. Der Hintergrundthread holt sich dort immer die Mausposition raus. Wenn es aktuell keine gibt, wartet er eben. Während dieser mit den rechenintensiven Berechnungen beschäftigt ist, fallen womöglich mehrere MouseEvents an. Der EDT ersetzt dann aber einfach die letzte Position im dem Puffer mit der neuen. So wird der EDT dann auch nie blockiert. Sind die Berechnungen fertig, wird ein
Code:
repaint()
aufgerufen.

Mit welcher Concurrent-Klasse man das am besten macht, weiß ich gerade nicht. Es lässt sich da aber bestimmt etwas finden.
 

HimBromBeere

Top Contributor
Danke für die Antworten.

Die Idee von Marco hatte ich auch schon, die hat nur den Nachteil, dass sich das aktuelle Dragergebnis nicht auf das vorherige sondern immer auf das letzte MouseMove-Eregnis bezieht. Das fällt v.a. dann auf, wenn die Drag-Operation ziemlich lang (und nicht geradlinig ist). Denn dann wird (selbst wenn ich z.B. mit der Maus eine Hoch-Bewegung mache aber noch immer unterhalb der ursprünglichen (im MouseMove errechneten) Mausposition bin (weil die Bewegung direkt davor extrem weit nach unten ging)) trotzdem der Billdauscchnitt nach unten geschoben anstatt nach oben.
Ich schicke mal, wie ich das bisher hab.

Java:
@Override
public void mouseDragged(MouseEvent e) {
	GISPanel.panTo(mouseX, mouseY, e.getX(), e.getY());
	GISPanel.repaint();
}

@Override
public void mouseMoved(MouseEvent e) {
	// get the mouse-pos
	mouseX = e.getX();
	mouseY = e.getY();
	// repaint the window
	mainFrame.repaint();
}

Und hier die Klasse, von der GISPanel eine Instanz ist:
Java:
public void panTo(int originX, int originY, int x, int y) {
	// get the current transformation
    // transformation for world-to screen-coords
	AffineTransform af = this.geoGraphics.getTransformation();
    // transformation for scree-to world-coords
	AffineTransform inv = new AffineTransform();

	try{
		int dx = originX - x; 
		int dy = originY - y;
		inv = af.createInverse();
               // hier liegt die Katze begraben...
		inv.translate(dx, dy);
		af = inv.createInverse();
	} catch(NoninvertibleTransformException e) {e.printStackTrace();}
		
	this.geoGraphics.setTransformation(af);
    // updates all shapes within this container
	this.geoGraphics.update();
}


[EDIT]Zum Thema Threads: in der aktuellen Version hab ich nur drei Geometrien, da ist das kein wirkliches Problem, aber da das eigtl. Ziel das Verarbeiten von Geodatenbanken ist (in der schonmal ein paar tausend Geometrien liegen können (am besten auch noch Mulltipart-Geometrien)), und selbst in etablierten Anwendungen das Zeichnen auf minimaler Zommstufe schon mal einige Minuten dauern kann, hab ich die Frage der Performacesteigerung durch Auslagerung in Thredas gestellt. Insofern schonmal Danke für die Antwort.[/EDIT]
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Die Idee von Marco hatte ich auch schon, die hat nur den Nachteil, dass sich das aktuelle Dragergebnis nicht auf das vorherige sondern immer auf das letzte MouseMove-Eregnis bezieht.

Bevor ich weiter versuche, das Problem nachzuvollziehen: Hast du gesehen, dass in MouseDragged auch die "previousMousePosition" gespeichert wird? Jedes mal, wenn eine Bewegung festgestellt wird, berechnet man, wie groß die Bewegung war - abhängig von der letzten bekannten Mausposition (unabhängig davon, ob die Maus zur letzten Position gemovt oder gedraggt wurde)
 

HimBromBeere

Top Contributor
Hab mal ein wenig rumphilophiert. Ich denke, das Problem ist (wie ich im Code bereits angemerkt habe), dass ich entweder die Position des Mauszeigers direkt VOR der aktuellen Position bräuchte (wobei die Definition von VOR schon ziemlich schwammig sein dürfte) (außerdem ist das Lesen dieser Position nach wie vor schwierig), oder aber das Merken der ursprünglichen Transformationsvorschirft, was ich jetzt mal versuche...

Wenn ich das tatsächlich in Form eines neuen Threads gestalte, wie bekomme ich es dann hin, die einzelnen Shapes zu zeichnen und in einem anderen Thread zu berechnen (woe sie ja gerade transformiert werden), ohne andauern CurrentModificationExceptions zu erhalten?

EDIT: Oha, hab grade erst deinen Beitrag gesehen, Marco. Das wäre natürlich genau, was ich ursprünglich gesucht hab, probier´s gleichmal aus... wie komm ich den an diese PreviousMouseMotion ran?
 
Zuletzt bearbeitet:

HimBromBeere

Top Contributor
Yeah... läuft.

Mit dem Hinweis previousMousePosition hat´s dann geklappt nach langen Versuchen. Ich speicher also bei jeder Mausberegung (MouseMove) die Position des Ereignisses in previousPosition ab. Bin ich jetzt innerhalb der Drag&Funktion kann ich an diese Position einfach rankommen und ziehe sie von der aktuellen Position ab.

Danke...
 

HimBromBeere

Top Contributor
Ja, hast recht... als ich´s eingetippt hatte (den Code mein ich), fiel´s mir auch wie Schuppen von den Haaren, dass ich da nicht selbst drauf gekommen bin und nach nochmaligem Lesen der Beiträge fiel mir auf, dass du das auch so geschrieben hattest :D

Warum ich da nicht selbst drauf kam? Aus unerfidnlichem Grund bin ich davon ausgegangen, dass das Drag-Ereignis permanent ausgeführt wird während der Zieh-Operation weswegen eine Differenz der Mauspositionen eigetl. IMMER auf 0 hinausliefe (es sei denn, man könnte die Maus schneller um 1px bewegen als der Interpreter von Zeile 1 zu Zeile 3 vorangeschritten ist). Aber das ist ja nicht der Fall, sondern sie wird ja nur für jedes verschobene Pixel einmal ausgeführt. Lange Rede, gar kein Sinn:
Dein Danke haste dir verdient:toll:
 
Zuletzt bearbeitet:

HimBromBeere

Top Contributor
Jetzt nutz ich dieses Thema doch nochmal und zwar für die Sache mit den Therads. Wie bekomme ich es hin, in meinem Thread für die Transformation alle Shapes neu zu berechnen (also deren Positionen), ohne dass ich andauernd CurrentModificationExceptions aus dem EDT bekomme, weil icj die Shapes ja gerade zeichne (und deshalb natürlich über dieselben drüberiterieren muss).
 

HimBromBeere

Top Contributor
Das Vermeiden der ModificationException hab ich dem SpieleTutorial entnommen (auch wenn meine Anwendung kein Speil ist, aber ab und zu ist themenübergreifendes Lernen durchaus nützlich...). Habe einfach alle meine Shapes in eine neue HashMap kopiert. Aber ganz fertig bin ich damit noch nicht, das mit den Threads ist mir nach wie vor ein wenig abstrakt.

Habe hier mal die wichtigsten Teile zusammengetragen:
GeoGraphics... eine Klasse, die hauptsächlich aus den beiden Eigenschaften Graphics und AffineTransformation besteht (also in gewisser Weise ein GraphikKontext, der eine Transformation besitzt), sowie eine
Code:
HashMap<Name des Layers, ArrayList<Geometry>> geometries
(in einem beliebigen Koordinatensystem) und einer
Code:
HashMap<Name des Layers, ArrayList<Shape>> shapes
(welche dann tatsächlich gezeichnet werden sollen).
Kernproblem ist, wie die Geometriedaten in Shapes transformiert werden können (dafür wird die AffineTransformation verwendet). Da dies wie bereits erwähnt ein wenig dauern kann, wollte ich genau diesen Prozess in einen eigenen Thread auslagern. Mit Hilfe dieses Threads sollen folgende Ziele erreicht werden:
  • Performanzsteigerung, um während der Transformation andere Aufgaben erledigen zu können (etwa das Zeichnen)
  • Das "Überrschreiben" einer Transformation (während eine Transformation gerechnet wird, kann es passieren, dass der Nutzer den Kartenausschnitt wechselt und sich daher die Transformationsdaten ändern. Passiert dies, soll die aktuelle T. abgebrochen und eine neue gestartet werden).

Soviel erstmal zur Vorgeschichte...

Java:
/**
	 * Updates all shapes within the GraphicsContext by recalculating them based
	 * on the last specified transformation.<br>
	 * Call this method after you set a new transformation.
	 */
	public void update() {
		
		// stop an existing update-Thread if existing
		if (!transformationDone) {
			try {job.join();} 
			catch (InterruptedException e) {e.printStackTrace();}
		}
		
		job.start();
		

		// terminate the thread when the job is done
		if (transformationDone) {
			try {job.join();} 
            catch (InterruptedException e) {e.printStackTrace();}
		}
		
	}
	
	
	private class TransformationJob extends Thread {
		
		@Override
		public void run() {
			transformationDone = false;
			this.doTransformation();
			transformationDone = true;
		}
		
		
		public void doTransformation() {
			// delete all existing shapes
			GeoGraphics.this.shapes.clear();
	
			for (String layerName: GeoGraphics.this.geometries.keySet()) {
				System.out.println("Starting transformation of layer " + layerName);
				// add new empty list of shapes into the map
				GeoGraphics.this.shapes.put(layerName, new ArrayList<Shape>());
				// get the geometries of the layer
				Iterator<Geometry> i = GeoGraphics.this.geometries.get(layerName).iterator();
				while (i.hasNext()) {
					try {sleep(10);} 
					catch (InterruptedException e) {e.printStackTrace();}
					Geometry geometry = i.next();
					// adds the transformed geometry into the appropriate shape-list
					GeoGraphics.this.shapes.get(layerName).add(GeoGraphics.this.toShape(geometry));
				}
				System.out.println("Tranformation of layer " + layerName + " done");
			}
			
		}
	}
 
Zuletzt bearbeitet:

Marco13

Top Contributor
GeoGraphics... eine Klasse, die hauptsächlich aus den beiden Eigenschaften Graphics und AffineTransformation besteht (also in gewisser Weise ein GraphikKontext, der eine Transformation besitzt),

So wie eine Graphics2D !?

sowie eine
Code:
HashMap<Name des Layers, ArrayList<Geometry>> geometries
(in einem beliebigen Koordinatensystem) und einer
Code:
HashMap<Name des Layers, ArrayList<Shape>> shapes
(welche dann tatsächlich gezeichnet werden sollen).

Ohne da jetzt zuuuu viel rum-hinterfragen zu wollen: Wenn bei jeder Änderung der Transformation die Objekte neu erstellt werden müssen, warum werden sie dann überhaupt gespeichert? Habe ich das richtig verstanden, dass das, was von einem eigenen Thread gemacht werden soll, NUR die Umwandlung von Geometries in Shapes ist?
 

HimBromBeere

Top Contributor
So wie eine Graphics2D !?
Nicht wie sondern es ist eine... hätte ich vlcht, mal sagen sollen.
Wenn bei jeder Änderung der Transformation die Objekte neu erstellt werden müssen, warum werden sie dann überhaupt gespeichert?
Weil sie nicht in der Klasse GeoGraphics gezeichnet werden (so wie ja auch nicht Graphics2D seine Elemente zeichnet, sondern die PaintComponent oder was auch immer dazu verwendet wird).
Habe ich das richtig verstanden, dass das, was von einem eigenen Thread gemacht werden soll, NUR die Umwandlung von Geometries in Shapes ist?
Inklusive der Transformation vom Weltkoordinatensystem (welches auch immer das sein mag) in Bildschirmkoordinaten.
 

Marco13

Top Contributor
So ganz ist mir die Frage immernoch nicht klar. Auf basis des bisher beschriebenen: Was spricht dagegen, eben nicht in die Map/Lists reinzuschreiben, die es schon gibt, sondern einfach bei jedem update eine neue Map zu erstellen? Es werden ja sowieso alle Daten weggeworfen...?! :bahnhof:
 

HimBromBeere

Top Contributor
Java:
// add new empty list of shapes into the map
GeoGraphics.this.shapes.put(layerName, new ArrayList<Shape>());
Meinst du das mit
bei jedem update eine neue Map zu erstellen
?
Der Grund ist, dass ich, sollte sich die Transformation ändern, sowieso alle in der bereits existierenden Map vorhandenen Shapes neu berechnen muss, also kann ich sie auch gleich komplett löschen/überschreiben. Abgesehen davon sollen später nicht mehr alle Geometrien in die Shape-Map übernommen werden, sondern nur die für den aktuellen Bildausschnitt relevante.
D.h. die Geometrieliste dürfte sich tendentiell seltener ändern als die Liste der Shapes.

EDIT: Ich versuch´ nochmal, mein Vorhaben zu erläutern. Ich habe eine Menge von Geometrien, die ich auf dem Bildschirm darstellen möchte. Um dies zu ermöglichen, muss irgendeine Transformation von Welt- in Bildschirmkoordinaten erfolgen. Da diese Operation für einige Hunderttausend Geometriedaten doch etwas zeitaufwendig sein kann, würde ich ihn gerne in einen eigenen Thread auslagern und ihn damit parallel zur Darstellung durchzuführen. Ist das denn ein völlig falscher Ansatz?

EDIT: Verdammt, hab gerade in der API gelesen, dass ein Thread sowieso nur ein einziges Mal gestartet werden kann... ich muss umdenken
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Hmja... GROB glaube ich es verstanden zu haben, aber so ein paar Punkte sind noch unklar. Ob der Ansatz an sich der geschickteste oder sinnvollste ist, darüber müßte man erst noch nachdenken. Aber... Angenommen man verschiebt, und der Thread fängt and, die Geometries in Shapes umzuwandeln. Wenn er 5% aller umwandlungen gemacht hat, kommt der Event-Dispatch-Thread und will gerne zeichnen. WAS soll dann gezeichnet werden? Die 5%? Oder nichts? Soll (wenn der Thread dann weiterarbeitet) das Bild stückweise von 5% bis 100% "aufgebaut" werden? Mir sind die Abläufe eben nicht klar.

Mit dem Neu-Erstellen meinte ich, dass man GROB sowas machen könnte wie
Java:
class PaintPanel
{
    private Map aktuelleShapes = ...

    void paintComponent(...)
    {
        male(aktuelleShapes);
    }
}

class UmwandlungsThread
{
    void berechne()
    {
        Map neueShapes = rechneUm();

        // Übergib eine komplett neue Map! Die Map, die zum
        // Zeichnen verwendet wird, wird während des 
        // Zeichnens NICHT verändert!
        paintPanel.aktuelleShapes = neueShapes; 
        paintPanel.repaint();
    }
}

Aber irgendwie glaube ich nicht, dass das das ist, was du dir vorstellst...?!
 

HimBromBeere

Top Contributor
Hab das Problem gestern abend bereits gelöst, bin aber immer noch am Untersuchen, ob es tatsächlich genau das macht, was ich wollte :D Ich fürchte jedoch, die Antwot lautet: irgendwie nicht:oops:

Die Frage mit den 5% ist natürlich berechtigt, die würde ich gerne mit
Bild stückweise von 5% bis 100% "aufgebaut
beantworten, alternativ dazu jedoch auch, wenn in der Zwischenzeit eine neue Transformation reinflattert, dann werden auch alle Geometrien auf Grundlage der neuen Transformation verarbeitet und die alte verworfen.
 

Marco13

Top Contributor
Für dieses "Aufbauen von 5% bis 100%" würde ich spontan mal schauen, ob da ein SwingWorker nicht das beste wäre: Der hat explizite Methoden, um Teilergebnisse auf dem EDT zu "publizieren" (die heißt AFAIR wirklich "publish" :) ). Damit wäre die Threadsicherheit da zumindest gewährleistet...
 

HimBromBeere

Top Contributor
Interessantes Teil dieser SwingWorker...
habe ich das richtig verstanden, dass ich dann im process-Teil die neuen Shapes in die Liste schreibe? Oder sollte ich jedes Mal, wenn ein ein Shape neu erzeugt wurde, einen publish machen und innerhalb meine GeoGraphics dieses eine Shape neuzeichnen? Finde ich persönlich zwar nicht so doll, da ich innerhalb meiner GeoGraphics nicht zeichnen möchte, aber vlcht. muss ich meine STrategie auch ein wenig verändern...
 

Marco13

Top Contributor
Grundsätzlich kann man sich ja aussuchen, wie viele Elemente man mit "publish" an "process" weiterreicht. Innerhalb von "process" kann man mit der Liste dann machen, was man will. In diesem Fall bräuchte da ja nicht gezeichnet zu werden oder so (hab' nicht ganz verstanden, wie du das meintest...?!) - man würde lediglich die Elemente, die bei "process" ankommen, z.B. mit
shapesToPaint.addAll(givenInProcess);
zu den zu zeichnenden Shapes hinzufügen (auf dem EDT - d.h. sicher von CMEs). Das war aber nur eine grobe, spontane Idee - ob und wie genau man ihn in diesem speziellen Fall am geschicktesten einsetzen würde, müßte man sich noch überlegen....
 

HimBromBeere

Top Contributor
Jut, das Prinzip hab ich so langsam durchschaut, aber es hapert natürlich an der Umsetzung. Mein Problem ist, dass ich im process-Teil jetzt zwar eine Liste mit Shapes habe, aber natürlich die Zuordnung zu einem Layer verlorengegangen ist (das, was ja in der HashMap als Schlüssel fungierte), wodurch die Definition evtl. zu verwendender Stile etwas umständlich werden dürfte.

Also hab ich mir gedacht, machst du halt für jeden Layer einen eigene update-Aufruf und damit einen eigenen SwingWorker. Ist nur die Frage: geht sowas?
Am besten, ich probier´s aus...
 

Marco13

Top Contributor
Hm... nochmal: Alles, was ich sage, könnte aufgrund des mangelnden Überblicks über die bestehenden Strukturen und das letztendliche Ziel auch unpassend sein, aber... in diesem Fall gibt's zumindest noch die Hyperpragmatische Lösung, sich eine Klasse zu erstellen
Java:
class ShapeInfo
{
    private final String layerOrWhatever;
    private final Shape shape;
 
    //Konstruktor, getter, und (WICHTIG!) equals und hashCode
    ....
}
und den SwingWorker dann eben ShapeInfo-Objekte liefern lassen...Aber irgendwelche anderen Optionen wie z.B. einen SwingWorker pro layer (man hat meistens >= 4 Cores ;) ) oder einen SwingWorker, der eine Liste von kleinen Map<String, List<Shape>>-Maps zurückgibt oder... oder... oder... sollte man in Erwägung ziehen (und ggf. verwerfen, aber trotzdem in Erwägung ziehen)
 

HimBromBeere

Top Contributor
man hat meistens >= 4 Cores
Das war Ironie, oder?

Deine "hyperpragma"-Lösung hatte ich natürlich auch schon, pragmatisch wie ich eben bin... aber die sieht mir doch alles andere als elegant aus... ich probier jetzt jedenfalls die "Ein-Arbeitssklave-pro-Layer-Variante" aus...
 

Marco13

Top Contributor
Nee... man kann heute schon kaum noch einen Rechner mit <4 cores kaufen... Und man muss davon ausgehen, dass sich die durchschnittliche Anzahl cores alle 18 Monate verdoppeln wird. Wie viele Layers gibt's da denn so?
 

HimBromBeere

Top Contributor
Naja, mein Rechner hat nur 2Kerne... ist aber auch schon 4Jahre alt... wie dem auch sei, die Anzahl der Ebenen liegt zwischen 1 und einigen Hundert... wobei erwartungsgemäß etwa 5-10Ebenen enthalten sein werden (das dürfte realistisch sein).
 

Marco13

Top Contributor
Ja, ich meinte nur, vielleicht kann man das auch GANZ GROB in dem Stile aufteilen/ablaufen lassen, dass man mit einem SwingWorker alle Ebenen nacheinander abarbeitet, und sich jeweils die Information merkt, um welche Ebene es gerade geht, damit man die Ergebnisse entsprechend verarbeiten kann, aber das vernünftig hinzukriegen (d.h. den Zustand sauber merken und nicht durcheinanderzukommen) könnte frickelig werden....
Nur... 100 SwingWorker würde ich jetzt nicht parallel laufen lassen...
 

HimBromBeere

Top Contributor
Naja, ich muss doch in jedem Fall eine Klasse vom Typ SwingWorker erstellen, kann ich doch die aktuelle Ebene als Instanzvariable der Klasse merken... oder bringt die Asynchronität das durcheiander?

EDIT: Achso... ich glaub, ich hab das Problem erkannt, da ja process nicht unbedingt direkt nach einem publish gemacht wird, kann es also auch sein, dass ich innerhalb eines Aufrufes von process mehrere "chunk" von mehreren Ebenen hab... ist es das oder was meinst du mit durcheinanderkommen?
 

Marco13

Top Contributor
Hmja... : Wenn man die Layer als Instanzvariable speichern würde, könnte der Ablauf eben so sein, dass z.B. setLayer("2") aufgerufen wird, aber noch ein "process" durchgeführt wird, das Daten bekommt, die noch zu Layer "1" gehören. Deswegen auch der erste pragmatische Vorschlag, beim publish/process mitzugeben, um welche Layer es geht
 

HimBromBeere

Top Contributor
Habe soeben dein Nicht-Swing-Tuorial gefunden, das hätteste mir aber auch mal eher sagen können, dass es so eins gibt :D
Naja, viele der Dinge, die ich bereits impelemntiert habe, sind da auch drin und kann mal vergleichen, wie andere das gemacht haben (soweit ich bisher gesehen hab, sieht´s bei mir schon minimal anders aus...). Ich acker mich da mal durch...:rtfm:
 

Neue Themen


Oben