// SPDX-FileCopyrightText: 2023 Deminder // SPDX-License-Identifier: GPL-3.0-or-later import Gtk from 'gi://Gtk'; import Gio from 'gi://Gio'; import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; import { Install } from './modules/install.js'; import { actionLabel, ACTIONS, mapLegacyAction, supportedActions, } from './dbus-service/action.js'; import { logDebug, Idle } from './modules/util.js'; const templateComponents = { shutdown: { 'shutdown-mode': 'combo', 'root-mode': 'switch', 'show-end-session-dialog': 'switch', 'shutdown-max-timer': 'adjustment', 'shutdown-ref-timer': 'buffer', 'shutdown-slider': 'adjustment', 'nonlinear-shutdown-slider': 'adjustment', }, wake: { 'auto-wake': 'switch', 'wake-max-timer': 'adjustment', 'wake-ref-timer': 'buffer', 'wake-slider': 'adjustment', 'nonlinear-wake-slider': 'adjustment', }, display: { 'show-settings': 'switch', 'show-shutdown-mode': 'buffer', 'show-shutdown-slider': 'switch', 'show-shutdown-indicator': 'switch', 'show-shutdown-absolute-timer': 'switch', 'show-textboxes': 'switch', 'show-wake-slider': 'switch', 'show-wake-items': 'switch', 'show-wake-absolute-timer': 'switch', }, }; export default class ShutdownTimerPreferences extends ExtensionPreferences { /** * Fill the preferences window with preferences. * * The default implementation adds the widget * returned by getPreferencesWidget(). * * @param {Adw.PreferencesWindow} window - the preferences window */ fillPreferencesWindow(window) { const builder = Gtk.Builder.new(); builder.add_from_file( this.dir.get_child('ui').get_child('prefs.ui').get_path() ); const settings = this.getSettings(); const handlers = []; const pageNames = ['install', 'shutdown', 'wake', 'display'].map( n => `shutdowntimer-prefs-${n}` ); for (const name of pageNames) { const pageId = name.replaceAll('-', '_'); const page = builder.get_object(pageId); const pageName = pageId.split('_').at(-1); if (!page) { throw new Error(`${pageId} not found!`); } if (pageName === 'install') { const idle = new Idle(); const install = new Install(); this.initInstallPage( builder, this.dir.get_child('tool').get_child('installer.sh').get_path(), install, idle ); window.connect('destroy', () => { idle.destroy(); install.destroy(); }); } this.initPage(pageName, builder, settings); window.add(page); } const selPageName = pageNames[settings.get_int('preferences-selected-page-value')]; if (selPageName) { window.set_visible_page_name(selPageName); } const pageVisHandlerId = window.connect('notify::visible-page-name', () => { logDebug(window.get_visible_page_name()); settings.set_int( 'preferences-selected-page-value', pageNames.indexOf(window.get_visible_page_name()) ); }); handlers.push([window, pageVisHandlerId]); window.connect('destroy', () => { handlers.forEach(([comp, handlerId]) => { comp.disconnect(handlerId); }); }); } async initShutdownModeCombo(settings, comp) { const model = new Gtk.StringList(); const actionIds = []; try { for await (const action of supportedActions()) { model.append(actionLabel(action)); actionIds.push(ACTIONS[action]); } } catch (err) { console.error(err); } comp.model = model; const updateComboRow = () => { const actionId = ACTIONS[mapLegacyAction(settings.get_string('shutdown-mode-value'))]; const index = actionIds.indexOf(actionId); if (index >= 0) comp.selected = index; }; comp.connect('notify::selected', () => { const actionId = actionIds[comp.selected]; const action = Object.entries(ACTIONS).find( ([_, id]) => id === actionId )[0]; if (action) settings.set_string('shutdown-mode-value', action); }); const comboHandlerId = settings.connect( 'changed::shutdown-mode-value', () => updateComboRow() ); comp.connect('destroy', () => settings.disconnect(comboHandlerId)); updateComboRow(); } initPage(pageName, builder, settings) { if (pageName in templateComponents) { for (const [baseName, component] of Object.entries( templateComponents[pageName] )) { const baseId = baseName.replaceAll('-', '_'); const settingsName = `${baseName}-value`; const compId = `${baseId}_${component}`; const comp = builder.get_object(compId); if (!comp) { throw new Error(`Component not found in template: ${compId}`); } if (compId === 'shutdown_mode_combo') { this.initShutdownModeCombo(settings, comp); } else { settings.bind( settingsName, comp, { adjustment: 'value', switch: 'active', textbuffer: 'text', buffer: 'text', }[component], Gio.SettingsBindFlags.DEFAULT ); } if ( [ 'show_wake_slider_switch', 'show_wake_absolute_timer_switch', ].includes(compId) ) this.switchDependsOnSetting(comp, settings, 'show-wake-items-value'); } } } switchDependsOnSetting(comp, settings, settingsName) { const update = () => { const active = settings.get_boolean(settingsName); comp.sensitive = active; const row = comp.get_parent().get_parent(); row.sensitive = active; }; const handlerId = settings.connect(`changed::${settingsName}`, () => update() ); comp.connect('destroy', () => settings.disconnect(handlerId)); update(); } initInstallPage(builder, installerScriptPath, install, idle) { // install log textbuffer updates const logTextBuffer = builder.get_object('install_log_textbuffer'); const scrollAdj = builder.get_object('installer_scrollbar_adjustment'); const errorTag = new Gtk.TextTag({ foreground: 'red' }); const successTag = new Gtk.TextTag({ foreground: 'green' }); const table = logTextBuffer.get_tag_table(); table.add(errorTag); table.add(successTag); const installSwitch = builder.get_object('install_policy_switch'); installSwitch.set_active(install.checkInstalled()); installSwitch.connect('notify::active', () => install.installAction( installerScriptPath, installSwitch.get_active() ? 'install' : 'uninstall', async message => { await idle.guiIdle(); // Format log lines message.split('\n').forEach(line => { line = ['[', '#'].includes(line[0]) ? line : ` ${line}`; const b = logTextBuffer; b.insert(b.get_end_iter(), `${line}\n`, -1); const end = b.get_end_iter(); const start = end.copy(); if (start.backward_line()) { if (line.startsWith('# ')) { b.apply_tag(errorTag, start, end); } else if (line.endsWith('🟢')) { b.apply_tag(successTag, start, end); } } }); await idle.guiIdle(); scrollAdj.set_value(1000000); } ) ); // Clear install log logTextBuffer.set_text('', -1); } }