Aktuelle TextTable als TextRange abrufen und damit suchen

Programmierung unter AOO/LO (StarBasic, Python, Java, ...)

Moderator: Moderatoren

juetho
******
Beiträge: 617
Registriert: Di, 20.04.2010 15:46
Wohnort: Berlin

Aktuelle TextTable als TextRange abrufen und damit suchen

Beitrag von juetho »

Aufgabe allgemein: Mehrere Text-Dokumente sollen mit OOo Basic bearbeitet werden. Jedes Dokument besteht aus "Abschnitten" (das ist nur meine logische Gliederung und hat nichts mit OOo-Objekten zu tun); jeder Abschnitt enthält 2 Absätze und 1 TextTable (jeweils 1 Spalte und 5 bis 20 Zeilen). Das zur Erläuterung für die Situation.

Aufgabe konkret: Wie frage ich (ausgehend von der aktuellen Cursor-Position) ab, innerhalb welcher TextTable ich mich befinde, damit diese TextTable vollständig ausgewählt wird und als TextRange für eine Textsuche vorgegeben wird.

Bisherige Teillösungen und Abläufe:
1. Folgende Variable sind vorbereitet:

Code: Alles auswählen

rem ** Variable für die Zieldatei
'destDoc ist das eigentliche Dokument
'destCursor ist der TextCursor
'zwei Suchverfahren
searchStudent = destDoc.createSearchDescriptor
searchObject = destDoc.createSearchDescriptor
2. Mit searchStudent suche ich den Textinhalt im zweiten Absatz eines jeden Abschnitts:

Code: Alles auswählen

	destCursor.gotoStart(false)
	searchStudent.searchString = schueler
	found = destDoc.findFirst(SearchStudent)
3. Dann markiere ich den Treffer, gehe in den nächsten Absatz und will mich damit in die erste Zelle der dazugehörigen TextTable bewegen (aber deren Name ist mir nicht bekannt). Wenn ich es manuell im Editor so mache, dann lande ich tatsächlich innerhalb der TextTable. Aber per Makro-Code komme ich nicht in die TextTable hinein, sondern lande am Anfang des nächsten Abschnitts.

Code: Alles auswählen

	destCursor.gotoRange(found, false)
	destCursor.gotoNextParagraph(false)
' oder alternativ auch:
	destCursor.goRight(1, false)
Frage 3. Mit welchem TextCursor.GoXxx-Befehl komme ich in die unmittelbar folgende TextTable hinein?

4. Jetzt will ich innerhalb dieser TextTable (also beschränkt auf die aktuelle TextTable) einen bestimmten Text suchen:

Code: Alles auswählen

	searchObject.searchString = fach
	found = destDoc.findFirst(searchObject)
Ich brauche also die Festlegung, dass searchObject nur innerhalb eines bestimmten Bereichs suchen soll. Also fehlen im Punkt 4 diese Schritte:

Frage 4.1. Mit welcher Eigenschaft wird die Suche auf einen bestimmten TextRange beschränkt? Irgendwo hatte ich so etwas schon gelesen, aber ich finde dies nicht mehr.

Frage 4.2. Wie bekomme ich die aktuelle TextTable gemäß Punkt 3 als Objekt vom Typ TextRange? Alle Beispiele, die ich dazu finde, gehen über den Index oder über den Namen der Tabelle. Beides steht mir bei diesem Arbeitsablauf nicht zur Verfügung. Aber es muss doch etwas wie currentTable geben...

Mit dem DispatchHelper kann ich die aktuelle TextTable markieren (wahlweise mit EntireColumn oder mit SelectTable), aber was hilft mir das beim searchObject?

Danke für Lösungsvorschläge! Jürgen

// Edit
PS 1. Eine Erklärung für das Problem mit Punkt 3 habe ich inzwischen gelesen, aber noch keine Lösung: OOo unterscheidet zwischen Paragraph und TextTable. Aber dennoch muss es doch möglich sein, den TextCursor in die "richtige" TextTable zu setzen oder ersatzweise wenigstens einen neuen TextCursor auf die zugehörige TextTable zu bekommen.

PS 2. Wenn ich die TextTables nicht durchnummeriert hätte, sondern mit einem Namen versehen hätte, wäre die ganze Suche wahrscheinlich viel einfacher geworden. Das kann ich bei den Zeugnissen im nächsten Jahr machen, aber jetzt habe ich keine Zeit dafür, alle Vorlagen nochmals neu zu erstellen.
Situation: LibO 3.6 auf Win 7 Home Premium (64-bit) mit MySQL (localhost) über JDBC
DPunch
*******
Beiträge: 1112
Registriert: Mo, 02.11.2009 16:16
Wohnort: Marburg

Re: Aktuelle TextTable als TextRange abrufen und damit suchen

Beitrag von DPunch »

Aloha

Das Ganze ist relativ umständlich und meines Wissens nach auch nur mit einem ViewCursor umsetzbar, weil ein TextCursor nicht in eine Tabelle springen kann.

Als Ansatz für einen Workaround kannst Du es mal auf folgende Weise probieren:

Code: Alles auswählen

	nMaxLinesBetweenStudentAndTable = 5	'Wie viele Zeilen können zwischen dem Schueler und der dazugehörigen Tabelle liegen?
	sSucheSchueler = "Max Mustermann" 'Suchbegriff für searchStudent
	sSucheObject = "bla"  'Suchbegriff für searchObject
	destDoc.lockControllers
	searchStudent = destDoc.createSearchDescriptor
	searchObject = destDoc.createSearchDescriptor
	searchStudent.searchString = sSucheSchueler
	found = destDoc.findFirst(SearchStudent)
	If isNull(found) Then
		MsgBox """" & sSucheSchueler & """ nicht gefunden"
		GoTo RemoveLock
	End If
	oViewCursor = destDoc.CurrentController.getViewCursor
	oViewCursor.gotoRange(found,False)
	For x = 1 To nMaxLinesBetweenStudentAndTable
		oViewCursor.goDown(1,False)
		If NOT isEmpty(oViewCursor.TextTable) Then
			Exit For
		End If
	Next x
	If isEmpty(oViewCursor.TextTable) Then
		GoTo RemoveLock
	End If
	oViewCursor.gotoEnd(True)
	oTable = oViewCursor.TextTable
	oStartCursor  = oTable.createCursorByCellName("A1")
	searchObject.searchString = sSucheObject
	oStartCursor = destDoc.findNext(oStartCursor,searchObject)
	If isEmpty(oStartCursor.TextTable) Then
		MsgBox """" & sSucheObject & """ nicht gefunden"
		GoTo RemoveLock
	ElseIf oStartCursor.TextTable.Name <> oTable.Name Then
		MsgBox """" & sSucheObject & """ nicht gefunden"
		GoTo RemoveLock
	End If
	oStartCursor.String = oStartCursor.String & " ***gefunden***"
	RemoveLock:
	oViewCursor.jumpToFirstPage
	Do While destDoc.hasControllersLocked
		destDoc.unlockControllers
	Loop
juetho
******
Beiträge: 617
Registriert: Di, 20.04.2010 15:46
Wohnort: Berlin

Notlösung: Verzicht auf TextRange, Suche mit Schleifen

Beitrag von juetho »

Danke für die Antwort, dann bin ich ja "beruhigt", dass es nur umständlich geht. Nun weiß ich auch, warum

Code: Alles auswählen

destCursor.gotoRange(TextTable-Zelle, false)
mit einer Exception endet; ich dachte immer, dass ich ein falsches TextRange-Objekt übergeben wollte.

Dann ist meine Notlösung, die ich in der Zwischenzeit gebastelt habe, im Vergleich auch nicht komplizierter.

Die erste Suche habe ich geändert; ich benutze jetzt das Verfahren von (Dannenhoefer) 8.3.3 Wie kann man auf Absätze zugreifen? So, wie ich das zweite Textdokument destDoc erstellt hatte, kann ich sicher sein, dass bei der Enumeration die Bestandteile so aufeinanderfolgen:
  • Index 0: Paragraph (unwichtig), Index 1: Paragraph (enthält ggf. den Suchbegriff), Index 2: TextTable 0
  • Index 3: Paragraph (unwichtig), Index 4: Paragraph (enthält ggf. den Suchbegriff), Index 5: TextTable 1 usw.
Also durchlaufe ich in einer Schleife alle Elemente des Textes, benutze zusätzlich einen laufenden Index und kann aus dem Index beim Treffer den Index der TextTable berechnen.

Code: Alles auswählen

Function Schueler_suchen_in_Klasse(ByVal Doc As Object, ByVal student As String) As Integer
Enum = Doc.Text.createEnumeration
Dim index As Integer
index = -1
' Schleife über alle Absätze
do While Enum.hasMoreElements
	index = index + 1
	element = Enum.nextElement
	check = hasunointerfaces(element,"com.sun.star.text.XTextTable")
	if not check then         ' nur Bestandteile prüfen, die nicht TextTable sind
		if element.string = student then
			'Treffer: der gesuchte Schüler befindet sich im Bestandteil index
			index = (index - 1) / 3      
			'daraus kann der Tables-Index der folgenden TextTable abgeleitet werden
			exit do
		end if
	end if
loop
Schueler_suchen_in_Klasse = index
End Function
Achtung für spätere Nutzer dieser Lösung: Das geht nur, weil die Bestandteile in dieser Reihenfolge erzeugt und gespeichert wurden!

Damit habe ich die gesuchte TextTable (1 Spalte, n Zeilen) und kann nach einem ähnlichen Verfahren das gesuchte Fach suchen; das ist immer der Inhalt des ersten Absatzes in einer Zelle.

Code: Alles auswählen

Function Fach_suchen_in_Klasse(ByVal studentTable As TextTable, ByVal current As String) As Integer
Dim result As Integer
result = -1
objLength = Len(current)
Dim maxRow As Integer
maxRow = studentTable.getRows().getcount()
rem Schleife über alle Zeilen und jeweils die erste (einzige) Spalte
for x1 = 0 to maxRow-1
   	currentCell = studentTable.getCellByPosition(0, x1)
   	if( not isNull(currentCell) ) then
   		' hole den Inhalt als Text und vergleiche den Anfang mit dem Suchbegriff
   		if( current = Mid(currentCell.getString(), 1, objLength) ) then
   			result = x1      ' Treffer: es handelt sich um den Inhalt von Zelle(0,x1)
	   		exit for
	   	end if
	end if
next x1
Fach_suchen_in_Klasse = result 
End Function
Also weiß ich jetzt, welche Zelle ich bearbeiten muss (also die Stelle, an die ich eigentlich mit dem TextCursor gehen wollte). Das geht zunächst durch den Aufruf dieser beiden Funktionen (jeweils mit Fehlerprüfung: index < 0 sagt "nicht gefunden"):

Code: Alles auswählen

	rem suche den Schüler in der Zieldatei
	index = Schueler_suchen_in_Klasse(destDoc, schueler)

	rem suche das Fach
	thisTable = destTables.getByIndex(index)
	index = Fach_suchen_in_Klasse(thisTable, fach)

	rem wenn gefunden, dann bearbeite diese Zelle
	found = thisTable.getCellByPosition(0, index)
Auch wenn jede Zelle genau zwei Absätze enthält (den ersten, der das gesuchte Fach enthält, und den zweiten, in den ich Text einfügen will), kann ich nicht direkt darauf zugreifen, sondern muss wieder nach dem o.g. Verfahren per Enumeration vorgehen. Aber das geht analog:

Code: Alles auswählen

	rem Umweg über alle Absätze, die in der Zelle enthalten sind
	Enum = found.Text.createEnumeration()
	do While Enum.hasMoreElements
		par = Enum.nextElement()
		If par.supportsService("com.sun.star.text.Paragraph") Then
        		para = par.createEnumeration()
        		do While para.hasMoreElements()
        			element = para.nextElement()
				elemType = element.TextPortionType
				elemInhalt = element.GetString
				If elemType = "Text" AND elemInhalt = "" Then
					element.setString(content)
				end if
			loop
		end if
	loop
Wie gesagt: schön ist etwas anderes. Aber da es keine Paragraph-Collection gibt sowie Absätze und TextTables gesondert behandelt werden müssen, sehe ich keine Vereinfachung (abgesehen davon, dass ich künftig die TextTables per "Schüler"-Name suchen will). Vorläufig funktioniert es; und ich kann meine Arbeit erledigen. Aber wenn jemand noch Verbesserungsvorschläge hat, nehme ich sie dankbar zur Kenntnis!

Gruß Jürgen

@DPunch
Heute zwischen 13 und 18 Uhr war ich mit meiner Notlösung beschäftigt. Deshalb konnte ich deinen Vorschlag nicht mehr berücksichtigen. Aber es sieht so aus, als wenn mich das auch nicht schneller zum Ziel geführt hätte. Jedenfalls hat er mein Verständnis für OOo-Feinheiten verbessert.
Situation: LibO 3.6 auf Win 7 Home Premium (64-bit) mit MySQL (localhost) über JDBC
Antworten