Page 1 of 1

Enumerating conditional text fields

Posted: Fri May 31, 2019 6:48 am
by DavidHMcCracken
LibreOffice 6.1.5.2 (x64) on W10. Writer. BASIC macro.
I'm trying to enumerate all text fields in a document in order to search for a specific one. Other objects enumerate easily. e.g.ThisComponent.Bookmarks is an array of bookmark objects. ThisComponent.TextFields exists but is not an array and has no iterative mechanism. To try to do this with general text fields I have modified Pitonyak's EnumerateTextSections (Listing 342) to show only text fields but in more detail, i.e. if not IsNull( oParSection.TextField) then inspect the section. For variable type text fields the object is fully constructed and all methods and elements are valid and accessible. But for conditional text fields the object is incomplete. Most elements are <out of scope> and all of the elements that are not like this have the same values as any fully constructed text field. Even the array elements that the BASIC IDE debugger implies are complete because it tells their index range, SupportedServiceName, Types, and ImplementationID, are not complete and expanding them crashes the debugger. Writer itself obviously knows how to make sense of this because Insert > Field > More Fields > Functions tab shows any existing conditional text fields. Does anyone have any idea how to access this same information from a macro?

Re: Enumerating conditional text fields

Posted: Fri May 31, 2019 4:50 pm
by Zizi64
Do you have at least one of the third party object inspection tools? (MRI, XrayTool...)

Re: Enumerating conditional text fields

Posted: Fri May 31, 2019 6:40 pm
by DavidHMcCracken
Thank you for the suggestion. I do not have these. I will check them out. If you have any additional thoughts, please let me know. Your insights have always been very helpful.

Re: Enumerating conditional text fields

Posted: Fri May 31, 2019 6:47 pm
by RoryOF
This thread has some discussion on the matter of User Fields
viewtopic.php?f=45&t=28211

Re: Enumerating conditional text fields

Posted: Fri May 31, 2019 7:19 pm
by DavidHMcCracken
To RoryOF. Thank you for that reference but it isn't quite what I'm looking for. It provides Perl and Python examples and I would like to avoid the complexity of non-native languages (although I like Python a lot more than BASIC). Also, the moderator in that discussion says that the topic has been moved to the "programming" forum and I can't find it to see if there is anything more that I might be able to use.

Re: Enumerating conditional text fields

Posted: Fri May 31, 2019 10:36 pm
by JeJe
adapting what I saw here:

https://stackoverflow.com/questions/497 ... e-document

if you install mri you can investigate further:

Code: Select all

Sub Main
paragraphs = thiscomponent.text.createEnumeration()
while paragraphs.hasMoreElements()
    text_portions = paragraphs.nextElement().createEnumeration()
    while text_portions.hasMoreElements()
        text_portion = text_portions.nextElement()
		 if text_portion.TextPortionType = "TextField" then
			msgbox text_portion.string
'			mri text_portion
			 end if
  wend
wend
end sub


Re: Enumerating conditional text fields

Posted: Sat Jun 01, 2019 7:24 am
by DavidHMcCracken
Thank you all for responding so quickly and offering good advice.

Per Zizi's suggestion I installed XrayTool and it cleared up everything. I have been having some difficulty determining the scope of UNO things and relying on the BASIC IDE to inspect objects to see what exists and what needs to be constructed. When that crashes, I'm left wondering whether what I'm asking is unknowable or just not yet known.

XrayTool had no trouble at all inspecting the objects. I imagine that MRI is similarly capable. "Get a tool that can inspect objects without crashing" is probably the first answer to any macro problem. The specific question of "enumerating conditional text fields" becomes fairly trivial when you can reliably inspect objects.

However, for anyone whose focus is this specific topic I would like to share what I have learned. For each element (section) of a paragraph, testing for TextField not NULL is a reliable and safe (i.e. it is a reference that exists for all sections) indication of TextField. Under this, all elements are either identical regardless of TextField sub-type or unique to the sub-type, in which case accessing them throws an exception, except for the SupportedServiceNames array.

Inspecting this crashed the native IDE but XrayTool showed clearly that the first element reveals the field type. For example SupportedServiceNames(0) = "com.sun.star.text.TextField.SetExpression" for a variable but "com.sun.star.text.TextField.ConditionalText" for conditional text. A macro that needs to know the specific TextField sub-type must either do a string comparison of this or try to access an element that exists only for the subtype and handle the exception thrown by all others. This works well in my situation.

I just want to know whether a TextField-Variable with the VariableName ShowBookmarks exists. If my macro tries to access the VariableName element of any TextField that is not a VARIABLE (SetExpression) it raises an exception and the handler can simply be to continue (on error resume next). For users who may have other requirements I present some macros based on Pitonyak's EnumerateTextSections (Listing 342) which may be helpful.

showTextFields demonstrates parsing the SupportedServiceNames to determine the sub-type.

Code: Select all

sub showTextFields
    parEnum = ThisComponent.Text.createEnumeration()
    do while parEnum.hasMoreElements()
        oPar = parEnum.nextElement()
        if oPar.supportsService("com.sun.star.text.Paragraph") then
            nPars = nPars + 1
            secEnum = oPar.createEnumeration()
            show = show & nPars & ":"
            do while secEnum.hasMoreElements()
                oSec = secEnum.nextElement()
                if not IsNull(oSec.TextField) then
                    'Xray oSec.TextField
                    ssn = split(oSec.TextField.SupportedServiceNames(0), ".")
                    show = show & "TextField-" & ssn(Ubound(ssn)) & " "
                end if 'Section is TextField
            loop 'while parEnum.hasMoreElements i.e. over all elements in this paragraph
            show = show & CHR$(10) 'Start new line for the next paragraph
        end if 'Paragraph
    loop 'while parEnum.hasMoreElements() i.e. over all paragraphs
    MsgBox show, 0, "Paragraph Text Sections"
end sub
showVarFields shows only the VARIABLE sub-type by throwing a resume next exception for any TextField that doesn't have the VariableName element:

Code: Select all

sub showVarFields
    on error resume next
    parEnum = ThisComponent.Text.createEnumeration()
    do while parEnum.hasMoreElements()
        oPar = parEnum.nextElement()
        if oPar.supportsService("com.sun.star.text.Paragraph") then
            nPars = nPars + 1
            secEnum = oPar.createEnumeration()
            show = show & nPars & ":"
            do while secEnum.hasMoreElements()
                oSec = secEnum.nextElement()
                if not IsNull(oSec.TextField) then
                    show = show & "VariableField-" & oSec.TextField.VariableName & " "
                end if 'Section is TextField
            loop 'while secEnum.hasMoreElements i.e. over all elements in this paragraph
            show = show & CHR$(10) 'Start new line for the next paragraph
        end if 'Paragraph
    loop 'while parEnum.hasMoreElements() i.e. over all paragraphs
    MsgBox show, 0, "Paragraph Text Sections"
end sub
throwFields shows how text field sub-types can be determined by no exception on accessing something unique to that type.

Code: Select all

sub throwFields()
    on error goto TfError
    parEnum = ThisComponent.Text.createEnumeration()
    do while parEnum.hasMoreElements()
        curPar = parEnum.nextElement()
        if curPar.supportsService("com.sun.star.text.Paragraph") then
            nPars = nPars + 1
            secEnum = curPar.createEnumeration()
            show = show & nPars & ":"
            do while secEnum.hasMoreElements()
                curSec = secEnum.nextElement()
                if Not IsNull(curSec.TextField) then
                    tfType = 0
TryAnother:         
                    if tfType = 0 then
                        show = show & "ConditionalText-onVar=" & _
                          curSec.TextField.Condition & "; "
                    elseIf tfType = 1 then 
                        show = show & "SetExpression-VarName=" & _
                          curSec.TextField.VariableName & "; "
                    else
                        show = show & "Other; "
                    end if
                end if 'Section is TextField
            loop ' Over all elements in this paragraph
            show = show & CHR$(10) 'Start new line for the next paragraph
        end if 'Paragraph
    loop ' Over all document elements.
    MsgBox show, 0, "Paragraph Text Sections"
    on error goto 0
    exit sub
TfError:
    tfType = tfType + 1
    resume TryAnother
end sub