Writer: text position

I still can’t find a solution.

I don’t mean to be rude… I have worked with VBA in the past… It was so simple and straightforward to use compared to LibreOffice BASIC (macros)…

Coult it be like this? But better is to use some Style than direct formatting, implemented in example as Character Style MyNewFont :slight_smile:.

Sub findDuoBrackets
	dim oDoc as object, oDesc as object, oFound as object, oVCur as object, oFound2 as object, oFoundR as object
	oDoc=ThisComponent
	createStyle
	oVCur=oDoc.CurrentController.ViewCursor 'visible cursor
	oVCur.goToStart(false) 'cursor to the start of document
	rem find 1st {
	oDesc=oDoc.createSearchDescriptor
	oDesc.SearchString="{"
	oFound=oDoc.findFirst(oDesc)
	do while NOT isNull(oFound)
		rem find next {
		oFound2=oDoc.findNext(oFound.End, oDesc)
		rem find } after 1st {
		oDesc.SearchString="}"
		oFoundR=oDoc.findNext(oFound.End, oDesc)
		rem compare regions
		if NOT isNull(oFound2) then '2nd { exists
			if NOT isNull(oFoundR) then '} exists	
				if oDoc.Text.compareRegionStarts(oFound2.Start, oFoundR.Start)=1 then
					oVCur.goToRange(oFound2.Start, false)
					oVCur.goToRange(oFound2.End, true)
					msgbox "Intruder!"
					exit do
				else
					oVCur.goToRange(oFound.End, false)
					oVCur.goToRange(oFoundR.Start, true)
				end if
				oDoc.CurrentController.Select(oVCur)
				oVCur.CharStyleName="MyNewFont"
				oDesc.SearchString="{"
				oFound=oDoc.findNext(oVCur.End, oDesc)
			else '2nd { exists, but } not exists
				exit do
			end if
		else '2nd { not exist, and } not exists
			if isNull(oFoundR) then exit do
		end if
	loop
End Sub

Sub createStyle 'create new character style if not exists
	const cStyle="MyNewFont"
	dim oDoc as object, oStyles as object, oStyle as object
	oDoc=ThisComponent
	oStyles=oDoc.StyleFamilies.getByName("CharacterStyles")
	if NOT oStyles.hasByName(cStyle) then
		oStyle=oDoc.createInstance("com.sun.star.style.CharacterStyle")
		with oStyle
			.CharFontName="Linux Libertine"
			.CharUnderline=2
		end with
		oStyles.insertByName(cStyle, oStyle)
	end if
End Sub
1 Like

I totally approve @KamilLanda’s suggestion about character style. You should apply some style to your sequence {…} (including or not the bracket, this is your choice). Define one (or several if the sequence has various meanings). This is called semantic styling. You are not compelled to configure all styles from start. You can leave them in a “neutral” state (logically equivalent to No Character Style) and customise the attributes when you need to emphasise the sequence. Automatically all sequence so styled will be instantly emphasised.

1 Like

Thank you! :smiley: :smiley:
I worked all day yesterday and came to a pretty good solution.
I will definitely take your example as a suggestion to improve what I have done :smiley:

The drawback of using style is that once created you cannot let user to modify values, so I define style but as the first step I destroy the previous one :wink:

A style is not supposed to describe some visual state. It is a markup for some significance or semantic value. Therefore, a user applies a style to some sequence which intrinsically has a defined meaning. This is the most important aspect. How this significance is rendered is rather an ancillary thing (a matter of aesthetics usually defined by the communication or PR guys). And yes users are not allowed to change the graphical charter. So, your task is to clearly specify what are the expected “significances” like emphasis, important, comment, non-normative, legal-constraint, heading, …

@KamilLanda

Next step:

if oDoc.Text.compareRegionStarts(oFound2.Start, oFoundR.Start)=1 then

can fail because I can have { xyz } in page header/footer.
I think that use of compareRegionStarts only works in the same container. But also I want to update { xyz } if included in text-area or whatever…

What I did is:

With oCursVisible
	.gotoRange(oFoundZoneOpen.End, False)
	.gotoRange(oFoundZoneClose.Start, True)

	If InStr( .String, "{") > 0 Then
		MsgBox "Found the opening parenthesis before the closing parenthesis"
		Exit Function
	End If
End With

And it works, also working in page header/footer. It doesn’t replace { xyz } inside text-area.
Questions are:

  • how can I inlcude text-area?
  • how can I exclude header/footer?

In general, how to recognize where am I…

Update: if there is a { without closing } AND there is also a {blabla} in header then it fails since fails:

With oCursVisible
	.gotoRange(oFoundZoneOpen.End, False)
	.gotoRange(oFoundZoneClose.Start, True)
End With

The problem is that oFoundZoneOpen and oFoundZoneClose are not in the same place and it is not possible to select range starting from document and ending to header.

It is true, see SDK documentation → “XTextRangeCompare Interface Referencepublished compares the positions of two TextRanges within a Text.”

It is not possible even manually.


Situation is more complicated. Not all objects are searchable with SearchDescriptor (or with UNO command .uno:ExecuteSearch for Find&Replace gotten for example with Macro Recorder).

Sub findAndSelect 'find the occurences with SearchDescriptor
	dim oDoc as object, oDesc as object, oFound as object
	oDoc=ThisComponent
	oDesc=oDoc.createSearchDescriptor
	oDesc.SearchString="{"
	oFound=oDoc.findAll(oDesc)
	if NOT isNull(oFound) then oDoc.CurrentController.Select(oFound)
End Sub

Graphical object included text (like Insert / Text Box, or Insert / Shape/ Basic Shapes / …) is necessary to traverse one by one. It is possible to use TextCursor in these objects via method .createTextCursor (like oDoc.Text.createTextCursor).

Sub useTextCursorInTextbox
	dim oDoc as object, oVCur as object, oTextBox as object, oCur as object
	oDoc=ThisComponent
	oVCur=oDoc.CurrentController.ViewCursor
	oTextBox=oDoc.DrawPages(0).getByIndex(0) 'it could be 1st Text Box etc.
	oCur=oTextBox.createTextCursor
	oCur.collapseToStart
	oCur.goRight(1, true) 'select 1st letter in Text Box
End Sub

But it isn’t possible to select the text from these objects with some text from main text of document.

The question could be how to put View Cursor (oDoc.CurrentController.ViewCursor) to these object, because it has other methods than Text Cursor (oDoc.Text.createTextCursor), but unfortunately I don’t know :frowning:.


And the Headers and Footers are accessible from PageStyles Make selection with ViewCursor for Text in Footer\Header - #5 by KamilLanda

Thank KamilLanda!

I think the only thing I need to know is what “element” I’m in.
So if oFoundZoneOpen is in the body section and oFoundZoneClose is in the header then I won’t compare them to each other.

What is not clear to me about Headers and Footers and the code you linked:

oStyles=oDoc.StyleFamilies.getByName("PageStyles")
oStyle=oStyles.getByName(oDoc.CurrentController.ViewCursor.PageStyleName) 'get actual page PageStyle
oCur=oStyle.HeaderText.createTextCursor

it’s counterintuitive… Should I use PageStyleName to obtain:
oStyle.HeaderText

Excuse me. I’m quite confused… :crazy_face:

check oVCur.Text.ImplementationName → there is SwXBodyText for “body”, SwXHeadFootText for Header/Footer.

The ascertainment the Header/Footer is accessible from Page Styles was very surprising for me, it was big clicking in XRay&MRI :slight_smile:

1 Like

oDoc.findFirst / findNext returns and object than contains .Text.ImplementationName, so it is exactly what I need to avoid explosion!
Thank you so much! You rock KamilLanda! :mechanical_arm:

Solution for the thing I didn’t know → how to put Visible Cursor to the “Draw objects” :slight_smile:.
It is possible to select the object,

ThisComponent.CurrentController.Select(ThisComponent.DrawPages(0).getByIndex(0)) 'select 1st Draw object

and then use pressing the Enter via macro

1 Like

Please try to always avoid relying on implementation details, which ImplementationName is. It is not guaranteed to be stable between versions.

Why do you need to know if it’s body or a header? Which task are you solving? In fact, it is possible that you may have two different headers - what then?

There is a method to check if the two Basic objects are the same UNO object: EqualUnoObjects Function. Other languages bindings have similar facilities.

Hi @mikekaganski
There are 2 purposes:

  1. there are cases where the { and } founded by findfirst/findnext are in different objects, so it’s not possible to “select” text in between and “hightlight” it
  2. I introduced some options so user can choose to highlight only strings in body or in header/footer too or also…

Update: I tried using EqualUnoObjects on two object returned by findfirst/findnext, searching { and } but it always said that are the same object…

Update 2: found another problem.
What if I insert a table in header? How can I recognize that I’m in table inside header?

I don’t know what Property use instead ImplementationName to get safe info. But maybe it could be sufficient to detect if current Impl. Name is different than Body’s ImplName.
Body’s ImplName is possible to get from standard Text Cursor (oDoc.Text.createTextCursor), but I didn’t discover how to get info where the Table is. But there is possibility to move Visible cursor to cell A1 and press Enter that adds the line above Table, move Visible Cursor to these new line and get ImplName of this place. Then delete this new line. But it is wild method (= probably unsafe) :frowning:.
Better is apply oDoc.lockControllers for it (line is commented). Enter+Del is possible to do via key-pressing simulations or by UNO commands.
Run macro Main: test-cursor-areas.odt (18.2 kB)

Thanks @KamilLanda for the workaround… :astonished:

I wait to know if @mikekaganski has a different solution without using sendkey… :sweat_smile:

I do not see which specific problem you are solving. Why would the code need to know that the table is in a header? What would change then? As far as I saw, the initial problem was that it was possible to try to select from one position to another, when these positions ware in different texts (text body vs. header/footer, which are separate “subdocuments”, bit parts of the main text flow). Unless there is a specific scenario, I can’t suggest anything specific; and the scenario should have a document with a macro, which could have some comments where something is missing.

Hi @mikekaganski, scenario is:

  • select text between { and }
  • highlight the text changing the font, the background, …
  • if { and } are not in the same container it means that there are a missing { or } => error
    • for example {is in the body and } is in the header
  • in the macro I added some constant (for now) and user can decide to set constant so to exclude of highlighting for: body, header/footer, cell (of table)
    • if the table is in the header and user want to exclude header/footer, I need to know that the founded text in cell is (or not is) of a table that is in the header/footer

You didn’t provide a sample document with macros, so just guessing…

This is exactly the case of the topmost XText instances being unequal. No matter what their implementation names are - even when the names are equal, the objects themselves may be different - e.g., different headers.

If one wants to exclude headers and footers, then just check that the topmost XText is the same as returned from the component’s Text property.

This is the code:

I will try your suggestions and, in case, update my code (or I will make some more questions :smiley: )
Thanks