A TextTableCursor object is extremely dumb. It neither knows its parent (the table its range is part of) nor can it tell if a given cell (by name e.g.) belongs to the range it covers. The .RangeName property isn’t really helpful in the case of a “complex table”.
Did I miss something? Is there a handy workaround?
In A.Pitonyak book OOME_4_0.odt, section 14.9.15 it is written:
Text table cursors are used to split and merge table cells. In general, I consider this to be the primary use of a text table cursor.
From my point of view, object TextTableCursor is useless for navigating through the cells of a text table. It is more promising to work with the getCellNames and getCellByName methods.
BTW, there seems to be a problem with parent objects in LO. I still don’t know the “normal” way to get the parent (document) for a spreadsheet sheet.
The background of this question was Macro for Each Cells(y,x) in Selection (View topic) • Apache OpenOffice Community Forum.
If you want to get access to each cell of a TextTable
whose area was selected by the user, or part of of a TextTable
which is selected in a view, you have no choice. The selection simply is a TextTableCursor
, and you need to get all the info based on it. As I wrote in the linked thread I judge the respective service to be extremely poor - but that’s as it is.
In the mentioned thread @whiteshark, @JeJe, and myself considered to use an otherwise unused (probably outdated or deprecated) property of TextTable
cells, to mark a selection in a way, that an inspection of the complete table can distinguish the originally selected cells from the others.
First of all you need anyway to get the TextTable
as an object from the selection. Already this step is complicated.
My example attached below is rather well working, but looks crude and is inefficient. It shows the current state, however, of what I found. It will return a list of cells designated as tablename.cellname for the current selection which may be TextRanges
or TextTableCursor
. If TextRanges
, a cell is listed, for the ranges with a non-empty TextTable
property - that’s inside a cell content.
Thanks for the interesting (almost math) problem!
I am (almost) sure that the task can be solved without any actions to modify the cells. When the time comes, I’ll think about it (if someone doesn’t do it earlier ).
Hmmm…
Am curious concerning
- a solution
- how you can be (almost) sure…
Let’s try. On the example of a complex A.Pitonyak table, the macro works.
Option Explicit
Sub Test
Dim v
v=GetTextTableSelectedCells
If Not IsEmpty(v) Then
Msgbox "Selected: " & Join(v, " ")
Else
Msgbox "Selected cells not found"
End If
End Sub
' --------------------------------------------------
' Returns array of names of selected cells or Empty.
' - oDoc Text document. If is missing then ThisComponent.
Function GetTextTableSelectedCells(Optional ByVal oDoc)
Dim arr(), i As Long, j As Long, oAcc, oAccChild
If IsMissing(oDoc) Then oDoc=ThisComponent
GetTextTableSelectedCells=Empty
oDoc.CurrentController.Select oDoc.CurrentSelection ' Cells must be on screen
oAcc=FindAccessibleRole(oDoc.CurrentController.ComponentWindow.AccessibleContext, _
com.sun.star.accessibility.AccessibleRole.DOCUMENT_TEXT)
For i=0 To oAcc.AccessibleChildCount-1
oAccChild=oAcc.getAccessibleChild(i).AccessibleContext
With oAccChild
If .AccessibleRole=com.sun.star.accessibility.AccessibleRole.TABLE Then
If .selectedAccessibleChildCount>0 Then
ReDim arr(.selectedAccessibleChildCount-1)
For j=0 To .selectedAccessibleChildCount-1
arr(j)=.getSelectedAccessibleChild(j).AccessibleName
Next j
GetTextTableSelectedCells=arr
Exit Function
End If
End If
End With
Next i
End Function
' --------------------------------------------------
' Hierarchically search AccessibleContext for role value.
' - oAccContext Object (supports AccessibleContext service).
' - role Role value.
' - index Which item is returned by a count. If less than zero, then the search is performed in reverse order.
' If missed or 0, then 1 is assigned.
'
' If the object has more than 500 child objects, then the child objects are not processed.
Function FindAccessibleRole(ByVal oAccContext, ByVal role As Long, Optional index As Long) As Object
Dim oAccChild, oResult, i As Long, j As Long, n As Long
If IsMissing(index) Then index=1
If index=0 Then index=1
FindAccessibleRole=Nothing
n=oAccContext.AccessibleChildCount
If n=0 Or n>500 Then Exit Function
For i=0 To n-1
oAccChild=oAccContext.getAccessibleChild(IIf(index<0, n-1-i, i)).AccessibleContext
If oAccChild.AccessibleRole=role Then
index=index+IIf(index<0, 1, -1)
If index=0 Then
FindAccessibleRole=oAccChild
Exit Function
End If
End If
' recursion
oResult=FindAccessibleRole(oAccChild, role, index)
If Not (oResult Is Nothing) Then
FindAccessibleRole=oResult
Exit Function
End If
Next i
End Function
A really respectable piece of work!
However, a user should note that the solution is based on a view, and therefore depending on effects of the zoom, of window size, and of page breaks (if a table spans over them).
(I didn’t research all the expectable effects.)
The poor functionality of the TextTableCursor service remains a problem.
For the TextRange it is possible to get the table name and range address from the View Cursor. It is easy.
Also it is possible to get the information from Status Bar, but it is not safe - sometimes there is some delay than the Statusbar is rendered, so if you click to the normal text (no in table) and then quickly to the macro button, sometimes it writes the position in the table, but real cursor is already in normal text.
(both in the example)
I also tested the RangeName and it is more complicated and unsafe.
test-KL1.odt (16.3 kB)
The CharFlash is easier. But also no 100% sureness. Select the C5:I11, it is 13 cells, and the macros fail (both: your with CharFlash or @sokol92 Test from Pitonyak) → only 8 addresses is written.
And also there is apparently the bug (Libre 7.2.2.2 Win10x64).
It seems no bug. The Table3 (selected on the image below) is probably inserted table in one cell of the Table1.
As you already supposed, this was intentional. (The algorithm isn’t looking for nested tables. It coluld do, of course. The parameter pMode
is included to allow for conditionally applicable enhancements of the kind. You may use it the flags way with powers of 2 (additive).
The irritations are related to the fact (bug) that a nested table isn’t in the foreground. (I tried to apply a different Background without success.
Next not so comfortable way. It is possible to test the Anchor of the Table. If some table has the Anchor in the Cell, then it is inserted table.
Sub IsTableInCell 'test the Anchors of all tables to ascertain if some table is inserted in the cell of some other table
dim oDoc as object, oTables as object, oTable as object, i&
oDoc=ThisComponent
oTables=oDoc.TextTables
for i=0 to oTables.Count-1
oTable=oTables(i) 'one table
if NOT isEmpty(oTable.Anchor.Cell) then 'test the Anchor of the table
msgbox oTable.Name & " is inside " & oTable.Anchor.TextTable.Name & "." & oTable.Anchor.Cell.CellName,,"Inserted table"
end if
next i
End Sub