2017-11-21 20:33:14 +00:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: t -*- */
|
2016-01-15 19:38:31 +00:00
|
|
|
/* Olaf Leidinger <oleid@mescharet.de>
|
2013-05-21 06:02:28 +00:00
|
|
|
Thomas Liebetraut <thomas@tommie-lie.de>
|
|
|
|
*/
|
2013-05-20 19:09:39 +00:00
|
|
|
|
|
|
|
const Gio = imports.gi.Gio;
|
2020-02-17 15:08:12 +00:00
|
|
|
const GObject = imports.gi.GObject;
|
2013-05-20 19:09:39 +00:00
|
|
|
const Mainloop = imports.mainloop; // timer
|
|
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const St = imports.gi.St;
|
|
|
|
const Clutter = imports.gi.Clutter;
|
2013-11-01 22:58:00 +00:00
|
|
|
const Layout = imports.ui.layout;
|
2013-05-20 19:09:39 +00:00
|
|
|
|
2017-11-03 19:52:04 +00:00
|
|
|
const Main = imports.ui.main;
|
2013-05-20 19:09:39 +00:00
|
|
|
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;
|
2013-05-20 19:09:39 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2013-05-21 03:43:31 +00:00
|
|
|
|
2020-02-17 15:08:12 +00:00
|
|
|
let PopupTeaMenuItem = GObject.registerClass(
|
2019-04-09 10:52:38 +00:00
|
|
|
class PopupTeaMenuItem extends PopupMenu.PopupBaseMenuItem {
|
2020-02-17 15:08:12 +00:00
|
|
|
_init(sTeaname, nBrewtime, params) {
|
|
|
|
super._init(params);
|
2017-11-03 19:52:04 +00:00
|
|
|
|
|
|
|
this.tealabel = new St.Label({
|
|
|
|
text: sTeaname
|
|
|
|
});
|
2018-03-18 12:05:25 +00:00
|
|
|
if (nBrewtime != 0) {
|
|
|
|
this.timelabel = new St.Label({
|
|
|
|
text: Utils.formatTime(nBrewtime)
|
|
|
|
});
|
|
|
|
}
|
2017-11-03 19:52:04 +00:00
|
|
|
|
|
|
|
if (this.actor instanceof St.BoxLayout) {
|
|
|
|
// will be used for gnome-shell 3.10 and possibly above where this.actor is BoxLayout
|
|
|
|
this.actor.add(this.tealabel, {
|
|
|
|
expand: true
|
|
|
|
});
|
2018-03-18 12:05:25 +00:00
|
|
|
if (nBrewtime != 0) {
|
|
|
|
this.actor.add(this.timelabel);
|
|
|
|
}
|
2017-11-03 19:52:04 +00:00
|
|
|
} else {
|
|
|
|
this.addActor(this.tealabel, {
|
|
|
|
expand: true
|
|
|
|
});
|
2018-03-18 12:05:25 +00:00
|
|
|
if (nBrewtime != 0) {
|
|
|
|
this.addActor(this.timelabel, {
|
|
|
|
expand: false
|
|
|
|
});
|
|
|
|
}
|
2017-11-03 19:52:04 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
});
|
2013-11-03 16:24:02 +00:00
|
|
|
|
2020-02-17 15:08:12 +00:00
|
|
|
let TeaTime = GObject.registerClass(
|
|
|
|
class TeaTime extends PanelMenu.Button {
|
|
|
|
_init() {
|
|
|
|
super._init(1.0, "TeaTime");
|
2013-05-21 03:43:31 +00:00
|
|
|
|
2020-02-17 15:08:12 +00:00
|
|
|
this.config_keys = Utils.GetConfigKeys();
|
2014-10-14 22:57:39 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._settings = Utils.getSettings();
|
2013-05-20 19:09:39 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._logo = new Icon.TwoColorIcon(20, Icon.TeaPot);
|
2014-10-14 22:57:39 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
// set timer widget
|
|
|
|
this._textualTimer = new St.Label({
|
|
|
|
text: "",
|
|
|
|
x_align: Clutter.ActorAlign.END,
|
|
|
|
y_align: Clutter.ActorAlign.CENTER
|
|
|
|
});
|
|
|
|
this._graphicalTimer = new Icon.TwoColorIcon(20, Icon.Pie);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this.actor.add_actor(this._logo);
|
|
|
|
this.actor.add_style_class_name('panel-status-button');
|
|
|
|
this.actor.connect('style-changed', this._onStyleChanged.bind(this));
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._idleTimeout = null;
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._createMenu();
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 15:08:12 +00:00
|
|
|
_createMenu() {
|
2020-02-17 16:53:56 +00:00
|
|
|
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;")
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
/*******************/
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
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);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._updateTeaList();
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateTeaList(config, output) {
|
2020-02-17 16:53:56 +00:00
|
|
|
// make sure the menu is empty
|
|
|
|
this.teaItemCont.removeAll();
|
|
|
|
|
|
|
|
// fill with new teas
|
|
|
|
let list = this._settings.get_value(this.config_keys.steep_times).unpack();
|
|
|
|
let menuItem = new PopupTeaMenuItem("Stop Timer", 0);
|
|
|
|
menuItem.connect('activate', function () {
|
|
|
|
this._stopCountdown();
|
|
|
|
}.bind(this));
|
|
|
|
this.teaItemCont.addMenuItem(menuItem);
|
|
|
|
for (let teaname in list) {
|
|
|
|
let time = list[teaname].get_uint32();
|
|
|
|
|
|
|
|
let menuItem = new PopupTeaMenuItem(_(teaname), time);
|
2019-04-09 10:52:38 +00:00
|
|
|
menuItem.connect('activate', function () {
|
2020-02-17 16:53:56 +00:00
|
|
|
this._initCountdown(time);
|
2019-04-09 10:52:38 +00:00
|
|
|
}.bind(this));
|
2017-11-03 19:52:04 +00:00
|
|
|
this.teaItemCont.addMenuItem(menuItem);
|
2020-02-17 16:53:56 +00:00
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateCountdownType(config, output) {
|
2020-02-17 16:53:56 +00:00
|
|
|
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.actor.remove_actor(this._bGraphicalCountdown ?
|
|
|
|
this._graphicalTimer : this._textualTimer);
|
|
|
|
this._bGraphicalCountdown = bWantGraphicalCountdown;
|
|
|
|
this.actor.add_actor(this._bGraphicalCountdown ?
|
|
|
|
this._graphicalTimer : this._textualTimer);
|
|
|
|
|
|
|
|
this._updateTimerDisplay(this._getRemainingSec());
|
|
|
|
} // if timeout active
|
|
|
|
} // value changed
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_createCustomTimer(text, event) {
|
2020-02-17 16:53:56 +00:00
|
|
|
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);
|
2019-04-09 10:52:38 +00:00
|
|
|
}
|
2020-02-17 16:53:56 +00:00
|
|
|
factor *= 60;
|
2019-04-09 10:52:38 +00:00
|
|
|
}
|
2020-02-17 16:53:56 +00:00
|
|
|
} else { // only seconds?
|
|
|
|
let s = match[3].replace(/^0/, '');
|
|
|
|
seconds = parseInt(s);
|
|
|
|
}
|
|
|
|
if (seconds > 0) {
|
|
|
|
this._initCountdown(seconds);
|
|
|
|
this.menu.close();
|
2017-11-03 19:52:04 +00:00
|
|
|
}
|
2019-04-09 10:52:38 +00:00
|
|
|
}
|
2020-02-17 16:53:56 +00:00
|
|
|
this._customEntry.set_text("");
|
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_showNotification(subject, text) {
|
2020-02-17 16:53:56 +00:00
|
|
|
let source = (Utils.isGnome34()) ?
|
|
|
|
new MessageTray.Source(_("TeaTime applet")) :
|
|
|
|
new MessageTray.Source(_("TeaTime applet"), 'utilities-teatime');
|
|
|
|
|
|
|
|
if (Utils.isGnome34()) {
|
|
|
|
source.createNotificationIcon =
|
|
|
|
function () {
|
|
|
|
let iconBox = new St.Bin();
|
|
|
|
iconBox._size = this.ICON_SIZE;
|
|
|
|
iconBox.child = new St.Icon({
|
|
|
|
icon_name: 'utilities-teatime',
|
|
|
|
icon_type: St.IconType.FULLCOLOR,
|
|
|
|
icon_size: iconBox._size
|
|
|
|
});
|
|
|
|
return iconBox;
|
|
|
|
} // createNotificationIcon
|
|
|
|
}
|
2017-11-03 19:52:04 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
Main.messageTray.add(source);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
let notification = new MessageTray.Notification(source, subject, text);
|
|
|
|
notification.setTransient(true);
|
|
|
|
source.notify(notification);
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_initCountdown(time) {
|
2020-02-17 16:53:56 +00:00
|
|
|
this._startTime = new Date();
|
|
|
|
this._stopTime = new Date();
|
|
|
|
this._cntdownStart = time;
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._bGraphicalCountdown = this._settings.get_boolean(this.config_keys.graphical_countdown);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
let dt = this._bGraphicalCountdown ?
|
|
|
|
Math.max(1.0, time / 90) // set time step to fit animation
|
|
|
|
:
|
|
|
|
1.0; // show every second for the textual countdown
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._stopTime.setTime(this._startTime.getTime() + time * 1000); // in msec
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this.actor.remove_actor(this._logo); // show timer instead of default icon
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this._updateTimerDisplay(time);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
this.actor.add_actor(this._bGraphicalCountdown ?
|
|
|
|
this._graphicalTimer : this._textualTimer);
|
2019-04-09 10:52:38 +00:00
|
|
|
|
2020-02-17 16:53:56 +00:00
|
|
|
if (this._idleTimeout != null) Mainloop.source_remove(this._idleTimeout);
|
|
|
|
this._idleTimeout = Mainloop.timeout_add_seconds(dt, this._doCountdown.bind(this));
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_stopCountdown() {
|
2020-02-17 16:53:56 +00:00
|
|
|
if (this._idleTimeout != null) Mainloop.source_remove(this._idleTimeout);
|
|
|
|
this.actor.remove_actor(this._bGraphicalCountdown ?
|
|
|
|
this._graphicalTimer : this._textualTimer);
|
|
|
|
this.actor.add_actor(this._logo);
|
|
|
|
this._idleTimeout = null;
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_getRemainingSec() {
|
2020-02-17 16:53:56 +00:00
|
|
|
let a = new Date();
|
|
|
|
return (this._stopTime.getTime() - a.getTime()) * 1e-3;
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_updateTimerDisplay(remainingTime) {
|
2020-02-17 16:53:56 +00:00
|
|
|
if (this._bGraphicalCountdown) {
|
|
|
|
this._graphicalTimer.setStatus((this._cntdownStart - remainingTime) / this._cntdownStart);
|
|
|
|
} else {
|
|
|
|
this._textualTimer.text = Utils.formatTime(remainingTime);
|
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_doCountdown() {
|
2020-02-17 16:53:56 +00:00
|
|
|
let remainingTime = this._getRemainingSec();
|
|
|
|
|
|
|
|
if (remainingTime <= 0) {
|
|
|
|
// count down finished, switch display again
|
|
|
|
this._stopCountdown();
|
|
|
|
this._playSound();
|
2020-02-17 17:04:02 +00:00
|
|
|
this._showNotification(_("Your tea is ready!"),
|
|
|
|
_("Drink it, while it is hot!"));
|
2020-02-17 16:53:56 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
this._updateTimerDisplay(remainingTime);
|
|
|
|
return true; // continue timer
|
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_playSound() {
|
2020-02-17 16:53:56 +00:00
|
|
|
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));
|
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_showPreferences() {
|
2020-02-17 16:53:56 +00:00
|
|
|
const currExt = ExtensionUtils.getCurrentExtension();
|
|
|
|
imports.misc.util.spawn(["gnome-shell-extension-prefs", currExt.metadata['uuid']]);
|
|
|
|
return 0;
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_onStyleChanged(actor) {
|
2020-02-17 16:53:56 +00:00
|
|
|
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);
|
2019-04-09 10:52:38 +00:00
|
|
|
}
|
2020-02-17 15:08:12 +00:00
|
|
|
});
|
2013-05-20 19:09:39 +00:00
|
|
|
|
|
|
|
function init(metadata) {
|
2017-11-03 19:52:04 +00:00
|
|
|
let theme = imports.gi.Gtk.IconTheme.get_default();
|
|
|
|
theme.append_search_path(metadata.path);
|
2013-05-20 19:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let _TeaTime;
|
|
|
|
|
|
|
|
function enable() {
|
2017-11-03 19:52:04 +00:00
|
|
|
_TeaTime = new TeaTime();
|
|
|
|
Main.panel.addToStatusArea('teatime', _TeaTime);
|
2013-05-20 19:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function disable() {
|
2017-11-03 19:52:04 +00:00
|
|
|
if (_TeaTime._idleTimeout != null) Mainloop.source_remove(_TeaTime._idleTimeout);
|
|
|
|
_TeaTime.destroy();
|
2020-02-17 15:08:12 +00:00
|
|
|
}
|