Bisherige VBA- und Star-Basic-4-Aktivisten, die leistungsfähige Makros für Star Office 6 schreiben wollen, benötigen Informationen. Was die Office-Dokumentation nicht bietet, liefert das Linux-Magazin. Dieses Mal: Zugriff auf Textdokumente.
Das Ableben von Star Office 4 änderte den Ansatz für die Entwicklung von Makros vollkommen: An die Stelle einer geschlossenen Welt von Star-Basic-Befehlen trat ein Kosmos von Modulen und Services, die das so genannte Star-Office-API bilden.
Nachdem aufmerksame Leser in [1] bereits die Grundlagen für die Zusammenarbeit zwischen Star Basic und dem Star-Office-API vermittelt bekommen haben, liefert dieser Beitrag nützliche Tipps für den programmierten Zugriff auf Star-Writer-Dokumente. Das beginnt bei einfachen Vorgängen wie dem Öffnen der Dateien und dem Zugriff auf den Dokumenten-Inhalt, geht weiter bei der Handhabung der relativ komplexen Textmarkierungen und endet mit dem Einfügen von Steuerzeichen in Texte und diversen Suchen-und-Ersetzen-Funktionen.
Tipp 1: Ein neues Dokument
Die »loadComponentFromURL«-Methode des »Desktop«-Service ermöglicht das Anlegen von neuen Dokumenten. Das Verfahren, das in [1] auf Seite 27 beschrieben ist, erfordert allerdings einige Arbeitsschritte, weshalb es zwecks Wiederverwendung in einer benutzerdefinierten Funktion namens »NeuesDokument« (Listing 1) zu kapseln ist. Sie bekommt als einziges Argument eine Zahl zwischen 1 und 3 übergeben, die den Typ des anzulegenden Dokuments bestimmt. Eine »1« steht für ein Textdokument, »2« für eine Kalkulationstabelle und »3« für eine neue Zeichnung.
Ohne Argument oder bei einer Zahl kleiner 1 respektive größer 3 legt die Routine automatisch ein neues Textdokument an. Als Ergebnis liefert »NeuesDokument« ein Objekt zurück, das das Dokument repräsentiert. Es ist in einer Variablen vom Typ »Object« zu speichern, über die das Makro im weiteren Verlauf auf das Dokument zugreifen kann. Das Beispiel
Dim objDokument As Object objDokument = NeuesDokument(2)
legt ein neues Tabellendokument an und speichert den Verweis darauf in der Variablen »objDokument«.
Listing 1: Funktion zum Anlegen eines Dokumentes
01 Function NeuesDokument(Optional intTyp As Integer) As Object
02 Dim objDesktop As Object
03 Dim objDokument As Object
04 Dim strModul As String
05
06 If IsMissing(intTyp) Then
07 intTyp = 1
08 End If
09
10 Select Case intTyp
11 Case 2
12 strModul = "scalc"
13 Case 3
14 strModul = "sdraw"
15 Case Else
16 strModul = "swriter"
17 End Select
18
19 objDesktop = createUnoService("com.sun.star.frame.Desktop")
20 NeuesDokument = objDesktop.loadComponentFromURL _
21 ("private:factory/" & strModul, "_blank", 0, mNoArgs())
22 End Function
Tipp 2: Auf das aktuelle Dokument verweisen
Die Arbeit eines Makros richtet sich zumeist auf das aktuelle Dokument. Allerdings ist die Definition dieses Begriffs keineswegs eindeutig. Aus Sicht des Makros kann es sich dabei um das Dokument handeln, in dem es selbst gespeichert ist. Ein Zugriff auf diese Art eines aktuellen Dokuments ist mit dem Star-Basic-Bezeichner »ThisComponent« möglich, wie es das folgende Beispiel demonstriert:
Dim objDokument As Object objDokument = ThisComponent MsgBox objDokument.URL
Der Entwickler versteht unter dem aktuellen Dokument aber in der Regel jenes, das der Anwender gerade bearbeitet. Falls sich das Interesse eines Makros darauf richten soll, muss er den Bezeichner »GlobalScope.ThisComponent« verwenden, um einen entsprechenden Verweis zu erhalten. Beispiel:
Dim objDokument As Object objDokument = GlobalScope.ThisComponent MsgBox objDokument.URL
VBA-Kenner wissen: »GlobalScope.ThisComponent« entspricht dem »ActiveDocument«-Objekt von Microsoft Word, während »ThisComponent« das Star- Basic-Pendant des »ThisDocument«-Objekts darstellt.

Abbildung 1: Wenn sich das gerade aktive Makro in einem anderen Dokument (hier: »MeineMakros.sxw«) als dem aktuellen befindet, müssen Zugriffe auf das aktuelle Dokument (hier: »Hussel2.sxw«) über den Bezeichner »GlobalScope.ThisComponent« erfolgen.
Tipp 3: Auf den Inhalt eines Dokuments zugreifen
Jedes Star-Writer-Dokument besitzt einen Service namens »Text«, der seinen gesamten Textinhalt repräsentiert. Wer »Text« nutzen will, weist ihm zunächst einer Variablen vom Typ »Object« zu. Das Beispiel speichert einen Verweis auf den Inhalt des aktuellen Dokuments in der Variablen »objText«:
Dim objDokument As Object Dim objText As Object objDokument = GlobalScope.ThisComponent objText = objDokument.Text
Die Objektvariable »objText« stellt diverse Methoden zur Verfügung, um auf den Dokumentinhalt zuzugreifen. Ein Beispiel ist die »getString«-Methode, die den Text des Dokuments als Zeichenkette zurückliefert und per »MsgBox«-Anweisung angezeigt wird:
MsgBox objText.getString
Tipp 4: Auf Absätze zugreifen
Leider verfügen weder das Textdokument noch dessen »Text«-Service über eine Kollektion ( [1], Seite 29), die Absätze zusammenfasst und über einen Namen oder eine Indexzahl unmittelbar zugänglich macht. Der programmierte Zugriff auf Absätze erfordert daher einen Umweg wie ihn das folgende Beispiel zeigt:
Dim objDokument as Object Dim objText As Object Dim objAufzaehlung As Object Dim objAbsatz As Object objDokument = GlobalScope.ThisComponent objText = objDokument.Text objAufzaehlung = objText.createEnumeration While objAufzaehlung.hasMoreElements objAbsatz = objAufzaehlung.nextElement MsgBox objAbsatz.getString Wend
Das Makro richtet einen Verweis auf den Inhalt des aktuellen (Text-)Dokuments ein und speichert ihn dann in der Variablen »objText«. Der Aufruf der »createEnumeration«-Methode in der siebten Zeile legt ein temporäres Aufzählungsobjekt an, das alle Bestandteile (in diesem Fall Absätze) enthält, aus denen das Objekt besteht.
Die While-Wend-Schleife am Ende des Beispiel-Makros durchläuft sämtliche Elemente des Aufzählungsobjekts und prüft jeweils mit Hilfe der »hasMoreElements«-Methode, ob noch weitere Elemente folgen.
Ist das der Fall, speichert die Schleife den von »nextElement« gelieferten Verweis auf den nächsten Absatz in der Variablen »objAbsatz« und zeigt dessen von der »getString«-Methode (siehe Tipp 3) bereit gestellten Inhalt per »MsgBox«-Meldung an.
Tipp 5: Einen virtuellen Cursor anlegen
Der »TextCursor«-Service liefert einen virtuellen Cursor, der im Unterschied zum echten (siehe Tipp 13) unsichtbar ist. Er dient als Zielmarkierung für den programmierten Zugriff auf beliebige Textbereiche. Zum Anlegen dieses Cursors eignet sich die »createTextCursor«-Methode des »Text«-Service:
Dim objDokument as Object Dim objText As Object Dim objCursor As Object objDokument = GlobalScope.ThisComponent objText = objDokument.Text objCursor = objText.createTextCursor
Tipp 6: Den virtuellen Cursor verschieben
In der Praxis ist das neu angelegte »TextCursor«-Objekt zunächst an die gewünschte Dokumentposition zu setzen, wozu auch ein ansehnliches Arsenal von Methoden zur Verfügung steht (siehe Tabelle 1).
Um einen definierten Ausgangspunkt zu erhalten, positioniert der Programmierer den Cursor mit
objCursor.gotoStart(False)
an den Anfang des Dokuments. Die Zeile
objCursor.goRight(10, False)
verschiebt die Marke um zehn Zeichen nach rechts.
Tipp 7: Texte mit dem virtuellen Cursor markieren
Jede Bewegungsmethode des »TextCursor«-Objekts besitzt ein Argument vom Typ Boolean (in Tabelle 1 als »blnExp« bezeichnet). Es legt fest, ob der Cursor nur verschoben (»False«) oder ob er sich über den gesamten Text zwischen der letzten und der aktuellen Position ausdehnt (»True«). Die Zeile
objCursor.goRight(10, True)
würde »objCursor« folglich also auf die ersten zehn Zeichen des aktuellen Dokuments erweitern und somit eine unsichtbaren Bereichsmarkierung produzieren, zu deren Inhalt die »getString«-Methode den Zugriff verschafft:
MsgBox objCursor.getString
Wird der erweiterte Cursor nicht mehr gebraucht, ist ihm schnell eine Diät verordnet, denn
objCursor.CollapseToStart
reduziert ihn auf einen (gedachten) Strich am Anfang der ehemaligen Markierung. Bei
objCursor.CollapseToEnd
dagegen schnurrt der Cursor am Ende der ehemaligen Markierung zusammen.
Tipp 8: Mit mehreren Markierungen arbeiten
Wenn ein Makro mit mehreren Textstellen arbeiten muss (siehe Abbildung 3), lässt sich jede in einer eigenen Bereichsmarkierung speichern. Dazu legt man jeweils ein »TextCursor«-Objekt an, dehnt es auf die gewünschte Textportion aus und kopiert seinen Inhalt schließlich in ein »TextRange«-Objekt. Das Beispiel sichert den aktuellen Inhalt von »objCursor« in das »TextRange«-Objekt »objRange1«:
Dim objRange1 As Object objRange1 = objCursor
Die Variable »objRange1« macht den Inhalt der Bereichsmarkierung verfügbar.

Abbildung 3: Star-Writer-Dokumente können eine Vielzahl von Markierungen enthalten, auf die man über die »CurrentController.Selection«-Eigenschaft des Dokuments zugreifen kann.
Tipp 9: Wörter zählen und anzeigen
Während man an die Absätze eines Dokuments noch halbwegs elegant mit einem selbst gebauten Aufzählungsobjekt herankommt (siehe Tipp 4), gibt es keine direkte Zugriffsmöglichkeit auf einzelne Wörter (wie sie etwa die »Words«-Auflistung in Word-VBA bietet). Hier hilft nur der Einsatz eines »TextCursor«-Objekts, das sich Wort für Wort durch den Dokumenttext bewegt.
Wie so etwas genau funktioniert, zeigt das Makro in Listing 2, das sämtliche Wörter des aktuellen Dokuments zählt und anzeigt. Die aktive Arbeit des Makros beginnt mit einer »gotoStart«-Anweisung, die die virtuelle Schreibmarke »objCursor« an den Anfang des Dokuments verschiebt.
Die Do-Loop-Schleife erweitert den Cursor bis ans Ende des aktuellen Worts (»gotoEndOfWord«-Anweisung) und speichert dessen Inhalt in der Variablen »strWort«. Sofern die Zeichenkette nicht leer ist, erhöht das Makro die Zählvariable »intZaehler« um 1 und zeigt den Inhalt von »strWort« per »MsgBox«-Dialog an. Danach navigiert die »gotoNextWord«-Zeile den Cursor an den Anfang des nächsten Worts. Das wiederholt sich so lange, bis »strWort« leer ist, also keine weiteren Wörter mehr vorhanden sind. Die »MsgBox«-Anweisung unterhalb der Schleife nennt schließlich den aktuellen Wert der Zählvariablen.

Abbildung 2: Das makrogesteuerte Durchsuchen von Textdokumenten ermöglicht viele Anwendungen, zum Beispiel auch das Auszählen von Worthäufigkeiten.
Listing 2: Zeigt und zählt Wörter
01 Sub WoerterZaehlenUndAnzeigen 02 Dim objDokument as Object 03 Dim objText As Object 04 Dim objCursor As Object 05 Dim strWort As String 06 Dim intZaehler As Integer 07 08 objDokument = GlobalScope.ThisComponent 09 objText = objDokument.Text 10 objCursor = objText.createTextCursor 11 12 objCursor.gotoStart(False) 13 Do 14 objCursor.gotoEndOfWord(True) 15 strWort = Trim(objCursor.getString()) 16 If strWort > "" Then 17 intZaehler = intZaehler + 1 18 MsgBox strWort 19 End If 20 objCursor.gotoNextWord(False) 21 Loop Until strWort = "" 22 MsgBox "Dokument hat " & intZaehler & " Wörter." 23 End Sub
Tipp 10: Neuen Text einfügen
Der »Text«-Service von Star-Writer-Dokumenten verfügt über die Methode »insertString«, mit der Makros beliebigen Text in Dokumente einfügen beziehungsweise vorhandenen Text verändern können. Dazu braucht es wieder ein »TextCursor«-Objekt, wie es das folgende Beispiel – unter aktiver Beihilfe der Funktion »NeuesDokument« aus Listing 1 – am Anfang eines neuen Textdokuments anlegt:
Dim objDokument as Object Dim objText As Object Dim objCursor As Object objDokument = NeuesDokument(1) objText = objDokument.Text objCursor = objText.createTextCursor
Die »insertString«-Methode bekommt als erstes Argument das »TextCursor«-Objekt übergeben, das die gewünschte Einfügeposition markiert. Das zweite Argument übermittelt den einzufügenden Text. Das dritte Argument bestimmt, ob »insertString« den von »objCursor« gegebenenfalls repräsentierten Text überschreiben soll (»True«) oder nicht (»False«). Soll einfach nur Text eingefügt werden, ist eine Anweisung wie diese richtig:
objText.insertString(objCursor, "Dieser Text ist neu.", False)
Tipp 11: Vorhandenen Text verändern
Möchte der Star-Basic-Programmierer die »insertString«-Methode nutzen, um vorhandenen Text zu verändern, muss er das im ersten Argument übergebene »TextCursor«-Objekt zunächst so erweitern, dass es den zu ändernden Text vollständig markiert. Das Beispiel
With objCursor .gotoStart(False) .gotoNextWord(False) .gotoNextWord(False) .gotoNextWord(False) .gotoEndOfWord(True) End With
arbeitet sich bis zum vierten Wort des Dokuments vor und legt das »TextCursor«-Objekt »objCursor« sozusagen darüber. Das dritte Argument der »insertString«-Methode mit »True« ersetzt den gesamten Inhalt des Objekts mit dem im zweiten Argument genannten Text. Die Zeile
objText.insertString(objCursor, "geändert",True)
überschreibt also das vierte Wort des Dokuments (»neu«) mit dem Wort »geändert«.
Tipp 12: Steuerzeichen einfügen
»insertString« kann ausschließlich darstellbare Textzeichen in ein Star-Writer-Dokument schreiben. Für das Einfügen von Steuerzeichen ist die Methode »insertControlCharacter« zuständig. Sie erwartet beim Aufruf ein »TextCursor«-Objekt, das die Einfügeposition markiert, sowie eine Konstante aus dem »ControlCharacter«-Fundus des API-Moduls »com.sun.star.text«, die das gewünschte Steuerzeichen definiert.
Das in Tabelle 2 aufgeführte Angebot umfasst je eine Zeilen- und Absatzschaltung, einen geschützten und bedingten Trennstrich sowie ein geschütztes Leerzeichen. Ein geschützter Trennstrich hindert die Silbentrennung daran, ein Wort an der vorgegebenen Position zu trennen, während die bedingte Variante die Trennstelle vorgibt. Das geschützte Leerzeichen verhindert, dass zwei damit verbundene Wörter getrennt werden. Die Beispielzeile
objText.insertControlCharacter (objCursor, com.sun.star.text.ControlCharacter.LINE_BREAK, False)
fügt der einfachen Logik von Tabelle 2 folgend an der aktuellen Position von »objCursor« eine Zeilenschaltung in das Textdokument ein.
Tipp 15: Suchen und ersetzen
Lautet die Aufgabe, die gefundenen Textstellen nicht nur zu zählen, sondern deren Inhalt zu ersetzen, muss die »Do-Loop«-Schleife aus dem letzten Tipp nur geringfügig umgebaut werden:
Do While Not IsNull(objFundstelle) objFundstelle.String = "StarBasic" objFundstelle = objDokument.findNext(objFundstelle.End, objBeschreibung) Loop
Der Trick besteht darin, dem »TextRange«-Objekt »objFundstelle« via »String«- Eigenschaft einen neuen Inhalt (»StarBasic«) zuzuweisen. Diese Form des Suchens und Ersetzens hat den Vorteil, den Schleifendurchlauf jederzeit per »Exit-Do«-Anweisung beenden zu können, um etwa nur die ersten drei Vorkommen eines Begriffs zu ersetzen.
Zum Austausch sämtlicher Vorkommen gibt es eine deutlich einfachere Methode: Man legt statt eines »SearchDescriptor«-Objekts ein »ReplaceDescriptor«-Objekt an, dessen zusätzliche »replaceString«-Eigenschaft den Austauschtext definiert. Das »ReplaceDescriptor«-Objekt wird dann für den kollektiven Austausch der »replaceAll«-Methode des Dokuments übergeben, wie das Beispiel demonstriert:
Dim objDokument as Object
Dim objBeschreibung As Object
objDokument = GlobalScope.ThisComponent
objBeschreibung = objDokument.createReplaceDescriptor
With objBeschreibung
.searchString = "VBA"
.searchWords = True
.searchCaseSensitive = True
.replaceString = "StarBasic"
End With
objDokument.replaceAll(objBeschreibung)

Abbildung 4: Schematischer Aufbau der wesentlichen API-Services für das Suchen und Ersetzen in Textdokumenten. (Quelle: Sun Microsystems)
Tipp 16: Star-Office-API-Dokumentation besorgen
Die ehemals nur online zugängliche API-Dokumentation steht nun zum Download [2] bereit. Obwohl es sich bei diesem Sammelsurium von HTML-Dateien um eine reine Referenz ohne Beispiele handelt, ist es doch ein unverzichtbares Hilfsmittel für Makroprogrammierer. Größtes Manko der Download-Version ist das Fehlen einer Suchfunktion. Dafür gibt es aber immerhin einen Global Index, der den alphabetischen Zugriff auf sämtliche Schlagworte ermöglicht (siehe Abbildung 5). (jk)

Abbildung 5: In der Download-Version der Star-Office-API-Referenz fehlt die Suchfunktion. Mit dem Global Index ist aber trotzdem jedes Schlüsselwort relativ schnell zu erreichen.
Infos: |
|
[1] Ralf Nebelo: “Tschüss, VBA – Tipps zur Programmierung von Star Office 6”; erster Teil. Linux-Magazin 5/02, S. 26ff. [2] Download der Star-Office-API-Dokumentation: [http://api.openoffice.org] |








