/* 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); })();