91 lines
3.2 KiB
Go
91 lines
3.2 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
)
|
|
|
|
type libreOfficeHeaderConfigurer interface {
|
|
ConfigureLibreOfficeHeader(atspi.Snapshot, atspi.ObjectID, bool, bool) error
|
|
}
|
|
|
|
func (handler *eventReviewHandler) configureCalcHeader(command input.Command) {
|
|
if handler.focusGate.Locked() || handler.focusGate.Suspended() {
|
|
return
|
|
}
|
|
handler.syncForeground()
|
|
state := handler.state
|
|
snapshot := state.current
|
|
context := snapshot.LibreOffice
|
|
text := "Not in a Calc cell."
|
|
if handler.calcHeaders != nil && context != nil && context.Cell && context.InSpreadsheet &&
|
|
!snapshot.Defunct && atspi.IsLibreOfficeProcess(snapshot.ApplicationProcess) &&
|
|
state.owner.window.Valid() && state.owner.applicationBus == snapshot.BusName &&
|
|
state.owner.focus == snapshot.ID() && snapshot.TablePositionKnown {
|
|
columnHeaders := command == input.SetColumnHeadersRow || command == input.ClearColumnHeadersRow
|
|
clear := command == input.ClearColumnHeadersRow || command == input.ClearRowHeadersColumn
|
|
if err := handler.calcHeaders.ConfigureLibreOfficeHeader(snapshot, state.owner.window, columnHeaders, clear); err != nil {
|
|
text = err.Error()
|
|
} else if columnHeaders {
|
|
// The command changes this axis immediately. Its text is captured
|
|
// again on the next cell event, never queried by the event loop.
|
|
state.current.TableColumnHeaders = ""
|
|
text = fmt.Sprintf("Row %d set as column headers.", snapshot.TableRowIndex+1)
|
|
if clear {
|
|
text = "Column headers cleared."
|
|
}
|
|
} else {
|
|
state.current.TableRowHeaders = ""
|
|
text = fmt.Sprintf("Column %s set as row headers.", calcColumnLabel(snapshot.TableColumnIndex))
|
|
if clear {
|
|
text = "Row headers cleared."
|
|
}
|
|
}
|
|
if handler.brailleOutput != nil {
|
|
options := config.DefaultSettings().Braille
|
|
if handler.live != nil {
|
|
options = handler.live.Load().Braille
|
|
}
|
|
state.brailleSnapshot = state.current
|
|
handler.brailleOutput.EnqueueFrame(brailleFocusFrameWithOptions(state.current, options))
|
|
}
|
|
}
|
|
state.pending.clear()
|
|
handler.speech.Enqueue(speechRequest{Text: text, Role: "system", Cause: "calc-headers"})
|
|
}
|
|
|
|
func calcColumnLabel(column int) string {
|
|
label := ""
|
|
for column >= 0 {
|
|
label = string(rune('A'+column%26)) + label
|
|
column = column/26 - 1
|
|
}
|
|
return label
|
|
}
|
|
|
|
// Use existing table presentation history, which belongs to the event loop.
|
|
// Keep the full snapshot for Where Am I and Braille; only automatic speech
|
|
// omits headings for an unchanged axis, following current Orca.
|
|
func (state *eventLoopState) calcHeaderSpeechEvent(event atspi.Event) atspi.Event {
|
|
snapshot := event.Source
|
|
if snapshot.LibreOffice == nil || !snapshot.LibreOffice.Cell ||
|
|
!atspi.IsLibreOfficeProcess(snapshot.ApplicationProcess) || !snapshot.TablePositionKnown ||
|
|
!state.lastTableCell.Valid() || state.lastTable != calcSheet(snapshot) {
|
|
return event
|
|
}
|
|
if snapshot.TableRowIndex == state.lastTableRow {
|
|
event.Source.TableRowHeaders = ""
|
|
}
|
|
if snapshot.TableColumnIndex == state.lastTableColumn {
|
|
event.Source.TableColumnHeaders = ""
|
|
}
|
|
return event
|
|
}
|