136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
/*
|
|
#include "adapter_native.h"
|
|
*/
|
|
import "C"
|
|
|
|
// Only the native owner calls this, after foreground validation. Counts use a
|
|
// fixed number of RPCs; no selected-row/column arrays or sheet walks are used.
|
|
// LibreOffice itself scans its row marks when answering selected-row counts.
|
|
func captureLibreOfficeSelection(source *C.AtspiAccessible) *LibreOfficeSelection {
|
|
selection := C.atspi_accessible_get_selection_iface(source)
|
|
table := C.atspi_accessible_get_table_iface(source)
|
|
if selection == nil || table == nil {
|
|
if selection != nil {
|
|
C.g_object_unref(C.gpointer(selection))
|
|
}
|
|
if table != nil {
|
|
C.g_object_unref(C.gpointer(table))
|
|
}
|
|
return nil
|
|
}
|
|
defer C.g_object_unref(C.gpointer(selection))
|
|
defer C.g_object_unref(C.gpointer(table))
|
|
var queryError *C.GError
|
|
count := int(C.atspi_selection_get_n_selected_children(selection, &queryError))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
if count < 0 {
|
|
return nil
|
|
}
|
|
result := &LibreOfficeSelection{Kind: "cells", Count: count, CountLimited: count == 1<<31-1}
|
|
if count == 0 {
|
|
result.Complete = true
|
|
return result
|
|
}
|
|
rows := int(C.atspi_table_get_n_rows(table, &queryError))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
columns := int(C.atspi_table_get_n_columns(table, &queryError))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
selectedRows, selectedColumns := 0, 0
|
|
// A small selection cannot contain a whole row or column. Avoid making
|
|
// LibreOffice scan all its row marks for ordinary Shift+Arrow movement.
|
|
if count >= columns {
|
|
selectedRows = int(C.atspi_table_get_n_selected_rows(table, &queryError))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
}
|
|
if count >= rows {
|
|
selectedColumns = int(C.atspi_table_get_n_selected_columns(table, &queryError))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
}
|
|
if rows <= 0 || columns <= 0 || selectedRows < 0 || selectedColumns < 0 {
|
|
return nil
|
|
}
|
|
if selectedRows == rows || selectedColumns == columns {
|
|
return &LibreOfficeSelection{Kind: "all", Complete: true}
|
|
}
|
|
stride := 1
|
|
// Only classify pure row/column selections. Mixed selections retain an
|
|
// honest cell count. GTK caps counts above INT_MAX, rather than wrapping.
|
|
if !result.CountLimited && selectedRows > 0 && int64(selectedRows)*int64(columns) == int64(count) {
|
|
result.Kind, result.Count, result.CountLimited = "rows", selectedRows, false
|
|
stride = columns
|
|
} else if !result.CountLimited && selectedColumns > 0 && int64(selectedColumns)*int64(rows) == int64(count) {
|
|
result.Kind, result.Count, result.CountLimited = "columns", selectedColumns, false
|
|
}
|
|
indices := make([]int, 0, libreOfficeSelectionLimit)
|
|
if result.Count > libreOfficeSelectionLimit {
|
|
indices = append(indices, 0, count-1)
|
|
} else {
|
|
for index := 0; index < result.Count; index++ {
|
|
indices = append(indices, index*stride)
|
|
}
|
|
}
|
|
for _, index := range indices {
|
|
child := C.atspi_selection_get_selected_child(selection, C.gint(index), &queryError)
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
if child != nil {
|
|
C.g_object_unref(C.gpointer(child))
|
|
}
|
|
return nil
|
|
}
|
|
if child == nil {
|
|
return nil
|
|
}
|
|
cell := C.atspi_accessible_get_table_cell(child)
|
|
var row, column C.gint
|
|
valid := false
|
|
if cell != nil {
|
|
valid = C.atspi_table_cell_get_position(cell, &row, &column, &queryError) != 0
|
|
C.g_object_unref(C.gpointer(cell))
|
|
}
|
|
C.g_object_unref(C.gpointer(child))
|
|
if queryError != nil {
|
|
C.g_error_free(queryError)
|
|
return nil
|
|
}
|
|
if !valid || row < 0 || column < 0 || int(row) >= rows || int(column) >= columns {
|
|
return nil
|
|
}
|
|
position := LibreOfficeCell{Row: int(row), Column: int(column)}
|
|
if result.Kind == "rows" {
|
|
position.Column = 0
|
|
}
|
|
if result.Kind == "columns" {
|
|
position.Row = 0
|
|
}
|
|
result.Cells = append(result.Cells, position)
|
|
}
|
|
result.First, result.Last = result.Cells[0], result.Cells[len(result.Cells)-1]
|
|
result.Complete = result.Count <= libreOfficeSelectionLimit
|
|
if !result.Complete {
|
|
result.Cells = nil
|
|
}
|
|
return result
|
|
}
|