Sie sind auf Seite 1von 136

v

Inhaltsverzeichnis

Einführung 1
Was ist Eclipse? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Das Eclipse-Umfeld . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Über dieses Buch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Wie dieses Buch organisiert ist . . . . . . . . . . . . . . . . . . . . . . 4
Danksagung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

Teil I Das Java-IDE 7

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

2 Effektiv programmieren mit Eclipse 35


2.1 Kleine Helfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.1.1 Systeminformationen . . . . . . . . . . . . . . . . . . . . . 35
2.1.2 Help und Hover-Info . . . . . . . . . . . . . . . . . . . . . 36
2.1.3 Java-Informationsfenster . . . . . . . . . . . . . . . . . . 38
vi Inhaltsverzeichnis

2.1.4 Automatische Codevervollständigung . . . . . . . . 38


2.1.5 Der Korrektur-Assistent . . . . . . . . . . . . . . . . . . . 44
2.1.6 QuickAssist . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.1.7 Komfortfunktionen des Java-Editors . . . . . . . . . 46
2.2 Sich im Code zurechtfinden . . . . . . . . . . . . . . . . . . . . . . . 47
2.3 Programme umformen . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.3.1 Änderungen an Typen . . . . . . . . . . . . . . . . . . . . 49
2.3.2 Den Code reorganisieren . . . . . . . . . . . . . . . . . . 50
2.4 Undo und Redo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.5 Local History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.5.1 Ressourcen vergleichen . . . . . . . . . . . . . . . . . . . 55
2.5.2 Durch ältere Version ersetzen . . . . . . . . . . . . . . 56
2.5.3 Gelöschte Ressource wiederherstellen . . . . . . . . 56

3 Die Kunst der (visuellen) Komposition 57


3.1 Einstellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.2 Komposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.3 Bohnen und deren Eigenschaften . . . . . . . . . . . . . . . . . . . 61
3.4 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.5 Ereignisverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

4 Projekte richtig organisieren 65


4.1 Die Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.2 Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.2.1 Ressourcentypen . . . . . . . . . . . . . . . . . . . . . . . . 67
4.2.2 Wo Ressourcen gespeichert werden . . . . . . . . . . 67
4.2.3 Ressourcen synchronisieren . . . . . . . . . . . . . . . . 67
4.2.4 Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.3 Assoziationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.4 Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.4.1 Verzeichnisse und Packages . . . . . . . . . . . . . . . . 70
4.4.2 Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.4.3 Typhierarchie . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5 Der Outline-View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.5.1 Darstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.5.2 Kontextfunktionen . . . . . . . . . . . . . . . . . . . . . . . 74
4.6 Suchen ....................................... 75
4.6.1 Die Suchfunktion . . . . . . . . . . . . . . . . . . . . . . . . 76
4.6.2 Finden und Ersetzen . . . . . . . . . . . . . . . . . . . . . . 78
4.6.3 Gleichnamige Elemente markieren . . . . . . . . . . . 79
Inhaltsverzeichnis vii

4.7 Editoren und Views anordnen . . . . . . . . . . . . . . . . . . . . . 79


4.8 Perspektiven verwalten . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.8.1 Neue Perspektiven definieren . . . . . . . . . . . . . . 81
4.8.2 Perspektiven konfigurieren . . . . . . . . . . . . . . . . 83
4.9 Dateien importieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.10 Projekteigenschaften . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
4.11 Die Java Browsing-Perspektive . . . . . . . . . . . . . . . . . . . . 88

5 Projekt 1: Duke spricht 89


5.1 Einrichten des Projekts . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.2 Kurzer Exkurs in die Sprachsynthese . . . . . . . . . . . . . . . . 90
5.3 Erweiterung des FreeTTS-Systems . . . . . . . . . . . . . . . . . . 92
5.3.1 Animationsereignisse . . . . . . . . . . . . . . . . . . . . . 92
5.3.2 Der Animator . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.3.3 Einbau in FreeTTS . . . . . . . . . . . . . . . . . . . . . . 97
5.3.4 Verbindung mit dem Java-Audiosystem . . . . . 100
5.4 Die Benutzeroberfläche . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.4.1 Das animierte Gesicht . . . . . . . . . . . . . . . . . . . 102
5.4.2 Die Bedieneinheit . . . . . . . . . . . . . . . . . . . . . . 103
5.4.3 Das Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5.4.4 Die Präsentation . . . . . . . . . . . . . . . . . . . . . . . 104
5.4.5 Die komplette Applikation . . . . . . . . . . . . . . . 110
5.5 Die Applikation aus Eclipse exportieren . . . . . . . . . . . . 113
5.6 Vertiefende Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5.7 Was konnten wir lernen? . . . . . . . . . . . . . . . . . . . . . . . . 114

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

7 Weiterführende Themen der Projektentwicklung 135


7.1 Entwicklung im Team . . . . . . . . . . . . . . . . . . . . . . . . . . 135
7.1.1 Repositorium einrichten . . . . . . . . . . . . . . . . . . 136
7.1.2 Projekte im Repositorium . . . . . . . . . . . . . . . . 138
7.1.3 Versionsverwaltung . . . . . . . . . . . . . . . . . . . . . 140
7.1.4 Arbeiten im Team . . . . . . . . . . . . . . . . . . . . . . 140
7.1.5 Weitere Funktionen . . . . . . . . . . . . . . . . . . . . . 142
7.2 Externe Werkzeuge . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

Teil II SWT statt Swing 145

8 Das SWT 147


8.1 Übersicht über die SWT-Funktionsgruppen . . . . . . . . . . 147
8.2 Vor- und Nachteile des SWT . . . . . . . . . . . . . . . . . . . . . 148
8.2.1 Vorteile des SWT . . . . . . . . . . . . . . . . . . . . . . . 148
8.2.2 Nachteile des SWT . . . . . . . . . . . . . . . . . . . . . . 149
8.3 Das Package SWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8.4 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8.4.1 Zuhörer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8.4.2 Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.4.3 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.4.4 Übersicht über Listener-, Adapter- und
Event-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.5 Widgets ..................................... 154
8.5.1 Visuelle Übersicht . . . . . . . . . . . . . . . . . . . . . . 155
8.5.2 Displays, Shells und Monitore . . . . . . . . . . . . . 157
8.5.3 Dialoge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
8.5.4 Composites, Groups und Canvas . . . . . . . . . . . 165
8.5.5 Tasten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
8.5.6 Schieberegler, Skalen und Fortschrittsbalken . . 168
8.5.7 Textfelder und Beschriftungen . . . . . . . . . . . . . 169
8.5.8 Tabellen, Listen und Combos . . . . . . . . . . . . . 171
8.5.9 Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
8.5.10 Verschiebbare Trennleisten . . . . . . . . . . . . . . . 178
8.5.11 Pultordner . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
8.5.12 Werkzeugleisten und Menüs . . . . . . . . . . . . . . 180
8.5.13 Spezielle Widgets . . . . . . . . . . . . . . . . . . . . . . . 186
8.5.14 Das Browser-Widget . . . . . . . . . . . . . . . . . . . . 188
8.6 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
8.6.1 Visuelle Übersicht . . . . . . . . . . . . . . . . . . . . . . 190
8.6.2 Die Klasse FillLayout . . . . . . . . . . . . . . . . . . . 190
Inhaltsverzeichnis ix

8.6.3 Die Klasse RowLayout . . . . . . . . . . . . . . . . . . . . 191


8.6.4 Die Klasse GridLayout . . . . . . . . . . . . . . . . . . . 192
8.6.5 Die Klasse FormLayout . . . . . . . . . . . . . . . . . . . 194
8.6.6 Die Klasse StackLayout . . . . . . . . . . . . . . . . . . 196
8.7 Grafik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.7.1 Der Grafikkontext . . . . . . . . . . . . . . . . . . . . . 197
8.7.2 Farben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.7.3 Schriftarten . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
8.7.4 Bilder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
8.7.5 Der Mauszeiger . . . . . . . . . . . . . . . . . . . . . . . . 202
8.8 Ein Widget mit Swing . . . . . . . . . . . . . . . . . . . . . . . . . . 203
8.8.1 Eingebettete Inhalte . . . . . . . . . . . . . . . . . . . . . 204
8.8.2 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
8.9 Ausgabe auf Drucker . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.10 Datentransfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
8.10.1 Die Zwischenablage . . . . . . . . . . . . . . . . . . . . 212
8.10.2 Drag&Drop . . . . . . . . . . . . . . . . . . . . . . . . . . 213
8.11 Ressourcenverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . . 215
8.12 Windows32-Unterstützung (OLE) . . . . . . . . . . . . . . . . . 217
8.13 SWT auf dem Pocket PC . . . . . . . . . . . . . . . . . . . . . . . . 217
8.14 Behindertengerechte Software . . . . . . . . . . . . . . . . . . . . 218

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

9.4.3 Der ProjectionViewer . . . . . . . . . . . . . . . . . . . . 243


9.4.4 Mehr Komfort für Textfelder und Combos . . . 244
9.5 Aktionen und Menüs . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
9.5.1 Das IAction-Interface . . . . . . . . . . . . . . . . . . . 244
9.5.2 Die Manager . . . . . . . . . . . . . . . . . . . . . . . . . . 245
9.6 Wizards ..................................... 246
9.6.1 Die Klasse Wizard . . . . . . . . . . . . . . . . . . . . . . . 247
9.6.2 Die Klasse WizardPage . . . . . . . . . . . . . . . . . . . 248
9.6.3 Die Klasse WizardSelectionPage . . . . . . . . . . . . 248
9.6.4 Die Klasse WizardDialog . . . . . . . . . . . . . . . . . 249
9.7 Präferenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
9.7.1 Die Klassen PreferenceStore und PreferenceConver-
ter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
9.7.2 Die Klasse PreferencePage . . . . . . . . . . . . . . . . 251
9.7.3 Feldeditoren . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
9.7.4 Präferenzenbäume . . . . . . . . . . . . . . . . . . . . . . 253

10 Projekt 2: Jukebox 255


10.1 Designziele und Umsetzung . . . . . . . . . . . . . . . . . . . . . . 255
10.2 Einrichten des Projekts . . . . . . . . . . . . . . . . . . . . . . . . . . 258
10.3 Das Player-Modul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
10.4 Das Datenmodell der Playlist . . . . . . . . . . . . . . . . . . . . . 281
10.5 Das Fenster für die Anzeige der Beschreibung . . . . . . . . 283
10.6 Der Playlist-Viewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
10.7 Der Editor für Beschreibungen . . . . . . . . . . . . . . . . . . . . 311
10.8 Die Jukebox zum Einsatz bringen . . . . . . . . . . . . . . . . . 325
10.9 Diskussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325

Teil III Eclipse als Ablaufplattform 327

11 Plugins für die Eclipse-Workbench entwickeln 329


11.1 Die Architektur der Eclipse-Plattform . . . . . . . . . . . . . . 330
11.1.1 OSGi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
11.1.2 Minimalplattform . . . . . . . . . . . . . . . . . . . . . . 331
11.1.3 Rich Client Platform vs. IDE . . . . . . . . . . . . . . 331
11.1.4 Ressourcenverwaltung . . . . . . . . . . . . . . . . . . . 332
11.1.5 Benutzeroberfläche . . . . . . . . . . . . . . . . . . . . . 332
11.1.6 Hilfesystem . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
11.1.7 Unterstützung für Arbeitsgruppen . . . . . . . . . . 333
11.1.8 Weitere Plugin-Gruppen . . . . . . . . . . . . . . . . . 334
11.1.9 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . 334
Inhaltsverzeichnis xi

11.2 Die Kernklassen der Eclipse-Plattform . . . . . . . . . . . . . . 335


11.2.1 Die Klasse Platform . . . . . . . . . . . . . . . . . . . . 335
11.2.2 Die Klasse Plugin . . . . . . . . . . . . . . . . . . . . . . 335
11.2.3 Die Klasse Preferences . . . . . . . . . . . . . . . . . . 336
11.2.4 Pfadangaben . . . . . . . . . . . . . . . . . . . . . . . . . . 337
11.2.5 Überwachung lang laufender Prozesse . . . . . . . 337
11.3 Der Eclipse-Workspace . . . . . . . . . . . . . . . . . . . . . . . . . 338
11.3.1 Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
11.3.2 Markierungen . . . . . . . . . . . . . . . . . . . . . . . . . 343
11.3.3 Auf Ressourcenänderungen reagieren . . . . . . . 344
11.3.4 Lang laufende Prozesse verwalten . . . . . . . . . . 346
11.4 Plugins konfigurieren . . . . . . . . . . . . . . . . . . . . . . . . . . 347
11.4.1 Die Plugin Development-Perspektive . . . . . . . . 347
11.4.2 Das Plugin-Manifest . . . . . . . . . . . . . . . . . . . . 350
11.4.3 Die wichtigsten Erweiterungspunkte im
Eclipse SDK . . . . . . . . . . . . . . . . . . . . . . . . . . 354
11.4.4 Der Schema-Editor . . . . . . . . . . . . . . . . . . . . . 363
11.5 Die Benutzeroberfläche der Eclipse-Plattform . . . . . . . . 366
11.5.1 Formulare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
11.5.2 Die Eclipse-Workbench . . . . . . . . . . . . . . . . . . 374
11.5.3 Die Architektur der Workbench . . . . . . . . . . . 375
11.5.4 Ereignisverarbeitung in der Workbench . . . . . 377
11.5.5 Editoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
11.5.6 Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
11.5.7 Aktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
11.5.8 Dialoge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
11.5.9 Workbench-Wizards . . . . . . . . . . . . . . . . . . . . 403
11.5.10 Präferenzen und PropertyPages . . . . . . . . . . . . 406
11.5.11 Perspektiven definieren . . . . . . . . . . . . . . . . . . 407
11.5.12 Das Hilfesystem . . . . . . . . . . . . . . . . . . . . . . . 409
11.5.13 Cheat Sheets . . . . . . . . . . . . . . . . . . . . . . . . . . 414

12 Eigene Produkte auf der Grundlage von Eclipse entwickeln 417


12.1 ANT-Einbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
12.2 Plugins und Fragmente . . . . . . . . . . . . . . . . . . . . . . . . . 421
12.3 Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
12.4 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
12.4.1 Features ausliefern . . . . . . . . . . . . . . . . . . . . . 424
12.4.2 Eigenständige Produkte ausliefern . . . . . . . . . . 425
12.4.3 Produktgestaltung . . . . . . . . . . . . . . . . . . . . . . 426
xii Inhaltsverzeichnis

12.4.4 Den Workspace bevölkern . . . . . . . . . . . . . . . . 428


12.4.5 Update-Sites erstellen . . . . . . . . . . . . . . . . . . . . 430
12.5 Von einer Update-Site installieren . . . . . . . . . . . . . . . . . 432
12.5.1 Update-Site hinzufügen . . . . . . . . . . . . . . . . . . 432
12.5.2 Features installieren . . . . . . . . . . . . . . . . . . . . . 432
12.5.3 Features aktualisieren . . . . . . . . . . . . . . . . . . . 433
12.5.4 Die Eclipse-Konfiguration verwalten . . . . . . . . 434
12.5.5 Install-Handler . . . . . . . . . . . . . . . . . . . . . . . . . 434
12.6 Produkte internationalisieren . . . . . . . . . . . . . . . . . . . . . 435
12.6.1 Textkonstanten in Programmen . . . . . . . . . . . . 435
12.6.2 Textkonstanten in Manifest-Dateien . . . . . . . . 437
12.6.3 Hilfetexte und Spickzettel . . . . . . . . . . . . . . . . 438
12.6.4 Sprachpakete ausliefern . . . . . . . . . . . . . . . . . . 439
12.7 Der Feature-Patch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439

13 Projekt 3: Rechtschreibprüfung als Eclipse-Plugin 441


13.1 Die Kernklassen der Rechtschreibprüfung . . . . . . . . . . . 442
13.2 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
13.2.1 Die Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
13.2.2 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
13.3 Das Projekt einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . 444
13.4 Die Plugin-Konfiguration . . . . . . . . . . . . . . . . . . . . . . . . 447
13.4.1 Das Manifest plugin.xml . . . . . . . . . . . . . . . . . 448
13.4.2 Das Schema documentTokenizer.exsd . . . . . . . . 454
13.4.3 Importierte Dateien . . . . . . . . . . . . . . . . . . . . . 455
13.5 Die Plugin-Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
13.6 Die Aktion »Check Spelling« . . . . . . . . . . . . . . . . . . . . . 462
13.6.1 Die Klasse SpellCheckingTarget . . . . . . . . . . . . 462
13.6.2 Die Klasse CheckSpellingActionDelegate . . . . . 469
13.7 Das Korrekturfenster . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
13.7.1 Die Klasse SpellCorrectionView . . . . . . . . . . . . 479
13.7.2 Aktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
13.7.3 Bilder verwalten . . . . . . . . . . . . . . . . . . . . . . . . 493
13.8 Das Zusammenspiel zwischen den Kernklassen und
den GUI-Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
13.9 Dokumente analysieren . . . . . . . . . . . . . . . . . . . . . . . . . 508
13.10 Die Rechtschreibprüfung konfigurieren . . . . . . . . . . . . . 508
13.10.1 Präferenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
13.10.2 Das GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
13.10.3 Den PreferenceStore auslesen . . . . . . . . . . . . . 517
Inhaltsverzeichnis xiii

13.11 Das Hilfesystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518


13.11.1 Das Hilfe-Inhaltsverzeichnis . . . . . . . . . . . . . . 518
13.11.2 Kontextabhängige Hilfe . . . . . . . . . . . . . . . . . 519
13.11.3 Aktive Hilfe . . . . . . . . . . . . . . . . . . . . . . . . . . 520
13.12 Ein Plugin für Java-Properties . . . . . . . . . . . . . . . . . . . . 523
13.12.1 Das Projekt einrichten . . . . . . . . . . . . . . . . . . . 524
13.12.2 Das Manifest . . . . . . . . . . . . . . . . . . . . . . . . . . 524
13.12.3 Die Plugin-Klasse . . . . . . . . . . . . . . . . . . . . . . 527
13.12.4 Die Präferenzen . . . . . . . . . . . . . . . . . . . . . . . . 528
13.12.5 Die PreferencePage . . . . . . . . . . . . . . . . . . . . . 529
13.12.6 Der Java-Properties-Tokenizer . . . . . . . . . . . . 530
13.12.7 Das Hilfesystem . . . . . . . . . . . . . . . . . . . . . . . 531
13.13 Die Rechtschreibprüfung internationalisieren . . . . . . . . 531
13.13.1 Textkonstanten in Java-Code . . . . . . . . . . . . . 531
13.13.2 Textkonstanten in Manifest-Dateien . . . . . . . . 534
13.13.3 Ein Sprachfragment erstellen . . . . . . . . . . . . . . 535
13.14 Den Spell Checker zum Einsatz bringen . . . . . . . . . . . . 539
13.14.1 Definition des Spell-Checker-Features . . . . . . . 539
13.14.2 Das Sprach-Feature erstellen . . . . . . . . . . . . . . 544
13.14.3 Definition der Update-Site . . . . . . . . . . . . . . . . 547
13.14.4 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
13.15 Erfahrungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549

14 Die Rich Client Platform 551


14.1 Motivation und Abgrenzung . . . . . . . . . . . . . . . . . . . . . 551
14.2 Plugins und die RCP . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
14.3 Erstellen einer Applikation . . . . . . . . . . . . . . . . . . . . . . 553
14.3.1 Das Interface IPlatformRunnable . . . . . . . . . . . 553
14.3.2 Die Klasse WorkbenchAdvisor . . . . . . . . . . . . 554
14.4 Testen einer Rich-Client-Applikation . . . . . . . . . . . . . . 558
14.5 Ausliefern einer Rich-Client-Applikation . . . . . . . . . . . . 559
14.6 Fortgeschrittene Produktgestaltung . . . . . . . . . . . . . . . . 560
14.7 Die globale Willkommensseite . . . . . . . . . . . . . . . . . . . . 560

15 Projekt 4: Das Spiel Hex als RCP-Applikation 561


15.1 Übersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
15.2 Das Projekt einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . 563
xiv Inhaltsverzeichnis

15.3 Das Manifest plugin.xml . . . . . . . . . . . . . . . . . . . . . . . . 563


15.3.1 Benötigte Eclipse-Plugins . . . . . . . . . . . . . . . . . 564
15.3.2 Applikation deklarieren . . . . . . . . . . . . . . . . . . 565
15.3.3 Perspektive anlegen . . . . . . . . . . . . . . . . . . . . . 565
15.3.4 View anlegen . . . . . . . . . . . . . . . . . . . . . . . . . . 566
15.3.5 Produktgestaltung . . . . . . . . . . . . . . . . . . . . . . 566
15.3.6 Willkommensschirm einbinden . . . . . . . . . . . . 566
15.3.7 Hilfe anbinden . . . . . . . . . . . . . . . . . . . . . . . . . 567
15.3.8 Das komplette Manifest . . . . . . . . . . . . . . . . . . 567
15.4 Die Klasse RcpApplication . . . . . . . . . . . . . . . . . . . . . . . 569
15.5 Die Klasse RcpWorkbenchAdvisor . . . . . . . . . . . . . . . . . . . 569
15.6 Die Klasse RcpPerspective . . . . . . . . . . . . . . . . . . . . . . . 571
15.7 Die Interfaces IGame und IStatusListener . . . . . . . . . . . 571
15.7.1 Das Interface IStatusListener . . . . . . . . . . . . . 571
15.7.2 Das Interface IGame . . . . . . . . . . . . . . . . . . . . . 572
15.8 Die Klasse HexView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
15.9 Die Game-Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
15.10 Der Willkommensschirm . . . . . . . . . . . . . . . . . . . . . . . . 585
15.11 Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
15.12 Auslieferung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
15.13 Diskussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589

16 Schlussfolgerungen und Ausblick 591


16.1 Wie programmieren? . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
16.1.1 Lauffähige Prototypen . . . . . . . . . . . . . . . . . . . 592
16.1.2 Automatisierte Tests . . . . . . . . . . . . . . . . . . . . 593
16.1.3 Verfeinerungen . . . . . . . . . . . . . . . . . . . . . . . . 593
16.1.4 »Embrace Change« . . . . . . . . . . . . . . . . . . . . . 595
16.1.5 Weg des geringsten Widerstandes . . . . . . . . . . 596
16.2 Java 1.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
16.3 Schlusswort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
Anhang
A Nützliche Plugins für Eclipse 601
B Verhalten bei einem Eclipse-Versionswechsel 605
C Wichtige Downloads 609
Bibliographie 611
Stichwortverzeichnis 613
1

Einführung

Was ist Eclipse?


Die erste Version von Eclipse wurde im November 2001 freigegeben.
Angekündigt wurde Eclipse von IBM als ein 40-Millionen-Dollar-
Geschenk an die Open-Source-Gemeinde. Die ersten Reaktionen auf
dieses Geschenk waren zwiespältig. Während viele Java-Programmie-
rer das Erscheinen von Eclipse begeistert begrüßten (wann wäre man
über ein 40-Millionen-Dollar-Geschenk nicht begeistert?), waren die
Reaktionen bei Sun Microsystems zunächst recht wütend.
In der Zwischenzeit hat Eclipse die Java-Welt (und nicht nur diese)
im Sturm genommen, während Sun Microsystems immer noch nicht
im Boot ist. Die Entwicklung von Eclipse wird nun komplett von
eclipse.org verwaltet, einer unabhängigen Nonprofit-Organisation in
der IBM allerdings eine einflussreiche Position einnimmt. Trotz der
Tatsache, dass der Mitgliedsbeitrag recht erklecklich ist ($250.000)
und aktive Teilnahme in Form von Bereitstellungen von Mitarbeitern
für die Eclipse-Entwicklung verlangt wird, ist die Zahl der Mitglieder
nicht klein. Das Eclipse-Konsortium besteht aus über 150 Mitgliedern,
und zum Direktorium gehören Mitarbeiter der Firmen Ericsson, Genu-
itec LLC, IBM, Hewlett Packard, Intel, MontaVista Software, QNX
Software Systems Ltd., SAP AG, SAS, Serena Software und der Univer-
sität von Washington. Microsoft (Sie haben es wahrscheinlich schon
erraten) ist nicht mit von der Partie.
Erhebt sich jetzt die Frage, was genau ist Eclipse? Ist es ein Java-
IDE? Ist es ein neues GUI für Java-Applikationen? Ist es eine Ablauf-
plattform?
Laut www.eclipse.org ist Eclipse eine Plattform für »alles Mögli-
che und nichts im Besonderen«. Dass man damit Java-Programme ent-
wickeln kann, ist nur eine spezielle Anwendungsmöglichkeit dieser
Plattform. Doch das eigentliche Anwendungsfeld von Eclipse geht weit
2 Einführung

über diese Anwendung hinaus. Dank seiner Plugin-Architektur ist


Eclipse wandlungsfähig wie ein Chamäleon und kann in den verschie-
densten Szenarien ein Habitat finden. Das Eclipse Java-IDE ist nur ein
herausragendes Beispiel für ein Eclipse-Plugin. Eine große Anzahl von
Plugins ist bereits von verschiedenen Firmen und Entwicklern für
Eclipse entwickelt worden oder befindet sich in der Entwicklung
(Anhang A enthält eine kleine Auswahl solcher Entwicklungen). In
diesem Buch werden wir uns freilich auf die Java-Entwicklung mit
Eclipse beschränken. So gibt es beispielsweise bereits ein Plugin für die
C++-Entwicklung. Weitere Plugins für andere Programmiersprachen
wie etwa RPG oder COBOL sind in Vorbereitung.
Eclipse ist allerdings mehr als eine reine Entwicklungsumgebung.
So stellt es mit seinen SWT- und JFace-Bibliotheken Alternativen zu
Suns Laufzeit-Java-Bibliotheken AWT und Swing zur Verfügung.
Diese Bibliotheken gestatten es, Java-Anwendungen zu erstellen, die in
puncto Ansprechverhalten und Look&Feel dem Verhalten nativer
(also z.B. in C oder C++ geschriebener) Anwendungen in nichts nach-
stehen. Mit Swing implementierte Benutzeroberflächen verhalten sich
dagegen oft etwas träge und entsprechen – trotz auswechselbarer Skins
– nicht immer den auf dem jeweiligen Betriebssystem vorherrschenden
Standards. SWT und JFace könnten für Java den Durchbruch bei
Desktop-Applikationen bewirken. Es wundert nicht, dass derzeit auf
den entsprechenden Diskussionsforen (z.B. auf www.javalobby.com)
eine Debatte pro und contra SWT/JFace beziehungsweise AWT/Swing
stattfindet, und dass das SWT 2003 zur innovativsten Java-Kompo-
nente gewählt wurde.
Schließlich stellt Eclipse ein umfangreiches Framework für Java-
Applikationen zur Verfügung. Über die GUI-Bibliotheken hinaus ste-
hen höhere Klassen wie Editoren, Viewer, Ressourcenverwaltung, Auf-
gabenverwaltung, Problembehandlung, Hilfesystem, verschiedene
Assistenten und Wizards zum Einbau in eigene Applikationen bereit.
Alle diese Klassen werden von Eclipse selbst z.B. bei der Implementie-
rung der Workbench oder beim Java-IDE verwendet. Besonders die
Rich Client Platform, die mit Eclipse 3 eingeführt wurde, stellt ein
generische Framework für eine weite Klasse von Anwendungen bereit.
Das Lizenzierungsmodell von Eclipse erlaubt es Anwendern jedoch,
diese Klassen in eigene Anwendungen einzubetten, zu verändern und
als Bestandteil der eigenen Anwendungen mit auszuliefern, selbstver-
ständlich ohne dass Lizenzgebühren fällig würden. Der gesamte Code
von Eclipse steht als Quellcode zur Verfügung und kann so leicht ein-
gesehen werden sowie als Ausgangspunkt für eigene Entwicklungen
dienen.
Das Eclipse-Umfeld 3

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.

Über dieses Buch


Es ist praktisch unmöglich, ein einziges Buch über Eclipse zu schrei-
ben. Eclipse ist derart umfangreich, dass es durchaus Stoff für mehrere
Bücher bietet. Wir haben versucht, besonders die Themen herauszu-
stellen, bei denen Eclipse wesentlich Neues zur Java-Entwicklung bei-
trägt. Dies sind insbesondere die neuen GUI-Bibliotheken (SWT,
JFace) und die Verwendung von Eclipse als Ablaufplattform für Desk-
top-Applikationen. Außen vor bleiben Themen wie die J2EE- und
Servlet-Entwicklung und andere WebSphere-spezifische Themen. Hier
– bei der Entwicklung von Desktop-Anwendungen – liegen auch die
Stärken von Eclipse.
Dieses Buch ist auch keine Einführung in die Programmierung mit
Java. Kenntnisse in der Programmiersprache Java und in der objekt-
orientierten Programmierung werden vorausgesetzt. Die im Buch ver-
4 Einführung

wendeten Beispiele sind alle nicht-trivial. Zwei entstammen dem Mul-


timediabereich. Hier hat der Leser die Möglichkeit, »cutting-edge«
Java-Technologien wie die Sprachausgabe oder MP3-Ausgabe (in rei-
nem Java!) kennen zu lernen. Im dritten Beispiel machen wir dann
etwas Nützliches und implementieren ein Rechtschreibprüfungs-Plu-
gin für Eclipse. Schluss mit den ewigen Tippfehlern in Java-Kommen-
taren! Das vierte Beispiel ist ein Brettspiel, so dass man mit der mit
Eclipse gewonnen Zeit auch etwas anfangen kann.
Das Buch wendet sich also an Java-Programmierer – vom Studie-
renden bis hin zum Profi – die eigene Desktop-Applikationen mit Hilfe
(beziehungsweise auf Grundlage) von Eclipse entwickeln wollen.
Dabei lernen wir alle Techniken kennen, die für die professionelle
Gestaltung solcher Applikationen notwendig sind.

Wie dieses Buch organisiert ist


Der Anfänger, der sich Eclipse nähert, fühlt sich zunächst einmal –
auch wenn es sich um einen erfahrenen Java-Programmierer handelt –
von der Fülle der Funktionen überwältigt. Dabei sind die dem Benut-
zer sichtbaren Funktionen nur die Spitze des Eisbergs. Steigt man in die
inneren Mechanismen von Eclipse, in sein API, ein, so kann man sich
leicht verirren. Allein der Eclipse-Download ist inzwischen 83 MByte
groß.
Dieser riesigen Menge von Informationen gegenüber vertritt dieses
Buch einen pragmatischen Ansatz. Getreu der Devise, dass die
Erkenntnis von außen nach innen geht, schauen wir uns zunächst an,
wie sich Eclipse gegenüber dem Endbenutzer darstellt. Der Nutzen hier
ist zweifach: Zum einen ist der Programmierer Endbenutzer des
Elicpse Java-IDE, zum anderen werden die einzelnen Komponenten
der Eclipse-Workbench, die Editoren, Views, Menüs, Dialoge, Formu-
lare, und vieles andere mehr in eigenen Programmen auftauchen.
Erfahrene Programmierer mögen vielleicht die Einführung in die
Benutzung des Java-IDE für überflüssig erachten. Trotzdem ist es nütz-
lich, sich gründlich mit der Benutzeroberfläche von Eclipse vertraut zu
machen, da man so wesentliche Konzepte und viele Details kennen
lernt, die später bei der Gestaltung eigener Applikationen angewandt
werden können.
In Teil I dieses Buches führen wir deshalb zunächst in die prakti-
sche Arbeit mit Eclipse ein, insbesondere natürlich in die Java-Ent-
wicklungsumgebung. Eclipse präsentiert sich hier als ausgesprochen
starkes Java-IDE, welches die positiven Traditionen von VisualAge for
Java fortsetzt, gleichzeitig aber neue und mächtige Funktionen ein-
Wie dieses Buch organisiert ist 5

führt. Insbesondere sind hier die Funktionen für die Codevervollstän-


digung zu nennen, aber auch die Funktion für die Umformung (Refac-
toring) von Programmen, das Unterbreiten von intelligenten
Korrekturvorschlägen und das Führen einer lokalen Historie, die eine
Rückkehr zu früheren Codeversionen erlaubt.
Auch gehen wir in diesem Teil auf die Organisation der Work-
bench ein, auf die Ressourcen des Eclipse-Workspace wie Projekte,
Verzeichnisse und Dateien, auf ihr Verhältnis zum nativen Dateisystem
und welche Hilfsmittel zur Navigation es hier gibt. Wir erklären, was
Perspektiven sind, und wie sie für die effektive Arbeit mit Eclipse
genutzt werden können. Auch der Java-Debugger kommt nicht zu
kurz genauso wie das Durchführen von Regressionstests mit Hilfe von
JUnit und eine kurze Einführung in die Teamarbeit.
Bei den Beispielen halten wir uns noch zurück und programmieren
zunächst brav mit AWT und Swing.
Das wird sich allerdings in Teil II rasch ändern. Hier führen wir in
die Geheimnisse des SWT und des JFace ein. Beim SWT behandeln wir
die Ereignisverarbeitung, die verschiedenen GUI-Elemente wie Text-
felder, Tabellen, Tasten, Bäume usw., die verschiedenen Layouts, Gra-
fikoperationen samt der Integration von SWT mit Swing und die Aus-
gabe auf den Drucker. Auch erläutern wir die Besonderheiten der
Thread- und Ressourcenverwaltung im SWT.
Beim JFace geht es dann um die höheren Schichten der Benutzer-
oberfläche wie Fenster, Dialoge, Viewer, Aktionen und Menüs, Text-
verarbeitung, Wizards und Präferenzen. Als Beispiel implementieren
wir einen MP3-Player komplett mit SWT und JFace, der aber unab-
hängig von der Eclipse-Plattform einsetzbar ist. Besonders interessant
ist hier der Einsatz von SWT in einer Anwendung mit mehreren
Threads.
In Teil III geht es darum, eigene Produkte auf der Grundlage der
Eclipse-Plattform zu entwickeln: entweder als Plugins zu Eclipse oder
aber als eigenständige Applikationen. Da Eclipse fast nur aus Plugins
besteht, führen wir zunächst in die Plugin-Architektur von Eclipse ein.
Hier erläutern wir die Grundausstattung für eine minimale Plattform,
führen in die Arbeit mit Ressourcen ein und zeigen, wie Plugins mit
Hilfe eines Manifestes gegenüber dem Gesamtsystem deklariert wer-
den können. Dann diskutieren wir die verschiedenen Komponenten
der Workbench wie Editoren, Views, Aktionen, Dialoge, Formulare,
Wizards, Präferenzen, Perspektiven und das Hilfesystem. Alle diese
Komponenten stehen dem Anwendungsentwickler als Bausteine zur
Verfügung, was die Erstellung komplexer und anspruchsvoller Appli-
kationen beträchtlich beschleunigen kann.
6 Einführung

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

4 Projekte richtig organisieren

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.

4.1 Die Workbench


Wie schon in der Einführung erwähnt, ist das Java Development Tool- Der Navigator
kit (JDT) nur eines von vielen Plugins für die Eclipse-Workbench. Inso-
fern ist die Eclipse-Workbench völlig unabhängig von Java. Schalten
wir also einmal aus der Java-Perspektive zurück in die Ressourcen-Per-
spektive (Abb. 4–1). Dort, wo in der Java-Perspektive der Package-
Explorer zu sehen war, sehen wir nun den Navigator. Die einzelnen
Packages sind verschwunden, stattdessen sehen wir eine Struktur
geschachtelter Verzeichnisse.
66 4 Projekte richtig organisieren

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

Die Workbench kennt drei verschiedene Ressourcentypen:


! Projekte. Ein Projekt bildet jeweils das Wurzelverzeichnis für einen
Verzeichnisbaum. Projekte können Verzeichnisse und Dateien ent-
halten.
! Verzeichnisse. Verzeichnisse können andere Verzeichnisse und
Dateien enthalten. Verzeichnisse können beliebig tief geschachtelt
werden.
! Dateien. Dateien bilden die Endpunkte von Ressourcenbäumen,
d.h. eine Datei enthält keine weiteren Ressourcen.

4.2.2 Wo Ressourcen gespeichert werden

Alle Ressourcen werden direkt im Dateisystem des ausführenden Com-


puters gespeichert. Dies ist anders als bei VisualAge, wo die Ressour-
cen der Workbench in einem Repository gespeichert waren. Bei Eclipse
entspricht jedoch die Struktur aus Projekten und Verzeichnissen direkt
einer entsprechenden Verzeichnisstruktur auf dem ausführenden Com-
puter. Das hat Vorteile bezüglich Crash-Sicherheit und Datensiche-
rung.
Der Speicherort für die Ressourcen der Workbench ist standard-
mäßig das Verzeichnis \eclipse\workspace. Hier ist jedes Projekt in
einem entsprechenden Unterverzeichnis enthalten. So ist beispielsweise
die Ressource AnimationEvent.java im Pfad \eclipse\workspace\Duke-
Speaks\com\sun\speech\freetts\relp\AnimationEvent.java gespeichert.
Freilich lässt sich das Workspace-Verzeichnis mit Hilfe des Komman-
dozeilenparameters -data auch an anderen Orten einrichten, so z.B.
mit
eclipse.exe -data C:\myOldWorkSpace

oder durch Angabe eines anderen Workspace-Ortes im Workspace


Launcher (siehe Abschnitt 1.1).

4.2.3 Ressourcen synchronisieren


Gelegentlich kommt es vor, dass der Zustand der Dateien in \eclipse\ Refresh
workspace nicht dem Kenntnisstand von Eclipse entspricht, denn
Eclipse speichert Metadaten über die Ressourcen der Workbench im
Verzeichnis \eclipse\workspace\.metadata. Wurde jedoch eine Datei
der Workbench an Eclipse vorbei verändert, z.B. durch die Anwen-
dung eines externen Werkzeugs, so kann es passieren, dass die Meta-
68 4 Projekte richtig organisieren

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

Die folgenden Kontextfunktionen und Werkzeugtasten stehen im Kon-


textmenü und auf der Werkzeugleiste des Navigators für die Naviga-
tion zur Verfügung:
! Go into. Diese Funktion reduziert die aktuelle Sicht auf den Inhalt
des ausgewählten Projekts oder Verzeichnisses. Wenn man Tau-
sende von Ressourcen in der Workbench hat, weiß man diese
Funktion zu schätzen.
! Back. Diese Taste (Pfeil nach links) wechselt zur vorigen Sicht.
! Forward. Diese Taste (Pfeil nach rechts) macht Back wieder rück-
gängig.
! Up to. Diese Taste (Ordnersymbol) geht ins nächsthöhere Ver-
zeichnis.
! Mit der Funktion Select Working Set ... kann ein benannter Wor-
king Set ausgewählt werden, um die im Navigator angezeigten Res-
sourcen auf die Ressourcen dieses Working Sets zu beschränken.
Die Funktion gestattet auch die Neuanlage von Working Sets.
! Die Funktion Deselect Working Set entfernt diese Einschränkun-
gen wieder.
! Mit der Funktion Edit Active Working Set ... kann der aktive Wor-
king Set modifiziert werden.
! Open in new Window. Wirkt wie die Funktion Go into, nur dass
ein weiteres Fenster (mit einer kompletten Workbench!) geöffnet
wird, bei der nur der Inhalt des ausgewählten Projekts oder Ver-
zeichnisses im Navigator gezeigt wird.
Das Menü der Toolbar (unter dem kleinen Dreieck) bietet noch einige
zusätzliche Funktionen:
! Mit der Sort-Funktion können die Dateien nach Name oder Typ
sortiert angeordnet werden.
! Mit der Filters ...-Funktion können Dateien mit bestimmten Datei-
erweiterungen aus dem Navigator ausgeblendet werden.
4.3 Assoziationen 69

! Die Funktion Link with Editor schaltet die automatische Synchro-


nisation der Ressourcenselektion mit dem Editorinhalt ein. Wenn
Sie durch einen Klick auf einen Reiter zu einem anderen Editor
wechseln, ändert sich die Selektion im Navigator entsprechend.

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.

4.4.1 Verzeichnisse und Packages

Packages sind keine realen Ressourcen, sondern virtuelle Objekte. Die


Package-Struktur eines Projekts ergibt sich aus den package-Deklara-
tionen am Beginn jeder Java-Quelldatei.
Die Java-Spezifikation verlangt allerdings, dass sich die Package-
Struktur isomorph auf eine Verzeichnisstruktur abbilden lässt. So
müssen – wie in Abbildung 4–3 gezeigt – alle Ressourcen des Package
com.sun.speech.freetts.relp im relativen Pfad com/sun/speech/
freetts/relp gespeichert sein. In Eclipse ist diese Pfadangabe immer
relativ zum Quelltext-Wurzelverzeichnis für das Projekt. Der relative
Pfad com/sun/speech/freetts/relp entspricht daher dem physischen
Pfad \eclipse\workspace\DukeSpeaks\com\sun\speech\freetts\relp.
4.4 Packages 71

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

Der Package-Explorer verfügt über ähnliche Navigationsfunktionen


wie der Navigator (siehe Abschnitt 4.2.4). So gibt es auch hier die Kon-
textfunktionen Go into und Open in new Window, in der Toolbar gibt
es Tasten für die Funktionen Back, Forward und Up to. Unter dem
Drop-down-Menü der Werkzeugleiste finden wir auch die gleichen
Funktionen zur Verwaltung von Working Sets und für die Synchroni-
sierung mit dem Editor.
Außerdem besteht noch die Möglichkeit, den Browser für die
Typhierarchie zu öffnen.

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 Der Outline-View


Der Outline-View (Abb. 4–5) gestattet die Navigation innerhalb einer
Quelldatei. Generell ist der Outline-View nicht auf Java-Programme
beschränkt, sondern steht – je nach installierten Plugins – auch für
andere Dateitypen zur Verfügung.
Bei Java-Programmen stellt der Outline-View sowohl die Felder
und Methoden als auch die import-Anweisungen dar. Sind innere
Klassen definiert, werden auch diese Klassen dargestellt: Die Haupt-
klasse und die inneren Klassen bilden eine Baumstruktur.
Durch einen einfachen Klick auf ein im Outline-View dargestelltes
Feld oder eine Methode wird der Editor auf die Definition des Feldes
bzw. der Methode positioniert. Darüber hinaus gibt es noch eine Reihe
weiterer nützlicher Funktionen. Doch beginnen wir mit der Darstel-
lung der Felder und Methoden im Outline-View.

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).

Das erste Icon vor einem Eintrag im Outline-View kennzeichnet die


Art des Eintrags (Package, import-Anweisung, Interface, Klasse, Icons
Methode, Feld) und die Sichtbarkeit (public, protected, private).
74 4 Projekte richtig organisieren

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)

Zusätzlich zu diesem ersten Icon können noch weitere Icons angezeigt


werden:

Konstruktor (constructor.tif)

statisches Element (static) (static.tif)

unveränderbares Element (final) (final.tif)

überschriebenes Element (override.tif)

Die Darstellung des Outline-View kann über Window>Preferen-


ces>Java>Appearance beeinflusst werden:
! Show method return types. Zeigt den Ergebnistyp von Methoden
im Outline-View an.
! Show override indicators ... Zeigt den -Indikator für überschrei-
bende Methoden an.
! Show member in Package Explorer. Ist diese Option gesetzt, so
erscheinen Methoden und Felder auch im Package-Explorer als
Kindelemente von Klassen und Interfaces. Die meisten Funktionen
des Outline-View stehen dann auch dort zur Verfügung.

4.5.2 Kontextfunktionen

Im Outline-View gibt es eine Reihe von Kontextfunktionen. Die wich-


tigsten sind auch auf der Toolbar des Outline-View als Buttons vor-
handen (siehe Abb. 4–5). Hier ist eine Übersicht über die vorhandenen
Kontextfunktionen:
4.6 Suchen 75

! Open Type Hierarchy. Zeigt die Typhierarchie für das ausge-


wählte Element (siehe Abschnitt 4.4.3). Diese Funktion kann nicht
nur auf einzelne Typen, sondern auch auf ganze Packages oder
Projekte angewandt werden.
! Open Call Hierarchy. Zeigt die Aufrufhierarchie für die ausge-
wählte Methode.
! Open Super Implementation. Diese Funktion erscheint nur bei
überschreibenden Elementen. Bei Ausführung der Funktion wird
das überschriebene Element im Editor geöffnet.
! Cut, Copy, Paste, Delete. Die üblichen Kopier- und Löschfunktio-
nen, angewandt auf das im Outline-View ausgewählte Element.
! Refactor>... Verschiedene Funktionen zum Umorganisieren von
Code (siehe Abschnitt 2.3.2).
! Source>... Verschiedene Funktionen zur automatischen Vervoll-
ständigung von Code (siehe Abschnitt 2.1.3).
! References>... Sucht nach Verweisen auf das selektierte Element
(siehe nächster Abschnitt).
! Declarations>... Sucht nach Definitionen des selektierten Elements
(siehe nächster Abschnitt).
! Read Access>... Sucht nach Lesezugriffen auf das selektierte Feld
(siehe nächster Abschnitt).
! Write Access>... Sucht nach Schreibzugriffen auf das selektierte
Feld (siehe nächster Abschnitt).
! Occurrences in File. Zeigt im Search View (siehe nächster
Abschnitt), wo das ausgewählte Element in der aktuellen Datei
vorkommt.
! Toggle Watchpoint. Diese Funktion erscheint nur bei Feldern und
gehört zum Instrumentarium des Debuggers (siehe Kapitel 6).
! Toggle Method Breakpoint. Diese Funktion erscheint nur bei
Methoden und gehört zum Instrumentarium des Debuggers (siehe
Kapitel 6).
! Compare With>..., Replace With>..., Restore from Local History ...
Mit diesen Funktionen kann die aktuelle Version mit früheren Ver-
sionen zu verglichen bzw. durch frühere Versionen ersetzt werden
(siehe Abschnitt 2.5).

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

4.6.1 Die Suchfunktion

Die Eclipse-Suchfunktion besteht aus zwei Komponenten: dem Dialog-


formular zur Eingabe der Suchkriterien (Abb. 4–6) und der Tabelle der
Suchergebnisse (Abb. 4–7).

Suchkriterien

Wird die Suchfunktion von der Toolbar der Eclipse-Workbench oder


aus dem Eclipse-Hauptmenü heraus aufgerufen, so erscheint zunächst
der Dialog für die Spezifikation der Suchkriterien. Beim Aufruf über
eine Kontextfunktion unterbleibt dieser Schritt, da die Suchkriterien
schon feststehen.

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

beschränkt werden. Der Suchbereich lässt sich auf selektierte Ressour-


cen oder auf Working Sets (benannte Ressourcenmengen) beschränken.
Neben der Java-Suche gibt es im Suchdialog weitere Seiten für die
Suche nach Texten in allgemeinen Ressourcen (in diesem Modus gibt
es auch eine Replace ...-Taste, um Ersetzungsoperationen durchzufüh-
ren), für die Suche im Hilfesystem und für die Suche nach Plugins. Mit
der Customize-Taste kann man einzelne Seiten des Suchdialogs aus-
bzw. einblenden.

Suchergebnisse

Die Ergebnisse der Suche werden im Search-View dargestellt, der in der


Standard-Java-Perspektive den Tasks-View überdeckt. Wurde vorher
im Menü des Views die Option Hierarchical Layout gewählt, können
mit einem Druck auf die entsprechende Werkzeugtaste die Suchergeb-
nisse nach Projekt, Package, Datei oder Klasse gruppiert werden.

Selektierten Eintrag löschen Nach Ordner gruppieren


Nächster Eintrag Alle Einträge löschen Nach Package gruppieren
Voriger Eintrag Alles expandieren Nach Datei gruppieren
Alles kollabieren Nach Typ gruppieren Vorige
Suche stoppen 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.

Mit den Abwärts- und Aufwärts-Pfeilen in der Toolbar des Search-


Views kann man bequem die einzelnen Fundstellen aufsuchen, die ent-
sprechende Kompiliereinheit wird dabei automatisch im Editor geöff-
net. In der linken Randzeile des Editors werden Zeilen mit Treffern der
Suchanfrage mit einem gelben Pfeil gekennzeichnet.
Der Search-View speichert auch vorhergehende Suchergebnisse.
Mit den entsprechenden Buttons (oder auch mit dem Menü des
Search-Views) kann man deshalb auch auf die Ergebnisse früherer
Suchanfragen zurückgreifen.
78 4 Projekte richtig organisieren

4.6.2 Finden und Ersetzen

Neben der oben diskutierten Suchfunktion gibt es natürlich auch eine


Editorfunktion zum Suchen und Ersetzen von Zeichenketten. Mit der
Funktion Edit>Find/Replace erhält man einen Dialog (siehe Abb. 4–8)
zur Eingabe von Suchbegriffen und verschiedenen Suchoptionen. Ist
beim Aufruf dieser Funktion bereits eine Zeichenkette selektiert, so
wird diese Zeichenkette als Suchbegriff übernommen.

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.

Bei der Suche besteht die Möglichkeit, vorwärts oder rückwärts zu


suchen und die Suche nur auf die selektierten Zeilen zu beschränken.
Daneben gibt es noch einige weitere Optionen:
! Case Sensitive. Ist dieses Feld markiert, so wird die Groß- und
Kleinschreibung bei der Suche berücksichtigt.
! Wrap Search. Ist dieses Feld markiert, beginnt die Suche am Ende
des Suchbereichs (bzw. am Anfang, wenn rückwärts gesucht wird)
wieder von vorne. Anderenfalls erfolgt eine Fehlermeldung.
! Whole Word. Ist dieses Feld markiert, werden nur ganze Wörter
gesucht.
! Incremental. Ist dieses Feld markiert, so beginnt die Suche sofort
bei der Eingabe des ersten Buchstabens. Bei Eingabe weiterer Buch-
staben wird ggf. weitergesucht.
! Regular Expression. Ist dieses Feld markiert, wird der Suchaus-
druck als regulärer Ausdruck interpretiert. Mit F1 bekommen Sie
4.7 Editoren und Views anordnen 79

Hilfe für die Syntax regulärer Ausdrücke, und mit Strg+Leertaste


erhalten Sie einen Inhaltsassistenten, der Ihnen bei der Konstruk-
tion eines regulären Ausdrucks hilft. Im Suchausdruck können
Capture-Gruppen verwendet werden. Die Ergebnisse stehen im
Ausdruck für die Ersetzung zur Verfügung.
Entsprechend zu diesen Optionen stehen zusätzlich zu Edit>Find/Replace
weitere spezialisierte Suchfunktionen zur Verfügung: Edit>Find Next,
Edit>Find Previous, Edit>Incremental Find.

4.6.3 Gleichnamige Elemente markieren

Wenn Sie unter Window>Preferences>Mark Occurrences die Option


Mark occurrences in file einschalten, so werden in Zukunft, wenn Sie
im Editor ein syntaktisches Element selektieren, auch alle gleichnami-
gen Elemente in der gleichen Datei markiert. Da diese Markierungen
auch in der rechten Randleiste erscheinen, kann man diese Stellen
leicht mit dem Scrollbalken aufsuchen und erspart sich in vielen Fällen
eine umständliche Suche.
Dabei können Sie in den Einstellungen noch festlegen, für welche
Art von syntaktischen Elementen diese Funktion wirksam sein soll: für
alle Typen, für Methoden, für Konstanten, Felder, Variablen usw.
Wenn Sie zusätzlich noch die Option Sticky einschalten, so bleiben die
Markierungen auch noch erhalten, wenn das betreffende Element
nicht mehr markiert ist.
Die Markierungsfunktion kann mit der Werkzeugtaste Mark
Occurrences an- oder abgeschaltet werden.

4.7 Editoren und Views anordnen


Die Anordnung der verschiedenen Fenster in der Eclipse-Workbench
ist nicht fest vorgegeben, sondern mit Einschränkungen frei konfigu-
rierbar. Dabei gibt es im Wesentlichen vier verschiedene Organisa-
tionsprinzipien:
! Anordnung der Fenster neben- bzw. übereinander wie in Abbil- Fenster andocken
dung 4–9. Alle Fenster sind gleichzeitig sichtbar. Man kann ein
Fenster an ein anderes Fenster andocken, indem man es bei der
Titelleiste oder beim Reiter packt und an den jeweiligen Rand des
Zielfensters zieht. Erscheint ein Pfeilsymbol, lässt man es los.
! Mehrere Fenster können in einem Stapel angeordnet werden. Gestapelte Fenster
Jeweils mit einem Druck auf einen Reiter kann ein Fenster im Sta-
pel nach oben gebracht werden und wird damit sichtbar. Man
80 4 Projekte richtig organisieren

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

Kontextfunktionen: Mit der Funktion Orientation kann festgelegt


werden, ob ein FastView vertikal oder horizontal expandiert wer-
den soll. Mit einem Klick auf die Funktion FastView kann das
Häkchen von dieser entfernt werden und der View wieder in den
Originalzustand zurückversetzt werden. Mit der Funktion Dock
On kann festgelegt werden, wo die FastView-Leiste erscheinen soll:
am unteren Rand des Workbench-Fensters (Standard) oder am lin-
ken oder rechten Rand.

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.

4.8 Perspektiven verwalten


Eine Perspektive ist eine bestimmte Konfiguration von Editoren, Views
und Menüs in der Workbench. In diesem Kapitel haben wir bereits die
Ressource-Perspektive und die Java-Perspektive kennen gelernt. In
Kapitel 6 werden wir dazu noch die Debug-Perspektive kennen lernen.

4.8.1 Neue Perspektiven definieren

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

Abbildung 4–9. Gibt es eine Möglichkeit, diese Einstellungen zu spei-


chern?
Ja, und zwar mit der Funktion Window>Save Perspective As ...
(Abb. 4–10). Hier können Sie die aktuelle Konfiguration unter einem
von Ihnen vergebenen Namen als neue Perspektive speichern. Falls Sie
später zu dieser Konfiguration zurückkehren wollen, können Sie das
entweder mit der Funktion Window>Reset Perspective oder mit den
Funktionen Window>Close Perspective und Window>Open Perspec-
tive ... erreichen.Die in Abbildung 4–9 veränderte Workbench-Konfi-
guration speichern wir hier als neue Perspektive unter dem Namen
Java (Search docked to Problems) ab.

Abb. 4–10 Die in Abbildung 4–9 veränderte Workbench-Konfiguration speichern wir


hier als neue Perspektive unter dem Namen Java (Search docked to Problems) ab.
4.8 Perspektiven verwalten 83

4.8.2 Perspektiven konfigurieren

Mit Hilfe der Funktion Window>Customize Perspective können


bestimmte Aspekte der aktuellen Perspektive geändert werden:
! Der Reiter Shortcuts erlaubt anzugeben, welche Elemente direkt in
einem gegebenen Untermenü aufgelistet werden sollen (andernfalls
müsste man auf Others ... klicken, um zu dem gewünschten Element
zu kommen). Shortcuts können für die Untermenüs File>New,
Window>Open Perspective und Window>Show View definiert
werden.
! Der Reiter Commands erlaubt anzugeben, welche Kommando-
gruppen (Action Sets) im Menü und der Werkzeugleiste der aktuel-
len Perspektive sichtbar sein sollen.
Diese Funktion kann übrigens auch bequem mit einem Klick der rech-
ten Maustaste auf die Werkzeugleiste aufgerufen werden.

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.

Daneben gibt es unter Window>Preferences>Workbench>Perspectives


die Möglichkeit festzulegen, ob neu geöffnete Perspektiven innerhalb
der aktuellen Workbench oder in einem neuen Workbench-Fenster
geöffnet werden. Diese letzte Option macht jedoch nur Sinn bei einem
84 4 Projekte richtig organisieren

sehr großen Bildschirm. Bei kleineren Bildschirmen wird man die


Eclipse-Workbench bildschirmfüllend einsetzen. Die Perspektive-Icons
auf der Perspektiven-Leiste der Workbench (rechts oben) sind dann
das geeignete Mittel für einen Perspektivwechsel.
Eine weitere Option erlaubt es, beim Neuanlegen eines Projekts
auch gleichzeitig eine neue Perspektive (im gleichen oder einem neuen
Workbench-Fenster) anzulegen. So könnte jedes Projekt eine eigens
auf es zugeschnittene Perspektive erhalten.

4.9 Dateien importieren


Wir gehen nun daran, unserem HelloWorld-Programm das Sprechen
beizubringen. Seit Version 1.4 enthält Java auch eine Sprachschnitt-
stelle, das Java Speech API (JSAPI). Dazu gibt es die kostenlose Refe-
renzimplementierung FreeTTS, die wir uns aus dem Internet herunter-
laden können. FreeTTS hat seine Wurzeln im Sprachsynthesizer Flite,
wurde aber komplett nach Java portiert. Interessant dabei ist, dass

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

Ordner ...\FreeTTS\demo\JSAPI\HelloWorld. Dieser Ordner erscheint


nun im linken Fenster des Dialogs. Wir selektieren ihn und sehen auf
der rechten Seite alle Dateien in diesem Ordner, wie in Abbildung 4–12
gezeigt. Dort machen wir ein Häkchen an HelloWorld.java und drü-
cken den Finish-Button.
Wenn alles richtig gelaufen ist, müsste nun das importierte Pro-
gramm HelloWorld.java ein Default-Package im Projekt FreeTTS sein
(Abb. 4–13). Doch, oh weh, jede Menge Fehlermeldungen!
Natürlich – das FreeTTS-Laufzeitsystem fehlt noch. Wir haben
zwei Möglichkeiten:
! Wir importieren auch die JAR-Dateien des Laufzeitsystems in den
Workspace. Damit koppeln wir uns aber von Versionswechseln ab.
Neue Versionen der JAR-Dateien müssten dann ggf. neu in den
Workspace importiert werden.
! Wir fügen die JAR-Dateien als externe Dateien dem Java Build
Path zu. Das erspart uns den Import dieser Dateien. So vermeiden
wir das doppelte Vorhalten dieser Archivdateien. Werden diese
Dateien durch neue Versionen ersetzt, werden die Änderungen
auch sofort für unser Projekt wirksam.

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.

Für das Package freetts.jar wählen wir die Kontextfunktion Proper-


ties. In den dann erscheinenden Package-Eigenschaften wählen wir
Java Source Attachment. Dann drücken wir auf die Taste External File
und navigieren zur Datei freetts-srcs-1_2_beta.zip. Und das war’s
auch schon. Öffnen wir nun eine Datei aus freetts.jar, so erscheint
der zugehörige Quellcode im Editor. Freilich sind diese Dateien nicht
editierbar, sondern dienen lediglich der Ansicht.
Eigentlich können wir das Programm nun ausprobieren. Wir füh-
ren es mit demselben Kommando wie schon unser erstes HelloWorld-
Programm aus: Run>Run As>Java Application.
Statt der versprochenen Sprachausgabe erhalten wir aber nur fol-
genden Text auf der Konsole:
Can't find synthesizer. Kein Bild, kein Ton ...
Make sure that there is a "speech.properties" file at either of
these locations:
user.home : H:\Dokumente und Einstellungen\Berthold Daum
java.home/lib: C:\j2sdk1.4.0\jre\lib

Richtig, davon war ja in der FreeTTS-Installationsanweisung die Rede.


Wir kopieren die Datei speech.properties aus ...\FreeTTS in eines der
genannten Verzeichnisse und führen dann unser Programm erneut aus.
88 4 Projekte richtig organisieren

Es ertönt:
Hello, World!
Vorausgesetzt freilich, Ihr Computer hat eine Soundkarte (oder Sound
on Board) und Sie haben einen Lautsprecher angeschlossen ...

4.11 Die Java Browsing-Perspektive


Die Java Browsing-Perspektive (Abb. 4–15) erlaubt eine etwas andere
Sicht auf die Struktur eines Java-Projekts und erinnert an VisualAge-
Zeiten. Wir können diese Perspektive einfach durch einen Druck auf
die Taste »Neue Perspektive« (siehe Abschnitt 1.2) und entsprechende
Auswahl anlegen.

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

Das Standard Widget Toolkit stellt eine Anzahl elementarer GUI-Klas-


sen zur Verfügung. In diesem Kapitel geben wir zunächst eine Über-
sicht über die Funktionsgruppen des SWT und diskutieren die Vor-
und Nachteile des SWT im Vergleich zu den Java-AWT-Bibliotheken.
Dann erläutern wir die wichtigsten Funktionsgruppen im Detail.
Dabei werden wir jedoch keine vollständige API-Beschreibung
vorlegen. Stattdessen beschränken wir uns auf wesentliche Merkmale
der einzelnen SWT-Funktionsgruppen und deren Zusammenspiel. Die
API-Dokumentation der einzelnen SWT-Packages findet man im
Eclipse-Hilfesystem unter Platform-Plugin Developer Guide>Refe-
rence>API Reference>Workbench.

8.1 Übersicht über die SWT-Funktionsgruppen


Die SWT-Klassen sind auf die folgenden Packages aufgeteilt:

Dieses Package enthält alle SWT-spezifischen


org.eclipse.swt
Konstanten und Exceptions. Siehe Abschnitt 8.3.
Dieses Package enthält Klassen für die
org.eclipse.swt.accessibility Implementierung behindertengerechter GUIs.
Siehe Abschnitt 8.14.
Dieses Package enthält die Klasse SWT-AWT für
org.eclipse.swt.awt die Einbettung von AWT-Elementen in das SWT.
Siehe Abschnitt 8.8.1.
Dieses Package enthält die Klassen für die
org.eclipse.swt.browser Implementierung des Browser-Widgets.
Siehe Abschnitt 8.5.14.
Dieses Package enthält Widgets für die Eclipse-
Plattform, die nicht direkt auf das Windowing-
org.eclipse.swt.custom
System abgebildet werden können, sondern in
Java implementiert sind. Siehe Abschnitt 8.5.13.
148 8 Das SWT

Dieses Package unterstützt Funktionen des


Datentransfers wie Drag&Drop oder Operationen
org.eclipse.swt.dnd
auf die Zwischenablage (Clipboard).
Siehe Abschnitt 8.10.
Dieses Package enthält alle SWT-spezifischen
org.eclipse.swt.events Klassen (Events) und Interfaces (Listener) für die
Ereignisverarbeitung. Siehe Abschnitt 8.4.3.
Dieses Package enthält Klassen für grafische
org.eclipse.swt.graphics
Operationen. Siehe Abschnitt 8.7.
Dieses Package enthält interne SWT-Klassen.
SWT-Anwendungen sollten nicht direkt auf diese
org.eclipse.swt.internal
Klassen zugreifen, da sich deren API ohne
Vorwarnung ändern kann.
Dieses Package enthält verschiedene Layout-
org.eclipse.swt.layout Klassen für die automatische Positionierung von
SWT-Elementen. Siehe Abschnitt 8.6.
Dieses Package unterstützt OLE für 32-Bit-
org.eclipse.swt.ole.win32
Windows-Betriebssysteme. Siehe Abschnitt 8.12.
Dieses Package unterstützt die Ausgabe auf
org.eclipse.swt.printing
Druckern.Siehe Abschnitt 8.9.
Dieses Package enthält nur die Klasse program.
Deren Instanzen repräsentieren Dateiassoziatio-
org.eclipse.swt.program
nen im zu Grunde liegenden Betriebssystem und
unterstützen den Start externer Programme.
Dieses Package enthält alle Widget-Klassen des
SWT-API. Im Wesentlichen ist es dieses
org.eclipse.swt.widgets
Package, das den Funktionsumfang des SWT
definiert. Siehe Abschnitt 8.5.

8.2 Vor- und Nachteile des SWT


Die Frage ist natürlich: Wann sollte man das SWT für die Implemen-
tierung eines GUI verwenden und wann Swing? Im folgenden
Abschnitt sehen wir uns einige Vor- und Nachteile des SWT im Ver-
gleich zu Swing an.

8.2.1 Vorteile des 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

aus wie ein Windows-2000-Button, unter Windows XP genauso wie


ein Windows-XP-Button und auf dem Mac genauso wie ein Mac-But-
ton. Das ist unter Swing oft nicht gegeben. Hier gibt es zwar eine
Anzahl von vordefinierten Skins, jedoch ist oft nicht das Richtige
dabei.
Bei der Ansprechgeschwindigkeit ist Eclipse auch im Vorteil. Das Bessere Interaktion
SWT unterscheidet sich verständlicherweise auch hier nicht von nati-
ven Applikationen, da direkt auf die Ereignisverarbeitung des jeweili-
gen Betriebs- oder Fenstersystems aufgesetzt wird. Swing ist dagegen
in der Interaktion oft etwas träge. Zudem ist SWT nicht so ressourcen-
hungrig wie Swing.
Da die Eclipse-Plattform selbst mit Hilfe des SWT programmiert
ist, ist das SWT natürlich auch die beste Wahl, wenn man Eclipse-Plug-
ins entwickeln und GUI-Elemente der Eclipse-Plattform für das Plugin
nutzen will.
Last but not least: Da SWT-Widgets direkt auf die entsprechenden Robuster
Dienste des nativen Windowing-Systems aufsetzen, darf erwartet wer-
den, dass das SWT sich robuster und toleranter gegenüber der verwen-
deten Hardware und gegenüber verschiedenen Grafikeinstellungen
verhält als das AWT. Tatsächlich habe ich unter Windows festgestellt,
dass SWT-Applikationen auch dort noch problemlos liefen, wo AWT-
oder Swing-Applikationen auf Grund von DirectX-Inkompatibilitäten
die Maschine zu einem jähen Halt brachten.

8.2.2 Nachteile des SWT

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

! Im Gegensatz zu dem AWT erfordert das SWT eine explizite Res-


sourcenverwaltung. So benutzt das SWT für Bilder, Farben und
Fonts Betriebssystemressourcen, die, sobald sie nicht mehr benö-
tigt werden, mit dispose() wieder explizit freigegeben werden soll-
ten. Wir werden die Ressourcenverwaltung ausführlich in Abschnitt
8.9 diskutieren.

8.3 Das Package SWT


Im Package org.eclipse.swt sind lediglich drei Klassen definiert: SWT,
SWTException und SWTError. Während die beiden letzteren Klassen der
Fehlerbehandlung (abfangbare bzw. nicht abfangbare Ausnahmebe-
dingungen) dienen, sind in der Klasse SWT alle SWT-spezifischen Kon-
stanten zusammengefasst. Dazu gehören Konstanten für die Bezeich-
nung von Tasten, für vordefinierte Farben, für Layout-Variationen bei
den Widgets, für Textstile, Cursorvariationen, Mausaktionen, vordefi-
nierte Knöpfe und anderes.
So bezeichnet z.B. SWT.LINE_DASHDOT eine Strich-Punkt-Linie, oder
SWT.MouseDoubleClick ein Mausdoppelklick-Ereignis. Wir werden in
den folgenden Beispielen einige dieser Konstanten im Einsatz sehen.

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

Für jede Ereignisart gibt es verschiedene Listener-Klassen. So kann


man z.B. einer Taste (Button) mit addSelectionListener() eine Selec-
tionListener-Instanz hinzufügen. Wird die Taste selektiert (ange-
klickt), wird die widgetSelected()-Methode dieser SelectionListe-
8.4 Ereignisse 151

ner-Instanz aufgerufen. Die Methode erhält als Argument eine


SelectionEvent-Instanz, die das Ereignis repräsentiert.
Beispiel:
public void createButton(Composite parent) {
Button myButton = new Button(parent, SWT.PUSH);
myButton.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button pressed!");
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}

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

Adapter sind Standardimplementierungen der jeweiligen Interfaces. Sie


enthalten leere Methoden für jede im Interface definierte Methode.
Adapter dienen so dem Komfort des Programmierers. Anstatt alle
Methoden eines Interface komplett implementieren zu müssen, defi-
niert man lediglich eine Unterklasse des zugehörigen Adapters und
überschreibt diejenigen Methoden, die im konkreten Anwendungsfall
von Interesse sind.
Im obigen Beispiel hätten wir natürlich auch einen SelectionAdap-
ter verwenden und so auf die Angabe der leeren Methode widget-
DefaultSelected() verzichten können:
152 8 Das SWT

public void createButton(Composite parent) {


Button myButton = new Button(parent, SWT.PUSH);
myButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button pressed!");
}
});
}

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.

Achtung: TypedEvent ist kein Abkömmling von Event!

Jeder Ereignistyp besitzt eine Anzahl von öffentlichen Feldern, welche


die spezifischen Daten des jeweiligen Ereignisses enthalten. So besitzt
z.B. der Typ MouseEvent die Integer-Felder x, y, stateMask und button.
Auf alle diese Felder kann direkt zugegriffen werden (also ohne eine
get...()-Methode). Außerdem besitzt jedes TypedEvent noch die
Methode getSource(), mit der man den Absender des TypedEvent-
Objekts ermitteln kann.
Bei der generischen Klasse Event dagegen kann die Art des Ereig-
nisses über das Feld type abgefragt werden. Den Absender erhält man
über das Feld widget.

8.4.4 Übersicht über Listener-, Adapter- und Event-Klassen

Listener Ereignis Adapter


Typisierte Ereignisse
ArmEvent
Tritt auf, wenn ein Widget, z.B. ein Menü, für die
ArmListener –
Selektion vorbereitet wird. Das ist z.B. der Fall, wenn
die Maus über dieses Element bewegt wird.
ControlEvent
ControlListener Tritt auf, wenn ein GUI-Element bewegt oder in der ControlAdapter
Größe verändert wird.
DisposeEvent
DisposeListener –
Tritt auf, wenn ein Widget entsorgt wird.
FocusEvent
FocusListener Tritt auf, wenn ein GUI-Element den Fokus erhält FocusAdapter
oder verliert.
8.4 Ereignisse 153

Listener Ereignis Adapter


Typisierte Ereignisse
HelpEvent
HelpListener Tritt auf, wenn für ein GUI-Element Hilfe angefordert –
wird (z.B. mit F1).
KeyEvent
KeyListener KeyAdapter
Tritt auf, wenn Tasten der Tastatur betätigt werden.
MenuEvent
MenuListener MenuAdapter
Tritt auf, wenn Menüs ein- oder ausgeblendet werden.
ModifyEvent
ModifyListener –
Tritt auf, nachdem Text modifiziert wurde.
MouseListener
MouseEvent
Wird benachrichtigt, wenn die MouseAdapter
Mausereignis.
Maustasten betätigt wurden.
MouseMoveListener
MouseEvent
Wird benachrichtigt, wenn der –
Mausereignis.
Mauszeiger sich bewegt.
MouseTrackListener
Wird benachrichtigt, wenn der MouseEvent
MouseTrackAdapter
Mauszeiger sich über GUI-Elementen Mausereignis.
bewegt oder über ihnen schwebt.
PaintEvent
PaintListener Tritt auf, wenn ein GUI-Element neu gezeichnet –
werden muss.
SelectionEvent
SelectionListener SelectionAdapter
Tritt auf, wenn ein GUI-Element selektiert wird.
ShellEvent
ShellListener Tritt auf, wenn sich der Zustand einer Shell-Instanz ShellAdapter
ändert (normal, minimiert, maximiert).
TraverseEvent
Tritt bei einem Traverse-Ereignis auf, z.B. wenn der
TraverseListener –
Benutzer mit TAB auf das nächste Feld geht oder
wenn die traverse()-Methode aufgerufen wird.
TreeEvent
TreeListener Tritt auf, wenn Baumknoten expandieren oder kolla- TreeAdapter
bieren.
VerifyEvent
Tritt auf, bevor Text modifiziert wird. Mittels einer
VerifyListener entsprechenden Zuweisung an die doit-Variable des –
Ereignisobjekts kann die Modifikation verhindert
werden (Veto).
Generische Ereignisse
Event
Listener Nicht typisiertes Ereignis innerhalb des SWT. –
Wird von nicht-Widget-Objekten erzeugt.
154 8 Das SWT

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.

Die Klasse Widget

Alle GUI-Elemente sind Abkömmlinge der abstrakten Klasse Widget.


Diese Klasse implementiert einige Basismethoden wie dispose() oder
addDisposeListener(). Bei der Ausführung von dispose() sendet sie
allen DisposeListener-Instanzen ein DisposeEvent-Objekt.
Unmittelbarer Abkömmling der Klasse Widget ist die abstrakte
Klasse Control. Instanzen dieser Klasse repräsentieren fensterbezogene
GUI-Elemente und korrelieren direkt mit Ressourcen des Betriebs-
oder Fenstersystems.

Die Klasse Control

Die Klasse Control kann folgende Event-Typen an die jeweiligen Liste-


ner schicken: ControlEvent, FocusEvent, HelpEvent, KeyEvent, MouseE-
vent, MouseTrackEvent, MouseMoveEvent, PaintEvent. Dazu stellt Con-
trol für die Verwaltung der jeweiligen Listener-Typen verschiedene
add...Listener()- und remove...Listener()-Methoden zur Verfü-
gung.
Außerdem stellt Control ein reichhaltiges Arsenal von Methoden
zur Verfügung, mit denen die verschiedenen Eigenschaften der jeweili-
gen GUI-Elemente gesetzt und abgefragt werden können. Insbesondere
sind die Methoden setVisible() und setEnabled() zu erwähnen, mit
denen man GUI-Elemente zeigen und verstecken bzw. für die Eingabe
freigeben oder sperren kann.
Größe und Position Anfänglich wird die Größe von Control-Instanzen auf einen
Default-Wert gesetzt. In vielen Fällen ist das die minimale Größe
(0x0), so dass das entsprechende GUI-Element unsichtbar bleibt. Mit
der Methode setBounds() kann die Größe der Instanz und auch die
Position relativ zum übergeordneten Composite (siehe Abschnitt 8.5.4)
gesetzt werden. Alternativ kann das übergeordnete Composite mit
einem Layout (siehe Abschnitt 8.6) versehen werden, das sich selbsttä-
tig um die Positionierung der im Composite enthaltenen Control-
Instanzen kümmert. Mit der Methode pack() wird die bevorzugte
Größe bzw. die aus dem Layout abgeleitete Größe neu berechnet.
8.5 Widgets 155

Widget

Caret DragSource DropTarget Menu ScrollBar Tracker

Control

Scale Slider Scrollable ProgressBar Sash Button Label

List Text

Composite

CCombo Combo CoolBar Table Tree

Group Toolbar Canvas Browser

TabFolder CTabFolder Decorations

Item Shell

CoolItem MenuItem TabItem CTabItem

TableColumn TableItem ToolItem TreeItem TableTreeItem

Abb. 8–1 Die Hierarchie der wichtigsten Widget-Klassen

8.5.1 Visuelle Übersicht

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

Abb. 8–2 Die SWT-Controls-Beispielapplikation besitzt für die verschiedenen nativen


Widget-Typen jeweils eine eigene Karteikarte. Rechts kann das Widget über Parameter
konfiguriert werden. Die Namen der Parameter entsprechen den jeweiligen SWT-Kon-
stanten. Links werden dann die Ergebnisse gezeigt. Im unteren Bereich können Ereig-
nisse protokolliert werden (hier Selektions- und Fokusereignisse).

ja bereits die Eclipse-Beispieldateien installiert. Jetzt müssen wir nur


die entsprechende Beispielapplikation starten. Dazu rufen wir die
Funktion Window>Show View>Other ... auf. Im folgenden Dialog
wählen wir die Applikation SWT Examples>SWT Controls aus. Diese
Applikation erscheint dann auch prompt im Fenster rechts unten (siehe
Abb. 8–2). Damit wir etwas mehr Platz haben, maximieren wir diese
Applikation mit einem Doppelklick auf den Reiter.
Da diese Applikation die verschiedenen Widgets ganz hervorra-
gend visualisieren kann, werden wir in den folgenden Abschnitten auf
die Abbildung einiger Widgets verzichten. Eine weitere Beispielappli-
8.5 Widgets 157

kation Custom Controls liefert eine Übersicht über die nicht-nativen


Widgets (siehe Abschnitt 8.5.13). Beide Applikationen erlauben Ihnen
außerdem mit Ereignissen und Listenern zu experimentieren.

8.5.2 Displays, Shells und Monitore

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

Die Klasse Display ist das Verbindungsglied zwischen Java-Applika-


tion und Betriebssystem. Jede Applikation mit einem SWT-GUI
erzeugt mindestens eine Instanz dieser Klasse. Genauer gesagt: Benutzt
man nur einen einzigen Thread, so ist auch nur eine Display-Instanz
notwendig. Führt man jedoch GUI-Operationen in mehreren Threads
aus, so wird für jeden dieser Threads eine eigene Display-Instanz
erzeugt. Mit der statischen Methode Display.getCurrent() kann man
die aktive Display-Instanz für den jeweiligen Thread herausfinden.
Im Unterschied zu AWT und Swing erzwingt das SWT, dass alle SWT-Thread
SWT-Objekte nur von dem Thread benutzt werden dürfen, in dem sie
erzeugt wurden. Trotzdem ist es möglich, auch unter SWT mit mehre-
ren Threads zu arbeiten. Die Klasse Display stellt zu diesem Zweck die
Methoden syncExec() und asyncExec() zur Verfügung, mit denen
Runnable-Objekte dem jeweiligen Thread der Display-Instanz zur Aus-
führung übergeben werden können. Wir werden in den folgenden
Kapiteln noch des Öfteren auf diese Technik zurückgreifen.
Außerdem besitzt Display Methoden, um GUI-Eigenschaften des
jeweiligen Betriebssystems abzufragen wie getSystemFont() oder get-
DoubleClickTime(), und verwaltet die Ressourcen des Betriebssystems.
Darüber hinaus gibt es Methoden für die Widget-Verwaltung, z.B.
getActiveShell() oder getFocusControl(). Mit der Methode map()
können Koordinaten von Punkten oder Rechtecken vom Koordinaten-
system des einen Control-Elements in das Koordinatensystem eines
anderen Control-Elements übersetzt werden.
Eine Display-Instanz erzeugt Ereignisse der Klasse Event (siehe Ereignisse
Abschnitt 8.4.3) mit dem Typ SWT.Open bzw. SWT.Close. Per Programm
lassen sich Ereignisse vom Typ KeyDown, KeyUp, MouseDown, MouseUp
oder MouseMove über die Methode post() erzeugen. So können Benut-
zeroberflächen automatisiert werden. Hier ist ein Beispiel:
158 8 Das SWT

// Fensterkoordinate (100,50) in das Display-


// Koordinatensystem übersetzen
Point coord = display.map(shell, null, 100, 50);
event = new Event();
event.type = SWT.MouseMove;
event.x = coord.x;
event.y = coord.y;
display.post(event);

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).

Shell-Typen Es gibt zwei verschiedene Arten von Shell-Instanzen: Top-Level-Shells


für das Hauptfenster der Applikation und Dialog-Shells, die von ande-
ren Shells abhängig sind. Welche Instanzenart erzeugt wird, hängt von
dem Parameter des Konstruktors ab: Wird eine Display-Instanz über-
geben, wird eine Top-Level-Shell erzeugt; wird eine andere Shell-
Instanz übergeben, wird eine Dialog-Shell erzeugt.
Beim Erzeugen einer Shell kann optional ein Stilparameter mit
angegeben werden. Dabei können die folgenden SWT-Konstanten
benutzt werden:

SWT.NONE Standardfenster, betriebssystemabhängig.


SWT.BORDER Fenster hat einen Rand (plattformabhängig).
SWT.CLOSE Fenster hat eine Titelzeile mit einem Button zum Schließen des Fensters.
SWT.MIN Fenster hat eine Titelzeile mit einem Button zum Minimalisieren des Fensters.
SWT.MAX Fenster hat eine Titelzeile mit einem Button zum Maximalisieren des Fensters.
SWT.NO_TRIM Fenster hat weder Titelzeile noch Rand.
SWT.RESIZE Fenstergröße kann durch Ziehen mit der Maus verändert werden.
SWT.TITLE Fenster hat eine Titelzeile.
Kombination von Stilelementen für das Top-Level-Fenster einer Applikation
SWT.SHELL_TRIM
(SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE).
Kombination von Stilelementen für Dialog-Fenster
SWT.DIALOG_TRIM
(SWT.CLOSE | SWT.TITLE | SWT.BORDER).
8.5 Widgets 159

Daneben gibt es noch die Konstanten SWT.APPLICATION_MODAL, SWT.


MODELESS, SWT.PRIMARY_MODAL und SWT.SYSTEM_MODAL, die das modale
Verhalten des Fensters bestimmen. Ein modales Fenster, das sich im
Vordergrund befindet, lässt keine anderen Fenster der Applikation
bzw. des Systems in den Vordergrund kommen. Ein solches Fenster
sollte so instrumentiert sein, dass der Anwender das Fenster auch
schließen kann.
Wird kein Stilparameter angegeben, so hängt der Standardstil sowohl
vom Betriebssystem als auch von der Art der Shell ab. So ist z.B. bei
WindowsCE der Standardstil SWT.NONE. Bei anderen Windows-Versio-
nen dagegen ist es SHELL_TRIM bzw. DIALOG_TRIM, je nach Art der Shell.
Mit der Methode setImage() kann ein Icon spezifiziert werden,
welches das Fenster repräsentiert, wenn es minimalisiert wird. Übli-
cherweise wird ein solches Icon auch in der Titelleiste gezeigt. Mit der
Methode setImages() können Icons in verschiedenen Auflösungen
spezifiziert werden, aus denen sich dann die Plattform für den jeweili-
gen Zweck das beste Icon aussuchen kann.

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.

Übrigens hat eine Shell nicht notwendigerweise eine rechteckige Region


Gestalt. Durch Zuweisen einer Region kann man einer Shell eine belie-
bige Gestalt geben. Zunächst erzeugt man dazu eine Region-Instanz,
der man dann mit add() Umrisslinien hinzufügt, oder aus der man mit
subtract() auch wieder Teilbereiche entfernen kann. So kann man
sogar »löchrige« Regions erzeugen. Mit setRegion() weist man dann
die Region-Instanz der Shell zu. Wirksam wird eine solche Zuweisung
aber nur für Shells vom Typ SWT.NO_TRIM. Das bedeutet, dass man für
diese Shell das Schließen, Verschieben und das Verändern der Größe
selbst organisieren muss. Abschnitt 10.3 zeigt die Konstruktion einer
solchen irregulären Shell in der Praxis.
Um ein erstes Beispielprogramm mit einer Shell zu schreiben, rich- Die Workbench einrichten
ten wir in der Workbench zunächst ein neues Projekt widgets ein und
legen in diesem Projekt die Klasse widgetTest an. Diesem Projekt sind
zunächst die SWT-Klassen nicht bekannt. Wir müssen die SWT-Biblio-
160 8 Das SWT

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

zu finden. Unter Linux-GTK benötigt man stattdessen zwei JAR-


Dateien:
/opt/eclipse/plugins/org.eclipse.swt.gtk_3.0.0/ws/gtk/swt.jar
/opt/eclipse/plugins/org.eclipse.swt.gtk_3.0.0/ws/gtk/swt-pi.jar

Für andere Betriebs- und Fenstersysteme gilt Ähnliches.1


Das erste SWT-Programm Wir können nun ein erstes Programm schreiben. Zunächst erzeu-
gen wir eine neue Display-Instanz. Dann erzeugen wir eine Top-Level-
Shell. Dies geschieht, indem man dem Konstruktor die Display-Instanz
als Parameter übergibt. Im Gegensatz dazu wird dem Konstruktor
einer Dialog-Shell eine andere Shell als Parameter übergeben.
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class widgetTest {

public static void main(String[] args) {


// Display-Instanz erzeugen
final Display display = new Display();
// TopLevel-Shell erzeugen (display als parent)
final Shell toplevelShell = new Shell(display);
// Titelzeile setzen
toplevelShell.setText("TopLevel.Titelzeile");
// Shell anzeigen
toplevelShell.open();
// Dialog-Shell erzeugen (toplevelShell als parent)
final Shell dialogShell = new Shell(toplevelShell);
// Titelzeile setzen
dialogShell.setText("Dialog.Titelzeile");
// Shell anzeigen
dialogShell.open();
// Warten bis TopLevel-Shell geschlossen wird
while (!toplevelShell.isDisposed()) {
// Prüfen ob Ereignisse warten
if (!display.readAndDispatch()) display.sleep();
}
}
}

1. Je nach Eclipse-Version müssen Sie diese Pfade ggf. anpassen!


8.5 Widgets 161

Wichtig ist hier die while-Schleife am Ende des Programms, denn im


SWT ist der Programmierer für die Organisation der Ereignisschleife
zuständig. Ohne diese Schleife würde die Benutzeroberfläche »einfrie-
ren«. Hier bewirken wir mit der Methode readAndDispatch(), dass die
Display-Instanz ein anstehendes Ereignis liest und an die entsprechen-
den GUI-Elemente weitergibt. Stehen keine weiteren Ereignisse mehr
an, legen wir den Thread mit der Methode sleep() schlafen, bis ein
neues Ereignis eintrifft.
Nun können wir dieses kleine Programm ausführen. Dazu müssen SWT Ablauf-Konfiguration
wir eine neue Run-Konfiguration vom Typ Java-Applikation erzeugen.
Das geschieht in der Run-Konfiguration, die Sie sowieso neu anlegen
müssen. Rufen Sie also Run>Run ... auf und betätigen Sie die New-
Taste, um eine neue Konfiguration für eine Java-Applikation anzule-
gen. Als Name geben Sie widgetTest ein, dergleichen unter Main Class.
Unter Projekt geben Sie widgets ein.
Allerdings reichen diese Angaben für das erfolgreiche Ausführen
des Programms noch nicht aus. Das SWT benötigt für die Ausführung
native Module, deren Ort der Java Virtual Machine noch mitgeteilt
werden müssen. Der Pfad der entsprechenden Modulbibliothek wird
in der Run-Konfiguration auf der Seite Arguments unter VM Argu-
ments eingegeben. Im Falle von Windows ist der folgende Parameter
einzugeben:
-Djava.library.path=
C:\eclipse\plugins\org.eclipse.swt.win32_3.0.0\os\win32\x86

Unter Linux-GTK ist es:


-Djava.library.path=
/opt/eclipse/plugins/org.eclipse.swt.gtk_3.0.0/os/linux/x86

Unter anderen Betriebs- bzw. Fenstersystemen finden Sie die Modul-


bibliothek an entsprechender Stelle.2
Anschließend dürfen Sie dann wirklich Run drücken, und wenn
alles gut geht, das neue Fenster bewundern.

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.

2. Je nach Eclipse-Version müssen Sie diese Pfade ggf. anpassen!


162 8 Das SWT

Eclipse unterstützt diese Hardware mit der SWT-Klasse Monitor.


Mit der Display-Methode getMonitors() erhält man ein Array der
angeschlossenen Bildschirme und mit der Methode getPrimaryMoni-
tor() erhält man den Primärbildschirm. Insbesondere verfügt die
Klasse Monitor über die Methoden getBounds() und getClientArea(),
mit der die Position und Größe des Bildschirms bzw. des nutzbaren
Bereiches abgefragt werden kann. So gehört z.B. die Windows-Tas-
kleiste nicht zum nutzbaren Bereich.
SWT-Applikationen, welche Dialoge, Menüs usw. positionieren,
verschieben oder in der Größe verändern, sollten diese Methoden nut-
zen, um z.B. zu erreichen, dass ein Dialog oder ein Menü jeweils nur
auf dem Primärbildschirm erscheint und nicht auf zwei Bildschirme
aufgespalten wird. In Abschnitt 10.3 zeigen wir, wie die Klasse Moni-
tor in der Praxis angewandt wird.

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:

ColorDialog Dialog zum Auswahl einer Farbe.


DirectoryDialog Dialog zur Auswahl eines Verzeichnisses im Dateisystem.
Dialog zur Auswahl einer Datei. Durch Stilparameter kann festgelegt werden, zu
FileDialog
welchem Zweck die Datei ausgewählt wird: SWT.OPEN und SWT.SAVE.
FontDialog Dialog zur Auswahl einer Schriftart.
Dialog zur Ausgabe einer Mitteilung. Durch Stilparameter kann festgelegt werden, mit
welchen Knöpfen der Dialog instrumentiert wird. Dabei sind folgende Kombinationen
möglich:
SWT.OK
SWT.OK | SWT.CANCEL)
SWT.YES | SWT.NO)
SWT.YES | SWT.NO | SWT.CANCEL)
MessageBox SWT.RETRY | SWT.CANCEL)
SWT.ABORT | SWT.RETRY | SWT.IGNORE)
Außerdem kann festgelegt werden, welches Icon mit abgebildet wird:
SWT.ICON_ERROR
SWT.ICON_INFORMATION
SWT.ICON_QUESTION
SWT.ICON_WARNING
SWT.ICON_WORKING
Dialog zur Auswahl eines Druckers und für die Druckereinrichtung. Siehe auch
PrintDialog
Abschnitt 8.9.

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 class widgetTest {

public widgetTest() {
super();
}

public static void main(String[] args) {


// Display-Instanz erzeugen
final Display display = new Display();
// TopLevel-Shell erzeugen (display als parent)
final Shell toplevelShell = new Shell(display);
// Titelzeile setzen
toplevelShell.setText("TopLevel.Titelzeile");
// Shell anzeigen
toplevelShell.open();
while (true) {
// MessageBox erzeugen
MessageBox box =
new MessageBox(
toplevelShell,
SWT.RETRY
| SWT.CANCEL
| SWT.APPLICATION_MODAL
| SWT.ICON_QUESTION);
// Titel setzen
box.setText("Test");
// Nachricht setzen
box.setMessage("Wollen Sie das noch einmal versuchen?");
// MessageBox öffnen
if (box.open() == SWT.CANCEL)
break;
}
}
}

Abb. 8–4 Die im Beispiel erzeugte Message-Box unter Windows XP.


8.5 Widgets 165

Für eigene, komplexere Dialoge wird man allerdings in der Regel


anstelle der oben besprochenen SWT-Klasse Dialog die gleichnamige
JFace-Klasse (siehe Kapitel 9) verwenden, die wesentlich komfortabler
in der Handhabung ist. Fast alle Dialoge in der Eclipse-Workbench
bauen auf JFace-Dialogen auf und nicht auf dem SWT-Dialog.

8.5.4 Composites, Groups und Canvas


Normalerweise bringt man Widgets auf einer Shell nicht direkt an, Composites
sondern setzt noch eine oder mehrere Hierarchien von Composite-
Instanzen dazwischen. Composites dienen dem Gruppieren von Wid-
gets. So wird man beispielsweise die Buttons eines Dialogs mit Hilfe
von Composites zu Gruppen zusammenfassen (besonders wichtig bei
Radiobuttons, bei denen sich die Buttons innerhalb eines Composites
gegenseitig auslösen) oder wird mehrere Eingabefelder sowie Labels
aus Layout- und Navigationsgründen zusammen gruppieren.
Will man nun Widgets einem Composite hinzufügen, so sucht man
zunächst vergebens nach einer add()-Methode. Stattdessen werden
GUI-Oberflächen auf andere Art und Weise konstruiert. Beim Anlegen
eines neuen Widgets wird das übergeordnete Composite als Parameter
übergeben, und damit ist das Widget dem Composite zugeordnet. Die
Anordnung auf dem Composite erfolgt dann in der Reihenfolge, in der
die einzelnen Widgets erzeugt wurden.
Da auch Composites Widgets sind, wird auch beim Erzeugen einer
Composite-Instanz ein übergeordnetes Composite angegeben (auch
Shells sind Composites). In der Regel wird man wichtige Eigenschaften
wie Hintergrund- und Vordergrundfarbe sowie Schriftart vom überge-
ordneten Composite übernehmen, wie im folgenden Code gezeigt.
Auch die Position und Abmessungen des Composite können relativ
zum übergeordneten Composite angegeben werden.
// neue Composite-Instanz erzeugen
final Composite composite = new Composite(parent,0);
// Eigenschaften von übergeordnetem Composite übernehmen
composite.setBackground(parent.getBackground());
composite.setForeground(parent.getForeground());
composite.setFont(parent.getFont());
// Position und Abmessungen setzen
composite.setBounds(X,Y,WIDTH,HEIGHT);

Optional kann im Konstruktor als zweiter Parameter die Konstante


SWT. NO_RADIO_GROUP angegeben werden, wenn man Radiobuttons
gruppieren will, ohne ihr Auslöseverhalten zu beeinflussen.
Eine Unterklasse der Klasse Composite ist die Klasse Group. Diese Groups
zeichnet zusätzlich eine Randlinie. Das Aussehen dieser Linie kann mit
166 8 Das SWT

den Stilkonstanten SWT.SHADOW_ETCHED_IN, SWT.SHADOW_ETCHED_OUT,


SWT.SHADOW_IN, SWT.SHADOW_OUT und SWT.SHADOW_NONE gesteuert wer-
den, sofern das Betriebssystem das unterstützt. Mit setText() kann ein
Titel in die Randlinie hineingesetzt werden. In vielen Fällen ist es güns-
tiger, eine Group anstelle eines Composite zu verwenden. Insbesondere
in komplexen Dialogen erlauben Groups eine bessere Navigation mit
Hilfe der Tastatur, sind also benutzerfreundlicher für Behinderte (siehe
auch Abschnitt 8.11).
Für Composite- und Group-Instanzen, die andere Widgets enthalten,
kann ein Layout gesetzt werden (siehe Abschnitt 8.6).
Canvas Die Klasse Canvas ist eine Unterklasse von Composite. Sie ist nicht
dazu bestimmt, andere GUI-Elemente zu enthalten, sondern dient als
Zeichenfläche für Grafikoperationen. Insbesondere ist es möglich, auf
der Grundlage von Canvas mit geeigneten Zeichenoperationen eigene
GUI-Elemente zu realisieren.
Außerdem bietet Canvas Unterstützung für eine Schreibmarke an
(setCaret() und getCaret()).

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:

Button mit einem kleinen Pfeil. Üblicherweise


SWT.ARROW
verwendet, um kleine Menüs einzublenden.
Kästchen (Checkbox) zum Ankreuzen. Der
Text befindet sich neben dem Kästchen.
SWT.CHECK

Taste mit Beschriftung auf der Tastenfläche.

SWT.PUSH

Radiobutton. Mehrere Radiobuttons innerhalb


einer Gruppe lösen sich gegenseitig aus.
SWT.RADIO

Wie SWT.PUSH, nur dass beim ersten Klick die


Taste gedrückt bleibt und beim zweiten Klick
SWT.TOGGLE wieder auslöst.
8.5 Widgets 167

Daneben gibt es noch Möglichkeiten, das Aussehen und die Ausrich-


tung der Buttons zu beeinflussen:

SWT.FLAT Der Button wird nicht dreidimensional gezeichnet, sondern »flach«.


SWT.BORDER Der Button wird mit einem Rahmen umgeben.

Diese Attribute werden allerdings nicht von allen Plattformen unter-


stützt.
Mit den Methoden setText() und setImage() können Buttons mit Texte und Bilder
einem Text und einer Grafik versehen werden. Bei den Arten PUSH und
TOGGLE erscheinen Text und Grafik auf der Fläche des Buttons, bei den
Arten CHECK und RADIO rechts neben dem Button. Buttons der Art ARROW
zeigen weder Text noch Grafik an.
Die beiden Methoden schließen sich gegenseitig aus. Entweder:
final Button button = new Button(composite,SWT.PUSH);
button.setText("Drück mich!");
// Auf Klickereignisse reagieren
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Taste wurde gedrückt");
}
});

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();
}
});

Im zweiten Fall war es notwendig, die Image-Ressource wieder freizu-


geben, sobald sie nicht mehr gebraucht wurde, denn Bilder belegen
Ressourcen im Betriebssystem.
168 8 Das SWT

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.

8.5.6 Schieberegler, Skalen und Fortschrittsbalken

Slider und Scale

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)

Abb. 8–5 Slider und Scale, jeweils innerhalb einer Group.

Die folgenden Stilkonstanten beeinflussen das Aussehen dieser Widgets:

SWT.HORIZONTAL
Horizontale oder vertikale Darstellung
SWT.VERTICAL
Skalen werden mit einem Rahmen umgeben. Wirkungslos bei
SWT.BORDER
Slider.

Das folgende Beispiel erzeugt einen einfachen Schieberegler:


final Slider slider = new Slider(composite,SWT.HORIZONTAL);
// Minimalwert setzen
slider.setMinimum(0);
// Maximalwert setzen
slider.setMaximum(1000);
// Inkrement für Pfeiltasten setzen
slider.setIncrement(50);
// Inkrement für Klick auf Schiebefläche setzen
slider.setPageIncrement(200);
// aktuelle Position setzen
slider.setSelection(500);
// Größe des Handgriffs setzen
8.5 Widgets 169

slider.setThumb(200);
// Auf Schiebeereignisse reagieren
slider.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Schieberegler verstellt: "
+slider.getSelection());
}
});

Mit entsprechenden get...-Methoden können diese Werte auch abge-


fragt werden. Scale hat die gleichen Methoden, allerdings gibt es hier
die Methoden setThumb() und getThumb() nicht.

ProgressBar

Die Klasse ProgressBar dient zur Darstellung von Fortschrittsbalken.


Das API ähnelt stark dem der Klasse Slider, allerdings erzeugen Pro-
gressBar-Instanzen keine Ereignisse. Außerdem kommen noch zwei
Stilkonstanten hinzu:
! SWT.SMOOTH bewirkt, dass der Balken nicht durchbrochen gezeichnet
wird.
! SWT.INDETERMINATE bewirkt, dass der Balken endlos durchläuft.
Hier ist also die Anwendung von setSelection() nicht erforderlich.
Die Anwendung dieser Klasse in der Praxis ist nicht ganz einfach, da
der Balken nur aktualisiert wird, wenn die Ereignisschleife nicht blo-
ckiert ist.

Scrollable und ScrollBar

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.

8.5.7 Textfelder und Beschriftungen

Instanzen der Klasse Text werden benutzt, um Text anzuzeigen, einzu-


geben oder zu verändern (Abb. 8–6). Mit Hilfe von Stilkonstanten
können Text-Instanzen konfiguriert werden:
170 8 Das SWT

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.

Instanzen der Klasse Text erzeugen folgende Ereignistypen:

Wenn die Enter-Taste gedrückt wird, wird die SelectionListener-


SelectionEvent
Methode widgetDefaultSelected() aufgerufen
ModifyEvent Nachdem Text verändert wurde.
Bevor Text verändert wird. Durch Zuweisung des Werts false an
VerifyEvent die Variable doit des Ereignisobjekts kann die Veränderung des
Textes verhindert werden (Veto).

Das folgende Beispiel erzeugt ein Textfeld mit einem VerifyListener


für die Gültigkeitsprüfung:
final Text text = new Text(composite,SWT.SINGLE);
text.setText("Eingabetext");
text.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
System.out.println("Enter gedrückt: "+text.getSelection());
}
});
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
System.out.println("Text nach Modifikation: "+text.getText());
}
});
text.addVerifyListener(new VerifyListener() {
public void verifyText(VerifyEvent e) {
String s = text.getText();
8.5 Widgets 171

System.out.println("Text vor Modifikation: "+s);


// Veto: Eingaben länger als 10 Zeichen verhindern
if (s.length() >= 10) e.doit = false;
}
});

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.

SWT.SEPARATOR Stellt eine horizontale oder vertikale Linie dar.


SWT.HORIZONTAL
Stellt Orientierung einer Linie fest.
SWT.VERTICAL
SWT.SHADOW_IN
SWT.SHADOW_OUT Schattendarstellung einer Linie.
SWT.SHADOW_NONE
SWT.CENTER
SWT.LEFT Ausrichtung eines Text- oder Bildlabels.
SWT.RIGHT
SWT.WRAP Automatischer Umbruch bei Textlabels.

Der folgende Code zeigt die Erzeugung eines Textlabels:


final Label label = new Label(composite, SWT.NULL);
label.setText("Eingabe");

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.

8.5.8 Tabellen, Listen und Combos

Tabellen und Listen dienen der tabellarischen Darstellung von Inhal-


ten, wobei auch die Auswahl von Elementen unterstützt wird. Eine
Platz sparende Variante für die Listenauswahl sind Combos (Abb. 8–7).
172 8 Das SWT

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.

Table-Instanzen erzeugen SelectionEvent-Objekte, wenn ein Tabellen-


element selektiert wird. Beim SelectionListener wird die Methode
widgetDefaultSelected() aufgerufen, wenn auf einem Tabellenele-
ment Enter gedrückt oder ein Doppelklick ausgeführt wurde.
Tabellenspalten Zur Konfiguration der einzelnen Tabellenspalten werden jeder
Tabelle TableColumn-Objekte zugeordnet. Das geschieht auf die gleiche
Weise, in der Widgets auf einem Composite angebracht werden. Die
Table-Instanz wird dem TableColumn()-Konstruktor als Parameter
8.5 Widgets 173

übergeben. Dann kann man für jedes TableColumn-Objekt noch mit


setText() eine Spaltenüberschrift und mit setWidth() die Breite in
Pixeln setzen.
Die Breite von Tabellenspalten kann vom Benutzer verändert wer-
den. Auch sind die Spaltenköpfe als Tasten ausgelegt. Folglich erzeu-
gen TableColumn-Objekte eine Anzahl von Ereignissen. Ein ControlE-
vent wird erzeugt, wenn die Tabellenspalte verschoben oder in der
Größe verändert wird. Ein SelectionEvent wird erzeugt, wenn der
Spaltenkopf angeklickt wird.
Die Ausrichtung der Spalten kann durch die Stilkonstanten
SWT.LEFT, SWT.CENTER und SWT.RIGHT bestimmt werden. Mit der
Methode showColumn() können wir eine bestimmte Spalte der Tabelle
in den sichtbaren Anzeigebereich bringen.
Auf ähnliche Weise werden die Tabellenzeilen als TableItem- Tabellenzeilen
Objekte erzeugt. Mit der Methode setText() wird der Inhalt der
Tabellenzeilen gesetzt. Als Parameter kann ein String oder – im Fall
mehrspaltiger Tabellen – ein Stringarray angegeben werden. Seit
Eclipse V3 kann man sogar mit setForeground(), setFont() bzw. set-
Background() die Textfarbe, die Schriftart bzw. Hintergrundfarbe für
jedes einzelne TableItem setzen.
Mit den Table-Methoden setHeaderVisible() und setLinesVisi-
ble() können die Spaltenüberschriften und Trennlinien angezeigt oder
unterdrückt werden.
Im folgenden Code erzeugen wir eine Tabelle mit drei Spalten und
zwei Zeilen:
final Table table = new Table(composite,
SWT.SINGLE | SWT.H_SCROLL |
SWT.V_SCROLL | SWT.BORDER |
SWT.FULL_SELECTION );
// Drei Tabellenspalten erzeugen
final TableColumn col1 = new TableColumn(table,SWT.LEFT);
col1.setText("Spalte 1");
col1.setWidth(80);
final TableColumn col2 = new TableColumn(table,SWT.LEFT);
col2.setText("Spalte 2");
col2.setWidth(80);
final TableColumn col3 = new TableColumn(table,SWT.LEFT);
col3.setText("Spalte 3");
col3.setWidth(80);
// Spaltenköpfe und Trennlinien sichtbar machen
table.setHeaderVisible(true);
table.setLinesVisible(true);
// Zwei Tabellenreihen erzeugen
174 8 Das SWT

final TableItem item1 = new TableItem(table,0);


item1.setText(new String[] {"a","b","c"});
final TableItem item2 = new TableItem(table,0);
item2.setText(new String[] {"d","c","e"});
// SelectionListener hinzufügen
table.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
processSelection("Enter gedrückt: ");
}
public void widgetSelected(SelectionEvent e) {
processSelection("Tabellenelement ausgewählt: ");
}
private void processSelection(String message) {
// Ausgewählte Tabellenzeilen holen
TableItem[] selection = table.getSelection();
// Wegen SWT.SINGLE ist nur eine Zeile ausgewählt
TableItem selectedRow = selection[0];
// Die einzelnen Tabellenelemente für Ausgabe aufbereiten
String s = selectedRow.getText(0)+", "+
selectedRow.getText(1)+", "+selectedRow.getText(2);
System.out.println(message + s);
}
});

Listen

Will man lediglich eine einspaltige Liste von String-Elementen zur


Selektion anbieten, so gibt es mit der Klasse List eine einfachere Mög-
lichkeit. List-Instanzen erzeugen die gleichen Ereignisse wie Table-
Instanzen, jedoch wird die Methode widgetDefaultSelected() nur bei
einem Doppelklick aufgerufen. Mit den SWT-Konstanten SWT.SINGLE
und SWT.MULTI kann bestimmt werden, ob nur eine oder aber mehrere
Listeneinträge selektiert werden können.
Im folgenden Code bauen wir eine Liste mit drei Einträgen auf.
Wir erlauben und verarbeiten die Selektion mehrerer Einträge:
final List list = new List(composite,SWT.MULTI);
list.add("Element1");
list.add("Element2");
list.add("Element3");
list.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
processSelection("Enter gedrückt: ");
}
public void widgetSelected(SelectionEvent e) {
processSelection("Listeneintrag ausgewählt: ");
}
8.5 Widgets 175

private void processSelection(String message) {


// Ausgewählte Element holen
String[] selection = list.getSelection();
// Für die Ausgabe aufbereiten
StringBuffer sb = new StringBuffer();
for (int i = 0; i < selection.length; i++) {
sb.append(selection[i]+" ");
}
System.out.println(message + sb);
}
});

Combos

Schließlich gibt es noch die Klasse Combo, die Listenauswahl und Text-
eingabe kombiniert.
Instanzen der Klasse Combo erzeugen folgende Ereignistypen:

Wenn die Enter-Taste gedrückt wird, wird die Selection-


Listener-Methode widgetDefaultSelected() aufgerufen.
SelectionEvent
Wird ein Listenelement ausgewählt, so wird die Methode
widgetSelected() aufgerufen.
Wird aufgerufen, wenn Text durch Tastatureingabe oder
ModifyEvent
Listenauswahl verändert wird.

Die folgenden Stilkonstanten beeinflussen die Funktionsweise und das


Aussehen der Combo-Instanzen:

Die Auswahlliste wird erst beim Klick auf die Pfeiltaste


SWT.DROP_DOWN
angezeigt.
Es ist nur möglich, vorgegebene Werte auszuwählen, jedoch
SWT.READ_ONLY
nicht, Werte im Textfeld einzugeben.
SWT.SIMPLE Die Auswahlliste ist immer sichtbar.

Das folgende Beispiel erzeugt ein solches Combo-GUI-Element:


final Combo combo = new Combo(composite,SWT.DROP_DOWN);
// Listenelement erzeugen
combo.add("Element1");
combo.add("Element2");
combo.add("Element3");
// Vorbelegung des Textfeldes
combo.setText("Select");
// SelectionListener hinzufügen
combo.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
176 8 Das SWT

System.out.println("Enter gedrückt: " + combo.getText());


}
public void widgetSelected(SelectionEvent e) {
System.out.println("Listeneintrag ausgewählt: " +
combo.getText());
}
});
// ModifyListener hinzufügen
combo.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
System.out.println("Text wurde modifiziert: "+combo.getText());
}
});

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:

SWT.SINGLE Der Benutzer kann nur eine bzw. mehrere Baumknoten


SWT.MULTI auswählen.
Vor jedem Baumknoten wird eine Checkbox abgebildet. Der
SWT.CHECK Zustand der Checkbox kann mit den Methoden setChecked()
und getChecked() gesetzt bzw. abgefragt werden.

Abb. 8–8 Zwei Bäume: links nur mit Textknoten; rechts wurden den Textknoten Bilder
zugewiesen.
8.5 Widgets 177

Tree-Instanzen erzeugen folgende Ereignistypen:

Bei einem Doppelklick oder Drücken der Enter-Taste wird


die SelectionListener-Methode widgetDefaultSelected()
SelectionEvent aufgerufen.
Wird ein Knoten ausgewählt, so wird die Methode
widgetSelected() aufgerufen.
Wird ein Knoten expandiert, wird die Methode treeExpanded()
der TreeListener-Instanz aufgerufen. Wird ein Knoten
TreeEvent kollabiert, wird treeCollapsed() aufgerufen. Der entspre-
chende Knoten wird im Feld item des TreeEvent-Objekts
übergeben.

Die einzelnen Baumknoten werden als TreeItem-Instanzen erzeugt.


Dabei wird dem TreeItem()-Konstruktor entweder der Baum oder ein
anderer Baumknoten als Argument übergeben. Textinhalte für TreeI-
tem-Instanzen können mit setText() gesetzt werden, wobei mit set-
Font() die Schriftart des Textes gesetzt werden kann. Zusätzlich zum
Text kann mit setImage() jeder TreeItem-Instanz eine Grafik zugeord-
net werden. Wie bei Buttons (Abschnitt 8.5.6) sollten alle Image-Instan-
zen wieder freigegeben werden, wenn sie nicht mehr benötigt werden.
Der folgende Code erzeugt einen einfachen Baum mit drei Knoten,
wobei der erste Knoten zwei Kindknoten hat:
final Tree tree = new Tree(composite,SWT.SINGLE);
// Oberste Knotenebene erzeugen
final TreeItem node1 = new TreeItem(tree,SWT.NULL);
node1.setText("Knoten 1");
final TreeItem node2 = new TreeItem(tree,SWT.NULL);
node2.setText("Knoten 2");
final TreeItem node3 = new TreeItem(tree,SWT.NULL);
node3.setText("Knoten 3");
// Zweite Knotenebene erzeugen
final TreeItem node11 = new TreeItem(node1,SWT.NULL);
node11.setText("Knoten 1.1");
final TreeItem node12 = new TreeItem(node1,SWT.NULL);
node12.setText("Knoten 1.2");
// SelectionListener hinzufügen
tree.addSelectionListener(new SelectionAdapter() {
public void widgetDefaultSelected(SelectionEvent e) {
System.out.println("Enter gedrückt: " +
tree.getSelection()[0].getText());
}
public void widgetSelected(SelectionEvent e) {
System.out.println("Baumknoteneintrag ausgewählt: " +
tree.getSelection()[0].getText());
}
178 8 Das SWT

});
// 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.10 Verschiebbare Trennleisten

Die Klasse Sash ist für verschiebbare Trennleisten verantwortlich.


Diese Trennleisten können benutzt werden, um ein Composite in ver-
schiedene Bereiche aufzuteilen. Die Trennleiste kann vom Benutzer
verschoben werden, so dass sich die Größe der Teilbereiche verändert.
Das verlangt einigen Aufwand an Ereignisbehandlung, da der Sash
nicht selbst eine Repositionierung der Nachbarelemente vornimmt.
Sash-Instanzen erzeugen Ereignisse vom Typ SelectEvent. Die Aus-
richtung einer Trennleiste kann mit den Stilkonstanten SWT.HORIZONTAL
und SWT.VERTICAL bestimmt werden (Abb. 8–9).
Anstelle dieser manuellen Methode kann auch die Klasse SashForm
verwendet werden, um durch verschiebbare Rahmen getrennte Kom-
ponenten untereinander zu koordinieren (siehe Abschnitt 8.5.13).

8.5.11 Pultordner

Die Klasse TabFolder implementiert einen Pultordner, also eine Ein-


heit, bei der Seiten über das Anklicken von Reitern ausgewählt werden
können. Jede TabFolder-Instanz ist ein Composite, das ein oder mehrere
TabItem-Instanzen enthalten kann. Jedes TabItem-Objekt entsprícht
einem Reiter, die Beschriftung des Reiters kann mit der Methode set-
Text() gesetzt werden. Mit setControl() kann jedem TabItem-Objekt
eine Control-Instanz (z.B. ein Composite) zugeordnet werden. Diese
wird zur Anzeige gebracht, wenn das entsprechende TabItem-Objekt
8.5 Widgets 179

selektiert wird. Die Control-Instanz muss auf dem TabFolder erzeugt


worden sein (also mit Angabe der TabFolder-Instanz im Konstruktor).
TabFolder unterstützt lediglich die Stilkonstante SWT.BORDER.
TabFolder-Instanzen erzeugen SelectionEvents, wenn ein TabItem
ausgewählt wird.
Der folgende Code zeigt als Beispiel einen Pultordner mit zwei Rei-
tern:
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
public class widgetTest {

public static void main(String[] args) {


// Display-Instanz erzeugen
final Display display = new Display();
// TopLevel-Shell erzeugen (display als parent)
final Shell toplevelShell = new Shell(display);
// Titelzeile setzen
toplevelShell.setText("TopLevel.Titelzeile");
// Shell komplett ausfüllen
toplevelShell.setLayout(new FillLayout());
// Pultordner erzeugen
TabFolder folder = new TabFolder(toplevelShell, SWT.NONE);
// Selektionsereignisse protokollieren
folder.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println(
"Reiter selektiert: " + ((TabItem)
(e.item)).getText());
}
});
// Pultordner komplett ausfüllen
folder.setLayout(new FillLayout());
Composite page1 = createTabPage(folder, "tab1");
// Auf page1 können nun weitere GUI-Elemente angeordnet werden
// ...
Composite page2 = createTabPage(folder, "tab2");
// Auf page2 können nun weitere GUI-Elemente angeordnet werden
// ...
// Shell anzeigen
toplevelShell.open();
// Ereignisschleife
while (!toplevelShell.isDisposed()) {
180 8 Das SWT

if (!display.readAndDispatch())
display.sleep();
}
}

private static Composite createTabPage(TabFolder folder,


String label) {
// Einen Reiter erzeugen und beschriften
TabItem tab = new TabItem(folder, SWT.NONE);
tab.setText(label);
// Einen Composite als Seite erzeugen
Composite page = new Composite(folder, SWT.NONE);
// ... und dem Reiter zuordnen
tab.setControl(page);
return page;
}
}

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.

8.5.12 Werkzeugleisten und Menüs

Werkzeugleisten

Mit der Klasse ToolBar können Werkzeugleisten implementiert wer-


den. Jede ToolBar-Instanz ist ein Composite, das ein oder mehrere Too-
lItem-Instanzen enthält.
Mit den folgenden Stilkonstanten kann das Erscheinungsbild von
Werkzeugleisten bestimmt werden:

SWT.FLAT Zweidimensionale statt dreidimensionaler Darstellung.


Plattformabhängig.
SWT.WRAP Automatischer Umbruch
SWT.RIGHT Rechtsbündige Ausrichtung
SWT.HORIZONTAL
Horizontale oder vertikale Ausrichtung
SWT.VERTICAL

ToolItem-Instanzen repräsentieren die Tasten auf der Werkzeugleiste.


Die Art der Taste kann über eine Stilkonstante bestimmt werden:
8.5 Widgets 181

SWT.PUSH Normale Taste mit sofortiger Auslösung


SWT.CHECK Arretierende Taste
SWT.RADIO Radiotaste mit gegenseitiger Auslösung
SWT.SEPARATOR Passives Element zum Trennen von Tastengruppen
SWT.DROP_DOWN Normale Taste mit assoziierter Pfeiltaste

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.

Verschiebbare Werkzeuggruppen (CoolBar)

Mit der Klasse CoolBar können mehrere ToolBar-Instanzen als so


genannte CoolItems zu verschiebbaren Gruppen zusammengefasst wer-
den. Ein gutes Beispiel für eine CoolBar ist die Werkzeugleiste der
Eclipse-Workbench. Jede einzelne Toolbar-Instanz wird dabei in eine
CoolItem-Instanz eingebettet. Diese CoolItem-Instanzen werden auf die
CoolBar platziert und sind dort verschiebbar. Die Zuordnung zwischen
CoolItem und Toolbar erfolgt mit der Methode setControl(). Dabei
muss anfänglich für jede CoolItem-Instanz eine minimale Größe gesetzt
werden. Im zweiten Beispiel dieses Abschnitts zeigen wir, wie das geht.
Wird für eine CoolItem-Instanz der Stil SWT.DROP_DOWN gesetzt, so
erscheint ein Pfeilsymbol, wenn in einer Werkzeuggruppe nicht alle
Werkzeuge abgebildet werden können. Dazu ist allerdings eine ent-
sprechende Ereignisverarbeitung mit Aufbau eines Drop-down-Menüs
zu implementieren, ähnlich wie das für Drop-down-Tasten notwendig
ist.
182 8 Das SWT

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ü

Menu-Instanzen erzeugen Ereignisse vom Typ HelpEvent und MenuEvent.


Erscheint ein Menü auf dem Bildschirm, wird die Methode menuS-
hown() der MenuListener-Instanz aufgerufen. Verschwindet das Menü,
wird die Methode menuHidden() aufgerufen.
MenuItem Die Menüeinträge werden von MenuItem-Instanzen repräsentiert.
Die Art des Eintrags kann über eine Stilkonstante bestimmt werden:

Menüeintrag mit Check-Symbol. Bei Betätigung erscheint und


SWT.CHECK
verschwindet das Check-Symbol wechselweise.
SWT.CASCADE Menüeintrag für kaskadierende Menüs
SWT.PUSH Normaler Menüeintrag
SWT.RADIO Menüeintrag mit gegenseitiger Auslösung
SWT.SEPARATOR Passive Trennlinie

Die Beschriftung eines Eintrags wird mit der Methode setText()


gesetzt.
MenuItem-Instanzen erzeugen bei Betätigung Ereignisse vom Typ
SelectionEvent, ArmEvent und HelpEvent. ArmEvents werden ausgelöst,
wenn der Mauszeiger über den Menüeintrag positioniert wird.
Will man beispielsweise eine klassische Menüleiste aufbauen, so
legt man zunächst die Menüleiste selbst als Menu-Instanz vom Typ
SWT.BAR an. Als übergeordnetes Composite muss dabei die Shell angege-
ben sein, für welche die Menüleiste aufgebaut werden soll. Freilich ist
dann die Menüleiste noch nicht für diese Shell aktiviert. Dies muss von
der Shell-Instanz aus geschehen, und zwar mit der Methode setMenu-
Bar(). Die einzelnen Menütitel werden dann als kaskadierende Menu-
Item-Instanzen angelegt. Deren Untermenüs werden als unabhängige
SWT.DROP_DOWN-Menüs unter der Shell-Instanz angelegt und den
Menütiteln mit setMenu()zugeordnet.
Das folgende Beispiel zeigt den Aufbau eines einfachen Menüs mit
einem einzigen Menütitel:
8.5 Widgets 183

// 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);

// Zweite ToolBar erzeugen


final ToolBar toolbar2 = new ToolBar(coolbar, SWT.NULL);
// Zwei Radiobuttons erzeugen
final ToolItem toolitem3a = new ToolItem(toolbar2, SWT.RADIO);
toolitem3a.setText("Radio");
toolitem3a.setToolTipText("Radio button a");
final ToolItem toolitem3b = new ToolItem(toolbar2, SWT.RADIO);
toolitem3b.setText("Radio");
toolitem3b.setToolTipText("Radio button b");
// Separator
new ToolItem(toolbar2, SWT.SEPARATOR);
// Drop-down-Menü-Button erzeugen
final ToolItem toolitem5 = new ToolItem(toolbar2, SWT.DROP_DOWN);
toolitem5.setText("Drop-down-Menu");
// Ereignisverarbeitung für Drop-down-Menü-Button
toolitem5.addSelectionListener(
// die Menüerzeugung findet in Klasse DropDownSelectionListener statt
new DropDownSelectionListener(composite.getShell()));
// zweiten CoolItem erzeugen, Toolbar zuordnen und Größe setzen
final CoolItem coolitem2 = new CoolItem(coolbar, SWT.NULL);
coolitem2.setControl(toolbar2);
size = toolbar2.computeSize(SWT.DEFAULT, SWT.DEFAULT);
size = coolitem2.computeSize(size.x, size.y);
coolitem2.setSize(size);
coolitem2.setMinimumSize(toolitem3a.getWidth(), size.y);

Die Klasse DropDownSelectionListener, die für den Menüaufbau


zuständig ist, ist folgendermaßen definiert:
class DropDownSelectionListener extends SelectionAdapter {
private Menu menu;
private Composite parent;

public DropDownSelectionListener(Composite parent) {


this.parent = parent;
}
8.5 Widgets 185

public void widgetSelected(final SelectionEvent e) {


// Wir erzeugen das Menü, wenn es noch nicht existiert
if (menu == null) {
menu = new Menu(parent);
final MenuItem menuItem1 = new MenuItem(menu, SWT.NULL);
menuItem1.setText("Eintrag1");
// SelectionListener für menuItem1 setzen
menuItem1.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent m) {
processMenuEvent(e, menuItem1);
}
});
menuItem1.addArmListener(new ArmListener() {
public void widgetArmed(ArmEvent m) {
System.out.println("Maus über Menü-Eintrag 1");
}
});

final MenuItem menuItem2 = new MenuItem(menu, SWT.NULL);


menuItem2.setText("Eintrag2");
// SelectionListener für menuItem1 setzen
menuItem2.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent m) {
processMenuEvent(e, menuItem2);
}
});
menuItem2.addArmListener(new ArmListener() {
public void widgetArmed(ArmEvent m) {
System.out.println("Maus über Menü-Eintrag 2");
}
});
}
// Prüfen, ob die Pfeiltaste gedrückt wurde
if (e.detail == SWT.ARROW) {
if (menu.isVisible()) {
// Ausblenden, wenn Menü bereits sichtbar
menu.setVisible(false);
} else {
// ToolItem und ToolBar aus dem Event auslesen
final ToolItem toolItem = (ToolItem) e.widget;
final ToolBar toolBar = toolItem.getParent();
// Position und Abmessung des ToolItem
Rectangle toolItemBounds = toolItem.getBounds();
// relative Position auf absolute Position umrechnen
Point point =
toolBar.toDisplay(
186 8 Das SWT

new Point(toolItemBounds.x, toolItemBounds.y));


// Menü positionieren
menu.setLocation(point.x, point.y +
toolItemBounds.height);
// Menü sichtbar machen
menu.setVisible(true);
}
} else {
final ToolItem toolItem = (ToolItem) e.widget;
System.out.println(
"Werkzeugtaste gedrückt: " + toolItem.getText());
}
}
private void processMenuEvent(
final SelectionEvent e,
final MenuItem item) {
// Text des Menüeintrags holen
final String s = item.getText();
// ToolItem holen
final ToolItem toolItem = (ToolItem) e.widget;
// Text des ToolItem durch Text des Menüeintrags ersetzen
toolItem.setText(s);
// Menü wieder verstecken
menu.setVisible(false);
}
}

8.5.13 Spezielle Widgets

Das Package org.eclipse.swt.custom enthält einige weitere Widgets,


die auf verschiedenen Plattformen nicht als native GUI-Elemente zur
Verfügung stehen. Deshalb sind alle Widgets in diesem Package reine
Java-Implementierungen.
Wir hatten bereits die Widgets CCombo und CTabFolder kennen
gelernt. In der folgenden Tabelle listen wir einige weitere dieser Wid-
get-Klassen auf:
8.5 Widgets 187

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

// Äußere SashForm erzeugen


SashForm sf1 = new SashForm(toplevelShell, SWT.HORIZONTAL);
// Innere SashForm erzeugen
SashForm sf2 = new SashForm(sf1, SWT.VERTICAL);
// Inhalt für vertikale SashForm erzeugen
List list1 = new List(sf2, SWT.NONE);
list1.setItems(new String[]{"red", "green", "blue"});
List list2 = new List(sf2, SWT.NONE);
list2.setItems(new String[]{"A", "B", "C"});
// Gleichmäßig gewichten
sf2.setWeights(new int[] {100,100});
// Inhalt für horizontale SashForm erzeugen
List list3 = new List(sf1, SWT.NONE);
list3.setItems(
new String[]{"one", "two", "three", "four", "five", "six"});
// Unterschiedlich gewichten
sf1.setWeights(new int[] {100,200});

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.

8.5.14 Das Browser-Widget

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

sem Widget kein eigenes Kontextmenü zuordnen (der Browser hat ja


schon eines), MouseListener und KeyListener empfangen keine Ereig-
nisse, und auf der Oberfläche des Widgets kann man weder herumma-
len noch andere Widgets darauf anordnen.
Dafür verfügt das Browser-Widget aber über spezielle Methoden,
um z.B. eine Webseite an einer spezifizierten Adresse anzuzeigen
(setURL()) oder die URL der aktuellen Seite abzufragen (getURL()).
Mit der Methode setText() kann dem Browser direkt ein HTML-Text
zur Anzeige übergeben werden. Daneben gibt es Methoden für die
Navigation wie back(), isBackEnabled(), forward(), isForwardEnab-
led(), refresh() und stop().
Außerdem kann das Browser-Widget mit verschiedenen Listenern
(CloseWindowListener, LocationListener, OpenWindowListener, Pro-
gressListener, StatusTextListener, TitleListener, Visibility-
WindowListener) instrumentiert werden, die auf Status- oder Inhaltsän-
derungen des eingebetteten Browsers reagieren können.
In Abschnitt 10.5 zeigen wir das Browser-Widget in einer prakti-
schen Anwendung. Weitere Beispiele finden Sie in [Daum2004].

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

Hinweis: In manchen Fällen ist es einfacher und benutzerfreundlicher, an


Stelle eines Composites mit einem Layout eine SashForm-Komponente (siehe
Abschnitt 8.5.13) zu verwenden. Diese erlaubt dem Endbenutzer eine eigen-
ständige Aufteilung des vorhandenen Raumes.

8.6.1 Visuelle Übersicht

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.

Abb. 8–10 Verschiedene FillLayout-, RowLayout-, GridLayout- und FormLayout-Konfi-


gurationen können mit der SWT Layouts-Beispielapplikation durchprobiert werden. Ein
Druck auf den Code-Button zeigt den erzeugten Source-Code.
So kann man diese Beispielapplikation zur Not als GUI-Designer verwenden.

Da diese Applikation die verschiedenen Layouts am besten visualisie-


ren kann, werden wir im Folgenden auf eine Abbildung der verschie-
denen Layouts verzichten.

8.6.2 Die Klasse FillLayout

FillLayout ist das einfachste der vordefinierten Layouts. Es besagt


schlicht, dass die GUI-Elemente das Composite komplett ausfüllen. Es
gibt weder Zwischenräume zwischen den einzelnen GUI-Elementen
noch Randbereiche, und auch ein automatischer Umbruch bei ungenü-
gendem Platz ist nicht möglich. Alle GUI-Elemente haben die gleiche
Größe, wobei die Höhe vom höchsten GUI-Element und die Breite
8.6 Layouts 191

vom breitesten GUI-Element bestimmt wird. Typische Einsatzfälle für


diese Layout-Klasse sind z.B. Werkzeugleisten, bei denen die einzelnen
Tasten unmittelbar aneinander liegen, oder auch der Fall, wenn ein
einzelnes GUI-Element ein Composite komplett ausfüllt.
Normalerweise werden die GUI-Elemente in horizontaler Rich-
tung aneinander positioniert, allerdings lässt sich durch die Zuweisung
der Stilkonstante SWT.VERTICAL an das type-Feld des Layouts auch eine
vertikale Ausrichtung erzwingen:
FillLayout fillLayout = new FillLayout();
fillLayout.type = SWT.VERTICAL;
composite.setLayout(fillLayout);
new Button(composite, SWT.RADIO).setText("One");
new Button(composite, SWT.RADIO).setText("Two");
new Button(composite, SWT.RADIO).setText("Three");

Bei FillLayout besteht keine Möglichkeit, die Größe der einzelnen


GUI-Elemente individuell zu setzen.

8.6.3 Die Klasse RowLayout

Ähnlich wie beim FillLayout ordnet das RowLayout die GUI-Elemente


in einer Reihe an. Dabei gibt es jedoch zusätzliche Optionen:

type wie bei FillLayout


Ist dieses Feld auf den Wert true gesetzt (Standardeinstellung), so werden
wrap überzählige GUI-Elemente in eine neue Reihe umgebrochen, wenn der Platz nicht
ausreicht.
Ist dieses Feld auf den Wert true gesetzt (Standardeinstellung), so werden alle
GUI-Elemente in ihrer natürlichen Größe und so weit links wie möglich dargestellt.
pack
Andernfalls füllen die GUI-Elemente den kompletten verfügbaren Platz aus, ähnlich
wie bei FillLayout.
Ist dieses Feld auf den Wert true gesetzt, so werden alle GUI-Elemente gleich-
justify
mäßig über den verfügbaren Raum verteilt. Die Standardeinstellung ist »false«.
marginLeft
marginTop
Diese Felder kontrollieren die Größe des Randbereichs in Pixeln.
marginRight
marginBottom
Dieses Feld kontrolliert den Minimalabstand zwischen
spacing
GUI-Elementen in Pixeln.

Das folgende Beispiel zeigt, wie die Felder einer RowLayout-Instanz


gesetzt werden können:
192 8 Das SWT

RowLayout rowLayout = new RowLayout();


rowLayout.wrap = false;
rowLayout.pack = false;
rowLayout.justify = true;
rowLayout.type = SWT.VERTICAL;
rowLayout.marginLeft = 10;
rowLayout.marginTop = 5;
rowLayout.marginRight = 10;
rowLayout.marginBottom = 8;
rowLayout.spacing = 5;
composite.setLayout(rowLayout);

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));

8.6.4 Die Klasse GridLayout

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!

Bestimmte Attribute können bereits im GridData()-Konstruktor gesetzt


werden. Dazu stehen folgende Stilkonstanten zur Verfügung:
194 8 Das SWT

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.

8.6.5 Die Klasse FormLayout

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.

FormData Die eigentlichen Layout-Möglichkeiten stecken in den Klassen Form-


Data und FormAttachment. FormData erlaubt folgende Optionen für
jedes einzelne GUI-Element:
8.6 Layouts 195

height Die gewünschte Höhe des GUI-Elements in Pixeln.


width Die gewünschte Breite des GUI-Elements in Pixeln.
top Eine FormAttachment-Instanz, die angibt, auf was sich die obere/
bottom untere/linke/rechte Kante des GUI-Elements beziehen soll.
left
right

Dabei gibt es für FormAttachment-Instanzen zwei Spielarten: FormAttachment

! Angabe einer relativen Position im Composite


! Angabe relativ zu einem anderen GUI-Element
Für die erste Methode der Positionierung stehen zwei Konstruktor- Composite-Position
Varianten zur Verfügung:
FormAttachment fa = new FormAttachment(prozent, offset);

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

Wurde nur ein Prozentwert angegeben, so ist die Formel natürlich:


p = d*prozent/100+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.

8.6.6 Die Klasse StackLayout


Zwischen GUI-Elementen Diese Klasse ist im Unterschied zu den vorigen vier vordefinierten Lay-
hin- und herschalten outs nicht im Package org.eclipse.swt.layout enthalten, sondern im
Package org.eclipse.swt.custom. Sie unterscheidet sich von anderen
Layout-Klassen auch darin, dass innerhalb eines Composite immer nur
ein GUI-Element sichtbar ist. Der Grund dafür ist, dass alle GUI-Ele-
mente gleich groß gemacht werden und an der gleichen Stelle überein-
ander positioniert sind. Damit ist immer nur das oberste Element sicht-
bar. Die Klasse StackLayout ist dann sinnvoll, wenn man zwischen
GUI-Elementen hin- und herschalten will. Man braucht lediglich die
gewünschte Control-Instanz an die höchste Position zu bringen.
Für StackLayout stehen 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.
topControl Die sichtbare Control-Instanz.

Im folgenden Beispiel legen wir zwei Button-Instanzen übereinander.


Wird eine Taste gedrückt, wird jeweils die andere Taste sichtbar:
// Neues Composite erzeugen
final Composite stackComposite = new Composite(composite,SWT.NULL);
final StackLayout stackLayout = new StackLayout();
// Text-Buttons erzeugen
final Button buttonA = new Button(stackComposite, SWT.PUSH);
8.7 Grafik 197

buttonA.setText("Taste A");
final Button buttonB = new Button(stackComposite, SWT.PUSH);
buttonB.setText("Taste B");

// Auf Klickereignisse reagieren


buttonA.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
stackLayout.topControl = buttonB;
// Neues Layout erzwingen
stackComposite.layout();
// Fokus auf sichtbare Taste setzen
buttonB.setFocus();
}
});
buttonB.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
stackLayout.topControl = buttonA;
// Neues Layout erzwingen
stackComposite.layout();
// Fokus auf sichtbare Taste setzen
buttonA.setFocus();
}
});
// Layout initialisieren
stackLayout.topControl = buttonA;
stackLayout.marginWidth = 10;
stackLayout.marginHeight = 5;
// Layout setzen
stackComposite.setLayout(stackLayout);

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.

8.7.1 Der Grafikkontext

Die Klasse GC enthält alle für Zeichenoperationen benötigten Methoden


– Methoden wie drawLine(), drawOval(), drawPolygon(), setFont(),
getFontMetrics(), u.v.a.m.
198 8 Das SWT

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:

Alle grafischen Operationen werden innerhalb eines PaintListener-Objekts


ausgeführt, also innerhalb der paintControl()-Ereignisverarbeitung einer
Control-Instanz.

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

In einem Grafikkontext setzt man Linien- und Textfarben – wie oben


gezeigt – mit Hilfe der Methode setForeground(). Füllfarben werden
mit setBackground() gesetzt.
Dazu muss man sich allerdings erst einmal Farbobjekte besorgen.
Das kann auf zwei verschiedene Arten erfolgen:
! Man holt sich eine Systemfarbe von einer Device-Instanz. Da Dis-
play eine Unterklasse von Device ist, können wir eine Systemfarbe
mit Hilfe der Methode getSystemColor() von der Display-Instanz
eines Widgets holen. Die dazu notwendigen COLOR_...-Konstanten
sind in der Klasse SWT definiert.
Farbobjekte, die man sich auf diese oder andere Weise von ande-
ren Instanzen holt, dürfen nicht mit dispose() freigegeben werden,
denn sie werden eventuell noch anderenorts benötigt!
! Man fertigt Farbobjekte selbst an, z.B.
Color red = new Color(device, 255,0,0)
oder
Color blue = new Color(device, new RGB(0,255,0));
Der Parameter device muss dabei vom Typ Device sein. RGB ist eine
geräteunabhängige Klasse zur Darstellung von RGB-Farbtupeln.
Auf Ausgabegeräten mit 24 Bit Farbtiefe ist die Farbdarstellung
exakt, bei Ausgabegeräten mit geringerer Farbtiefe wird die Farbe
von Eclipse so gut wie möglich angenähert [Moody2001].
Solch selbst angefertigte Farbobjekte müssen mit dispose() wie-
der entsorgt werden, wenn sie nicht mehr gebraucht werden.

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

Im folgenden Beispiel holen wir uns den aktuellen Systemfont, produ-


zieren eine kursive Variante, konfigurieren den Grafikkontext mit die-
sem Font und zeichnen das Wort »Hello«:
// Display-Instanz holen
Display display = composite.getDisplay();
// Systemfont holen
Font systemFont = display.getSystemFont();
// FontData-Objekte enthalten die Fonteigenschaften.
// Bei manchen Betriebssystemen kann ein Font mehrere
// FontData-Instanzen besitzen. Wir verwenden nur die Erste.
FontData[] data = systemFont.getFontData();
FontData data0 = data[0];
// Den Fontstil auf kursiv setzen
data0.setStyle(SWT.ITALIC);
// Neuen Font erzeugen
Font italicFont = new Font(display, data0);
// Font im Grafikkontext setzen
gc.setFont(italicFont);
// Nicht vergessen: italicFont.dispose() im
// DisposeListener von composite aufrufen.
// Text an Position (4,4) mit transparentem Hintergrund zeichnen.
gc.drawText("Hello",4,4,true);

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

Image und ImageData unterstützen Bilder sowohl im direkten RGB-


Format als auch im Indexformat. Auch Transparenz wird unterstützt
(alpha-Kanal bei RGB, Transparentfarbe im indizierten Format). Min-
destens die folgenden Dateiformate werden gelesen: BMP, GIF, JPG,
PNG, TIFF und ICO. In Abschnitt 8.5.5 hatten wir bereits gezeigt, wie
ein Bild von einer Datei gelesen werden kann.
Im folgenden Beispiel benutzen wir eine Image-Instanz für den Double Buffering
doppelt gepufferten Bildaufbau. Double Buffering wird oft verwendet,
um ein Flackern des Bildschirms zu vermeiden. Man legt zunächst eine
genügend große Image-Instanz an, erzeugt dann eine GC-Instanz auf
diese Image-Instanz und führt alle Zeichenoperationen innerhalb dieses
GC-Kontextes aus. Anschließend zeichnet man das gesamte Image
komplett auf das Ziel-Drawable.
// Canvas erzeugen
final Canvas canvas = new Canvas(composite,SWT.BORDER);
// Weisse Systemfarbe holden
Color white = canvas.getDisplay().getSystemColor(SWT.COLOR_WHITE);
// Canvashintergrund setzen
canvas.setBackground(white);
// PaintListener zufügen
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
// Display aus Ereignis holen
Display display = e.display;
// Rote und schwarze Systemfarben holen
// - brauchen nicht entsorgt zu werden
Color black = display.getSystemColor(SWT.COLOR_BLACK);
Color red = display.getSystemColor(SWT.COLOR_RED);
// Das Ereignis liefert auch den Grafikkontext
GC gc = e.gc;
// Das Widget, das das Ereignis verursacht hat
Composite source = (Composite) e.widget;
// Größe der nutzbaren Fläche
Rectangle rect = source.getClientArea();
// Puffer aufbauen
Image buffer = new Image(display,rect.width,rect.height);
// Neuer Grafikkontext für Puffer
GC bufferGC = new GC(buffer);
// verschiedene Zeichenoperationen
bufferGC.setBackground(red);
bufferGC.fillRectangle(5,5,rect.width-10,rect.height-10);
bufferGC.setForeground(black);
bufferGC.drawRectangle(5,5,rect.width-10,rect.height-10);
bufferGC.setBackground(source.getBackground());
202 8 Das SWT

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();
}
});

Im System verwendete Bilder lassen sich von der aktuellen Display-


Instanz mit der Methode getSystemImage() holen. Dabei stehen die fol-
genden Konstanten für die Identifizierung des Bildes zur Verfügung:
SWT.ICON_ERROR, SWT.ICON_INFORMATION, SWT.ICON_QUESTION, SWT.ICON_
WARNING.

8.7.5 Der Mauszeiger

Ebenfalls im Package org.eclipse.swt.graphics befindet sich die


Klasse Cursor, welche den Mauszeiger repräsentiert. Um der aktuellen
Schreibmarke eine neue Gestalt zu geben, muss explizit eine neue
Instanz dieser Klasse erzeugt werden. Dabei wird das aktuelle Display
als Parameter übergeben, außerdem wird eine Stilkonstante angege-
ben, welche die Gestalt des Mauszeigers festlegt. Dabei hält natürlich
die konkrete Ausprägung von der Ablaufplattform ab:

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

CURSOR_SIZESE Größenänderung Südostrichtung


CURSOR_SIZESW Größenänderung Südwestrichtung
CURSOR_SIZENW Größenänderung Nordwestrichtung
CURSOR_UPARROW Pfeil nach oben
CURSOR_IBEAM Schreibmarke
CURSOR_NO unzulässige Operation
CURSOR_HAND Hand zum Verschieben

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 Ein Widget mit Swing


Auf Grund seiner Nähe zum jeweiligen Betriebssystem stellt das SWT
einen neuen Ansatz für die Implementierung der untersten Schicht
einer grafischen Benutzeroberfläche dar. Es stellt sich allerdings die
Frage: Was ist mit den oberen Schichten?
Wenn es um Dinge wie Fenster, Dialoge und Menüs geht, ist die
Antwort einfach. Funktionalität, z.B. mit Swing bereitgestellt, steht in
Eclipse als JFace-Bibliothek zur Verfügung (siehe Kapitel 9).
Schwierig wird es allerdings, wenn es um andere Grafikschichten
geht, z.B. eine leistungsfähige Grafikschicht, wie sie von Java2D oder
Java3D bereitgestellt wird, SVG-Verarbeitung wie in Batik (www.
apache.org) oder Bitmap-Manipulationen wie in Java Advanced Ima-
ging (JAI). Alle diese APIs passten bisher nicht zum SWT. Fortgeschrit-
tene Funktionen wie Kantenglättung, transparente Grafiken und Text-
rotation blieben deshalb außen vor.
Mit Eclipse 3 hat sich das gründlich geändert. Nun ist es möglich,
Swing- und AWT-Inhalte innerhalb von SWT-Composites zu präsen-
tieren. Damit lassen sich auch grundsätzlich höhere Grafikschichten,
die auf Swing oder dem AWT aufbauen, in SWT-Anwendungen inte-
grieren. Und plötzlich macht dann Swing wieder Spaß, dank SWT.
Unter Windows funktioniert das unter JRE 1.3 und 1.4, für die ande-
ren Plattformen wird JRE 1.5 benötigt.
204 8 Das SWT

8.8.1 Eingebettete Inhalte

Ermöglicht wird das durch die neue Stilkonstante EMBEDDED in der


Klasse SWT. Ein Composite, das mit dieser Stilkonstante erzeugt wird,
kann SWT-fremde Inhalte enthalten (allerdings keine zusätzlichen
SWT-Inhalte). In Eclipse 3 können das zunächst java.awt.Frame-Kom-
ponenten sein, die mit Hilfe der Factory-Methode SWT_AWT.new_
Frame() erzeugt werden können. Also z.B.
Composite awtContainer = new Composite(parent, SWT.EMBEDDED);
java.awt.Frame myFrame = SWT_AWT.new_Frame(awtContainer);

Nun kann man nach Herzenslust AWT- und Swing-Komponenten auf


die übliche Art zu der so erzeugten Frame-Instanz hinzufügen.
Außerdem verfügt die Klasse SWT_AWT noch über die Methode
new_Shell(). Diese erzeugt für einen gegebenen AWT-Canvas eine
SWT-Shell, so dass der AWT-Canvas zwar in einem eigenen Fenster,
aber noch innerhalb der SWT-Applikation ausgeführt wird.

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.*;

public class SWT2D {

// Shell für Popup-Editor


Shell eShell = null;
// Text-Widget für Editor
Text eText = null;
// Liste eingegebener Zeichenketten
ArrayList wordList = new ArrayList(12);

public static void main(String[] args) {


SWT2D swtawt = new SWT2D();
swtawt.run();
}

Zunächst wird die SWT-Shell erzeugt. Darin werden in einem GridLay-


out das Container-Composite (EMBEDDED) für den AWT-Canvas und
später noch eine Taste platziert.
private void run() {
// Top-Level Shell erzeugen
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("Java 2D-Beispiel");
// GridLayout für Canvas und Taste
shell.setLayout(new GridLayout());
// Container für AWT-Canvas erzeugen
final Composite canvasComp = new Composite(shell, SWT.EMBEDDED);
// Vorzugsgröße setzen
GridData data = new GridData();
data.widthHint = 600;
data.heightHint = 500;
canvasComp.setLayoutData(data);

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

Grafikkontext wieder auf den Grundzustand zurückbringen zu kön-


nen. Außerdem schalten wir die Kantenglättung ein – eine weitere
Schönheit von Java2D.
// AWT-Frame für Canvas erzeugen
java.awt.Frame canvasFrame = SWT_AWT
.new_Frame(canvasComp);
// Canvas erzeugen und zufügen
final java.awt.Canvas canvas = new java.awt.Canvas();
canvasFrame.add(canvas);
// Graphischen Kontext holen und in Java2D-Kontext umwandeln
final java.awt.Graphics2D g2d = (java.awt.Graphics2D) canvas
.getGraphics();
// Kantenglättung einschalten
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Ursprüngliche Transformation merken
final java.awt.geom.AffineTransform origTransform = g2d
.getTransform();

Nun erzeugen wir die Löschtaste. In ihrer Ereignisverarbeitung wird


ein Neuzeichnen des Canvas veranlasst, und zwar durch den Aufruf
der redraw()-Methode für das SWT-Container-Composite.
// Clear-Taste anlegen und positionieren
Button clearButton = new Button(shell, SWT.PUSH);
clearButton.setText("Clear");
data = new GridData();
data.horizontalAlignment = GridData.CENTER;
clearButton.setLayoutData(data);
// Ereignisverarbeitung für Taste
clearButton
.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
// Wortliste löschen und Canvas neu zeichnen
wordList.clear();
canvasComp.redraw();
}
});

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) {}

public void mouseEntered(


java.awt.event.MouseEvent e) {}

public void mouseExited(


java.awt.event.MouseEvent e) {}

public void mousePressed(


java.awt.event.MouseEvent e) {
// Popup-Editor verwalten
display.syncExec(new Runnable() {
public void run() {
if (eShell == null) {
// Neue Shell anlegen: nicht-modal!
eShell = new Shell(shell, SWT.NO_TRIM
| SWT.MODELESS);
eShell.setLayout(new FillLayout());
// Texteingabefeld
eText = new Text(eShell, SWT.BORDER);
eText.setText("Textrotation im SWT?");
eShell.pack();
// Positionieren (Displaykoordinaten)
java.awt.Rectangle bounds = canvas
.getBounds();
org.eclipse.swt.graphics.Point pos = canvasComp
.toDisplay(bounds.width / 2,
bounds.height / 2);
Point size = eShell.getSize();
eShell.setBounds(pos.x, pos.y, size.x,
208 8 Das SWT

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();
}
}
});
}

public void mouseReleased(


java.awt.event.MouseEvent e) {}
});

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();
}
}

Abbildung 8–11 zeigt das Ergebnis.


210 8 Das SWT

Abb. 8–11 Textrotation und geglättete Kanten im SWT? Mit eingebettetem Java2D-
Canvas kein Problem.

8.9 Ausgabe auf Drucker


Druckausgaben werden mit Hilfe der Klassen PrintDialog, Printer-
Data und Printer bewerkstelligt. PrintDialog ist eine Unterklasse der
uns schon bekannten abstrakten Klasse Dialog und repräsentiert den
Druckauswahldialog des jeweiligen Betriebssystems. Als Ergebnis lie-
fert PrintDialog entweder eine PrinterData-Instanz oder null zurück.
Die PrinterData-Instanz enthält alle im Druckerauswahldialog gemach-
ten Angaben wie Anzahl der Kopien, Druckbereich etc. Durch Abfrage
der entsprechenden Felder (copyCount, scope etc.) kann man diese
Angaben auswerten.
Anschließend erzeugt man dann eine Instanz der Printer-Klasse,
die eine Device-Unterklasse ist. Diese Instanz benutzt man, um einen
neuen Grafikkontext zu erzeugen. Auf diesen Grafikkontext werden
dann alle Ausgaben durchgeführt, die notwendig sind, um die Druck-
seiten zu füllen.
8.9 Ausgabe auf Drucker 211

Zunächst wird die startJob()-Methode der Printer-Instanz auf-


gerufen, um einen neuen Druckauftrag zu erzeugen. Dann wird für
jede Seite zunächst die startPage()-Methode aufgerufen, die grafi-
schen Ausgaben auf den Grafikkontext durchgeführt und dann die
endPage()-Methode aufgerufen. Sind alle Seiten ausgedruckt, wird der
Druckauftrag mit endJob() geschlossen. Zum Schluss müssen noch der
Grafikkontext und das Printer-Objekt mit dispose() entsorgt wer-
den.
Der folgende Beispielcode zeigt, wie es geht:
// Taste für Druckausgabe erzeugen
final Button primtButton = new Button(composite, SWT.PUSH);
primtButton.setText("Drucken");
// Auf Klickereignisse reagieren
primtButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
// Shell holen
Shell shell = composite.getShell();
// Druckdialog erzeugen
PrintDialog printDialog = new PrintDialog(shell);
// und ausführen
PrinterData printerData = printDialog.open();
// Prüfen, ob OK gedrückt
if (printerData != null) {
// Neue Printer-Instanz erzeugen
Printer printer = new Printer(printerData);
// Grafikkontext für diesen Drucker erzeugen
GC gc = new GC(printer);
// Druckauftrag öffnen
if (!printer.startJob("Hello"))
System.out.println("Printer start job failed");
else {
// Erste Seite drucken
if (!printer.startPage())
System.out.println("Printer start page 1 failed");
else {
// grüne Systemfarbe vom Drucker holen und setzen
Color green =
printer.getSystemColor(SWT.COLOR_DARK_GREEN);
gc.setForeground(green);
// Text zeichnen
gc.drawText("Hello World", 4, 4, true);
// Seite beenden
printer.endPage();
}
212 8 Das SWT

// Zweite Seite drucken


if (!printer.startPage())
System.out.println("Printer start page 2 failed");
else {
// blaue Systemfarbe vom Drucker holen und setzen
Color blue = printer.getSystemColor(SWT.COLOR_BLUE);
gc.setForeground(blue);
// Text zeichnen
gc.drawText("Hello Eclipse", 4, 4, true);
// Seite beenden
printer.endPage();
}
// Druckauftrag beenden
printer.endJob();
}
// aufräumen
gc.dispose();
printer.dispose();
}
}
});

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.1 Die Zwischenablage

Als Zwischenablage wird die systemweite Zwischenablage der Ablauf-


plattform verwendet, zu der SWT einen Zugang bereitstellt. Implemen-
tiert wird dieser Zugang in Form der Klasse Clipboard. Diese Klasse
stellt die Methoden setContents() und getContents() zur Verfügung,
mit denen der Inhalt der Zwischenablage gesetzt bzw. abgefragt wer-
den kann. Beim Erzeugen einer Clipboard-Instanz muss eine Display-
8.10 Datentransfer 213

Instanz mit angegeben werden. Da das Clipboard Betriebssystemres-


sourcen belegt, muss es mit dispose() wieder freigegeben werden,
wenn es nicht mehr benötigt wird.
Üblicherweise enthält eine Zwischenablage die kopierten Daten in Transfertypen
mehreren Formaten. Textprozessoren legen beispielsweise die kopier-
ten Textsegmente im RTF-Format und als einfachen Text ab. Eclipse
unterscheidet diese Datenformate anhand von Transfertypen. Trans-
fertypen sind als Unterklassen der abstrakten Klasse Transfer imple-
mentiert. Unter anderem sind vorhanden: FileTransfer, MarkerTrans-
fer, RTFTransfer, TextTransfer und weitere speziellere Transfertypen.
Hat man selbst speziellere Anforderungen an den Transfertyp, so kann
man eigene Transfertypen (üblicherweise als Unterklasse von ByteAr-
rayTransfer) implementieren. Der Quellcode von ByteArrayTransfer
enthält eine Anleitung, wie das geht.
Die jeweiligen konkreten Transfertypen dienen dabei der
Umwandlung der typspezifischen Datenformate in ein betriebssystem-
abhängiges Datenformat für die Zwischenablage. Will man etwas in
die Zwischenablage ablegen, so übergibt man der Methode setCon-
tents() ein Array mit den Daten in den verschiedenen Formaten sowie
ein Array mit den Instanzen der Transfertypen. Entsprechend erhält
man von der Methode getContents() unter Angabe eines Transfertyps
die Daten aus der Zwischenablage im gewünschten Format. Mit der
Methode getAvailableTypes() erhält man ein Array mit den in der
Zwischenablage vertretenen Transfertypen. So erhält man schnell Auf-
schluss darüber, ob das gewünschte Format vorhanden ist, ohne den
Inhalt der Zwischenablage lesen zu müssen.
Im Plugin org.eclipse.swt.examples findet man unter Clip-
board.java ein Beispielprogramm für die Verwendung der Zwischena-
blage.

8.10.2 Drag&Drop

Für Drag&Drop-Operationen benötigt man eine Datenquelle und eine


Datensenke. Dabei kann die eine oder die andere von einer anderen
Anwendung oder auch vom System bereitgestellt werden. In Eclipse
wird die Datenquelle durch die Klasse DragSource implementiert, die
Datensenke durch die Klasse DropTarget. Beide Klassen sind Unter-
klassen der Klasse Widget. Eine Anwendung kann mit mehreren Instan-
zen dieser Klassen ausgerüstet werden, allerdings muss jede Instanz
eindeutig einer Control-Instanz zugeordnet werden. Das geschieht
durch die Angabe der Control-Instanz im Konstruktor von DragSource
oder DropTarget. Damit sind auch die Position und Größe des Daten-
214 8 Das SWT

transferelements festgelegt. Außerdem werden an dieser Stelle auch die


erlaubten Operationen (DND.NONE, DND.MOVE, DND.COPY, DND.LINK)
deklariert.
Bei einer Drag&Drop-Operation erzeugen die Instanzen dieser
Klassen entsprechende Ereignisse (DragSourceEvent bzw. DropTarget-
Event), die mit entsprechenden Listener-Instanzen (DragSourceListe-
ner bzw. DropTargetListener) empfangen werden können. Die ver-
schiedenen Methoden dieser Listener erlauben eine lückenlose Über-
wachung der Drag&Drop-Operation. So wird die dragStart()-
Methode des DragSourceListeners beim Beginn der Drag&Drop-Ope-
ration aufgerufen. Wird der Bereich des DropTargets betreten, wird die
dragEnter()-Methode des DropTargetListeners aufgerufen. drag-
Leave() wird beim Verlassen dieses Bereichs aufgerufen und drag-
Over(), wenn der Mauszeiger über diesem Bereich bewegt wird. Wird
während der Operation der Operationsmodus geändert (z.B. durch
Drücken von Strg oder Alt), wird die Methode dragOperationChan-
ged() aufgerufen. Wird die Maustaste über der Datensenke losgelas-
sen, so wird zunächst die Methode dropAccept() des DropTargetListe-
ners aufgerufen. Hier besteht die letzte Möglichkeit, die Operation
abzulehnen. Anschließend wird die Methode dragSetData() des
DragSourceListeners aufgerufen, die nun die zu übermittelnden Daten
bereitstellen muss. Dann erfolgt der Aufruf der Methode drop() beim
DropTargetListener, die diese Daten entgegennimmt. Schließlich wird
noch die Methode dragFinished() beim DragSourceListener aufgeru-
fen. Hier besteht noch die Möglichkeit, notwendige Aufräumarbeiten
durchzuführen.
Bei allen Methoden, die vor der eigentlichen Datenübermittlung
aufgerufen werden, kann noch Einfluss auf die Operation genommen
werden. Durch Zuweisen von DND.DROP_NONE an das Feld detail des
DropTargetEvents kann die Operation abgelehnt werden, durch das
Zuweisen eines anderen Operationscodes kann die Operation modifi-
ziert werden.
Die eigentliche Datenübergabe erfolgt über das data-Feld des Drag-
SourceEvents bzw. des DropTargetEvents. Wie bei der Zwischenablage
(siehe Abschnitt 8.9.1) können die Daten in verschiedenen Formaten
übergeben werden, die – genau wie dort – mit Hilfe von Transfertypen
beschrieben werden. Die Felder dataType bzw. currentDataType der
Ereignisobjekte enthalten den aktuellen Transfertyp.
In Abschnitt 10.6 zeigen wir die Implementierung einer
Drag&Drop-Datensenke am praktischen Beispiel. Einen ausführlichen
Artikel über SWT-basiertes Drag&Drop findet man in der Eclipse Cor-
ner (www.eclipse.org) [Irvine2003].
8.11 Ressourcenverwaltung 215

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.

Also kurz gesagt, es gilt das Verursacherprinzip. Am Ende eines Pro-


gramms braucht man freilich die Ressourcen nicht selbst zu entsorgen,
das Betriebssystem sorgt dann schon automatisch dafür. Es geht hier
also lediglich um Ressourcen, die nur in einem bestimmten Abschnitt
und zu einer bestimmten Zeit von einer Applikation belegt werden.
Das klingt zunächst recht einfach, erweist sich in der Praxis aber
mitunter als kompliziert. Oft ist es so, dass eine Farbe, eine Schriftart
oder ein Bild an mehreren Stellen eines Programms eingesetzt wird.
Wer ist dann für die Entsorgung zuständig? Und ist es wirklich not-
wendig, eine Ressource zu entsorgen, die kurz hinterher wieder an
anderer Stelle verwendet wird und dann neu erzeugt werden muss?
Aus diesen Gründen greift man – fast wie im richtigen Leben – auf ResourceStore
die Idee des Leihhauses zurück. Man implementiert einen Resource-
Store, eine Instanz, die über das Leben mehrerer Ressourcen wacht,
und erst dann, wenn der ResourceStore selbst entsorgt wird, werden
auch seine Ressourcen entsorgt. Damit wird es auch möglich, die glei-
che Ressource mehrfach zu verwenden. Das ist insbesondere bei Bil-
dern nützlich, die oft recht speicherhungrig sind.
Wir zeigen hier kurz das Prinzip eines ResourceStore an einem Bei-
spiel für das Verwalten von Farbressourcen. Erhält die Klasse Color-
Store eine Anfrage nach einer Farbe, die sie noch nicht kennt, so wird
eine Color-Instanz neu erzeugt und dem ColorStore zugefügt. Ist die
Farbe bereits bekannt, so wird die Anfrage aus dem ColorStore befrie-
digt. Erst wenn für den ColorStore die dispose()-Methode aufgerufen
wird, werden alle Farbressourcen freigegeben.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
216 8 Das SWT

public class ColorStore {

private static Map store = new HashMap();

/**
* 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();
}
}
}

Der Aufruf sieht dann z.B. so aus:


Color green = ColorStore.getColor("green",display,0,255,0);

Da alle Methoden der Klasse ColorStore statisch sind, kann Color-


Store die Farben der gesamten Applikation verwalten. Erst wenn man
keine Farben mehr braucht, entsorgt man alle Farben mit:
ColorStore.dispose();

In Abschnitt 9.1 werden wir einige vorgefertigte Registraturen für


Schriftarten und Bilder diskutieren.
8.12 Windows32-Unterstützung (OLE) 217

8.12 Windows32-Unterstützung (OLE)


SWT stellt eine spezielle Bibliothek für die Unterstützung des OLE-
Mechanismus in Microsoft Windows Betriebssystemen zur Verfügung.
Der Microsoft Win32 Object Linking and Embedding-Mechanismus
(OLE) wird über die Klassen der Package org.eclipse.swt.ole.win32
unterstützt. OLE gestattet es, OLE-Dokumente oder ActiveX-Kontroll-
elemente in andere Applikationen (Container) einzubetten. So wird es
z.B. möglich, den Microsoft Internet Explorer als SWT-GUI-Element
zu verwenden (das Browser-Widget ist so implementiert), oder aber ein
Microsoft-Office-Dokument in ein SWT-GUI einzubetten. Um diese
Klassen nutzen zu können, sollte man über ausreichende OLE-Kennt-
nisse verfügen. Ein kleines Beispiel-Plugin findet man in der Eclipse-Bei-
spielsammlung unter org.eclipse.swt.examples.ole.win32_3.0.0.

8.13 SWT auf dem Pocket PC


Die Microsoft Pocket-PC-Plattform ist eine gültige Ablaufplattform
für SWT-Anwendungen. Allerdings gelten dabei eine Reihe von Rand-
bedingungen, die es bei der Erstellung von SWT-Applikationen für
unter WindowsCE betriebenen Maschinen zu beachten gibt:
! Noch mehr als Desktop-Anwender legen PDA-Anwender größten
Wert auf Benutzungskomfort.
! Die Prozessoren sind langsamer als bei Desktop-Maschinen.
! Der verfügbare Speicherplatz ist meist kleiner als bei Desktop-
Maschinen.
! Die Größe des Anzeigeschirms ist im Vergleich zum Desktop stark
eingeschränkt.
! Oft steht keine Tastatur, sondern nur ein Stift zur Verfügung.
Ist keine physische Tastatur vorhanden, ist es gängige Praxis, für Text-
eingaben eine emulierte Tastatur auf der Anzeigeeinheit abzubilden.
Das verringert den für andere Fenster verfügbaren Platz. Pocket-PC-
Anwendungen sollten deshalb beim Erzeugen einer Shell die Stilvari-
able SWT.RESIZE verwenden, um eine automatische Größenanpassung
zu ermöglichen.
Um die Größe der SWT-Bibliothek zu verkleinern, wurden bei der
Pocket-PC-Version, die auf www.eclipse.org erhältlich ist, einige
Packages weggelassen. Dazu gehören:
! org.eclipse.swt.dnd (Drag&Drop, siehe Abschnitt 8.10.2)
! org.eclipse.swt.ole (OLE, siehe Abschnitt 8.12)
! org.eclipse.swt.accessibility (Eingabehilfen, siehe Abschnitt 8.14)
218 8 Das SWT

! org.eclipse.swt.custom (Spezielle Widgets, siehe Abschnitt 8.5.13)


! org.eclipse.swt.printing (Druckfunktion, siehe Abschnitt 8.9)
! org.eclipse.swt.program (Dateiassoziationen)
Natürlich kann man auch eine SWT-Bibliothek nach eigenen Bedürf-
nissen zusammenstellen. Durch Löschen unbenutzter Klassen kann die
Bibliothek weiter verkleinert werden. Der Pocket-PC-Artikel [Cornu2003]
erklärt detailliert, wie man das macht, und wie man auch die Startzeit
einer Pocket-PC-Applikation minimieren kann.

8.14 Behindertengerechte Software


Zum Schluss gehen wir noch kurz darauf ein, wie das SWT die Gestal-
tung behindertengerechter Software unterstützt. Insbesondere bei
kommerziellen Entwicklungen ist die behindertengerechte Gestaltung
der Benutzeroberfläche ein wichtiger Punkt. So dürfen beispielsweise
viele Behörden nur Softwareprodukte erwerben, die behindertenge-
recht gestaltet wurden.
In der Eclipse-Dokumentation gibt es ein besonderes Kapitel, wie
SWT-basierte Oberflächen behindertengerecht gestaltet werden kön-
nen. Wir finden diesen Abschnitt unter Platform Plugin Developer
Guide unter Reference>Other Reference Information>Tips For
Making User Interfaces Accessible.
Manche Betriebssysteme unterstützen spezielle behindertenge-
rechte Endgeräte und stellen dafür eine Programmierschnittstelle zur
Verfügung. Eclipse unterstützt das Microsoft Active Accessibility
(MSAA) API. Diese Unterstützung wird durch die Klassen im Package
org.eclipse.swt.accessibility bereitgestellt. SWT-Control-Instan-
zen können durch den Aufruf getAccessible() eine Instanz der Klasse
Accessible bereitstellen, die als Bindeglied zum Accessibility API
dient.
417

12 Eigene Produkte auf der


Grundlage von Eclipse entwickeln

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

Plugin Plugin Plugin

Fragment Fragment Fragment

Feature

Abb. 12–1 Features können mehrere Plugins in einer Auslieferungseinheit vereinen.


Plugins können durch zusätzliche Fragmente, die in einem separaten Feature ausgeliefert
werden, erweitert werden.

Für die Auslieferung des fertigen Produkts gibt es verschiedene Optio-


nen. So kann man ein Plugin mühelos als ZIP-Datei ausliefern, die
dann einfach in das eclipse/plugins-Verzeichnis entpackt wird. Die
Installation von Eclipse verläuft ja auch nicht anders. Eine weitere
Möglichkeit ist der Einsatz von kommerziellen Installationsmanagern
418 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

wie InstallAnyWhere oder InstallShield. Schließlich bietet Eclipse noch


eine eigene, relativ elegante Installationsmöglichkeit an, den Eclipse
Update-Manager. Diese Möglichkeit kommt allerdings nur bei der
Auslieferung von Eclipse-Erweiterungen in Betracht, nicht jedoch bei
der Auslieferung kompletter eigenständiger Produkte.
Bei Letzteren ist auch die Frage der Produktgestaltung interessant.
Ein eigenständiges Produkt soll wahrscheinlich nicht mit dem Eclipse-
Splash-Screen starten. Auch die Gestaltung der About-Seiten und der
Hilfeseiten wird in der Regel von der Standardlösung abweichen.
Anwendungen, die auf der Rich Client Platform (siehe Kapitel 14) auf-
bauen, benötigen ebenfalls Modifikationen in der Installationskonfi-
guration.
Schließlich werden wir die Frage der Internationalisierung behan-
deln. Eclipse bietet hier verschiedene Möglichkeiten, um eine Applika-
tion nationalen oder kulturellen Eigenheiten anzupassen.
Zum Schluss sei noch erwähnt, dass es für die Auslieferung von
Fehlerkorrekturen die Möglichkeit gibt, Feature-Patches zu erzeugen.

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

Sofern noch keine ANT-Konfiguration existiert, muss zunächst eine


neue Konfiguration erstellt werden. Das geschieht ganz ähnlich wie bei
der Run-Konfiguration (siehe Abschnitt 7.2). Rufen Sie einfach die
Funktion Run>External Tools>External Tools ... auf.
12.1 ANT-Einbindung 419

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.

ANT wird als externes Werkzeug ausgeführt. Das bedeutet, dass es


außerhalb des Eclipse-Workspace läuft. Die von ANT durchgeführten
Änderungen an Ressourcen oder auch neu erzeugte Ressourcen
erscheinen deshalb zunächst nicht im Resource Navigator der Work-
bench. Nach einem ANT-Lauf ist deshalb in der Regel die Anwendung
der Kontextfunktion Refresh auf den Projektordner erforderlich.
Allerdings kann dieser Vorgang auch automatisiert werden.
! Auf der Seite Refresh des Konfigurationsdialogs kann festgelegt
werden, für welche Ressourcen ein Refresh durchgeführt werden
soll, wenn der ANT-Lauf beendet ist.
! Auf der Seite Targets können die einzelnen ANT-Targets ausge-
wählt werden, die ausgeführt werden sollen.
! Auf der Seite Classpath können Modifikationen am Classpath vor-
genommen werden, unter dem ANT abläuft. Hier Änderungen
vorzunehmen, wird nur erforderlich, wenn man eigene Erweiterun-
gen in Form von Java-Klassen in ANT eingebracht hat.
420 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

! Auf der Seite Properties können zum einen manuell ANT-Variab-


lenbelegungen definiert, zum anderen aber auch Property-Dateien
angegeben werden, die solche Variablenbelegungen enthalten.
! Auf der Seite JRE kann eine andere JRE für die Ausführung des
Ant-Build-Prozesses angegeben werden. Normalerweise wird die
JRE, unter der Eclipse abläuft, verwendet.
! Wird eine andere JRE verwendet, so können auf der Seite Environ-
ment Umgebungsvariablen spezifiziert werden.
Für Eclipse-typische Arbeiten übernimmt jedoch Eclipse die Erstellung
der Skripte, insbesondere für die Zusammenstellung von Ausliefe-
rungsarchiven für Features, Plugins und Fragmente automatisch und
löscht sie auch wieder nach Benutzung. Die Generierung dieser Skripte
wird durch die Datei build.properties gesteuert (siehe Abschnitt
12.4.1). Sollten Sie expliziten Bedarf für Ant-Skripte haben, können Sie
diese manuell generieren, indem Sie eine Manifest-Datei wie plu-
gin.xml, fragment.xml oder feature.xml selektieren und dann die Kon-
textfunktion PDE Tools>Create Ant Build File. aufrufen. Eclipse legt
die erforderlichen Skript-Targets dann in der Datei build.xml ab.

Abb. 12–3 Der Ant-Editor. Mit Strg+Leertaste kann der Inhaltsassistent aufgerufen wer-
den.
12.2 Plugins und Fragmente 421

Die eigene Erstellung bzw. die Modifizierung generierter ANT-


Skripte verlangt natürlich Kenntnisse in der ANT-Skriptsprache.
Detaillierte Informationen über ANT erhält man auf der Apache-Web-
site ant.apache.org. Daneben gibt es inzwischen auch zwei deutsch-
sprachige Bücher: [Edlich2002] und [Matzke2003].
Ant-Skripte können mit dem Ant-Editor bearbeitet werden (übli- Ant-Skripte editieren
cherweise reicht ein Doppelklick auf ein Ant-Skript, um diesen Editor
aufzurufen). Der von Eclipse bereitgestellte Ant-Editor ist ziemlich
komfortabel. Er verfügt über Syntaxkolorierung, einen Outline View
und einen Inhaltsassistenten.

12.2 Plugins und Fragmente


Plugins bestehen im Minimalfall nur aus der Datei plugin.xml. Nor-
malerweise kommen aber noch binäre Java-Dateien hinzu – insbeson-
dere die Plugin-Klasse – aber auch Hilfeseiten, Inhaltsverzeichnisse,
Kontextbeschreibungen, Icons, Schemata und andere Ressourcen.
In manchen Fällen kann es sinnvoll sein, ein Plugin in mehrere Frag-
mente aufzuteilen. So wird man die Kernfunktionalität eines Plugin
manchmal schon so früh wie möglich ausliefern und dann ergänzende
Komponenten wie Sprachpakete oder Unterstützung für andere Betriebs-
systeme später nachschieben. Genau das ist mit Fragmenten möglich.
Fragmente werden auf ähnliche Weise mit dem New-Wizard
erzeugt wie Plugins (siehe Abschnitt 11.4.1). Nur wählt man als Pro-
jekttyp statt Plugin Project den Typ Fragment Project. Statt einer
Manifest-Datei plugin.xml wird dann eine Datei fragment.xml
erzeugt, die aber einer Plugin-Manifest-Datei sehr ähnlich sieht. Auch
wird keine Plugin-Klasse erzeugt, denn Fragmente dürfen keine eigene
Plugin-Klasse besitzen. Zusätzlich muss in der Manifest-Datei frag-
ment.xml noch die Identifikation und Version des zugehörigen Plugin
angegeben werden.
Das Fragment kann dann unabhängig vom Rest des Plugin entwi-
ckelt und getrennt ausgeliefert werden. Bei der Installation wird es in
das zugehörige Plugin hineingemischt, so dass vom Plugin aus auf die
Funktionalität des Fragments zugegriffen werden kann. Plugins sollten
allerdings so geschrieben sein, dass sie auch dann ablauffähig sind,
wenn Fragmente fehlen. Für den Endbenutzer ist das Fragment nicht
als separate Einheit erkennbar – es geht völlig im Plugin auf.

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.

Abb. 12–4 Die Overview-Seite des Feature-Editors.

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

Ankreuzen von Exclusive Install kann das Installieren anderer Fea-


tures verhindert werden. Außerdem kann man noch ein Banner Image
spezifizieren, das erscheint, wenn das Plugin zum ersten Mal aktiviert
wird.
In der Rubrik Supported Environments kann zudem noch der Ein-
satz auf bestimmte Betriebs- und Windowing-Systeme, nationale Spra-
chen und Prozessorarchitekturen beschränkt werden. Wird hier nichts
angegeben, so kann das Feature überall installiert werden.
Mit einem Druck auf die Taste Versions ... kann man bestimmen,
ob die Versionsnummer des Features aus den Versionsnummern der
Plugins bestimmt werden soll, oder ob die Versionsnummer des
Features von den Plugins übernommen werden soll. Wichtig ist nach
der Änderung von Versionsnummern in Plugins, diese Taste erneut zu
betätigen, um das Feature-Manifest auf den neuesten Stand zu brin-
gen! Zur Taste Export ... kommen wir in Abschnitt 12.4.1.
Auf der nächsten Seite Information kann ein Beschreibungstext
eine Copyright-Notiz und Lizenzbedingungen definieren. Zusätzlich
zu expliziten Texten kann hier auch mit URLs auf HTML-Seiten ver-

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

wiesen werden. Allerdings sollten die Lizenzbedingungen sowohl


explizit als auch per URL angegeben werden. Die expliziten Texte wer-
den dem Benutzer vor der Installation zur Annahme vorgelegt, die
Texte, auf die per URL verwiesen wird, erscheinen, wenn der Anwen-
der die Funktion Help>About Eclipse Platform>Feature Details>More
Info anklickt. Auf der Seite Advanced können noch bereits existie-
rende Features und externe Archivdateien diesem Feature zugefügt
werden. Auch können hier spezielle Install Handlers definiert werden
(siehe Abschnitt 12.5).

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.

12.4.1 Features ausliefern

Eine Auslieferungseinheit eines Features kann durch einen Klick auf


die Export ...-Taste in der Seite Overview des Feature Editors erzeugt
werden. Dabei haben Sie die Wahl zwischen einer einzigen ZIP-Datei,
die am spezifizierten Ort (Destination) erzeugt wird (eine solche Datei
kann leicht durch Entpacken in das Eclipse-Wurzelverzeichnis
\eclipse\ installiert werden), und individuellen JAR-Dateien für eine
Update-Site (siehe Abschnitt 12.4.2). Ein dritte Möglichkeit ist der
Export der auszuliefernden Dateien in eine spezifizierte Verzeich-
nisstruktur. Wahlweise können Sie auch den Quellcode der im Feature
enthaltenen Plugins zusammen mit den Binärdateien ausliefern.
Wenn die Export-Funktion gestartet wird, erzeugt Eclipse intern
ein Ant-Skript (siehe Abschnitt 12.1), führt den Ant-Build-Prozess mit
diesem Skript aus und löscht anschließend das Skript. Wollen Sie das
Skript lieber behalten, so kreuzen Sie einfach die Option Save this
export operation as an Ant build script an und spezifizieren noch einen
12.4 Deployment 425

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.2 Eigenständige Produkte ausliefern

Ein auf Eclipse basierendes Produkt besteht natürlich zunächst einmal


aus der Eclipse-Ablaufumgebung. Hier ist es nicht nötig, das komplette
SDK mit auszuliefern. Es reicht aus, die Eclipse-Laufzeitumgebung und
die von der eigenen Applikation benötigten Plugins mit auszuliefern.
Minimale Eclipse-Laufzeitumgebungen sind übrigens auf der eclipse.org-
Website als Downloads verfügbar.
Die Dependencies-Seite des Manifests plugin.xml ist ein guter Per ZIP
Anhaltspunkt dafür, welche Plugins mit ausgeliefert werden müssen.
Dazu kommen natürlich noch die eigenen Plugins und das Manifest
des Features.
Im einfachsten Fall kann man mit der Export ...-Funktion im Fea-
ture Editor (siehe Abschnitt 12.4.1) alles zusammenzippen und dem
426 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

Anwender die Installation überlassen. Schließlich funktioniert die


Installation beim Eclipse-SDK auch nicht anders. Das funktioniert
recht gut, mindestens solange man für jede Plattform eine getrennte
Auslieferung bereitstellt.
Installationshilfe Im komplexeren Fällen – z.B. wenn man die Auslieferung für ver-
schiedene Betriebssysteme und Sprachen kombinieren will oder wenn
man eine Java-Umgebung mit ausliefern möchte – sollte man jedoch auf
eine Installationshilfe wie InstallShield oder InstallAnyWhere zurück-
greifen. Derartige Installationshilfen benötigen Skripte, die noch zu
erstellen sind. Die beste Art, die auszuliefernden Ressourcen aus
Eclipse herauszuziehen, ist wieder die Anwendung der Export ...-
Funktion im Feature Editor, wobei allerdings a directory structure als
Ausgabeformat (Deploy as) gewählt wird. Eine detaillierte Anleitung
zum Thema Installationshilfen findet man im Eclipse-Hilfesystem
unter Platform Plugin Developer Guide>Programmer’s Guide>
Packaging and delivering Eclipse based products>Product installation
guidelines.
Binärobjekte In beiden Fällen muss man sich jedoch darüber klar werden, was
in welchen Verzeichnissen ausgeliefert wird. Zunächst ist es erforder-
lich, die Classpath-Archive erstellen. Das erreicht man am einfachsten
durch den Aufruf der Kontextfunktion Run Ant ... auf die Datei
build.xml im jeweiligen Plugin-Projekt. Existiert eine solche Datei
noch nicht, kann man sie durch Anwendung der Kontextfunktion Cre-
ate Ant Build File auf die Manifest-Datei plugin.xml erzeugen.
Andere Ressourcen Dann exportiert man alle eigenen Plugin-Projekte, Fragment-Pro-
jekte und Feature-Projekte in ein Auslieferungsverzeichnis (Export>
File System). Der Name des Verzeichnisses muss dabei jeweils dem Plu-
gin-Namen entsprechen. Das bedeutet, dass Fragment-Projekte in das
Auslieferungsverzeichnis ihres Ziel-Plugins exportiert werden. Nicht
exportiert werden die Unterverzeichnisse mit den Quelldateien (es sei
denn, man möchte Quelldateien mit ausliefern) und mit den Binärda-
teien (diese sind ja bereits im erstellten Classpath-Archiv enthalten).
Außerdem werden auch die Dateien .project, .classpath, .template,
build.xml und build.properties nicht benötigt. Bei der Installation
werden dann die Plugin-Auslieferungsverzeichnisse in das Verzeichnis
.../eclipse/plugins installiert, die Feature-Auslieferungsverzeich-
nisse dagegen in das Verzeichnis .../eclipse/features.

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

lich eigenständiges Auftreten zu erreichen, sind noch einige weitere


Anpassungen erforderlich. Diese Anpassungen lassen sich durch die
Definition einiger weiterer Dateien erreichen, wie in der Tabelle aufge-
listet. Alle diese Dateien (bis auf splash.bmp) werden im Feature-
Plugin, also dem Plugin, das die gleiche Identifikation wie das Feature
hat, gespeichert und im einfachsten Fall über die Angaben in Datei
about.ini gesteuert.

Diese Datei beschreibt Feature-Eigenschaften. Das Dateiformat entspricht dem


einer Properties-Datei (java.io.Properties). Die folgenden Eigenschaften können
spezifiziert werden.
! aboutText. Anzuzeigender Text für den About-Dialog.
! windowImage. Verweist auf ein Icon der Größe 16x16 Pixel. Das Icon erscheint
links oben in der Titelzeile aller Fenster. Diese Angabe ist nur für primäre Fea-
tures notwendig.
! featureImage. Verweist auf ein Icon der Größe 32x32 Pixel. Das Icon erscheint
bei der Feature-Beschreibung im About-Dialog.
! aboutImage. Verweist auf ein Bild der Größe 500x330 oder 115x164. Das Bild
about.ini
erscheint bei der Produktbeschreibung im About-Dialog.
! appName. Enthält den Namen der Applikation. Diese Angabe ist nur für primäre
Features notwendig.
! welcomePerspective. Enthält die Identifikation der Perspektive (siehe Abschnitt
11.5.11), die nach der Installation des Plugin geöffnet werden soll.
! tipsAndTricksHref. Verweist auf eine Tipps&Tricks-HTML-Seite. Die Seite erhält
dann auf der Zielplattform einen Eintrag unter Help>Tips and Tricks....
Die gleichen Einstellungen können übrigens in den properties-Elementen des
Erweiterungspunktes org.eclipse.core.runtime.products gemacht werden
(siehe Abschnitt 11.4.3).
HTML-Seite mit zusätzlichem Text über das Plugin oder das Feature. Diese Seite
about.html
erscheint, wenn im About-Dialog die Taste More Info gedrückt wird.
Diese Datei kann Parameterwerte enthalten, die in About-Texte eingesetzt werden.
Das Dateiformat entspricht dem einer Properties-Datei (java.io.Properties).
about.mappings Beispiel:
aboutText in about.ini enthält den Text »Dieses Produkt hat die Registriernummer {0}«.
Dazu passend könnte about.mappings den Text »0=2342-8A8S-234B« enthalten.
Enthält übersetzte Eigenschaften aus about.ini (nur erforderlich bei mehrsprachi-
about.properties
ger Auslieferung, siehe Abschnitt 12.6).
Diese Datei kann Vorbelegungen für Präferenzen (siehe Abschnitt 11.2.3) für
andere Plugins enthalten. Das Dateiformat entspricht dem einer Properties-Datei
(java.io.Properties). Bedingung ist allerdings, dass die Identifikationen der Präfe-
plugin_customization.ini renzen dieser Plugins öffentlich sind. Beispielsweise können die Präferenzen der
Workbench anders vorbelegt werden:
org.eclipse.ui/defaultPerspectiveId=com.us.prod.ourPerspective
plugin_customization. Enthält übersetzte Eigenschaften aus plugin_customization.ini (nur erforderlich
properties bei mehrsprachiger Auslieferung, siehe Abschnitt 12.6).

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

Auf fortgeschrittene Techniken der Produktgestaltung im Rahmen der


Rich Client Platform gehen wir in Abschnitt 14.6 ein.

12.4.4 Den Workspace bevölkern

Will man Beispielprojekte und -dateien mit ausliefern, so sollte man


darauf verzichten, den Ordner workspace schon bei der Installation zu
füllen. Die Projekte und Dateien würden nämlich nicht im Eclipse-
Workspace erscheinen, da die erforderlichen Metadaten fehlen. Und
den Ordner .metadata sollte man keinesfalls mit ausliefern – die darin
enthaltenen Dateien sind konfigurations-, plattform- und versionsab-
hängig.
Stattdessen ist es besser, alle Beispieldateien mit im Plugin-Ver-
zeichnis auszuliefern. Eine Möglichkeit, die Dateien im Workspace zu
installieren, wäre bei der ersten Ausführung der Plugin-Klasse. Doch
leider wird die Plugin-Klasse erst aktiviert, wenn sie tatsächlich benö-
tigt wird. Das ist gewöhnlich dann der Fall, wenn die erste zum Plugin
gehörige Ressource geöffnet werden soll. Es ist natürlich nicht beson-
ders anwenderfreundlich, wenn die Beispieldateien erst dann im Work-
space erscheinen.
Eine andere Möglichkeit – und das ist wahrscheinlich die elegan-
teste, da sie dem Benutzer die Wahl lässt – ist, die Installation der Bei-
spieldateien von einem Cheat Sheet (siehe Abschnitt 11.5.14) aus zu
starten. Das folgende Beispiel zeigt, wie die Implementierung der zuge-
hörigen Aktion aussehen könnte:
package com.bdaum.multimedia.studio.actions;

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;

public class InstallFirstProjectAction extends Action {


12.4 Deployment 429

private static String EXAMPLE_PROJECT = "firstStudio";


private static String EXAMPLE_FILE = "HipHopStudio.mms";

//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);
}
}
}
}

12.4.5 Update-Sites erstellen

Features, welche die im Feld existierenden Eclipse-Plattformen erwei-


tern sollen, werden am besten in einem vom Update-Manager (siehe
Abschnitt 12.5) unterstützten Format ausgeliefert. Lassen Sie sich vom
Namen des Managers nicht verwirren – Sie können damit auch kom-
plett neue Plugins und Features ausliefern. »Update« bezieht sich auf
die Eclipse-Plattform und nicht auf das einzelne Plugin oder Feature.
Natürlich kann man auch Plugins manuell zusammenzippen und
ausliefern, doch die Verwendung des Update-Managers hat einige Vor-
teile. Bei der Installation kann geprüft werden, ob benötigte andere
Plugins vorhanden sind und wenn ja, ob die Versionen kompatibel sind.
Überdies kann geprüft werden, ob das Betriebssystem, das Windowing-
System und die Prozessorarchitektur für eine Installation geeignet sind.
Vor einer Installation kann der Update-Manager die Lizenzbedingungen
vorlegen und den Benutzer um Annahme derselben auffordern. Außer-
dem wird die Installation direkt von einer Webadresse unterstützt, so
dass es ausreicht, nur die Installations-URL auszuliefern.
Update-Site Um ein Plugin für die Installation mit dem Update-Manager aus-
zuliefern, muss man eine Update-Site erstellen. Eine solche Update-Site
besteht aus zwei Ordnern – dem features-Ordner und dem plugins-
Ordner – und aus einem Site-Manifest site.xml. Dabei kann eine
Update-Site durchaus mehrere Features enthalten, die selektiv instal-
liert werden können.
12.4 Deployment 431

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.

Abb. 12–6 Der Site-Editor.

! 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

für alle Features den Build-Prozess durchzuführen, betätigen Sie


die Taste Build all.
! Auf der Seite Site Layout können Sie beschreibenden Text einge-
ben, und eine URL, die auf die veröffentlichte Update-Site im Web
zeigt. Unter dieser Adresse sollten Sie auch eine Datei index.html
bereitstellen, die Besucher dieser Adresse über die Site und die
Installation informiert. Zum Beispiel können Sie hier die Datei
index.html verwenden, die Sie beim Anlegen der Update-Site gene-
riert haben (siehe oben). Außerdem können Sie auf dieser Seite
noch die Namen der Feature- und Plugin-Ordner ändern. Falls Sie
noch zusätzliche Dateien, die nicht in diesen Ordnern enthalten
sind, zur Update-Site hinzufügen wollen, können Sie das durch die
Definition von Pfad-auf-URL-Abbildungen tun.

12.5 Von einer Update-Site installieren


Auf den Eclipse Update Manager kann über den Menüpunkt Help>
Software Updates zugegriffen werden. Er bietet Funktionen für das
Installieren und Entfernen von Features, für die Aktualisierung instal-
lierter Features und für die Verwaltung der Plattform-Konfiguration.

12.5.1 Update-Site hinzufügen

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.

12.5.2 Features installieren

Nachdem Sie die neue Update-Site hinzugefügt haben, markieren Sie


alle Update-Sites in der Liste, in denen nach installierbaren Features
gesucht werden kann. Dann drücken Sie die Next-Taste. Eclipse geht
12.5 Von einer Update-Site installieren 433

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.

12.5.3 Features aktualisieren

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.4 Die Eclipse-Konfiguration verwalten


Features deaktivieren Fall Sie ein Feature deaktivieren oder entfernen wollen, oder wenn Sie
zu einer früheren Plattformkonfiguration zurückkehren möchten, fin-
den Sie die notwendigen Funktionen unter Help>Software Updates>
Manage Configuration... Auf der linken Seite des Dialogfensters sehen
Sie den Baum mit der aktuellen Konfiguration. Durch einen Klick auf
ein ›+‹-Icon können Sie Baumknoten expandieren und so die Details
der Konfiguration anschauen. Um ein Feature zu deaktivieren, selek-
tieren Sie es und klicken dann auf den Disable-Hyperlink auf der rech-
ten Seite. Anschließend können Sie das Feature wieder aktivieren
(Enable) oder komplett entfernen (Remove).
Wenn Sie den Wurzelknoten Eclipse Platform selektieren, finden
Sie noch einige weitere Funktionen auf der rechten Seite des Fensters.
Durch Anklicken des Hyperlinks Revert to Previous können Sie die
Plattform in den Zustand versetzen, den sie vor Anbringen der letzten
Änderung hatte. Falls Sie vorher Features durch einfaches Entpacken
in das Eclipse-Wurzelverzeichnis zur Plattform hinzugefügt hatten, ist
der Hyperlink Process detected changes sichtbar. Durch Anklicken die-
ser Hyperlinks können Sie die noch fehlenden Installationsschritte für
diese Features nachholen.

12.5.5 Install-Handler

Der Eclipse-Update-Manager kann durch so genannte Install-Handler


erweitert werden. Dies sind Java-Klassen, die das Interface IInstall-
Handler aus Package org.eclipse.update.core implementieren (und
dabei gewöhnlich auf der Klasse BaseInstallHandler aufsetzen). Die
verschiedenen Methoden einer solchen Klasse werden an bestimmten
Punkten des Installations- oder Update-Prozesses aufgerufen und kön-
nen dann spezifische Aktionen durchführen.
Install-Handler können vorab als globale Install-Handler definiert
werden. Dies geschieht in der Manifest-Datei plugin.xml eines geeig-
neten Plugin am Erweiterungspunkt org.eclipse.update.core.install-
Handlers. Alternativ können aber auch die Install-Handler-Klassen im
Installationspaket selbst enthalten sein, lassen sich dann aber nur für
die jeweilige Installation nutzen (lokale Install-Handler).
Um Install-Handler bei der Installation eines Features nutzen zu
können, muss die Verwendung der Install-Handler auf der Advanced-
Seite des Feature-Manifests (siehe Abschnitt 12.3.1) deklariert werden.
Dabei wird im Feld Library der Name des mitgelieferten Archivs ange-
geben, das den Install-Handler enthält. Im Feld Handler wird der
Name der IInstallHandler-Klasse selbst angegeben. Wird kein lokaler,
12.6 Produkte internationalisieren 435

sondern ein globaler Install-Handler verwendet, so bleibt das Feld


Library leer und das Feld Handler spezifiziert die Identifikation, unter
welcher der globale Install-Handler installiert wurde.
Eine typische Anwendung von Install-Handlern ist die Installation
von Ressourcen außerhalb der Eclipse-Plattform, z.B. wenn man eine
Java-Umgebung mit ausliefern will.

12.6 Produkte internationalisieren


Eigentlich sollte ich hier gar nichts zu diesem Thema schreiben, denn
es existiert ein ausgezeichneter Artikel von Dan Kehn, Scott Fair-
brother und Cam-Thu Le [Kehn2002] über dieses Thema auf eclipse.
org. Eine kurze Zusammenfassung sollte deshalb genügen. Ein Beispiel
aus der Praxis wird in Abschnitt 13.12 diskutiert.
Oft wird unter Internationalisierung verstanden, dass die dem
Endbenutzer präsentierten Texte in die Landessprache übersetzt wer-
den sollen. Das ist zwar ein wichtiger Bestandteil der Internationalisie-
rung, doch bei weitem nicht der einzige. So können auch Bilder und
Icons kulturell verschiedene Bedeutung haben und sollten deshalb
angepasst werden, und viele von uns kennen die Konfusion (europä-
isch/amerikanisch) der Datumsformate. Aber auch die Anordnung von
GUI-Elementen kann kulturell unterschiedlich sein. So wird zum Bei-
spiel in den arabischen Ländern und in Israel von rechts nach links
gelesen, und in vielen asiatischen Ländern von oben nach unten. So hat
z.B. eine Waschmaschinenreklame mit dem Text »Große Wirkung in
wenigen Minuten« in arabischen Ländern zu großer Heiterkeit
geführt. Die Reklame zeigte links die schmutzige, rechts die saubere
Wäsche.
Entsprechend verschieden sind die Erwartungen, wo sich wichtige
Formularelemente befinden. Auch können die in verschiedenen Spra-
chen verschieden langen Textkonstanten Einfluss auf das Formular-
layout haben.
Trotzdem wollen wir uns hier auf Textelemente beschränken.
Andere Internationalisierungen wie Bilder und Icons, Währungs- und
Datumsformate lassen sich oft auf die Übersetzung von Textelementen
(Bildname, Formatstrings) abbilden.

12.6.1 Textkonstanten in Programmen

Am einfachsten ist die Internationalisierung von Textkonstanten in


Programmen, denn hier bietet Eclipse im Java-Programmeditor mit der
Kontextfunktion Source>Externalize Strings eine hervorragende
436 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

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

geschehen, die in den String-Literalen der Properties-Datei z.B. in fol-


gender Form definiert werden können:
Editor.save.SVG.error=Error saving SVG file {0} in folder {1}

Verwendet man solche Parameter, so erweitert man am besten die Mes-


sages-Klasse um eine parametrisierte Variante der Methode get-
String():
public static String getString(String key, Object[] params) {
if (params == null)
return getString(key);
try {
return java.text.MessageFormat.format(getString(key), params);
} catch (Exception e) {
return "!"+key+"!";
}
}

12.6.2 Textkonstanten in Manifest-Dateien

In den verschiedenen Manifest-Dateien wie plugin.xml, feature.xml,


fragment.xml, site.xml usw. hat man es etwas schwerer, Textkonstan-
ten zu internationalisieren, da eine entsprechende Werkzeugunterstüt-
zung fehlt.
Hier geht man so vor, dass man zu jeder dieser Dateien eine korre-
spondierende Properties-Datei anlegt, also plugin.properties, fea-
ture.properties, site.properties, about.properties usw. Die Aus-
nahme ist fragment.xml. Hier wird statt fragment.properties die
Datei plugin.properties verwendet.
In der Originaldatei ersetzt man die übersetzbaren String-Konstan-
ten durch einen Schlüsselbegriff, der durch ein vorangestelltes %-Zei-
chen gekennzeichnet wird. In der korrepondierenden Properties-Datei
legt man dann die Definition des Schlüssels ab. Dann können diese
Properties-Dateien, wie im vorigen Abschnitt bereits diskutiert, über-
setzt werden.
Hier ist ein Beispiel aus einer Deklaration für einen Action Set in
einer Manifest-Datei plugin.xml:
label="Check spelling"
Dies würde man ändern in
label="%checkSpelling"
und würde in plugin.properties die Zeile
checkSpelling=Check spelling
ablegen.
438 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

12.6.3 Hilfetexte und Spickzettel

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

zeichnisstrukturen. So können z.B. deutschsprachige Hilfeseiten und


Spickzettel in einem Verzeichnis nl/de oder auch nl/de/DE abgelegt
werden, je nachdem, ob man sich auf das gesamte deutschsprachige
Gebiet oder nur auf Deutschland beziehen will. Zum Ablaufzeitpunkt
wertet Eclipse die Locale-Information der JVM aus und sucht dann
nach einem entsprechenden Ordner innerhalb des nl/-Verzeichnisses.
Wird ein solcher Ordner nicht gefunden, werden die Standardhilfesei-
ten und Spickzettel verwendet. Diese Standardseiten, die normaler-
weise in englischer Sprache abgefasst sind, werden nicht innerhalb des
nl/-Verzeichnisses abgelegt.

12.6.4 Sprachpakete ausliefern

Sprachpakete liefert man am besten als eigenständige Fragmente aus


(siehe Abschnitt 12.2). Dazu erzeugt man innerhalb eines neuen Frag-
ments eine Package-Struktur, die der Package-Struktur des zugehöri-
gen Plugin gleicht. Nur enthalten die Packages im Fragment lediglich
die in die Zielsprache übersetzten Properties-Dateien. Auch den über-
setzten nl-Ordner fügt man dem Fragment zu und markiert ihn in der
Build-Seite des Manifest-Editors (siehe Abschnitt 12.4.1), damit der
Ordner in das Auslieferungsarchiv übernommen wird. Eine Ausnahme
bilden die plugin_locale.properties Dateien. Sie verbleiben nicht im
Projektverzeichnis, sondern müssen in den src-Ordner gestellt werden.

12.7 Der Feature-Patch


Wurde ein Produkt ausgeliefert, so ist es fast unvermeidlich, dass hin-
terher Fehler entdeckt werden. Sollen Fehlerkorrekturen ausgeliefert
werden, so ist es für den Kunden oft nicht akzeptabel, dass das gesamte
Produkt erneut ausgeliefert wird. Diese gilt insbesondere dann, wenn
das Produkt groß ist. Die Zeiten für das Herunterladen des Produkts
können sehr lang sein, und in einigen Fällen werden so Online-Aktua-
lisierungen unmöglich. Das Verteilen von Korrekturen über CDs schei-
det jedoch aus Kostengründen meist aus. Glücklicherweise sind Appli-
kationen, die auf Eclipse basieren, modular aufgebaut, so dass partielle
Aktualisierungen möglich werden.
Genau das ist mit dem Feature-Patch möglich. Anstatt das gesamte
aktualisierte Feature neu auszuliefern, wird nur der Feature-Patch mit
den geänderten Plugins ausgeliefert. Der Eclipse-Update-Manager ist
schlau genug, um den Patch in das installierte Feature einzufügen.
Um einen Feature-Patch zu erzeugen, legen Sie ein neues Projekt
mit Hilfe von File>New>Other ...>Plugin Development>Feature Patch
440 12 Eigene Produkte auf der Grundlage von Eclipse entwickeln

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.

Das könnte Ihnen auch gefallen