Macro: Setting table column/cell widths by setting separator positions

I am attempting to set the width of all the cells in a table to a certain value by setting the position of each row’s separators using the TableColumnRelativeSum to calculate the appropriate relative Position. It’s necessary to use the relative values because, as the TableColumnSeparator docs explain:

The real width of a table depends on the environment (page style and number of text columns at the table’s position, alignment, and left and right margins). For that reason, the table column separator does not contain metric values for the column widths. The values are relative to the value of the property TextTable::TableColumnRelativeSum.

So here’s my attempt. It calculates the correct relative column separator positions for my table (using inches and a specific table width and column width), row by row. When I step through the macro, I can see the variables changing to the correct values, including the Position of each separator in each row.

The problem is, the changes are never reflected in the document. I tried setting the TableColumnSeparators and even each row, but it doesn’t do anything.

I found a forum post discussing how to do it in Java, but that doesn’t help me much, I’m afraid. It looks like in a simple table (one where all the rows have cells of equal width, so it’s uniform), you can set the TableColumnSeparators on the whole table at once, but it’s not possible to set them for a row. I guess this is a bug or missing functionality in Writer.

Sub Main

tableName = "Table5"
tables = ThisComponent.TextTables
table = tables.getByName(tableName)

tableWidthRelative = table.TableColumnRelativeSum
tableWidthIn = 5.5

columnWidthIn = 0.89
columnWidthRelative = columnWidthIn / tableWidthIn * tableWidthRelative

rows = table.getRows()
for i = 0 to (rows.Count() - 1)
	row = rows.getByIndex(i)
	
	seps = row.TableColumnSeparators

    ' TableColumnSeparators is a Sequence, which 
      does not support the Count method.  You must use
      UBound() to get its length. '
	numSeps = UBound(seps)
	
	for s = 0 to numSeps
        sep = seps(s)
        sep.Position = columnWidthRelative * (s+1)
        seps(s) = sep
	next

	row.TableColumnSeparators = seps

	table.Rows(i) = row
next

tables.getByName(tableName) = table

end sub

Edit after JohnSUN’s answer

JohnSUN’s answer was very helpful, and nearly worked, but I ran into issues where some rows would have separators that were somehow conflicting with each other. I tried to use IsVisible to work around that, but I couldn’t get it to work. It might be possible to hack something up with counting cells in rows and calculating how many separators there are per row, or how many visible ones, or something like that. I corrected the script above so it works now, as much as seems possible.

In the end, the most reliable method to set all the columns’ widths was this Bash script, bound to a global keyboard shortcut. I have it open in Emacs in one window, so I can easily modify it as needed.

#!/bin/bash

delay="--delay 25"

doTimes()
{
    times=$1
    shift
    
    for i in $(seq $times)
    do
	    eval "$@"
    done
    sleep 0.1
}
setTo()
{
    xdotool key --clearmodifiers $delay Menu type --clearmodifiers $delay "mw"
    xdotool search "Column Width" windowfocus
    xdotool key --clearmodifiers $delay Tab type --clearmodifiers $delay "$1"
    xdotool key --clearmodifiers  Return
}
tab()
{
    xdotool key --clearmodifiers  Tab
}

doTimes 4 "setTo 0.89; tab"
#doTimes 2 "setTo 0.08; tab"
doTimes 1 "setTo 0.16; tab"
doTimes 2 "setTo 0.89; tab"

If only Writer would actually change all of the cells’ widths when you select all of them…

You just forgot that to change the values in structures need to copy a value to a variable, change it, and copy back (I stumbled on it too).

Add a line

seps(s) = sep

after

sep.Position = columnWidthRelative * (s+1)

inside the loop by seps

Wow, thank you, it’s almost working correctly now. It doesn’t seem to be changing every cell in the table, but doing another column each time I run the script. But you have helped me tremendously. This is my first time doing LibreOffice macros, so I’m not familiar with the idiosyncracies. :slight_smile:

One question, I didn’t understand the second part of your answer, “and also inside…”

Try to read chapter “8.2. Enumerating cells in a text table.” in Useful Macro Information For OpenOffice.org By Andrew Pitonyak and see remark to Listing 7.61 (about “copied back into”)

About “and also inside…” I was not very attentive when debugging your code. Remembered that I had to come back the changed value twice and forgot what it was second. :slight_smile: So I remove these lines from the answer

Wow, thanks, I never would have found that. That really needs to be published somewhere more visible as HTML, like on the LO web site.