summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 15:05:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 15:05:14 +0000
commit301ec35e13e64d13989834f7ec34206c0e875e78 (patch)
tree6c3fef8f6ac8d04e00d400e4121c4d9603de3c37
parentInitial commit. (diff)
downloadgnome-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>
-rw-r--r--.project11
-rw-r--r--LICENSE340
-rw-r--r--README.md33
-rw-r--r--multi-monitors-add-on@spin83/convenience.js93
-rw-r--r--multi-monitors-add-on@spin83/extension.js288
-rw-r--r--multi-monitors-add-on@spin83/icons/multi-monitors-l-symbolic.svg392
-rw-r--r--multi-monitors-add-on@spin83/icons/multi-monitors-r-symbolic.svg393
-rw-r--r--multi-monitors-add-on@spin83/indicator.js109
-rw-r--r--multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1722 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.po95
-rw-r--r--multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1593 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.po91
-rw-r--r--multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1701 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.po94
-rw-r--r--multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1820 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.po93
-rw-r--r--multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1743 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.po94
-rw-r--r--multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mobin0 -> 1759 bytes
-rw-r--r--multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.po89
-rw-r--r--multi-monitors-add-on@spin83/metadata.json10
-rw-r--r--multi-monitors-add-on@spin83/mmcalendar.js387
-rw-r--r--multi-monitors-add-on@spin83/mmlayout.js251
-rw-r--r--multi-monitors-add-on@spin83/mmoverview.js669
-rw-r--r--multi-monitors-add-on@spin83/mmpanel.js526
-rw-r--r--multi-monitors-add-on@spin83/multi-monitors-add-on.pot89
-rw-r--r--multi-monitors-add-on@spin83/prefs.js279
-rw-r--r--multi-monitors-add-on@spin83/schemas/gschemas.compiledbin0 -> 788 bytes
-rw-r--r--multi-monitors-add-on@spin83/schemas/org.gnome.shell.extensions.multi-monitors-add-on.gschema.xml59
-rw-r--r--multi-monitors-add-on@spin83/stylesheet.css29
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>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d6a9326
--- /dev/null
+++ b/LICENSE
@@ -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
new file mode 100644
index 0000000..ce5795c
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/de/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..0cd6a92
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/es/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..2aefbb2
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/fr/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..14040b3
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/it/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..84f0f8e
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/pl/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..beae2e9
--- /dev/null
+++ b/multi-monitors-add-on@spin83/locale/pt_BR/LC_MESSAGES/multi-monitors-add-on.mo
Binary files differ
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
new file mode 100644
index 0000000..5a9e945
--- /dev/null
+++ b/multi-monitors-add-on@spin83/schemas/gschemas.compiled
Binary files differ
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;
+}