linux-presets/gui/gnome/autocustom-gnome-macos/res/extensions/date-menu-formatter@marcinj.../extension.js

303 lines
9.1 KiB
JavaScript

/* extension.js
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import GLib from 'gi://GLib'
import Clutter from 'gi://Clutter'
import St from 'gi://St'
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'
import * as Main from 'resource:///org/gnome/shell/ui/main.js'
const MainPanel = Main.panel
import {
getCurrentLocale,
getCurrentCalendar,
getCurrentTimezone,
updateLevel,
TEXT_ALIGN_CENTER,
} from './utils/general.js'
import { FormatterManager } from './utils/formatter.js'
import * as prefFields from './utils/prefFields.js'
let PATTERN = ''
let USE_DEFAULT_LOCALE = true
let USE_DEFAULT_CALENDAR = true
let USE_DEFAULT_TIMEZONE = true
let CUSTOM_LOCALE = ''
let CUSTOM_CALENDAR = ''
let CUSTOM_TIMEZONE = ''
let REMOVE_MESSAGES_INDICATOR = false
let APPLY_ALL_PANELS = false
let FONT_SIZE = 1
let EVERY = null
let TEXT_ALIGN_MODE = ''
function _getDateMenuButton(panel) {
return panel.statusArea.dateMenu.get_children()[0]
}
export default class DateMenuFormatter extends Extension {
constructor(metadata) {
super(metadata)
this.formatters = null
this._formatters_load_promise = null
this._displays = null
this._timerId = -1
this._settingsChangedId = null
this._dashToPanelConnection = null
this._formatter = null
this._update = true
}
_createDisplay() {
const display = new St.Label({
style_class: 'clock',
style: 'font-size: 9pt; text-align: center',
})
display.clutter_text.x_align = Clutter.ActorAlign.CENTER
display.clutter_text.y_align = Clutter.ActorAlign.CENTER
display.text = '...'
return display
}
_loadSettings() {
this._settings = this.getSettings()
this._settingsChangedId = this._settings.connect(
'changed',
this._onSettingsChange.bind(this)
)
this._onSettingsChange()
}
_fetchSettings() {
PATTERN = this._settings.get_string(prefFields.PATTERN)
REMOVE_MESSAGES_INDICATOR = this._settings.get_boolean(
prefFields.REMOVE_MESSAGES_INDICATOR
)
USE_DEFAULT_LOCALE = this._settings.get_boolean(
prefFields.USE_DEFAULT_LOCALE
)
CUSTOM_LOCALE = this._settings.get_string(prefFields.CUSTOM_LOCALE)
USE_DEFAULT_CALENDAR = this._settings.get_boolean(
prefFields.USE_DEFAULT_CALENDAR
)
CUSTOM_CALENDAR = this._settings.get_string(prefFields.CUSTOM_CALENDAR)
USE_DEFAULT_TIMEZONE = this._settings.get_boolean(
prefFields.USE_DEFAULT_TIMEZONE
)
CUSTOM_TIMEZONE = this._settings.get_string(prefFields.CUSTOM_TIMEZONE)
APPLY_ALL_PANELS = this._settings.get_boolean(prefFields.APPLY_ALL_PANELS)
FONT_SIZE = this._settings.get_int(prefFields.FONT_SIZE)
TEXT_ALIGN_MODE =
this._settings.get_string(prefFields.TEXT_ALIGN) || TEXT_ALIGN_CENTER
const curLvl = this._settings.get_int(prefFields.UPDATE_LEVEL)
if (EVERY.lvl !== curLvl) {
EVERY = updateLevel(curLvl)
if (this._timerId !== -1) this.restart()
}
const locale = USE_DEFAULT_LOCALE ? getCurrentLocale() : CUSTOM_LOCALE
const calendar = USE_DEFAULT_CALENDAR
? getCurrentCalendar()
: CUSTOM_CALENDAR
const timezone = USE_DEFAULT_TIMEZONE
? getCurrentTimezone()
: CUSTOM_TIMEZONE
this._formatters_load_promise.then(() => {
const formatterKey = this._settings.get_string(prefFields.FORMATTER)
const formatter = this.formatters.getFormatter(formatterKey)
if (formatter) {
this._formatter = new formatter(timezone, locale, calendar)
}
})
}
_removeIndicator(panels) {
panels.forEach((panel) => {
if (panel.statusArea.dateMenu._indicator.get_parent())
_getDateMenuButton(panel).remove_child(
panel.statusArea.dateMenu._indicator
)
})
}
_restoreIndicator(panels) {
panels.forEach((panel) => {
if (!panel.statusArea.dateMenu._indicator.get_parent())
_getDateMenuButton(panel).insert_child_at_index(
panel.statusArea.dateMenu._indicator,
2
)
})
}
// returns affected and unaffected panels based on settings and Dash To Panel availability
_getPanels() {
if (!global.dashToPanel) return [[MainPanel], []]
else if (APPLY_ALL_PANELS) {
return [global.dashToPanel.panels, []]
} else {
// MainPanel is not the same as primary Dash To Panel panel, but their dateMenus are the same
return [
[MainPanel],
global.dashToPanel.panels.filter(
(panel) => panel.statusArea.dateMenu != MainPanel.statusArea.dateMenu
),
]
}
}
_enableOn(panels) {
panels.forEach((panel, idx) => {
const dateMenuButton = _getDateMenuButton(panel)
if (!this._displays[idx].get_parent()) {
dateMenuButton.insert_child_at_index(this._displays[idx], 1)
dateMenuButton.dateMenuFormatterDisplay = this._displays[idx]
}
if (panel.statusArea.dateMenu._clockDisplay.get_parent()) {
dateMenuButton.remove_child(panel.statusArea.dateMenu._clockDisplay)
}
})
}
_disableOn(panels) {
panels.forEach((panel) => {
const dateMenuButton = _getDateMenuButton(panel)
if (!panel.statusArea.dateMenu._clockDisplay.get_parent()) {
dateMenuButton.insert_child_at_index(
panel.statusArea.dateMenu._clockDisplay,
1
)
}
if (
dateMenuButton.dateMenuFormatterDisplay &&
dateMenuButton.dateMenuFormatterDisplay.get_parent()
) {
dateMenuButton.remove_child(dateMenuButton.dateMenuFormatterDisplay)
}
})
}
_onSettingsChange() {
this._fetchSettings()
// does Dash to Panel support more than 2 panels? better to be safe than sorry
if (
global.dashToPanel &&
this._displays.length < global.dashToPanel.panels.length
) {
const missingPanels =
global.dashToPanel.panels.length - this._displays.length
this._displays = [
...this._displays,
...Array.from({ length: missingPanels }, () => this._createDisplay()),
]
}
const [affectedPanels, unaffectedPanels] = this._getPanels()
if (REMOVE_MESSAGES_INDICATOR) {
this._removeIndicator(affectedPanels)
this._restoreIndicator(unaffectedPanels)
} else {
this._restoreIndicator([...affectedPanels, ...unaffectedPanels])
}
this._enableOn(affectedPanels)
this._disableOn(unaffectedPanels)
this._displays.forEach(
(display) =>
(display.style = `font-size: ${FONT_SIZE}pt; text-align: ${TEXT_ALIGN_MODE}`)
)
}
enable() {
EVERY = updateLevel()
this.formatters = new FormatterManager()
this._formatters_load_promise = this.formatters.loadFormatters()
this._displays = [this._createDisplay()]
if (global.dashToPanel) {
this._dashToPanelConnection = global.dashToPanel.connect(
'panels-created',
() => this._onSettingsChange()
)
}
this._loadSettings()
const [affectedPanels, _] = this._getPanels()
this._enableOn(affectedPanels)
this.start()
}
start() {
this._update = true
this._timerId = GLib.timeout_add(EVERY.priority, EVERY.timeout, () =>
this.update()
)
this.update()
}
stop(force) {
if (force) {
GLib.Source.remove(this._timerId)
} else {
this._update = false
}
}
restart() {
this.stop(true)
this.start()
}
update() {
const setText = (text) =>
this._displays.forEach((display) => (display.text = text))
try {
setText(this._formatter.format(PATTERN, new Date()))
} catch (e) {
// if there is an exception during formatting, use the default display's text
setText(MainPanel.statusArea.dateMenu._clockDisplay.text)
if (this._formatter !== null && this._formatter !== undefined)
console.log('DateMenuFormatter: ' + e.message)
}
return this._update
}
disable() {
EVERY = null
const [affectedPanels, unaffectedPanels] = this._getPanels()
const allPanels = [...affectedPanels, ...unaffectedPanels]
this._disableOn(allPanels)
this._restoreIndicator(allPanels)
this.stop()
if (this._settingsChangedId) {
this._settings.disconnect(this._settingsChangedId)
this._settingsChangedId = null
}
if (this._dashToPanelConnection) {
global.dashToPanel.disconnect(this._dashToPanelConnection)
this._dashToPanelConnection = null
}
this._settings = null
this.formatters = null
this._formatter = null
this._formatters_load_promise = null
this.display?.forEach((d) => d?.destroy())
this._displays = null
}
}