// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package preferences /* #include #include "native.h" */ import "C" import ( "fmt" "git.stormux.org/storm/cthulhu/internal/interaction" "git.stormux.org/storm/cthulhu/internal/tentaclewire" "unsafe" ) func extension_page_key(page ExtensionPage) string { return page.Session + "/" + page.ID } // GTK owns native focus. Refreshes keep unchanged widgets and their unapplied // drafts, and move focus to the notebook before removing a revoked page. func (session *uiSession) set_extension_pages(pages []ExtensionPage) error { selected := C.GoString(C.prefs_extension_selected()) ids := map[string]bool{} for _, page := range pages { key := extension_page_key(page) if page.Session == "" || ids[key] { return fmt.Errorf("invalid extension page identity") } ids[key] = true if err := tentaclewire.ValidateExtensions(tentaclewire.Extensions{Pages: []tentaclewire.PreferencePage{page.PreferencePage}}); err != nil { return err } } keep := map[string]bool{} for _, previous := range session.extensionPages { key := extension_page_key(previous) for _, page := range pages { if extension_page_key(page) == key && page.Revision == previous.Revision { keep[key] = true break } } if !keep[key] { cKey := C.CString(key) C.prefs_extension_remove(cKey) C.free(unsafe.Pointer(cKey)) } } for _, page := range pages { key := extension_page_key(page) if keep[key] { continue } cKey, title := C.CString(key), C.CString(page.Title) C.prefs_extension_add(cKey, title) C.free(unsafe.Pointer(title)) for row, element := range page.Elements { id, label := C.CString(element.ID), C.CString(element.Label) C.prefs_extension_control(cKey, id, label, C.int(element.Kind), C.int(row), C.int(element.MaxLength), boolean(element.Disabled)) C.free(unsafe.Pointer(label)) for _, option := range element.Items { optionID, optionLabel := C.CString(option.ID), C.CString(option.Label) C.prefs_extension_option(cKey, id, optionID, optionLabel, boolean(option.Disabled)) C.free(unsafe.Pointer(optionID)) C.free(unsafe.Pointer(optionLabel)) } value := page.Values[element.ID] text := value.Text if element.Kind == interaction.List { text = value.Selected } cText := C.CString(text) C.prefs_extension_set(cKey, id, C.int(element.Kind), cText, boolean(value.Checked)) C.free(unsafe.Pointer(cText)) C.free(unsafe.Pointer(id)) } C.free(unsafe.Pointer(cKey)) } session.extensionPages = pages if selected != "" { key := C.CString(selected) C.prefs_extension_select(key) C.free(unsafe.Pointer(key)) } return nil } func (session *uiSession) extension_drafts() ([]PageDraft, error) { var drafts []PageDraft for _, page := range session.extensionPages { values := map[string]interaction.Value{} key := C.CString(extension_page_key(page)) for _, element := range page.Elements { id := C.CString(element.ID) value := interaction.Value{} switch element.Kind { case interaction.List: value.Selected = C.GoString(C.prefs_extension_text(key, id, C.int(element.Kind))) case interaction.Entry: value.Text = C.GoString(C.prefs_extension_text(key, id, C.int(element.Kind))) case interaction.CheckBox: value.Checked = C.prefs_extension_checked(key, id) != 0 } C.free(unsafe.Pointer(id)) values[element.ID] = value } C.free(unsafe.Pointer(key)) declaration := page.PreferencePage declaration.Values = values if err := tentaclewire.ValidateExtensions(tentaclewire.Extensions{Pages: []tentaclewire.PreferencePage{declaration}}); err != nil { return nil, fmt.Errorf("Check the values on %s: %w", page.Title, err) } drafts = append(drafts, PageDraft{Session: page.Session, ID: page.ID, Revision: page.Revision, Values: values}) } return drafts, nil }