ping-monitor/extension.js

1036 lines
32 KiB
JavaScript

import Clutter from 'gi://Clutter';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gio from 'gi://Gio';
import St from 'gi://St';
import Shell from 'gi://Shell';
import GTop from 'gi://GTop';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
let debugOutput = false;
let pingDepsGtop = true;
function print_info(str) {
log('[Ping monitor INFO] ' + str);
}
function print_debug(str) {
if (debugOutput) {
log('[Ping monitor DEBUG] ' + str);
}
}
function color_from_string(color) {
let clutterColor = new Clutter.Color();
clutterColor = Clutter.Color.from_string(color)[1];
return clutterColor;
}
const PingStyleManager = class PingMonitor_PingStyleManager {
constructor() {
print_debug('pingStyleManager constructor()');
this._extension = '';
this._iconsize = 1;
this._text_scaling = 1;
let interfaceSettings = new Gio.Settings({
schema: 'org.gnome.desktop.interface'
});
this._text_scaling = interfaceSettings.get_double('text-scaling-factor');
if (!this._text_scaling) {
this._text_scaling = 1;
}
}
get(style) {
return style + this._extension;
}
text_scaling() {
return this._text_scaling;
}
};
const PingDialog = GObject.registerClass(
class PingDialog extends ModalDialog.ModalDialog {
_init() {
super._init({styleClass: 'prompt-dialog'});
print_debug('PingDialog construct()');
let mainContentBox = new St.BoxLayout({
style_class: 'prompt-dialog-main-layout',
vertical: false
});
this.contentLayout.add_child(mainContentBox);
let messageBox = new St.BoxLayout({
style_class: 'prompt-dialog-message-layout',
vertical: true
});
mainContentBox.add_child(messageBox);
this._subjectLabel = new St.Label({
style_class: 'prompt-dialog-headline',
text: _('Ping Monitor Extension')
});
messageBox.add_child(this._subjectLabel);
const MESSAGE = _('Dependencies Missing\n\
Please install: \n\
libgtop and gir bindings\n\
\t on Ubuntu: gir1.2-gtop-2.0\n\
\t on Fedora: libgtop2-devel\n\
\t on Arch: libgtop\n\
\t on openSUSE: typelib-1_0-GTop-2_0\n');
this._descriptionLabel = new St.Label({
style_class: 'prompt-dialog-description',
text: MESSAGE
});
messageBox.add_child(this._descriptionLabel);
this.setButtons([{
label: _('Cancel'),
action: () => {
this.close();
},
key: Clutter.KEY_Escape
}]);
}
});
const StatusSquare = class PingMonitor_StatusSquare {
constructor(height, parent) {
print_debug('StatusSquare constructor()');
this._width = 12;
this._height = 12;
this._color = '#ff0000';
this._activityState = 0;
this._isPingUpdate = false;
this.actor = new St.Widget({
style_class: 'ping-chart',
reactive: false,
width: this._width,
height: this._height,
y_align: Clutter.ActorAlign.CENTER,
y_expand: false
});
this.parentC = parent;
this.data = [];
this._updateStyle();
}
update(color, isPingUpdate) {
print_debug('StatusSquare update()');
this._color = color;
this._isPingUpdate = isPingUpdate;
if (!this.actor.visible) {
return;
}
if (this._isPingUpdate) {
this._activityState = (this._activityState + 1) % 4;
}
this._updateStyle();
}
_updateStyle() {
print_debug('StatusSquare _updateStyle()');
if (!this.actor.visible) {
return;
}
this.actor.set_style(`
background-color: ${this._color};
border-radius: 2px;
width: ${this._width}px;
height: ${this._height}px;
min-width: ${this._width}px;
min-height: ${this._height}px;
`);
}
resize(schema, key) {
print_debug('StatusSquare resize()');
}
};
const PingTipItem = GObject.registerClass(
class PingTipItem extends PopupMenu.PopupBaseMenuItem {
_init() {
super._init();
this.remove_style_class_name('popup-menu-item');
this.add_style_class_name('ping-tooltip-item');
}
});
const TipMenu = class PingMonitor_TipMenu extends PopupMenu.PopupMenuBase {
constructor(sourceActor) {
print_debug('TipMenu constructor()');
super(sourceActor, 'ping-tooltip-box');
this.actor = new Clutter.Actor();
this.actor.add_child(this.box);
}
_shift() {
print_debug('TipMenu _shift()');
let node = this.sourceActor.get_theme_node();
let contentbox = node.get_content_box(this.sourceActor.get_allocation_box());
let allocation = Shell.util_get_transformed_allocation(this.sourceActor);
let monitor = Main.layoutManager.findMonitorForActor(this.sourceActor);
let [x, y] = [allocation.x1 + contentbox.x1,
allocation.y1 + contentbox.y1];
let [cx, cy] = [allocation.x1 + (contentbox.x1 + contentbox.x2) / 2,
allocation.y1 + (contentbox.y1 + contentbox.y2) / 2];
let [xm, ym] = [allocation.x1 + contentbox.x2,
allocation.y1 + contentbox.y2];
let [width, height] = this.actor.get_size();
let tipx = cx - width / 2;
tipx = Math.max(tipx, monitor.x);
tipx = Math.min(tipx, monitor.x + monitor.width - width);
let tipy = Math.floor(ym);
if (allocation.y1 / monitor.height > 0.3) {
tipy = allocation.y1 - height;
}
this.actor.set_position(tipx, tipy);
}
open(animate) {
print_debug('TipMenu open()');
if (this.isOpen) {
return;
}
this.isOpen = true;
this.actor.show();
this._shift();
this.actor.get_parent()?.set_child_above_sibling(this.actor, null);
this.emit('open-state-changed', true);
}
close(animate) {
print_debug('TipMenu close()');
this.isOpen = false;
this.actor.hide();
this.emit('open-state-changed', false);
}
};
const TipBox = class PingMonitor_TipBox {
constructor() {
print_debug('TipBox constructor()');
this.show_tooltip = true;
this.actor = new St.BoxLayout({
reactive: true,
y_align: Clutter.ActorAlign.CENTER
});
this.actor._delegate = this;
this.set_tip(new TipMenu(this.actor));
this.in_to = this.out_to = 0;
this.actor.connect('enter-event', this.on_enter.bind(this));
this.actor.connect('leave-event', this.on_leave.bind(this));
}
set_tip(tipmenu) {
print_debug('TipBox set_tip()');
if (this.tipmenu) {
this.tipmenu.destroy();
}
this.tipmenu = tipmenu;
if (this.tipmenu) {
Main.uiGroup.add_child(this.tipmenu.actor);
this.hide_tip();
}
}
show_tip() {
print_debug('TipBox show_tip()');
if (!this.tipmenu) {
return;
}
this.tipmenu.open();
if (this.in_to) {
GLib.Source.remove(this.in_to);
this.in_to = 0;
}
}
hide_tip() {
print_debug('TipBox hide_tip()');
if (!this.tipmenu) {
return;
}
this.tipmenu.close();
if (this.out_to) {
GLib.Source.remove(this.out_to);
this.out_to = 0;
}
if (this.in_to) {
GLib.Source.remove(this.in_to);
this.in_to = 0;
}
}
on_enter() {
print_debug('TipBox on_enter()');
let show_tooltip = this.show_tooltip;
if (!show_tooltip) {
return;
}
if (this.out_to) {
GLib.Source.remove(this.out_to);
this.out_to = 0;
}
if (!this.in_to) {
this.in_to = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
this.show_tip();
this.in_to = 0;
return GLib.SOURCE_REMOVE;
});
}
}
on_leave() {
print_debug('TipBox on_leave()');
if (this.in_to) {
GLib.Source.remove(this.in_to);
this.in_to = 0;
}
if (!this.out_to) {
this.out_to = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
this.hide_tip();
this.out_to = 0;
return GLib.SOURCE_REMOVE;
});
}
}
destroy() {
print_debug('TipBox destroy()');
if (this.in_to) {
GLib.Source.remove(this.in_to);
this.in_to = 0;
}
if (this.out_to) {
GLib.Source.remove(this.out_to);
this.out_to = 0;
}
this.actor.destroy();
}
};
const ElementBase = class PingMonitor_ElementBase extends TipBox {
constructor(properties, settings, extensionPath) {
super();
print_debug('ElementBase constructor()');
this._settings = settings;
this._extensionPath = extensionPath;
this.elt = '';
this.tag = '';
this.name = '';
this.show_name = false;
this.color_name = [];
this.text_items = [];
this.menu_items = [];
this.menu_visible = true;
this.color = '#ff0000';
this.refresh_interval = 5000;
this.visible = true;
this.timeout = undefined;
this._pingStdout = null;
this._pingDataStdout = null;
this._pingStderr = null;
this._pingDataStderr = null;
this._prepareToDestroy = false;
Object.assign(this, properties);
this.vals = [];
this.tip_labels = [];
this.tip_vals = [];
this.tip_unit_labels = [];
this.chart = new StatusSquare(Math.round(20 * 4 / 5), this);
this._settings.connect('changed::background', () => {
this.chart._updateStyle();
});
this.actor.visible = this.visible;
this.interval = this.refresh_interval;
this.add_timeout();
this.label = new St.Label({
text: this.name,
style_class: 'ping-status-label',
y_align: Clutter.ActorAlign.CENTER,
x_expand: false,
y_expand: false,
natural_height_set: false
});
this.label.visible = this.show_name;
this.menu_visible = true;
this.actor.add_child(this.label);
this.text_box = new St.BoxLayout();
this.text_items = this.create_text_items();
this.actor.add_child(this.chart.actor);
this.text_box.visible = true;
this.chart.actor.visible = this.visible;
this.menu_items = this.create_menu_items();
}
add_timeout() {
this.remove_timeout();
print_debug('Add timeout: ' + this.tag);
if (!this._prepareToDestroy) {
this.timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this.interval, () => {
return this.update();
});
}
}
remove_timeout() {
print_debug('Remove (try) timeout: ' + this.tag);
if (this.timeout !== undefined) {
print_debug('Remove timeout: ' + this.tag);
GLib.Source.remove(this.timeout);
this.timeout = undefined;
}
}
tip_format() {
print_debug('ElementBase tip_format()');
for (let i = 0; i < this.color_name.length; i++) {
let tipline = new PingTipItem();
this.tipmenu.addMenuItem(tipline);
this.tip_labels[i] = new St.Label({text: ''});
tipline.add_child(this.tip_labels[i]);
this.tip_vals[i] = 0;
}
}
update() {
print_debug('ElementBase update()');
this.remove_timeout();
if (!this.menu_visible && !this.actor.visible) {
return false;
}
this.refresh();
return GLib.SOURCE_CONTINUE;
}
updateDrawing() {
this._apply();
this.chart.update(this.color, true);
for (let i = 0; i < this.tip_vals.length; i++) {
if (this.tip_labels[i]) {
this.tip_labels[i].text = this.tip_vals[i].toString();
}
}
}
destroy() {
print_debug('ElementBase destroy()');
this.remove_timeout();
TipBox.prototype.destroy.call(this);
}
stop() {
this._prepareToDestroy = true;
this.remove_timeout();
}
isRunning() {
return (
this._pingStdout != null ||
this._pingDataStdout != null ||
this._pingStderr != null ||
this._pingDataStderr != null
);
}
};
const Ping = class PingMonitor_Ping extends ElementBase {
constructor(id, tag, name, address, ping_count, ping_interval,
ping_deadline, refresh_interval, active, visible,
show_name, show_address, show_tooltip, warning_threshold,
settings, extensionPath) {
if (show_address) {
name = name + '\n' + address;
}
super({
elt: 'ping',
tag: tag,
name: name,
show_name: show_name,
visible: visible,
refresh_interval: refresh_interval,
color_name: ['used'],
}, settings, extensionPath);
print_debug('Ping constructor()');
this.ping_message = '';
this.id = id;
this.address = address;
this.ping_count = ping_count;
this.ping_interval = ping_interval;
this.ping_deadline = ping_deadline;
this.active = active;
this.show_address = show_address;
this.show_tooltip = show_tooltip;
this.warning_threshold = warning_threshold;
this.tip_format();
this.update();
}
_pingReadStdout() {
this._pingDataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => {
if (stream.fill_finish(result) == 0) {
try {
let buffer = stream.peek_buffer();
if (buffer instanceof Uint8Array) {
this._pingOutput = new TextDecoder().decode(buffer);
} else {
this._pingOutput = buffer.toString();
}
if (this._pingOutput) {
print_debug('Ping info: ' + this._pingOutput);
let firstLine = this._pingOutput.match(/[\w .:()]+\n/m);
print_debug('First line: ' + firstLine[0]);
let lastLines = this._pingOutput.match(/---[\w\W]+/m);
lastLines[0] = lastLines[0].replace(/^\s+|\s+$/g, '');
print_debug('Last lines: ' + lastLines[0]);
this.ping_message = firstLine[0] + lastLines[0];
print_debug('Ping info: ' + this.ping_message);
let loss = this._pingOutput.match(/received, (\d*)/m);
let times = this._pingOutput.match(/mdev = (\d*.\d*)\/(\d*.\d*)\/(\d*.\d*)\/(\d*.\d*)/m);
if (times != null && times.length == 5 &&
loss != null && loss.length == 2) {
print_debug('loss: ' + loss[1]);
print_debug('min: ' + times[1]);
print_debug('avg: ' + times[2]);
print_debug('max: ' + times[3]);
print_debug('mdev: ' + times[4]);
if (loss[1] != 0 && loss[1] != 100) {
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-loss-color');
}
} else if (loss[1] == 100) {
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-bad-color');
}
} else if (times[3] > this.warning_threshold) {
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-warning-color');
}
} else {
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-good-color');
}
}
} else {
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-bad-color');
}
}
if (!this._prepareToDestroy) {
this.updateDrawing();
}
}
} catch (e) {
print_info(e.toString());
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-bad-color');
this.updateDrawing();
}
}
this._pingStdout.close(null);
this._pingStdout = null;
this._pingDataStdout.close(null);
this._pingDataStdout = null;
this.add_timeout();
return;
}
stream.set_buffer_size(2 * stream.get_buffer_size());
this._pingReadStdout();
});
}
_pingReadStderr() {
this._pingDataStderr.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => {
if (stream.fill_finish(result) == 0) {
try {
let buffer = stream.peek_buffer();
if (buffer instanceof Uint8Array) {
this._pingOutputErr = new TextDecoder().decode(buffer);
} else {
this._pingOutputErr = buffer.toString();
}
if (this._pingOutputErr) {
this.ping_message = this._pingOutputErr;
print_debug('Ping error: ' + this.ping_message);
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-bad-color');
this.updateDrawing();
}
}
} catch (e) {
print_info(e.toString());
if (!this._prepareToDestroy) {
this.color = this._settings.get_string('ping-bad-color');
this.updateDrawing();
}
}
this._pingStderr.close(null);
this._pingStderr = null;
this._pingDataStderr.close(null);
this._pingDataStderr = null;
return;
}
stream.set_buffer_size(2 * stream.get_buffer_size());
this._pingReadStderr();
});
}
refresh() {
print_debug('Ping refresh()');
try {
let script = [
'/bin/bash',
this._extensionPath + '/ping.sh',
this.address,
'' + this.ping_count,
'' + this.ping_deadline,
'' + this.ping_interval
];
let success;
[success, this.child_pid, this.in_fd, this.out_fd, this.err_fd] = GLib.spawn_async_with_pipes(
null,
script,
null,
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null
);
this._pingStdout = new Gio.UnixInputStream({fd: this.out_fd, close_fd: true});
this._pingDataStdout = new Gio.DataInputStream({base_stream: this._pingStdout});
this._pingStderr = new Gio.UnixInputStream({fd: this.err_fd, close_fd: true});
this._pingDataStderr = new Gio.DataInputStream({base_stream: this._pingStderr});
new Gio.UnixOutputStream({fd: this.in_fd, close_fd: true}).close(null);
this._tagWatchChild = GLib.child_watch_add(GLib.PRIORITY_DEFAULT, this.child_pid,
(pid, status, data) => {
GLib.Source.remove(this._tagWatchChild);
GLib.spawn_close_pid(pid);
this.child_pid = undefined;
}
);
this._pingReadStdout();
this._pingReadStderr();
} catch (e) {
print_info(e.toString());
}
}
_apply() {
print_debug('Ping _apply()');
this.menu_items[0].text = this.ping_message;
this.tip_vals[0] = this.ping_message;
}
create_text_items() {
print_debug('Ping create_text_items()');
return [
new St.Label({
text: '',
style_class: 'ping-status-value',
y_align: Clutter.ActorAlign.CENTER
}),
new St.Label({
text: '%',
style_class: 'ping-perc-label',
y_align: Clutter.ActorAlign.CENTER
})
];
}
create_menu_items() {
print_debug('Ping create_menu_items()');
return [
new St.Label({
text: '',
style_class: 'ping-value-left'
})
];
}
};
const Icon = class PingMonitor_Icon {
constructor(settings) {
print_debug('Icon constructor()');
this._settings = settings;
this.actor = new St.Icon({
icon_name: 'system-run-symbolic',
style_class: 'system-status-icon'
});
this.actor.visible = this._settings.get_boolean('icon-display');
this._settings.connect('changed::icon-display', () => {
print_debug('changed icon-display');
this.actor.visible = this._settings.get_boolean('icon-display');
});
}
};
export default class PingMonitorExtension extends Extension {
enable() {
print_info('applet enabling');
this._settings = this.getSettings();
this._style = new PingStyleManager();
this._pingBox = null;
this._elts = [];
this._tray = null;
if (!pingDepsGtop) {
this._pingDialog = new PingDialog();
this._dialogTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
this._pingDialog.open();
this._dialogTimeout = null;
return GLib.SOURCE_REMOVE;
});
} else {
this._tray = new PanelMenu.Button(0.5, 'PingMonitor', false);
this._icon = new Icon(this._settings);
this._elts = [];
let isFileOk = false;
let path = this._settings.get_string('ping-config-path');
if (path == '') {
path = GLib.getenv('HOME') + '/.config/ping-monitor.conf';
this._settings.set_string('ping-config-path', path);
}
isFileOk = this._read_from_file(path);
this._settings.set_boolean('icon-display', !isFileOk);
let spacing = '4';
this._pingBox = new St.BoxLayout({style: 'spacing: ' + spacing + 'px;'});
this._tray.add_child(this._pingBox);
this._pingBox.add_child(this._icon.actor);
for (let elt of this._elts) {
this._pingBox.add_child(elt.actor);
}
let menu_info = new PopupMenu.PopupBaseMenuItem({reactive: false});
let menu_info_box = new St.BoxLayout();
menu_info.add_child(menu_info_box);
this._tray.menu.addMenuItem(menu_info, 0);
this._build_menu_info();
this._tray.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._tray.menu.connect('open-state-changed', (menu, isOpen) => {
if (isOpen) {
this._ping_menu_timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
return GLib.SOURCE_CONTINUE;
});
} else {
if (this._ping_menu_timeout) {
GLib.Source.remove(this._ping_menu_timeout);
this._ping_menu_timeout = null;
}
}
});
let item = new PopupMenu.PopupMenuItem(_('Reload config'));
item.connect('activate', () => {
this._reload_async();
});
this._tray.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_('Preferences...'));
item.connect('activate', () => {
this.openPreferences();
});
this._tray.menu.addMenuItem(item);
Main.panel.addToStatusArea('ping-monitor', this._tray);
}
print_info('applet enabling done');
}
disable() {
print_info('disable applet');
if (this._dialogTimeout) {
GLib.Source.remove(this._dialogTimeout);
this._dialogTimeout = null;
}
if (this._pingDialog) {
this._pingDialog.destroy();
this._pingDialog = null;
}
if (this._ping_menu_timeout) {
GLib.Source.remove(this._ping_menu_timeout);
this._ping_menu_timeout = null;
}
for (let elt of this._elts) {
elt.stop();
}
if (this._pingBox) {
for (let elt of this._elts) {
this._pingBox.remove_child(elt.actor);
}
}
for (let elt of this._elts) {
elt.destroy();
}
if (this._tray) {
this._tray.destroy();
this._tray = null;
}
this._elts = [];
this._pingBox = null;
this._icon = null;
this._settings = null;
this._style = null;
print_info('applet disabled');
}
_read_from_file(path) {
print_info('read_from_file()');
try {
let [ok, contents] = GLib.file_get_contents(path);
if (ok) {
if (contents instanceof Uint8Array) {
contents = new TextDecoder().decode(contents);
}
let map = JSON.parse(contents);
try {
debugOutput = map['debug_output'];
} catch (e) {
debugOutput = false;
}
try {
for (let i = 0; i < map['ping_config'].length; i++) {
let tag = map['ping_config'][i]['tag'];
let name = map['ping_config'][i]['name'];
let address = map['ping_config'][i]['address'];
let ping_count = map['ping_config'][i]['ping_count'];
let ping_interval = map['ping_config'][i]['ping_interval'];
let ping_deadline = map['ping_config'][i]['ping_deadline'];
let refresh_interval = map['ping_config'][i]['refresh_interval'];
let active = map['ping_config'][i]['active'];
let visible = map['ping_config'][i]['visible'];
let show_name = map['ping_config'][i]['show_name'];
let show_address = map['ping_config'][i]['show_address'];
let show_tooltip = map['ping_config'][i]['show_tooltip'];
let warning_threshold = map['ping_config'][i]['warning_threshold'];
print_debug('tag: ' + tag);
print_debug('name: ' + name);
print_debug('address: ' + address);
this._elts.push(new Ping(
i,
tag,
name,
address,
ping_count,
ping_interval,
ping_deadline,
refresh_interval,
active,
visible,
show_name,
show_address,
show_tooltip,
warning_threshold,
this._settings,
this.path));
}
} catch (e) {
print_info('could not load config');
print_info('error: ' + e);
return false;
}
}
} catch (e) {
print_info('Error: ' + e);
return false;
}
return true;
}
_build_menu_info() {
print_debug('build_menu_info()');
if (this._tray.menu._getMenuItems().length &&
this._tray.menu._getMenuItems()[0].get_last_child) {
let lastChild = this._tray.menu._getMenuItems()[0].get_last_child();
if (lastChild) {
lastChild.destroy_all_children();
for (let elt of this._elts) {
elt.menu_items = elt.create_menu_items();
}
} else {
return;
}
} else {
return;
}
let menu_info_box_table = new St.Widget({
style: 'padding: 10px 0px 10px 0px; spacing-rows: 10px; spacing-columns: 15px;',
layout_manager: new Clutter.GridLayout({orientation: Clutter.Orientation.VERTICAL})
});
let menu_info_box_table_layout = menu_info_box_table.layout_manager;
let row_index = 0;
for (let elt of this._elts) {
if (!elt.menu_visible) {
continue;
}
menu_info_box_table_layout.attach(
new St.Label({
text: elt.name,
style_class: 'ping-title',
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER
}), 0, row_index, 1, 1);
let col_index = 1;
for (let item of elt.menu_items) {
menu_info_box_table_layout.attach(item, col_index, row_index, 1, 1);
col_index++;
}
row_index++;
}
let lastChild = this._tray.menu._getMenuItems()[0].get_last_child();
if (lastChild) {
lastChild.add_child(menu_info_box_table);
}
}
async _reload_async() {
print_info('Reload ping applet async...');
for (let elt of this._elts) {
elt.stop();
}
for (let elt of this._elts) {
while (elt.isRunning()) {
print_info('still running');
await new Promise(resolve => GLib.timeout_add(GLib.PRIORITY_DEFAULT, 10, () => {
resolve();
return GLib.SOURCE_REMOVE;
}));
}
}
for (let elt of this._elts) {
this._pingBox.remove_child(elt.actor);
}
for (let elt of this._elts) {
elt.destroy();
}
this._elts = [];
let isFileOk = false;
let path = this._settings.get_string('ping-config-path');
if (path == '') {
path = GLib.getenv('HOME') + '/.config/ping-monitor.conf';
this._settings.set_string('ping-config-path', path);
}
isFileOk = this._read_from_file(path);
this._settings.set_boolean('icon-display', !isFileOk);
for (let elt of this._elts) {
this._pingBox.add_child(elt.actor);
}
this._build_menu_info();
print_info('Reloaded.');
}
}