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)…
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 .
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
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.
Thank you!
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
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
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, …
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:
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 .
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…
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
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!
Solution for the thing I didn’t know → how to put Visible Cursor to the “Draw objects” .
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
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:
{
and }
founded by findfirst
/findnext
are in different objects, so it’s not possible to “select” text in between and “hightlight” itUpdate: 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) .
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…
I wait to know if @mikekaganski has a different solution without using sendkey…
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:
{
and }
{
and }
are not in the same container it means that there are a missing {
or }
=> error
{
is in the body and }
is in the headerYou 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 )
Thanks