const St = imports.gi.St;
const Gio = imports.gi.Gio
const Gtk = imports.gi.Gtk;
const GLib = imports.gi.GLib;
const Pango = imports.gi.Pango;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Clutter = imports.gi.Clutter;
const Main = imports.ui.main;
const CheckBox = imports.ui.checkBox;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const ByteArray = imports.byteArray;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const ME = imports.misc.extensionUtils.getCurrentExtension();
const Gettext = imports.gettext.domain(ME.metadata['gettext-domain']);
const _ = Gettext.gettext;
const ngettext = Gettext.ngettext;
const SOUND_PLAYER = ME.imports.lib.sound_player;
const FULLSCREEN = ME.imports.lib.fullscreen;
const SIG_MANAGER = ME.imports.lib.signal_manager;
const KEY_MANAGER = ME.imports.lib.keybinding_manager;
const PANEL_ITEM = ME.imports.lib.panel_item;
const MULTIL_ENTRY = ME.imports.lib.multiline_entry;
const NUM_PICKER = ME.imports.lib.num_picker;
const DAY_CHOOSER = ME.imports.lib.day_chooser;
const MISC_UTILS = ME.imports.lib.misc_utils;
const TEXT_LINKS_MNGR = ME.imports.lib.text_links_manager;
const REG = ME.imports.lib.regex;
const CACHE_FILE = '~/.cache/timepp_gnome_shell_extension/timepp_alarms.json';
const NotifStyle = {
STANDARD : 0,
FULLSCREEN : 1,
NONE : 2,
};
// =====================================================================
// @@@ Main
//
// @ext : obj (main extension object)
// @settings : obj (extension settings)
// =====================================================================
var SectionMain = class SectionMain extends ME.imports.sections.section_base.SectionBase{
constructor (section_name, ext, settings) {
super(section_name, ext, settings);
this.actor = new St.BoxLayout({ vertical: true, style_class: 'section alarm-section' });
this.panel_item.icon.gicon = MISC_UTILS.getIcon('timepp-alarms-symbolic');
this.panel_item.actor.add_style_class_name('alarm-panel-item');
this.panel_item.set_mode('icon');
this.separate_menu = this.settings.get_boolean('alarms-separate-menu');
this.cache_file = null;
this.cache = null;
this.css = this.ext.custom_css;
this.linkm = new TEXT_LINKS_MNGR.TextLinksManager();
this.sigm = new SIG_MANAGER.SignalManager();
this.keym = new KEY_MANAGER.KeybindingManager(this.settings);
this.sound_player = new SOUND_PLAYER.SoundPlayer();
this.fullscreen = new AlarmFullscreen(this.ext, this, this.settings.get_int('alarms-fullscreen-monitor-pos'));
this.wallclock = new GnomeDesktop.WallClock();
this.wallclock_str = ''; // time_str
// AlarmItem objects
this.alarm_items = new Set();
// @key: alarm obj
// @val: time_str
this.snoozed_alarms = new Map();
try {
this.cache_file = MISC_UTILS.file_new_for_path(CACHE_FILE);
let cache_format_version =
ME.metadata['cache-file-format-version'].alarms;
if (this.cache_file.query_exists(null)) {
let [, contents] = this.cache_file.load_contents(null);
this.cache = JSON.parse(ByteArray.toString(contents));
}
if (!this.cache || !this.cache.format_version ||
this.cache.format_version !== cache_format_version) {
this.cache = {
format_version: cache_format_version,
// Array of alarm objects where each object is of the form:
// {
// time_str : string (a time_str)
// msg : string
// days : array (of ints; Sunday is 0)
// toogle : bool
// snooze_dur : int (minutes)
// repeat_sound : bool
// }
alarms: [],
};
}
} catch (e) {
logError(e);
return;
}
//
// keybindings
//
this.keym.add('alarms-keybinding-open', () => {
this.ext.open_menu(this.section_name);
});
//
// add new alarm item
//
{
this.header = new St.BoxLayout({ x_expand: true, style_class: 'timepp-menu-item header' });
this.actor.add_actor(this.header);
this.add_alarm_button = new St.Button({ x_expand: true, can_focus: true, x_align: St.Align.START, style_class: 'add-alarm' });
this.header.add_actor(this.add_alarm_button);
let box = new St.BoxLayout({ x_expand: true });
this.add_alarm_button.add_actor(box);
let icon = new St.Icon({ gicon: MISC_UTILS.getIcon('timepp-alarms-symbolic')});
box.add_child(icon);
let label = new St.Label({ x_expand: true, text: _('Add New Alarm...'), y_align: Clutter.ActorAlign.CENTER });
box.add_actor(label);
}
//
// alarm items box
//
this.alarms_scroll = new St.ScrollView({ visible: false, style_class: 'timepp-menu-item alarms-container vfade', y_align: St.Align.START});
this.actor.add_actor(this.alarms_scroll);
this.alarms_scroll.vscrollbar_policy = Gtk.PolicyType.NEVER;
this.alarms_scroll.hscrollbar_policy = Gtk.PolicyType.NEVER;
this.alarms_scroll_content = new St.BoxLayout({ vertical: true, style_class: 'alarms-content-box' });
this.alarms_scroll.add_actor(this.alarms_scroll_content);
//
// listen
//
this.sigm.connect(this.settings, 'changed::alarms-separate-menu', () => {
this.separate_menu = this.settings.get_boolean('alarms-separate-menu');
this.ext.update_panel_items();
});
this.sigm.connect(this.alarms_scroll_content, 'allocation-changed', () => {
this.alarms_scroll.vscrollbar_policy = Gtk.PolicyType.NEVER;
Mainloop.idle_add(() => {
if (ext.needs_scrollbar())
this.alarms_scroll.vscrollbar_policy = Gtk.PolicyType.ALWAYS;
});
});
this.sigm.connect(this.wallclock, 'notify::clock', () => this._tic());
this.sigm.connect(this.fullscreen, 'monitor-changed', () => this.settings.set_int('alarms-fullscreen-monitor-pos', this.fullscreen.monitor));
this.sigm.connect_release(this.add_alarm_button, Clutter.BUTTON_PRIMARY, true, () => this.alarm_editor());
//
// finally
//
this.cache.alarms.forEach((a) => this._add_alarm(a));
this._update_panel_item_UI();
}
disable_section () {
for (let it of this.alarm_items) it.close();
this.sigm.clear();
this.keym.clear();
this.snoozed_alarms.clear();
if (this.fullscreen) {
this.fullscreen.destroy();
this.fullscreen = null;
}
super.disable_section();
}
_store_cache () {
if (! this.cache_file.query_exists(null))
this.cache_file.create(Gio.FileCreateFlags.NONE, null);
this.cache_file.replace_contents(JSON.stringify(this.cache, null, 2),
null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
}
_tic () {
let d = GLib.DateTime.new_now(this.wallclock.timezone);
let time = d.format('%H:%M');
if (time === this.wallclock_str || d.get_seconds() > 1) return;
this.wallclock_str = time;
let today = new Date().getDay();
for (let it of this.alarm_items) {
let a = it.alarm;
it.update_time_label();
if (a.toggle && (a.time_str === time) && (a.days.indexOf(today) !== -1)) {
this._send_notif(a);
}
}
for (let [a, time_str] of this.snoozed_alarms) {
if (a.toggle && time_str === time) {
this._send_notif(a);
this.snoozed_alarms.delete(a);
for (let it of this.alarm_items) {
if (a === it.alarm) it.update_time_label();
}
}
}
this._update_panel_item_UI(today);
}
// @alarm_item: obj
// If @alarm_item is not provided, then we are adding a new alarm.
alarm_editor (alarm_item) {
let alarm_obj = alarm_item ? alarm_item.alarm : null;
let editor = new AlarmEditor(this.ext, this, alarm_obj);
this.actor.insert_child_at_index(editor.actor, 0);
editor.button_cancel.grab_key_focus();
this.header.hide();
this.alarms_scroll.hide();
if (! alarm_item) {
editor.connect('add-alarm', (_, alarm) => {
this.cache.alarms.push(alarm);
this._store_cache();
this._add_alarm(alarm);
this.header.show();
this.alarms_scroll.show();
this.add_alarm_button.grab_key_focus();
editor.actor.destroy();
});
} else {
editor.connect('edited-alarm', (_, alarm) => {
this.snoozed_alarms.delete(alarm);
alarm_item.toggle.setToggleState(alarm.toggle);
alarm_item.update_time_label();
alarm_item.alarm_item_content.show();
if (alarm.msg) {
alarm_item.set_body_text(alarm.msg);
alarm_item.msg.visible = true;
} else {
alarm_item.msg.visible = false;
}
this._update_panel_item_UI();
this.header.show();
this.alarms_scroll.show();
this.add_alarm_button.grab_key_focus();
editor.actor.destroy();
this._store_cache();
});
editor.connect('delete-alarm', () => {
this.header.show();
this.alarms_scroll.show();
this.add_alarm_button.grab_key_focus();
editor.actor.destroy();
alarm_item.actor.destroy();
this._delete_alarm(alarm_item.alarm);
alarm_item.close();
this.alarm_items.delete(alarm_item);
});
}
editor.connect('cancel', () => {
this.header.show();
this.add_alarm_button.grab_key_focus();
editor.actor.destroy();
if (this.alarms_scroll_content.get_n_children() > 0)
this.alarms_scroll.show();
});
}
// NOTE: This func assumes that @alarm has already been added to the
// this.cache.alarms array.
_add_alarm (alarm) {
this._update_panel_item_UI();
let alarm_item = new AlarmItem(this.ext, this, alarm);
this.alarm_items.add(alarm_item);
this.alarms_scroll_content.add_actor(alarm_item.actor);
this.alarms_scroll.show();
alarm_item.connect('alarm-toggled', () => {
this.snoozed_alarms.delete(alarm);
this._update_panel_item_UI();
this._store_cache();
});
}
_delete_alarm (alarm) {
this.cache.alarms = this.cache.alarms.filter(a => a !== alarm);
this.snoozed_alarms.delete(alarm);
this._store_cache();
this._update_panel_item_UI();
if (this.alarms_scroll_content.get_n_children() === 0)
this.alarms_scroll.hide();
}
snooze_alarm (alarm) {
let t;
t = GLib.DateTime.new_now(this.wallclock.timezone)
t = t.add_minutes(alarm.snooze_dur);
t = t.format('%H:%M');
this.snoozed_alarms.set(alarm, t);
for (let it of this.alarm_items) {
if (it.alarm === alarm) it.update_time_label();
}
}
_send_notif (alarm) {
let notif_style = this.settings.get_enum('alarms-notif-style');
if (notif_style === NotifStyle.NONE) {
// no visual notification
} else if (notif_style === NotifStyle.FULLSCREEN) {
this.fullscreen.fire_alarm(alarm);
} else {
let source = new MessageTray.Source();
Main.messageTray.add(source);
source.connect('destroy', () => this.sound_player.stop());
// TRANSLATORS: %s is a time string in the format HH:MM (e.g., 13:44)
let title = _('Alarm at %s').format(alarm.time_str);
let icon = new St.Icon({ gicon : MISC_UTILS.getIcon('timepp-alarms-symbolic') });
let params = {
bannerMarkup : true,
gicon : icon.gicon,
};
let notif = new MessageTray.Notification(
source,
title,
alarm.msg,
params
);
notif.setUrgency(MessageTray.Urgency.CRITICAL);
notif.addAction(`${_('Snooze')} (${alarm.snooze_dur} min)`, () => {
this.snooze_alarm(alarm);
});
source.notify(notif);
}
if (this.settings.get_boolean('alarms-play-sound')) {
this.sound_player.set_sound_uri(this.settings.get_string('alarms-sound-file-path'));
this.sound_player.play(alarm.repeat_sound);
}
}
_update_panel_item_UI (today) {
today = today || new Date().getDay()
this.panel_item.actor.remove_style_class_name('on');
for (let a of this.cache.alarms) {
if (a.toggle && a.days.indexOf(today) !== -1) {
this.panel_item.actor.add_style_class_name('on');
break;
}
}
}
highlight_tokens (text) {
if (! text) return "";
text = GLib.markup_escape_text(text, -1);
text = MISC_UTILS.split_on_whitespace(text);
let inside_backticks = false;
for (let i = 0; i < text.length; i++) {
let token = text[i];
if (token.startsWith('`') || token.endsWith('`')) inside_backticks = !inside_backticks;
if (inside_backticks) continue;
if (REG.URL.test(token) || REG.FILE_PATH.test(token)) {
text[i] =
'`' + token + '`';
}
}
text = text.join('');
return MISC_UTILS.markdown_to_pango(text, this.ext.markdown_map);
}
}
Signals.addSignalMethods(SectionMain.prototype);
// =====================================================================
// @@@ Alarm Editor
//
// @ext : obj (main ext object)
// @delegate : obj (main section object)
// @alarm : obj (alarm object)
//
// @signals: 'add-alarm', 'edited-alarm', 'delete-alarm', 'cancel'
//
// If @alarm is given, it's time_str, days, msg, and snooze_dur props will be
// updated, and the alarm editor widget will be pre-populated with the alarms
// settings; otherwise, a complete new alarm object will be returned with the
// 'add-alarm' signal.
// =====================================================================
var AlarmEditor = class AlarmEditor{
constructor (ext, delegate, alarm) {
this.ext = ext;
this.delegate = delegate;
this.alarm = alarm;
//
// container
//
this.actor = new St.Bin({ x_fill: true, style_class: 'view-box' });
this.content_box = new St.BoxLayout({ x_expand: true, vertical: true, style_class: 'view-box-content'});
this.actor.add_actor(this.content_box);
//
// alarm time
//
{
let box = new St.BoxLayout({style_class: 'row numpicker-box'});
this.content_box.add_actor(box);
let label = new St.Label({ text: `${_('Alarm time')} ${_('(h:min)')} `, x_expand: true, y_align: Clutter.ActorAlign.CENTER });
box.add_child(label);
this.hh = new NUM_PICKER.NumPicker(0, 23);
box.add_child(this.hh.actor);
this.mm = new NUM_PICKER.NumPicker(0, 59);
box.add_child(this.mm.actor);
if (alarm) {
let [hr_str, min_str] = alarm.time_str.split(':');
this.hh.set_counter(parseInt(hr_str));
this.mm.set_counter(parseInt(min_str));
}
}
//
// choose day
//
this.day_chooser = new DAY_CHOOSER.DayChooser(alarm ? false : true);
this.content_box.add_actor(this.day_chooser.actor);
if (alarm) {
alarm.days.forEach((day) => {
let btn = this.day_chooser.buttons[day];
btn.checked = true;
btn.add_style_pseudo_class('active');
});
}
//
// snooze duration
//
{
let box = new St.BoxLayout({style_class: 'row'});
this.content_box.add_actor(box);
let label = new St.Label({ text: `${_('Snooze duration')} ${_('(min)')} `, x_expand: true, y_align: Clutter.ActorAlign.CENTER });
box.add_child(label);
this.snooze_duration_picker = new NUM_PICKER.NumPicker(1, null);
box.add_child(this.snooze_duration_picker.actor);
if (alarm)
this.snooze_duration_picker.set_counter(alarm.snooze_dur);
}
//
// repeat sound checkbox
//
this.checkbox_item = new St.BoxLayout({ reactive: true, x_expand: true, style_class: 'row' });
this.content_box.add_actor(this.checkbox_item);
this.checkbox_item.add_child(
new St.Label({ text: _('Repeat notification sound?'), x_expand: true, y_align: Clutter.ActorAlign.CENTER }));
this.sound_checkbox = new CheckBox.CheckBox();
this.checkbox_item.add_child(this.sound_checkbox.actor);
this.sound_checkbox.actor.checked = alarm && alarm.repeat_sound;
//
// entry
//
this.alarm_entry_container = new St.BoxLayout({ vertical: true, style_class: 'row entry-container' });
this.content_box.add_actor(this.alarm_entry_container);
this.entry = new MULTIL_ENTRY.MultiLineEntry(_('Alarm Message...'), true, false);
this.entry.scroll_box.vscrollbar_policy = Gtk.PolicyType.NEVER;
this.entry.scroll_box.hscrollbar_policy = Gtk.PolicyType.NEVER;
this.alarm_entry_container.add_actor(this.entry.actor);
if (alarm)
this.entry.set_text(alarm.msg);
//
// buttons
//
let btn_box = new St.BoxLayout({ style_class: 'row btn-box' });
this.content_box.add_actor(btn_box);
if (alarm) {
this.button_delete = new St.Button({ can_focus: true, label: _('Delete'), style_class: 'btn-delete button', x_expand: true });
btn_box.add(this.button_delete, {expand: true});
this.button_delete.connect('clicked', () => {
this.emit('delete-alarm');
});
};
this.button_cancel = new St.Button({ can_focus: true, label: _('Cancel'), style_class: 'btn-cancel button', x_expand: true });
this.button_ok = new St.Button({ can_focus: true, label: _('Ok'), style_class: 'btn-ok button', x_expand: true });
btn_box.add(this.button_cancel, {expand: true });
btn_box.add(this.button_ok, {expand: true});
//
// listen
//
this.button_ok.connect('clicked', () => {
if (alarm) {
alarm.time_str = this._get_time_str(),
alarm.msg = this.entry.entry.get_text(),
alarm.days = this.day_chooser.get_days(),
alarm.snooze_dur = this.snooze_duration_picker.counter;
alarm.repeat_sound = this.sound_checkbox.actor.checked;
this.emit('edited-alarm', alarm);
}
else {
this.emit('add-alarm', {
time_str : this._get_time_str(),
msg : this.entry.entry.get_text(),
days : this.day_chooser.get_days(),
toggle : true,
snooze_dur : this.snooze_duration_picker.counter,
repeat_sound : this.sound_checkbox.actor.checked,
});
}
});
this.button_cancel.connect('clicked', () => {
this.emit('cancel');
});
this.checkbox_item.connect('button-press-event', () => {
this.sound_checkbox.actor.checked = !this.sound_checkbox.actor.checked;
});
this.entry.entry.connect('allocation-changed', () => {
this.entry.scroll_box.vscrollbar_policy = Gtk.PolicyType.NEVER;
if (this.ext.needs_scrollbar())
this.entry.scroll_box.vscrollbar_policy = Gtk.PolicyType.ALWAYS;
});
}
_get_time_str () {
return this.hh.counter_label.get_text() + ':' +
this.mm.counter_label.get_text();
}
}
Signals.addSignalMethods(AlarmEditor.prototype);
// =====================================================================
// @@@ Alarm Item
//
// @ext : obj (main extension object)
// @delegate : obj (main section object)
// @alarm : obj (an alarm object)
//
// signals: 'alarm-toggled'
// =====================================================================
var AlarmItem = class AlarmItem {
constructor (ext, delegate, alarm) {
this.ext = ext;
this.delegate = delegate;
this.alarm = alarm;
this.msg_vert_padding = -1;
//
// container
//
this.actor = new St.BoxLayout({ reactive: true, vertical: true, style_class: 'alarm-item menu-favorites-box' });
this.alarm_item_content = new St.BoxLayout({vertical: true, style_class: 'alarm-item-content'});
this.actor.add_actor(this.alarm_item_content);
//
// header
//
this.header = new St.BoxLayout({ style_class: 'alarm-item-header' });
this.alarm_item_content.add_actor(this.header);
this.time = new St.Label({ y_align: St.Align.END, x_align: St.Align.START, style_class: 'alarm-item-time' });
this.header.add(this.time, {expand: true});
this.icon_box = new St.BoxLayout({y_align: Clutter.ActorAlign.CENTER, x_align: Clutter.ActorAlign.CENTER, style_class: 'icon-box'});
this.header.add_actor(this.icon_box);
this.edit_icon = new St.Icon({ visible: false, reactive: true, can_focus: true, track_hover: true, gicon : MISC_UTILS.getIcon('timepp-edit-symbolic'), style_class: 'settings-icon' });
this.icon_box.add(this.edit_icon);
this.toggle = new PopupMenu.Switch(alarm.toggle);
this.toggle_bin = new St.Button({can_focus: true, y_align: St.Align.START, x_align: St.Align.END });
this.toggle_bin.add_actor(this.toggle.actor);
this.icon_box.add(this.toggle_bin);
//
// body
//
this.msg = new St.Label({ y_align: St.Align.END, x_align: St.Align.START, style_class: 'alarm-item-message'});
this.alarm_item_content.add_actor(this.msg);
this.delegate.linkm.add_label_actor(this.msg, new Map([
[REG.URL , MISC_UTILS.open_web_uri],
[REG.FILE_PATH , MISC_UTILS.open_file_path],
]));
this.msg.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this.msg.clutter_text.set_single_line_mode(false);
this.msg.clutter_text.set_line_wrap(true);
this.msg.clutter_text.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR);
if (!alarm.msg) this.msg.hide();
else this.set_body_text(alarm.msg);
this.update_time_label();
//
// listen
//
this.css_sig_id =
this.ext.connect('custom-css-changed', () => this._on_custom_css_updated());
this.toggle_bin.connect('clicked', () => this._on_toggle());
this.delegate.sigm.connect_release(this.edit_icon, Clutter.BUTTON_PRIMARY, true, () => this._on_edit());
this.actor.connect('enter-event', () => this.edit_icon.show());
this.actor.connect('event', (actor, event) => this._on_event(actor, event));
}
// @markup: string
set_body_text (markup) {
this.msg.clutter_text.set_markup(this.delegate.highlight_tokens(markup));
}
update_time_label () {
let date = new Date();
let markup = `${this.alarm.time_str}`;
// update clock ETA (time until alarm goes off)
if (this.alarm.days.indexOf(date.getDay()) === -1) {
markup += ` ${_('inactive today')}`;
if (this.alarm.toggle) this.actor.remove_style_class_name('active');
} else if (this.alarm.toggle) {
let clock_then;
let clock_now;
let snoozed_string = '';
if (this.delegate.snoozed_alarms.has(this.alarm)) {
clock_then = this.delegate.snoozed_alarms.get(this.alarm);
snoozed_string = ` ${_('Snoozed')}`;
} else {
clock_then = this.alarm.time_str;
}
if (this.delegate.wallclock_str) {
clock_now = this.delegate.wallclock_str;
} else {
clock_now = GLib.DateTime.new_now(this.delegate.wallclock.timezone);
clock_now = clock_now.format('%H:%M');
}
if (clock_now >= clock_then) date.setDate(date.getDate() + 1);
clock_then = clock_then.split(':');
date.setHours(+(clock_then[0]));
date.setMinutes(+(clock_then[1]));
let delta = Math.floor((date.getTime() - Date.now()) / 1000);
let h = Math.floor(delta / 3600);
let min = Math.round(delta % 3600 / 60);
markup += ` (${h}h ${min}min)${snoozed_string}`;
if (this.alarm.toggle) this.actor.add_style_class_name('active');
}
this.time.clutter_text.set_markup(markup);
}
_on_toggle () {
this.toggle.toggle();
this.alarm.toggle = !this.alarm.toggle;
if (this.alarm.days.indexOf(new Date().getDay()) !== -1) {
if (this.alarm.toggle) this.actor.add_style_class_name('active');
else this.actor.remove_style_class_name('active');
}
this.update_time_label();
this.emit('alarm-toggled');
}
_on_edit () {
Main.panel.menuManager.ignoreRelease();
this.edit_icon.hide();
this.delegate.alarm_editor(this);
}
_on_custom_css_updated () {
for (let alarm_item of this.delegate.alarm_items) {
alarm_item.set_body_text(alarm_item.alarm.msg);
}
}
_on_event (actor, event) {
switch (event.type()) {
case Clutter.EventType.ENTER: {
this.edit_icon.show();
} break;
case Clutter.EventType.LEAVE: {
if (! this.header.contains(global.stage.get_key_focus()))
this.edit_icon.hide();
} break;
case Clutter.EventType.KEY_RELEASE: {
this.edit_icon.show();
MISC_UTILS.scroll_to_item(this.delegate.alarms_scroll, this.delegate.alarms_scroll_content, actor);
} break;
case Clutter.EventType.KEY_PRESS: {
Mainloop.idle_add(() => {
if (! this.header.contains(global.stage.get_key_focus()))
this.edit_icon.hide();
});
} break;
}
}
close () {
this.ext.disconnect(this.css_sig_id);
this.actor.destroy();
}
}
Signals.addSignalMethods(AlarmItem.prototype);
// =====================================================================
// @@@ Alarm fullscreen interface
//
// @ext : obj (main extension object)
// @delegate : obj (main section object)
// @monitor : int
//
// signals: 'monitor-changed'
// =====================================================================
var AlarmFullscreen = class AlarmFullscreen extends FULLSCREEN.Fullscreen {
constructor (ext, delegate, monitor) {
super(monitor);
this.actor.add_style_class_name('alarm');
this.ext = ext;
this.delegate = delegate;
this.delegate.linkm.add_label_actor(this.banner, new Map([
[REG.URL , MISC_UTILS.open_web_uri],
[REG.FILE_PATH , MISC_UTILS.open_file_path],
]));
this.alarms = [];
//
// multi alarm view
//
this.alarm_cards_container = new St.BoxLayout({ vertical: true, x_expand: true, x_align: Clutter.ActorAlign.CENTER });
this.middle_box.insert_child_at_index(this.alarm_cards_container, 0);
this.alarm_cards_scroll = new St.ScrollView({ y_expand: true, style_class: 'vfade' });
this.alarm_cards_container.add_actor(this.alarm_cards_scroll);
this.alarm_cards_scroll.hscrollbar_policy = Gtk.PolicyType.NEVER;
this.alarm_cards_scroll_bin = new St.BoxLayout({ y_expand: true, y_align: Clutter.ActorAlign.CENTER, vertical: true, style_class: 'alarm-cards-container'});
this.alarm_cards_scroll.add_actor(this.alarm_cards_scroll_bin);
//
// title
//
this.title = new St.Label({ x_expand: true, x_align: Clutter.ActorAlign.CENTER, style_class: 'main-title' });
this.middle_box.insert_child_at_index(this.title, 0);
//
// snooze button
//
this.button_box = new St.BoxLayout({ x_expand: true, y_expand: true, style_class: 'btn-box', x_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER, });
this.bottom_box.add_child(this.button_box)
this.button_snooze = new St.Button({ can_focus: true, style_class: 'button' });
this.button_box.add_child(this.button_snooze);
//
// listen
//
this.button_snooze.connect('clicked', () => {
if (this.alarms.length > 0)
this.delegate.snooze_alarm(this.alarms[0]);
this.close();
});
}
set_banner_text (markup) {
super.set_banner_text(this.delegate.highlight_tokens(markup));
}
close () {
this.delegate.sound_player.stop();
this.alarms = [];
this.alarm_cards_scroll_bin.destroy_all_children();
super.close();
}
fire_alarm (alarm) {
this.alarms.push(alarm);
// TRANSLATORS: %s is a time string in the format HH:MM (e.g., 13:44)
let title = _('Alarm at %s').format(alarm.time_str);
let msg = alarm.msg.trim();
this._add_alarm_card(title, msg)
if (this.alarms.length === 1) {
this.bottom_box.show();
this.button_snooze.label = `${_('Snooze')} (${alarm.snooze_dur} min)`;
this.alarm_cards_container.hide();
this.banner_container.show();
if (msg) {
this.title.text = title;
this.set_banner_text(msg);
}
else {
this.set_banner_text(title);
}
}
else {
this.bottom_box.hide();
this.alarm_cards_container.show();
this.banner_container.hide();
this.title.text =
ngettext('%d alarm went off!', '%d alarms went off!', this.alarms.length)
.format(this.alarms.length);
}
this.open();
}
_add_alarm_card (title, msg) {
let alarm_card = new St.BoxLayout({ vertical: true, style_class: 'alarm-card' });
this.alarm_cards_scroll_bin.add_child(alarm_card);
alarm_card.add_child(new St.Label({ text: title, style_class: 'title' }));
let body;
if (msg) {
body = new St.Label({ y_align: St.Align.END, x_align: St.Align.START, style_class: 'body'});
alarm_card.add_child(body);
body.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
body.clutter_text.single_line_mode = false;
body.clutter_text.line_wrap = true;
body.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
body.clutter_text.set_markup(this.delegate.highlight_tokens(msg));
this.delegate.linkm.add_label_actor(body, new Map([
[REG.URL , MISC_UTILS.open_web_uri],
[REG.FILE_PATH , MISC_UTILS.open_file_path],
]));
alarm_card.connect('allocation-changed', () => MISC_UTILS.resize_label(body));
}
}
}
Signals.addSignalMethods(AlarmFullscreen.prototype);