gnome-shell-teatime/src/extension.js

349 lines
11 KiB
JavaScript
Raw Permalink Normal View History

/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: t -*- */
/* Olaf Leidinger <oleid@mescharet.de>
2013-05-21 06:02:28 +00:00
Thomas Liebetraut <thomas@tommie-lie.de>
*/
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Mainloop = imports.mainloop; // timer
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const Layout = imports.ui.layout;
2017-11-03 19:52:04 +00:00
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
2017-11-03 19:52:04 +00:00
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Panel = imports.ui.panel;
const ExtensionUtils = imports.misc.extensionUtils;
2017-11-03 19:52:04 +00:00
const Me = ExtensionUtils.getCurrentExtension();
const Utils = Me.imports.utils;
const Icon = Me.imports.icon;
2013-10-31 09:25:41 +00:00
2017-11-20 22:22:15 +00:00
const _ = Utils.getTranslationFunc();
2017-11-03 19:52:04 +00:00
const N_ = function (e) {
return e;
};
2013-10-31 09:25:41 +00:00
let PopupTeaMenuItem = GObject.registerClass(
2021-09-12 17:31:57 +00:00
class PopupTeaMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(sTeaname, nBrewtime, params) {
super._init(params);
this.tealabel = new St.Label({
text: sTeaname
});
if (nBrewtime != 0) {
this.timelabel = new St.Label({
text: Utils.formatTime(nBrewtime)
});
}
this.add(this.tealabel);
if (nBrewtime != 0) {
this.add(this.timelabel);
}
this._delegate = this;
}
});
let TeaTime = GObject.registerClass(
class TeaTime extends PanelMenu.Button {
_init() {
super._init(1.0, "TeaTime");
this.config_keys = Utils.GetConfigKeys();
this._settings = Utils.getSettings();
this._logo = new Icon.TwoColorIcon(20, Icon.TeaPot);
// set timer widget
this._textualTimer = new St.Label({
text: "",
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER
2018-03-18 12:05:25 +00:00
});
2021-09-12 17:31:57 +00:00
this._graphicalTimer = new Icon.TwoColorIcon(20, Icon.Pie);
this.add_actor(this._logo);
this.add_style_class_name('panel-status-button');
this.connect('style-changed', this._onStyleChanged.bind(this));
this._idleTimeout = null;
this._createMenu();
this._continueRunningTimer();
2018-03-18 12:05:25 +00:00
}
2017-11-03 19:52:04 +00:00
2021-09-12 17:31:57 +00:00
_createMenu() {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._settings.connect("changed::" + this.config_keys.steep_times,
this._updateTeaList.bind(this));
this._settings.connect("changed::" + this.config_keys.graphical_countdown,
this._updateCountdownType.bind(this));
this.teaItemCont = new PopupMenu.PopupMenuSection();
/*******************/
// maybe one day the PopupImageMenuItem works^^
let head = new PopupMenu.PopupMenuSection();
let item = new PopupMenu.PopupMenuItem(_("Show settings")); //, 'gtk-preferences');
// item._icon.icon_size = 15;
item.connect('activate', this._showPreferences.bind(this));
head.addMenuItem(item);
/*******************/
let bottom = new PopupMenu.PopupMenuSection();
this._customEntry = new St.Entry({
style_class: 'teatime-custom-entry',
track_hover: true,
hint_text: _("min:sec")
});
this._customEntry.get_clutter_text().set_max_length(10);
this._customEntry.get_clutter_text().connect("key-press-event", this._createCustomTimer.bind(this));
bottom.box.add(this._customEntry);
bottom.actor.set_style("padding: 0px 18px;")
/*******************/
this.menu.addMenuItem(head);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this.teaItemCont);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(bottom);
this._updateTeaList();
2017-11-03 19:52:04 +00:00
}
_continueRunningTimer() {
let running = this._settings.get_string(this.config_keys.running_timer).split("#");
if (running.length == 2) {
try {
this._initCountdown(new Date(running[0]), parseInt(running[1]));
this.graphicalCounter = 0; // redraw with correct values
} catch (e) {
// remove unreadable timer
this._settings.set_string(this.config_keys.running_timer, '');
}
}
}
2021-09-12 17:31:57 +00:00
_updateTeaList(config, output) {
// make sure the menu is empty
this.teaItemCont.removeAll();
2013-11-03 16:24:02 +00:00
2021-09-12 17:31:57 +00:00
// fill with new teas
let list = this._settings.get_value(this.config_keys.steep_times).unpack();
let menuItem = new PopupTeaMenuItem(_("Stop Timer"), 0);
this.stopMenu = menuItem.tealabel
menuItem.connect('activate', function () {
2021-09-12 17:31:57 +00:00
this._stopCountdown();
}.bind(this));
2017-11-03 19:52:04 +00:00
this.teaItemCont.addMenuItem(menuItem);
2021-09-12 17:31:57 +00:00
for (let teaname in list) {
let time = list[teaname].get_uint32();
let menuItem = new PopupTeaMenuItem(_(teaname), time);
menuItem.connect('activate', function () {
this._initCountdown(new Date(), time);
2021-09-12 17:31:57 +00:00
}.bind(this));
this.teaItemCont.addMenuItem(menuItem);
}
}
2021-09-12 17:31:57 +00:00
_updateCountdownType(config, output) {
let bWantGraphicalCountdown = this._settings.get_boolean(this.config_keys.graphical_countdown);
if (bWantGraphicalCountdown != this._bGraphicalCountdown) {
if (this._idleTimeout != null) {
// we have a running countdown, replace the display
this.remove_actor(this._bGraphicalCountdown ?
this._graphicalTimer : this._textualTimer);
this._bGraphicalCountdown = bWantGraphicalCountdown;
this.add_actor(this._bGraphicalCountdown ?
this._graphicalTimer : this._textualTimer);
this._updateTimerDisplay(this._getRemainingSec());
} // if timeout active
} // value changed
}
_createCustomTimer(text, event) {
if (event.get_key_symbol() == Clutter.KEY_Enter ||
event.get_key_symbol() == Clutter.KEY_Return ||
event.get_key_symbol() == Clutter.KEY_KP_Enter) {
let customTime = text.get_text();
let seconds = 0;
let match = customTime.match(/^(?:(\d+)(?::(\d{0,2}))?|:(\d+))$/)
if (match) {
let factor = 1;
if (match[3] === undefined) { // minutes and seconds?
for (var i = match.length - 2; i > 0; i--) {
let s = match[i] === undefined ? "" : match[i].replace(/^0/, ''); // fix for elder GNOME <= 3.10 which don't like leading zeros
if (s.match(/^\d+$/)) { // only if something left
seconds += factor * parseInt(s);
}
factor *= 60;
}
2021-09-12 17:31:57 +00:00
} else { // only seconds?
let s = match[3].replace(/^0/, '');
seconds = parseInt(s);
}
if (seconds > 0) {
this._initCountdown(new Date(), seconds);
2021-09-12 17:31:57 +00:00
this.menu.close();
}
2017-11-03 19:52:04 +00:00
}
2021-09-12 17:31:57 +00:00
this._customEntry.set_text("");
}
}
2021-09-12 17:31:57 +00:00
_showNotification(subject, text) {
let source = new MessageTray.Source(_("TeaTime applet"), 'utilities-teatime');
Main.messageTray.add(source);
let notification = new MessageTray.Notification(source, subject, text);
notification.setTransient(true);
if (typeof source.showNotification === 'function') {
source.showNotification(notification);
} else {
source.notify(notification);
}
}
_initCountdown(startTime, time) {
this._startTime = startTime;
2021-09-12 17:31:57 +00:00
this._stopTime = new Date();
this._cntdownStart = time;
this._bGraphicalCountdown = this._settings.get_boolean(this.config_keys.graphical_countdown);
this.graphicalInterval = this._bGraphicalCountdown ?
2021-09-12 17:31:57 +00:00
Math.max(1.0, time / 90) // set time step to fit animation
:
1.0; // show every second for the textual countdown
this.graphicalCounter = 0;
2021-09-12 17:31:57 +00:00
this._stopTime.setTime(this._startTime.getTime() + time * 1000); // in msec
2021-09-12 17:31:57 +00:00
this.remove_actor(this._logo); // show timer instead of default icon
2021-09-12 17:31:57 +00:00
this._updateTimerDisplay(time);
2021-09-12 17:31:57 +00:00
this.add_actor(this._bGraphicalCountdown ?
this._graphicalTimer : this._textualTimer);
if (this._idleTimeout != null) Mainloop.source_remove(this._idleTimeout);
this._idleTimeout = Mainloop.timeout_add_seconds(1, this._doCountdown.bind(this));
if (this._settings.get_boolean(this.config_keys.remember_running_timer)) {
// remember timer
this._settings.set_string(this.config_keys.running_timer, this._startTime.toJSON() + '#' + time);
}
2021-09-12 17:31:57 +00:00
}
2021-09-12 17:31:57 +00:00
_stopCountdown() {
if (this._idleTimeout != null) Mainloop.source_remove(this._idleTimeout);
this.remove_actor(this._bGraphicalCountdown ?
this._graphicalTimer : this._textualTimer);
this.add_actor(this._logo);
this._idleTimeout = null;
// always remove remembered timer
this._settings.set_string(this.config_keys.running_timer, '');
this.stopMenu.text = _("Stop Timer");
2021-09-12 17:31:57 +00:00
}
2021-09-12 17:31:57 +00:00
_getRemainingSec() {
let a = new Date();
return (this._stopTime.getTime() - a.getTime()) * 1e-3;
}
2021-09-12 17:31:57 +00:00
_updateTimerDisplay(remainingTime) {
if (this._bGraphicalCountdown) {
if (this.graphicalCounter-- <= 0) {
this.graphicalCounter = this.graphicalInterval;
this._graphicalTimer.setStatus((this._cntdownStart - remainingTime) / this._cntdownStart);
}
this.stopMenu.text = _("Stop Timer") + ': ' + _('%s to go').replace('%s', Utils.formatTime(remainingTime));
2021-09-12 17:31:57 +00:00
} else {
this._textualTimer.text = Utils.formatTime(remainingTime);
this.stopMenu.text = _("Stop Timer");
2021-09-12 17:31:57 +00:00
}
}
2021-09-12 17:31:57 +00:00
_doCountdown() {
let remainingTime = this._getRemainingSec();
if (remainingTime <= 0) {
// count down finished, switch display again
this._stopCountdown();
this._playSound();
this._showNotification(_("Your tea is ready!"),
_("Drink it, while it is hot!"));
return false;
} else {
this._updateTimerDisplay(remainingTime);
return true; // continue timer
}
}
2021-09-12 17:31:57 +00:00
_playSound() {
let bPlayAlarmSound = this._settings.get_boolean(this.config_keys.use_alarm_sound);
if (bPlayAlarmSound) {
Utils.playSound(this._settings.get_string(this.config_keys.alarm_sound));
}
}
2021-09-12 17:31:57 +00:00
_showPreferences() {
const currExt = ExtensionUtils.getCurrentExtension();
imports.misc.util.spawn(["gnome-shell-extension-prefs", currExt.metadata['uuid']]);
return 0;
}
2021-09-12 17:31:57 +00:00
_onStyleChanged(actor) {
let themeNode = actor.get_theme_node();
let color = themeNode.get_foreground_color()
let [bHasPadding, padding] = themeNode.lookup_length("-natural-hpadding", false);
this._primaryColor = color;
this._secondaryColor = new Clutter.Color({
red: color.red,
green: color.green,
blue: color.blue,
alpha: color.alpha * 0.3
});
this._logo.setPadding(bHasPadding * padding);
this._graphicalTimer.setPadding(bHasPadding * padding);
this._textualTimer.margin_right = bHasPadding * padding;
this._textualTimer.margin_left = bHasPadding * padding;
this._logo.setColor(this._primaryColor, this._secondaryColor);
this._graphicalTimer.setColor(this._primaryColor, this._secondaryColor);
// forward (possible) scaling style change to child
let scaling = Utils.getGlobalDisplayScaleFactor();
this._logo.setScaling(scaling);
this._graphicalTimer.setScaling(scaling);
}
2021-09-12 17:31:57 +00:00
});
function init(metadata) {}
let _TeaTime;
function enable() {
2017-11-03 19:52:04 +00:00
_TeaTime = new TeaTime();
Main.panel.addToStatusArea('teatime', _TeaTime);
}
function disable() {
2017-11-03 19:52:04 +00:00
if (_TeaTime._idleTimeout != null) Mainloop.source_remove(_TeaTime._idleTimeout);
_TeaTime.destroy();
}