How to create macro to insert comment on / footnote after all examples of search string

Hi all, I’m new to LibreOffice Basic: many thanks in advance for your help!

In VBA I am able to search for all instances of a search string in a document, and insert a comment bubble on (or alternatively a footnote after) each such instance, e.g. inserting a comment “Hello World” on each instance of “Hello” in a document using this simple code:

Sub HelloWorld()
Dim range As range
Set range = ActiveDocument.Content
Do While range.Find.Execute(“Hello”, False) = True
ActiveDocument.Comments.Add range, “Hello World”
Loop
End Sub

I see that LibreOffice Basic supports Do Loops, and I also like that (unlike VBA) LibreOffice Basic appears to support search strings using characters outside ASCII. However my attempts to reproduce a similar macro in LibreOffice Basic to the above VBA code (so that it works in LibreOffice Writer) have failed. Any help from the experts on this forum would be greatly appreciated, thanks!

You try:

Sub main()
	doc = ThisComponent
	search = doc.createSearchDescriptor()
	search.searchString = "Hello"
	ranges = doc.findAll(search)
	
	For i = 0 To ranges.Count - 1
		note = doc.createInstance("com.sun.star.text.textfield.Annotation")
		note.Content = "Hello World"
		range = ranges.getByIndex(i)
		cursor = range.Text.createTextCursorByRange(range.End)
		range.Text.insertTextContent(cursor, note, False)
	Next	
End Sub

1 Like

That worked perfectly, thank you very much!

Sorry for posting on an old thread, but I’m curious, is there a way to insert an annotation over a text range?

Here is a slightly modified example from A. Pitonyak’s book AndrewMacro.odt .

Sub AddNoteAtCursor2()
  Dim oDoc as Object, oViewCursor as Object, oTextField as Object
  oDoc = ThisComponent
  oViewCursor = oDoc.CurrentController.ViewCursor
  oTextField = oDoc.createInstance("com.sun.star.text.TextField.Annotation")
  With oTextField
    .Author = "AP"
    .Content = "It sure is fun to insert notes into my document"
    
    'Lets lie and say that this was added ten days ago!
    .DateTimeValue = CDateToUnoDateTime(Now() - 10)
  End With
  oDoc.Text.insertTextContent(oViewCursor, oTextField, False)
End Sub 

Thanks, but does that really create the annotation over the whole cursor range for you? For me it only creates comment at the start of the cursor range, which is the same what the accepted reply would do without the .End in range.Text.createTextCursorByRange(range.End).

Sorry, I didn’t understand the question at first.
I couldn’t get it to work without the “.uno:” commands.
The “archaic” methods seem to work.
To check, select a text range and call the TestInsertAnnotation macro.

Sub InsertAnnotation(Byval oDoc as Object, ByVal oTextRange as Object, Byval annotation as String)
  Dim oContr as Object, oDisp as Object, oSel as Object
  Dim oKeyEvent As New  com.sun.star.awt.KeyEvent
  Dim props(0) As New com.sun.star.beans.PropertyValue  

  oContr = oDoc.CurrentController
  oSel = oContr.Selection
  oContr.Select oTextRange
  
  props(0).Name = "Text"
  props(0).Value = annotation
  oDisp = createUnoService("com.sun.star.frame.DispatchHelper")
  oDisp.executeDispatch(oContr.Frame, ".uno:InsertAnnotation", "", 0, props)
  
  ' Return to the document body
  oKeyEvent.KeyCode=com.sun.star.awt.Key.ESCAPE
  Simulate_KeyPress oDoc, oKeyEvent
 
End Sub  

' It is assumed that a text fragment (range) is selected in the current text document.
Sub TestInsertAnnotation()
  InsertAnnotation ThisComponent, ThisComponent.CurrentSelection(0), "My note"
End Sub


' https://ask.libreoffice.org/t/create-a-macro-that-press-enter-automatic-once/27172/3?u=sokol92.
Sub Simulate_KeyPress(Byval oDoc As Object, Byval oKeyEvent as Object)
  Dim oWindow as object, oToolkit as Object
  If (Not (oKeyEvent Is Nothing)) And (Not (oDoc Is Nothing)) Then
    oWindow=oDoc.CurrentController.Frame.ContainerWindow
    oKeyEvent.Source=oWindow
    oToolkit=oWindow.Toolkit
    With oToolkit
      .keyPress(oKeyEvent)	
      .keyRelease(oKeyEvent)
    End With
  End If  
End Sub
1 Like

While that does work as you say when I try to use it as you suggest, if I try to use it in the context of the original question, it works maybe 3 out of 5 times.

Sub CreateComments(searchText as String, commentText as String)
    oDoc = ThisComponent
	search = oDoc.createSearchDescriptor()
	search.searchString = searchText
	ranges = oDoc.findAll(search)
	
	For i = 0 To ranges.Count - 1
		InsertAnnotation(oDoc, ranges.getByIndex(i), commentText)
	Next
End Sub

In order for us to compare the results, please upload the file and indicate which macro should be run.

Right, sorry. The macro is TestCreateComments.

lo_test_2.odt (13.9 KB)

A common mistake in such cases.
You’re changing the document’s contents inside a loop.
So

For i = ranges.Count - 1 To 0 Step -1

Thanks. Five comments after clarifying the macro code.

2 Likes

Ah, I see, that didn’t occur to me. In my defense, that’s how the macro in the accepted answer did it, and I just copied it, because I’m not really familiar with how it all works. Thank you so much for your help!

1 Like