LibreOffice Writer Macro to run on Selected Text Only

I am using Ubuntu 14.04 and LibreOffice 6.4. I have created a macro with several Find and Replace commands. But that applies to entire document. I want a macro by running which the macro will affect only on the selected text. Means I will select certain text then will run macro. I wish after running the macro, the find and replace commands in the macro should work on selected text only.

Hallo
It might be easier to help you if you show the actual macro code!

Formatted code (short example):

REM ***** BASIC *****
sub KrutiDevToUnicode
rem ----------------------------------------------------------------------
rem define variables
dim document as object

dim dispatcher as object

rem ----------------------------------------------------------------------

rem get access to the document

document = ThisComponent.CurrentController.Frame

dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")

rem ----------------------------------------------------------------------

dim args1043(21) as new com.sun.star.beans.PropertyValue

args1043(0).Name = "SearchItem.StyleFamily"

args1043(0).Value = 2

args1043(1).Name = "SearchItem.CellType"

args1043(1).Value = 0

args1043(2).Name = "SearchItem.RowDirection"

args1043(2).Value = true

args1043(3).Name = "SearchItem.AllTables"

args1043(3).Value = false

args1043(4).Name = "SearchItem.SearchFiltered"

args1043(4).Value = false

args1043(5).Name = "SearchItem.Backward"

args1043(5).Value = false

args1043(6).Name = "SearchItem.Pattern"

args1043(6).Value = false

args1043(7).Name = "SearchItem.Content"

args1043(7).Value = false

args1043(8).Name = "SearchItem.AsianOptions"

args1043(8).Value = false

args1043(9).Name = "SearchItem.AlgorithmType"

args1043(9).Value = 0

args1043(10).Name = "SearchItem.SearchFlags"

args1043(10).Value = 65536

args1043(11).Name = "SearchItem.SearchString"

args1043(11).Value = "yka"

args1043(12).Name = "SearchItem.ReplaceString"

args1043(12).Value = "लां"

args1043(13).Name = "SearchItem.Locale"

args1043(13).Value = 255

args1043(14).Name = "SearchItem.ChangedChars"

args1043(14).Value = 2

args1043(15).Name = "SearchItem.DeletedChars"

args1043(15).Value = 2

args1043(16).Name = "SearchItem.InsertedChars"

args1043(16).Value = 2

args1043(17).Name = "SearchItem.TransliterateFlags"

args1043(17).Value = 1073744896

args1043(18).Name = "SearchItem.Command"

args1043(18).Value = 3

args1043(19).Name = "SearchItem.SearchFormatted"

args1043(19).Value = false

args1043(20).Name = "SearchItem.AlgorithmType2"

args1043(20).Value = 1

args1043(21).Name = "Quiet"

args1043(21).Value = true

dispatcher.executeDispatch(document, ".uno:ExecuteSearch", "", 0, args1043())

rem ----------------------------------------------------------------------

dim args1027(21) as new com.sun.star.beans.PropertyValue

args1027(0).Name = "SearchItem.StyleFamily"

args1027(0).Value = 2

args1027(1).Name = "SearchItem.CellType"

args1027(1).Value = 0

args1027(2).Name = "SearchItem.RowDirection"

args1027(2).Value = true

args1027(3).Name = "SearchItem.AllTables"

args1027(3).Value = false

args1027(4).Name = "SearchItem.SearchFiltered"

args1027(4).Value = false

args1027(5).Name = "SearchItem.Backward"

args1027(5).Value = false

args1027(6).Name = "SearchItem.Pattern"

args1027(6).Value = false

args1027(7).Name = "SearchItem.Content"

args1027(7).Value = false

args1027(8).Name = "SearchItem.AsianOptions"

args1027(8).Value = false

args1027(9).Name = "SearchItem.AlgorithmType"

args1027(9).Value = 0

args1027(10).Name = "SearchItem.SearchFlags"

args1027(10).Value = 65536

args1027(11).Name = "SearchItem.SearchString"

args1027(11).Value = "”k"

args1027(12).Name = "SearchItem.ReplaceString"

args1027(12).Value = "ष"

args1027(13).Name = "SearchItem.Locale"

args1027(13).Value = 255

args1027(14).Name = "SearchItem.ChangedChars"

args1027(14).Value = 2

args1027(15).Name = "SearchItem.DeletedChars"

args1027(15).Value = 2

args1027(16).Name = "SearchItem.InsertedChars"

args1027(16).Value = 2

args1027(17).Name = "SearchItem.TransliterateFlags"

args1027(17).Value = 1073744896

args1027(18).Name = "SearchItem.Command"

args1027(18).Value = 3

args1027(19).Name = "SearchItem.SearchFormatted"

args1027(19).Value = false

args1027(20).Name = "SearchItem.AlgorithmType2"

args1027(20).Value = 1

args1027(21).Name = "Quiet"

args1027(21).Value = true

dispatcher.executeDispatch(document, ".uno:ExecuteSearch", "", 0, args1027())

end sub

In other words, do you want to replace all yka with लां and all k with ष only in the preselected fragment? Do you always select only one solid fragment, or can you select several fragments by holding Ctrl?

Yes, I want to replace all occurences of a letter. I want to usr one solid fragment at a time.

Okay, try this subroutine:

Sub ReplaceInSelection
Dim OriginalStrings As Variant, TargetStrings As Variant
Dim oFind As Variant, oFound As Variant
Dim oSelections As Variant, oCompare As Variant 
Dim i As Long, j As Long, k As Long
	OriginalStrings = Array("yka", "k")
	TargetStrings = Array("लां", "ष")

	oFind = ThisComponent.createReplaceDescriptor()
	oFind.SearchCaseSensitive = True
	oSelections =ThisComponent.getCurrentSelection
	oCompare = ThisComponent.getText()
	For i = LBound(OriginalStrings) To UBound(OriginalStrings)
		oFind.setSearchString(OriginalStrings(i))
		oFound = ThisComponent.findAll(oFind)
		If Not IsNull(oFound) Then
			For j =0 To oFound.getCount()-1
				For k = 0 To oSelections.getCount()-1
					If Len(oSelections.getByIndex(k).getString()) >= Len(OriginalStrings(i)) Then
						If oCompare.compareRegionStarts(oFound(j), oSelections.getByIndex(k)) <= 0 And _
								oCompare.compareRegionEnds(oFound(j), oSelections.getByIndex(k)) >=0 Then
							oFound(j).setString(TargetStrings(i))
						EndIf 
					EndIf 
				Next k
			Next j
		EndIf 
	Next i
End Sub
1 Like

There are hundreds of find and replace commands in the macro. I just posted two because the original was too large.

@AniruddhaMohod Well, I started from your code - in the description of the problem, the number of replacements was not mentioned.

If we are talking about a dozen replacements, then you can use the above code - just add the strings you want to find to the OriginalStrings array, and add the corresponding replacement strings to the TargetStrings array.

If we are really talking about hundreds of replaced strings, then most likely you need a completely different approach to solving the problem. It is a pity that you did not tell about your problem enough for understanding from the very beginning.

@AniruddhaMohod Do you need keep up the formatting or not?
And is it possible to use one regular expression for your searching like (aa|bb|c)?


If the answers are No&Yes, the fastest way should be with string replacements in Python like in @karolus example, but I think the more practical for hundreds replacements is to have the items in one array p=[ ["bc", "+1"], ["op", "#"] ]


Here is Basic macro with the method “com.sun.star.util.TextSearch”, mostly it is very fast method for strings in Basic. The searched regular expression has the form: (aa)|(bb)|(c).

Sub findReplaceNoFormatting
	dim oDoc as object, p(), s$, oSel as object
	p=array( array("bc", "+1"), array("op", "#") ) 'the replacements: array(Find, Replace)
	s=joinArray(p) 'regular expression like (aa)|(bb)|(c)
	oDoc=ThisComponent
	oSel=oDoc.CurrentController.Selection.getByIndex(0) 'current selection (no multiselection)
	oSel.String=replString(oSel.String, s, p) 'replace whole string in selection
End Sub

Function replString(s$, sreg$, p()) as string 'replace in string
	on local error goto bug
	dim oSearch as object, oParam as object, oFound as object, iCount%, i%, a&, b&, iStart&, sRepl$
	oSearch=CreateUnoService("com.sun.star.util.TextSearch")
	oParam=CreateUnoStruct("com.sun.star.util.SearchOptions")
	With oParam
	  .algorithmType=com.sun.star.util.SearchAlgorithms.REGEXP
	  .searchString=sreg 'string to find in form: (aa)|(bb)|(c)
	End With
	oSearch.setOptions(oParam)
	oFound=oSearch.searchForward(s, 0, len(s)) 'search the string from Start
	iStart=1
	do while oFound.subRegExpressions>0 'some occurence
		a=oFound.startOffset(0) 'start position in string
		b=oFound.endOffset(0) 'end position in string
		for i=1 to oFound.subRegExpressions-1
			if oFound.startOffset(i)>-1 then 'get replacement from array
				sRepl=p(i-1)(1) 'replacement
				exit for
			end if
		next i
		s=mid(s, iStart, a) & sRepl & mid(s, b+1) 'new string
		oFound=oSearch.searchForward(s, b-1, len(s)) 'search string
	loop	
	replString=s	
	exit function
bug:
	bug(Erl, Err, Error, "replString")
End Function

Function joinArray(p) as string 'create regular expression from 1st values in array
	on local error goto bug
	dim p1(ubound(p)), i&
	for i=lbound(p) to ubound(p)
		p1(i)=p(i)(0) 'p1() has the 1st values from p()
	next i
	joinArray="(" & join(p1, ")|(")  & ")" 'output string like: (aa)|(bb)|(c)
	exit function
bug:
	bug(Erl, Err, Error, "joinArray")
End Function

Sub bug(sErl$, sErr$, sError$, sFce$) 'show the error message
	msgbox(sErr & ": " & sError & chr(13) & "Line: " & sErl & chr(13), 16, sFce)
End Sub

This may be helpful to contributors wanting to help:
Legacy Kruti Dev representation
To the OP: Please give relevant information. Most users and contributors in this English forum won’t know anything concerning the transformation of old workarounds for the representation of Hindi scripts to the current Unicode representation.
Attach an example of a text document (.odt) containing parts in KrutiDev. There may be need for experimental steps!
Not knowing anything about the background, users trying to help may waste many hours without a functional result.
There must be a transcription table! Give a link to one! (Best as a spreadsheet.)
Thousands of recorded macros won’t help. One well designed Custom Routine should do.

Kruti Dev to Unicode Macro Code.odt (159.0 KB)

Ohoho… Well, try this

Sub ReplaceInSelection

There was incorrect code here that didn’t do what it was supposed to -
I removed it so that future readers of the thread wouldn’t use it by mistake.

End Sub

Is the order of searched/replaced items proper? 2nd is k, 4th is kk, 5th is kkW, 6th kkS. If it will search k and replace it, then it is not possible to search and replace correctly kk nor kkW nor kkS. There is strange numbering in ODT, args1043, args1029-1042, args1026-1023, args1019-1018, args1016-1017, args962 … Are these argsNumbers for proper order?

@KamilLanda I just reduced 43910 lines to 120 without changing the sequence of replacements. I did not have the patience to analyze the correctness of the code - I simply repeated the sequence of replacements from the original KrutiDevToUnicode procedure. Yes, you’re right, it was necessary to at least pre-sort the table of replacements in descending order of string lengths…

image

This part will not work correctly if there is at least one "”k" in the text. All 934 pairs should be checked for compatibility and for duplicates.

2 Likes

… and in this process, made it possible to grasp, analyze and fix problems in the code. It’s simply unmanageable with nearly 50 thousand lines.

This code works more than 95â„…. But the problem in this code is it also converts the the text which is in English and which is not in kruti dev font. There are several kruti dev fonts like Kruti Dev 055, Kruti Dev 010 etc. I want the code should not disturb the English text. So I feel that if you select the text in kruti dev font and then run the macro and if the macro will affect only on the selected text it will be more useful.

Why? Why didn’t you put it in the body of your question? @Lupp was right

Also heed the advice of @Lupp

Yes, not the text with the code of your macro, but a sample of the transcoded text - this is necessary to check the correct operation of the code before publishing it.

Please carefully check this conversion table and make the necessary corrections (or confirm it is correct) - TrueType_To_Unichar.ods (51.4 KB)

Hallo
Its pretty simple with python

import re 
rex = re.compile(r"yka|k")

def repl(match):
    if match.group() == "yka":
        return "लां"
    else:
        return "ष"

def replace_in_selections(*_):
    doc = XSCRIPTCONTEXT.getDocument()
    selections = doc.CurrentSelection
    for selection in selections:
        selection.String = rex.sub(repl, selection.String)

To organize and run python, you should install apso.oxt from here

@karolus Yes, you’re right, this approach is better than JeJe’s idea - why search for all available occurrences of the search string in the entire document, if we can just replace the strings in the selections? This works much faster:

Sub ReplaceInSelection
Dim OriginalStrings As Variant, TargetStrings As Variant
Dim oSelections As Variant, sText As String
Dim i As Long, k As Long
	OriginalStrings = Array("yka", "k")
	TargetStrings = Array("लां", "ष")

	oSelections =ThisComponent.getCurrentSelection()
	For k = 0 To oSelections.getCount()-1
		sText = oSelections.getByIndex(k).getString()
		For i = LBound(OriginalStrings) To UBound(OriginalStrings)
			sText = Replace(sText,OriginalStrings(i),TargetStrings(i),1,-1,0)
		Next i
		oSelections.getByIndex(k).setString(sText)
	Next k
End Sub

If you want to replace only the simple characters and you don’t need the regular expressions for finding, then it is possible to search the regular expression from searched characters and replace the found parts. The advantage is, it will keep up the formatting; and it can be faster than searching the selection again and again for every item in array.

I haven’t installed the fonts for your example yka, so the example for replacing is: bc → @1; op → #:. So the macro searches the (bc|op) and then replaces the found part bc or op.

I don’t know how large document you need to search, so I put there also the lockControllers (it could be faster), and Undo Manager for only one step in Undo (it could be more comfortable for mistake in replacing).

Edit the variable p() to your replacements.
find-repl-in-selection.odt (15.0 kB)