diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 15:05:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 15:05:14 +0000 |
commit | 301ec35e13e64d13989834f7ec34206c0e875e78 (patch) | |
tree | 6c3fef8f6ac8d04e00d400e4121c4d9603de3c37 | |
parent | Initial commit. (diff) | |
download | gnome-shell-extension-multi-monitors-301ec35e13e64d13989834f7ec34206c0e875e78.tar.xz gnome-shell-extension-multi-monitors-301ec35e13e64d13989834f7ec34206c0e875e78.zip |
Adding upstream version 23.upstream/23upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
30 files changed, 4514 insertions, 0 deletions
diff --git a/.project b/.project new file mode 100644 index 0000000..58f94f4 --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>multi-monitors-add-on</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + </buildSpec> + <natures> + </natures> +</projectDescription> @@ -0,0 +1,340 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..afca60d --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +Multi Monitors Add-On +===================== + +Extension inspired by https://github.com/darkxst/multiple-monitor-panels +and rewritten from scratch for gnome-shell version 3.10.4. Adds panels +and thumbnails for additional monitors. Settings changes are applied +in dynamic fashion, no restart needed. + +Versions +======== + +* Branch [master](https://github.com/spin83/multi-monitors-add-on/tree/master) contains extension for GNOME 3.38 +* Branch [gnome-3-32_3-36](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-32_3-36) contains extension for GNOME 3.32, 3.34 and 3.36 +* Branch [gnome-3-24_3-30](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-24_3-30) contains extension for GNOME 3.24, 3.26, 3.28 and 3.30 +* Branch [gnome-3-20_3-22](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-20_3-22) contains extension for GNOME 3.20 and 3.22 +* Branch [gnome-3-16_3-18](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-16_3-18) contains extension for GNOME 3.16 and 3.18 +* Branch [gnome-3-14](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-14) contains extension for GNOME 3.14 +* Branch [gnome-3-10](https://github.com/spin83/multi-monitors-add-on/tree/gnome-3-10) contains extension for GNOME 3.10 + +Installation from git +===================== + + git clone git://github.com/spin83/multi-monitors-add-on.git + cd multi-monitors-add-on + cp -r multi-monitors-add-on@spin83 ~/.local/share/gnome-shell/extensions/ + +Restart the shell and then enable the extension. + +License +======= + +Multi Monitors Add-On extension is distributed under the terms of the +GNU General Public License, version 2 or later. See the LICENSE file for details. diff --git a/multi-monitors-add-on@spin83/convenience.js b/multi-monitors-add-on@spin83/convenience.js new file mode 100644 index 0000000..bbc8608 --- /dev/null +++ b/multi-monitors-add-on@spin83/convenience.js @@ -0,0 +1,93 @@ +/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (c) 2011-2012, Giovanni Campagna <scampa.giovanni@gmail.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the GNOME nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +const Gettext = imports.gettext; +const Gio = imports.gi.Gio; + +const Config = imports.misc.config; +const ExtensionUtils = imports.misc.extensionUtils; + +/** + * initTranslations: + * @domain: (optional): the gettext domain to use + * + * Initialize Gettext to load translations from extensionsdir/locale. + * If @domain is not provided, it will be taken from metadata['gettext-domain'] + */ +function initTranslations(domain) { + let extension = ExtensionUtils.getCurrentExtension(); + + domain = domain || extension.metadata['gettext-domain']; + + // check if this extension was built with "make zip-file", and thus + // has the locale files in a subfolder + // otherwise assume that extension has been installed in the + // same prefix as gnome-shell + let localeDir = extension.dir.get_child('locale'); + if (localeDir.query_exists(null)) + Gettext.bindtextdomain(domain, localeDir.get_path()); + else + Gettext.bindtextdomain(domain, Config.LOCALEDIR); +} + +/** + * getSettings: + * @schema: (optional): the GSettings schema id + * + * Builds and return a GSettings schema for @schema, using schema files + * in extensionsdir/schemas. If @schema is not provided, it is taken from + * metadata['settings-schema']. + */ +function getSettings(schema) { + let extension = ExtensionUtils.getCurrentExtension(); + + schema = schema || extension.metadata['settings-schema']; + + const GioSSS = Gio.SettingsSchemaSource; + + // check if this extension was built with "make zip-file", and thus + // has the schema files in a subfolder + // otherwise assume that extension has been installed in the + // same prefix as gnome-shell (and therefore schemas are available + // in the standard folders) + let schemaDir = extension.dir.get_child('schemas'); + let schemaSource; + if (schemaDir.query_exists(null)) + schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), + GioSSS.get_default(), + false); + else + schemaSource = GioSSS.get_default(); + + let schemaObj = schemaSource.lookup(schema, true); + if (!schemaObj) + throw new Error('Schema ' + schema + ' could not be found for extension ' + + extension.metadata.uuid + '. Please check your installation.'); + + return new Gio.Settings({ settings_schema: schemaObj }); +} + diff --git a/multi-monitors-add-on@spin83/extension.js b/multi-monitors-add-on@spin83/extension.js new file mode 100644 index 0000000..e1928e9 --- /dev/null +++ b/multi-monitors-add-on@spin83/extension.js @@ -0,0 +1,288 @@ +/* +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 { Clutter, Gio } = imports.gi; + +const Main = imports.ui.main; +var { ANIMATION_TIME } = imports.ui.overview; + +const Config = imports.misc.config; +const GNOME_SHELL_VERSION = Config.PACKAGE_VERSION.split('.'); + +const ExtensionUtils = imports.misc.extensionUtils; +const MultiMonitors = ExtensionUtils.getCurrentExtension(); +const Convenience = MultiMonitors.imports.convenience; + +const MMLayout = MultiMonitors.imports.mmlayout; +const MMOverview = MultiMonitors.imports.mmoverview; +const MMIndicator = MultiMonitors.imports.indicator; + +const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; +const MUTTER_SCHEMA = 'org.gnome.mutter'; +const WORKSPACES_ONLY_ON_PRIMARY_ID = 'workspaces-only-on-primary'; + +const SHOW_INDICATOR_ID = 'show-indicator'; +const THUMBNAILS_SLIDER_POSITION_ID = 'thumbnails-slider-position'; + +function copyClass (s, d) { +// global.log(s.name +" > "+ d.name); + let propertyNames = Reflect.ownKeys(s.prototype); + for (let pName of propertyNames.values()) { + +// global.log(" ) "+pName.toString()); + if (typeof pName === "symbol") continue; + if (d.prototype.hasOwnProperty(pName)) continue; + if (pName === "prototype") continue; + if (pName === "constructor") continue; +// global.log(pName); + let pDesc = Reflect.getOwnPropertyDescriptor(s.prototype, pName); +// global.log(typeof pDesc); + if (typeof pDesc !== 'object') continue; + Reflect.defineProperty(d.prototype, pName, pDesc); + } +}; + +function gnomeShellVersion() { + return GNOME_SHELL_VERSION; +} + +class MultiMonitorsAddOn { + + constructor() { + this._settings = Convenience.getSettings(); + this._ov_settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA }); + this._mu_settings = new Gio.Settings({ schema: MUTTER_SCHEMA }); + + this.mmIndicator = null; + Main.mmOverview = null; + Main.mmLayoutManager = null; + + this._mmMonitors = 0; + this.syncWorkspacesActualGeometry = null; + } + + _showIndicator() { + if(this._settings.get_boolean(SHOW_INDICATOR_ID)) { + if(!this.mmIndicator) { + this.mmIndicator = Main.panel.addToStatusArea('MultiMonitorsAddOn', new MMIndicator.MultiMonitorsIndicator()); + } + } + else { + this._hideIndicator(); + } + } + + _hideIndicator() { + if(this.mmIndicator) { + this.mmIndicator.destroy(); + this.mmIndicator = null; + } + } + + _showThumbnailsSlider() { + if (this._settings.get_string(THUMBNAILS_SLIDER_POSITION_ID) === 'none') { + this._hideThumbnailsSlider(); + return; + } + + if(this._ov_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID)) + this._ov_settings.set_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID, false); + if(this._mu_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID)) + this._mu_settings.set_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID, false); + + if (Main.mmOverview) + return; + + Main.mmOverview = []; + for (let idx = 0; idx < Main.layoutManager.monitors.length; idx++) { + if (idx != Main.layoutManager.primaryIndex) { + Main.mmOverview[idx] = new MMOverview.MultiMonitorsOverview(idx); + } + } + + this.syncWorkspacesActualGeometry = Main.overview.viewSelector._workspacesDisplay._syncWorkspacesActualGeometry; + Main.overview.viewSelector._workspacesDisplay._syncWorkspacesActualGeometry = function() { + if (this._inWindowFade) + return; + + const primaryView = this._getPrimaryView(); + if (primaryView) { + primaryView.ease({ + ...this._actualGeometry, + duration: Main.overview.animationInProgress ? ANIMATION_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + for (let idx = 0; idx < Main.mmOverview.length; idx++) { + if (!Main.mmOverview[idx]) + continue; + if (!Main.mmOverview[idx]._overview) + continue; + const mmView = Main.mmOverview[idx]._overview._controls._workspacesViews; + if (!mmView) + continue; + + const mmGeometry = Main.mmOverview[idx].getWorkspacesActualGeometry(); + mmView.ease({ + ...mmGeometry, + duration: Main.overview.animationInProgress ? ANIMATION_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + } + } + + _hideThumbnailsSlider() { + if (!Main.mmOverview) + return; + + for (let idx = 0; idx < Main.mmOverview.length; idx++) { + if (Main.mmOverview[idx]) + Main.mmOverview[idx].destroy(); + } + Main.mmOverview = null; + Main.overview.viewSelector._workspacesDisplay._syncWorkspacesActualGeometry = this.syncWorkspacesActualGeometry; + } + + _relayout() { + if(this._mmMonitors!=Main.layoutManager.monitors.length){ + this._mmMonitors = Main.layoutManager.monitors.length; + global.log("pi:"+Main.layoutManager.primaryIndex); + for (let i = 0; i < Main.layoutManager.monitors.length; i++) { + let monitor = Main.layoutManager.monitors[i]; + global.log("i:"+i+" x:"+monitor.x+" y:"+monitor.y+" w:"+monitor.width+" h:"+monitor.height); + } + this._hideThumbnailsSlider(); + this._showThumbnailsSlider(); + } + } + + _switchOffThumbnails() { + if (this._ov_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID) || this._mu_settings.get_boolean(WORKSPACES_ONLY_ON_PRIMARY_ID)) { + this._settings.set_string(THUMBNAILS_SLIDER_POSITION_ID, 'none'); + } + } + + enable(version) { + global.log("Enable Multi Monitors Add-On ("+version+")...") + + if(Main.panel.statusArea.MultiMonitorsAddOn) + disable(); + + this._mmMonitors = 0; + + this._switchOffThumbnailsOvId = this._ov_settings.connect('changed::'+WORKSPACES_ONLY_ON_PRIMARY_ID, + this._switchOffThumbnails.bind(this)); + this._switchOffThumbnailsMuId = this._mu_settings.connect('changed::'+WORKSPACES_ONLY_ON_PRIMARY_ID, + this._switchOffThumbnails.bind(this)); + + this._showIndicatorId = this._settings.connect('changed::'+SHOW_INDICATOR_ID, this._showIndicator.bind(this)); + this._showIndicator(); + + Main.mmLayoutManager = new MMLayout.MultiMonitorsLayoutManager(); + this._showPanelId = this._settings.connect('changed::'+MMLayout.SHOW_PANEL_ID, Main.mmLayoutManager.showPanel.bind(Main.mmLayoutManager)); + Main.mmLayoutManager.showPanel(); + + this._thumbnailsSliderPositionId = this._settings.connect('changed::'+THUMBNAILS_SLIDER_POSITION_ID, this._showThumbnailsSlider.bind(this)); + this._relayoutId = Main.layoutManager.connect('monitors-changed', this._relayout.bind(this)); + this._relayout(); + } + + disable() { + Main.layoutManager.disconnect(this._relayoutId); + this._ov_settings.disconnect(this._switchOffThumbnailsOvId); + this._mu_settings.disconnect(this._switchOffThumbnailsMuId); + + this._settings.disconnect(this._showPanelId); + this._settings.disconnect(this._thumbnailsSliderPositionId); + this._settings.disconnect(this._showIndicatorId); + + + this._hideIndicator(); + + Main.mmLayoutManager.hidePanel(); + Main.mmLayoutManager = null; + + this._hideThumbnailsSlider(); + this._mmMonitors = 0; + + global.log("Disable Multi Monitors Add-On ...") + } +} + +var multiMonitorsAddOn = null; +var version = null; + +function init() { + Convenience.initTranslations(); + + // fix bug in panel: Destroy function many time added to this same indicator. + Main.panel._ensureIndicator = function(role) { + let indicator = this.statusArea[role]; + if (indicator) { + indicator.container.show(); + return null; + } + else { + let constructor = 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; + }; + + const metaVersion = MultiMonitors.metadata['version']; + if (Number.isFinite(metaVersion)) { + version = 'v'+Math.trunc(metaVersion); + switch(Math.round((metaVersion%1)*10)) { + case 0: + break; + case 1: + version += '+bugfix'; + break; + case 2: + version += '+develop'; + break; + default: + version += '+modified'; + break; + } + } + else + version = metaVersion; +} + +function enable() { + if (multiMonitorsAddOn !== null) + return; + + multiMonitorsAddOn = new MultiMonitorsAddOn(); + multiMonitorsAddOn.enable(version); +} + +function disable() { + if (multiMonitorsAddOn == null) + return; + + multiMonitorsAddOn.disable(); + multiMonitorsAddOn = null; +} diff --git a/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg b/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg new file mode 100644 index 0000000..6341c21 --- /dev/null +++ b/multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg @@ -0,0 +1,392 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="multi-monitor-symbolic.svg" + height="16" + id="svg7384" + inkscape:version="0.48.5 r10040" + version="1.1" + width="16"> + <metadata + id="metadata90"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Gnome Symbolic Icon Theme</dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:bbox-paths="false" + bordercolor="#666666" + borderopacity="1" + inkscape:current-layer="layer11" + inkscape:cx="3.128503" + inkscape:cy="7.3630629" + gridtolerance="10" + inkscape:guide-bbox="true" + guidetolerance="10" + id="namedview88" + inkscape:object-nodes="false" + inkscape:object-paths="false" + objecttolerance="10" + pagecolor="#555753" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + showborder="false" + showgrid="true" + showguides="true" + inkscape:snap-bbox="true" + inkscape:snap-bbox-midpoints="false" + inkscape:snap-global="true" + inkscape:snap-grids="true" + inkscape:snap-nodes="false" + inkscape:snap-others="false" + inkscape:snap-to-guides="true" + inkscape:window-height="1014" + inkscape:window-maximized="1" + inkscape:window-width="1920" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:zoom="32"> + <inkscape:grid + empspacing="2" + enabled="true" + id="grid4866" + originx="-42.000009px" + originy="412px" + snapvisiblegridlinesonly="true" + spacingx="1px" + spacingy="1px" + type="xygrid" + visible="true" /> + <sodipodi:guide + orientation="0,1" + position="3.4692426,9.4354561" + id="guide4029" /> + </sodipodi:namedview> + <title + id="title9167">Gnome Symbolic Icon Theme</title> + <defs + id="defs7386"> + <linearGradient + id="linearGradient5351" + osb:paint="solid"> + <stop + style="stop-color:#00a600;stop-opacity:1;" + offset="0" + id="stop5353" /> + </linearGradient> + <marker + inkscape:stockid="EmptyTriangleOutS" + orient="auto" + refY="0" + refX="0" + id="EmptyTriangleOutS" + style="overflow:visible"> + <path + id="path4013" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.2,0,0,0.2,-0.6,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Legs" + orient="auto" + refY="0" + refX="0" + id="Legs" + style="overflow:visible"> + <g + id="g4046" + transform="scale(-0.7,-0.7)"> + <g + id="g4048" + transform="matrix(0,-1,-1,0,20.70862,21.31391)"> + <path + id="path4050" + d="m 21.22125,20.67536 c -6.910151,4.721157 -2.454525,6.606844 -5.841071,13.443235" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4052" + d="m 21.39811,20.54812 c -1.360509,8.347524 3.536072,8.76994 4.505041,13.824958" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + <path + id="path4054" + d="m -14.09007,-6.7318716 -0.922168,4.043383 3.962751,-1.22307 -3.040583,-2.820313 z" + style="fill:#030300;fill-rule:evenodd;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4056" + d="m -15.215679,4.5567534 1.874127,3.699613 2.266874,-3.472855 -4.141001,-0.226758 z" + style="fill:#030300;fill-rule:evenodd;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="Torso" + orient="auto" + refY="0" + refX="0" + id="Torso" + style="overflow:visible"> + <g + id="g4059" + transform="scale(0.7,0.7)"> + <path + id="path4061" + d="m -4.7792281,-3.239542 c 2.350374,0.3659393 5.30026732,1.9375477 5.03715532,3.62748546 C -0.00518779,2.0778819 -2.2126741,2.6176539 -4.5630471,2.2517169 -6.9134221,1.8857769 -8.521035,0.75201414 -8.257922,-0.93792336 -7.994809,-2.6278615 -7.1296041,-3.6054813 -4.7792281,-3.239542 z" + style="fill:none;stroke:#000000;stroke-width:1.25" + inkscape:connector-curvature="0" /> + <path + id="path4063" + d="M 4.4598789,0.08866574 C -2.5564571,-4.378332 5.2248769,-3.9061806 -0.84829578,-8.7197331" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4065" + d="M 4.9298719,0.05752074 C -1.3872731,1.7494689 1.8027579,5.4782079 -4.9448731,7.5462725" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <rect + id="rect4067" + transform="matrix(0.527536,-0.849533,0.887668,0.460484,0,0)" + y="-1.7408575" + x="-10.391706" + height="2.7608147" + width="2.6366582" + style="fill-rule:evenodd;stroke-width:1pt" /> + <rect + id="rect4069" + transform="matrix(0.671205,-0.741272,0.790802,0.612072,0,0)" + y="-7.9629307" + x="4.9587269" + height="2.8614161" + width="2.7327356" + style="fill-rule:evenodd;stroke-width:1pt" /> + <path + id="path4071" + transform="matrix(0,-1.109517,1.109517,0,25.96648,19.71619)" + d="m 16.779951,-28.685045 a 0.60731727,0.60731727 0 1 0 -1.214634,0 0.60731727,0.60731727 0 1 0 1.214634,0 z" + style="fill:#ff0000;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4073" + transform="matrix(0,-1.109517,1.109517,0,26.8245,16.99126)" + d="m 16.779951,-28.685045 a 0.60731727,0.60731727 0 1 0 -1.214634,0 0.60731727,0.60731727 0 1 0 1.214634,0 z" + style="fill:#ff0000;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="EmptyDiamondLstart" + orient="auto" + refY="0" + refX="0" + id="EmptyDiamondLstart" + style="overflow:visible"> + <path + id="path3962" + d="M 0,-7.0710768 -7.0710894,0 0,7.0710589 7.0710462,0 0,-7.0710768 z" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.8,0,0,0.8,5.6,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="DiamondSend" + orient="auto" + refY="0" + refX="0" + id="DiamondSend" + style="overflow:visible"> + <path + id="path3950" + d="M 0,-7.0710768 -7.0710894,0 0,7.0710589 7.0710462,0 0,-7.0710768 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.2,0,0,0.2,-1.2,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mend" + style="overflow:visible"> + <path + id="path3856" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(-0.4,0,0,-0.4,-4,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Tail" + orient="auto" + refY="0" + refX="0" + id="Tail" + style="overflow:visible"> + <g + id="g3883" + transform="scale(-1.2,-1.2)"> + <path + id="path3885" + d="M -3.8048674,-3.9585227 0.54352094,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3887" + d="M -1.2866832,-3.9585227 3.0617053,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3889" + d="M 1.3053582,-3.9585227 5.6537466,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3891" + d="M -3.8048674,4.1775838 0.54352094,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3893" + d="M -1.2866832,4.1775838 3.0617053,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3895" + d="M 1.3053582,4.1775838 5.6537466,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="Arrow2Sstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Sstart" + style="overflow:visible"> + <path + id="path3877" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(0.3,0,0,0.3,-0.69,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="DotL" + orient="auto" + refY="0" + refX="0" + id="DotL" + style="overflow:visible"> + <path + id="path3908" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.8,0,0,0.8,5.92,0.8)" + inkscape:connector-curvature="0" /> + </marker> + </defs> + <g + inkscape:groupmode="layer" + id="layer9" + inkscape:label="status" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer10" + inkscape:label="devices" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer11" + inkscape:label="apps" + style="display:inline" + transform="translate(-283.00021,-629)"> + <g + id="g3040"> + <path + id="path3795-3" + d="m 283.60374,629.58458 0,12.86095 14.78834,-1.16918 0,-10.5226 -14.78834,-1.16917 z m 0.73172,0.65766 13.28639,1.05957 0,9.46303 -13.32491,1.02303 0.0385,-11.54563 z" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:1.20035386;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path3839-5" + d="m 290.99791,641.86094 -2e-5,2.33836" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:3.60105371;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path3841-2" + d="m 295.34001,643.66019 -8.13359,0.64305" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:1.32038927;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;display:inline" /> + </g> + </g> + <g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="on" + style="display:inline" /> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="off" + style="display:inline" /> + <g + inkscape:groupmode="layer" + id="layer13" + inkscape:label="places" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer14" + inkscape:label="mimetypes" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer15" + inkscape:label="emblems" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="g71291" + inkscape:label="emotes" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="g4953" + inkscape:label="categories" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer12" + inkscape:label="actions" + style="display:inline" + transform="translate(-283.00021,-629)" /> +</svg> diff --git a/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg b/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg new file mode 100644 index 0000000..6bf4651 --- /dev/null +++ b/multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg @@ -0,0 +1,393 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="multi-monitor-l-symbolic.svg" + height="16" + id="svg7384" + inkscape:version="0.48.5 r10040" + version="1.1" + width="16"> + <metadata + id="metadata90"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Gnome Symbolic Icon Theme</dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:bbox-paths="false" + bordercolor="#666666" + borderopacity="1" + inkscape:current-layer="layer11" + inkscape:cx="3.191003" + inkscape:cy="7.3005629" + gridtolerance="10" + inkscape:guide-bbox="true" + guidetolerance="10" + id="namedview88" + inkscape:object-nodes="false" + inkscape:object-paths="false" + objecttolerance="10" + pagecolor="#555753" + inkscape:pageopacity="1" + inkscape:pageshadow="2" + showborder="false" + showgrid="true" + showguides="true" + inkscape:snap-bbox="true" + inkscape:snap-bbox-midpoints="false" + inkscape:snap-global="true" + inkscape:snap-grids="true" + inkscape:snap-nodes="false" + inkscape:snap-others="false" + inkscape:snap-to-guides="true" + inkscape:window-height="958" + inkscape:window-maximized="1" + inkscape:window-width="1280" + inkscape:window-x="1920" + inkscape:window-y="27" + inkscape:zoom="32"> + <inkscape:grid + empspacing="2" + enabled="true" + id="grid4866" + originx="-42.000009px" + originy="412px" + snapvisiblegridlinesonly="true" + spacingx="1px" + spacingy="1px" + type="xygrid" + visible="true" /> + <sodipodi:guide + orientation="0,1" + position="3.4692426,9.4354561" + id="guide4029" /> + </sodipodi:namedview> + <title + id="title9167">Gnome Symbolic Icon Theme</title> + <defs + id="defs7386"> + <linearGradient + id="linearGradient5351" + osb:paint="solid"> + <stop + style="stop-color:#00a600;stop-opacity:1;" + offset="0" + id="stop5353" /> + </linearGradient> + <marker + inkscape:stockid="EmptyTriangleOutS" + orient="auto" + refY="0" + refX="0" + id="EmptyTriangleOutS" + style="overflow:visible"> + <path + id="path4013" + d="m 5.77,0 -8.65,5 0,-10 8.65,5 z" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.2,0,0,0.2,-0.6,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Legs" + orient="auto" + refY="0" + refX="0" + id="Legs" + style="overflow:visible"> + <g + id="g4046" + transform="scale(-0.7,-0.7)"> + <g + id="g4048" + transform="matrix(0,-1,-1,0,20.70862,21.31391)"> + <path + id="path4050" + d="m 21.22125,20.67536 c -6.910151,4.721157 -2.454525,6.606844 -5.841071,13.443235" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4052" + d="m 21.39811,20.54812 c -1.360509,8.347524 3.536072,8.76994 4.505041,13.824958" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + <path + id="path4054" + d="m -14.09007,-6.7318716 -0.922168,4.043383 3.962751,-1.22307 -3.040583,-2.820313 z" + style="fill:#030300;fill-rule:evenodd;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4056" + d="m -15.215679,4.5567534 1.874127,3.699613 2.266874,-3.472855 -4.141001,-0.226758 z" + style="fill:#030300;fill-rule:evenodd;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="Torso" + orient="auto" + refY="0" + refX="0" + id="Torso" + style="overflow:visible"> + <g + id="g4059" + transform="scale(0.7,0.7)"> + <path + id="path4061" + d="m -4.7792281,-3.239542 c 2.350374,0.3659393 5.30026732,1.9375477 5.03715532,3.62748546 C -0.00518779,2.0778819 -2.2126741,2.6176539 -4.5630471,2.2517169 -6.9134221,1.8857769 -8.521035,0.75201414 -8.257922,-0.93792336 -7.994809,-2.6278615 -7.1296041,-3.6054813 -4.7792281,-3.239542 z" + style="fill:none;stroke:#000000;stroke-width:1.25" + inkscape:connector-curvature="0" /> + <path + id="path4063" + d="M 4.4598789,0.08866574 C -2.5564571,-4.378332 5.2248769,-3.9061806 -0.84829578,-8.7197331" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4065" + d="M 4.9298719,0.05752074 C -1.3872731,1.7494689 1.8027579,5.4782079 -4.9448731,7.5462725" + style="fill:none;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <rect + id="rect4067" + transform="matrix(0.527536,-0.849533,0.887668,0.460484,0,0)" + y="-1.7408575" + x="-10.391706" + height="2.7608147" + width="2.6366582" + style="fill-rule:evenodd;stroke-width:1pt" /> + <rect + id="rect4069" + transform="matrix(0.671205,-0.741272,0.790802,0.612072,0,0)" + y="-7.9629307" + x="4.9587269" + height="2.8614161" + width="2.7327356" + style="fill-rule:evenodd;stroke-width:1pt" /> + <path + id="path4071" + transform="matrix(0,-1.109517,1.109517,0,25.96648,19.71619)" + d="m 16.779951,-28.685045 a 0.60731727,0.60731727 0 1 0 -1.214634,0 0.60731727,0.60731727 0 1 0 1.214634,0 z" + style="fill:#ff0000;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + <path + id="path4073" + transform="matrix(0,-1.109517,1.109517,0,26.8245,16.99126)" + d="m 16.779951,-28.685045 a 0.60731727,0.60731727 0 1 0 -1.214634,0 0.60731727,0.60731727 0 1 0 1.214634,0 z" + style="fill:#ff0000;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="EmptyDiamondLstart" + orient="auto" + refY="0" + refX="0" + id="EmptyDiamondLstart" + style="overflow:visible"> + <path + id="path3962" + d="M 0,-7.0710768 -7.0710894,0 0,7.0710589 7.0710462,0 0,-7.0710768 z" + style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.8,0,0,0.8,5.6,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="DiamondSend" + orient="auto" + refY="0" + refX="0" + id="DiamondSend" + style="overflow:visible"> + <path + id="path3950" + d="M 0,-7.0710768 -7.0710894,0 0,7.0710589 7.0710462,0 0,-7.0710768 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.2,0,0,0.2,-1.2,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mend" + style="overflow:visible"> + <path + id="path3856" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(-0.4,0,0,-0.4,-4,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Tail" + orient="auto" + refY="0" + refX="0" + id="Tail" + style="overflow:visible"> + <g + id="g3883" + transform="scale(-1.2,-1.2)"> + <path + id="path3885" + d="M -3.8048674,-3.9585227 0.54352094,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3887" + d="M -1.2866832,-3.9585227 3.0617053,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3889" + d="M 1.3053582,-3.9585227 5.6537466,0" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3891" + d="M -3.8048674,4.1775838 0.54352094,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3893" + d="M -1.2866832,4.1775838 3.0617053,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + <path + id="path3895" + d="M 1.3053582,4.1775838 5.6537466,0.21974226" + style="fill:none;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round" + inkscape:connector-curvature="0" /> + </g> + </marker> + <marker + inkscape:stockid="Arrow2Sstart" + orient="auto" + refY="0" + refX="0" + id="Arrow2Sstart" + style="overflow:visible"> + <path + id="path3877" + style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + transform="matrix(0.3,0,0,0.3,-0.69,0)" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="DotL" + orient="auto" + refY="0" + refX="0" + id="DotL" + style="overflow:visible"> + <path + id="path3908" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" + transform="matrix(0.8,0,0,0.8,5.92,0.8)" + inkscape:connector-curvature="0" /> + </marker> + </defs> + <g + inkscape:groupmode="layer" + id="layer9" + inkscape:label="status" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer10" + inkscape:label="devices" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer11" + inkscape:label="apps" + style="display:inline" + transform="translate(-283.00021,-629)"> + <g + id="g3040" + transform="matrix(-1,0,0,1,581.99582,0)"> + <path + id="path3795-3" + d="m 283.60374,629.58458 0,12.86095 14.78834,-1.16918 0,-10.5226 -14.78834,-1.16917 z m 0.73172,0.65766 13.28639,1.05957 0,9.46303 -13.32491,1.02303 0.0385,-11.54563 z" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:1.20035386;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path3839-5" + d="m 290.99791,641.86094 -2e-5,2.33836" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:3.60105371;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path3841-2" + d="m 295.34001,643.66019 -8.13359,0.64305" + style="fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:#bebebe;stroke-width:1.32038927;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;display:inline" /> + </g> + </g> + <g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="on" + style="display:inline" /> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="off" + style="display:inline" /> + <g + inkscape:groupmode="layer" + id="layer13" + inkscape:label="places" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer14" + inkscape:label="mimetypes" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer15" + inkscape:label="emblems" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="g71291" + inkscape:label="emotes" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="g4953" + inkscape:label="categories" + style="display:inline" + transform="translate(-283.00021,-629)" /> + <g + inkscape:groupmode="layer" + id="layer12" + inkscape:label="actions" + style="display:inline" + transform="translate(-283.00021,-629)" /> +</svg> diff --git a/multi-monitors-add-on@spin83/indicator.js b/multi-monitors-add-on@spin83/indicator.js new file mode 100644 index 0000000..8b500ee --- /dev/null +++ b/multi-monitors-add-on@spin83/indicator.js @@ -0,0 +1,109 @@ +/* +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, Gio, GLib, GObject } = imports.gi; + +const Util = imports.misc.util; +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; + +const Gettext = imports.gettext.domain('multi-monitors-add-on'); +const _ = Gettext.gettext; +const CE = imports.misc.extensionUtils.getCurrentExtension(); +const MultiMonitors = CE.imports.extension; +const Convenience = CE.imports.convenience; +const extensionPath = CE.path; + +var MultiMonitorsIndicator = (() => { + let MultiMonitorsIndicator = class MultiMonitorsIndicator extends PanelMenu.Button { + _init() { + super._init(0.0, "MultiMonitorsAddOn", false); + + Convenience.initTranslations(); + + this.text = null; + this._mmStatusIcon = new St.BoxLayout({ style_class: 'multimonitor-status-indicators-box' }); + this._mmStatusIcon.hide(); + this.add_child(this._mmStatusIcon); + this._leftRightIcon = true; + this.menu.addAction(_("Preferences"), this._onPreferences.bind(this)); + this._viewMonitorsId = Main.layoutManager.connect('monitors-changed', this._viewMonitors.bind(this)); + this._viewMonitors(); + } + + _onDestroy() { + Main.layoutManager.disconnect(this._viewMonitorsId); + super._onDestroy(); + } + + _syncIndicatorsVisible() { + this._mmStatusIcon.visible = this._mmStatusIcon.get_children().some(a => a.visible); + } + + _icon_name (icon, iconName) { + icon.set_gicon(Gio.icon_new_for_string(extensionPath+"/icons/"+iconName+".svg")); + } + + _viewMonitors() { + let monitors = this._mmStatusIcon.get_children(); + + let monitorChange = Main.layoutManager.monitors.length - monitors.length; + if(monitorChange>0){ + global.log("Add Monitors ..."); + for(let idx = 0; idx<monitorChange; idx++){ + let icon; + icon = new St.Icon({style_class: 'system-status-icon multimonitor-status-icon'}); + this._mmStatusIcon.add_child(icon); + icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this)); + + if (this._leftRightIcon) + this._icon_name(icon, 'multi-monitors-l-symbolic'); + else + this._icon_name(icon, 'multi-monitors-r-symbolic'); + this._leftRightIcon = !this._leftRightIcon; + } + this._syncIndicatorsVisible(); + } + else if(monitorChange<0){ + global.log("Remove Monitors ..."); + monitorChange = -monitorChange; + + for(let idx = 0; idx<monitorChange; idx++){ + let icon = this._mmStatusIcon.get_last_child(); + this._mmStatusIcon.remove_child(icon); + icon.destroy(); + this._leftRightIcon = !this._leftRightIcon; + } + } + } + + _onPreferences() { + const uuid = "multi-monitors-add-on@spin83"; + Gio.DBus.session.call( + 'org.gnome.Shell.Extensions', + '/org/gnome/Shell/Extensions', + 'org.gnome.Shell.Extensions', + 'OpenExtensionPrefs', + new GLib.Variant('(ssa{sv})', [uuid, '', {}]), + null, + Gio.DBusCallFlags.NONE, + -1, + null); + } + }; + return GObject.registerClass(MultiMonitorsIndicator); +})(); diff --git a/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..ce5795c --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..0691ac2 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,95 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Multi Monitors Add On Gnome Shell Extension\n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: 2015-01-23 22:30+0100\n" +"Last-Translator: Jonatan Zeidler <jonatan_zeidler@gmx.de>\n" +"Language-Team: German <jonatan_zeidler@gmx.de>\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "Einstellungen" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "Multimonitor-Erweiterung" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Multimonitor-Indikator in der oberen Leiste anzeigen" + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "Leiste auf zusätzlichen Monitoren anzeigen" + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "Arbeitsflächenübersicht auf zusätzlichen Monitoren anzeigen" + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "Aktivitäten-Schaltfläche auf zusätzlichen Monitoren anzeigen" + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Anwendungsmenü auf zusätzlichen Monitoren anzeigen" + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Datum-Zeit auf zusätzlichen Monitoren anzeigen." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "" + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "" +"Eine Liste von Indikatoren, die auf die zusätzlichen Monitore verschoben " +"werden sollen" + +#: prefs.js:124 +msgid "Select indicator" +msgstr "Indikator auswählen" + +#: prefs.js:127 +msgid "Add" +msgstr "Hinzufügen" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "Indikatoren in der oberen Leiste" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "Monitorindex:" + +#~ msgid "Test" +#~ msgstr "Test" diff --git a/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..0cd6a92 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..6c51dca --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,91 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Alonso Lara <alonso.lara.plana@gmail.com>, 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: 2017-03-04 23:59+0100\n" +"Last-Translator: Alonso Lara <alonso.lara.plana@gmail.com>\n" +"Language-Team: Spanish <LL@li.org>\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "Preferencias" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "Accesorio de monitores múltiples" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Mostrar indicador de monitores múltiples en el panel." + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "Mostrar el panel en monitores adicionales." + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "Mostrar las miniaturas en los monitores adicionales." + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "Mostrar las actividades en los monitores adicionales." + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Mostrar el menú de aplicaciones en los monitores adicionales." + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Mostrar la fecha en los monitores adicionales." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "" + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "Un listado de indicadores para transferir a monitores adicionales." + +#: prefs.js:124 +msgid "Select indicator" +msgstr "Seleccione indicador" + +#: prefs.js:127 +msgid "Add" +msgstr "Añadir" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "Indicadores en el panel" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "Monitor número:" + +#~ msgid "Test" +#~ msgstr "Prueba" diff --git a/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..2aefbb2 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..3b207cc --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,94 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Multi Monitors Add On Gnome Shell Extension\n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: 2015-12-26 22:30+0100\n" +"Last-Translator: Quentin Daem\n" +"Language-Team: Language: fr_FR\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.5.4\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "Préférences" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "Multi Moniteurs Add-On" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Afficher l'icone Multi Moniteurs sur la barre du haut" + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "Afficher Menu sur les moniteurs secondaires" + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "" +"Afficher le dock listant les espaces de travail sur les moniteurs secondaires" + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "Afficher le bouton Activités sur les moniteurs secondaires" + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Afficher le bouton du menu Applications sur les moniteurs secondaires" + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Afficher le bouton Date-Heure sur les moniteurs secondaires." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "" + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "Une liste d'indicateurs pour transfert vers les moniteurs secondaires " + +#: prefs.js:124 +msgid "Select indicator" +msgstr "Selectionner indicateur" + +#: prefs.js:127 +msgid "Add" +msgstr "Ajouter" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "Indicateur dans le panneau du haut" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "Index moniteur:" + +#~ msgid "Test" +#~ msgstr "Test" diff --git a/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..14040b3 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..fd41a90 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,93 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Multi Monitors Add On Gnome Shell Extension\n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: 2019-10-21 14:53+0200\n" +"Last-Translator: Luca Bandini (@Vombato) <luca.vombato@gmail.com>\n" +"Language-Team: ItalianLanguage: it\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.2.4\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "Preferenze" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "Componente aggiuntivo per più monitor" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "Panoramica" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "Attività" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "Barra Superiore" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Mostra l'icona Multi Monitor sul pannello superiore." + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "Mostra Menu su monitor secondari." + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "Visualizza le aree di lavoro sui monitor secondari." + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "Mostra il Bottone Attività sui monitor secondari." + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Visualizza il pulsante del menu Applicazioni sui monitor secondari." + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Mostra il Bottone Data/Ora sui monitor secondari." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "" + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "Un elenco di indicatori per il trasferimento a monitor secondari." + +#: prefs.js:124 +msgid "Select indicator" +msgstr "Seleziona indicatore" + +#: prefs.js:127 +msgid "Add" +msgstr "Aggiungere" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "Indicatore nel pannello superiore" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "Indice monitor:" + +#~ msgid "Test" +#~ msgstr "Test" diff --git a/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..84f0f8e --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..80116d8 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,94 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: 2016-12-29 14:25+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: pl_PL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.11\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "Ustawienia" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "Multi Monitors Add-On" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Wyświetl wskaźnik rozszerzenia na głównym pasku." + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "Wyświetl główny pasek na dodatkowych monitorach." + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "Wyświetl pasek miniatur na dodatkowych monitorach." + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "Wyświetl przycisk podglądu na dodatkowych monitorach." + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Wyświetl przycisk aplikacji na dodatkowych monitorach." + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Wyświetl przycisk daty i czasu na dodatkowych monitorach." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "Włączenie podglądu po najechaniu rogu ekranu." + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "Lista wskaźników do przesunięcia na dodatkowe monitory." + +#: prefs.js:124 +msgid "Select indicator" +msgstr "Wybierz wskaźnik" + +#: prefs.js:127 +msgid "Add" +msgstr "Dodaj" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "Wskaźniki na głównym panelu" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "Indeks monitora:" + +#~ msgid "Test" +#~ msgstr "Test" diff --git a/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo b/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo Binary files differnew file mode 100644 index 0000000..beae2e9 --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo diff --git a/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po b/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po new file mode 100644 index 0000000..94c370c --- /dev/null +++ b/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po @@ -0,0 +1,89 @@ +# Brazilian Portuguese translation for multi-monitors-add-on +# Copyright (C) 2019 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the multi-monitors-add-on package. +# Rafael Fontenelle <rafaelff@gnome.org>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: Multi Monitors Add On Gnome Shell Extension\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-23 22:29+0100\n" +"PO-Revision-Date: 2019-10-03 06:49-0300\n" +"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n" +"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"X-Generator: Gtranslator 3.32.0\n" +"X-Project-Style: gnome\n" + +#: prefs.js:61 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "Mostrar o indicador do Multi Monitors no painel superior" + +#: prefs.js:62 +msgid "Show Panel on additional monitors." +msgstr "Mostrar o painel em monitores adicionais" + +#: prefs.js:63 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "Mostrar controle deslizante de miniaturas em monitores adicionais" + +#: prefs.js:64 +msgid "Show Activities-Button on additional monitors." +msgstr "Mostrar botão de Atividades em monitores adicionais" + +#: prefs.js:65 +msgid "Show AppMenu-Button on additional monitors." +msgstr "Mostrar botão de menu de aplicativos em monitores adicionais" + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "Mostrar o botão de data e hora em monitores adicionais" + +#: prefs.js:75 +msgid "A list of indicators for transfer to additional monitors." +msgstr "Uma lista de indicadores para transferir para monitores adicionais." + +#: prefs.js:71 +msgid "Enable hot corners." +msgstr "" + +#: prefs.js:122 +msgid "Select indicator" +msgstr "Selecionar indicador" + +#: prefs.js:125 +msgid "Add" +msgstr "Adicionar" + +#: prefs.js:139 +msgid "Indicators on Top Panel" +msgstr "Indicadores no painel superior" + +#: prefs.js:168 +msgid "Monitor index:" +msgstr "Índice do monitor:" + +#: indicator.js:106 +msgid "Preferences" +msgstr "Preferências" + +#: indicator.js:107 +msgid "Test" +msgstr "Testar" + +#: indicator.js:129 +msgid "Multi Monitors Add-On" +msgstr "Multi Monitors Add-On" + +#~ msgid "Overview" +#~ msgstr "Panorama" + +#~ msgid "Activities" +#~ msgstr "Atividades" + +#~ msgid "Top Bar" +#~ msgstr "Barra superior" diff --git a/multi-monitors-add-on@spin83/metadata.json b/multi-monitors-add-on@spin83/metadata.json new file mode 100644 index 0000000..d285459 --- /dev/null +++ b/multi-monitors-add-on@spin83/metadata.json @@ -0,0 +1,10 @@ +{ + "shell-version": ["3.38"], + "uuid": "multi-monitors-add-on@spin83", + "name": "Multi Monitors Add-On", + "settings-schema": "org.gnome.shell.extensions.multi-monitors-add-on", + "gettext-domain": "multi-monitors-add-on", + "description": "Add multiple monitors overview and panel for gnome-shell.", + "url": "https://github.com/spin83/multi-monitors-add-on.git", + "version": 23 +} diff --git a/multi-monitors-add-on@spin83/mmcalendar.js b/multi-monitors-add-on@spin83/mmcalendar.js new file mode 100644 index 0000000..8e3521b --- /dev/null +++ b/multi-monitors-add-on@spin83/mmcalendar.js @@ -0,0 +1,387 @@ +/* +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 Signals = imports.signals; + +const { St, Gio, Shell, Clutter, GnomeDesktop, Pango, GObject, GLib } = imports.gi; + +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +const MessageList = imports.ui.messageList; +const DateMenu = imports.ui.dateMenu; +const Calendar = imports.ui.calendar; + +const ExtensionUtils = imports.misc.extensionUtils; +const CE = ExtensionUtils.getCurrentExtension(); +const MultiMonitors = CE.imports.extension; +const Convenience = CE.imports.convenience; + +var MultiMonitorsCalendar = (() => { + let MultiMonitorsCalendar = class MultiMonitorsCalendar extends St.Widget { + _init () { + this._weekStart = Shell.util_get_week_start(); + this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' }); + + this._showWeekdateKeyId = this._settings.connect('changed::%s'.format(Calendar.SHOW_WEEKDATE_KEY), this._onSettingsChange.bind(this)); + this._useWeekdate = this._settings.get_boolean(Calendar.SHOW_WEEKDATE_KEY); + + this._headerFormatWithoutYear = _('%OB'); + this._headerFormat = _('%OB %Y'); + + // Start off with the current date + this._selectedDate = new Date(); + + this._shouldDateGrabFocus = false; + + super._init({ + style_class: 'calendar', + layout_manager: new Clutter.GridLayout(), + reactive: true, + }); + + this._buildHeader(); + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + this._settings.disconnect(this._showWeekdateKeyId); + } + }; + MultiMonitors.copyClass(Calendar.Calendar, MultiMonitorsCalendar); + return GObject.registerClass({ + Signals: { 'selected-date-changed': { param_types: [GLib.DateTime.$gtype] } }, + }, MultiMonitorsCalendar); +})(); + +var MultiMonitorsEventsSection = (() => { + let MultiMonitorsEventsSection = class MultiMonitorsEventsSection extends St.Button { + _init() { + super._init({ + style_class: 'events-button', + can_focus: true, + x_expand: true, + child: new St.BoxLayout({ + style_class: 'events-box', + vertical: true, + x_expand: true, + }), + }); + + this._startDate = null; + this._endDate = null; + + this._eventSource = null; + this._calendarApp = null; + + this._title = new St.Label({ + style_class: 'events-title', + }); + this.child.add_child(this._title); + + this._eventsList = new St.BoxLayout({ + style_class: 'events-list', + vertical: true, + x_expand: true, + }); + this.child.add_child(this._eventsList); + + this._appSys = Shell.AppSystem.get_default(); + this._appInstalledChangedId = this._appSys.connect('installed-changed', + this._appInstalledChanged.bind(this)); + this._appInstalledChanged(); + + this.connect('destroy', this._onDestroy.bind(this)); + this._appInstalledChanged(); + } + + _onDestroy() { + this._appSys.disconnect(this._appInstalledChangedId); + }}; + + MultiMonitors.copyClass(DateMenu.EventsSection, MultiMonitorsEventsSection); + return GObject.registerClass(MultiMonitorsEventsSection); +})(); + +var MultiMonitorsNotificationSection = (() => { + let MultiMonitorsNotificationSection = class MultiMonitorsNotificationSection extends MessageList.MessageListSection { + _init() { + super._init(); + + this._sources = new Map(); + this._nUrgent = 0; + + this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this)); + Main.messageTray.getSources().forEach(source => { + this._sourceAdded(Main.messageTray, source); + }); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + Main.messageTray.disconnect(this._sourceAddedId); + let source, obj; + for ([source, obj] of this._sources.entries()) { + this._onSourceDestroy(source, obj); + } + }}; + + MultiMonitors.copyClass(Calendar.NotificationSection, MultiMonitorsNotificationSection); + return GObject.registerClass(MultiMonitorsNotificationSection); +})(); + +var MultiMonitorsCalendarMessageList = (() => { + let MultiMonitorsCalendarMessageList = class MultiMonitorsCalendarMessageList extends St.Widget { + _init() { + super._init({ + style_class: 'message-list', + layout_manager: new Clutter.BinLayout(), + x_expand: true, + y_expand: true, + }); + + this._sessionModeUpdatedId = 0; + + this._placeholder = new Calendar.Placeholder(); + this.add_actor(this._placeholder); + + let box = new St.BoxLayout({ vertical: true, + x_expand: true, y_expand: true }); + this.add_actor(box); + + this._scrollView = new St.ScrollView({ + style_class: 'vfade', + overlay_scrollbars: true, + x_expand: true, y_expand: true, + }); + this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); + box.add_actor(this._scrollView); + + let hbox = new St.BoxLayout({ style_class: 'message-list-controls' }); + box.add_child(hbox); + + const dndLabel = new St.Label({ + text: _('Do Not Disturb'), + y_align: Clutter.ActorAlign.CENTER, + }); + hbox.add_child(dndLabel); + + this._dndSwitch = new Calendar.DoNotDisturbSwitch(); + this._dndButton = new St.Button({ + can_focus: true, + toggle_mode: true, + child: this._dndSwitch, + label_actor: dndLabel, + }); + + this._dndSwitch.bind_property('state', + this._dndButton, 'checked', + GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE); + + hbox.add_child(this._dndButton); + + this._clearButton = new St.Button({ + style_class: 'message-list-clear-button button', + label: _('Clear'), + can_focus: true, + x_expand: true, + x_align: Clutter.ActorAlign.END, + }); + this._clearButton.connect('clicked', () => { + this._sectionList.get_children().forEach(s => s.clear()); + }); + hbox.add_actor(this._clearButton); + + this._placeholder.bind_property('visible', + this._clearButton, 'visible', + GObject.BindingFlags.INVERT_BOOLEAN); + + this._sectionList = new St.BoxLayout({ style_class: 'message-list-sections', + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START }); + this._sectionList.connect('actor-added', this._sync.bind(this)); + this._sectionList.connect('actor-removed', this._sync.bind(this)); + this._scrollView.add_actor(this._sectionList); + + this._notificationSection = new MultiMonitorsNotificationSection(); + this._addSection(this._notificationSection); + + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sync.bind(this)); + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._sessionModeUpdatedId = 0; + } + + _sync() { + if (this._sessionModeUpdatedId === 0) return; + Calendar.CalendarMessageList.prototype._sync.call(this); + }}; + + MultiMonitors.copyClass(Calendar.CalendarMessageList, MultiMonitorsCalendarMessageList); + return GObject.registerClass(MultiMonitorsCalendarMessageList); +})(); + +var MultiMonitorsMessagesIndicator = (() => { + let MultiMonitorsMessagesIndicator = class MultiMonitorsMessagesIndicator extends St.Icon { + _init() { + super._init({ + icon_size: 16, + visible: false, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + + this._sources = []; + this._count = 0; + + this._settings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.notifications', + }); + this._settings.connect('changed::show-banners', this._sync.bind(this)); + + this._sourceAddedId = Main.messageTray.connect('source-added', this._onSourceAdded.bind(this)); + this._sourceRemovedId = Main.messageTray.connect('source-removed', this._onSourceRemoved.bind(this)); + this._queueChangedId = Main.messageTray.connect('queue-changed', this._updateCount.bind(this)); + + let sources = Main.messageTray.getSources(); + sources.forEach(source => this._onSourceAdded(null, source)); + + this._sync(); + + this.connect('destroy', () => { + this._settings.run_dispose(); + this._settings = null; + Main.messageTray.disconnect(this._sourceAddedId); + Main.messageTray.disconnect(this._sourceRemovedId); + Main.messageTray.disconnect(this._queueChangedId); + }); + }}; + + MultiMonitors.copyClass(DateMenu.MessagesIndicator, MultiMonitorsMessagesIndicator); + return GObject.registerClass(MultiMonitorsMessagesIndicator); +})(); + +var MultiMonitorsDateMenuButton = (() => { + let MultiMonitorsDateMenuButton = class MultiMonitorsDateMenuButton extends PanelMenu.Button { + _init() { + let hbox; + let vbox; + + super._init(0.5); + + this._clockDisplay = new St.Label({ style_class: 'clock' }); + this._clockDisplay.clutter_text.y_align = Clutter.ActorAlign.CENTER; + this._clockDisplay.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + + this._indicator = new MultiMonitorsMessagesIndicator(); + + const indicatorPad = new St.Widget(); + this._indicator.bind_property('visible', + indicatorPad, 'visible', + GObject.BindingFlags.SYNC_CREATE); + indicatorPad.add_constraint(new Clutter.BindConstraint({ + source: this._indicator, + coordinate: Clutter.BindCoordinate.SIZE, + })); + + let box = new St.BoxLayout({ style_class: 'clock-display-box' }); + box.add_actor(indicatorPad); + box.add_actor(this._clockDisplay); + box.add_actor(this._indicator); + + this.label_actor = this._clockDisplay; + this.add_actor(box); + this.add_style_class_name('clock-display'); + + let layout = new DateMenu.FreezableBinLayout(); + let bin = new St.Widget({ layout_manager: layout }); + // For some minimal compatibility with PopupMenuItem + bin._delegate = this; + this.menu.box.add_child(bin); + + hbox = new St.BoxLayout({ name: 'calendarArea' }); + bin.add_actor(hbox); + + this._calendar = new MultiMonitorsCalendar(); + this._calendar.connect('selected-date-changed', (_calendar, datetime) => { + let date = DateMenu._gDateTimeToDate(datetime); + layout.frozen = !DateMenu._isToday(date); + this._eventsItem.setDate(date); + }); + this._date = new DateMenu.TodayButton(this._calendar); + + this.menu.connect('open-state-changed', (menu, isOpen) => { + // Whenever the menu is opened, select today + if (isOpen) { + let now = new Date(); + this._calendar.setDate(now); + this._date.setDate(now); + this._eventsItem.setDate(now); + } + }); + + // Fill up the first column + this._messageList = new MultiMonitorsCalendarMessageList(); + hbox.add_child(this._messageList); + + // Fill up the second column + const boxLayout = new DateMenu.CalendarColumnLayout([this._calendar, this._date]); + vbox = new St.Widget({ style_class: 'datemenu-calendar-column', + layout_manager: boxLayout }); + boxLayout.hookup_style(vbox); + hbox.add(vbox); + + vbox.add_actor(this._date); + vbox.add_actor(this._calendar); + + this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade', + x_expand: true, + overlay_scrollbars: true }); + this._displaysSection.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + vbox.add_actor(this._displaysSection); + + let displaysBox = new St.BoxLayout({ vertical: true, + x_expand: true, + style_class: 'datemenu-displays-box' }); + this._displaysSection.add_actor(displaysBox); + + this._eventsItem = new MultiMonitorsEventsSection(); + displaysBox.add_child(this._eventsItem); + + this._clock = new GnomeDesktop.WallClock(); + this._clock.bind_property('clock', this._clockDisplay, 'text', GObject.BindingFlags.SYNC_CREATE); + this._clockNotifyTimezoneId = this._clock.connect('notify::timezone', this._updateTimeZone.bind(this)); + + this._sessionModeUpdatedId = Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + this._sessionUpdated(); + } + + _onDestroy() { + Main.sessionMode.disconnect(this._sessionModeUpdatedId); + this._clock.disconnect(this._clockNotifyTimezoneId); + super._onDestroy(); + }}; + + MultiMonitors.copyClass(DateMenu.DateMenuButton, MultiMonitorsDateMenuButton); + return GObject.registerClass(MultiMonitorsDateMenuButton); +})(); + diff --git a/multi-monitors-add-on@spin83/mmlayout.js b/multi-monitors-add-on@spin83/mmlayout.js new file mode 100644 index 0000000..a354ec1 --- /dev/null +++ b/multi-monitors-add-on@spin83/mmlayout.js @@ -0,0 +1,251 @@ +/** + * New node file + */ + +const { St, Meta } = imports.gi; + +const Main = imports.ui.main; +const Panel = imports.ui.panel; +const Layout = imports.ui.layout; + +const Config = imports.misc.config; + +const ExtensionUtils = imports.misc.extensionUtils; +const CE = ExtensionUtils.getCurrentExtension(); +const Convenience = CE.imports.convenience; +const MultiMonitors = CE.imports.extension; +const MMPanel = CE.imports.mmpanel; + +var SHOW_PANEL_ID = 'show-panel'; +var ENABLE_HOT_CORNERS = 'enable-hot-corners'; + +const MultiMonitorsPanelBox = class MultiMonitorsPanelBox { + constructor(monitor) { + this.panelBox = new St.BoxLayout({ name: 'panelBox', vertical: true, clip_to_allocation: true }); + Main.layoutManager.addChrome(this.panelBox, { affectsStruts: true, trackFullscreen: true }); + this.panelBox.set_position(monitor.x, monitor.y); + this.panelBox.set_size(monitor.width, -1); + Main.uiGroup.set_child_below_sibling(this.panelBox, Main.layoutManager.panelBox); + } + + destroy() { + this.panelBox.destroy(); + } + + updatePanel(monitor) { + this.panelBox.set_position(monitor.x, monitor.y); + this.panelBox.set_size(monitor.width, -1); + } +}; + +var MultiMonitorsLayoutManager = class MultiMonitorsLayoutManager { + constructor() { + this._settings = Convenience.getSettings(); + this._desktopSettings = Convenience.getSettings("org.gnome.desktop.interface"); + + Main.mmPanel = []; + + this._monitorIds = []; + this.mmPanelBox = []; + this.mmappMenu = false; + + this._showAppMenuId = null; + this._monitorsChangedId = null; + + this.statusIndicatorsController = null; + this._layoutManager_updateHotCorners = null; + this._changedEnableHotCornersId = null; + } + + showPanel() { + if (this._settings.get_boolean(SHOW_PANEL_ID)) { + if (!this._monitorsChangedId) { + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', this._monitorsChanged.bind(this)); + this._monitorsChanged(); + } + if (!this._showAppMenuId) { + this._showAppMenuId = this._settings.connect('changed::'+MMPanel.SHOW_APP_MENU_ID, this._showAppMenu.bind(this)); + } + + if (!this.statusIndicatorsController) { + this.statusIndicatorsController = new MMPanel.StatusIndicatorsController(); + } + + if (!this._layoutManager_updateHotCorners) { + this._layoutManager_updateHotCorners = Main.layoutManager._updateHotCorners; + + const _this = this; + Main.layoutManager._updateHotCorners = function() { + this.hotCorners.forEach((corner) => { + if (corner) + corner.destroy(); + }); + this.hotCorners = []; + + if (!_this._desktopSettings.get_boolean(ENABLE_HOT_CORNERS)) { + this.emit('hot-corners-changed'); + return; + } + + let size = this.panelBox.height; + + for (let i = 0; i < this.monitors.length; i++) { + let monitor = this.monitors[i]; + let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; + let cornerY = monitor.y; + + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); + corner.setBarrierSize(size); + this.hotCorners.push(corner); + } + + this.emit('hot-corners-changed'); + }; + + if (!this._changedEnableHotCornersId) { + this._changedEnableHotCornersId = this._desktopSettings.connect('changed::'+ENABLE_HOT_CORNERS, + Main.layoutManager._updateHotCorners.bind(Main.layoutManager)); + } + + Main.layoutManager._updateHotCorners(); + } + } + else { + this.hidePanel(); + } + } + + hidePanel() { + if (this._changedEnableHotCornersId) { + global.settings.disconnect(this._changedEnableHotCornersId); + this._changedEnableHotCornersId = null; + } + + if (this._layoutManager_updateHotCorners) { + Main.layoutManager['_updateHotCorners'] = this._layoutManager_updateHotCorners; + this._layoutManager_updateHotCorners = null; + Main.layoutManager._updateHotCorners(); + } + + if (this.statusIndicatorsController) { + this.statusIndicatorsController.destroy(); + this.statusIndicatorsController = null; + } + + if (this._showAppMenuId) { + this._settings.disconnect(this._showAppMenuId); + this._showAppMenuId = null; + } + this._hideAppMenu(); + + if (this._monitorsChangedId) { + Main.layoutManager.disconnect(this._monitorsChangedId); + this._monitorsChangedId = null; + } + + let panels2remove = this._monitorIds.length; + for (let i = 0; i < panels2remove; i++) { + let monitorId = this._monitorIds.pop(); + this._popPanel(); + global.log("remove: "+monitorId); + } + } + + _monitorsChanged () { + let monitorChange = Main.layoutManager.monitors.length - this._monitorIds.length -1; + if (monitorChange<0) { + for (let idx = 0; idx<-monitorChange; idx++) { + let monitorId = this._monitorIds.pop(); + this._popPanel(); + global.log("remove: "+monitorId); + } + } + + let j = 0; + let tIndicators = false; + for (let i = 0; i < Main.layoutManager.monitors.length; i++) { + if (i!=Main.layoutManager.primaryIndex) { + let monitor = Main.layoutManager.monitors[i]; + let monitorId = "i"+i+"x"+monitor.x+"y"+monitor.y+"w"+monitor.width+"h"+monitor.height; + if (monitorChange>0 && j==this._monitorIds.length) { + this._monitorIds.push(monitorId); + this._pushPanel(i, monitor); + global.log("new: "+monitorId); + tIndicators = true; + } + else if (this._monitorIds[j]>monitorId || this._monitorIds[j]<monitorId) { + let oldMonitorId = this._monitorIds[j]; + this._monitorIds[j]=monitorId; + this.mmPanelBox[j].updatePanel(monitor); + global.log("update: "+oldMonitorId+">"+monitorId); + } + j++; + } + } + this._showAppMenu(); + if (tIndicators && this.statusIndicatorsController) { + this.statusIndicatorsController.transferIndicators(); + } + } + + _pushPanel(i, monitor) { + let mmPanelBox = new MultiMonitorsPanelBox(monitor); + let panel = new MMPanel.MultiMonitorsPanel(i, mmPanelBox); + + Main.mmPanel.push(panel); + this.mmPanelBox.push(mmPanelBox); + } + + _popPanel() { + let panel = Main.mmPanel.pop(); + if (this.statusIndicatorsController) { + this.statusIndicatorsController.transferBack(panel); + } + let mmPanelBox = this.mmPanelBox.pop(); + mmPanelBox.destroy(); + } + + _changeMainPanelAppMenuButton(appMenuButton) { + let role = "appMenu"; + let panel = Main.panel; + let indicator = panel.statusArea[role]; + panel.menuManager.removeMenu(indicator.menu); + indicator.destroy(); + if (indicator._actionGroupNotifyId) { + indicator._targetApp.disconnect(indicator._actionGroupNotifyId); + indicator._actionGroupNotifyId = 0; + } + if (indicator._busyNotifyId) { + indicator._targetApp.disconnect(indicator._busyNotifyId); + indicator._busyNotifyId = 0; + } + if (indicator.menu._windowsChangedId) { + indicator.menu._app.disconnect(indicator.menu._windowsChangedId); + indicator.menu._windowsChangedId = 0; + } + indicator = new appMenuButton(panel); + panel.statusArea[role] = indicator; + let box = panel._leftBox; + panel._addToPanelBox(role, indicator, box.get_n_children()+1, box); + } + + _showAppMenu() { + if (this._settings.get_boolean(MMPanel.SHOW_APP_MENU_ID) && Main.mmPanel.length>0) { + if (!this.mmappMenu) { + this._changeMainPanelAppMenuButton(MMPanel.MultiMonitorsAppMenuButton); + this.mmappMenu = true; + } + } + else { + this._hideAppMenu(); + } + } + + _hideAppMenu() { + if (this.mmappMenu) { + this._changeMainPanelAppMenuButton(Panel.AppMenuButton); + this.mmappMenu = false; + Main.panel._updatePanel() + } + } +}; diff --git a/multi-monitors-add-on@spin83/mmoverview.js b/multi-monitors-add-on@spin83/mmoverview.js new file mode 100644 index 0000000..b688452 --- /dev/null +++ b/multi-monitors-add-on@spin83/mmoverview.js @@ -0,0 +1,669 @@ +/* +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 { Clutter, GObject, St, Shell, GLib, Gio, Meta } = imports.gi; + +const Main = imports.ui.main; +const Params = imports.misc.params; +const WorkspaceThumbnail = imports.ui.workspaceThumbnail; +const OverviewControls = imports.ui.overviewControls; +const Overview = imports.ui.overview; +const ViewSelector = imports.ui.viewSelector; +const LayoutManager = imports.ui.layout; +const Background = imports.ui.background; +const WorkspacesView = imports.ui.workspacesView; + +const ExtensionUtils = imports.misc.extensionUtils; +const CE = ExtensionUtils.getCurrentExtension(); +const MultiMonitors = CE.imports.extension; +const Convenience = CE.imports.convenience; + +const THUMBNAILS_SLIDER_POSITION_ID = 'thumbnails-slider-position'; + +var MultiMonitorsWorkspaceThumbnail = (() => { + let MultiMonitorsWorkspaceThumbnail = class MultiMonitorsWorkspaceThumbnail extends St.Widget { + _init(metaWorkspace, monitorIndex) { + super._init({ + clip_to_allocation: true, + style_class: 'workspace-thumbnail', + }); + this._delegate = this; + + this.metaWorkspace = metaWorkspace; + this.monitorIndex = monitorIndex; + + this._removed = false; + + this._contents = new Clutter.Actor(); + this.add_child(this._contents); + + this.connect('destroy', this._onDestroy.bind(this)); + + this._createBackground(); + + let workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex); + this.setPorthole(workArea.x, workArea.y, workArea.width, workArea.height); + + let windows = global.get_window_actors().filter(actor => { + let win = actor.meta_window; + return win.located_on_workspace(metaWorkspace); + }); + + // Create clones for windows that should be visible in the Overview + this._windows = []; + this._allWindows = []; + this._minimizedChangedIds = []; + for (let i = 0; i < windows.length; i++) { + let minimizedChangedId = + windows[i].meta_window.connect('notify::minimized', + this._updateMinimized.bind(this)); + this._allWindows.push(windows[i].meta_window); + this._minimizedChangedIds.push(minimizedChangedId); + + if (this._isMyWindow(windows[i]) && this._isOverviewWindow(windows[i])) + this._addWindowClone(windows[i]); + } + + // Track window changes + this._windowAddedId = this.metaWorkspace.connect('window-added', + this._windowAdded.bind(this)); + this._windowRemovedId = this.metaWorkspace.connect('window-removed', + this._windowRemoved.bind(this)); + this._windowEnteredMonitorId = global.display.connect('window-entered-monitor', + this._windowEnteredMonitor.bind(this)); + this._windowLeftMonitorId = global.display.connect('window-left-monitor', + this._windowLeftMonitor.bind(this)); + + this.state = WorkspaceThumbnail.ThumbnailState.NORMAL; + this._slidePosition = 0; // Fully slid in + this._collapseFraction = 0; // Not collapsed + } + + _createBackground() { + this._bgManager = new Background.BackgroundManager({ monitorIndex: this.monitorIndex, + container: this._contents, + vignette: false }); + }}; + + MultiMonitors.copyClass(WorkspaceThumbnail.WorkspaceThumbnail, MultiMonitorsWorkspaceThumbnail); + return GObject.registerClass({ + Properties: { + 'collapse-fraction': GObject.ParamSpec.double( + 'collapse-fraction', 'collapse-fraction', 'collapse-fraction', + GObject.ParamFlags.READWRITE, + 0, 1, 0), + 'slide-position': GObject.ParamSpec.double( + 'slide-position', 'slide-position', 'slide-position', + GObject.ParamFlags.READWRITE, + 0, 1, 0), + }, + }, MultiMonitorsWorkspaceThumbnail); +})(); + +const MultiMonitorsThumbnailsBox = (() => { + let MultiMonitorsThumbnailsBox = class MultiMonitorsThumbnailsBox extends St.Widget { + _init(scrollAdjustment, monitorIndex) { + + super._init({ reactive: true, + style_class: 'workspace-thumbnails', + request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT }); + + this._delegate = this; + this._monitorIndex = monitorIndex; + + let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' }); + + // We don't want the indicator to affect drag-and-drop + Shell.util_set_hidden_from_pick(indicator, true); + + this._indicator = indicator; + this.add_actor(indicator); + + // The porthole is the part of the screen we're showing in the thumbnails + this._porthole = { width: global.stage.width, height: global.stage.height, + x: global.stage.x, y: global.stage.y }; + + this._dropWorkspace = -1; + this._dropPlaceholderPos = -1; + this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' }); + this.add_actor(this._dropPlaceholder); + this._spliceIndex = -1; + + this._targetScale = 0; + this._scale = 0; + this._pendingScaleUpdate = false; + this._stateUpdateQueued = false; + this._animatingIndicator = false; + + this._stateCounts = {}; + for (let key in WorkspaceThumbnail.ThumbnailState) + this._stateCounts[WorkspaceThumbnail.ThumbnailState[key]] = 0; + + this._thumbnails = []; + + this._showingId = Main.overview.connect('showing', + this._createThumbnails.bind(this)); + this._hiddenId = Main.overview.connect('hidden', + this._destroyThumbnails.bind(this)); + + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + this._onDragBegin.bind(this)); + this._itemDragEndId = Main.overview.connect('item-drag-end', + this._onDragEnd.bind(this)); + this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled', + this._onDragCancelled.bind(this)); + this._windowDragBeginId = Main.overview.connect('window-drag-begin', + this._onDragBegin.bind(this)); + this._windowDragEndId = Main.overview.connect('window-drag-end', + this._onDragEnd.bind(this)); + this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled', + this._onDragCancelled.bind(this)); + + this._settings = new Gio.Settings({ schema_id: WorkspaceThumbnail.MUTTER_SCHEMA }); + this._changedDynamicWorkspacesId = this._settings.connect('changed::dynamic-workspaces', + this._updateSwitcherVisibility.bind(this)); + + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => { + this._destroyThumbnails(); + if (Main.overview.visible) + this._createThumbnails(); + }); + + this._workareasChangedPortholeId = global.display.connect('workareas-changed', + this._updatePorthole.bind(this)); + + this._switchWorkspaceNotifyId = 0; + this._nWorkspacesNotifyId = 0; + this._syncStackingId = 0; + this._workareasChangedId = 0; + + this._scrollAdjustment = scrollAdjustment; + + this._scrollAdjustmentNotifyValueId = this._scrollAdjustment.connect('notify::value', adj => { + let workspaceManager = global.workspace_manager; + let activeIndex = workspaceManager.get_active_workspace_index(); + + this._animatingIndicator = adj.value !== activeIndex; + + if (!this._animatingIndicator) + this._queueUpdateStates(); + + this.queue_relayout(); + }); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + this._destroyThumbnails(); + this._scrollAdjustment.disconnect(this._scrollAdjustmentNotifyValueId); + Main.overview.disconnect(this._showingId); + Main.overview.disconnect(this._hiddenId); + + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + Main.overview.disconnect(this._itemDragCancelledId); + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragEndId); + Main.overview.disconnect(this._windowDragCancelledId); + + this._settings.disconnect(this._changedDynamicWorkspacesId); + Main.layoutManager.disconnect(this._monitorsChangedId); + global.display.disconnect(this._workareasChangedPortholeId); + } + + addThumbnails(start, count) { + let workspaceManager = global.workspace_manager; + + for (let k = start; k < start + count; k++) { + let metaWorkspace = workspaceManager.get_workspace_by_index(k); + let thumbnail = new MultiMonitorsWorkspaceThumbnail(metaWorkspace, this._monitorIndex); + thumbnail.setPorthole(this._porthole.x, this._porthole.y, + this._porthole.width, this._porthole.height); + this._thumbnails.push(thumbnail); + this.add_actor(thumbnail); + + if (start > 0 && this._spliceIndex == -1) { + // not the initial fill, and not splicing via DND + thumbnail.state = WorkspaceThumbnail.ThumbnailState.NEW; + thumbnail.slide_position = 1; // start slid out + this._haveNewThumbnails = true; + } else { + thumbnail.state = WorkspaceThumbnail.ThumbnailState.NORMAL; + } + + this._stateCounts[thumbnail.state]++; + } + + this._queueUpdateStates(); + + // The thumbnails indicator actually needs to be on top of the thumbnails + this.set_child_above_sibling(this._indicator, null); + + // Clear the splice index, we got the message + this._spliceIndex = -1; + } + + _updatePorthole() { + this._porthole = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex); + this.queue_relayout(); + }}; + + MultiMonitors.copyClass(WorkspaceThumbnail.ThumbnailsBox, MultiMonitorsThumbnailsBox); + return GObject.registerClass({ + Properties: { + 'indicator-y': GObject.ParamSpec.double( + 'indicator-y', 'indicator-y', 'indicator-y', + GObject.ParamFlags.READWRITE, + 0, Infinity, 0), + 'scale': GObject.ParamSpec.double( + 'scale', 'scale', 'scale', + GObject.ParamFlags.READWRITE, + 0, Infinity, 0), + }, + }, MultiMonitorsThumbnailsBox); +})(); + +var MultiMonitorsSlidingControl = (() => { + let MultiMonitorsSlidingControl = class MultiMonitorsSlidingControl extends St.Widget { + _init(params) { + params = Params.parse(params, { slideDirection: OverviewControls.SlideDirection.LEFT }); + + this.layout = new OverviewControls.SlideLayout(); + this.layout.slideDirection = params.slideDirection; + super._init({ + layout_manager: this.layout, + style_class: 'overview-controls', + clip_to_allocation: true, + }); + + this._visible = true; + this._inDrag = false; + + this.connect('destroy', this._onDestroy.bind(this)); + this._hidingId = Main.overview.connect('hiding', this._onOverviewHiding.bind(this)); + + this._itemDragBeginId = Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this)); + this._itemDragEndId = Main.overview.connect('item-drag-end', this._onDragEnd.bind(this)); + this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled', this._onDragEnd.bind(this)); + + this._windowDragBeginId = Main.overview.connect('window-drag-begin', this._onWindowDragBegin.bind(this)); + this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled', this._onWindowDragEnd.bind(this)); + this._windowDragEndId = Main.overview.connect('window-drag-end', this._onWindowDragEnd.bind(this)); + } + + _onDestroy() { + Main.overview.disconnect(this._hidingId); + + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + Main.overview.disconnect(this._itemDragCancelledId); + + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragCancelledId); + Main.overview.disconnect(this._windowDragEndId); + }}; + + MultiMonitors.copyClass(OverviewControls.SlidingControl, MultiMonitorsSlidingControl); + return GObject.registerClass(MultiMonitorsSlidingControl); +})(); + +var MultiMonitorsThumbnailsSlider = (() => { + let MultiMonitorsThumbnailsSlider = class MultiMonitorsThumbnailsSlider extends MultiMonitorsSlidingControl { + _init(thumbnailsBox) { + super._init({ slideDirection: OverviewControls.SlideDirection.RIGHT }); + + this._thumbnailsBox = thumbnailsBox; + + this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; + this.reactive = true; + this.track_hover = true; + this.add_actor(this._thumbnailsBox); + + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this)); + this._activeWorkspaceChangedId = global.workspace_manager.connect('active-workspace-changed', + this._updateSlide.bind(this)); + this._notifyNWorkspacesId = global.workspace_manager.connect('notify::n-workspaces', + this._updateSlide.bind(this)); + this.connect('notify::hover', this._updateSlide.bind(this)); + this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE); + } + + _onDestroy() { + global.workspace_manager.disconnect(this._activeWorkspaceChangedId); + global.workspace_manager.disconnect(this._notifyNWorkspacesId); + Main.layoutManager.disconnect(this._monitorsChangedId); + super._onDestroy(); + }}; + + MultiMonitors.copyClass(OverviewControls.ThumbnailsSlider, MultiMonitorsThumbnailsSlider); + return GObject.registerClass(MultiMonitorsThumbnailsSlider); +})(); + +var MultiMonitorsControlsManager = GObject.registerClass( +class MultiMonitorsControlsManager extends St.Widget { + _init(index) { + this._monitorIndex = index; + this._workspacesViews = null; + this._spacer_height = 0; + this._fixGeometry = 0; + this._visible = false; + + let layout = new OverviewControls.ControlsLayout(); + super._init({ + layout_manager: layout, + x_expand: true, + y_expand: true, + clip_to_allocation: true, + }); + + this._workspaceAdjustment = Main.overview._overview._controls._workspaceAdjustment; + + this._thumbnailsBox = + new MultiMonitorsThumbnailsBox(this._workspaceAdjustment, this._monitorIndex); + this._thumbnailsSlider = new MultiMonitorsThumbnailsSlider(this._thumbnailsBox); + + this._viewSelector = new St.Widget({ visible: false, x_expand: true, y_expand: true, clip_to_allocation: true }); + this._pageChangedId = Main.overview.viewSelector.connect('page-changed', this._setVisibility.bind(this)); + this._pageEmptyId = Main.overview.viewSelector.connect('page-empty', this._onPageEmpty.bind(this)); + + this._group = new St.BoxLayout({ name: 'mm-overview-group-'+index, + x_expand: true, y_expand: true }); + this.add_actor(this._group); + + this._group.add_child(this._viewSelector); + this._group.add_actor(this._thumbnailsSlider); + + this._settings = Convenience.getSettings(); + + this._monitorsChanged(); + this._thumbnailsSlider.slideOut(); + this._thumbnailsBox._updatePorthole(); + + this.connect('notify::allocation', this._updateSpacerVisibility.bind(this)); + this.connect('destroy', this._onDestroy.bind(this)); + this._thumbnailsSelectSideId = this._settings.connect('changed::'+THUMBNAILS_SLIDER_POSITION_ID, + this._thumbnailsSelectSide.bind(this)); + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', this._monitorsChanged.bind(this)); + } + + _onDestroy() { + Main.overview.viewSelector.disconnect(this._pageChangedId); + Main.overview.viewSelector.disconnect(this._pageEmptyId); + this._settings.disconnect(this._thumbnailsSelectSideId); + Main.layoutManager.disconnect(this._monitorsChangedId); + } + + _monitorsChanged() { + this._primaryMonitorOnTheLeft = Main.layoutManager.monitors[this._monitorIndex].x > Main.layoutManager.primaryMonitor.x; + this._thumbnailsSelectSide(); + } + + _thumbnailsSelectSide() { + let thumbnailsSlider; + thumbnailsSlider = this._thumbnailsSlider; + + let sett = this._settings.get_string(THUMBNAILS_SLIDER_POSITION_ID); + let onLeftSide = sett === 'left' || (sett === 'auto' && this._primaryMonitorOnTheLeft); + + if (onLeftSide) { + let first = this._group.get_first_child(); + if (first != thumbnailsSlider) { + this._thumbnailsSlider.layout.slideDirection = OverviewControls.SlideDirection.LEFT; + this._thumbnailsBox.remove_style_class_name('workspace-thumbnails'); + this._thumbnailsBox.set_style_class_name('workspace-thumbnails workspace-thumbnails-left'); + this._group.set_child_below_sibling(thumbnailsSlider, first) + } + } + else { + let last = this._group.get_last_child(); + if (last != thumbnailsSlider) { + this._thumbnailsSlider.layout.slideDirection = OverviewControls.SlideDirection.RIGHT; + this._thumbnailsBox.remove_style_class_name('workspace-thumbnails workspace-thumbnails-left'); + this._thumbnailsBox.set_style_class_name('workspace-thumbnails'); + this._group.set_child_above_sibling(thumbnailsSlider, last); + } + } + this._fixGeometry = 3; + } + + _updateSpacerVisibility() { + if (Main.layoutManager.monitors.length<this._monitorIndex) + return; + + let top_spacer_height = Main.layoutManager.primaryMonitor.height; + + let panelGhost_height = 0; + if (Main.mmOverview[this._monitorIndex]._overview._panelGhost) + panelGhost_height = Main.mmOverview[this._monitorIndex]._overview._panelGhost.get_height(); + + let allocation = Main.overview._overview._controls.allocation; + let primaryControl_height = allocation.get_height(); + let bottom_spacer_height = Main.layoutManager.primaryMonitor.height - allocation.y2; + + top_spacer_height -= primaryControl_height + panelGhost_height + bottom_spacer_height; + top_spacer_height = Math.round(top_spacer_height); + + let spacer = Main.mmOverview[this._monitorIndex]._overview._spacer; + if (spacer.get_height()!=top_spacer_height) { + this._spacer_height = top_spacer_height; + spacer.set_height(top_spacer_height); + } + } + + getWorkspacesActualGeometry() { + let geometry; + if (this._visible) { + const [x, y] = this._viewSelector.get_transformed_position(); + const width = this._viewSelector.allocation.get_width(); + const height = this._viewSelector.allocation.get_height(); + geometry = { x, y, width, height }; + } + else { + let [x, y] = this.get_transformed_position(); + const width = this.allocation.get_width(); + let height = this.allocation.get_height(); + y -= this._spacer_height; + height += this._spacer_height; + geometry = { x, y, width, height }; + } + if (isNaN(geometry.x)) + return null; + //global.log("actualG+ i: "+this._monitorIndex+" x: "+geometry.x+" y: "+geometry.y+" width: "+geometry.width+" height: "+geometry.height); + return geometry; + } + + _setVisibility() { + // Ignore the case when we're leaving the overview, since + // actors will be made visible again when entering the overview + // next time, and animating them while doing so is just + // unnecessary noise + if (!Main.overview.visible || + (Main.overview.animationInProgress && !Main.overview.visibleTarget)) + return; + + let activePage = Main.overview.viewSelector.getActivePage(); + let thumbnailsVisible = activePage == ViewSelector.ViewPage.WINDOWS; + + let opacity = null; + if (thumbnailsVisible) { + opacity = 255; + if (this._fixGeometry===1) + this._fixGeometry = 0; + this._thumbnailsSlider.slideIn(); + } + else { + opacity = 0; + this._fixGeometry = 1; + this._thumbnailsSlider.slideOut(); + } + + if (!this._workspacesViews) + return; + + this._workspacesViews.ease({ + opacity: opacity, + duration: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + _onPageEmpty() { + this._thumbnailsSlider.pageEmpty(); + } + + show() { + this._viewSelector.visible = true; + this._workspacesViews = Main.overview.viewSelector._workspacesDisplay._workspacesViews[this._monitorIndex]; + this._visible = true; + const geometry = this.getWorkspacesActualGeometry(); + + if (!geometry) { + this._fixGeometry = 0; + return; + } + + if (this._fixGeometry) { + const width = this._thumbnailsSlider.get_width(); + if (this._fixGeometry===2) { + geometry.width = geometry.width-width; + if (this._thumbnailsSlider.layout.slideDirection === OverviewControls.SlideDirection.LEFT) + geometry.x = geometry.x + width; + } + else if (this._fixGeometry===3) { + if (this._thumbnailsSlider.layout.slideDirection === OverviewControls.SlideDirection.LEFT) + geometry.x = geometry.x + width; + else + geometry.x = geometry.x - width; + } + this._fixGeometry = 0; + } + + this._workspacesViews.ease({ + ...geometry, + duration: Main.overview.animationInProgress ? Overview.ANIMATION_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + + hide() { + this._visible = false; + this._workspacesViews.opacity = 255; + if (this._fixGeometry===1) + this._fixGeometry = 2; + const geometry = this.getWorkspacesActualGeometry(); + this._workspacesViews.ease({ + ...geometry, + duration: Main.overview.animationInProgress ? Overview.ANIMATION_TIME : 0, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this._viewSelector.visible = false; + }, + }); + this._workspacesViews = null; + } +}); + +var MultiMonitorsOverviewActor = GObject.registerClass( +class MultiMonitorsOverviewActor extends St.BoxLayout { + _init(index) { + this._monitorIndex = index; + super._init({ + name: 'mm-overview-'+index, + /* Translators: This is the main view to select + activities. See also note for "Activities" string. */ + accessible_name: _("MMOverview@"+index), + vertical: true, + }); + + this.add_constraint(new LayoutManager.MonitorConstraint({ index: this._monitorIndex })); + + this._panelGhost = null; + if (Main.mmPanel) { + for (let idx in Main.mmPanel) { + if (Main.mmPanel[idx].monitorIndex !== this._monitorIndex) + continue + // Add a clone of the panel to the overview so spacing and such is + // automatic + this._panelGhost = new St.Bin({ + child: new Clutter.Clone({ source: Main.mmPanel[idx] }), + reactive: false, + opacity: 0, + }); + this.add_actor(this._panelGhost); + break; + } + } + + this._spacer = new St.Widget(); + this.add_actor(this._spacer); + + this._controls = new MultiMonitorsControlsManager(this._monitorIndex); + + // Add our same-line elements after the search entry + this.add_child(this._controls); + } +}); + + +var MultiMonitorsOverview = class MultiMonitorsOverview { + constructor(index) { + this.monitorIndex = index; + + this._initCalled = true; + this._overview = new MultiMonitorsOverviewActor(this.monitorIndex); + this._overview._delegate = this; + this._overview.connect('destroy', this._onDestroy.bind(this)); + Main.layoutManager.overviewGroup.add_child(this._overview); + + this._showingId = Main.overview.connect('showing', this._show.bind(this)); + this._hidingId = Main.overview.connect('hiding', this._hide.bind(this)); + } + + getWorkspacesActualGeometry() { + return this._overview._controls.getWorkspacesActualGeometry(); + } + + _onDestroy() { + Main.overview.disconnect(this._showingId); + Main.overview.disconnect(this._hidingId); + + Main.layoutManager.overviewGroup.remove_child(this._overview); + this._overview._delegate = null; + } + + _show() { + this._overview._controls.show(); + } + + _hide() { + this._overview._controls.hide(); + } + + destroy() { + this._overview.destroy(); + } + + addAction(action) { + this._overview.add_action(action); + } + + removeAction(action) { + if (action.get_actor()) + this._overview.remove_action(action); + } +}; diff --git a/multi-monitors-add-on@spin83/mmpanel.js b/multi-monitors-add-on@spin83/mmpanel.js new file mode 100644 index 0000000..bf46617 --- /dev/null +++ b/multi-monitors-add-on@spin83/mmpanel.js @@ -0,0 +1,526 @@ +/* +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._leftCorner = new Panel.PanelCorner(St.Side.LEFT); + this.add_child(this._leftCorner); + + this._rightCorner = new Panel.PanelCorner(St.Side.RIGHT); + this.add_child(this._rightCorner); + + 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); +})(); diff --git a/multi-monitors-add-on@spin83/multi-monitors-add-on.pot b/multi-monitors-add-on@spin83/multi-monitors-add-on.pot new file mode 100644 index 0000000..6b18906 --- /dev/null +++ b/multi-monitors-add-on@spin83/multi-monitors-add-on.pot @@ -0,0 +1,89 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the multi-monitors-add-on package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: multi-monitors-add-on\n" +"Report-Msgid-Bugs-To: https://github.com/spin83/multi-monitors-add-on/" +"issues\n" +"POT-Creation-Date: 2019-10-04 04:44-0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: indicator.js:46 +msgid "Preferences" +msgstr "" + +#: indicator.js:115 +msgid "Multi Monitors Add-On" +msgstr "" + +#: mmoverview.js:642 +msgid "Overview" +msgstr "" + +#. Translators: If there is no suitable word for "Activities" +#. in your language, you can use the word for "Overview". +#: mmpanel.js:333 +msgid "Activities" +msgstr "" + +#: mmpanel.js:414 +msgid "Top Bar" +msgstr "" + +#: prefs.js:62 +msgid "Show Multi Monitors indicator on Top Panel." +msgstr "" + +#: prefs.js:63 +msgid "Show Panel on additional monitors." +msgstr "" + +#: prefs.js:64 +msgid "Show Thumbnails-Slider on additional monitors." +msgstr "" + +#: prefs.js:65 +msgid "Show Activities-Button on additional monitors." +msgstr "" + +#: prefs.js:66 +msgid "Show AppMenu-Button on additional monitors." +msgstr "" + +#: prefs.js:67 +msgid "Show DateTime-Button on additional monitors." +msgstr "" + +#: prefs.js:68 +msgid "Show Thumbnails-Slider on left side of additional monitors." +msgstr "" + +#: prefs.js:77 +msgid "A list of indicators for transfer to additional monitors." +msgstr "" + +#: prefs.js:124 +msgid "Select indicator" +msgstr "" + +#: prefs.js:127 +msgid "Add" +msgstr "" + +#: prefs.js:141 +msgid "Indicators on Top Panel" +msgstr "" + +#: prefs.js:170 +msgid "Monitor index:" +msgstr "" diff --git a/multi-monitors-add-on@spin83/prefs.js b/multi-monitors-add-on@spin83/prefs.js new file mode 100644 index 0000000..a8d1b58 --- /dev/null +++ b/multi-monitors-add-on@spin83/prefs.js @@ -0,0 +1,279 @@ +/* +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 Lang = imports.lang; + +const GObject = imports.gi.GObject; +const Gdk = imports.gi.Gdk; +const Gtk = imports.gi.Gtk; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; + +const Gettext = imports.gettext.domain('multi-monitors-add-on'); +const _ = Gettext.gettext; + +const ExtensionUtils = imports.misc.extensionUtils; +const MultiMonitors = ExtensionUtils.getCurrentExtension(); +const Convenience = MultiMonitors.imports.convenience; + +const SHOW_INDICATOR_ID = 'show-indicator'; +const SHOW_PANEL_ID = 'show-panel'; +const SHOW_ACTIVITIES_ID = 'show-activities'; +const SHOW_APP_MENU_ID = 'show-app-menu'; +const SHOW_DATE_TIME_ID = 'show-date-time'; +const THUMBNAILS_SLIDER_POSITION_ID = 'thumbnails-slider-position'; +const AVAILABLE_INDICATORS_ID = 'available-indicators'; +const TRANSFER_INDICATORS_ID = 'transfer-indicators'; +const ENABLE_HOT_CORNERS = 'enable-hot-corners'; + +const Columns = { + INDICATOR_NAME: 0, + MONITOR_NUMBER: 1 +}; + + +var MultiMonitorsPrefsWidget = GObject.registerClass( +class MultiMonitorsPrefsWidget extends Gtk.Grid { + _init() { + super._init({ + margin: 6, + }); + + this.set_orientation(Gtk.Orientation.VERTICAL); + + this._settings = Convenience.getSettings(); + this._desktopSettings = Convenience.getSettings("org.gnome.desktop.interface"); + + this._screen = Gdk.Screen.get_default(); + + this._addBooleanSwitch(_('Show Multi Monitors indicator on Top Panel.'), SHOW_INDICATOR_ID); + this._addBooleanSwitch(_('Show Panel on additional monitors.'), SHOW_PANEL_ID); + this._addBooleanSwitch(_('Show Activities-Button on additional monitors.'), SHOW_ACTIVITIES_ID); + this._addBooleanSwitch(_('Show AppMenu-Button on additional monitors.'), SHOW_APP_MENU_ID); + this._addBooleanSwitch(_('Show DateTime-Button on additional monitors.'), SHOW_DATE_TIME_ID); + this._addComboBoxSwitch(_('Show Thumbnails-Slider on additional monitors.'), THUMBNAILS_SLIDER_POSITION_ID, { + none: _('No'), + right: _('On the right'), + left: _('On the left'), + auto: _('Auto') + }); + this._addSettingsBooleanSwitch(_('Enable hot corners.'), this._desktopSettings, ENABLE_HOT_CORNERS); + + this._store = new Gtk.ListStore(); + this._store.set_column_types([GObject.TYPE_STRING, GObject.TYPE_INT]); + + this._treeView = new Gtk.TreeView({ model: this._store, hexpand: true, vexpand: true }); + this._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE); + + let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.INDICATOR_NAME, + title: _("A list of indicators for transfer to additional monitors.") }); + + let nameRenderer = new Gtk.CellRendererText; + appColumn.pack_start(nameRenderer, true); + appColumn.add_attribute(nameRenderer, "text", Columns.INDICATOR_NAME); + + nameRenderer = new Gtk.CellRendererText; + appColumn.pack_start(nameRenderer, true); + appColumn.add_attribute(nameRenderer, "text", Columns.MONITOR_NUMBER); + + this._treeView.append_column(appColumn); + this.add(this._treeView); + + let toolbar = new Gtk.Toolbar(); + toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR); + + this._settings.connect('changed::'+TRANSFER_INDICATORS_ID, Lang.bind(this, this._updateIndicators)); + this._updateIndicators(); + + let addTButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_ADD }); + addTButton.connect('clicked', Lang.bind(this, this._addIndicator)); + toolbar.add(addTButton); + + let removeTButton = new Gtk.ToolButton({ stock_id: Gtk.STOCK_REMOVE }); + removeTButton.connect('clicked', Lang.bind(this, this._removeIndicator)); + toolbar.add(removeTButton); + + this.add(toolbar); + } + + _updateIndicators() { + this._store.clear(); + + let transfers = this._settings.get_value(TRANSFER_INDICATORS_ID).deep_unpack(); + + for(let indicator in transfers) { + if(transfers.hasOwnProperty(indicator)){ + let monitor = transfers[indicator]; + let iter = this._store.append(); + this._store.set(iter, [Columns.INDICATOR_NAME, Columns.MONITOR_NUMBER], [indicator, monitor]); + } + } + } + + _addIndicator() { + + let dialog = new Gtk.Dialog({ title: _("Select indicator"), + transient_for: this.get_toplevel(), modal: true }); + dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL); + dialog.add_button(_("Add"), Gtk.ResponseType.OK); + dialog.set_default_response(Gtk.ResponseType.OK); + + let grid = new Gtk.Grid({ column_spacing: 10, row_spacing: 15, margin: 10 }); + + grid.set_orientation(Gtk.Orientation.VERTICAL); + + dialog._store = new Gtk.ListStore(); + dialog._store.set_column_types([GObject.TYPE_STRING]); + + dialog._treeView = new Gtk.TreeView({ model: dialog._store, hexpand: true, vexpand: true }); + dialog._treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE); + + let appColumn = new Gtk.TreeViewColumn({ expand: true, sort_column_id: Columns.INDICATOR_NAME, + title: _("Indicators on Top Panel") }); + + let nameRenderer = new Gtk.CellRendererText; + appColumn.pack_start(nameRenderer, true); + appColumn.add_attribute(nameRenderer, "text", Columns.INDICATOR_NAME); + + dialog._treeView.append_column(appColumn); + + let availableIndicators = () => { + let transfers = this._settings.get_value(TRANSFER_INDICATORS_ID).unpack(); + dialog._store.clear(); + this._settings.get_strv(AVAILABLE_INDICATORS_ID).forEach((indicator) => { + if(!transfers.hasOwnProperty(indicator)){ + let iter = dialog._store.append(); + dialog._store.set(iter, [Columns.INDICATOR_NAME], [indicator]); + } + }); + }; + + let availableIndicatorsId = this._settings.connect('changed::'+AVAILABLE_INDICATORS_ID, + availableIndicators); + let transferIndicatorsId = this._settings.connect('changed::'+TRANSFER_INDICATORS_ID, + availableIndicators); + + availableIndicators.apply(this); +// grid.attach(dialog._treeView, 0, 0, 2, 1); + grid.add(dialog._treeView); + + let gHBox = new Gtk.HBox({margin: 10, spacing: 20, hexpand: true}); + let gLabel = new Gtk.Label({label: _('Monitor index:'), halign: Gtk.Align.START}); + gHBox.add(gLabel); + dialog._adjustment = new Gtk.Adjustment({lower: 0.0, upper: 0.0, step_increment:1.0}); + let spinButton = new Gtk.SpinButton({halign: Gtk.Align.END, adjustment: dialog._adjustment, numeric: 1}); + gHBox.add(spinButton); + + let monitorsChanged = () => { + let n_monitors = this._screen.get_n_monitors() -1; + dialog._adjustment.set_upper(n_monitors) + dialog._adjustment.set_value(n_monitors); + }; + + let monitorsChangedId = this._screen.connect('monitors-changed', monitorsChanged); + + monitorsChanged.apply(this); + grid.add(gHBox); + + dialog.get_content_area().add(grid); + + dialog.connect('response', (dialog, id) => { + this._screen.disconnect(monitorsChangedId); + this._settings.disconnect(availableIndicatorsId); + this._settings.disconnect(transferIndicatorsId); + if (id != Gtk.ResponseType.OK) { + dialog.destroy(); + return; + } + + let [any, model, iter] = dialog._treeView.get_selection().get_selected(); + if (any) { + let indicator = model.get_value(iter, Columns.INDICATOR_NAME); + + let transfers = this._settings.get_value(TRANSFER_INDICATORS_ID).deep_unpack(); + if(!transfers.hasOwnProperty(indicator)){ + transfers[indicator] = dialog._adjustment.get_value(); + this._settings.set_value(TRANSFER_INDICATORS_ID, new GLib.Variant('a{si}', transfers)); + } + } + + dialog.destroy(); + }); + + dialog.show_all(); + } + + _removeIndicator() { + let [any, model, iter] = this._treeView.get_selection().get_selected(); + if (any) { + let indicator = model.get_value(iter, Columns.INDICATOR_NAME); + + let transfers = this._settings.get_value(TRANSFER_INDICATORS_ID).deep_unpack(); + if(transfers.hasOwnProperty(indicator)){ + delete transfers[indicator]; + this._settings.set_value(TRANSFER_INDICATORS_ID, new GLib.Variant('a{si}', transfers)); + } + } + } + + _addComboBoxSwitch(label, schema_id, options) { + this._addSettingsComboBoxSwitch(label, this._settings, schema_id, options) + } + + _addSettingsComboBoxSwitch(label, settings, schema_id, options) { + let gHBox = new Gtk.HBox({margin: 10, spacing: 20, hexpand: true}); + let gLabel = new Gtk.Label({label: _(label), halign: Gtk.Align.START}); + gHBox.add(gLabel); + + let gCBox = new Gtk.ComboBoxText({halign: Gtk.Align.END}); + Object.entries(options).forEach(function(entry) { + const [key, val] = entry; + gCBox.append(key, val); + }); + gHBox.add(gCBox); + + this.add(gHBox); + + settings.bind(schema_id, gCBox, 'active-id', Gio.SettingsBindFlags.DEFAULT); + } + + _addBooleanSwitch(label, schema_id) { + this._addSettingsBooleanSwitch(label, this._settings, schema_id); + } + + _addSettingsBooleanSwitch(label, settings, schema_id) { + let gHBox = new Gtk.HBox({margin: 10, spacing: 20, hexpand: true}); + let gLabel = new Gtk.Label({label: _(label), halign: Gtk.Align.START}); + gHBox.add(gLabel); + let gSwitch = new Gtk.Switch({halign: Gtk.Align.END}); + gHBox.add(gSwitch); + this.add(gHBox); + + settings.bind(schema_id, gSwitch, 'active', Gio.SettingsBindFlags.DEFAULT); + } +}); + +function init() { + Convenience.initTranslations(); +} + +function buildPrefsWidget() { + let widget = new MultiMonitorsPrefsWidget(); + widget.show_all(); + + return widget; +} diff --git a/multi-monitors-add-on@spin83/schemas/gschemas.compiled b/multi-monitors-add-on@spin83/schemas/gschemas.compiled Binary files differnew file mode 100644 index 0000000..5a9e945 --- /dev/null +++ b/multi-monitors-add-on@spin83/schemas/gschemas.compiled diff --git a/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml b/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml new file mode 100644 index 0000000..4a42a23 --- /dev/null +++ b/multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml @@ -0,0 +1,59 @@ +<schemalist gettext-domain="gnome-shell-extensions"> + <schema id="org.gnome.shell.extensions.multi-monitors-add-on" path="/org/gnome/shell/extensions/multi-monitors-add-on/"> + + <key name="show-indicator" type="b"> + <default>true</default> + <summary>Show Multi Monitors indicator on Top Panel.</summary> + <description>Add or remove Multi Monitors indicator from Top Panel.</description> + </key> + + <key name="show-panel" type="b"> + <default>true</default> + <summary>Show Panel on additional monitors.</summary> + <description>Add or remove Panel from additional monitors.</description> + </key> + + <key name="show-activities" type="b"> + <default>true</default> + <summary>Show Activities-Button on additional monitors.</summary> + <description>Change visibility of Activities-Button on additional monitors.</description> + </key> + + <key name="show-app-menu" type="b"> + <default>true</default> + <summary>Show AppMenu-Button on additional monitors.</summary> + <description>Change visibility of AppMenu-Button on additional monitors.</description> + </key> + + <key name="show-date-time" type="b"> + <default>true</default> + <summary>Show DateTime-Button on additional monitors.</summary> + <description>Change visibility of DateTime-Button on additional monitors.</description> + </key> + + <key name="thumbnails-slider-position" type="s"> + <choices> + <choice value='none'/> + <choice value='right'/> + <choice value='left'/> + <choice value='auto'/> + </choices> + <default>'auto'</default> + <summary>Show Thumbnails-Slider on additional monitors.</summary> + <description>Select position of Thumbnails-Slider on additional monitors.</description> + </key> + + <key name="available-indicators" type="as"> + <default>[]</default> + <summary>A list of available indicators.</summary> + <description>A list of indicators that are available for transfer. For internal use only.</description> + </key> + + <key name="transfer-indicators" type="a{si}"> + <default>{}</default> + <summary>A list of indicators for transfer.</summary> + <description>A list of indicators selected for transfer to additional Panel.</description> + </key> + + </schema> +</schemalist>
\ No newline at end of file diff --git a/multi-monitors-add-on@spin83/stylesheet.css b/multi-monitors-add-on@spin83/stylesheet.css new file mode 100644 index 0000000..b1d20c5 --- /dev/null +++ b/multi-monitors-add-on@spin83/stylesheet.css @@ -0,0 +1,29 @@ + +.helloworld-label { + font-size: 72px; + font-weight: bold; + color: #ffffff; + background-color: rgba(0,0,0,0.5); + border-radius: 5px; + padding: .5em; +} + +.multimonitor-spacer { + height: 4em; +} + +.multimonitor-status-indicators-box { + spacing: 0px; +} + +.multimonitor-status-icon { + padding: 0 2px; +} + +.workspace-thumbnails-left { + border-radius: 0 9px 9px 0; +} + +.workspace-thumbnails-left:rtl { + border-radius: 9px 0 0 9px; +} |