GEF-Tutorial Kapitel 6
Aus Wirthi's Wiki
| GEF-Tutorial Eclipse Graphical Editor Framework | |
|---|---|
| Allgemeines Kapitel 1 (Überblick) Kapitel 2 (Model-View-Controller) Kapitel 3 (Model) Kapitel 4 (Controller) Kapitel 5 (View) Kapitel 6 (Editor-Hauptklasse) Kapitel 7 (Einsatz und Zusammenfassung) | |
|
In diesem Kapitel 6 des GEF-Tutorials werden die Hauptklasse eines GEF-Editors und einige kleinere Hilfsklassen beschrieben.
[bearbeiten] Aufgabe
Die Hauptklasse des Editors ist für die Steuerung des gesamten Ablaufes zuständig. In ihr kann definiert werden, wie und wo Daten geladen und gespeichert werden; welche Benutzereingaben (Menupunkte, Toolbar-Einträge, etc.) möglich sind; weche Art von Fenster zur Anzeige benutzt wird (etwa, ob Scrollbars vorhanden sind oder nicht); welche Werkzeug-Palette am Editor angedockt ist, und vieles mehr.
Der Editor ist die Schnittstelle zwischen Eclipse und GEF. Hier wird das (Eclipse-)Fenster definiert und wie der Benutzer mit diesem interagieren kann.
[bearbeiten] Implementierung
Die Editor-Klasse ist von org.eclipse.ui.part.EditorPart abgeleitet und somit ein typischer Eclipse-Editor. Die Anforderungen, welche Methoden bereitzustellen sind, kommen großteils von Eclipse (geerbt von EditorPart) und nicht von GEF. Weiters ist im Allgemeinen das Interface org.eclipse.core.runtime.IAdaptable zu implementieren.
Im folgenden wird eine Beispielimplementierung beschrieben. Es sind dabei nur die minimal erforderlichen Daten und Methoden vorhanden; weitere Funktionalität kann später einfach ergänzt werden.
[bearbeiten] Datenobjekte
Folgende Datenobjekte müssen unbedingt gesetzt werden:
- RootEditPart: Stellt die Schnittstelle des Editor zum angezeigten Inhalt, genauer gesagt zu den EditParts, dar.
- GraphicalViewer: Dieser Viewer repräsentiert die View-Sicht des Editors. Der Viewer enthält später die View-Objekte.
- EditDomain: Kapselt den Zustand des Editors.
- Ein Model-Objekt (meist die Wurzel eines Baumes oder ein Startknoten im Netzwerk).
Es müssen nicht unbedingt korrespondierende Variablen existieren, bei einigen der Objekte reicht es, sie anzulegen zu gleich korrekt zu zu weisen. Wir wählen hier aber den Weg, dass auf die Objekte per Zugriffmethode (Getter) zugegriffen wird, die die Objekte anlegt, falls dies noch nicht geschehen ist.
private RootEditPart rootEditPart; private GraphicalViewer graphicalViewer; private EditDomain editDomain; private Parent modelElement;
Die ersten drei Typen sind im Package org.eclipse.gef definiert, der Typ Parent ist eine unserer Model-Klassen. Die Zugriffsmethoden lauten wie folgt:
public EditDomain getEditDomain() {
if (editDomain == null) {
editDomain = new DefaultEditDomain(this);
}
return editDomain;
}
public GraphicalViewer getGraphicalViewer() {
if (graphicalViewer == null) {
graphicalViewer = new ScrollingGraphicalViewer();
}
return graphicalViewer;
}
public RootEditPart getRootEditPart() {
if (rootEditPart == null) {
rootEditPart = new ScalableRootEditPart();
}
return rootEditPart;
}
Das Model würde man üblicherweise aus dem zu bearbeitenden Datenfile auslesen. Wir machen es uns hier vorerst einfacher und legen ein Model immer gleich in einer Methode an:
private Parent createModel() {
Parent father = new Parent("Joe");
Child son = new Child("Mary");
Child daughter = new Child("Sally");
father.addChild(son);
father.addChild(daughter);
return father;
}
[bearbeiten] EditorPart-Methoden
Nun müssen wir die Methoden implementieren, die Eclipse für jeden Editor vorschreibt. Zuerst wird das der Editor in der Methode init initialisiert.
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setSite(site);
setInput(input);
}
Der IEditorInput repräsentiert die zu bearbeitende Datei. Üblicherweise würde man diese Information lesen, die beschriebene Datei einlesen und daraus ein Model generieren. Diesen Schritt lassen wir aus Gründen der Einfachheit voreraus aus; wir haben ohnehin bereits ein Model angelegt.
Zwei Methoden benötigt Eclipse zum Speichern der Daten, wenn der Benutzer auf den Menupunkt File-Save (oder Save as) oder den korrespondierenden Toolbar-Knopf klickt:
public void doSave(IProgressMonitor monitor) {
}
public void doSaveAs() {
}
Da unser Editor vorerst keine Daten speichern muss, können wir die Methoden leer implementieren. Über die folgende Funktion teilen wir Eclipse mit, dass wir die Funktion "Save as" überhaupt nicht anbieten wollen:
public boolean isSaveAsAllowed() {
return false;
}
Nach einer Änderung der Daten durch den Benutzer zeigt Eclipse ein Stern-Symbol neben dem Namen des Editors an. Dadurch wird deutlich, dass ungespeicherte Daten vorliegen. Diesen Zustand fragt Eclipse über eine Funktion ab; wir machen deutlich, dass keinesfalls Daten verändert wurden:
public boolean isDirty() {
return false;
}
Nun müssen wir eine Methode implementieren, die aufgerufen wird, wann immer der Editor den Fokus erhält. Auch hier brauchen wir vorerst keine Aktion des Editors, wir implementieren daher eine leere Methode:
public void setFocus() {
}
Zu guter letzt erfüllen wir die Schnittstelle IAdaptable durch Einfügen folgender Methode:
public Object getAdapter(Class adapter) {
if (adapter == EditPartViewer.class) {
return getGraphicalViewer();
} else if (adapter == EditDomain.class) {
return getEditDomain();
} else if (adapter == IWorkbenchPage.class) {
return getSite().getPage();
} else if (adapter == ISelectionProvider.class) {
return this.getSite().getSelectionProvider();
} else if (adapter == Example1Editor.class) {
return this;
}
return super.getAdapter(adapter);
}
Damit beantworten wir den Wissensdrang von Eclipse und GEF auf einfache und elegante Weise über das Entwicklungsmuster Adapter.
Jetzt fehlt nur mehr der eigentlich wichtigste Teil der Klasse - die Methode createPartControl. Durch diese werden die bisher erzeugten Teile erzeugt und integriert:
public void createPartControl(Composite parent) {
this.modelElement = createModel();
GraphicalViewer graphicalViewer = getGraphicalViewer();
graphicalViewer.setRootEditPart(getRootEditPart());
getEditDomain().addViewer(graphicalViewer);
graphicalViewer.createControl(parent);
graphicalViewer.setEditPartFactory(new Example1EditPartFactory());
graphicalViewer.setContents(this.modelElement);
}
Zuerst erzeugen wir ein Model-Objekt (in Produktivsystemen würden wir das, wie oben beschrieben, eher der Methode init erledigen). Anschließend wir ein Viewer angelegt, mit dem RootEditPart und der EditDomain verbunden und auf dem parent-Objekt (ein SWT-Composite) angezeigt. Weiters wird die EditPartFactory erzeugt und schließlich das Model-Objekt zur Anzeige zugewiesen.
[bearbeiten] EditPartFactory
Die im letzten Codestück beschriebene EditPartFactory müssen wir ebenfalls noch anlegen. Diese Klasse muss das Interface org.eclipse.gef.EditPartFactory implementieren und die darin vorgesehene Methode implementieren:
public EditPart createEditPart(EditPart context, Object object) {
if (object instanceof Parent) {
return new ParentEditPart((Parent) object);
} else if (object instanceof Child) {
return new ChildEditPart((Child) object);
} else {
assert (false);
return null;
}
}
Die Aufgabe dieser Factory ist einfach ersichtlich: es wird ihr ein Modell-Objekt übergeben (als generisches Modell wird tatsächlich die Klasse Object verwendet) und sie liefert einen neuen, für das Modellobjekt passenden EditPart zurück. Im Falle von Parent ist das ein ParentEditPart, bei Child ein ChildEditPart, ansonsten wird ein Fehler gemeldet und null zurück gegeben.
Wenn wir den Editor nun ausführen, kann dieser unser Model anzeigen. Wie das geht, wird im nächsten Kapitel beschrieben.
| GEF-Tutorial: Allgemeines - Kapitel 1 - Kapitel 2 - Kapitel 3 - Kapitel 4 - Kapitel 5 - Kapitel 6 - Kapitel 7 |
