Beruflich Dokumente
Kultur Dokumente
Inhaltsverzeichnis
Einführung 1
Was ist Eclipse? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Das Eclipse-Umfeld . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Über dieses Buch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Wie dieses Buch organisiert ist . . . . . . . . . . . . . . . . . . . . . . 4
Danksagung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1 Wo ist was? 9
1.1 Eclipse installieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2 Die erste Applikation: Hello World . . . . . . . . . . . . . . . . . 14
1.3 Die wichtigsten Präferenzen für die Java-Entwicklung . . . 19
1.3.1 Workbench-Einstellungen . . . . . . . . . . . . . . . . . 20
1.3.2 Installierte JREs . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.3 Compiler-Einstellungen . . . . . . . . . . . . . . . . . . . 23
1.3.4 Codeformatierung . . . . . . . . . . . . . . . . . . . . . . . 24
1.3.5 Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.4 Tasks und Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.4.1 Probleme, Probleme . . . . . . . . . . . . . . . . . . . . . 28
1.4.2 Die Aufgabenliste . . . . . . . . . . . . . . . . . . . . . . . 31
1.4.3 Lesezeichen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.5 Das Scrapbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6 Projektentwicklung 117
6.1 Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.1.1 Die Debug-Konfiguration . . . . . . . . . . . . . . . . 117
6.1.2 Die Debug-Perspektive . . . . . . . . . . . . . . . . . . 118
6.1.3 Den Programmablauf steuern . . . . . . . . . . . . . 119
6.1.4 Breakpoints verwalten . . . . . . . . . . . . . . . . . . . 122
6.1.5 Die Konsole . . . . . . . . . . . . . . . . . . . . . . . . . . 123
6.1.6 Remote-Debugging . . . . . . . . . . . . . . . . . . . . . 124
6.2 JUnit ...................................... 125
6.2.1 JUnit einrichten . . . . . . . . . . . . . . . . . . . . . . . . 125
6.2.2 Eine Testsuite erstellen . . . . . . . . . . . . . . . . . . 127
6.2.3 Eine Testsuite zum Ablauf bringen . . . . . . . . . 130
6.3 Dokumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
viii Inhaltsverzeichnis
9 JFace 219
9.1 Ressourcenverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . 219
9.1.1 Die Klasse FontRegistry . . . . . . . . . . . . . . . . . 219
9.1.2 Die Klasse ImageRegistry . . . . . . . . . . . . . . . . 220
9.1.3 Die Klasse JFaceColors . . . . . . . . . . . . . . . . . . 220
9.1.4 Die Klasse JFaceResources . . . . . . . . . . . . . . . 220
9.2 Dialoge und Fenster . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
9.2.1 Verschiedene Dialogtypen . . . . . . . . . . . . . . . . 222
9.2.2 Eigene Dialoge implementieren . . . . . . . . . . . . 225
9.2.3 Dialoge persistent machen . . . . . . . . . . . . . . . . 229
9.3 Viewer ...................................... 229
9.3.1 Das Viewer-Ereignismodell . . . . . . . . . . . . . . . 230
9.3.2 Die Viewer-Hierarchie . . . . . . . . . . . . . . . . . . 230
9.3.3 Zelleneditoren . . . . . . . . . . . . . . . . . . . . . . . . . 232
9.3.4 Datentransfer . . . . . . . . . . . . . . . . . . . . . . . . . 233
9.4 Textverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
9.4.1 Basisklassen der Textverarbeitung . . . . . . . . . . 234
9.4.2 Der SourceViewer . . . . . . . . . . . . . . . . . . . . . . 239
x Inhaltsverzeichnis
Einführung
Das Eclipse-Umfeld
Eclipse wurde natürlich nicht auf der grünen Wiese entwickelt. Es hat
sozusagen einen Stammbaum. Der Autor dieses Buches, dessen Lieb-
lings-Java-IDE jahrelang VisualAge for Java war, kann viele Konstruk-
tionselemente dieses Werkzeugs in Eclipse wieder erkennen. Und in der
Tat – dieselbe Firma, die hinter der Entwicklung von VisualAge stand,
steht auch hinter der Entwicklung von Eclipse. Es ist die Rede von OTI
(www.oti.com). Schon 1988 entwickelte OTI mit ENVY® eine kollabo-
rative Entwicklungsplattform für Smalltalk, die später unter dem
Namen VisualAge® von IBM lizenziert wurde. Darauf folgte die Ent-
wicklung von VisualAge für Java. Aber auch VisualAge für Java war
noch in Smalltalk implementiert. Mit Eclipse hat nun OTI die nächste
Generation der Entwicklungswerkzeuge eingeläutet. Viele der Design-
prinzipien von VisualAge finden sich in Eclipse – freilich mit dem
Unterschied, dass Eclipse in Java geschrieben ist und eine wesentlich
offenere Architektur hat als VisualAge.
Auch Eclipse wurde von IBM lizenziert, um es später der Open-
Source-Gemeinde zur Verfügung zu stellen. Das geschah durchaus
nicht ohne Eigennutz: Eclipse bildet sozusagen die Community-Edi-
tion der WebSphere Studio Application Developer (WSAD). Diese
schließt die Funktionalität von Eclipse mit ein, bietet darüber hinaus
allerdings zusätzliche Plugins für die Entwicklung von Web- und
Datenbank-Applikationen. Während Eclipse 3.0 aus etwa 90 Plugins
besteht, verfügt WSAD über 500-700 Plugins, so z.B. über Plugins für
die Entwicklung von Webanwendungen und Datenbankapplikationen.
Dann zeigen wir, wie eigene Produkte für die Auslieferung fertig
gemacht werden können. Auch hier bietet Eclipse eine integrierte
Unterstützung für alle Tätigkeiten an, die bis zur Erstellung einer
Update-Site und dem automatischen Einspielen von Updates reicht.
Als Beispiel entwickeln wir ein universelles und voll funktionsfähiges
Plugin für die Rechtschreibprüfung in Eclipse-Plattformen.
Den Abschluss bildet die Diskussion der Rich Client Platform
(RCP), die mit Eclipse 3 eingeführt wurde und als generische Plattform
für eine weite Klasse von Anwendungen in Frage kommt. Das Brett-
spiel Hex wird als Beispiel für eine RCP-Anwendung implementiert.
Im Anhang A listen wir noch einige weitere interessante Plugins
auf, die von verschiedenen Autoren auf dem Web zur Verfügung
gestellt werden. In Anhang B erläutern wir, was bei einem Versions-
wechsel der Eclipse-Plattform zu tun ist. Anhang C enthält Download-
adressen für die in diesem Buch verwendete Software und für den
Quellcode der Beispielprogramme.
Danksagung
Bücher sind immer Teamarbeit, auch wenn nur der Name des Autors
über oder unter dem Titel steht. Auch in diesem Falle ist das so, und
hier ist der Ort, an dem die Arbeit der anderen Mitglieder im Team
gewürdigt werden kann.
Besonderer Dank geht natürlich an meinen Verleger, den
dpunkt.verlag, insbesondere an René Schönfeldt, der dieses Projekt als
verantwortlicher Lektor betreute. Das Korrekturlesen besorgte
Annette Schwarz, für den Satz und Layout ist Birgit Bäuerlein verant-
wortlich, und das Titelbild entwarf Helmut Kraus.
Viele wertvolle Tips für dieses Buch stammten von den Gutach-
tern, insbesondere auch von Entwicklern und Mitarbeitern von OTI,
denen an dieser Stelle mein herzliches Dankeschön gilt. Überhaupt
wäre ja ohne die Entwicklung von Eclipse dieses Buch gar nicht
geschrieben worden, und Eclipse ist ein Werkzeug, das ich wirklich
nicht mehr missen möchte. Also nochmals: Danke!
Juni 2004
Berthold Daum
berthold.daum@bdaum.de
65
In diesem Kapitel diskutieren wir zunächst den Umgang mit den ver-
schiedenen Elementen der Benutzeroberfläche: Editoren, Betrachtern
und Perspektiven. Anschließend behandeln wir die grundlegenden Res-
sourcentypen: Projekte, Verzeichnisse und Dateien.
Dann wenden wir uns wieder der Java-Programmierung zu. Dies-
mal geben wir »Hello World« nicht auf der Konsole aus, sondern über
die Soundkarte! Dabei behandeln wir Themen wie den Import und
Export von Dateien und Archiven, das Assoziieren von Quelldateien
zu Binärdateien und die richtige Einstellung der Projekteigenschaften.
Schnellzugriff für
Navigator Ressourcen-Perspektive
Abb. 4–1 Der Navigator zeigt Projekte, Verzeichnisse und Dateien. Wir haben hier
schon etwas vorgegriffen: Das dargestellte Projekt wird erst in Kapitel 5 entwickelt. Beach-
ten Sie, dass im Ressourcen-Navigator Binär- und Quelldateien getrennt dargestellt wer-
den.
4.2 Ressourcen
Der Navigator der Eclipse-Workbench zeigt einen Überblick über die
von der Eclipse-Workbench verwalteten Ressourcen und gestattet die
Navigation in der Menge der Ressourcen.
4.2 Ressourcen 67
4.2.1 Ressourcentypen
daten in .metadata nicht mehr mit dem aktuellen Zustand einer Res-
source übereinstimmen.
Das ist nicht weiter schlimm. In diesem Fall führt man eine Syn-
chronisierung auf die betroffene Ressource durch. Man selektiert die
betroffene Ressource im Navigator und wendet die Kontextfunktion
Refresh an. Übrigens lässt sich diese Funktion nicht nur auf einzelne
Dateien, sondern auch auf ganze Verzeichnisse oder Projekte anwen-
den.
4.2.4 Navigation
4.3 Assoziationen
Der Typ einer Datei wird aus der Dateierweiterung bestimmt. In
Abbildung 4–1 sehen wir textbasierte Dateien wie die .java- und
.html-Dateien, aber auch binäre Dateien wie die .class-Dateien.
Dateierweiterungen bestimmen, was geschieht, wenn eine Datei geöff-
net wird.
Wenn wir z.B. eine .java-Datei mit der rechten Maustaste ankli- Öffnen mit ...
cken, so erhalten wir ein Pop-up mit den Kontextfunktionen. Wählen
wir dort das Untermenü Open with ..., erhalten wir ein weiteres Pop-
up mit Editoren. Im ersten Abschnitt sehen wir den für diese Datei
aktiven Editor (normalerweise den Java-Editor). Im zweiten Abschnitt
sehen wir alle für diesen Dateityp registrierten Editoren, darunter auch
Text Editor und System Editor. Der Texteditor ist der im Eclipse SDK
enthaltene Texteditor. Dieser kann für alle textbasierten Dateien
benutzt werden. Der Systemeditor ist der Editor, der auf dem ausfüh-
renden Betriebssystem für den jeweiligen Dateityp registriert ist.
Eclipse kann derartige externe Editoren aus der Workbench heraus
starten: Öffnen wir beispielsweise eine HTML-Datei, wird ein Web-
browser gestartet.
Im Wesentlichen werden diese Dateiassoziationen (welcher Editor Neue Assoziation
mit welchem Dateityp) von den in Eclipse installierten Plugins festlegen
bestimmt. Es ist jedoch auch möglich, manuell solche Assoziationen
hinzuzufügen. Hierzu ruft man die Funktion Window>Preferences>
Workbench>File Associations auf. Im oberen Fenster sieht man nun
eine Liste der registrierten Dateierweiterungen. Mit Hilfe der Add- und
Remove-Buttons kann man neue Dateierweiterungen hinzufügen oder
löschen. Im unteren Fenster erscheinen die registrierten Editoren für
die gerade selektierte Dateierweiterung. Auch hier können Editoren
hinzugefügt oder gelöscht werden. Mit dem Default-Button kann ein
bestimmter Editor zum Standardeditor erklärt werden.
Drückt man den Add-Button, so erhält man zunächst eine Liste
der internen, in Eclipse installierten Editoren. Markiert man das Feld
External Programs, erhält man die Liste der im ausführenden Betriebs-
system registrierten Programme (siehe Abb. 4–2). Durch einen Dop-
pelklick auf eines dieser Programme wählt man es als neuen Editor
aus.
70 4 Projekte richtig organisieren
Abb. 4–2 So können Dateiassoziationen definiert werden. Hier haben wir zunächst die
Dateierweiterung *.html hinzugefügt und assoziieren dann Microsoft Frontpage mit die-
sem Dateityp.
4.4 Packages
Schalten wir wieder zurück in die Java-Perspektive, ergibt sich ein
anderes Bild. Der Package-Explorer zeigt die verschiedenen Projekte
mit ihrer Package-Struktur und den Kompiliereinheiten.
Abb. 4–3 Jedes Package kann eindeutig auf einen Knoten im Verzeichnisbaum abge-
bildet werden. Kompiliereinheiten im Package-Explorer können dagegen aus mehreren
Ressourcen bestehen: aus einer Quelldatei und einer oder mehreren Binärdateien. Im Falle
der Klasse AnimatedAudioPlayer gibt es zwei Binärdateien – eine für AnimatedAudioPlayer
selbst und eine für die innere Klasse JavaClipLineListener.
4.4.2 Navigation
4.4.3 Typhierarchie
Die Typhierarchie zeigt die Super- und Subtypen für Klassen und Inter-
faces an. Dabei besteht die Möglichkeit, entweder die Sicht nur auf
Super- oder Subtypen zu beschränken oder die komplette Hierarchie
anzuzeigen. Mit der History-Funktion kann man rasch zwischen den
verschiedenen Sichten wechseln oder vorher angezeigte Hierarchien
noch einmal anzeigen (Abb. 4–4).
72 4 Projekte richtig organisieren
Abb. 4–4 Der Hierarchie-Browser besteht aus zwei Fenstern. Im oberen Fenster wird
die Typhierarchie angezeigt, im unteren Fenster werden die Felder und Methoden des
selektierten Typs angezeigt.
In der Toolbar des unteren Fensters findet man einige weitere Funk-
tionen. Der erste Button verändert das obere Fenster und zeigt dort nur
die Typen an, die das im unteren Fenster selektierte Feld bzw. die selek-
tierte Methode implementieren. Der zweite Button bewirkt, dass im
unteren Fenster auch die ererbten Methoden und Fenster des aktuellen
Typs angezeigt werden. Die restlichen Buttons verhalten sich wie die
entsprechenden Buttons des Outline-Fensters (siehe nächster Abschnitt).
Der Hierarchie-Browser erweist sich insbesondere dann als nütz-
lich, wenn man vorhandene Projekte oder Bibliotheken analysieren
will. Beim Erstellen eines neuen Projekts wird man ihn in der Regel erst
bei fortgeschrittenem Projektstand benötigen.
Eine schnelle Methode, die Typhierarchie zur Anzeige zu bringen,
ist die Taste F4, die genau wie die Kontextfunktion Open Type Hier-
archy wirkt. Alternativ gibt es noch die Tastenkombination Strg+T,
mit der die Typhierarchie in einem Popup-Fenster zur Anzeige
gebracht werden kann.
4.5 Der Outline-View 73
4.5.1 Darstellung
Abb. 4–5 Die verschiedenen Buttons der Outline-Toolbar erlauben es, bestimmte
Elemente aus dem Outline-View herauszufiltern. Mit dem Sortieren-Button können Felder
und Methoden alphabetisch geordnet werden (andernfalls gilt die Reihenfolge in der
Quelldatei).
import-Anweisung (importicon.tif)
Interface (interfacedef.tif)
Klasse (classdef.tif)
öffentliche Methode (public) (publicmethod.tif)
geschützte Methode (protected) (protectedmethod.tif)
private Methode (private) (privatemethod.tif)
Standardmethode (ohne Modifikation) (defaultmethod.tif)
öffentliches Feld (public) (publicfield.tif)
geschütztes Feld (protected) (protectedfield.tif)
privates Feld (private) (privatefield.tif)
Standardfeld (ohne Modifikation) (defaultfield.tif)
Konstruktor (constructor.tif)
4.5.2 Kontextfunktionen
4.6 Suchen
Suchen und Finden sind in Eclipse zweierlei: Die Search-Funktion führt
eine Suche im gesamten Workspace durch, während die Find-Funktion
nach Zeichenketten im aktuellen Dokument sucht.
76 4 Projekte richtig organisieren
Suchkriterien
Abb. 4–6 Der Dialog für die Suchkriterien besitzt mehrere Seiten, abhängig von den
installierten Plugins. Hier haben wir eine Seite für die generelle Suche nach Dateien, die
einen Suchbegriff enthalten, eine Seite für die Suche nach Hilfebegriffen, eine Seite für
die Java-spezifische Suche (geöffnet) und eine Seite für die Suche nach Plugins.
Bei der Java-Suche kann als Suchbegriff der Name eines Typs, einer
Methode, eines Package, eines Konstruktors oder eines Felds eingege-
ben werden. Dabei lässt sich der Name ganz oder nur teilweise quali-
fizieren. Zusätzlich kann die Suche eingeschränkt werden. Man kann
nur nach Deklarationen oder nur nach Verweisen suchen bzw. nach
beiden. Bei Feldern kann die Suche auf Lese- oder Schreibzugriffe
4.6 Suchen 77
Suchergebnisse
Abb. 4–7 Der Search-View zeigt alle Kompiliereinheiten, in denen der Suchbegriff
gefunden wurde. Die Anzahl der Vorkommen wird hinter jedem Eintrag in Klammern
angegeben, wenn der Suchbegriff mehrfach in einer Einheit erscheint. Ein Doppelklick auf
einen Eintrag öffnet die entsprechende Kompiliereinheit im Editor.
Abb. 4–8 Der Find/Replace-Dialog erlaubt es, Zeichenketten zu suchen und ggf. durch
andere Zeichenketten zu ersetzen. Seit Eclipse 3 werden auch reguläre Ausdrücke sowohl
beim Finden als auch beim Ersetzen unterstützt, wie hier gezeigt.
kann ein Fenster auf andere Fenster stapeln, indem man es bei der
Titelleiste oder beim Reiter packt und auf das Zielfenster zieht.
Erscheint ein Stapelsymbol, lässt man es los.
Als Fenster ! Views können auch als eigenständige Fenster außerhalb des Work-
bench-Fensters auf dem Desktop angelegt werden. Das geht aller-
dings nur unter Windows und Linux GTK. Man packt das Fenster
einfach beim Reiter and legt es auf dem Desktop ab.
FastView ! Im so genannten FastView kann ein View auf der FastView-Leiste
der Workbench minimalisiert werden: Es wird nur noch als ein
Icon repräsentiert. Ein Klick auf das Icon und es wird erneut sicht-
bar, ein weiterer Klick und es verschwindet wieder. Die FastView-
Leiste ist allerdings erst sichtbar, wenn mindestens ein View als
FastView angelegt wurde. Um das zu tun, klicken Sie mit der rech-
ten Maustaste auf den Reiter des Views und wählen dann die Kon-
textfunktion FastView. Auch die FastView-Leiste verfügt über
Abb. 4–9 Hier wurde der Search-View auf den rechten Rand des Problems-View
gezogen. Nun erscheinen beide Views nebeneinander.
4.8 Perspektiven verwalten 81
Selbstverständlich kann man jedes Fenster durch einen Klick auf das Fenster öffnen und
Kreuz in der rechten oberen Ecke des Fensters schließen. Doch wie öff- schließen
net man es wieder?
! Bei Editoren ist das einfach: Ein Doppelklick auf die entsprechende
Ressource im Navigator oder Package-Explorer öffnet die Res-
source im jeweiligen Editor.
! Etwas komplizierter ist es bei Views. Hier benutzt man die Funk-
tion Window>Show View>..., in der man dann den zu öffnenden
View auswählt.
Alle Fenster der Workbench sind maximierbar. Ein Doppelklick auf Fenster maximieren
den Reiter eines Views oder eines Editors bzw. ein Klick auf das Maxi-
mize-Icon maximiert das betreffende Fenster, d.h. das Fenster nimmt
allen Raum in der Workbench ein. Andere Fenster sind dann nicht
mehr sichtbar. Ein weiterer Doppelklick auf den Reiter oder ein Klick
auf das Restore-Icon stellt den alten Zustand wieder her.
Ein Klick auf das Minimize-Icon lässt einen View (und alle anderen Views minimieren
Views, die im selben Bereich gestapelt sind) auf die reine Titelzeile
zusammenschrumpfen. Mit einem Klick auf das Restore-Icon lässt sich
der alte Zustand wiederherstellen.
Nehmen wir an, Sie haben nun alle Fenster der Java-Perspektive ent-
sprechend Ihren Vorlieben und Bedürfnissen angeordnet wie in
82 4 Projekte richtig organisieren
Abb. 4–11 Mit der Funktion Window>Customize Perspective können ganze Kommando-
gruppen (Action Sets) aus einer Perspektive herausgenommen bzw. neue Kommando-
gruppen hineingenommen werden. Das mittlere und rechte Fenster zeigen an, wie sich
die ausgewählte Kommandogruppe auf das Menü und die Werkzeugleiste auswirkt.
Abb. 4–12 Importieren aus einem Dateisystem. Im Feld From Directory enthält die
Drop-down-Liste alle bisher verwendeten Import-Quellen, so dass man sich langwieri-
ges Suchen oft ersparen kann.
4.9 Dateien importieren 85
FreeTTS erheblich schneller abläuft als Flite. Java scheint also auch in
Sachen Geschwindigkeit C++ immer mehr Konkurrenz zu machen.
FreeTTS (Version 1.2.0) findet man unter http://source-
forge.net/projects/freetts. Nach dem Download der Binär- und
Quelldateien, die zusammen etwa 24 MB groß sind (also ein Fliegenge-
wicht im Vergleich zur Eclipse SDK Distribution), entpackt man die
Binärdateien in ein beliebiges Verzeichnis. Außerdem muss noch das
JSAPI (Java Speech API) entpackt werden, da es einem anderen Lizenz-
modell unterliegt. Dazu führen wir einfach das Programm jsapi.exe
im Ordner FreeTTS\lib aus.
Man kann nun der FreeTTS-Installationsanleitung folgen und
FreeTTS ausprobieren. Das lassen wir aber bleiben und gehen daran,
das System direkt nach Eclipse zu importieren.
Zunächst legen wir uns ein neues Java-Projekt namens FreeTTS an. Fremdsoftware
Dabei kreuzen wir die Option Create separate source and output fol- importieren
ders an. In diesem Projekt selektieren wir den Ordner src und rufen
mit der Kontextfunktion Import ... den Import-Wizard auf. Im folgen-
den Dialog sehen wir eine Liste möglicher Importquellen. Dort wählen
wir Filesystem aus und drücken den Next-Button. Im nun erscheinen-
den Dialog drücken wir den Browse-Button und navigieren bis zum
Abb. 4–13 Nach dem Import. Die importierte Datei wurde sofort kompiliert, was zu
einer Menge offener Verweise führte.
86 4 Projekte richtig organisieren
4.10 Projekteigenschaften
Wir entscheiden uns bei diesem Beispiel für die zweite Möglichkeit.
Der Java Build Path Um JAR-Dateien dem Java Build Path hinzuzufügen, rufen wir die
Funktion Project>Properties und dann Java Build Path ... auf und
gehen zur Seite Libraries. Hier sehen wir nur einen einzigen Eintrag,
nämlich das Java-1.4-Laufzeitsystem rt.jar.
Wir drücken die Taste Add External Jars und navigieren zum
Ordner ...\FreeTTS\lib. Von den nun aufgelisteten JAR-Dateien
(cmu_time_awb.jar, ..., jsapi.jar) selektieren wir alle und drücken
dann den Öffnen-Button. Die so ausgewählten JAR-Dateien werden
nun der Libraries-Liste hinzugefügt (Abb. 4–14). Dann drücken wir
noch den Ok-Button und harren der Dinge, die da kommen. Alle Feh-
lermeldungen sind verschwunden! (Falls nicht, müssen Sie den Build-
Prozess für das Projekt mit der Kontextfunktion Build Project manuell
ausführen.)
Was uns noch fehlt, ist der Quellcode der FreeTTS-Binärdateien.
Dieser Code ist im heruntergeladenen Archiv freetts-srcs-
1_2_beta.zip enthalten. Wir müssen diese Datei lediglich den entspre-
chenden Packages des FreeTTS-Projekts zuordnen. Und das geschieht
so:
4.10 Projekteigenschaften 87
Abb. 4–14 Die Libraries-Seite der Projekteigenschaften nach dem Einfügen der FreeTTS-
Jars.
Es ertönt:
Hello, World!
Vorausgesetzt freilich, Ihr Computer hat eine Soundkarte (oder Sound
on Board) und Sie haben einen Lautsprecher angeschlossen ...
Abb. 4–15 Die Java Browsing-Perspektive erlaubt in den oberen vier Fenstern die hierar-
chische Auswahl von Projekten, Packages, Typen und Methoden bzw. Feldern. Da man
leicht zwischen dieser Perspektive und der normalen Java-Perspektive hin und her wech-
seln kann, ist diese Perspektive ein gutes Hilfsmittel, um den Überblick über ein Projekt
nicht zu verlieren.
147
8 Das SWT
Die Vorteile von SWT liegen in der nahtlosen Einbettung der Applika-
tion in die jeweilige Ablaufumgebung. Da die SWT-Widgets nicht –
wie z.B. das Swing – die nativen Oberflächen emulieren, sondern ledig-
lich als Adapter für die entsprechenden Betriebssystemdienste wirken,
lassen sich mit dem SWT programmierte Benutzeroberflächen vom
Endanwender praktisch nicht von den Oberflächen nativer Applika-
tionen unterscheiden. Ein Button sieht unter Windows 2000 genauso
8.2 Vor- und Nachteile des SWT 149
Allerdings gibt es beim SWT gegenüber dem AWT auch einige Nach-
teile:
! Zunächst laufen mit dem SWT realisierte Anwendungen nur auf
den Plattformen ab, für die auch ein SWT implementiert wurde.
Das sind zurzeit vor allen Dingen die verschiedenen Windows-
Plattformen (einschließlich Windows CE), Linux mit den Fenster-
systemen GTK und Motif (einschließlich 64-Bit-GTK auf AMD64),
verschiedene Unix-Derivate (Solaris, QNX, AIX und HP-UX) und
Mac OS X.
! Im Großen und Ganzen sind die verschiedenen Implementierungen
des SWT funktional äquivalent, aber bekanntlich steckt der Teufel
im Detail. So gibt es einzelne Funktionen, bei denen das Verhalten
von Dialogelementen auf den verschiedenen Plattformen voneinan-
der abweicht. Plant man, ein Softwareprodukt für den Einsatz auf
mehreren Plattformen zu entwickeln, ist es unbedingt erforderlich,
das Produkt auf allen Plattformen gründlich zu testen.
150 8 Das SWT
8.4 Ereignisse
Ereignisse bilden den grundlegenden Mechanismus, mit dem Anwen-
dungen mit dem GUI kommunizieren. Applikationen registrieren
Zuhörerinstanzen (Listener) mit den Widgets, um auf Ereignisse rea-
gieren zu können. Meistens von Benutzeraktionen wie z.B. Mausklicks
oder Tastaturbetätigungen hervorgerufen, informieren die Ereignisse
über die Listener die Anwendung über die Art der Aktion.
Das Package org.eclipse.swt.events enthält drei verschiedene
Gruppen: Listener-Interfaces, Event-Klassen und Adapter-Klassen. Bei
den Ereignissen unterscheiden wir zwischen zwei Kategorien – typi-
sierten Ereignissen wie ControlEvent oder MouseEvent und generischen
Ereignissen (Event). Entsprechend setzt sich diese Aufteilung auch bei
den Listener-Interfaces fort.
8.4.1 Zuhörer
Hier haben wir für einen neu erzeugten Button eine innere anonyme
SelectionListener-Klasse als Zuhörer definiert.
Zu jeder Methode add...Listener() gibt es natürlich auch eine
Methode remove...Listener(). Insbesondere in komplexen Systemen
sollten Komponenten, die sich bei anderen Komponenten als Zuhörer
registrieren, auch wieder mit remove...Listener() abmelden, wenn sie
deaktiviert werden. So kann überflüssiger Overhead vermieden wer-
den. Später, wenn die Komponente wieder aktiviert wird, kann sie sich
ja wieder mit add...Listener() anmelden.
Genau aus diesem Grund sollte man auch keine Annahmen über
die zeitliche Reihenfolge beim Aufruf von registrierten Listenern
machen. Zwar wird intern beim Auftreten eines Ereignisses die Liste
der Zuhörer sequenziell abgearbeitet, da sich jedoch Komponenten in
eigener Regie an- und abmelden können, ist es praktisch unmöglich,
die Übersicht zu behalten, welcher Zuhörer vor oder nach einem ande-
ren aufgerufen wird.
8.4.2 Adapter
8.4.3 Ereignisse
Alle Ereignisklassen des SWT (bis auf die Klasse Event) sind Unterklas-
sen der Klasse TypedEvent, die ihrerseits ein Abkömmling der Klasse
java.util.EventObject ist.
8.5 Widgets
In diesem Abschnitt behandeln wir die verschiedenen GUI-Elemente
entlang ihrer Vererbungshierarchie (siehe Abb. 8–1), an deren Spitze
die Klasse Widget steht. Dazu gehören selbstverständlich die unmittel-
baren Bedienelemente wie Tasten (Button), Textfelder (Text) oder
Schieberegler (Slider), aber auch Elemente, die der Gruppierung von
Bedienelementen dienen wie die Klassen Group und Composite.
Widget
Control
List Text
Composite
Item Shell
Die beste Übersicht über die verschiedenen Widgets des SWT ist in den
Beispielapplikationen zu Eclipse enthalten. In Abschnitt 1.1 hatten wir
156 8 Das SWT
Die Klassen Display und Shell bilden die Basis für die Konstruktion
eines GUI. Dabei repräsentiert Display den GUI-Prozess (oder Thread)
und Shell die jeweiligen Fenster.
Display
Shell
Die Klasse Shell repräsentiert ein Fenster auf dem Desktop des jewei-
ligen Betriebssystems. Dabei kann eine Shell-Instanz drei verschiedene
Betriebszustände annehmen: maximiert, normal oder minimiert. Beim
Wechsel dieser Betriebszustände erzeugen Shell-Instanzen Ereignisse
vom Typ ShellEvent.
Achtung: Von Shell dürfen keine Unterklassen gebildet werden. Das merkt man
allerdings erst bei der Ausführung. Für die Implementierung eigener Fenster-
klassen verwendet man besser die JFace-Klasse Window (siehe Abschnitt 9.2).
Abb. 8–3 Eine Shell mit zwei Buttons (hier unter Windows2000). Die Shell wurde mit
den Optionen SWT.BORDER, SWT.TITLE, SWT.CLOSE, SWT.MIN und SWT.MAX erzeugt, verfügt
also über einen erhabenen Rand, eine Titelzeile und Tasten zum Schließen, Minimieren
und Maximieren.
thek als externe .JAR-Datei zum Classpath hinzufügen. Wie das geht,
ist bereits in Abschnitt 4.10 beschrieben. Die SWT-Bibliothek ist bei
Windows unter
\eclipse\plugins\org.eclipse.swt.win32_3.0.0\ws\win32\swt.jar
Monitor
Seit Eclipse 3.0 unterstützt das SWT auch Hardware, bei welcher der
grafische Arbeitsbereich auf mehrere Monitore verteilt sein kann. Das
mag zunächst nach Spezialanwendung klingen, ist es aber nicht. So
sind höherwertige Notebooks oft in der Lage, den Arbeitsbereich auf
das Notebook-LCD und auf einen externen Monitor zu verteilen.
8.5.3 Dialoge
Die Klasse Dialog ist eine abstrakte Klasse, von der konkrete native
Dialoge abgeleitet werden können. Der dazu notwendige Code folgt in
etwa diesem Muster:
public class MyDialog extends Dialog {
Object result;
// Konstruktor mit Stilparameter
public MyDialog (Shell parent, int style) {
super (parent, style);
}
// Konstruktor ohne Stilparameter
public MyDialog (Shell parent) {
this (parent, 0);
// Die Null kann durch eigene Defaultstilparameter ersetzt
// werden
}
public Object open () {
// Übergeordnete Shell holen (wie im Konstruktor gesetzt)
final Shell parent = getParent();
// Neue Dialog-Shell erzeugen
final Shell shell = new Shell(parent, SWT.DIALOG_TRIM |
SWT.APPLICATION_MODAL);
// Dialogtitel auf Shell-Titel übertragen
shell.setText(getText());
// Hier legen wir alle Widgets an
// Die Variable result wird üblicherweise in der
// Ereignisverarbeitung dieser Widgets gesetzt
8.5 Widgets 163
shell.open();
// Warten bis Dialog-Shell geschlossen wird
final Display display = parent.getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
return result;
}
}
Einige konkrete Unterklassen von Dialog sind allerdings bereits im Vordefinierte Dialoge
SWT enthalten. Dies sind:
Das Aussehen und die Funktionsweise all dieser Dialoge hängt natür-
lich vom jeweiligen Betriebssystem ab. Am besten schauen Sie sich die
verschiedenen Dialoge auf der Seite Dialog in der oben genannten
Eclipse-Beispielanwendung SWT Controls einmal an.
Im folgenden Code zeigen wir, wie wir den MessageBox-Dialog in MessageBox
einem Beispielprogramm anwenden (siehe Abb. 8–4):
164 8 Das SWT
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
public widgetTest() {
super();
}
8.5.5 Tasten
Damit kommen wir auch schon zu den Buttons. Dabei gibt es verschie-
dene Spielarten. Welcher Button-Typ von einem Konstruktor erzeugt
wird, wird über den Stilparameter gesteuert:
SWT.PUSH
oder:
final Button button = new Button(composite,SWT.PUSH);
Display display = composite.getDisplay();
final Image image = new Image(display, "bilder/grafik.gif");
button.setImage(image);
// Auf Klickereignisse reagieren
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Taste wurde gedrückt");
}
});
// Bild entsorgen, wenn der Button entsorgt wird
button.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
image.dispose();
}
});
Tipp: Eine gute Quelle für Bilder für Buttons, Werkzeugleisten und anderes
sind die jeweiligen icons-Verzeichnisse der verschiedenen Eclipse-Plugins,
z.B. \eclipse\plugins\org.eclipse.pde.ui_3.0.0\icons\obj16.
Die beiden Klassen Slider und Scale dienen der Eingabe eines nume-
rischen Werts mittels eines Schiebereglers. Üblicherweise wird Slider
für die Positionierung von Fensterinhalten verwendet (Scroll), wäh-
rend Scale für die Einstellung numerischer Parameter verwendet wird,
z.B. für Lautstärke, Helligkeit, Kontrast, etc. (Abb. 8–5)
SWT.HORIZONTAL
Horizontale oder vertikale Darstellung
SWT.VERTICAL
Skalen werden mit einem Rahmen umgeben. Wirkungslos bei
SWT.BORDER
Slider.
slider.setThumb(200);
// Auf Schiebeereignisse reagieren
slider.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Schieberegler verstellt: "
+slider.getSelection());
}
});
ProgressBar
Eine Reihe von Widgets sind von vornherein mit Schiebern ausgestat-
tet, und zwar alle Abkömmlinge von Scrollable. Welche Schieber bei
diesen Widgets aktiv sein sollen, kann mit den Stilkonstanten
SWT.H_SCROLL und SWT.V_SCROLL gesteuert werden. Die Klasse Scrolla-
ble benutzt als Schieberegler übrigens nicht Slider-Instanzen, sondern
Instanzen der Klasse ScrollBar.
ScrollBar ist im Unterschied zu Slider und Scale keine Unter-
klasse von Control.
SWT.MULTI
Bestimmt, ob das Textfeld mehrere oder nur eine Zeile hat.
SWT.SINGLE
SWT.READ_ONLY Text im Textfeld kann nicht vom Benutzer verändert werden.
SWT.WRAP Automatischer Umbruch wird unterstützt.
Abb. 8–6 Das obere Feld ist ein Feld vom Typ Text, das untere ist vom Typ StyledText
(siehe auch Abschnitt 8.5.13). Für beide Felder wurde die Schriftart Eras Book gesetzt, im
unteren Feld wurden noch Formatierungen vorgenommen. Außerdem wurde für beide
Felder eine vertikale Scrollbar gewählt.
Die Klasse Text besitzt ein reichhaltiges Arsenal an Methoden für die
Texteingabe. Insbesondere gibt es auch Methoden, um Textinhalte mit
dem systemweiten Clipboard auszutauschen (cut(), copy(), paste()).
Instanzen der Klasse Label dienen meist dazu, andere Widgets zu Beschriftungen
beschriften. Daneben können Label benutzt werden, um eine Grafik
bzw. eine horizontale oder vertikale Linie abzubilden. Der jeweilige
Einsatzzweck und das Aussehen eines Labels können über folgende
Stilkonstanten beeinflusst werden.
Bildlabel werden mit der Methode setImage() erzeugt. Wie bei But-
tons (Abschnitt 8.5.6) sollten Image-Instanzen wieder freigegeben wer-
den, wenn sie nicht mehr benötigt werden.
Abb. 8–7 Von links nach rechts: Tabelle, Liste und Combo. Oben sieht man das Combo
im Normalzustand, darunter nach einem Klick auf die Pfeiltaste. Bei der Tabelle wurden
die Gitterlinien und die Kopfzeile sichtbar geschaltet.
Tabellen
Die Klasse Table ist für die Darstellung von Tabellen verantwortlich.
Zusätzlich zu den Composite-Stilkonstanten stehen noch folgende
Stilkonstanten zur Verfügung:
SWT.SINGLE
Der Benutzer kann nur eine bzw. mehrere Tabellenzeilen auswählen.
SWT.MULTI
Die gesamte Tabellenzeile ist selektierbar (normalerweise kann nur das erste
SWT.FULL_SELECTION
Element selektiert werden).
Vor jeder Tabellenzeile wird eine Checkbox abgebildet. Der Zustand der Checkbox kann
SWT.CHECK
mit den Methoden setChecked() und getChecked() gesetzt bzw. abgefragt werden.
Diese Konstante zeigt eine virtuelle Tabelle an, d.h. eine Tabelle, deren Elemente
erst erzeugt werden, wenn sie tatsächlich benötigt werden. Damit können sehr große
Tabellen realisiert werden. Beim Aufbau einer virtuellen Tabelle muss die Anzahl der
Tabelleneinträge mit der Methode setItemCount() gesetzt werden. Sobald ein neues
SWT.VIRTUAL
Tabellenelement (TableItem) benötigt wird, wird es von der Tabelle erzeugt. Dann
wird ein SWT.SetData-Ereignis ausgelöst, wobei das Event-Objekt das neue Tabellen-
element enthält. Im Listener kann dann das Tabellenelement fertiggestellt werden,
bevor es dann von der Tabelle zur Anzeige gebracht wird.
Listen
Combos
Schließlich gibt es noch die Klasse Combo, die Listenauswahl und Text-
eingabe kombiniert.
Instanzen der Klasse Combo erzeugen folgende Ereignistypen:
CCombo Das (nicht native) Widget CCombo ist dem Combo-Widget sehr ähnlich.
Es kann jedoch auch randlos auftreten und kann so in Tabellenzellen
angewandt werden.
8.5.9 Bäume
Die Klasse Tree ist für die Darstellung von Bäumen verantwortlich
(Abb. 8–8). Dabei werden die Funktionsweise und das Aussehen des
Baums durch folgende Stilkonstanten beeinflusst:
Abb. 8–8 Zwei Bäume: links nur mit Textknoten; rechts wurden den Textknoten Bilder
zugewiesen.
8.5 Widgets 177
});
// TreeListener hinzufügen
tree.addTreeListener(new TreeAdapter() {
public void treeCollapsed(TreeEvent e) {
System.out.println("Knoten kollabiert: " +
((TreeItem) e.item).getText());
}
public void treeExpanded(TreeEvent e) {
System.out.println("Knoten expandiert: " +
((TreeItem) e.item).getText());
}
});
Bei größeren Bäumen wird man in der Regel darauf verzichten, den
Baum gleich komplett aufzubauen. Stattdessen bietet sich hier an,
Baumknoten erst dann vollständig aufzubauen, wenn sie sichtbar wer-
den, also wenn der entsprechende Elternknoten expandiert wird.
8.5.11 Pultordner
if (!display.readAndDispatch())
display.sleep();
}
}
CTabFolder Das (nicht native) Widget CTabFolder ist dem TabFolder-Widget recht
ähnlich, erlaubt jedoch die Positionierung der Reiter (CTabItem) am
Kopf (SWT.TOP) oder Fuß (SWT.BOTTOM) des Ordners und verwendet Rei-
ter mit geschwungenem Umriss.
Werkzeugleisten
Die Beschriftung einer Taste wird mit der Methode setText() gesetzt.
Bildtasten werden mit der Methode setImage() erzeugt. Zusätzliche
Bilder, mit setHotImage() (für Tasten unter dem Mauszeiger) und set-
DisabledImage() (für inaktive Tasten) gesetzt, können die verschiede-
nen Betriebszustände der Werkzeugtaste anzeigen. Wie bei Buttons
(Abschnitt 8.5.6) sollten die jeweiligen Image-Instanzen wieder freige-
geben werden, wenn sie nicht mehr benötigt werden. Mit setToolTip-
Text() kann ein Text gesetzt werden, der angezeigt wird, wenn die
Maus über die Taste bewegt wird.
ToolItem-Instanzen erzeugen bei Betätigung SelectionEvent-Ereig-
nisse. Bei DROP_DOWN-Tasten muss ermittelt werden, ob die Haupttaste
oder die Pfeiltaste betätigt wurde. Dies kann über die Abfrage
(event.detail == SWT.ARROW) geschehen. Die Applikation kann dann
eine Menüliste aufbauen, welche die Auswahl einer Funktion erlaubt.
Menüs
Menüs können mit Hilfe der Klasse Menu aufgebaut werden. Mit den
folgenden Stilkonstanten kann das Erscheinungsbild von Menüs
bestimmt werden:
SWT.BAR Menüleiste
SWT.DROP_DOWN Drop-down-Menü
SWT.POP_UP Pop-up-Menü
// Menüleiste anlegen
Menu menuBar = new Menu(toplevelShell, SWT.BAR);
toplevelShell.setMenuBar(menuBar);
// Menütitel anlegen
MenuItem fileTitle = new MenuItem(menuBar, SWT.CASCADE);
fileTitle.setText("File");
// Untermenü für diesen Menütitel anlegen
Menu fileMenu = new Menu(toplevelShell, SWT.DROP_DOWN);
fileTitle.setMenu(fileMenu);
// Menüeintrag anlegen
MenuItem item = new MenuItem(fileMenu, SWT.NULL);
item.setText("Exit");
// Ereignisverarbeitung für Menüeintrag
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
toplevelShell.close();
}
});
Im nächsten Beispiel erzeugen wir eine CoolBar, die aus zwei verschieb-
baren Gruppen mit insgesamt fünf verschiedenen Tasten besteht. Dar-
unter ist auch eine Drop-down-Taste, in deren Ereignisverarbeitung
ein Menü mit zwei Einträgen aufgebaut wird.
// CoolBar erzeugen
final CoolBar coolbar = new CoolBar(composite, SWT.NULL);
// ToolBar als Bestandteil der CoolBar erzeugen
final ToolBar toolbar1 = new ToolBar(coolbar, SWT.NULL);
// Pushbutton erzeugen
final ToolItem toolitem1 = new ToolItem(toolbar1, SWT.PUSH);
toolitem1.setText("Push");
toolitem1.setToolTipText("Push button");
// Ereignisverarbeitung für pushbutton
toolitem1.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println(
"Werkzeugtaste gedrückt: " + toolitem1.getText());
}
});
// Checkbutton erzeugen
final ToolItem toolitem2 = new ToolItem(toolbar1, SWT.CHECK);
toolitem2.setText("Check");
toolitem2.setToolTipText("Check button");
// CoolItem erzeugen
final CoolItem coolitem1 = new CoolItem(coolbar, SWT.NULL);
// Die Toolbar diesem Coolitem zuordnen
184 8 Das SWT
coolitem1.setControl(toolbar1);
// Größe der Toolbar berechnen
Point size = toolbar1.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// Benötigte Größe des CoolItem berechnen
size = coolitem1.computeSize(size.x, size.y);
// auf diese Größe setzen
coolitem1.setSize(size);
// Die minimale Breite entspricht der des ersten Buttons
coolitem1.setMinimumSize(toolitem1.getWidth(), size.y);
Diese Klasse dient dazu, den Mauszeiger als Tätigkeitssymbol (Sanduhr etc.) darzustellen.
Dazu wird die Methode showWhile(display, runnable)verwendet, wobei der zweite Parame-
BusyIndicator
ter vom Typ java.lang.Runnable sein muss. In dessen run()-Methode muss die komplette
Verarbeitung ausgeführt werden; währenddessen wird das Tätigkeitssymbol angezeigt.
Mit dieser Klasse kann einem Composite ein anderes GUI-Element zugeordnet werden.
Wird das Composite verschoben oder in der Größe verändert, so verändert sich ggf. auch
die Position des zugeordneten Elements. Üblicherweise verwendet man ControlEditor, um
ControlEditor einem nicht editierfähigen Composite GUI-Elemente für die Eingabe von Werten zuzuord-
nen. Die Eclipse-API-Referenzdokumentation enthält ein Beispiel, in dem einer Canvas-
Instanz (siehe Abschnitt 8.7) ein Button zugeordnet wird, bei dessen Betätigung die Hinter-
grundfarbe der Zeichenfläche geändert werden kann.
Diese Klasse funktioniert ähnlich wie die List-Klasse (siehe Abschnitt 8.5.8), jedoch
erscheint die Liste in einer eigenen Shell über der im Konstruktor spezifizierten Shell.
PopupList
Üblicherweise wird diese Klasse verwendet, um innerhalb einer Tabellenzelle Werte aus
einer Liste auszuwählen.
Diese Klasse ist als eine Unterklasse von Composite implementiert und organisiert seine
Kindelemente horizontal oder vertikal (wie angegeben) und durch verschiebbare Rahmen
(Sash) getrennt (siehe Abschnitt 8.5.10). Jedem Kindelement kann ein Gewicht zugeordnet
SashForm
werden, um Breite bzw. Höhe des Kindelements vorzugeben. Mit der Methode setMaxi-
mizedControl() kann ein einzelnes Kindelement temporär maximiert und die anderen
minimiert werden.
Diese Klasse implementiert ein ein- oder mehrzeiliges Texteingabefeld ähnlich der Klasse
Text. Zusätzlich werden jedoch bestimmte Textattribute unterstützt (Vorder- und Hinter-
grundfarbe, Textfont, fetter, kursiver und normaler Textstil). Diese Funktionalität reicht für
Programmeditoren aus, für die Textverarbeitung jedoch nicht.
Die Textformatierung erfolgt mit Hilfe der Methoden getStyleRangeAtOffset(), get-
StyleRanges(), setStyleRange(), setStyleRanges(), mit denen StyleRange-Instanzen abge-
StyledText fragt und gesetzt werden können. Außerdem gibt es die Methoden getLineBackground()
und setLineBackground() zum Abfragen und Setzen der Hintergrundfarbe einer Zeile.
Alternativ zu diesen Methoden kann eine eigene Stilverarbeitung mit Hilfe von Line-
StyleListener- bzw. LineBackgroundListener-Instanzen implementiert werden.
Der Textinhalt eines StyledText-Widgets muss das Interface StyledTextContent imple-
mentieren. Insofern können Sie auch eigene Inhaltsmodelle definieren. Mit der Method set-
Content() kann ein StyledText-Widget initialisiert werden.
Diese Klasse entspricht in ihrer Funktion der Klasse Tree (siehe Abschnitt 8.5.9). Allerdings
weicht die grafische Darstellung ab: Die Baumstruktur erscheint als eine Serie von hierar-
TableTree
chisch eingerückten Tabellen, die Linien, welche die Zweige des Baums andeuten sollen,
entfallen. Die einzelnen Baumknoten werden durch TableTreeItem-Instanzen gebildet.
Diese Klassen gleichen dem oben erwähnten ControlEditor, jedoch spezialisiert auf die
TableEditor
Klassen Table, Tree und TableTree. Die Eclipse-API-Referenzdokumentation enthält
TreeEditor
Beispiele, in denen einzelnen TableItem-, TreeItem- und TableTreeItem-Instanzen ein
TableTreeEditor
Textfeld zugeordnet wird, das die Modifikation der jeweiligen Elemente erlaubt.
Hier ist ein Beispiel für die Anwendung der Klasse SashForm. Zwei Sas-
hForm-Instanzen werden erzeugt, zunächst eine horizontale und dann
innerhalb dieser eine vertikale. Beide SashForm-Komponenten haben
List-Widgets als Kindelemente.
188 8 Das SWT
Abb. 8–9 Das Ergebnis: eine horizontale und eine vertikale Trennleiste, welche drei
Listenfelder voneinander trennen. Beide Trennleisten können mit der Maus verschoben
werden. Wird die Größe des Fensters geändert, verschieben sich die Trennleisten entspre-
chend.
Seit Eclipse V3 ist auch ein Webbrowser als Widget verfügbar und
zwar als Klasse Browser in Package org.eclipse.swt.browser. Damit
wird es möglich, auf einfache Art und Weise HTML-Inhalte in SWT-
Applikationen anzuzeigen. Das Eclipse-Team hat freilich hier keinen
eigenen Browser implementiert, sondern benutzt die nativen Browser
der Ablaufplattform. Unter Windows implementiert die Klasse Browser
eine OLE-Einbettung des Internet Explorers. Unter Linux wird Mozilla
benutzt, unter Mac OS X der Safari-Browser. Angenehm an dieser
Strategie ist, dass das Browser-Widget genauso mächtig ist wie die ver-
wendeten Webbrowser. Sicherheits- und andere Einstellungen im
Internet Explorer oder in Mozilla beeinflussen auch das Browser-Wid-
get. Nachteilig an dieser Strategie ist, dass sich das Widget in vielerlei
Hinsicht nicht wie ein Standard-Widget verhält. So kann man z.B. die-
8.6 Layouts 189
8.6 Layouts
Layouts werden benutzt, um GUI-Elemente auf einem Composite auto-
matisch anzuordnen. Das Layout errechnet die Position und Größe
jedes GUI-Elements, das auf dem Composite angebracht ist. Sollte das
Composite in der Größe verändert werden – durch Programmaktion
oder durch Benutzereinwirkung –, so wird die Anordnung der GUI-
Elemente automatisch neu berechnet.
Normalerweise werden dabei alle GUI-Elemente über einen Kamm
geschoren. Allerdings ist es auch möglich, das Layout individueller
GUI-Elemente durch die Zuordnung spezifischer Layoutdaten zu
beeinflussen. Das erfolgt mit der Control-Methode setLayoutData().
Eclipse stellt fünf vordefinierte Layout-Klassen zur Verfügung.
Außerdem besteht die Möglichkeit, selbst Layout-Klassen zu erstellen.
Die Namen der vordefinierten Layout-Klassen folgen alle dem Muster
»*Layout«. Die Namen der zugehörigen Klassen für die individuellen
Layoutdaten folgen dem Muster »*Data«. Bis auf die Klasse StackLay-
out, die im Package org.eclipse.swt.custom (siehe Abschnitt 8.5.13)
enthalten ist, sind alle anderen vordefinierten Layouts im Package
org.eclipse.swt.layout enthalten.
An dieser Stelle sei auch auf den ausgezeichneten Artikel Under-
standing Layouts in SWT von Carolyn MacLeod und Shantha Rama-
chandran [MacLeod2002] hingewiesen.
190 8 Das SWT
Die beste Übersicht über die verschiedenen Layouts ist in den Beispiel-
applikationen zu Eclipse enthalten. Dazu rufen wir die Funktion Win-
dow>Show View>Other ... auf. Im folgenden Dialog wählen wir die
Applikation SWT Examples>SWT Layouts aus. Diese Applikation
erscheint dann auch prompt im Fenster rechts unten (Abb. 8–10).
Damit wir etwas mehr Platz haben, maximieren wir diese Applikation
mit einem Doppelklick auf den Reiter.
RowData Höhe und Breite jedes einzelnen GUI-Elements innerhalb einer RowLay-
out-Instanz können individuell mittels RowData-Instanzen gesetzt wer-
den. Im folgenden Beispiel erzeugen wir zwei Tasten und setzen deren
Höhe und Breite.
Button button1 = new Button(composite, SWT.PUSH);
button1.setText("70x20");
button1.setLayoutData(new RowData(70, 20));
Button button2 = new Button(composite, SWT.PUSH);
button2.setText("50x35");
button2.setLayoutData(new RowData(50, 35));
Die Klasse GridLayout ist die wohl nützlichste und mächtigste Klasse
der vordefinierten Layout-Klassen. Allerdings ist sie auf Grund der vie-
len verschiedenen Einstellungsmöglichkeiten auch recht kompliziert zu
handhaben. Wer etwas Erfahrung mit der Gestaltung von HTML-Sei-
ten mit Hilfe von Tabellen hat, weiß, wovon ich rede.
Das GridLayout hat in der Tat einige Ähnlichkeiten mit HTML-
Tabellen. Auch hier gibt es Reihen und Spalten, und es ist möglich,
horizontal oder vertikal angrenzende Tabellenelemente miteinander zu
verschmelzen.
Die folgenden Optionen stehen für das GridLayout zur Verfügung:
Anzahl der Spalten. Die Anzahl der Reihen ergibt sich aus der Anzahl der GUI-
numColumns
Elemente und der Anzahl der Spalten.
Ist dieses Feld auf den Wert true gesetzt, so wird allen Spalten die gleiche Breite
makeColumnsEqualWidth
zugeordnet. Die Standardeinstellung ist »false«.
marginHeight Dieses Feld kontrolliert die Größe des Randbereichs oben und unten in Pixeln.
marginWidth Dieses Feld kontrolliert die Größe des Randbereichs links und rechts in Pixeln.
horizontalSpacing Dieses Feld kontrolliert den Minimalabstand zwischen den Spalten in Pixeln.
verticalSpacing Dieses Feld kontrolliert den Minimalabstand zwischen den Reihen in Pixeln.
8.6 Layouts 193
Das folgende Beispiel zeigt, wie die einzelnen Optionen für eine Grid-
Layout-Instanz gesetzt werden:
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
gridLayout.marginWidth = 10;
gridLayout.makeColumnsEqualWidth = true;
gridLayout.marginHeight = 5;
gridLayout.horizontalSpacing = 6;
gridLayout.verticalSpacing = 4;
gridLayout.makeColumnsEqualWidth = true;
composite.setLayout(gridLayout);
Die Layout-Optionen, die mit Hilfe von GridData-Instanzen für indivi- GridData
duelle GUI-Elemente innerhalb einer GridLayout-Instanz gesetzt wer-
den können, sind recht üppig. So verfügen GridData-Instanzen über die
folgenden Felder:
Ist dieses Feld auf den Wert true gesetzt, so füllt das GUI-Element den eventuell
grabExcessHorizontalSpace
verbleibenden horizontalen Raum aus. Die Standardeinstellung ist »false«.
Ist dieses Feld auf den Wert true gesetzt, so füllt das GUI-Element den eventuell
grabExcessVerticalSpace
verbleibenden vertikalen Raum aus. Die Standardeinstellung ist »false«.
Spezifiziert eine minimale Höhe in Pixeln. Ist hier ein Wert angegeben, so ist bei dem
heightHint
jeweiligen GUI-Element eine etwaige vertikale Scroll-Funktion außer Kraft gesetzt!
Spezifiziert, wie das GUI-Element horizontal in seiner Zelle ausgerichtet wird. Die
folgenden Konstanten können angegeben werden:
GridData.BEGINNING (Standard)
horizontalAlignment
GridData.CENTER
GridData.END
GridData.FILL
horizontalIndent Spezifiziert in Pixeln, wie weit ein GUI-Element von links eingerückt wird.
Spezifiziert, wie viele Zellen das GUI-Element in horizontaler Richtung verbraucht
horizontalSpan
(die Zellen werden miteinander verschmolzen).
Spezifiziert, wie das GUI-Element vertikal in seiner Zelle ausgerichtet wird. Die
folgenden Konstanten können angegeben werden:
verticalAlignment GridData.BEGINNING
GridData.CENTER (Standard)
GridData.END
GridData.FILL
Spezifiziert, wie viele Zellen das GUI-Element in vertikaler Richtung verbraucht
verticalSpan
(die Zellen werden miteinander verschmolzen).
Spezifiziert eine minimale Breite in Pixeln. Ist hier ein Wert angegeben, so ist die
widthHint
horizontale Scroll-Funktion außer Kraft gesetzt!
Konstante Äquivalent
GridData.GRAB_HORIZONTAL grabExcessHorizontalSpace = true
GridData.GRAB_VERTICAL grabExcessVerticalSpace = true
horizontalAlignment =
GridData.HORIZONTAL_ALIGN_BEGINNING
GridData.BEGINNING
GridData.HORIZONTAL_ALIGN_CENTER horizontalAlignment = GridData.CENTER
GridData.HORIZONTAL_ALIGN_END horizontalAlignment = GridData.END
GridData.HORIZONTAL_ALIGN_FILL horizontalAlignment = GridData.FILL
GridData.VERTICAL_ALIGN_BEGINNING verticalAlignment = GridData.BEGINNING
GridData.VERTICAL_ALIGN_CENTER verticalAlignment = GridData.CENTER
GridData.VERTICAL_ALIGN_END verticalAlignment = GridData.END
GridData.VERTICAL_ALIGN_FILL verticalAlignment = GridData.FILL
HORIZONTAL_ALIGN_FILL |
GridData.FILL_HORIZONTAL
GRAB_HORIZONTAL
GridData.FILL_VERTICAL VERTICAL_ALIGN_FILL | GRAB_VERTICAL
GridData.FILL_BOTH FILL_VERTICAL | FILL_HORIZONTAL
In Abschnitt 10.3 finden Sie ein Beispiel für die Anwendung der Grid-
Layout-Klasse.
Sollten alle diese Layout-Möglichkeiten nicht ausreichen, kann
man immer noch Composite-Instanzen mit Hilfe von GridLayouts
schachteln, eine Technik, die von HTML-Seite her wohl bekannt ist.
Die Klasse FormLayout wurde mit Eclipse 2.0 eingeführt. Sie erlaubt es,
die einzelnen GUI-Elemente auf einer zweidimensionalen Fläche
jeweils aneinander bzw. an die Ränder des übergeordneten Composite
zu docken. Das geschieht mit Hilfe von FormAttachment-Instanzen.
Für FormLayout stehen nur die folgenden Optionen zur Verfügung:
marginHeight Dieses Feld kontrolliert die Größe des Randbereichs oben und unten in Pixeln.
marginWidth Dieses Feld kontrolliert die Größe des Randbereichs links und rechts in Pixeln.
und
FormAttachment fa = new FormAttachment(zaehler, nenner, offset);
Die Position p ermittelt sich dann aus der Höhe oder Breite d des Com-
posite wie folgt:
p = d*zaehler/nenner+offset
Nehmen wir z.B. an, dass unser Composite 400 Pixel breit ist und 300
Pixel hoch ist. Erzeugen wir nun eine FormAttachment-Instanz mit
FormAttachment(30,10) und weisen sie dem top-Feld einer FormData-
Instanz zu, so ergibt sich:
p = 30/100*300+5 = 95
Der obere Rand unseres GUI-Elements ist also 95 Pixel vom oberen
Rand der Client-Area des Composite entfernt. Würden wir dieselbe
Instanz stattdessen dem bottom-Feld zuweisen, so wäre der untere
Rand unseres GUI-Elements 95 Pixel vom unteren der Client-Area des
Composite entfernt.
Würden wir dagegen dieselbe Instanz dem left-Feld zuweisen, so
ergäbe sich
p = 30/100*400+5 = 125
Der linke Rand unseres GUI-Elements ist also 125 Pixel vom linken
Rand der Client-Area des Composite entfernt. Entsprechendes gilt für
die Zuweisung an das right-Feld.
196 8 Das SWT
Referenz-GUI-Element Für die zweite Methode der Positionierung gibt es drei Konstruk-
tor-Varianten:
FormAttachment (control, offset, alignment)
FormAttachment (control, offset)
FormAttachment (control)
Der Parameter control gibt dabei die Control-Instanz (also das GUI-
Element) an, auf die wir uns beziehen.
Der Parameter offset gibt den Abstand zwischen den GUI-Ele-
menten an. Wird der Parameter weggelassen, ist der Abstand Null.
Der Parameter alignment gibt an, auf welche Kante des Referenze-
lements wir uns beziehen. Für Zuweisungen an top- und bottom-Felder
können hier die Stilkonstanten SWT.TOP, SWT.BOTTOM und SWT.CENTER
verwendet werden. Für Zuweisungen an left- und right-Felder sind
es die Konstanten SWT.LEFT, SWT.RIGHT und SWT.CENTER. Wird der Para-
meter alignment weggelassen, so wird die nächstliegende Kante
benutzt.
marginHeight Dieses Feld kontrolliert die Größe des Randbereichs oben und unten in Pixeln.
marginWidth Dieses Feld kontrolliert die Größe des Randbereichs links und rechts in Pixeln.
topControl Die sichtbare Control-Instanz.
buttonA.setText("Taste A");
final Button buttonB = new Button(stackComposite, SWT.PUSH);
buttonB.setText("Taste B");
8.7 Grafik
Die Interfaces und Klassen für grafische Operationen sind im Package
org.eclipse.swt.graphics enthalten. Im Wesentlichen orientiert sich
der Funktionsumfang dieser Bibliothek an den grafischen Fähigkeiten
der unterstützten Plattformen. Obwohl sie etwas mehr bietet als die
grafischen AWT-Grundfunktionen, reicht der Funktionsumfang nicht
an den des Java2D-API heran. In Abschnitt 8.8 werden wir diskutie-
ren, wie man den Funktionsumfang erweitern kann.
Gezeichnet werden kann auf Instanzen aller Klassen, die das Dra-
wable-Interface implementieren. Insbesondere sind das die Klassen
Image, Control und ihre Unterklassen wie Canvas und Display. Dabei
wird Image besonders für Double-Buffering-Zwecke eingesetzt, Canvas
dient normalerweise als Zeichenfläche für Grafikausgaben (z.B. Dia-
gramme) oder auch als Zeichenfläche für den Endbenutzer, Display
dient der bildschirmfüllenden Ausgabe von Grafiken.
Die Auswahl des Zeichenmediums erfolgt im Konstruktor GC().
Erzeugt man einen Grafikkontext mit Hilfe des GC()-Konstruk-
tors, muss man die GC-Instanz mit dispose() wieder entsorgen, wenn
sie nicht mehr benötigt wird, denn eine GC-Instanz belegt Betriebssys-
temressourcen. Im Regelfall wird man jedoch nicht selbst einen Gra-
fikkontext erzeugen, sondern einen Grafikkontext benutzen, der von
einem PaintEvent übergeben wird. Denn die goldene Regel bei der
Grafikverarbeitung lautet:
Wir zeigen das an einem Beispiel, in dem wir eine grüne Zierlinie um
eine Composite-Instanz zeichnen:
composite.addPaintListener(new PaintListener () {
public void paintControl(PaintEvent event){
// Display aus Ereignis holen
Display display = event.display;
// Grüne Systemfarbe holen - braucht nicht entsorgt zu werden
Color green = display.getSystemColor(SWT.COLOR_DARK_GREEN);
// Das Ereignis liefert auch den Grafikkontext
GC gc = event.gc;
// Linienfarbe setzen
gc.setForeground(green);
// Größe des nutzbaren Bereichs im Composite
Rectangle rect = ((Composite) event.widget).getClientArea();
// Nun ein Rechteck zeichnen
gc.drawRectangle(rect.x + 2, rect.y + 2,
rect.width - 4, rect.height - 4);
}
});
8.7 Grafik 199
8.7.2 Farben
8.7.3 Schriftarten
Ähnlich wie mit Farben verhält es sich auch mit Fonts. Der aktuelle
Font wird im Grafikkontext mit setFont() gesetzt.
! Den aktuellen Systemfont kann man sich von einer Device-Instanz
mit Hilfe der Methode getSystemFont() holen. Einen solchen Font
darf man nicht mit dispose() entsorgen.
! Neue Font-Instanzen können mit Hilfe eines Konstruktors erzeugt
werden, z.B.
Font font = new Font(device,"Arial",12,SWT.ITALIC)
oder
Font font = new Font(device,new FontData("Arial",12,SWT.ITALIC))
FontData ist eine geräteunabhängige Repräsentation einer Schriftart.
Font-Instanzen müssen mit dispose() wieder entsorgt werden,
wenn sie nicht mehr gebraucht werden.
200 8 Das SWT
Die Klasse GC stellt noch einige weitere Methoden für die Textverarbei-
tung zur Verfügung. So liefert z.B. die Methode getFontMetrics() ein
FontMetrics-Objekt, das die charakteristischen Maße des aktuellen
Fonts enthält. Mit den Methoden stringExtent() und textExtent()
kann man ermitteln, welche Abmessungen eine Zeichenkette hätte,
wenn sie unter dem aktuellen Font gezeichnet würde. stringExtent()
ignoriert dabei TAB- und CR-Zeichen.
8.7.4 Bilder
Die Klasse Image ist für die geräteabhängige Darstellung von Bildern
verantwortlich. Image-Instanzen können auf verschiedene Arten
erzeugt werden: durch Angabe eines java.io.Stream-Objekts, durch
Angabe eines Dateinamens (absolut oder relativ zum Projekt) oder
durch Angabe eines ImageData-Objekts.
Die Klasse ImageData ist für die geräteunabhängige Darstellung
von Bildern verantwortlich. Instanzen dieser Klasse können ebenfalls
durch Angabe eines java.io.Stream-Objekts oder durch Angabe eines
Dateinamen erzeugt werden. Sie können auch mit Hilfe der Methode
getImageData() aus einer Image-Instanz geholt werden. So kann man
jederzeit zwischen geräteabhängiger und geräteunabhängiger Darstel-
lung wechseln.
8.7 Grafik 201
bufferGC.fillRectangle(10,10,rect.width-20,rect.height-20);
// Nun das gepufferte Bild auf Canvas zeichnen
gc.drawImage(buffer,0,0);
// Den Grafikkontext des Puffers entsorgen
bufferGC.dispose();
// Den Puffer entsorgen
buffer.dispose();
}
});
CURSOR_ARROW Pfeil
CURSOR_WAIT Warten
CURSOR_CROSS Fadenkreuz
CURSOR_APPSTARTING Start einer Anwendung
CURSOR_HELP Hilfe
CURSOR_SIZEALL Gesamtgröße ändern
CURSOR_SIZENESW Größenänderung auf NO/SW-Achse
CURSOR_SIZENS Größenänderung auf N/S-Achse
CURSOR_SIZENWSE Größenänderung auf NW/SE-Achse
CURSOR_SIZEWE Größenänderung auf W/O-Achse
CURSOR_SIZEN Größenänderung Nordrichtung
CURSOR_SIZES Größenänderung Südrichtung
CURSOR_SIZEE Größenänderung Ostrichtung
CURSOR_SIZEW Größenänderung Westrichtung
CURSOR_SIZENE Größenänderung Nordostrichtung
8.8 Ein Widget mit Swing 203
Seit Eclipse V3 besteht auch die Möglichkeit, die Pixel des Mauszeigers
auch direkt über die Angabe einer ImageData-Instanz (siehe Abschnitt
8.7.4) anzugeben. Eine zweite ImageData-Instanz kann eine zusätzliche
Maske definieren.
Wichtig ist, dass die Cursor-Instanz wieder mit dispose() freigege-
ben werden musss, wenn sie nicht mehr benötigt wird. Das Gleiche gilt
für die ImageData-Instanzen.
8.8.2 Ereignisse
Wie verhält es sich nun mit Ereignissen? Nun, das ist nicht besonders
schwierig: den AWT- und Swing-Komponenten werden auf die übliche
Art und Weise Listener zugeordnet, die auf die jeweiligen Ereignisse
reagieren. Vorsicht ist allerdings geboten, wenn man aus einer solchen
Ereignisverarbeitung auf SWT-Ressourcen zugreifen will. SWT und
AWT laufen in verschiedenen Threads ab. Deshalb müssen diese
Zugriffe, wie bereits in Abschnitt 8.5.2 diskutiert, in ein Runnable
gekapselt und mit Hilfe der Display-Methode syncExec(), asyncExec()
oder timerExec() ausgeführt werden.
Und umgekehrt, beim Zugriff von der SWT-Ereignisverarbeitung
auf AWT- bzw. Swing-Komponenten, funktioniert’s ganz ähnlich: auch
hier wird die eigentliche Verarbeitung in ein Runnable gekapselt und
dann mit Hilfe der AWT-Methode EventQueue.invokeLater() ausge-
führt. Das wird vom AWT zwar nicht erzwungen (wie es das SWT
macht), wird aber dringend empfohlen.
Im folgenden Beispiel zeigen wir diese Techniken im Zusammen-
hang. Dieses Beispiel zeigt auch, wie man SWT-GUI-Elemente auf eine
AWT-Oberfläche platzieren kann (in einer eigenen Shell). Das Beispiel
implementiert einen Java2D-Canvas innerhalb einer SWT-Shell. Eine in
SWT implementierte Taste erlaubt es, den Canvas zu löschen. Klickt
man auf den Canvas, erscheint ein in SWT implementiertes Texteinga-
befeld auf dem Canvas. Mit einem weiteren Klick verschwindet dieses
Feld wieder, und der eingegebene Text wird auf den Canvas geschrieben.
8.8 Ein Widget mit Swing 205
import java.util.ArrayList;
import java.util.Iterator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
Dann wird mit Hilfe der Klasse SWT_AWT ein AWT-Frame im SWT-
Composite erzeugt. Auf die übliche Art wird diesem Frame dann ein
AWT-Canvas hinzugefügt. Vom Canvas holen wir uns den grafischen
Kontext, auf dem wir später die Zeichenoperationen durchführen.
Außerdem sichern wir uns die anfängliche affine Transformation die-
ses grafischen Kontexts, um später nach Rotationsoperationen den
206 8 Das SWT
Bei einem Mausklick auf den Canvas (hier befinden wir uns also in der
AWT-Ereignisverarbeitung) wird das Texteingabefeld wechselweise
sichtbar oder unsichtbar geschaltet. Nur bei der allerersten Benutzung
wird dieser kleine Editor neu erzeugt. Da AWT-Canvassen natürlich
keine SWT-Widgets zugefügt werden können, erzeugen wir diesen Edi-
tor in einer eigenen Shell. Wichtig ist, diese Shell nicht-modal anzule-
8.8 Ein Widget mit Swing 207
gen, so dass auch nach Öffnen der Shell der Canvas für Mausklicks
zugänglich bleibt.
Die Technik, diese Shell mit setVisible() abwechselnd sichtbar
und unsichtbar zu schalten, ist einem wechselweisen Neuanlegen und
Schließen vorzuziehen. Nicht nur werden damit Ressourcen geschont,
auch werden damit unschöne Effekte vermieden. Nach einem close()
würden Teile der close()-Ereignisverarbeitung erst nach Ablauf der
AWT-Ereignisverarbeitung (also auch nach canvasComp.redraw()) aus-
geführt, so dass an der Position des Editors ein hässlicher weißer Fleck
zurückbleiben würde. Das ist bei Verwendung der Methode set-
Visible(false) nicht der Fall.
// Mausklicks auf dem Canvas verarbeiten
canvas
.addMouseListener(new java.awt.event.MouseListener() {
public void mouseClicked(
java.awt.event.MouseEvent e) {}
size.y);
// Shell öffnen
eShell.open();
} else if (!eShell.isVisible()) {
// Editor versteckt, sichtbar machen
eShell.setVisible(true);
} else {
// Editor sichtbar - Text holen
String t = eText.getText();
// und Editor unsichtbar machen
eShell.setVisible(false);
// Text zur Liste zufügen und Canvas neu zeichnen.
wordList.add(t);
canvasComp.redraw();
}
}
});
}
Schließlich zeigen wir noch die Routine für das Zeichnen des Canvas-
Inhaltes. Dies geschieht in einem PaintListener, der dem SWT-Contai-
ner des Canvas zugeordnet ist. Wir setzen Java2D-Textrotation ein,
um den eingegebenen Text sternförmig anzuordnen. Da es sich bei den
verwendeten Ressourcen (Color, Font) um AWT-Ressourcen handelt,
müssen diese auch nicht wie im SWT nach der Verwendung mit dis-
pose() entsorgt werden. Die Java Garbage Collection wird sich schon
darum kümmern.
// Den Canvas neu zeichnen
canvasComp.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
// Die Verarbeitung der AWT-Event-Warteschlange übergeben
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// Canvasmittelpunkt
java.awt.Rectangle bounds = canvas.getBounds();
int originX = bounds.width / 2;
int originY = bounds.height / 2;
// Canvas rücksetzen
g2d.setTransform(origTransform);
g2d.setColor(java.awt.Color.WHITE);
g2d.fillRect(0, 0, bounds.width, bounds.height);
8.8 Ein Widget mit Swing 209
// Font setzen
g2d.setFont(new java.awt.Font("Myriad",
java.awt.Font.PLAIN, 32));
double angle = 0d;
// Sternförmige Anordnung vorbereiten
double increment = Math.toRadians(30);
Iterator iter = wordList.iterator();
while (iter.hasNext()) {
// Textfarben im RGB-Farbkreis bestimmen
float red = (float) (0.5 + 0.5 * Math
.sin(angle));
float green = (float) (0.5 + 0.5 * Math
.sin(angle + Math.toRadians(120)));
float blue = (float) (0.5 + 0.5 * Math
.sin(angle + Math.toRadians(240)));
g2d.setColor(new java.awt.Color(red, green,
blue));
// Text zeichnen
String text = (String) iter.next();
g2d.drawString(text, originX + 50, originY);
// Rotation für die nächste Textausgabe
g2d.rotate(increment, originX, originY);
angle += increment;
}
}
});
}
});
// Shell fertigstellen und öffnen
shell.pack();
shell.open();
// SWT-Ereignisschleife
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
Abb. 8–11 Textrotation und geglättete Kanten im SWT? Mit eingebettetem Java2D-
Canvas kein Problem.
Diese Routine zeigt allerdings nur den einfachsten Fall. Die Logik wird
komplizierter, wenn man PrinterData-Angaben wie die Anzahl der
Kopien, kollationierten Ausdruck oder Seitenbereiche berücksichtigen
will. Auch ist es angeraten, sich vom Printer-Objekt mit Hilfe der
Methode getDPI() die Druckauflösung zu holen und die grafischen
Operationen entsprechend zu transformieren.
8.10 Datentransfer
Der SWT-Datentransfer umfasst sowohl den Austausch von Daten
über die Zwischenablage als auch die Drag&Drop-Operationen mit
der Maus. Die Klassen des Datentransfers befinden sich in Package
org.eclipse.swt.dnd.
8.10.2 Drag&Drop
8.11 Ressourcenverwaltung
Im Verlaufe dieses Kapitels haben wir verschiedene Ressourcen kennen
gelernt, die, wenn nicht mehr benötigt, mit dispose() wieder freigege-
ben werden müssen. Dazu gehören insbesondere Instanzen der Klassen
Color, Font, Image, GC, Cursor, Printer, Display, Shell und Clipboard.
Bei allen diesen Ressourcen gilt der Grundsatz:
Haben Sie etwas selbst erzeugt, müssen Sie es auch selbst entsorgen.
Haben Sie dagegen eine Ressource von anderswo bezogen (z.B. mit get-
SystemColor()), so dürfen Sie diese Ressource nicht selbst entsorgen.
/**
* Method getColor.
* @param name some Color name
* @param device Device instance
* @param r red-value
* @param g green-value
* @param b blue-value
* @return Color requested color
*/
public static Color getColor(String name,Device device,
int r, int g, int b) {
Object obj = store.get(name);
if (obj == null) {
Color newColor = new Color(device,r,g,b);
store.put(name,newColor);
return newColor;
}
return (Color) obj;
}
/**
* Method dispose.
*/
public static void dispose() {
Iterator iter = store.values().iterator();
while (iter.hasNext()) {
Color color = (Color) iter.next();
color.dispose();
}
}
}
Von der Implementierung eines Plugin und dem Testen dieses Plugin in
der Workbench bis zur Auslieferung des Plugin als fertiges Produkt ist
noch ein gutes Stück Wegs zu gehen. Eclipse bietet hier einige Unter-
stützung an, doch manche Schritte erfordern manuelles Eingreifen.
Zunächst sollten wir uns darüber klar werden, wie wir ein Produkt
am besten segmentieren. Hier bietet Eclipse drei verschiedene Kon-
strukte an: Features, Plugins und Fragmente. Bei allen diesen drei Kon-
strukten setzt Eclipse für die Erstellung der Auslieferungsbibliotheken
das Assemblierungswerkzeug ANT ein. ANT wird durch Skripte
gesteuert, die von Eclipse automatisch erstellt werden – manuelle
Modifikationen sind jedoch möglich.
Feature
Feature
12.1 ANT-Einbindung
ANT ist ein Projekt von Apache.org (ant.apache.org). Es besitzt eine
ähnliche Funktionalität wie das MAKE-Werkzeug oder andere Werk-
zeuge, die Auslieferungsdateien skriptgesteuert aus Entwicklungsarte-
fakten zusammenstellen können. Der große Unterschied von ANT zu
MAKE & Co ist, dass ANT keine Betriebssystemkommandos verwen-
det, sondern alle Aktionen mit Hilfe von Java-Klassen ausführt. Die
Steuerung erfolgt durch ein XML-Skript. Das hat den Vorteil, dass
ANT-Skripte (wie auch das ANT-System selbst) plattformunabhängig
sind.
In Eclipse ist ANT bereits eingebunden. Um ein ANT-Skript aus-
zuführen, selektiert man die Skript-Datei (muss die Dateierweiterung
.xml haben) und führt ANT entweder über die Kontextfunktion Run
Ant ... oder über die Menüfunktion Run>External Tools>Run As>Ant
Build aus.
Konfiguration
Abb. 12–2 Der Dialog für die Konfiguration externer Werkzeuge. Je nach Werkzeug –
hier ist ANT gewählt – können verschiedene Konfigurationen mit verschiedenen Ablauf-
parametern erstellt werden.
Abb. 12–3 Der Ant-Editor. Mit Strg+Leertaste kann der Inhaltsassistent aufgerufen wer-
den.
12.2 Plugins und Fragmente 421
12.3 Features
Feature-Beschreibungen begleiten die Auslieferung von einem oder
mehreren Plugins, die eine abgeschlossene Funktionsgruppe bilden. So
422 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
ist z.B. das Eclipse Java-IDE ein Feature, besteht aber aus mehreren
Plugins. Die Feature-Beschreibung fügt der Auslieferung außerdem
Copyright-Informationen und Lizenzbedingungen hinzu.
Auch Features werden als gesonderte Feature-Projekte angelegt.
Dies geschieht ebenfalls mit Hilfe des New-Wizards. Der Wizard führt
dann durch die Spezifikation des Projektnamens, des Feature-Namens,
der Identifikation des Features, der Version und des Feature Providers.
Abschließend werden die Plugins angekreuzt, die zu dem Feature gehö-
ren. Der Wizard erzeugt aus diesen Angaben eine Datei feature.xml,
die anschließend im Feature-Editor geöffnet wird. Hier können weitere
Angaben gemacht werden. Die Identifikation des Features sollte mit
der Identifikation des hauptsächlichen Plugin übereinstimmen.
Auf der Overview-Seite treffen wir auf die Angaben, die wir schon
beim Neuanlegen des Features gemacht haben. Zusätzlich können wir
zwei unterschiedliche URL-Arten angeben. Eine Discovery URL kann
die Adresse einer Website sein, auf der weitere Produkte oder techni-
sche Unterstützung angeboten werden können. Eine Update URL spe-
zifiziert die Adresse, unter welcher der Update Manager (siehe
Abschnitt 12.5) neue Versionen dieses Features finden kann.
Die Checkbox Primary Feature wird angekreuzt, wenn das Fea-
ture nicht als Ergänzung zu Eclipse gedacht ist, sondern zusammen mit
der Eclipse-Plattform ein eigenständiges Produkt bildet. Durch
12.3 Features 423
Abb. 12–5 Die Seite Content im Feature-Editor zeigt links die im Feature enthaltenen
Plugins. Rechts werden die Eclipse-Plugins angegeben, die für einen Ablauf erforderlich
sind. Es ist nicht nötig, diese Liste manuell aufzubauen – ein Druck auf die Compute-Taste
genügt.
424 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
12.4 Deployment
Normalerweise wird ein Produkt in einem der folgenden Formate aus-
geliefert:
! Als eine Erweiterung für existierende Plattformen. Solche Erweite-
rungen werden üblicherweise in Form eines Features ausgeliefert.
Die Auslieferung einzelner Features wird in Abschnitt 12.4.1 dis-
kutiert. Die Erstellung einer Update-Site (d.h. einer Kollektion
installierbarer Features) wird in Abschnitt 12.4.5 diskutiert.
! Als ein komplettes Produkt, welches die Eclipse-Laufzeitumgebung
mit einschließt. Das wird in Abschnitt 12.4.2 diskutiert.
In beiden Fällen kann man entweder nur die Binärdateien ausliefern,
oder aber die Quelldateien mit einschließen, z.B. wenn man ein SDK
(Software Developers Kit) ausliefern will.
passenden Dateinamen für das Skript. Später können Sie die Export-
operation leicht wiederholen (sogar außerhalb von Eclipse), indem Sie
dieses Ant-Skript ausführen.
Die Frage ist allerdings: Wie bestimmt man, was in das Ausliefe- Build-Konfiguration
rungsarchiv hineinkommt? Das ist recht einfach, denn das Generieren
von Ant-Skripten – und somit der Inhalt des Auslieferungsarchivs –
lässt sich durch Einträge in der Datei build.properties kontrollieren.
Solche Dateien existieren für das Feature-Projekt, für alle Plugin-Pro-
jekte und auch für alle Fragment-Projekte.
Eclipse stellt hierzu die Build-Seite im Manifest-Editor zur Ver-
fügung, der sich mit einem Doppelklick auf die Datei plugin.xml auf-
rufen lässt. Hier können auf einfache Art und Weise Java-Bibliotheken
(JAR-Dateien) und andere Dateien und Ordner zum Build-Prozess hin-
zugefügt werden. Dabei kann sogar zwischen einem binären Build und
einem Build für Archive mit Quelldateien unterschieden werden. In
Abschnitt 13.13.2 sehen Sie ein Beispiel.
Falls Sie lieber mit einem hausgemachten Ant-Skript arbeiten wol-
len, sollten Sie auf der Build-Seite die Option Custom Build ankreu-
zen. Damit wird verhindert, dass Eclipse das Ant-Skript beim Aufruf
der Export ...-Funktion überschreibt.
Nun, was gehört in ein Auslieferungsarchiv? Binärdateien müssen
Sie nicht markieren, denn die befinden sich bereits in den JAR-Dateien,
die Sie zur Auslieferung hinzugefügt haben. Nicht benötigt werden
außerdem Meta-Dateien wie .project, .classpath, .template, build.xml
und build.properties. Benötigt dagegen werden Icons, Hilfeseiten,
Kontrolldateien für die Hilfe wie toc.xml und contexts.xml, Lizenz-
dateien, die in Abschnitt 12.4.3 diskutierten Dateien für die Produkt-
gestaltung (about.ini, ...) usw.
12.4.3 Produktgestaltung
Liefert man das Produkt nun in dieser Form aus, so sieht es nach der
Installation allerdings nicht wie ein eigenständiges Produkt aus, son-
dern wie die Eclipse-Workbench mit installierten Plugins. Um ein wirk-
12.4 Deployment 427
Der Splash-Schirm wird gezeigt, solange die Plattform geladen wird. Das Bild sollte
eine 24-Bit-BMP-Datei sein, mit einer Größe von etwa 500x330 Pixel. Die Datei wird
splash.bmp
in den Plugins org.eclipse.platform und org.eclipse.core.boot verwendet und ist
in den entsprechenden Plugin-Verzeichnissen gespeichert.
428 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import com.bdaum.multimedia.studio.StudioPlugin;
//Konstruktor
public InstallFirstProjectAction() {
}
// run-Methode überschreiben
public void run() {
// Workspace-Instanz holen
IWorkspace workspace = ResourcesPlugin.getWorkspace();
// Workspace-Wurzelverzeichnis holen
IWorkspaceRoot root = workspace.getRoot();
// IProject-Instanz mit angegebenem Namen erzeugen
IProject firstModel = root.getProject(EXAMPLE_PROJECT);
if (!firstModel.exists()) {
try {
// Wenn das Projekt noch nicht existiert,
// wird es erzeugt
firstModel.create(null);
} catch (CoreException e) {
System.err.println(e);
}
}
if (!firstModel.isOpen()) {
try {
// Wenn das Projekt noch nicht geöffnet ist,
// wird es nun geöffnet
firstModel.open(null);
} catch (CoreException e) {
System.err.println(e);
}
}
// Pfad der Workspace-Datei erstellen
IPath path = new Path(EXAMPLE_PROJECT + "/" + EXAMPLE_FILE);
// IFile-Instanz mit angegebenem Pfad erzeugen
IFile mFile = root.getFile(path);
if (!mFile.exists()) {
// Wenn die Datei noch nicht existiert,
// holen wir uns die URL der Beispieldatei im Plugin-Verzeichnis
URL url = StudioPlugin.getDefault().getBundle().
getEntry(EXAMPLE_FILE);
try {
// Eclipse Pseudo-URL auflösen
url = Platform.resolve(url);
// Dateinamen extrahieren
String urls = url.getPath();
430 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
try {
// Datei im Plugin-Verzeichnis
java.io.File input = new java.io.File(urls);
// Neue Datei im Workspace erzeugen und mit
// Inhalt füllen
mFile.create(new FileInputStream(input),
true, null);
} catch (FileNotFoundException e) {
System.err.println(e);
} catch (CoreException e) {
System.err.println(e);
}
} catch (IOException e) {
System.err.println(e);
}
}
}
}
Eine solche Site lässt sich rasch in Form eines neuen Projekts mit
Hilfe des New-Wizard erstellen. Als Projekttyp wählt man Plugin
Development>Update-Site Project. Zunächst muss der Projektname
eingegeben werden. Auf der nächsten Seite können dann noch die
Namen der Plugin- und Feature-Ordner abgeändert werden. Optional
können Sie auch eine Datei namens index.html generieren lassen, wel-
che die auf der Update-Site angebotenen Features anpreist. Nach
einem Druck auf die Finish-Taste wird das Manifest site.xml erzeugt
und der Site-Editor geöffnet. Hier können nun auf den verschiedenen
Seiten des Editors die notwendigen Spezifikationen eingegeben werden.
! Auf der Seite Features können Sie auf der linken Seite die Features
angeben, für die ein Build-Prozess durchgeführt werden soll. Fea-
tures werden hier mit einem Druck auf die Add ...-Taste hinzuge-
fügt. In der dann erscheinenden Liste werden alle Features ange-
kreuzt, die in die Update-Site aufgenommen werden sollen. Auf der
rechten Seite werden die zu veröffentlichenden Features angeord-
net. Mit der Taste New Category ... können Sie Kategorien anle-
gen, um die zu veröffentlichenden Features in Gruppen anzuord-
nen. Unterkategorien werden erzeugt, indem man eine Kategorie
selektiert und dann die Taste New Category ... erneut betätigt. Fea-
tures werden einer Kategorie hinzugefügt, indem man sie von der
linken Seite auf eine Kategorie zieht. Dabei kann ein Feature meh-
reren Kategorien zugeordnet werden. Falls Sie ein Feature nicht auf
eine Kategorie, sondern auf das offene Feld ziehen, wird es dem
Endbenutzer unter der Standardkategorie Other angeboten. Um
432 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
Nachdem man eine Update-Site generiert hat, kann man die darin ent-
haltenen Features mit dem Eclipse Update Manager installieren. Dazu
muss die Update-Site zunächst dem Update Manager bekannt gemacht
werden. Das geschieht mit der Funktion Help>Software Updates>Find
and Install... Auf der ersten Seite des Install/Update Wizards wählt
man zunächst die Option Search for new Features to Install und drückt
dann die Next-Taste. Auf der nächsten Seite werden dann alle Eclipse
bekannten Update-Sites aufgelistet. Mit der Taste Add Update-Site ...
kann die URL einer Update-Site auf dem Web hinzugefügt werden. Mit
der Taste Add Local Site ... kann zu einer auf dem lokalen Computer
befindlichen Update-Site navigiert werden. Schließlich kann man noch
mit der Taste Add Archived Site ... zu einer ZIP- oder JAR-Datei, die
eine Update-Site enthält, navigieren.
nun Schritt für Schritt durch alle diese Sites und präsentiert die darin
angebotenen Features. Wenn Sie ein Feature selektieren, wird im Text-
feld unter der Featureliste die Beschreibung des Features angezeigt. Für
detaillierte Information über ein Feature klicken Sie die Properties ...-
Taste. Um ein oder mehrere Features zu installieren, markieren Sie die
betreffenden Features und drücken dann die Next-Taste. Eclipse führt
Sie nun Schritt für Schritt durch den Installationsprozess. Nach der
Installation werden Sie gefragt, ob die Workbench neu gestartet wer-
den soll. Die Eclipse-Entwickler haben das Ziel, die Installation von
Plugins und Features komplett dynamisch zu gestalten, so dass ein
Neustart der Workbench nach einer Installation entfallen kann. Zur
Zeit wird jedoch immer noch ein Neustart empfohlen.
Falls Sie ein Feature installiert haben, und es erscheint nach einem Problembehebung
Neustart nicht in der Workbench, kann es gut sein, dass Eclipse das
Feature nicht aktiviert hat, weil notwendige Voraussetzungen (benö-
tigte Plugins oder Features) fehlen. Leider gibt es keinen einfachen
Weg, an Information über benötigte Komponenten während der
Installation heranzukommen. Sie müssen in einem solchen Fall deshalb
mit dem Lieferanten des Features abklären, welche anderen Kompo-
nenten für den Betrieb des installierten Features benötigt werden.
Nachdem Sie Features installiert haben, ist es recht einfach auf das
Vorhandensein neuer Versionen zu prüfen und die Features gegebe-
nenfalls zu aktualisieren. Dazu rufen Sie wieder die Funktion
Help>Software Updates>Find and Install... auf und selektieren die
Option Search for updates of the currently installed features. Nach
einem Klick auf die Next-Taste sucht Eclipse alle bekannten Update-
Sites ab und schaut nach neueren Versionen für die installierten Fea-
tures. Falls solche Versionen existieren, werden Sie in einer Liste ange-
zeigt. Sie können dann die jeweiligen Features in der üblichen Weise
markieren und dann installieren.
Sie können die Aktualisierung Ihrer Plattform sogar automatisie-
ren. Unter Window>Preferences>Install/Update>Automatic Updates
können Sie wählen, ob die bekannten Update-Sites bei jedem Platt-
formstart oder regelmäßig nach einem spezifizierten Zeitplan nach
neuen Versionen durchsucht werden sollen.
434 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln
12.5.5 Install-Handler
Unterstützung an. Dieser Wizard erstellt eine Liste aller in einem Pro-
gramm verwendeten String-Konstanten. Anschließend lassen sich die
aufgelisteten String-Konstanten in drei Kategorien einteilen:
! Translate. In diesem Fall wird die String-Konstante in eine Proper-
ties-Datei ausgelagert. Im Quelltext wird die String-Konstante
durch den Aufruf einer Zugriffsroutine ersetzt. Diese holt zum
Ablaufzeitpunkt die String-Konstante anhand des mitgegebenen
Schlüssels aus der Properties-Datei. Diese Programmzeile wird
zusätzlich mit einem Kommentar wie // $NON-NLS-1$ versehen, der
anzeigt, dass bei der nächsten Ausführung der Funktion Externa-
lize Strings die entsprechende String-Konstante (nun der Schlüssel)
nicht untersucht werden soll. So wird z.B. die Anweisung
replaceAction.setText("Replace");
umgesetzt in
replaceAction.setText(
Messages.getString("SpellCorrectionView.Replace_5"));
//$NON-NLS-1$
! Never Translate. Die String-Konstante wird mit einem // $NON-
NLS-...$-Kommentar versehen, damit sie in Zukunft nicht mehr
im Externalize Strings-Dialog erscheint. So wird z.B.
manager.add(new Separator("Additions"));
umgesetzt in
manager.add(new Separator("Additions")); //$NON-NLS-1$
! Skip. Es wird nichts verändert. Die String-Konstante erscheint
auch in Zukunft im Externalize Strings-Dialog.
Der Wizard führt in der Quelldatei die entsprechenden Ersetzungen
aus. Er erzeugt außerdem in der Package eine Properties-Datei, die die
ausgelagerten String-Konstanten enthält, und eine Klasse Messages, die
den Zugriff auf diese Datei mit Hilfe der Methode getString() orga-
nisiert.
Alles, was zu tun bleibt, ist die Properties-Datei in die jeweilige
Landessprache zu übersetzen. Hält man sich dabei an die Java-
Namenskonventionen basename_lang_region_variant.properties, so
wird von der Messages-Klasse automatisch je nach Ablaufplattform die
richtige Properties-Datei verwendet. Man kann also neue Sprachen in
Form neuer Properties-Dateien hinzufügen, ohne eine einzige Java-
Klasse neu kompilieren zu müssen.
In manchen Fällen ist es erforderlich, programmgenerierte Werte
in String-Konstanten einzusetzen. Dies kann mit Hilfe von Parametern
12.6 Produkte internationalisieren 437
Bei Hilfeseiten und Cheat Sheets ist dagegen das oben gezeigte Vorge-
hen kaum angebracht. Hier ist es besser, die Seiten komplett zu über-
setzen. Für jede Sprache wird ein getrennter Ordner angelegt, in den
dann die übersetzten Seiten abgelegt werden.
Die Auswahl des entsprechenden Ordners geschieht über Substitu-
tionsvariable. Eclipse kennt vier solcher Substitutionsvariablen, die
Bibliothekspfade verändern können:
os Wird durch das aktuelle Betriebssystem (linux, macosx, qnx, solaris, win32)
ersetzt.
ws Wird durch das aktuelle Windowing-System (carbon, gtk, motif, photon,
win32) ersetzt.
nl Wird durch das aktuelle Java-Locale ersetzt.
arch Wird durch die aktuelle Prozessorarchitektur (PA_RISC, ppc, sparc, x86)
ersetzt.
Alle diese Variablen lassen sich für das Testen von Plugins in den Ein-
stellungen Window>Preferences>Plugin Development>Target Envi-
ronment setzen.
Spezifiziert man nun z.B. in plugin.xml ein Cheat Sheet (siehe
Abschnitt 11.5.14), so gibt man als Ordnernamen die entsprechende
Substitutionsvariable an:
contentFile="$nl/vedit.xml"
Zum Ablaufzeitpunkt wird die Variable $nl durch das Kürzel der aktu-
ellen Locale ersetzt, also z.B. durch DE_de. Das Cheat Sheet wird dann
unter DE_de/vedit.xml gesucht.
Ganz ähnlich verläuft das bei Hilfeseiten (siehe Abschnitt
11.5.12). Hier würden nicht nur die HTML-Seiten, sondern auch das
Inhaltsverzeichnis toc.xml und die Kontextzuordnung contexts.xml
übersetzt werden, da sie nicht nur Verweise auf Hilfeseiten, sondern
auch selbst einige Textkonstanten enthalten. Verweisen auf toc.xml
und contexts.xml würde dann ein $nl/ vorangestellt. Für die Verweise
innerhalb von toc.xml und contexts.xml ist dies normalerweise nicht
nötig, da diese Verweise relativ zum Speicherort einer solchen Datei
angegeben werden.
Allerdings hat dieses Vorgehen einen gravierenden Nachteil: Bei
einem fehlenden Sprachpaket wird einfach nichts angezeigt, anstatt
auf die englische Version zurückzugreifen. Glücklicherweise gibt es
jedoch eine Alternative, und die kommt ohne Substitutionsvariable
aus. Sie beruht ausschließlich auf einer Namenskonvention für Ver-
12.7 Der Feature-Patch 439
an. Im Wizard geben Sie zunächst den Projektnamen ein und gehen zur
nächsten Seite. Dort definieren Sie die Patch-ID, den Patch-Namen
und den Hersteller des Patch. Dann wählen Sie das Feature aus, das
gepatcht werden soll. Auf der Seite Included Plugins and Fragments
wählen Sie die Plugins und Fragmente aus, die in den Patch gehen sol-
len. Dann drücken Sie Finish. Der Feature-Patch ist nun fertig für die
Auslieferung. Diese verläuft genau wie bei jedem anderen Feature-Pro-
jekt.