Macro/Extension for detecting duplicate words based on the last inputed word

I want to make an extension that detects for me if the last inputed (writed) word has a duplicate in the existing document.
My main problem is trying to suscribe it via a listener to a document modified event, or text insertion / input recieved event. Right now it works if you click a UI button attatched to "org.extension.sample.do", but that’s non-practical. I have read some examples from the OOdev team () and the examples from the sdk and wiki, but i’m struggling to make it work in an event-driven manner, since debugging it’s not as streamlined.

rn my code its based on the wavelet example from the wiki:

import uno
import unohelper
from com.sun.star.task import XJobExecutor
from com.sun.star.text import XTextDocument
from com.sun.star.lang import XEventListener

 
class Wavelet( unohelper.Base, XJobExecutor, XEventListener ):
    def __init__( self, ctx ):
        self.ctx = ctx

    def documentEventOccured(self, event):
        if event.EventName == "InsertText":
            self.trigger(None)

    def get_cursor_on_last_modified_word(self, document):
        text = document.Text

        # Get the view cursor
        view_cursor = document.CurrentController.getViewCursor()
        
        # Get the position of the last modified character

        last_modified_position = view_cursor.getStart()

        # Get the word at the last modified position

        cursor = text.createTextCursorByRange(last_modified_position)
        cursor.gotoStartOfWord(False)
        cursor.gotoEndOfWord(True)
        last_modified_word = cursor.getString()
        
        return cursor
    
    def trigger( self, args ):
        desktop = self.ctx.ServiceManager.createInstanceWithContext(
            "com.sun.star.frame.Desktop", self.ctx )
        doc = desktop.getCurrentComponent()
        text = doc.Text
        
        last_word_cursor = self.get_cursor_on_last_modified_word(doc)

        try:
            search = doc.createSearchDescriptor()
            search.SearchWords = True
            search.SearchString = last_word_cursor.getString()

            found = doc.findAll( search )
            
            if(found.Count > 1):
                last_word_cursor.CharBackColor = 0xFFFF00
                
            else:

                last_word_cursor.CharBackColor = -1
                
    
        except Error as e:
            show_message_box(e, "Error")
            pass

# Starting from Python IDE
def main():
    try:
        ctx = XSCRIPTCONTEXT
    except NameError:
        ctx = officehelper.bootstrap()
        if ctx is None:
            print("ERROR: Could not bootstrap default Office.")
            sys.exit(1)

# Starting from command line
if __name__ == "__main__":
    main()


# pythonloader loads a static g_ImplementationHelper variable
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
    Wavelet,  # UNO object class
    "org.extension.sample.do", 
    ("com.sun.star.task.Job",), )  

I tried to write the example in Basic that is easier for me. It is semi-functional, but maybe it can partially help you. There isn’t findAll but only the searching for 1st next word or 1st previous word. And it checks the type of written character during writing.
realtime-letter-checker.odt (22.1 kB)

I don’t know much BASIC, but i’m reading trough it.
Any idea why libreoffice won’t allow me to run the macro for security reasons?

Tools/ Options/ LibreOffice/ Security/ Macro Security. But now you have the solution from @elmau with the same event OnLayoutFinished that I used in Basic macro.

Use you the event OnLayoutFinished

import unohelper
from com.sun.star.document import XDocumentEventListener


class DocumentEventListener(unohelper.Base, XDocumentEventListener):

    def __init__(self, doc):
        self._doc = doc

    def documentEventOccured(self, event):
        # ~ app.debug(event.EventName)
        if event.EventName == 'OnLayoutFinished':
            self._search()
        return

    def _search(self):
        current = self._doc.CurrentController.ViewCursor.Start
        cursor = self._doc.Text.createTextCursorByRange(current)
        cursor.gotoStartOfWord(False)
        cursor.gotoEndOfWord(True)

        search = self._doc.createSearchDescriptor()
        search.SearchWords = True
        search.SearchString = cursor.String

        found = self._doc.findAll(search)
        cursor.CharBackColor = -1
        if found.Count > 1:
            cursor.CharBackColor = 0xFFFF00

        return

Add listener to your doc:

doc.addDocumentEventListener(DocumentEventListener(doc))
1 Like

Works like a charm! Thanks! Would be further learning into libreoffice dev.