You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
10 KiB

'use strict';
const {Gdk, Gio, GLib, Gtk, GObject} = imports.gi;
const Util = imports.util;
const Widgets = imports.widgets;
const LibSnapcast = imports.snapcast;
const Avahi = imports.avahi;
var SnapControlWindow = GObject.registerClass({
Template: Util.getBuilderFile('appwindow.ui')
, InternalChildren: [
'storeServerProps'
, 'storeServers'
, 'titlebar'
, 'cbtServers'
, 'spinner'
, 'btnInfo'
, 'btnAppMenu'
, 'vBox'
, 'labelState'
, 'popoverConfig'
]
}, class SnapControlWindow extends Gtk.ApplicationWindow {
_init(params) {
super._init(Object.assign({
title: _("Snapcast Control")
, role: _("Snapcast Control")
, 'show-menubar': false
, 'icon-name': pkg.id
, name: 'snap-control-window'
}, params));
this._settings = Util.getSettings(pkg.id);
this._btnAppMenu.set_menu_model(this.application.get_app_menu());
this.AS = new Avahi.AvahiSnapcast();
this.AS.connect('added', (AS, server) => {
// XXX: Check if not exists
this._storeServers.set(
this._storeServers.append()
, [0, 1, 2, 3, 4, 5]
, [server.name, server.hostname.toLowerCase(), server.domain, server.ip, server.port, false]
);
this._cbtServers.set_sensitive(true);
if (this.snapcast._state == LibSnapcast.ClientStates.connected) {
let [tie, iter] = this._storeServers.get_iter_first();
let model = this._cbtServers.get_model();
while (tie) {
let host = [this._storeServers.get_value(iter, 3), this._storeServers.get_value(iter, 4)].join(':');
if (host === this.snapcast.host) {
this._cbtServers.set_active_iter(iter);
break;
}
tie = this._storeServers.iter_next(iter);
}
} else if (this._cbtServers.get_active() === -1) {
this._cbtServers.set_active(0);
}
});
this.AS.connect('removed', (AS, name) => {
let [tie, iter] = this._storeServers.get_iter_first();
let model = this._cbtServers.get_model();
while (tie) {
if (this._storeServers.get_value(iter, 0) === name) {
if (this._storeServers.get_value(iter, 5) === false) {
this._storeServers.remove(iter);
}
break;
}
tie = this._storeServers.iter_next(iter);
}
if (this._cbtServers.get_active() === -1) {
this._cbtServers.set_active(0);
}
});
this._spinner.start();
this._popoverConfig.connect('closed', () => {
this._btnInfo.set_active(false);
});
this._btnInfo.connect('toggled', (btn) => {
if (btn.get_active()) {
this._popoverConfig.set_relative_to(this._btnInfo);
//this._popoverConfig.show_all();
this._popoverConfig.popup();
} else {
this._popoverConfig.popdown();
}
});
/*
this._cbtServers.set_has_tooltip(true);
this._cbtServers.connect('query-tooltip', (widget, x, y, keyboar_mode, tooltip) => {
this._popoverConfig.set_relative_to(widget);
this._popoverConfig.show_all();
this._popoverConfig.popup();
log(this._popoverConfig);
return false;
});
*/
this._btnAppMenu = new Gtk.MenuButton();
this._btnAppMenu.set_menu_model(this.application.get_app_menu());
this._btnAppMenu.set_image(new Gtk.Image({ 'icon-name': 'open-menu-symbolic' }));
this.show_all();
let lastUsed = this._settings.get_string('last-used');
this.snapcast = new LibSnapcast.Snapcast(lastUsed);
this.snapcast.connect("state::ready", this._on_snapcast_ready.bind(this));
this.snapcast.connect("state::disconnected", this._on_snapcast_disconnected.bind(this));
this.snapcast.connect("state::connecting", this._on_snapcast_connecting.bind(this));
this.snapcast.connect("updated", this._on_snapcast_updated.bind(this));
this.snapcast.enable();
this._cbtServers.connect('changed', (combo) => {
let [tie, tree_iter] = combo.get_active_iter();
let activeItem = combo.get_active();
let model = combo.get_model();
if (tie) {
let hostname = this._storeServers.get_value(tree_iter, 1)
, domain = this._storeServers.get_value(tree_iter, 2)
, ip = this._storeServers.get_value(tree_iter, 3)
, port = this._storeServers.get_value(tree_iter, 4)
, host;
if (ip !== "") {
host = ip;
} else {
host = hostname;
if (domain !== "") {
host += '.' + domain;
}
}
if (port === "") {
port = 1705;
}
this.snapcast.host = [host, port].join(':');
}
});
this.AS.enable();
}
_on_snapcast_updated() {
log("SNAPCAST_UPDATED");
this.updateInfoPopover();
//this._popoverConfig.add(this.buildInfoPopover());
}
_on_snapcast_disconnected() {
log("SNAPCAST_DISCONNECTED");
this._titlebar.set_subtitle(_("Disconnected"));
this._vBox.foreach((child) => {
if (child instanceof Widgets.GroupWidget) {
this._vBox.remove(child);
}
});
if (this._popoverConfig.is_visible()) {
this._popoverConfig.popdown();
}
this._btnInfo.set_sensitive(false);
this._btnInfo.set_visible(false);
this._labelState.set_visible(true);
}
_on_snapcast_connecting() {
this._spinner.start();
this._spinner.set_visible(true);
}
_on_snapcast_ready() {
log("SNAPCAST_READY");
this._titlebar.set_subtitle(_("Connected"));
this._settings.set_string('last-used', this.snapcast.host);
this._labelState.set_visible(false);
this._spinner.stop();
this._spinner.set_visible(false);
this._btnInfo.set_sensitive(true);
this._btnInfo.set_visible(true);
this.snapcast.sServer.connect("streams::added", this._on_snapcast_stream_added.bind(this));
this.snapcast.sServer.connect("streams::deleted", this._on_snapcast_stream_deleted.bind(this));
this.snapcast.sServer.connect("groups::added", this._on_snapcast_group_added.bind(this));
this.snapcast.sServer.connect("groups::deleted", this._on_snapcast_group_deleted.bind(this));
this.snapcast.sServer.connect("clients::added", this._on_snapcast_client_added.bind(this));
this.snapcast.sServer.connect("clients::deleted", this._on_snapcast_client_deleted.bind(this));
}
_on_snapcast_stream_added() {
// XXX
}
_on_snapcast_stream_deleted() {
// XXX
}
_on_snapcast_group_added(event, group) {
this._vBox.add(new Widgets.GroupWidget(this, this.snapcast, group));
this._refresh_window_size();
}
_on_snapcast_group_deleted(event, group_id) {
this._refresh_window_size();
}
_on_snapcast_client_added(event, client) {
this._refresh_window_size();
}
_on_snapcast_client_deleted(event, client) {
this._refresh_window_size();
}
_refresh_window_size() {
let [wm, wn] = this.get_preferred_width();
let [hm, hn] = this._vBox.get_preferred_height();
let [thm, thn] = this._titlebar.get_preferred_height();
let ch = 0;
this._vBox.get_children().forEach((child) => {
let [chm, chn] = child.get_preferred_height();
ch = ch + chn + (this._vBox.get_spacing() * 2);
});
ch += this._vBox.get_parent().get_margin_top() + this._vBox.get_parent().get_margin_bottom();
let nh = thn + this._vBox.get_margin_top() + this._vBox.get_margin_bottom() + ch;
let display = this.get_display();
let monitor = display.get_monitor_at_window(this.get_window());
let wa = monitor.get_workarea();
if (nh > wa.height - thn) {
nh = wa.height - thn;
}
this.resize(wn, nh);
}
updateInfoPopover() {
this._storeServerProps.clear();
let props = this.snapcast.sServer.server;
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Name: "), props.snapserver.name]);
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Hostname: "), props.host.name]);
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Operating System: "), props.host.os]);
if (props.host.arch !== "") {
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Architecture: "), props.host.arch]);
}
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Snapserver Version: "), props.snapserver.version]);
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Protocol Version: "), props.snapserver.protocolVersion]);
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Control Version: "), props.snapserver.controlProtocolVersion]);
this._storeServerProps.set(this._storeServerProps.append(), [0,1],
[_("Streams: "), Object.keys(this.snapcast.sServer.streams).length]);
}
showServersWindow() {
log('SHOW SERVER WINDOWS');
let modal = new Gtk.Dialog({
default_width: 250
, modal: true
, transient_for: this
, title: _("Servers")
, use_header_bar: false
});
let contentArea = modal.get_content_area()
, actionArea = modal.get_action_area();
let treeview = new Gtk.TreeView({
model: this._storeServers
, 'headers-visible': false
});
let sel = treeview.get_selection();
sel.set_mode(Gtk.SelectionMode.NONE);
let renderer0 = new Gtk.CellRendererText();
let renderer1 = new Gtk.CellRendererText();
let renderer2 = new Gtk.CellRendererText();
let renderer3 = new Gtk.CellRendererText();
let renderer4 = new Gtk.CellRendererText();
let renderer5 = new Gtk.CellRendererText();
let column0 = new Gtk.TreeViewColumn({ title: "ID" });
let column1 = new Gtk.TreeViewColumn({ title: "Hostname" });
let column2 = new Gtk.TreeViewColumn({ title: "Domain" });
let column3 = new Gtk.TreeViewColumn({ title: "IP" });
let column4 = new Gtk.TreeViewColumn({ title: "Port" });
let column5 = new Gtk.TreeViewColumn({ title: "Manual" });
column0.pack_start(renderer0, false);
column0.add_attribute(renderer0, "text", 0);
column1.pack_start(renderer1, false);
column1.add_attribute(renderer1, "text", 1);
column2.pack_start(renderer2, false);
column2.add_attribute(renderer2, "text", 2);
column2.pack_start(renderer3, false);
column2.add_attribute(renderer3, "text", 3);
column2.pack_start(renderer4, false);
column2.add_attribute(renderer4, "text", 4);
column2.pack_start(renderer5, false);
column2.add_attribute(renderer5, "text", 5);
treeview.append_column(column0);
treeview.append_column(column1);
treeview.append_column(column2);
treeview.append_column(column3);
treeview.append_column(column4);
treeview.append_column(column5);
treeview.set_margin_top(10);
treeview.set_margin_bottom(10);
contentArea.add(treeview);
let boxBtn = new Gtk.ButtonBox({
orientation: Gtk.Orientation.HORIZONTAL
});
boxBtn.set_layout(Gtk.ButtonBoxStyle.EDGE);
let btnOk = Gtk.Button.new_from_stock(Gtk.STOCK_OK);
btnOk.connect("clicked", () => {
modal.close();
});
boxBtn.add(btnOk);
actionArea.add(boxBtn);
modal.show_all();
}
});