521 lines
19 KiB
JavaScript
521 lines
19 KiB
JavaScript
|
/*
|
||
|
Copyright (C) 2014 spin83
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, visit https://www.gnu.org/licenses/.
|
||
|
*/
|
||
|
|
||
|
const { St, Shell, Meta, Atk, Clutter, GObject } = imports.gi;
|
||
|
|
||
|
const Main = imports.ui.main;
|
||
|
const Panel = imports.ui.panel;
|
||
|
const PopupMenu = imports.ui.popupMenu;
|
||
|
const PanelMenu = imports.ui.panelMenu;
|
||
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||
|
|
||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||
|
const CE = ExtensionUtils.getCurrentExtension();
|
||
|
const MultiMonitors = CE.imports.extension;
|
||
|
const Convenience = CE.imports.convenience;
|
||
|
const MMCalendar = CE.imports.mmcalendar;
|
||
|
|
||
|
const SHOW_ACTIVITIES_ID = 'show-activities';
|
||
|
var SHOW_APP_MENU_ID = 'show-app-menu';
|
||
|
const SHOW_DATE_TIME_ID = 'show-date-time';
|
||
|
const AVAILABLE_INDICATORS_ID = 'available-indicators';
|
||
|
const TRANSFER_INDICATORS_ID = 'transfer-indicators';
|
||
|
|
||
|
var StatusIndicatorsController = class StatusIndicatorsController {
|
||
|
constructor() {
|
||
|
this._transfered_indicators = [];
|
||
|
this._settings = Convenience.getSettings();
|
||
|
|
||
|
this._updatedSessionId = Main.sessionMode.connect('updated', this._updateSessionIndicators.bind(this));
|
||
|
this._updateSessionIndicators();
|
||
|
this._extensionStateChangedId = Main.extensionManager.connect('extension-state-changed',
|
||
|
this._extensionStateChanged.bind(this));
|
||
|
|
||
|
this._transferIndicatorsId = this._settings.connect('changed::'+TRANSFER_INDICATORS_ID,
|
||
|
this.transferIndicators.bind(this));
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
this._settings.disconnect(this._transferIndicatorsId);
|
||
|
Main.extensionManager.disconnect(this._extensionStateChangedId);
|
||
|
Main.sessionMode.disconnect(this._updatedSessionId);
|
||
|
this._settings.set_strv(AVAILABLE_INDICATORS_ID, []);
|
||
|
this._transferBack(this._transfered_indicators);
|
||
|
}
|
||
|
|
||
|
transferBack(panel) {
|
||
|
let transfer_back = this._transfered_indicators.filter((element) => {
|
||
|
return element.monitor==panel.monitorIndex;
|
||
|
});
|
||
|
|
||
|
this._transferBack(transfer_back, panel);
|
||
|
}
|
||
|
|
||
|
transferIndicators() {
|
||
|
let boxs = ['_leftBox', '_centerBox', '_rightBox'];
|
||
|
let transfers = this._settings.get_value(TRANSFER_INDICATORS_ID).deep_unpack();
|
||
|
let show_app_menu = this._settings.get_value(SHOW_APP_MENU_ID);
|
||
|
|
||
|
let transfer_back = this._transfered_indicators.filter((element) => {
|
||
|
return !transfers.hasOwnProperty(element.iname);
|
||
|
});
|
||
|
|
||
|
this._transferBack(transfer_back);
|
||
|
|
||
|
for(let iname in transfers) {
|
||
|
if(transfers.hasOwnProperty(iname) && Main.panel.statusArea[iname]) {
|
||
|
let monitor = transfers[iname];
|
||
|
|
||
|
let indicator = Main.panel.statusArea[iname];
|
||
|
let panel = this._findPanel(monitor);
|
||
|
boxs.forEach((box) => {
|
||
|
if(Main.panel[box].contains(indicator.container) && panel) {
|
||
|
global.log('a '+box+ " > " + iname + " : "+ monitor);
|
||
|
this._transfered_indicators.push({iname:iname, box:box, monitor:monitor});
|
||
|
Main.panel[box].remove_child(indicator.container);
|
||
|
if (show_app_menu && box === '_leftBox')
|
||
|
panel[box].insert_child_at_index(indicator.container, 1);
|
||
|
else
|
||
|
panel[box].insert_child_at_index(indicator.container, 0);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_findPanel(monitor) {
|
||
|
for (let i = 0; i < Main.mmPanel.length; i++) {
|
||
|
if (Main.mmPanel[i].monitorIndex == monitor) {
|
||
|
return Main.mmPanel[i];
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
_transferBack(transfer_back, panel) {
|
||
|
transfer_back.forEach((element) => {
|
||
|
this._transfered_indicators.splice(this._transfered_indicators.indexOf(element));
|
||
|
if(Main.panel.statusArea[element.iname]) {
|
||
|
let indicator = Main.panel.statusArea[element.iname];
|
||
|
if(!panel) {
|
||
|
panel = this._findPanel(element.monitor);
|
||
|
}
|
||
|
if(panel[element.box].contains(indicator.container)) {
|
||
|
global.log("r "+element.box+ " > " + element.iname + " : "+ element.monitor);
|
||
|
panel[element.box].remove_child(indicator.container);
|
||
|
if (element.box === '_leftBox')
|
||
|
Main.panel[element.box].insert_child_at_index(indicator.container, 1);
|
||
|
else
|
||
|
Main.panel[element.box].insert_child_at_index(indicator.container, 0);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_extensionStateChanged() {
|
||
|
this._findAvailableIndicators();
|
||
|
this.transferIndicators();
|
||
|
}
|
||
|
|
||
|
_updateSessionIndicators() {
|
||
|
let session_indicators = [];
|
||
|
session_indicators.push('MultiMonitorsAddOn');
|
||
|
let sessionPanel = Main.sessionMode.panel;
|
||
|
for (let sessionBox in sessionPanel){
|
||
|
sessionPanel[sessionBox].forEach((sesionIndicator) => {
|
||
|
session_indicators.push(sesionIndicator);
|
||
|
});
|
||
|
}
|
||
|
this._session_indicators = session_indicators;
|
||
|
this._available_indicators = [];
|
||
|
|
||
|
this._findAvailableIndicators();
|
||
|
this.transferIndicators();
|
||
|
}
|
||
|
|
||
|
_findAvailableIndicators() {
|
||
|
let available_indicators = [];
|
||
|
let statusArea = Main.panel.statusArea;
|
||
|
for(let indicator in statusArea) {
|
||
|
if(statusArea.hasOwnProperty(indicator) && this._session_indicators.indexOf(indicator)<0){
|
||
|
available_indicators.push(indicator);
|
||
|
}
|
||
|
}
|
||
|
if(available_indicators.length!=this._available_indicators.length) {
|
||
|
this._available_indicators = available_indicators;
|
||
|
// global.log(this._available_indicators);
|
||
|
this._settings.set_strv(AVAILABLE_INDICATORS_ID, this._available_indicators);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var MultiMonitorsAppMenuButton = (() => {
|
||
|
let MultiMonitorsAppMenuButton = class MultiMonitorsAppMenuButton extends PanelMenu.Button {
|
||
|
_init(panel) {
|
||
|
if (panel.monitorIndex==undefined)
|
||
|
this._monitorIndex = Main.layoutManager.primaryIndex;
|
||
|
else
|
||
|
this._monitorIndex = panel.monitorIndex;
|
||
|
this._actionOnWorkspaceGroupNotifyId = 0;
|
||
|
this._targetAppGroup = null;
|
||
|
this._lastFocusedWindow = null;
|
||
|
Panel.AppMenuButton.prototype._init.call(this, panel);
|
||
|
|
||
|
this._windowEnteredMonitorId = global.display.connect('window-entered-monitor',
|
||
|
this._windowEnteredMonitor.bind(this));
|
||
|
this._windowLeftMonitorId = global.display.connect('window-left-monitor',
|
||
|
this._windowLeftMonitor.bind(this));
|
||
|
}
|
||
|
|
||
|
_windowEnteredMonitor (metaScreen, monitorIndex, metaWin) {
|
||
|
if (monitorIndex == this._monitorIndex) {
|
||
|
switch(metaWin.get_window_type()){
|
||
|
case Meta.WindowType.NORMAL:
|
||
|
case Meta.WindowType.DIALOG:
|
||
|
case Meta.WindowType.MODAL_DIALOG:
|
||
|
case Meta.WindowType.SPLASHSCREEN:
|
||
|
this._sync();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_windowLeftMonitor (metaScreen, monitorIndex, metaWin) {
|
||
|
if (monitorIndex == this._monitorIndex) {
|
||
|
switch(metaWin.get_window_type()){
|
||
|
case Meta.WindowType.NORMAL:
|
||
|
case Meta.WindowType.DIALOG:
|
||
|
case Meta.WindowType.MODAL_DIALOG:
|
||
|
case Meta.WindowType.SPLASHSCREEN:
|
||
|
this._sync();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_findTargetApp() {
|
||
|
|
||
|
if (this._actionOnWorkspaceGroupNotifyId) {
|
||
|
this._targetAppGroup.disconnect(this._actionOnWorkspaceGroupNotifyId);
|
||
|
this._actionOnWorkspaceGroupNotifyId = 0;
|
||
|
this._targetAppGroup = null;
|
||
|
}
|
||
|
let groupWindow = false;
|
||
|
let groupFocus = false;
|
||
|
|
||
|
let workspaceManager = global.workspace_manager;
|
||
|
let workspace = workspaceManager.get_active_workspace();
|
||
|
let tracker = Shell.WindowTracker.get_default();
|
||
|
let focusedApp = tracker.focus_app;
|
||
|
if (focusedApp && focusedApp.is_on_workspace(workspace)){
|
||
|
let windows = focusedApp.get_windows();
|
||
|
for (let i = 0; i < windows.length; i++) {
|
||
|
let win = windows[i];
|
||
|
if (win.located_on_workspace(workspace)){
|
||
|
if (win.get_monitor() == this._monitorIndex){
|
||
|
if (win.has_focus()){
|
||
|
this._lastFocusedWindow = win;
|
||
|
// global.log(this._monitorIndex+": focus :"+win.get_title()+" : "+win.has_focus());
|
||
|
return focusedApp;
|
||
|
}
|
||
|
else
|
||
|
groupWindow = true;
|
||
|
}
|
||
|
else {
|
||
|
if(win.has_focus())
|
||
|
groupFocus = true;
|
||
|
}
|
||
|
if (groupFocus && groupWindow) {
|
||
|
if(focusedApp != this._targetApp){
|
||
|
this._targetAppGroup = focusedApp;
|
||
|
this._actionOnWorkspaceGroupNotifyId = this._targetAppGroup.connect('notify::action-group',
|
||
|
this._sync.bind(this));
|
||
|
// global.log(this._monitorIndex+": gConnect :"+win.get_title()+" : "+win.has_focus());
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < this._startingApps.length; i++)
|
||
|
if (this._startingApps[i].is_on_workspace(workspace)){
|
||
|
// global.log(this._monitorIndex+": newAppFocus");
|
||
|
return this._startingApps[i];
|
||
|
}
|
||
|
|
||
|
if (this._lastFocusedWindow && this._lastFocusedWindow.located_on_workspace(workspace) &&
|
||
|
this._lastFocusedWindow.get_monitor() == this._monitorIndex){
|
||
|
// global.log(this._monitorIndex+": lastFocus :"+this._lastFocusedWindow.get_title());
|
||
|
return tracker.get_window_app(this._lastFocusedWindow);
|
||
|
}
|
||
|
|
||
|
let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, workspace);
|
||
|
|
||
|
for (let i = 0; i < windows.length; i++) {
|
||
|
if(windows[i].get_monitor() == this._monitorIndex){
|
||
|
this._lastFocusedWindow = windows[i];
|
||
|
// global.log(this._monitorIndex+": appFind :"+windows[i].get_title());
|
||
|
return tracker.get_window_app(windows[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
_sync() {
|
||
|
if (!this._switchWorkspaceNotifyId)
|
||
|
return;
|
||
|
Panel.AppMenuButton.prototype._sync.call(this);
|
||
|
}
|
||
|
|
||
|
_onDestroy() {
|
||
|
if (this._actionGroupNotifyId) {
|
||
|
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||
|
this._actionGroupNotifyId = 0;
|
||
|
}
|
||
|
|
||
|
global.display.disconnect(this._windowEnteredMonitorId);
|
||
|
global.display.disconnect(this._windowLeftMonitorId);
|
||
|
|
||
|
if (this._busyNotifyId) {
|
||
|
this._targetApp.disconnect(this._busyNotifyId);
|
||
|
this._busyNotifyId = 0;
|
||
|
}
|
||
|
|
||
|
if (this.menu._windowsChangedId) {
|
||
|
this.menu._app.disconnect(this.menu._windowsChangedId);
|
||
|
this.menu._windowsChangedId = 0;
|
||
|
}
|
||
|
Panel.AppMenuButton.prototype._onDestroy.call(this);
|
||
|
}
|
||
|
};
|
||
|
MultiMonitors.copyClass(Panel.AppMenuButton, MultiMonitorsAppMenuButton);
|
||
|
return GObject.registerClass({Signals: {'changed': {}},}, MultiMonitorsAppMenuButton);
|
||
|
})();
|
||
|
|
||
|
var MultiMonitorsActivitiesButton = (() => {
|
||
|
let MultiMonitorsActivitiesButton = class MultiMonitorsActivitiesButton extends PanelMenu.Button {
|
||
|
_init() {
|
||
|
super._init(0.0, null, true);
|
||
|
this.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
||
|
|
||
|
this.name = 'mmPanelActivities';
|
||
|
|
||
|
/* Translators: If there is no suitable word for "Activities"
|
||
|
in your language, you can use the word for "Overview". */
|
||
|
this._label = new St.Label({ text: _("Activities"),
|
||
|
y_align: Clutter.ActorAlign.CENTER });
|
||
|
this.add_actor(this._label);
|
||
|
|
||
|
this.label_actor = this._label;
|
||
|
|
||
|
this._showingId = Main.overview.connect('showing', () => {
|
||
|
this.add_style_pseudo_class('overview');
|
||
|
this.add_accessible_state (Atk.StateType.CHECKED);
|
||
|
});
|
||
|
this._hidingId = Main.overview.connect('hiding', () => {
|
||
|
this.remove_style_pseudo_class('overview');
|
||
|
this.remove_accessible_state (Atk.StateType.CHECKED);
|
||
|
});
|
||
|
|
||
|
this._xdndTimeOut = 0;
|
||
|
}
|
||
|
|
||
|
_onDestroy() {
|
||
|
Main.overview.disconnect(this._showingId);
|
||
|
Main.overview.disconnect(this._hidingId);
|
||
|
super._onDestroy();
|
||
|
}
|
||
|
}
|
||
|
MultiMonitors.copyClass(Panel.ActivitiesButton, MultiMonitorsActivitiesButton);
|
||
|
return GObject.registerClass(MultiMonitorsActivitiesButton);
|
||
|
})();
|
||
|
|
||
|
const MULTI_MONITOR_PANEL_ITEM_IMPLEMENTATIONS = {
|
||
|
'activities': MultiMonitorsActivitiesButton,
|
||
|
'appMenu': MultiMonitorsAppMenuButton,
|
||
|
'dateMenu': MMCalendar.MultiMonitorsDateMenuButton,
|
||
|
};
|
||
|
|
||
|
var MultiMonitorsPanel = (() => {
|
||
|
let MultiMonitorsPanel = class MultiMonitorsPanel extends St.Widget {
|
||
|
_init(monitorIndex, mmPanelBox) {
|
||
|
super._init({ name: 'panel',
|
||
|
reactive: true });
|
||
|
|
||
|
this.monitorIndex = monitorIndex;
|
||
|
|
||
|
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS);
|
||
|
|
||
|
this._sessionStyle = null;
|
||
|
|
||
|
this.statusArea = {};
|
||
|
|
||
|
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
||
|
|
||
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||
|
this.add_child(this._leftBox);
|
||
|
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||
|
this.add_child(this._centerBox);
|
||
|
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||
|
this.add_child(this._rightBox);
|
||
|
|
||
|
this._showingId = Main.overview.connect('showing', () => {
|
||
|
this.add_style_pseudo_class('overview');
|
||
|
});
|
||
|
this._hidingId = Main.overview.connect('hiding', () => {
|
||
|
this.remove_style_pseudo_class('overview');
|
||
|
});
|
||
|
|
||
|
mmPanelBox.panelBox.add(this);
|
||
|
Main.ctrlAltTabManager.addGroup(this, _("Top Bar"), 'focus-top-bar-symbolic',
|
||
|
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||
|
|
||
|
this._updatedId = Main.sessionMode.connect('updated', this._updatePanel.bind(this));
|
||
|
|
||
|
this._workareasChangedId = global.display.connect('workareas-changed', () => this.queue_relayout());
|
||
|
this._updatePanel();
|
||
|
|
||
|
this._settings = Convenience.getSettings();
|
||
|
this._showActivitiesId = this._settings.connect('changed::'+SHOW_ACTIVITIES_ID,
|
||
|
this._showActivities.bind(this));
|
||
|
this._showActivities();
|
||
|
|
||
|
this._showAppMenuId = this._settings.connect('changed::'+SHOW_APP_MENU_ID,
|
||
|
this._showAppMenu.bind(this));
|
||
|
this._showAppMenu();
|
||
|
|
||
|
this._showDateTimeId = this._settings.connect('changed::'+SHOW_DATE_TIME_ID,
|
||
|
this._showDateTime.bind(this));
|
||
|
this._showDateTime();
|
||
|
|
||
|
this.connect('destroy', this._onDestroy.bind(this));
|
||
|
}
|
||
|
|
||
|
_onDestroy() {
|
||
|
global.display.disconnect(this._workareasChangedId);
|
||
|
Main.overview.disconnect(this._showingId);
|
||
|
Main.overview.disconnect(this._hidingId);
|
||
|
|
||
|
this._settings.disconnect(this._showActivitiesId);
|
||
|
this._settings.disconnect(this._showAppMenuId);
|
||
|
this._settings.disconnect(this._showDateTimeId);
|
||
|
|
||
|
Main.ctrlAltTabManager.removeGroup(this);
|
||
|
Main.sessionMode.disconnect(this._updatedId);
|
||
|
}
|
||
|
|
||
|
_showActivities() {
|
||
|
let name = 'activities';
|
||
|
if (this._settings.get_boolean(SHOW_ACTIVITIES_ID)) {
|
||
|
if (this.statusArea[name])
|
||
|
this.statusArea[name].visible = true;
|
||
|
}
|
||
|
else {
|
||
|
if (this.statusArea[name])
|
||
|
this.statusArea[name].visible = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_showDateTime() {
|
||
|
let name = 'dateMenu';
|
||
|
if (this._settings.get_boolean(SHOW_DATE_TIME_ID)) {
|
||
|
if (this.statusArea[name])
|
||
|
this.statusArea[name].visible = true;
|
||
|
}
|
||
|
else {
|
||
|
if (this.statusArea[name])
|
||
|
this.statusArea[name].visible = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_showAppMenu() {
|
||
|
let name = 'appMenu';
|
||
|
if (this._settings.get_boolean(SHOW_APP_MENU_ID)) {
|
||
|
if (!this.statusArea[name]) {
|
||
|
let indicator = new MultiMonitorsAppMenuButton(this);
|
||
|
this.statusArea[name] = indicator;
|
||
|
let box = this._leftBox;
|
||
|
this._addToPanelBox(name, indicator, box.get_n_children()+1, box);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (this.statusArea[name]) {
|
||
|
let indicator = this.statusArea[name];
|
||
|
this.menuManager.removeMenu(indicator.menu);
|
||
|
indicator.destroy();
|
||
|
delete this.statusArea[name];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vfunc_get_preferred_width(forHeight) {
|
||
|
if (Main.layoutManager.monitors.length>this.monitorIndex)
|
||
|
return [0, Main.layoutManager.monitors[this.monitorIndex].width];
|
||
|
|
||
|
return [0, 0];
|
||
|
}
|
||
|
|
||
|
_hideIndicators() {
|
||
|
for (let role in MULTI_MONITOR_PANEL_ITEM_IMPLEMENTATIONS) {
|
||
|
let indicator = this.statusArea[role];
|
||
|
if (!indicator)
|
||
|
continue;
|
||
|
indicator.container.hide();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_ensureIndicator(role) {
|
||
|
let indicator = this.statusArea[role];
|
||
|
if (indicator) {
|
||
|
indicator.container.show();
|
||
|
return null;
|
||
|
}
|
||
|
else {
|
||
|
let constructor = MULTI_MONITOR_PANEL_ITEM_IMPLEMENTATIONS[role];
|
||
|
if (!constructor) {
|
||
|
// This icon is not implemented (this is a bug)
|
||
|
return null;
|
||
|
}
|
||
|
indicator = new constructor(this);
|
||
|
this.statusArea[role] = indicator;
|
||
|
}
|
||
|
return indicator;
|
||
|
}
|
||
|
|
||
|
_getDraggableWindowForPosition(stageX) {
|
||
|
let workspaceManager = global.workspace_manager;
|
||
|
const windows = workspaceManager.get_active_workspace().list_windows();
|
||
|
const allWindowsByStacking =
|
||
|
global.display.sort_windows_by_stacking(windows).reverse();
|
||
|
|
||
|
return allWindowsByStacking.find(metaWindow => {
|
||
|
let rect = metaWindow.get_frame_rect();
|
||
|
return metaWindow.get_monitor() == this.monitorIndex &&
|
||
|
metaWindow.showing_on_its_workspace() &&
|
||
|
metaWindow.get_window_type() != Meta.WindowType.DESKTOP &&
|
||
|
metaWindow.maximized_vertically &&
|
||
|
stageX > rect.x && stageX < rect.x + rect.width;
|
||
|
});
|
||
|
}};
|
||
|
|
||
|
MultiMonitors.copyClass(Panel.Panel, MultiMonitorsPanel);
|
||
|
return GObject.registerClass(MultiMonitorsPanel);
|
||
|
})();
|