Compare commits

..

5 Commits

Author SHA1 Message Date
Giovanni Campagna
7b5f024531 Bump version to 3.0.2
Bump package version to 3.0.2, and at the same time write out all
extensions' version explicitly (in case gnome-shell does not release
3.0.2)
2011-05-20 15:48:08 +02:00
Elad Alfassa
7576883744 Support globally installed themes
If a theme cannot be found in the user directory, try in system
data dirs (XDG_DATA_DIRS, usually /usr/share and /usr/local/share)

Signed-off-by: Giovanni Campagna <gcampagna@src.gnome.org>
2011-05-20 15:47:54 +02:00
Giovanni Campagna
f7e32aabc5 user-theme: fix resetting theme to null
Previously null stylesheets were ignored, making it impossible to
reset the default. We still need to filter out invalid (non existing)
stylesheets, as no check is performed by shell code.
2011-05-20 15:47:54 +02:00
Giovanni Campagna
757ab0096e Add COPYING
License file for GNU General Public Licence v2
2011-05-20 15:43:51 +02:00
Giovanni Campagna
2b10d99295 Bump version to 3.0.1
This is in case someone uses make dist. Has no effect on gnome-shell
compatibility, for the stable branch.
2011-04-26 01:20:23 +02:00
198 changed files with 3397 additions and 39412 deletions

View File

@@ -1,3 +0,0 @@
extends:
- ./lint/eslintrc-gjs.yml
- ./lint/eslintrc-shell.yml

19
.gitignore vendored
View File

@@ -1,29 +1,18 @@
ABOUT-NLS
Makefile
Makefile.in
Makefile.in.in
aclocal.m4
autom4te.cache/
config/
configure
config.log
config.status
data/*.json
m4/
po/*.header
po/*.sed
po/*.sin
po/Makevars.template
po/POTFILES
po/Rules-quot
aclocal.m4
autom4te.cache/
po/gnome-shell-extensions.pot
po/POTFILES
po/stamp-it
staging/
zip-files/
*~
*.gmo
metadata.json
*.desktop
*.gschema.xml
*.gschema.valid
*.session

View File

@@ -1,40 +0,0 @@
stages:
- commit_check
- source_check
- build
variables:
LINT_LOG: "eslint-report.txt"
.only_default: &only_default
only:
- branches
- tags
- merge_requests
check_commit_log:
image: registry.gitlab.gnome.org/gnome/gjs:fedora.static-analysis
stage: commit_check
script:
- ./.gitlab-ci/check-commit-log.sh
only:
- merge_requests
eslint:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: source_check
script:
- eslint -o $LINT_LOG extensions --no-color || { cat $LINT_LOG; false; }
<<: *only_default
artifacts:
paths:
- ${LINT_LOG}
when: on_failure
build-shell-extensions:
image: registry.gitlab.gnome.org/gnome/gnome-shell/extension-ci:v1
stage: build
script:
- meson _build .
- ninja -C _build test install
<<: *only_default

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env bash
if [ -z "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then
echo Cannot review non-merge request
exit 1
fi
git fetch $CI_MERGE_REQUEST_PROJECT_URL.git $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
branch_point=$(git merge-base HEAD FETCH_HEAD)
commits=$(git log --format='format:%H' $branch_point..$CI_COMMIT_SHA)
if [ -z "$commits" ]; then
echo Commit range empty
exit 1
fi
function commit_message_has_url() {
commit=$1
commit_message=$(git show -s --format='format:%b' $commit)
echo "$commit_message" | grep -qe "\($CI_MERGE_REQUEST_PROJECT_URL/\(-/\)\?\(issues\|merge_requests\)/[0-9]\+\|https://bugzilla.gnome.org/show_bug.cgi?id=[0-9]\+\)"
return $?
}
for commit in $commits; do
if ! commit_message_has_url $commit; then
echo "Missing merge request or issue URL on commit $(echo $commit | cut -c -8)"
exit 1
fi
done

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "data/gnome-shell-sass"]
path = data/gnome-shell-sass
url = https://gitlab.gnome.org/GNOME/gnome-shell-sass.git

View File

@@ -1,7 +1,8 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

31
HACKING Normal file
View File

@@ -0,0 +1,31 @@
--- Creating a New Extension ---
To create a new extension, add a subdirectory in extensions.
Then create a Makefile.am like the one in example, replacing
the EXTENSION_ID with the basename of your extension, which
must match the UUID in metadata.json.
If you need additional files, add them to EXTENSION_EXTRA.
Then modify extensions/Makefile.am and configure.ac. It should
be pretty self-explanatory.
Don't forget to add any translatable file to po/POTFILES.in, and
then you're done.
The Gettext domain you should choose is gnome-shell-extensions,
not gnome-shell, unless you're sure there is the string you
need in gnome-shell.
--- Coding Style ---
Generally, we follow GJS coding style (you can find it at
http://git.gnome.org/browse/gjs/tree/doc/Style_Guide.txt), which
in short is: indent 4 spaces, no tabs, space after comma, no space
after function call.
The Emacs mode line for this
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
Imports should be at the top, in two groups, one for standard
imports (like imports.lang or imports.dbus) and introspection,
the other for Shell API. Within the same group, put everything
in alphabetic order.

View File

@@ -1,31 +0,0 @@
## Creating a New Extension
To create a new extension, add a subdirectory in extensions. Then create
a meson.build from the provided [template](extensions/meson.build.template).
If you need additional sources, add them to extension_sources. Similarily add
GSettings schemas to extension_schemas and other files to extension_data.
Then modify the [toplevel Meson file](meson.build) to add the new
extension name in the appropriate set (that is one of classic_extensions,
default_extensions or all_extensions).
Don't forget to add any translatable file to po/POTFILES.in, and
then you're done.
The Gettext domain you should choose is gnome-shell-extensions,
not gnome-shell, unless you're sure there is the string you
need in gnome-shell.
## Coding Style
Generally, we follow [GJS coding style][coding-style], which in short is:
indent 4 spaces, no tabs, space after comma, no space after function call.
The Emacs mode line for this
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
Imports should be at the top, in two groups, one for standard
imports (like imports.lang or imports.dbus) and introspection,
the other for Shell API. Within the same group, put everything
in alphabetic order.
[coding-style]: https://gitlab.gnome.org/GNOME/gjs/blob/master/doc/Style_Guide.md

5
Makefile.am Normal file
View File

@@ -0,0 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = extensions po
DISTCHECK_CONFIGURE_FLAGS = --enable-extensions=all

1009
NEWS

File diff suppressed because it is too large Load Diff

71
README Normal file
View File

@@ -0,0 +1,71 @@
GNOME Shell Extensions is a collection of extensions providing additional
and optional functionality to GNOME Shell.
Since GNOME Shell is not API stable, extensions work only against a very
specific version of the shell, usually the same as this package (see
"configure --version"). Also, since extensions are built from many
individual contributors, we cannot guarantee stability or quality for any
specific extension.
For these reasons, distributions are advised to avoid installing or packaging
this module by defaul.
For more information about GNOME Shell Extensions
http://live.gnome.org/GnomeShell/Extensions
For general information about GNOME Shell
http://live.gnome.org/GnomeShell
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
product, with the 'extensions' component.
Extensions
==========
alternate-tab
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
alternative-status-menu
For those who want a power off item visible at all the time, replaces GNOME Shell
status menu with one featuring separate Suspend and Power Off. Adds the ability to
hibernate as well.
auto-move-windows
Lets you manage your workspaces more easily, assigning a specific workspace to
each application as soon as it creates a window, in a manner configurable with a
GSettings key.
dock
Shows a dock-style task switcher on the right side of the screen.
example
A minimal example illustrating how to write extensions.
gajim
Integration with Gajim, a Jabber/XMPP instant messaging client.
user-theme
Loads a shell theme from ~/.themes/<name>/gnome-shell.
windowsNavigator
Allow keyboard selection of windows and workspaces in overlay mode.
xrandr-indicator
Replace the GTK+ based indicator from gnome-settings-daemon with
a native one. Lets the user rotate the laptop monitor and open
display preferences quickly.
License
=======
GNOME Shell Extensions are distributed under the terms of the GNU General Public License,
version 2 or later. See the COPYING file for details.
Individual extensions may be licensed under different terms, see each source
file for details.

View File

@@ -1,83 +0,0 @@
# GNOME Shell Extensions
GNOME Shell Extensions is a collection of extensions providing additional
and optional functionality to GNOME Shell.
Since GNOME Shell is not API stable, extensions work only against a very
specific version of the shell, usually the same as this package (see
"configure --version"). The extensions in this package are supported by GNOME
and will be updated to reflect future API changes in GNOME Shell.
The GNOME wiki has more information about [GNOME Shell Extensions][project-page],
as well as some general information about [GNOME Shell][shell-page].
Bugs should be reported to the GNOME [bug tracking system][bug-tracker].
## Extensions
* alternate-tab (**OBSOLETE**)
Lets you use classic Alt+Tab (window-based instead of app-based) in GNOME Shell.
This extension is obsolete since GNOME 3.30, see [this blogpost][alternatetab-post]
for further details.
* apps-menu
Lets you reach an application using gnome 2.x style menu on the panel.
* auto-move-windows
Lets you manage your workspaces more easily, assigning a specific workspace to
each application as soon as it creates a window, in a manner configurable with a
GSettings key.
* drive-menu
Shows a status menu for rapid unmount and power off of external storage devices
(i.e. pendrives)
* launch-new-instance
Changes application icons to always launch a new instance when activated.
* native-window-placement
An alternative algorithm for layouting the thumbnails in the windows overview, that
more closely reflects the actual positions and sizes.
* places-menu
Shows a status Indicator for navigating to Places.
* screenshot-window-sizer
Adds a shortcut for resizing the focus window to a size that is suitable for GNOME Software screenshots
* user-theme
Loads a shell theme from ~/.themes/<name>/gnome-shell.
* window-list
Adds a bottom panel with a traditional window list.
* windowsNavigator
Allow keyboard selection of windows and workspaces in overlay mode.
* workspace-indicator
Adds a simple workspace switcher to the top bar.
## License
GNOME Shell Extensions are distributed under the terms of the GNU General
Public License, version 2 or later. See the [COPYING file][license] for details.
Individual extensions may be licensed under different terms, see each source
file for details.
[project-page]: https://wiki.gnome.org/Projects/GnomeShell/Extensions
[shell-page]: https://wiki.gnome.org/Projects/GnomeShell
[bug-tracker]: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/issues
[license]: COPYING
[alternatetab-post]: https://blogs.gnome.org/fmuellner/2018/10/11/the-future-of-alternatetab-and-why-you-need-not-worry/

20
autogen.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Run this to generate all the initial makefiles, etc.
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
PKG_NAME="gnome-shell-extensions"
test -f $srcdir/configure.ac || {
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
echo " top-level gnome-shell-extensions directory"
exit 1
}
which gnome-autogen.sh || {
echo "You need to install gnome-common from GNOME Git (or from"
echo "your OS vendor's package manager)."
exit 1
}
. gnome-autogen.sh

3
config/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
install-sh
mkinstalldirs
missing

77
configure.ac Normal file
View File

@@ -0,0 +1,77 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell-extensions],[3.0.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=extensions])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([config])
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign tar-ustar])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
GETTEXT_PACKAGE=gnome-shell-extensions
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
[The prefix for our gettext translation domains.])
IT_PROG_INTLTOOL(0.26)
PKG_PROG_PKG_CONFIG([0.22])
GLIB_GSETTINGS
ADDITIONAL_PACKAGES=
dnl keep this in sync with extensions/Makefile.am
ALL_EXTENSIONS="example alternate-tab xrandr-indicator windowsNavigator auto-move-windows dock user-theme alternative-status-menu gajim"
DEFAULT_EXTENSIONS="alternate-tab windowsNavigator dock alternative-status-menu"
AC_ARG_ENABLE([extensions],
[AS_HELP_STRING([--enable-extensions],[Space separated list of extensions to enable.
The default is to build all extensions that can be installed in the home directory and have no external depedencies.
Use "all" to enable all available extensions.])],
[],
[enable_extensions=$DEFAULT_EXTENSIONS]
)
if test x$enable_extensions = xall; then
enable_extensions="$ALL_EXTENSIONS"
fi
ADDITIONAL_PACKAGES=
ENABLED_EXTENSIONS=
for e in $enable_extensions; do
case $e in
xrandr-indicator)
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
ADDITIONAL_PACKAGES="$ADDITIONAL_PAGKAGES gnome-desktop-3.0 >= 2.91.6"
;;
alternate-tab|example|windowsNavigator|auto-move-windows|dock|user-theme|alternative-status-menu|gajim)
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
;;
*)
AC_MSG_ERROR([invalid extension $e])
esac
done
AC_SUBST(ENABLED_EXTENSIONS, [$ENABLED_EXTENSIONS])
dnl We don't really need cflags or libdirs, we just check
dnl to ensure we don't fail at runtime.
if test "x$ADDITIONAL_PACKAGES" != x; then
PKG_CHECK_MODULES(ADDITIONAL, [$ADDITIONAL_PACKAGES])
fi
dnl Please keep this sorted alphabetically
AC_CONFIG_FILES([
extensions/alternate-tab/Makefile
extensions/alternative-status-menu/Makefile
extensions/auto-move-windows/Makefile
extensions/dock/Makefile
extensions/example/Makefile
extensions/windowsNavigator/Makefile
extensions/gajim/Makefile
extensions/xrandr-indicator/Makefile
extensions/user-theme/Makefile
extensions/Makefile
Makefile
po/Makefile.in
])
AC_OUTPUT

View File

@@ -1,9 +0,0 @@
[org.gnome.mutter:GNOME-Classic]
dynamic-workspaces=false
[org.gnome.desktop.wm.preferences:GNOME-Classic]
button-layout='appmenu:minimize,maximize,close'
[org.gnome.desktop.wm.keybindings:GNOME-Classic]
switch-applications=[]
switch-windows=['<Super>Tab','<Alt>Tab']

View File

@@ -1,2 +0,0 @@
To update the gnome-shell-sass submodule to latest upstream commit:
git submodule update --rebase

View File

@@ -1,178 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
id="svg10621"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="calendar-today.svg">
<defs
id="defs10623">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient99561-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<linearGradient
inkscape:collect="always"
id="linearGradient34508-1-3">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop34510-1-9" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop34512-4-5" />
</linearGradient>
<radialGradient
r="42"
fy="30"
fx="51"
cy="30"
cx="51"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
gradientUnits="userSpaceOnUse"
id="radialGradient10592"
xlink:href="#linearGradient34508-1-3"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3770"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3001"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3007"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3067"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient3072"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient34508-1-3"
id="radialGradient2997"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
cx="51"
cy="30"
fx="51"
fy="30"
r="42" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#d3d3d3"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="8"
inkscape:cx="-55.349829"
inkscape:cy="-31.442864"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="2133"
inkscape:window-height="1241"
inkscape:window-x="238"
inkscape:window-y="89"
inkscape:window-maximized="0"
borderlayer="true"
inkscape:showpageshadow="false">
<inkscape:grid
type="xygrid"
id="grid3109"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata10626">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-469.08263,-537.99307)">
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path7305"
cx="481.57138"
cy="559.4649"
r="1.5" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -1,262 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65"
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-off-intl.svg">
<defs
id="defs10867">
<linearGradient
id="linearGradient62852-6-5">
<stop
id="stop62854-6-7"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0" />
<stop
id="stop62860-5-3"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<linearGradient
id="linearGradient62821-5-8">
<stop
id="stop62823-2-4"
offset="0"
style="stop-color:#d1d3d1;stop-opacity:1;" />
<stop
id="stop62825-3-8"
offset="1"
style="stop-color:#ebebeb;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient62852-6-5-3">
<stop
id="stop62854-6-7-6"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0-3" />
<stop
id="stop62860-5-3-9"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect62829-6-8-0"
is_visible="true" />
<linearGradient
id="linearGradient62821-5-8-1">
<stop
id="stop62823-2-4-2"
offset="0"
style="stop-color:#d1d3d1;stop-opacity:1;" />
<stop
id="stop62825-3-8-9"
offset="1"
style="stop-color:#ebebeb;stop-opacity:1;" />
</linearGradient>
<linearGradient
y2="507.42715"
x2="702.58966"
y1="484.49405"
x1="702.58966"
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
gradientUnits="userSpaceOnUse"
id="linearGradient11647"
xlink:href="#linearGradient62821-5-8-1"
inkscape:collect="always" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65663">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
id="rect65665"
width="96.999924"
height="24.292892"
x="708.71954"
y="406.96973"
rx="3.1139846"
ry="1.9595497"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
<stop
style="stop-color:#f4f6f4;stop-opacity:1"
offset="0"
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
<stop
style="stop-color:#d7dad7;stop-opacity:1"
offset="1"
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient65584">
<stop
style="stop-color:#f1f1f1;stop-opacity:1"
offset="0"
id="stop65586" />
<stop
style="stop-color:#a7aba7;stop-opacity:0;"
offset="1"
id="stop65588" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582-5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,-75.348673,-359.16458)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient65584"
id="linearGradient65590-6"
x1="716.62506"
y1="537.23358"
x2="716.62506"
y2="535.23358"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0139469,0,0,1.0139469,-705.52354,-521.99915)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="23.565368"
inkscape:cy="19.596892"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
<inkscape:grid
type="xygrid"
id="grid11512" />
</sodipodi:namedview>
<metadata
id="metadata10870">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1030.3622)">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
style="display:inline"
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
transform="translate(0,30)"
id="g62931">
<rect
style="fill:#cecece;fill-opacity:1;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;solid-color:#000000;solid-opacity:1;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto"
id="rect62935"
width="63.856125"
height="21.093594"
x="644.5"
y="483.5"
rx="2"
ry="2" />
<rect
style="fill:url(#linearGradient65582);fill-opacity:1;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;color:#000000;clip-rule:nonzero;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect62937"
width="29.97398"
height="17.022524"
x="646.55896"
y="485.61429"
rx="1"
ry="1" />
<g
transform="translate(-38.048674,-1.9445437)"
id="g62939" />
<path
inkscape:connector-curvature="0"
inkscape:original-d="m 647.78241,486.57192 27.73523,0"
inkscape:path-effect="#path-effect62989-8-0"
id="path62947"
d="m 647.78241,486.57192 27.73523,0"
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g11665"
transform="matrix(0.78906097,0,0,0.78906097,178.78814,111.57844)" />
<path
sodipodi:type="arc"
id="path3016"
sodipodi:cx="50.375"
sodipodi:cy="11.875"
sodipodi:rx="4.625"
sodipodi:ry="4.625"
d="M 55,11.875 A 4.625,4.625 0 0 1 50.375,16.5 4.625,4.625 0 0 1 45.75,11.875 4.625,4.625 0 0 1 50.375,7.25 4.625,4.625 0 0 1 55,11.875 Z"
transform="translate(642.41421,512.02037)"
style="fill:none;stroke:#2e3436;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -1,222 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65"
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-off-us.svg">
<defs
id="defs10867">
<linearGradient
id="linearGradient62852-6-5">
<stop
id="stop62854-6-7"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0" />
<stop
id="stop62860-5-3"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<linearGradient
id="linearGradient62821-5-8">
<stop
id="stop62823-2-4"
offset="0"
style="stop-color:#d1d3d1;stop-opacity:1;" />
<stop
id="stop62825-3-8"
offset="1"
style="stop-color:#ebebeb;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient62852-6-5-3">
<stop
id="stop62854-6-7-6"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0-3" />
<stop
id="stop62860-5-3-9"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
effect="spiro"
id="path-effect62829-6-8-0"
is_visible="true" />
<linearGradient
id="linearGradient62821-5-8-1">
<stop
id="stop62823-2-4-2"
offset="0"
style="stop-color:#d1d3d1;stop-opacity:1;" />
<stop
id="stop62825-3-8-9"
offset="1"
style="stop-color:#ebebeb;stop-opacity:1;" />
</linearGradient>
<linearGradient
y2="507.42715"
x2="702.58966"
y1="484.49405"
x1="702.58966"
gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)"
gradientUnits="userSpaceOnUse"
id="linearGradient11647"
xlink:href="#linearGradient62821-5-8-1"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9"
id="linearGradient65582"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5250827,0,0,0.72144839,620.16092,156.4917)"
x1="207.17195"
y1="497.39584"
x2="207.17195"
y2="531.48669" />
<linearGradient
inkscape:collect="always"
id="linearGradient5378-2-5-9-9-0-80-7-6-8-4-5-7-3-6-2-9">
<stop
style="stop-color:#f4f6f4;stop-opacity:1"
offset="0"
id="stop5380-6-1-1-9-3-6-3-6-45-6-4-4-9-9-8-9" />
<stop
style="stop-color:#d7dad7;stop-opacity:1"
offset="1"
id="stop5382-06-3-6-4-2-4-6-8-0-9-6-8-7-7-6-7" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0-6"
effect="spiro" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="78.222729"
inkscape:cy="-3.6620078"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
<inkscape:grid
type="xygrid"
id="grid11512" />
</sodipodi:namedview>
<metadata
id="metadata10870">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1030.3622)">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
style="display:inline"
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
style="display:inline"
transform="translate(0,30)"
id="g62931">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cecece;fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new"
id="rect62935"
width="63.856125"
height="21.093594"
x="644.5"
y="483.5"
rx="2"
ry="2" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient65582);fill-opacity:1;fill-rule:nonzero;stroke:#a7a7a7;stroke-width:0.97313344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect62937"
width="29.97398"
height="17.022524"
x="646.55896"
y="485.61429"
rx="1"
ry="1" />
<g
transform="translate(-38.048674,-1.9445437)"
id="g62939" />
<path
inkscape:connector-curvature="0"
inkscape:original-d="m 647.78241,486.57192 27.73523,0"
inkscape:path-effect="#path-effect62989-8-0-6"
id="path62947"
d="m 647.78241,486.57192 27.73523,0"
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
id="g11665"
transform="matrix(0.78906097,0,0,0.78906097,167.78814,111.57844)">
<text
xml:space="preserve"
style="font-size:12.69556618px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#2e3436;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
x="644.46393"
y="533.87158"
id="text62949"
sodipodi:linespacing="125%"
transform="scale(1.0113976,0.98873084)"><tspan
sodipodi:role="line"
id="tspan62951"
x="644.46393"
y="533.87158">OFF</tspan></text>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -1,175 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65"
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-on-intl.svg">
<defs
id="defs10867">
<linearGradient
id="linearGradient62852-6-5">
<stop
id="stop62854-6-7"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0" />
<stop
id="stop62860-5-3"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient38513">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop38515" />
<stop
style="stop-color:#6f9ccd;stop-opacity:1"
offset="1"
id="stop38517" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient62852-6-5"
id="linearGradient62981-1-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
x1="740"
y1="486.10501"
x2="740"
y2="505.3204" />
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0-8"
effect="spiro" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="9.3225163"
inkscape:cy="-0.1588306"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
<inkscape:grid
type="xygrid"
id="grid11512" />
</sodipodi:namedview>
<metadata
id="metadata10870">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1030.3622)">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
style="display:inline"
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
style="display:inline"
transform="translate(0,30)"
id="g62931">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient38519);fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect62935"
width="63.856125"
height="21.093594"
x="644.5"
y="483.5"
rx="2"
ry="2" />
<rect
style="display:inline;fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect62937"
width="29.97398"
height="17.022524"
x="676.49646"
y="485.67679"
rx="1"
ry="1" />
<g
transform="translate(-7.9861743,-1.9445437)"
id="g62939" />
<path
inkscape:connector-curvature="0"
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
inkscape:path-effect="#path-effect62989-8-0-8"
id="path62947"
d="m 677.59874,486.57192 27.94632,0"
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<path
inkscape:connector-curvature="0"
id="path4444"
d="m 661.91421,522.09451 0,7"
style="fill:#3465a4;stroke:#3465a4;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;fill-opacity:1;fill-rule:nonzero;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 18,7.96875 0,7"
id="path3922"
inkscape:connector-curvature="0"
transform="translate(643.91421,513.06326)" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,209 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="65"
height="22"
id="svg10865"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="classic-toggle-on-us.svg">
<defs
id="defs10867">
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient62852-6-5"
id="linearGradient62981-1-1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.85639946,0,0,0.81059641,57.747905,92.132229)"
x1="740"
y1="486.10501"
x2="740"
y2="505.3204" />
<linearGradient
id="linearGradient62852-6-5">
<stop
id="stop62854-6-7"
offset="0"
style="stop-color:#ffffff;stop-opacity:1" />
<stop
style="stop-color:#eeeeec;stop-opacity:1"
offset="0.97726452"
id="stop62858-8-0" />
<stop
id="stop62860-5-3"
offset="1"
style="stop-color:#f5f5f4;stop-opacity:1" />
</linearGradient>
<inkscape:path-effect
is_visible="true"
id="path-effect62989-8-0"
effect="spiro" />
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65663">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:new"
id="rect65665"
width="96.999924"
height="24.292892"
x="708.71954"
y="406.96973"
rx="3.1139846"
ry="1.9595497"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/content selection/content-selection.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</clipPath>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient38513">
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="0"
id="stop38515" />
<stop
style="stop-color:#6f9ccd;stop-opacity:1"
offset="1"
id="stop38517" />
</linearGradient>
<linearGradient
gradientTransform="translate(-644,-483)"
inkscape:collect="always"
xlink:href="#linearGradient38513"
id="linearGradient38519-3"
x1="690"
y1="506.25049"
x2="690"
y2="480.93414"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="20.775745"
inkscape:cy="7.0434022"
inkscape:document-units="px"
inkscape:current-layer="g62929"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:snap-nodes="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:snap-others="false"
inkscape:window-width="2560"
inkscape:window-height="1342"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1"
showborder="false">
<inkscape:grid
type="xygrid"
id="grid11512" />
</sodipodi:namedview>
<metadata
id="metadata10870">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1030.3622)">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/home/lapo.fedora/SparkleShare/gnome-mockups/system-settings/network/network-panel-summary.png"
style="display:inline"
id="g62929"
transform="translate(-643.91421,517.29894)">
<g
transform="translate(0,30)"
id="g62931">
<rect
style="fill:url(#linearGradient38519);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill-rule:nonzero;stroke-linejoin:miter;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect62935"
width="63.856125"
height="21.093594"
x="644.5"
y="483.5"
rx="2"
ry="2" />
<rect
style="fill:url(#linearGradient62981-1-1);fill-opacity:1;stroke:#3465a4;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
id="rect62937"
width="29.97398"
height="17.022524"
x="676.49646"
y="485.67679"
rx="1"
ry="1" />
<g
transform="translate(-7.9861743,-1.9445437)"
id="g62939" />
<path
inkscape:connector-curvature="0"
inkscape:original-d="m 677.59874,486.57192 27.94632,0"
inkscape:path-effect="#path-effect62989-8-0"
id="path62947"
d="m 677.59874,486.57192 27.94632,0"
style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<text
transform="scale(1.0113976,0.98873084)"
sodipodi:linespacing="125%"
id="text62949-7"
y="534.96918"
x="646.71399"
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#3465a4;fill-opacity:1;stroke:none;display:inline;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold;"
xml:space="preserve"><tspan
y="534.96918"
x="646.71399"
id="tspan62951-7"
sodipodi:role="line">ON</tspan></text>
<text
transform="scale(1.0113976,0.98873084)"
sodipodi:linespacing="125%"
id="text62949"
y="533.94482"
x="646.71399"
style="font-size:10.01104736px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Cantarell;-inkscape-font-specification:Cantarell Bold"
xml:space="preserve"><tspan
y="533.94482"
x="646.71399"
id="tspan62951"
sodipodi:role="line">ON</tspan></text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,10 +0,0 @@
{
"parentMode": "user",
"stylesheetName": "gnome-classic.css",
"hasOverview": false,
"enabledExtensions": [@CLASSIC_EXTENSIONS@],
"panel": { "left": ["appMenu"],
"center": [],
"right": ["a11y", "keyboard", "dateMenu", "aggregateMenu"]
}
}

View File

@@ -1,5 +0,0 @@
@import url("gnome-classic.css");
stage {
-st-icon-style: symbolic;
}

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Name=GNOME Classic
Comment=This session logs you into GNOME Classic
Exec=env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic
TryExec=gnome-session
Type=Application
DesktopNames=GNOME-Classic;GNOME;

View File

@@ -1,96 +0,0 @@
/* Use the gnome-shell theme, but with light colors */
$variant: 'light';
@import "gnome-shell-sass/_colors"; //use gtk colors
@import "gnome-shell-sass/_drawing";
@import "gnome-shell-sass/_common";
@import "gnome-shell-sass/_widgets";
/* Overrides */
#panel, #panel.solid {
font-weight: normal;
background-color: $bg_color;
background-gradient-direction: vertical;
background-gradient-end: darken($bg_color,5%);
border-top-color: #666; /* we don't support non-uniform border-colors and
use the top border color for any border, so we
need to set it even if all we want is a bottom
border */
border-bottom: 1px solid #666;
app-icon-bottom-clip: 0px;
&:overview {
background-color: #000;
background-gradient-end: #000;
border-top-color: #000;
border-bottom: 1px solid #000;
.panel-button { color: #fff; }
}
.panel-button {
-natural-hpadding: 8px;
-minimum-hpadding: 4px;
font-weight: normal;
color: $fg_color;
text-shadow: none;
&:hover {
color: lighten($fg_color,10%);
text-shadow: none;
& .system-status-icon { icon-shadow: none; }
}
&:active, &:overview, &:focus, &:checked {
// Trick due to St limitations. It needs a background to draw
// a box-shadow
background-color: $selected_bg_color;
color: $selected_fg_color;
box-shadow: none;
& > .system-status-icon { icon-shadow: none; }
}
.app-menu-icon { width: 0; height: 0; margin: 0; } // shell's display:none; :D
.system-status-icon {
icon-shadow: none;
}
}
.panel-corner,
.panel-corner:active,
.panel-corner:overview,
.panel-corner:focus {
-panel-corner-radius: 0;
}
&.lock-screen,
&.unlock-screen,
&.login-screen {
background-color: transparentize($bg_color, 0.5);
background-gradient-start: transparentize($bg_color, 0.5);
background-gradient-end: transparentize($bg_color, 0.5);
border-bottom: none;
.panel-button { color: $osd_fg_color; }
}
.popup-menu-arrow { width: 0; height: 0; } // shell's display: none;
}
#appMenu {
padding: 0 8px 0 8px;
spinner-image: url("classic-process-working.svg");
}
.tile-preview-left.on-primary,
.tile-preview-right.on-primary,
.tile-preview-left.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 0;
}
@each $v in us, intl {
.toggle-switch-#{$v} {
background-image: url("classic-toggle-off-#{$v}.svg");
&:checked { background-image: url("classic-toggle-on-#{$v}.svg"); }
}
}
.calendar-day-with-events {
background-image: url("calendar-today.svg");
}

View File

@@ -1,3 +0,0 @@
[GNOME Session]
Name=GNOME Classic
RequiredComponents=org.gnome.Shell;org.gnome.SettingsDaemon.A11ySettings;org.gnome.SettingsDaemon.Color;org.gnome.SettingsDaemon.Datetime;org.gnome.SettingsDaemon.Housekeeping;org.gnome.SettingsDaemon.Keyboard;org.gnome.SettingsDaemon.MediaKeys;org.gnome.SettingsDaemon.Power;org.gnome.SettingsDaemon.PrintNotifications;org.gnome.SettingsDaemon.Rfkill;org.gnome.SettingsDaemon.ScreensaverProxy;org.gnome.SettingsDaemon.Sharing;org.gnome.SettingsDaemon.Smartcard;org.gnome.SettingsDaemon.Sound;org.gnome.SettingsDaemon.Wacom;org.gnome.SettingsDaemon.XSettings;

View File

@@ -1,63 +0,0 @@
sessions = [
['gnome-classic.session.desktop.in', sessiondir],
['gnome-classic.desktop.in', xsessiondir]
]
foreach s : sessions
name_array = s[0].split('.')
i18n.merge_file('',
input: s[0],
output: '.'.join([name_array[0], name_array[1]]),
po_dir: '../po',
install: true,
install_dir: s[1],
type: 'desktop'
)
endforeach
classic_uuids = []
foreach e : classic_extensions
classic_uuids += e + uuid_suffix
endforeach
mode_conf = configuration_data()
mode_conf.set('CLASSIC_EXTENSIONS', '"' + '", "'.join(classic_uuids) + '"')
mode_file = 'classic.json'
configure_file(
input: mode_file + '.in',
output: mode_file,
configuration: mode_conf,
install_dir: modedir
)
theme_sources = files(
'gnome-shell-sass/_colors.scss',
'gnome-shell-sass/_common.scss',
'gnome-shell-sass/_drawing.scss',
'gnome-shell-sass/_high-contrast-colors.scss'
)
theme_data = [
'calendar-today.svg',
'classic-process-working.svg',
'classic-toggle-off-intl.svg',
'classic-toggle-off-us.svg',
'classic-toggle-on-intl.svg',
'classic-toggle-on-us.svg',
'gnome-classic-high-contrast.css'
]
style = 'gnome-classic'
custom_target(style + '.css',
input: style + '.scss',
output: style + '.css',
depend_files: theme_sources,
command: [sassc, '-a', '@INPUT@', '@OUTPUT@'],
install: true,
install_dir: themedir
)
install_data(theme_data, install_dir: themedir)
classic_override = '00_org.gnome.shell.extensions.classic.gschema.override'
install_data(classic_override, install_dir: schemadir)

View File

@@ -1,37 +0,0 @@
#!/bin/sh
srcdir=`dirname $0`
srcdir=`(cd $srcdir && pwd)`
builddir=`mktemp -p $srcdir -d _build.XXXXXX` || exit 1
installdir=`mktemp -p $srcdir -d _install.XXXXXX` || exit 1
meson setup --prefix=$installdir -Dextension_set=all $srcdir $builddir
ninja -C$builddir install
rm -rf $srcdir/zip-files
mkdir $srcdir/zip-files
extensiondir=$installdir/share/gnome-shell/extensions
schemadir=$installdir/share/glib-2.0/schemas
localedir=$installdir/share/locale
for f in $extensiondir/*; do
name=`basename ${f%%@*}`
uuid=$name@gnome-shell-extensions.gcampax.github.com
schema=$schemadir/org.gnome.shell.extensions.$name.gschema.xml
cp $srcdir/NEWS $srcdir/COPYING $f
cp -r $localedir $f
if [ -f $schema ]; then
mkdir $f/schemas
cp $schema $f/schemas;
glib-compile-schemas $f/schemas
fi
(cd $f && zip -rmq $srcdir/zip-files/$uuid.shell-extension.zip .)
done
rm -rf $builddir
rm -rf $installdir

22
extension.mk Normal file
View File

@@ -0,0 +1,22 @@
extensionurl = http://git.gnome.org/gnome-shell-extensions
# Change these to modify how installation is performed
topextensiondir = $(datadir)/gnome-shell/extensions
extensionbase = @gnome-shell-extensions.gnome.org
uuid = $(EXTENSION_ID)$(extensionbase)
extensiondir = $(topextensiondir)/$(uuid)
dist_extension_DATA = extension.js stylesheet.css
nodist_extension_DATA = metadata.json $(EXTRA_EXTENSION)
EXTRA_DIST = metadata.json.in
metadata.json: metadata.json.in $(top_builddir)/config.status
$(AM_V_GEN) sed -e "s|[@]LOCALEDIR@|$(datadir)/locale|" \
-e "s|[@]uuid@|$(uuid)|" \
-e "s|[@]shell_current@|$(PACKAGE_VERSION)|" \
-e "s|[@]url@|$(extensionurl)|" $< > $@
CLEANFILES = metadata.json

3
extensions/Makefile.am Normal file
View File

@@ -0,0 +1,3 @@
DIST_SUBDIRS = example alternate-tab xrandr-indicator windowsNavigator auto-move-windows dock user-theme alternative-status-menu gajim
SUBDIRS = $(ENABLED_EXTENSIONS)

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = alternate-tab
include ../../extension.mk

View File

@@ -0,0 +1,237 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell= imports.gi.Shell;
const St = imports.gi.St;
const AltTab=imports.ui.altTab;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
function AltTabPopup2() {
this._init();
}
AltTabPopup2.prototype = {
__proto__ : AltTab.AltTabPopup.prototype,
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
reactive: true });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false;
this._currentApp = 0;
this._currentWindow = -1;
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
// Initially disable hover so we ignore the enter-event if
// the switcher appears underneath the current pointer location
this._disableHover();
this.show();
Main.uiGroup.add_actor(this.actor);
},
show : function(backward) {
let windows = global.get_window_actors();
let list = '';
let normal_windows= [];
let appIcons = [];
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
for (let w = windows.length-1; w >= 0; w--) {
let win = windows[w].get_meta_window();
if (win.window_type == 0) {
normal_windows.push(win);
}
}
normal_windows.sort(Lang.bind(this, this._sortWindows));
let win_on_top = normal_windows.shift();
normal_windows.push(win_on_top);
windows = normal_windows;
for (let w = 0; w < windows.length; w++) {
let win = windows[w];
let ap1 = null;
for (let i = 0;i < apps.length; i++) {
let app_wins = apps[i].get_windows();
for (let j = 0;j < app_wins.length; j++) {
if (app_wins[j] == win)
ap1 = new AltTab.AppIcon(apps[i]);
}
}
ap1.cachedWindows = [win];
appIcons.push(ap1);
}
if (!windows.length)
return false;
if (!Main.pushModal(this.actor))
return false;
this._haveModal = true;
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this._appSwitcher = new WindowList(windows);
this._appSwitcher._altTabPopup=this;
this._appSwitcher.highlight(0,false);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
this._appIcons = appIcons;
return true
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
// X allows servers to represent Shift+Tab in two different ways
if (shift && keysym == Clutter.Tab)
keysym = Clutter.ISO_Left_Tab;
this._disableHover();
if (keysym == Clutter.grave)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.asciitilde)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Escape)
this.destroy();
else if (this._thumbnailsFocused) {
if (keysym == Clutter.Tab) {
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
this._select(this._nextApp());
else
this._select(this._currentApp, this._nextWindow());
} else if (keysym == Clutter.ISO_Left_Tab) {
if (this._currentWindow == 0 || this._currentWindow == -1)
this._select(this._previousApp());
else
this._select(this._currentApp, this._previousWindow());
} else if (keysym == Clutter.Left)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Right)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.Up)
this._select(this._currentApp, null, true);
} else {
if (keysym == Clutter.Tab)
this._select(this._nextApp());
else if (keysym == Clutter.ISO_Left_Tab)
this._select(this._previousApp());
else if (keysym == Clutter.Left)
this._select(this._previousApp());
else if (keysym == Clutter.Right)
this._select(this._nextApp());
}
return true;
},
_sortWindows : function(win1,win2) {
let t1 = win1.get_user_time();
let t2 = win2.get_user_time();
if (t2 > t1) return 1;
else return -1;
},
_appActivated : function(thumbnailList, n) {
let appIcon = this._appIcons[this._currentApp];
Main.activateWindow(appIcon.cachedWindows[0]);
this.destroy();
},
_finish : function() {
let app = this._appIcons[this._currentApp];
Main.activateWindow(app.cachedWindows[0]);
this.destroy();
},
};
function WindowList(windows) {
this._init(windows);
}
WindowList.prototype = {
__proto__ : AltTab.AppSwitcher.prototype,
_init : function(windows) {
AltTab.AppSwitcher.prototype._init.call(this, []);
let activeWorkspace = global.screen.get_active_workspace();
this._labels = new Array();
this._thumbnailBins = new Array();
this._clones = new Array();
this._windows = windows;
this._arrows= new Array();
this.icons= new Array();
for (let w = 0; w < windows.length; w++) {
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
arrow.connect('repaint', Lang.bind(this, function (area) {
Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
}));
this._list.add_actor(arrow);
this._arrows.push(arrow);
arrow.hide();
let win=windows[w];
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
let ap1 = null;
for (let i = 0; i < apps.length; i++) {
let app_wins = apps[i].get_windows();
for (let j = 0; j < app_wins.length; j++) {
if (app_wins[j] == win) {
ap1 = new AltTab.AppIcon(apps[i]);
let mutterWindow = win.get_compositor_private();
let windowTexture = mutterWindow.get_texture ();
let [width, height] = windowTexture.get_size();
let scale = Math.min(1.0, 128 / width, 128 / height);
let clone = new Clutter.Clone ({ source: windowTexture, reactive: true, width: width * scale, height: height * scale });
ap1.icon = ap1.app.create_icon_texture(128);
ap1._iconBin.set_size(128,128);
ap1._iconBin.child=clone;
ap1.label.text=win.get_title();
}
}
}
ap1.cachedWindows = [win];
this._addIcon(ap1);
}
},
addSeparator: function () {
this._separator=null;
}
};
function main() {
Main.wm.setKeybindingHandler('switch_windows', function() {
let alpopup = new AltTabPopup2();
});
}

View File

@@ -0,0 +1,9 @@
{
"uuid": "@uuid@",
"name": "AlternateTab",
"description": "A replacement for Alt-Tab, allows to cycle between windows and does not group by application",
"original-author": "thomas.bouffon@gmail.com",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"url": "@url@"
}

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = alternative-status-menu
include ../../extension.mk

View File

@@ -0,0 +1,112 @@
/* -*- mode: js2 - indent-tabs-mode: nil - js2-basic-offset: 4 -*- */
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const GnomeSession = imports.misc.gnomeSession;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
function updateSuspendOrHibernate(object, pspec, item) {
let canSuspend = this._upClient.get_can_suspend();
let canHibernate = this._upClient.get_can_hibernate();
if (!canSuspend && !canHibernate) {
item.actor.hide();
return;
} else
item.actor.show();
if (!canSuspend && canHibernate) {
item.updateText(_("Hibernate"), null);
return;
}
let suspendText = _("Suspend");
let hibernateText = canHibernate ? _("Hibernate") : null;
item.updateText(suspendText, hibernateText);
}
function onSuspendOrHibernateActivate(item) {
Main.overview.hide();
let haveSuspend = this._upClient.get_can_suspend();
let haveHibernate = this._upClient.get_can_hibernate();
if (haveSuspend &&
item.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));
} else {
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
this._upClient.hibernate_sync(null);
}));
}
}
function createSubMenu() {
let item;
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("My Account"));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Hibernate"));
this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, onSuspendOrHibernateActivate));
this._upClient.connect('notify::can-suspend', Lang.bind(this, updateSuspendOrHibernate, item));
this._upClient.connect('notify::can-hibernate', Lang.bind(this, updateSuspendOrHibernate, item));
updateSuspendOrHibernate.call(this, null, null, item);
item = new PopupMenu.PopupMenuItem(_("Power Off..."));
item.connect('activate', Lang.bind(this, function() {
this._session.ShutdownRemote();
}));
this.menu.addMenuItem(item);
}
// Put your extension initialization code here
function main(metadata) {
let statusMenu = Main.panel._statusmenu;
statusMenu.menu.removeAll();
createSubMenu.call(statusMenu);
}

View File

@@ -0,0 +1,8 @@
{
"uuid": "@uuid@",
"name": "Alternative Status Menu",
"description": "Replaces GNOME Shell Status Menu with one showing Suspend/Hibernate and Power Off as separate items",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"url": "@url@"
}

View File

@@ -0,0 +1,3 @@
.popup-alternating-menu-item:alternate {
font-weight: normal !important;
}

View File

@@ -1,693 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/* exported init enable disable */
const {
Atk, Clutter, Gio, GLib, GMenu, GObject, Gtk, Meta, Shell, St,
} = imports.gi;
const Signals = imports.signals;
const DND = imports.ui.dnd;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const appSys = Shell.AppSystem.get_default();
const APPLICATION_ICON_SIZE = 32;
const HORIZ_FACTOR = 5;
const MENU_HEIGHT_OFFSET = 132;
const NAVIGATION_REGION_OVERSHOOT = 50;
Gio._promisify(Gio._LocalFilePrototype, 'query_info_async', 'query_info_finish');
Gio._promisify(Gio._LocalFilePrototype, 'set_attributes_async', 'set_attributes_finish');
var ApplicationMenuItem = GObject.registerClass(
class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(button, app) {
super._init();
this._app = app;
this._button = button;
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
let appLabel = new St.Label({
text: app.get_name(),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
this.add_child(appLabel);
this.label_actor = appLabel;
let textureCache = St.TextureCache.get_default();
let iconThemeChangedId = textureCache.connect('icon-theme-changed',
this._updateIcon.bind(this));
this.connect('destroy', () => {
textureCache.disconnect(iconThemeChangedId);
});
this._updateIcon();
this._delegate = this;
let draggable = DND.makeDraggable(this);
let maybeStartDrag = draggable._maybeStartDrag;
draggable._maybeStartDrag = event => {
if (this._dragEnabled)
return maybeStartDrag.call(draggable, event);
return false;
};
}
activate(event) {
this._app.open_new_window(-1);
this._button.selectCategory(null);
this._button.menu.toggle();
super.activate(event);
Main.overview.hide();
}
setActive(active, params) {
if (active)
this._button.scrollToButton(this);
super.setActive(active, params);
}
setDragEnabled(enabled) {
this._dragEnabled = enabled;
}
getDragActor() {
return this._app.create_icon_texture(APPLICATION_ICON_SIZE);
}
getDragActorSource() {
return this._iconBin;
}
_updateIcon() {
let icon = this.getDragActor();
icon.style_class = 'icon-dropshadow';
this._iconBin.set_child(icon);
}
});
var CategoryMenuItem = GObject.registerClass(
class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(button, category) {
super._init();
this._category = category;
this._button = button;
this._oldX = -1;
this._oldY = -1;
let name;
if (this._category)
name = this._category.get_name();
else
name = _('Favorites');
this.add_child(new St.Label({ text: name }));
this.connect('motion-event', this._onMotionEvent.bind(this));
this.connect('notify::active', this._onActiveChanged.bind(this));
}
activate(event) {
this._button.selectCategory(this._category);
this._button.scrollToCatButton(this);
super.activate(event);
}
_isNavigatingSubmenu([x, y]) {
let [posX, posY] = this.get_transformed_position();
if (this._oldX === -1) {
this._oldX = x;
this._oldY = y;
return true;
}
let deltaX = Math.abs(x - this._oldX);
let deltaY = Math.abs(y - this._oldY);
this._oldX = x;
this._oldY = y;
// If it lies outside the x-coordinates then it is definitely outside.
if (posX > x || posX + this.width < x)
return false;
// If it lies inside the menu item then it is definitely inside.
if (posY <= y && posY + this.height >= y)
return true;
// We want the keep-up triangle only if the movement is more
// horizontal than vertical.
if (deltaX * HORIZ_FACTOR < deltaY)
return false;
// Check whether the point lies inside triangle ABC, and a similar
// triangle on the other side of the menu item.
//
// +---------------------+
// | menu item |
// A +---------------------+ C
// P |
// B
// Ensure that the point P always lies below line AC so that we can
// only check for triangle ABC.
if (posY > y) {
let offset = posY - y;
y = posY + this.height + offset;
}
// Ensure that A is (0, 0).
x -= posX;
y -= posY + this.height;
// Check which side of line AB the point P lies on by taking the
// cross-product of AB and AP. See:
// http://stackoverflow.com/questions/3461453/determine-which-side-of-a-line-a-point-lies
if (this.width * y - NAVIGATION_REGION_OVERSHOOT * x <= 0)
return true;
return false;
}
_onMotionEvent(actor, event) {
let device = event.get_device();
if (!device.get_grabbed_actor()) {
this._oldX = -1;
this._oldY = -1;
device.grab(this);
}
this.hover = true;
if (this._isNavigatingSubmenu(event.get_coords()))
return true;
this._oldX = -1;
this._oldY = -1;
this.hover = false;
device.ungrab();
let source = event.get_source();
if (source instanceof St.Widget)
source.sync_hover();
return false;
}
_onActiveChanged() {
if (!this.active)
return;
this._button.selectCategory(this._category);
this._button.scrollToCatButton(this);
}
});
class ApplicationsMenu extends PopupMenu.PopupMenu {
constructor(sourceActor, arrowAlignment, arrowSide, button) {
super(sourceActor, arrowAlignment, arrowSide);
this._button = button;
}
isEmpty() {
return false;
}
toggle() {
if (this.isOpen)
this._button.selectCategory(null);
super.toggle();
}
}
class DesktopTarget {
constructor() {
this._desktop = null;
this._desktopDestroyedId = 0;
this._windowAddedId =
global.window_group.connect('actor-added',
this._onWindowAdded.bind(this));
global.get_window_actors().forEach(a => {
this._onWindowAdded(a.get_parent(), a);
});
}
get hasDesktop() {
return this._desktop !== null;
}
_onWindowAdded(group, actor) {
if (!(actor instanceof Meta.WindowActor))
return;
if (actor.meta_window.get_window_type() === Meta.WindowType.DESKTOP)
this._setDesktop(actor);
}
_setDesktop(desktop) {
if (this._desktop) {
this._desktop.disconnect(this._desktopDestroyedId);
this._desktopDestroyedId = 0;
delete this._desktop._delegate;
}
this._desktop = desktop;
this.emit('desktop-changed');
if (this._desktop) {
this._desktopDestroyedId = this._desktop.connect('destroy', () => {
this._setDesktop(null);
});
this._desktop._delegate = this;
}
}
_getSourceAppInfo(source) {
if (!(source instanceof ApplicationMenuItem))
return null;
return source._app.app_info;
}
async _markTrusted(file) {
let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE;
let trustedAttr = 'metadata::trusted';
let queryFlags = Gio.FileQueryInfoFlags.NONE;
let ioPriority = GLib.PRIORITY_DEFAULT;
try {
let info = await file.query_info_async(modeAttr, queryFlags, ioPriority, null);
let mode = info.get_attribute_uint32(modeAttr) | 0o100;
info.set_attribute_uint32(modeAttr, mode);
info.set_attribute_string(trustedAttr, 'yes');
await file.set_attributes_async(info, queryFlags, ioPriority, null);
// Hack: force nautilus to reload file info
info = new Gio.FileInfo();
info.set_attribute_uint64(
Gio.FILE_ATTRIBUTE_TIME_ACCESS, GLib.get_real_time());
try {
await file.set_attributes_async(info, queryFlags, ioPriority, null);
} catch (e) {
log(`Failed to update access time: ${e.message}`);
}
} catch (e) {
log(`Failed to mark file as trusted: ${e.message}`);
}
}
destroy() {
if (this._windowAddedId)
global.window_group.disconnect(this._windowAddedId);
this._windowAddedId = 0;
this._setDesktop(null);
}
handleDragOver(source, _actor, _x, _y, _time) {
let appInfo = this._getSourceAppInfo(source);
if (!appInfo)
return DND.DragMotionResult.CONTINUE;
return DND.DragMotionResult.COPY_DROP;
}
acceptDrop(source, _actor, _x, _y, _time) {
let appInfo = this._getSourceAppInfo(source);
if (!appInfo)
return false;
this.emit('app-dropped');
let desktop = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let src = Gio.File.new_for_path(appInfo.get_filename());
let dst = Gio.File.new_for_path(GLib.build_filenamev([desktop, src.get_basename()]));
try {
// copy_async() isn't introspectable :-(
src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null);
this._markTrusted(dst);
} catch (e) {
log(`Failed to copy to desktop: ${e.message}`);
}
return true;
}
}
Signals.addSignalMethods(DesktopTarget.prototype);
let ApplicationsButton = GObject.registerClass(
class ApplicationsButton extends PanelMenu.Button {
_init() {
super._init(1.0, null, false);
this.setMenu(new ApplicationsMenu(this, 1.0, St.Side.TOP, this));
Main.panel.menuManager.addMenu(this.menu);
// At this moment applications menu is not keyboard navigable at
// all (so not accessible), so it doesn't make sense to set as
// role ATK_ROLE_MENU like other elements of the panel.
this.accessible_role = Atk.Role.LABEL;
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._label = new St.Label({
text: _('Applications'),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
hbox.add_child(this._label);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.add_actor(hbox);
this.name = 'panelApplications';
this.label_actor = this._label;
this._showingId = Main.overview.connect('showing', () => {
this.add_accessible_state(Atk.StateType.CHECKED);
});
this._hidingId = Main.overview.connect('hiding', () => {
this.remove_accessible_state(Atk.StateType.CHECKED);
});
Main.layoutManager.connect('startup-complete',
this._setKeybinding.bind(this));
this._setKeybinding();
this._desktopTarget = new DesktopTarget();
this._desktopTarget.connect('app-dropped', () => {
this.menu.close();
});
this._desktopTarget.connect('desktop-changed', () => {
this._applicationsButtons.forEach(item => {
item.setDragEnabled(this._desktopTarget.hasDesktop);
});
});
this._tree = new GMenu.Tree({ menu_basename: 'applications.menu' });
this._treeChangedId = this._tree.connect('changed',
this._onTreeChanged.bind(this));
this._applicationsButtons = new Map();
this.reloadFlag = false;
this._createLayout();
this._display();
this._installedChangedId = appSys.connect('installed-changed',
this._onTreeChanged.bind(this));
}
_onTreeChanged() {
if (this.menu.isOpen) {
this._redisplay();
this.mainBox.show();
} else {
this.reloadFlag = true;
}
}
_createVertSeparator() {
let separator = new St.DrawingArea({
style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted',
});
separator.connect('repaint', this._onVertSepRepaint.bind(this));
return separator;
}
_onDestroy() {
super._onDestroy();
Main.overview.disconnect(this._showingId);
Main.overview.disconnect(this._hidingId);
appSys.disconnect(this._installedChangedId);
this._tree.disconnect(this._treeChangedId);
this._tree = null;
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
Main.sessionMode.hasOverview
? Main.overview.toggle.bind(Main.overview)
: null);
this._desktopTarget.destroy();
}
_onMenuKeyPress(actor, event) {
let symbol = event.get_key_symbol();
if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right) {
let direction = symbol === Clutter.KEY_Left
? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
if (this.menu.actor.navigate_focus(global.stage.key_focus, direction, false))
return true;
}
return super._onMenuKeyPress(actor, event);
}
_onVertSepRepaint(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let stippleColor = themeNode.get_color('-stipple-color');
let stippleWidth = themeNode.get_length('-stipple-width');
let x = Math.floor(width / 2) + 0.5;
cr.moveTo(x, 0);
cr.lineTo(x, height);
Clutter.cairo_set_source_color(cr, stippleColor);
cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth);
cr.stroke();
}
_onOpenStateChanged(menu, open) {
if (open) {
if (this.reloadFlag) {
this._redisplay();
this.reloadFlag = false;
}
this.mainBox.show();
}
super._onOpenStateChanged(menu, open);
}
_setKeybinding() {
Main.wm.setCustomKeybindingHandler('panel-main-menu',
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => this.menu.toggle());
}
_redisplay() {
this.applicationsBox.destroy_all_children();
this.categoriesBox.destroy_all_children();
this._display();
}
_loadCategory(categoryId, dir) {
let iter = dir.iter();
let nextType;
while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) {
if (nextType === GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let id;
try {
id = entry.get_desktop_file_id(); // catch non-UTF8 filenames
} catch (e) {
continue;
}
let app = appSys.lookup_app(id);
if (!app)
app = new Shell.App({ app_info: entry.get_app_info() });
if (app.get_app_info().should_show())
this.applicationsByCategory[categoryId].push(app);
} else if (nextType === GMenu.TreeItemType.SEPARATOR) {
this.applicationsByCategory[categoryId].push('separator');
} else if (nextType === GMenu.TreeItemType.DIRECTORY) {
let subdir = iter.get_directory();
if (!subdir.get_is_nodisplay())
this._loadCategory(categoryId, subdir);
}
}
}
scrollToButton(button) {
let appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
let appsScrollBoxAlloc = this.applicationsScrollBox.get_allocation_box();
let currentScrollValue = appsScrollBoxAdj.get_value();
let boxHeight = appsScrollBoxAlloc.y2 - appsScrollBoxAlloc.y1;
let buttonAlloc = button.get_allocation_box();
let newScrollValue = currentScrollValue;
if (currentScrollValue > buttonAlloc.y1 - 10)
newScrollValue = buttonAlloc.y1 - 10;
if (boxHeight + currentScrollValue < buttonAlloc.y2 + 10)
newScrollValue = buttonAlloc.y2 - boxHeight + 10;
if (newScrollValue !== currentScrollValue)
appsScrollBoxAdj.set_value(newScrollValue);
}
scrollToCatButton(button) {
let catsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
let catsScrollBoxAlloc = this.categoriesScrollBox.get_allocation_box();
let currentScrollValue = catsScrollBoxAdj.get_value();
let boxHeight = catsScrollBoxAlloc.y2 - catsScrollBoxAlloc.y1;
let buttonAlloc = button.get_allocation_box();
let newScrollValue = currentScrollValue;
if (currentScrollValue > buttonAlloc.y1 - 10)
newScrollValue = buttonAlloc.y1 - 10;
if (boxHeight + currentScrollValue < buttonAlloc.y2 + 10)
newScrollValue = buttonAlloc.y2 - boxHeight + 10;
if (newScrollValue !== currentScrollValue)
catsScrollBoxAdj.set_value(newScrollValue);
}
_createLayout() {
let section = new PopupMenu.PopupMenuSection();
this.menu.addMenuItem(section);
this.mainBox = new St.BoxLayout({ vertical: false });
this.leftBox = new St.BoxLayout({ vertical: true });
this.applicationsScrollBox = new St.ScrollView({
style_class: 'apps-menu vfade',
x_expand: true,
});
this.applicationsScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
let vscroll = this.applicationsScrollBox.get_vscroll_bar();
vscroll.connect('scroll-start', () => {
this.menu.passEvents = true;
});
vscroll.connect('scroll-stop', () => {
this.menu.passEvents = false;
});
this.categoriesScrollBox = new St.ScrollView({
style_class: 'vfade',
});
this.categoriesScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
vscroll = this.categoriesScrollBox.get_vscroll_bar();
vscroll.connect('scroll-start', () => (this.menu.passEvents = true));
vscroll.connect('scroll-stop', () => (this.menu.passEvents = false));
this.leftBox.add_child(this.categoriesScrollBox);
this.applicationsBox = new St.BoxLayout({ vertical: true });
this.applicationsScrollBox.add_actor(this.applicationsBox);
this.categoriesBox = new St.BoxLayout({ vertical: true });
this.categoriesScrollBox.add_actor(this.categoriesBox);
this.mainBox.add(this.leftBox);
this.mainBox.add_child(this._createVertSeparator());
this.mainBox.add_child(this.applicationsScrollBox);
section.actor.add_actor(this.mainBox);
}
_display() {
this._applicationsButtons.clear();
this.mainBox.style = 'width: 35em;';
this.mainBox.hide();
// Load categories
this.applicationsByCategory = {};
this._tree.load_sync();
let root = this._tree.get_root_directory();
let categoryMenuItem = new CategoryMenuItem(this, null);
this.categoriesBox.add_actor(categoryMenuItem);
let iter = root.iter();
let nextType;
while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) {
if (nextType !== GMenu.TreeItemType.DIRECTORY)
continue;
let dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
let categoryId = dir.get_menu_id();
this.applicationsByCategory[categoryId] = [];
this._loadCategory(categoryId, dir);
if (this.applicationsByCategory[categoryId].length > 0) {
categoryMenuItem = new CategoryMenuItem(this, dir);
this.categoriesBox.add_actor(categoryMenuItem);
}
}
// Load applications
this._displayButtons(this._listApplications(null));
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let scaleFactor = themeContext.scale_factor;
let categoriesHeight = this.categoriesBox.height / scaleFactor;
let height = Math.round(categoriesHeight) + MENU_HEIGHT_OFFSET;
this.mainBox.style += `height: ${height}px`;
}
selectCategory(dir) {
this.applicationsBox.get_children().forEach(c => {
if (c._delegate instanceof PopupMenu.PopupSeparatorMenuItem)
c._delegate.destroy();
else
this.applicationsBox.remove_actor(c);
});
if (dir)
this._displayButtons(this._listApplications(dir.get_menu_id()));
else
this._displayButtons(this._listApplications(null));
}
_displayButtons(apps) {
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
let item;
if (app instanceof Shell.App)
item = this._applicationsButtons.get(app);
else
item = new PopupMenu.PopupSeparatorMenuItem();
if (!item) {
item = new ApplicationMenuItem(this, app);
item.setDragEnabled(this._desktopTarget.hasDesktop);
this._applicationsButtons.set(app, item);
}
if (!item.get_parent())
this.applicationsBox.add_actor(item);
}
}
_listApplications(categoryMenuId) {
let applist;
if (categoryMenuId) {
applist = this.applicationsByCategory[categoryMenuId];
} else {
applist = global.settings.get_strv('favorite-apps')
.map(id => appSys.lookup_app(id))
.filter(app => app);
}
return applist;
}
});
let appsMenuButton;
function enable() {
appsMenuButton = new ApplicationsButton();
let index = Main.sessionMode.panel.left.indexOf('activities') + 1;
Main.panel.addToStatusArea('apps-menu', appsMenuButton, index, 'left');
}
function disable() {
Main.panel.menuManager.removeMenu(appsMenuButton.menu);
appsMenuButton.destroy();
}
function init() {
ExtensionUtils.initTranslations();
}

View File

@@ -1,5 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"gettext-domain": "@gettext_domain@",
"name": "Applications Menu",
"description": "Add a category-based menu for applications.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
"original-authors": [ "e2002@bk.ru", "debarshir@gnome.org" ],
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1,7 +0,0 @@
.apps-menu:ltr {
padding-right: 3px;
}
.apps-menu:rtl {
padding-left: 3px;
}

View File

@@ -0,0 +1,14 @@
EXTENSION_ID = auto-move-windows
include ../../extension.mk
gschemas_in = org.gnome.shell.extensions.auto-move-windows.gschema.xml.in
@INTLTOOL_XML_NOMERGE_RULE@
gsettings_SCHEMAS = $(gschemas_in:.xml.in=.xml)
@GSETTINGS_RULES@
CLEANFILES += $(gschemas_in:.xml.in=.valid) $(gsettings_SCHEMAS)
EXTRA_DIST += $(gschemas_in)

View File

@@ -1,144 +1,109 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces
/* exported init enable disable */
const { Shell } = imports.gi;
const Glib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
class WindowMover {
constructor() {
this._settings = ExtensionUtils.getSettings();
this._appSystem = Shell.AppSystem.get_default();
this._appConfigs = new Map();
this._appData = new Map();
const SETTINGS_SCHEMA = 'org.gnome.shell.extensions.auto-move-windows';
const SETTINGS_KEY = 'application-list';
this._appsChangedId =
this._appSystem.connect('installed-changed',
this._updateAppData.bind(this));
function WindowMover() {
this._init();
}
this._settings.connect('changed', this._updateAppConfigs.bind(this));
this._updateAppConfigs();
}
WindowMover.prototype = {
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._windowTracker = Shell.WindowTracker.get_default();
_updateAppConfigs() {
this._appConfigs.clear();
let display = global.screen.get_display();
// Connect after so the handler from ShellWindowTracker has already run
display.connect_after('window-created', Lang.bind(this, this._findAndMove));
},
this._settings.get_strv('application-list').forEach(v => {
let [appId, num] = v.split(':');
this._appConfigs.set(appId, parseInt(num) - 1);
});
this._updateAppData();
}
_updateAppData() {
let ids = [...this._appConfigs.keys()];
let removedApps = [...this._appData.keys()].filter(
a => !ids.includes(a.id)
);
removedApps.forEach(app => {
app.disconnect(this._appData.get(app).windowsChangedId);
this._appData.delete(app);
});
let addedApps = ids.map(id => this._appSystem.lookup_app(id)).filter(
app => app && !this._appData.has(app)
);
addedApps.forEach(app => {
let data = {
windowsChangedId: app.connect('windows-changed',
this._appWindowsChanged.bind(this)),
moveWindowsId: 0,
windows: app.get_windows(),
};
this._appData.set(app, data);
});
}
destroy() {
if (this._appsChangedId) {
this._appSystem.disconnect(this._appsChangedId);
this._appsChangedId = 0;
_ensureAtLeastWorkspaces: function(num, window) {
for (let j = global.screen.n_workspaces; j <= num; j++) {
window.change_workspace_by_index(j-1, false, global.get_current_time());
global.screen.append_new_workspace(false, 0);
}
},
if (this._settings) {
this._settings.run_dispose();
this._settings = null;
}
this._appConfigs.clear();
this._updateAppData();
}
_moveWindow(window, workspaceNum) {
if (window.skip_taskbar)
_findAndMove: function(display, window, noRecurse) {
if (!this._windowTracker.is_window_interesting(window))
return;
// ensure we have the required number of workspaces
let workspaceManager = global.workspace_manager;
for (let i = workspaceManager.n_workspaces; i <= workspaceNum; i++) {
window.change_workspace_by_index(i - 1, false);
workspaceManager.append_new_workspace(false, 0);
let spaces = this._settings.get_strv(SETTINGS_KEY);
let app = this._windowTracker.get_window_app(window);
if (!app) {
if (!noRecurse) {
// window is not tracked yet
Mainloop.idle_add(Lang.bind(this, function() {
this._findAndMove(display, window, true);
return false;
}));
} else
log ('Cannot find application for window');
return;
}
let app_id = app.get_id();
for ( let j = 0 ; j < spaces.length; j++ ) {
let apps_to_space = spaces[j].split(":");
// Match application id
if (apps_to_space[0] == app_id) {
let workspace_num = parseInt(apps_to_space[1]) - 1;
if (workspace_num >= global.screen.n_workspaces)
this._ensureAtLeastWorkspaces(workspace_num, window);
window.change_workspace_by_index(workspace_num, false, global.get_current_time());
}
}
}
}
function main(extensionMeta) {
Main._checkWorkspaces = function() {
let i;
let emptyWorkspaces = new Array(Main._workspaces.length);
for (i = 0; i < Main._workspaces.length; i++)
emptyWorkspaces[i] = true;
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
window.change_workspace_by_index(workspaceNum, false);
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
_appWindowsChanged(app) {
let data = this._appData.get(app);
let windows = app.get_windows();
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(Main._workspaces[i], global.get_current_time());
else
break;
}
Main._checkWorkspacesId = 0;
return false;
// If get_compositor_private() returns non-NULL on a removed windows,
// the window still exists and is just moved to a different workspace
// or something; assume it'll be added back immediately, so keep it
// to avoid moving it again
windows.push(...data.windows.filter(
w => !windows.includes(w) && w.get_compositor_private() !== null
));
};
let workspaceNum = this._appConfigs.get(app.id);
windows.filter(w => !data.windows.includes(w)).forEach(window => {
this._moveWindow(window, workspaceNum);
});
data.windows = windows;
}
}
let prevCheckWorkspaces;
let winMover;
function init() {
ExtensionUtils.initTranslations();
}
function myCheckWorkspaces() {
let keepAliveWorkspaces = [];
let foundNonEmpty = false;
for (let i = this._workspaces.length - 1; i >= 0; i--) {
if (!foundNonEmpty)
foundNonEmpty = this._workspaces[i].list_windows().length > 0;
else if (!this._workspaces[i]._keepAliveId)
keepAliveWorkspaces.push(this._workspaces[i]);
}
// make sure the original method only removes empty workspaces at the end
keepAliveWorkspaces.forEach(ws => (ws._keepAliveId = 1));
prevCheckWorkspaces.call(this);
keepAliveWorkspaces.forEach(ws => delete ws._keepAliveId);
return false;
}
function enable() {
prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces;
Main.wm._workspaceTracker._checkWorkspaces = myCheckWorkspaces;
winMover = new WindowMover();
}
function disable() {
Main.wm._workspaceTracker._checkWorkspaces = prevCheckWorkspaces;
winMover.destroy();
new WindowMover();
}

View File

@@ -1,8 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_sources += files('prefs.js')
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')

View File

@@ -1,11 +1,9 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Auto Move Windows",
"description": "Move applications to specific workspaces when they create windows.",
"shell-version": [ "@shell_current@" ],
"description": "Move applications to specific workspaces when they create windows",
"shell-version": [ "3.0.0", "3.0.1", "3.0.2" ],
"localedir": "@LOCALEDIR@",
"original-authors": [ "alessandro.crismani@gmail.com", "thomas.bouffon@gmail.com" ],
"url": "@url@"
}

View File

@@ -3,8 +3,8 @@
<key name="application-list" type="as">
<!-- FIXME: should be a(su), when JS supports more of GVariant -->
<default>[ ]</default>
<summary>Application and workspace list</summary>
<description>A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number</description>
<_summary>Application and workspace list</_summary>
<_description>A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number</_description>
</key>
</schema>
</schemalist>

View File

@@ -1,282 +0,0 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Start apps on custom workspaces
/* exported init buildPrefsWidget */
const { Gio, GObject, Gtk } = imports.gi;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = e => e;
const ExtensionUtils = imports.misc.extensionUtils;
const SETTINGS_KEY = 'application-list';
const WORKSPACE_MAX = 36; // compiled in limit of mutter
const Columns = {
APPINFO: 0,
DISPLAY_NAME: 1,
ICON: 2,
WORKSPACE: 3,
ADJUSTMENT: 4,
};
const Widget = GObject.registerClass(
class Widget extends Gtk.Grid {
_init(params) {
super._init(params);
this.set_orientation(Gtk.Orientation.VERTICAL);
this._settings = ExtensionUtils.getSettings();
this._settings.connect('changed', this._refresh.bind(this));
this._changedPermitted = false;
this._store = new Gtk.ListStore();
this._store.set_column_types([
Gio.AppInfo,
GObject.TYPE_STRING,
Gio.Icon,
GObject.TYPE_INT,
Gtk.Adjustment,
]);
let scrolled = new Gtk.ScrolledWindow({ shadow_type: Gtk.ShadowType.IN });
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.add(scrolled);
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.DISPLAY_NAME,
title: _('Application'),
});
let iconRenderer = new Gtk.CellRendererPixbuf();
appColumn.pack_start(iconRenderer, false);
appColumn.add_attribute(iconRenderer, 'gicon', Columns.ICON);
let nameRenderer = new Gtk.CellRendererText();
appColumn.pack_start(nameRenderer, true);
appColumn.add_attribute(nameRenderer, 'text', Columns.DISPLAY_NAME);
this._treeView.append_column(appColumn);
let workspaceColumn = new Gtk.TreeViewColumn({
title: _('Workspace'),
sort_column_id: Columns.WORKSPACE,
});
let workspaceRenderer = new Gtk.CellRendererSpin({ editable: true });
workspaceRenderer.connect('edited', this._workspaceEdited.bind(this));
workspaceColumn.pack_start(workspaceRenderer, true);
workspaceColumn.add_attribute(workspaceRenderer, 'adjustment', Columns.ADJUSTMENT);
workspaceColumn.add_attribute(workspaceRenderer, 'text', Columns.WORKSPACE);
this._treeView.append_column(workspaceColumn);
scrolled.add(this._treeView);
let toolbar = new Gtk.Toolbar({ icon_size: Gtk.IconSize.SMALL_TOOLBAR });
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
this.add(toolbar);
let newButton = new Gtk.ToolButton({
icon_name: 'bookmark-new-symbolic',
label: _('Add Rule'),
is_important: true,
});
newButton.connect('clicked', this._createNew.bind(this));
toolbar.add(newButton);
let delButton = new Gtk.ToolButton({ icon_name: 'edit-delete-symbolic' });
delButton.connect('clicked', this._deleteSelected.bind(this));
toolbar.add(delButton);
let selection = this._treeView.get_selection();
selection.connect('changed', () => {
delButton.sensitive = selection.count_selected_rows() > 0;
});
delButton.sensitive = selection.count_selected_rows() > 0;
this._changedPermitted = true;
this._refresh();
}
_createNew() {
let dialog = new Gtk.Dialog({
title: _('Create new matching rule'),
transient_for: this.get_toplevel(),
use_header_bar: true,
modal: true,
});
dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
let addButton = 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,
});
dialog._appChooser = new Gtk.AppChooserWidget({ show_all: true });
dialog._appChooser.connect('application-selected', (w, appInfo) => {
addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
});
let appInfo = dialog._appChooser.get_app_info();
addButton.sensitive = appInfo && this._checkId(appInfo.get_id());
grid.attach(dialog._appChooser, 0, 0, 2, 1);
grid.attach(new Gtk.Label({
label: _('Workspace'),
halign: Gtk.Align.END,
}), 0, 1, 1, 1);
let adjustment = new Gtk.Adjustment({
lower: 1,
upper: WORKSPACE_MAX,
step_increment: 1,
});
dialog._spin = new Gtk.SpinButton({
adjustment,
snap_to_ticks: true,
});
dialog._spin.set_value(1);
grid.attach(dialog._spin, 1, 1, 1, 1);
dialog.get_content_area().add(grid);
dialog.connect('response', (dlg, id) => {
if (id !== Gtk.ResponseType.OK) {
dialog.destroy();
return;
}
appInfo = dialog._appChooser.get_app_info();
if (!appInfo)
return;
let index = Math.floor(dialog._spin.value);
if (isNaN(index) || index < 0)
index = 1;
this._changedPermitted = false;
this._appendItem(appInfo.get_id(), index);
this._changedPermitted = true;
this._appendRow(appInfo, index);
dialog.destroy();
});
dialog.show_all();
}
_deleteSelected() {
let [any, model_, iter] = this._treeView.get_selection().get_selected();
if (any) {
let appInfo = this._store.get_value(iter, Columns.APPINFO);
this._changedPermitted = false;
this._removeItem(appInfo.get_id());
this._changedPermitted = true;
this._store.remove(iter);
}
}
_workspaceEdited(renderer, pathString, text) {
let index = parseInt(text);
if (isNaN(index) || index < 0)
index = 1;
let path = Gtk.TreePath.new_from_string(pathString);
let [model_, iter] = this._store.get_iter(path);
let appInfo = this._store.get_value(iter, Columns.APPINFO);
this._changedPermitted = false;
this._changeItem(appInfo.get_id(), index);
this._store.set_value(iter, Columns.WORKSPACE, index);
this._changedPermitted = true;
}
_refresh() {
if (!this._changedPermitted)
// Ignore this notification, model is being modified outside
return;
this._store.clear();
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let validItems = [];
for (let i = 0; i < currentItems.length; i++) {
let [id, index] = currentItems[i].split(':');
let appInfo = Gio.DesktopAppInfo.new(id);
if (!appInfo)
continue;
validItems.push(currentItems[i]);
this._appendRow(appInfo, parseInt(index));
}
if (validItems.length !== currentItems.length) // some items were filtered out
this._settings.set_strv(SETTINGS_KEY, validItems);
}
_appendRow(appInfo, workspace) {
let iter = this._store.append();
let icon = appInfo.get_icon();
let displayName = appInfo.get_display_name();
let adj = new Gtk.Adjustment({
lower: 1,
upper: WORKSPACE_MAX,
step_increment: 1,
value: workspace,
});
let { APPINFO, ICON, DISPLAY_NAME, WORKSPACE, ADJUSTMENT } = Columns;
this._store.set(iter,
[APPINFO, ICON, DISPLAY_NAME, WORKSPACE, ADJUSTMENT],
[appInfo, icon, displayName, workspace, adj]);
}
_checkId(id) {
let items = this._settings.get_strv(SETTINGS_KEY);
return !items.some(i => i.startsWith(`${id}:`));
}
_appendItem(id, workspace) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
currentItems.push(`${id}:${workspace}`);
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
_removeItem(id) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
if (index < 0)
return;
currentItems.splice(index, 1);
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
_changeItem(id, workspace) {
let currentItems = this._settings.get_strv(SETTINGS_KEY);
let index = currentItems.map(el => el.split(':')[0]).indexOf(id);
if (index < 0)
currentItems.push(`${id}:${workspace}`);
else
currentItems[index] = `${id}:${workspace}`;
this._settings.set_strv(SETTINGS_KEY, currentItems);
}
});
function init() {
ExtensionUtils.initTranslations();
}
function buildPrefsWidget() {
let widget = new Widget({ margin: 12 });
widget.show_all();
return widget;
}

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = dock
include ../../extension.mk

View File

@@ -0,0 +1,456 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const AppDisplay = imports.ui.appDisplay;
const AltTab = imports.ui.altTab;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const DOCKICON_SIZE = 48;
const DND_RAISE_APP_TIMEOUT = 500;
function Dock() {
this._init();
}
Dock.prototype = {
_init : function() {
this._placeholderText = null;
this._menus = [];
this._menuDisplays = [];
this._favorites = [];
this._spacing = 4;
this._item_size = DOCKICON_SIZE;
this.actor = new St.BoxLayout({ name: 'dock', vertical: true, reactive: true });
this._grid = new Shell.GenericContainer();
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._grid.connect('allocate', Lang.bind(this, this._allocate));
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
this._tracker = Shell.WindowTracker.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
Main.chrome.addActor(this.actor, { visibleInOverview: false });
this.actor.lower_bottom();
},
_appIdListToHash: function(apps) {
let ids = {};
for (let i = 0; i < apps.length; i++)
ids[apps[i].get_id()] = apps[i];
return ids;
},
_queueRedisplay: function () {
Main.queueDeferredWork(this._workId);
},
_redisplay: function () {
this.removeAll();
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = '';
let running = this._tracker.get_running_apps(contextId);
let runningIds = this._appIdListToHash(running);
let icons = 0;
let nFavorites = 0;
for (let id in favorites) {
let app = favorites[id];
let display = new DockIcon(app);
this.addItem(display.actor);
nFavorites++;
icons++;
}
for (let i = 0; i < running.length; i++) {
let app = running[i];
if (app.get_id() in favorites)
continue;
let display = new DockIcon(app);
icons++;
this.addItem(display.actor);
}
if (this._placeholderText) {
this._placeholderText.destroy();
this._placeholderText = null;
}
if (running.length == 0 && nFavorites == 0) {
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
this.actor.add_actor(this._placeholderText);
}
let primary = global.get_primary_monitor();
let height = (icons)*(this._item_size + this._spacing) + 2*this._spacing;
this.actor.set_size(this._item_size + 4*this._spacing, height);
this.actor.set_position(primary.width-this._item_size-this._spacing-2, (primary.height-height)/2);
},
_getPreferredWidth: function (grid, forHeight, alloc) {
alloc.min_size = this._item_size;
alloc.natural_size = this._item_size + this._spacing;
},
_getPreferredHeight: function (grid, forWidth, alloc) {
let children = this._grid.get_children();
let nRows = children.length;
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
let height = nRows * this._item_size + totalSpacing;
alloc.min_size = height;
alloc.natural_size = height;
},
_allocate: function (grid, box, flags) {
let children = this._grid.get_children();
let x = box.x1 + this._spacing;
let y = box.y1 + this._spacing;
for (let i = 0; i < children.length; i++) {
let childBox = new Clutter.ActorBox();
childBox.x1 = x;
childBox.y1 = y;
childBox.x2 = childBox.x1 + this._item_size;
childBox.y2 = childBox.y1 + this._item_size;
children[i].allocate(childBox, flags);
y += this._item_size + this._spacing;
}
},
_onStyleChanged: function() {
let themeNode = this.actor.get_theme_node();
let [success, len] = themeNode.get_length('spacing', false);
if (success)
this._spacing = len;
[success, len] = themeNode.get_length('-shell-grid-item-size', false);
if (success)
this._item_size = len;
this._grid.queue_relayout();
},
removeAll: function () {
this._grid.get_children().forEach(Lang.bind(this, function (child) {
child.destroy();
}));
},
addItem: function(actor) {
this._grid.add_actor(actor);
}
};
Signals.addSignalMethods(Dock.prototype);
function DockIcon(app) {
this._init(app);
}
DockIcon.prototype = {
_init : function(app) {
this.app = app;
this.actor = new St.Button({ style_class: 'dock-app',
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
reactive: true,
x_fill: true,
y_fill: true });
this.actor._delegate = this;
this.actor.set_size(DOCKICON_SIZE, DOCKICON_SIZE);
this._icon = this.app.create_icon_texture(DOCKICON_SIZE);
this.actor.set_child(this._icon);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._menu = null;
this._menuManager = new PopupMenu.PopupMenuManager(this);
this._has_focus = false;
let tracker = Shell.WindowTracker.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._onStateChanged));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('notify::hover', Lang.bind(this, this._hoverChanged));
this._menuTimeoutId = 0;
this._stateChangedId = this.app.connect('notify::state',
Lang.bind(this, this._onStateChanged));
this._onStateChanged();
},
_onDestroy: function() {
if (this._stateChangedId > 0)
this.app.disconnect(this._stateChangedId);
this._stateChangedId = 0;
this._removeMenuTimeout();
},
_removeMenuTimeout: function() {
if (this._menuTimeoutId > 0) {
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = 0;
}
},
_hoverChanged: function(actor) {
if (actor != this.actor)
this._has_focus = false;
else
this._has_focus = true;
return false;
},
_onStateChanged: function() {
let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app;
if (this.app.state != Shell.AppState.STOPPED) {
this.actor.add_style_class_name('running');
if (this.app == focusedApp) {
this.actor.add_style_class_name('focused');
} else {
this.actor.remove_style_class_name('focused');
}
} else {
this.actor.remove_style_class_name('focused');
this.actor.remove_style_class_name('running');
}
},
_onButtonPress: function(actor, event) {
let button = event.get_button();
if (button == 1) {
this._removeMenuTimeout();
this._menuTimeoutId = Mainloop.timeout_add(AppDisplay.MENU_POPUP_TIMEOUT, Lang.bind(this, function() {
this.popupMenu();
}));
} else if (button == 3) {
this.popupMenu();
}
},
_onClicked: function(actor, button) {
this._removeMenuTimeout();
if (button == 1) {
this._onActivate(Clutter.get_current_event());
} else if (button == 2) {
// Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1);
}
return false;
},
getId: function() {
return this.app.get_id();
},
popupMenu: function() {
this._removeMenuTimeout();
this.actor.fake_release();
if (!this._menu) {
this._menu = new DockIconMenu(this);
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
this.activateWindow(window);
}));
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp)
this._onMenuPoppedDown();
}));
this._menuManager.addMenu(this._menu, true);
}
this._menu.popup();
return false;
},
activateWindow: function(metaWindow) {
if (metaWindow) {
this._didActivateWindow = true;
Main.activateWindow(metaWindow);
}
},
setSelected: function (isSelected) {
this._selected = isSelected;
if (this._selected)
this.actor.add_style_class_name('selected');
else
this.actor.remove_style_class_name('selected');
},
_onMenuPoppedDown: function() {
this.actor.sync_hover();
},
_getRunning: function() {
return this.app.state != Shell.AppState.STOPPED;
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
let current_workspace = global.screen.get_active_workspace().index();
this.app.open_new_window(current_workspace);
} else {
let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app;
if (this.app == focusedApp) {
let windows = this.app.get_windows();
let current_workspace = global.screen.get_active_workspace();
for (let i = 0; i < windows.length; i++) {
let w = windows[i];
if (w.get_workspace() == current_workspace)
w.minimize();
}
} else {
this.app.activate(-1);
}
}
Main.overview.hide();
},
shellWorkspaceLaunch : function() {
this.app.open_new_window();
}
};
Signals.addSignalMethods(DockIcon.prototype);
function DockIconMenu(source) {
this._init(source);
}
DockIconMenu.prototype = {
__proto__: AppDisplay.AppIconMenu.prototype,
_init: function(source) {
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.RIGHT, 0);
this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate));
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.actor.add_style_class_name('dock-menu');
// Chain our visibility and lifecycle to that of the source
source.actor.connect('notify::mapped', Lang.bind(this, function () {
if (!source.actor.mapped)
this.close();
}));
source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));
Main.chrome.addActor(this.actor);
},
_redisplay: function() {
this.removeAll();
let windows = this._source.app.get_windows();
// Display the app windows menu items and the separator between windows
// of the current desktop and other windows.
let activeWorkspace = global.screen.get_active_workspace();
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
for (let i = 0; i < windows.length; i++) {
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
this._appendSeparator();
separatorShown = true;
}
let item = this._appendMenuItem(windows[i].title);
item._window = windows[i];
}
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
this._quitAppMenuItem = windows.length >0 ? this._appendMenuItem(_("Quit Application")) : null;
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ?
_("Remove from Favorites")
: _("Add to Favorites"));
this._highlightedItem = null;
},
_onActivate: function (actor, child) {
if (child._window) {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
let current_workspace = global.screen.get_active_workspace().index();
this._source.app.open_new_window(current_workspace);
this.emit('activate-window', null);
} else if (child == this._quitAppMenuItem) {
this._source.app.request_quit();
} else if (child == this._toggleFavoriteMenuItem) {
let favs = AppFavorites.getAppFavorites();
let isFavorite = favs.isFavorite(this._source.app.get_id());
if (isFavorite)
favs.removeFavorite(this._source.app.get_id());
else
favs.addFavorite(this._source.app.get_id());
}
this.close();
}
}
function main(extensionMeta) {
imports.gettext.bindtextdomain('gnome-shell-extensions', extensionMeta.localedir);
let dock = new Dock();
}

View File

@@ -0,0 +1,9 @@
{
"uuid": "@uuid@",
"name": "Dock",
"description": "A dock for the GNOME Shell -- displays favorite and running applications",
"original-author": "tclaesson@gmail.com",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"url": "@url@"
}

View File

@@ -0,0 +1,54 @@
#dock {
border-radius: 9px;
background-color: rgba(0,0,0,0.9);
border-width: 2px;
border-color: #5f5f5f;
}
/* Panel */
.dock-app {
padding: 4px;
width: 70px;
height: 70px;
border-radius: 4px;
transition-duration: 100;
}
.dock-app.running {
padding: 3px;
border: 1px solid #181818;
background-gradient-direction: vertical;
background-gradient-start: #3d3d3d;
background-gradient-end: #181818;
}
.dock-app.selected {
padding: 3px;
border: 1px solid #666666;
}
.dock-app.focused {
padding: 3px;
border: 1px solid #5f5f5f;
background-gradient-direction: vertical;
background-gradient-start: rgba(61,61,61,0.8);
background-gradient-end: rgba(24,24,24,0.2);
}
.dock-app:hover {
padding: 3px;
border: 1px solid #666666;
background-gradient-direction: vertical;
background-gradient-start: rgba(61,61,61,0.8);
background-gradient-end: rgba(24,24,24,0.2);
transition-duration: 100;
}
.dock-app:active {
padding: 3px;
background-color: #1e1e1e;
border: 1px solid #5f5f5f;
}
.dock-menu {
font-size: 12px
}

View File

@@ -1,205 +0,0 @@
/* exported init enable disable */
// Drive menu extension
const { Gio, GObject, Shell, St } = imports.gi;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ShellMountOperation = imports.ui.shellMountOperation;
var MountMenuItem = GObject.registerClass(
class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(mount) {
super._init();
this.label = new St.Label({ text: mount.get_name() });
this.add_child(this.label);
this.label_actor = this.label;
this.mount = mount;
this.connect('destroy', this._onDestroy.bind(this));
let ejectIcon = new St.Icon({
icon_name: 'media-eject-symbolic',
style_class: 'popup-menu-icon',
});
let ejectButton = new St.Button({ child: ejectIcon });
ejectButton.connect('clicked', this._eject.bind(this));
this.add(ejectButton);
this._changedId = mount.connect('changed', this._syncVisibility.bind(this));
this._syncVisibility();
}
_onDestroy() {
if (this._changedId) {
this.mount.disconnect(this._changedId);
this._changedId = 0;
}
super.destroy();
}
_isInteresting() {
if (!this.mount.can_eject() && !this.mount.can_unmount())
return false;
if (this.mount.is_shadowed())
return false;
let volume = this.mount.get_volume();
if (!volume) {
// probably a GDaemonMount, could be network or
// local, but we can't tell; assume it's local for now
return true;
}
return volume.get_identifier('class') !== 'network';
}
_syncVisibility() {
this.visible = this._isInteresting();
}
_eject() {
let unmountArgs = [
Gio.MountUnmountFlags.NONE,
new ShellMountOperation.ShellMountOperation(this.mount).mountOp,
null, // Gio.Cancellable
];
if (this.mount.can_eject()) {
this.mount.eject_with_operation(...unmountArgs,
this._ejectFinish.bind(this));
} else {
this.mount.unmount_with_operation(...unmountArgs,
this._unmountFinish.bind(this));
}
}
_unmountFinish(mount, result) {
try {
mount.unmount_with_operation_finish(result);
} catch (e) {
this._reportFailure(e);
}
}
_ejectFinish(mount, result) {
try {
mount.eject_with_operation_finish(result);
} catch (e) {
this._reportFailure(e);
}
}
_reportFailure(exception) {
// TRANSLATORS: %s is the filesystem name
let msg = _('Ejecting drive “%s” failed:').format(this.mount.get_name());
Main.notifyError(msg, exception.message);
}
activate(event) {
let uri = this.mount.get_root().get_uri();
let context = global.create_app_launch_context(event.get_time(), -1);
Gio.AppInfo.launch_default_for_uri(uri, context);
super.activate(event);
}
});
let DriveMenu = GObject.registerClass(
class DriveMenu extends PanelMenu.Button {
_init() {
super._init(0.0, _('Removable devices'));
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
let icon = new St.Icon({
icon_name: 'media-eject-symbolic',
style_class: 'system-status-icon',
});
hbox.add_child(icon);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.add_child(hbox);
this._monitor = Gio.VolumeMonitor.get();
this._addedId = this._monitor.connect('mount-added', (monitor, mount) => {
this._addMount(mount);
this._updateMenuVisibility();
});
this._removedId = this._monitor.connect('mount-removed', (monitor, mount) => {
this._removeMount(mount);
this._updateMenuVisibility();
});
this._mounts = [];
this._monitor.get_mounts().forEach(this._addMount.bind(this));
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_('Open Files'), event => {
let appSystem = Shell.AppSystem.get_default();
let app = appSystem.lookup_app('org.gnome.Nautilus.desktop');
app.activate_full(-1, event.get_time());
});
this._updateMenuVisibility();
}
_updateMenuVisibility() {
if (this._mounts.filter(i => i.visible).length > 0)
this.show();
else
this.hide();
}
_addMount(mount) {
let item = new MountMenuItem(mount);
this._mounts.unshift(item);
this.menu.addMenuItem(item, 0);
}
_removeMount(mount) {
for (let i = 0; i < this._mounts.length; i++) {
let item = this._mounts[i];
if (item.mount === mount) {
item.destroy();
this._mounts.splice(i, 1);
return;
}
}
log('Removing a mount that was never added to the menu');
}
_onDestroy() {
if (this._addedId) {
this._monitor.disconnect(this._addedId);
this._monitor.disconnect(this._removedId);
this._addedId = 0;
this._removedId = 0;
}
super._onDestroy();
}
});
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
function enable() {
_indicator = new DriveMenu();
Main.panel.addToStatusArea('drive-menu', _indicator);
}
function disable() {
_indicator.destroy();
}

View File

@@ -1,5 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Removable Drive Menu",
"description": "A status menu for accessing and unmounting removable devices.",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1 +0,0 @@
/* This extensions requires no custom styling */

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = example
include ../../extension.mk

View File

@@ -0,0 +1,24 @@
// Sample extension code, makes clicking on the panel show a message
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const Main = imports.ui.main;
function _showHello() {
let text = new St.Label({ style_class: 'helloworld-label', text: _("Hello, world!") });
let monitor = global.get_primary_monitor();
global.stage.add_actor(text);
text.set_position(Math.floor (monitor.width / 2 - text.width / 2), Math.floor(monitor.height / 2 - text.height / 2));
Mainloop.timeout_add(3000, function () { text.destroy(); });
}
// Put your extension initialization code here
function main(metadata) {
imports.gettext.bindtextdomain('gnome-shell-extensions', metadata.localedir);
Main.panel.actor.reactive = true;
Main.panel.actor.connect('button-release-event', _showHello);
}

View File

@@ -0,0 +1,8 @@
{
"uuid": "@uuid@",
"name": "Hello, World!",
"description": "An example extension to show how it works. Shows Hello, world when clicking on the top panel.",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"url": "@url@"
}

View File

@@ -1,8 +1,8 @@
.screenshot-sizer-message {
/* Example stylesheet */
.helloworld-label {
font-size: 36px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 5px;
padding: .5em;
}

View File

@@ -0,0 +1,3 @@
EXTENSION_ID = gajim
include ../../extension.mk

View File

@@ -0,0 +1,313 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Gettext = imports.gettext.domain('gnome-shell');
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const MessageTray = imports.ui.messageTray;
const Shell = imports.gi.Shell;
const TelepathyClient = imports.ui.telepathyClient;
const _ = Gettext.gettext;
// http://ntt.cc/ext/base64-Encoding-Decoding.html
const keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function decode64(input) {
let output = "";
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return unescape(output);
}
function wrappedText(text, sender, timestamp, direction) {
return {
messageType: Tp.ChannelTextMessageType.NORMAL,
text: text,
sender: sender,
timestamp: timestamp,
direction: direction
};
}
function Source(gajimClient, accountName, author, initialMessage) {
this._init(gajimClient, accountName, author, initialMessage);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(gajimClient, accountName, author, initialMessage) {
MessageTray.Source.prototype._init.call(this, author);
this.isChat = true;
this._author = author;
this._gajimClient = gajimClient;
this._accountName = accountName;
this._initialMessage = initialMessage;
this._iconUri = null;
this._presence = "online";
this._notification = new TelepathyClient.Notification(this);
this._notification.setUrgency(MessageTray.Urgency.HIGH);
let jid = author.split('/')[0];
let proxy = this._gajimClient.proxy();
proxy.contact_infoRemote(jid, Lang.bind(this, this._gotContactInfos));
this._statusChangeId = proxy.connect('ContactStatus',
Lang.bind(this, this._onStatusChange));
this._contactAbsenceId = proxy.connect('ContactAbsence',
Lang.bind(this, this._onStatusChange));
this._chatStateId = proxy.connect('ChatState',
Lang.bind(this, this._onChatState));
this._messageSentId = proxy.connect('MessageSent',
Lang.bind(this, this._messageSent));
this._newMessageId = proxy.connect('NewMessage',
Lang.bind(this, this._messageReceived));
},
destroy: function() {
let proxy = this._gajimClient.proxy();
proxy.disconnect(this._statusChangeId);
proxy.disconnect(this._contactAbsenceId);
proxy.disconnect(this._chatStateId);
proxy.disconnect(this._messageSentId);
proxy.disconnect(this._newMessageId);
MessageTray.Source.prototype.destroy.call(this);
},
_gotContactInfos: function(result, excp) {
this.title = result['FN'];
let avatarUri = null;
if (result['PHOTO']) {
let mimeType = result['PHOTO']['TYPE'];
let avatarData = decode64(result['PHOTO']['BINVAL']);
let sha = result['PHOTO']['SHA'];
avatarUri = this._gajimClient.cacheAvatar(mimeType, sha, avatarData);
}
this._iconUri = avatarUri;
this._setSummaryIcon(this.createNotificationIcon());
let message = wrappedText(this._initialMessage, this._author, null, TelepathyClient.NotificationDirection.RECEIVED);
this._notification.appendMessage(message, false);
if (!Main.messageTray.contains(this))
Main.messageTray.add(this);
this.notify(this._notification);
},
createNotificationIcon: function() {
let iconBox = new St.Bin({ style_class: 'avatar-box' });
iconBox._size = this.ICON_SIZE;
if (!this._iconUri) {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconBox._size });
} else {
let textureCache = St.TextureCache.get_default();
iconBox.child = textureCache.load_uri_async(this._iconUri, iconBox._size, iconBox._size);
}
return iconBox;
},
open: function(notification) {
// Lookup for the messages window and display it. In the case where it's not o
// opened yet fallback to the roster window.
let windows = global.get_window_actors();
for (let i = 0; i < windows.length; i++) {
let metaWindow = windows[i].metaWindow;
if (metaWindow.get_wm_class_instance() == "gajim" &&
metaWindow.get_role() == "messages") {
Main.activateWindow(metaWindow);
return;
}
}
let app = Shell.AppSystem.get_default().get_app('gajim.desktop');
app.activate_window(null, global.get_current_time());
},
_onChatState: function(emitter, data) {
let chatstate = data[1][5];
if (chatstate == 'gone')
this.destroy();
},
_messageReceived: function(emitter, data) {
let author = data[1][0];
let text = data[1][1];
if (text && (author == this._author)) {
let message = wrappedText(text, this._author, null, TelepathyClient.NotificationDirection.RECEIVED);
this._notification.appendMessage(message, false);
this.notify(this._notification);
}
},
_messageSent: function(emitter, data) {
let text = data[1][1];
let chatstate = data[1][3];
if (text) {
let message = wrappedText(text, this._author, null, TelepathyClient.NotificationDirection.SENT);
this._notification.appendMessage(message, false);
} else if (chatstate == 'gone')
this.destroy();
},
notify: function() {
MessageTray.Source.prototype.notify.call(this, this._notification);
},
respond: function(text) {
let jid = this._author;
let keyID = ""; // unencrypted.
this._gajimClient.proxy().send_chat_messageRemote(jid, text, keyID, this._accountName);
},
_onStatusChange: function(emitter, data) {
if (!this.title)
return;
let jid = data[1][0];
let presence = data[1][1];
let message = data[1][2];
if (jid != this._author.split('/')[0])
return;
let presenceMessage, shouldNotify, title;
title = GLib.markup_escape_text(this.title, -1);
if (presence == "away") {
presenceMessage = _("%s is away.").format(title);
shouldNotify = false;
} else if (presence == "offline") {
presenceMessage = _("%s is offline.").format(title);
shouldNotify = (this._presence != "offline");
} else if (presence == "online") {
presenceMessage = _("%s is online.").format(title);
shouldNotify = (this._presence == "offline");
} else if (presence == "dnd") {
presenceMessage = _("%s is busy.").format(title);
shouldNotify = false;
} else
return;
this._presence = presence;
if (message)
presenceMessage += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
this._notification.appendPresence(presenceMessage, shouldNotify);
if (shouldNotify)
this.notify(this._notification);
}
};
const GajimIface = {
name: 'org.gajim.dbus.RemoteInterface',
properties: [],
methods: [{ name: 'send_chat_message', inSignature: 'ssss', outSignature: 'b'},
{ name: 'contact_info', inSignature: 's', outSignature: 'a{sv}'}],
signals: [{ name: 'NewMessage', inSignature: 'av' },
{ name: 'ChatState', inSignature: 'av' },
{ name: 'ContactStatus', inSignature: 'av' },
{ name: 'ContactAbsence', inSignature: 'av' },
{ name: 'MessageSent', inSignature: 'av' }]
};
let Gajim = DBus.makeProxyClass(GajimIface);
function GajimClient() {
this._init();
}
GajimClient.prototype = {
_init: function() {
this._sources = {};
this._cacheDir = GLib.get_user_cache_dir() + '/gnome-shell/gajim-avatars';
GLib.mkdir_with_parents(this._cacheDir, 0x1c0); // 0x1c0 = octal 0700
this._proxy = new Gajim(DBus.session, 'org.gajim.dbus', '/org/gajim/dbus/RemoteObject');
this._proxy.connect('NewMessage', Lang.bind(this, this._messageReceived));
},
proxy : function() {
return this._proxy;
},
_messageReceived : function(emitter, data) {
let author = data[1][0];
let message = data[1][1];
let account = data[0];
let source = this._sources[author];
if (!source) {
source = new Source(this, account, author, message);
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[author];
}));
this._sources[author] = source;
}
},
cacheAvatar : function(mimeType, sha, avatarData) {
let ext = mimeType.split('/')[1];
let file = this._cacheDir + '/' + sha + '.' + ext;
let uri = GLib.filename_to_uri(file, null);
if (GLib.file_test(file, GLib.FileTest.EXISTS))
return uri;
let success = false;
try {
success = GLib.file_set_contents(file, avatarData, avatarData.length);
} catch (e) {
logError(e, 'Error caching avatar data');
}
return uri;
}
};
function main() {
let client = new GajimClient();
}

View File

@@ -0,0 +1,8 @@
{
"uuid": "@uuid@",
"name": "Gajim IM integration",
"description": "Display Gajim incoming chats as notifications in the Shell message tray.",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"url": "http://base-art.net"
}

View File

View File

@@ -1,38 +0,0 @@
/* exported init */
const { Meta } = imports.gi;
const { ThumbnailsBox } = imports.ui.workspaceThumbnail;
class Extension {
constructor() {
this._origUpdateSwitcherVisibility =
ThumbnailsBox.prototype._updateSwitcherVisibility;
}
enable() {
global.workspace_manager.override_workspace_layout(
Meta.DisplayCorner.TOPLEFT,
false,
1,
-1);
ThumbnailsBox.prototype._updateSwitcherVisibility = function () {
this.hide();
};
}
disable() {
global.workspace_manager.override_workspace_layout(
Meta.DisplayCorner.TOPLEFT,
false,
-1,
1);
ThumbnailsBox.prototype._updateSwitcherVisibility =
this._origUpdateSwitcherVisibility;
}
}
function init() {
return new Extension();
}

View File

@@ -1,5 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Horizontal workspaces",
"description": "Use a horizontal workspace layout",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1,15 +0,0 @@
/* exported enable disable */
const AppDisplay = imports.ui.appDisplay;
let _activateOriginal = null;
function enable() {
_activateOriginal = AppDisplay.AppIcon.prototype.activate;
AppDisplay.AppIcon.prototype.activate = function () {
_activateOriginal.call(this, 2);
};
}
function disable() {
AppDisplay.AppIcon.prototype.activate = _activateOriginal;
}

View File

@@ -1,5 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Launch new instance",
"description": "Always launch a new instance when clicking in the dash or the application view.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1 +0,0 @@
/* This extensions requires no special styling */

View File

@@ -1,45 +0,0 @@
extension_schemas = []
js_sources = []
metadata_name = 'metadata.json'
foreach e : all_extensions
uuid = e + uuid_suffix
metadata_conf = configuration_data()
metadata_conf.set('extension_id', e)
metadata_conf.set('uuid', uuid)
metadata_conf.set('gschemaname', 'org.gnome.shell.extensions.' + e)
metadata_conf.set('gettext_domain', gettext_domain)
metadata_conf.set('shell_current', shell_version)
metadata_conf.set('url', 'https://gitlab.gnome.org/GNOME/gnome-shell-extensions')
extension_sources = files(e + '/extension.js')
extension_data = files(e + '/stylesheet.css')
subdir(e)
js_sources += extension_sources
if (enabled_extensions.contains(e))
install_data (extension_sources + extension_data,
install_dir: join_paths(extensiondir, uuid)
)
endif
endforeach
install_data (extension_schemas,
install_dir: schemadir
)
foreach js_source : js_sources
if (js68.found())
path_array = '@0@'.format(js_source).split('/')
name = join_paths(path_array[-2], path_array[-1])
test('Checking syntax of ' + name, js68,
args: ['-s', '-c', js_source],
workdir: meson.current_source_dir()
)
endif
endforeach

View File

@@ -1,8 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
# extension_sources += files('prefs.js')
# extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')

View File

@@ -1,282 +0,0 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported enable disable */
const ExtensionUtils = imports.misc.extensionUtils;
const Workspace = imports.ui.workspace;
// testing settings for natural window placement strategy:
const WINDOW_PLACEMENT_NATURAL_ACCURACY = 20; // accuracy of window translate moves (KDE-default: 20)
const WINDOW_PLACEMENT_NATURAL_GAPS = 5; // half of the minimum gap between windows
const WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS = 5000; // safety limit for preventing endless loop if something is wrong in the algorithm
class Rect {
constructor(x, y, width, height) {
[this.x, this.y, this.width, this.height] = [x, y, width, height];
}
// used in _calculateWindowTransformationsNatural to replace Meta.Rectangle that is too slow.
copy() {
return new Rect(this.x, this.y, this.width, this.height);
}
union(rect2) {
let dest = this.copy();
if (rect2.x < dest.x) {
dest.width += dest.x - rect2.x;
dest.x = rect2.x;
}
if (rect2.y < dest.y) {
dest.height += dest.y - rect2.y;
dest.y = rect2.y;
}
if (rect2.x + rect2.width > dest.x + dest.width)
dest.width = rect2.x + rect2.width - dest.x;
if (rect2.y + rect2.height > dest.y + dest.height)
dest.height = rect2.y + rect2.height - dest.y;
return dest;
}
adjusted(dx, dy, dx2, dy2) {
let dest = this.copy();
dest.x += dx;
dest.y += dy;
dest.width += -dx + dx2;
dest.height += -dy + dy2;
return dest;
}
overlap(rect2) {
return !(this.x + this.width <= rect2.x ||
rect2.x + rect2.width <= this.x ||
this.y + this.height <= rect2.y ||
rect2.y + rect2.height <= this.y);
}
center() {
return [this.x + this.width / 2, this.y + this.height / 2];
}
translate(dx, dy) {
this.x += dx;
this.y += dy;
}
}
class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
constructor(settings) {
super();
this._settings = settings;
}
computeLayout(windows, layout) {
layout.windows = windows;
}
/*
* Returns clones with matching target coordinates and scales to arrange windows in a natural way that no overlap exists and relative window size is preserved.
* This function is almost a 1:1 copy of the function
* PresentWindowsEffect::calculateWindowTransformationsNatural() from KDE, see:
* https://projects.kde.org/projects/kde/kdebase/kde-workspace/repository/revisions/master/entry/kwin/effects/presentwindows/presentwindows.cpp
*/
computeWindowSlots(layout, area) {
// As we are using pseudo-random movement (See "slot") we need to make sure the list
// is always sorted the same way no matter which window is currently active.
let areaRect = new Rect(area.x, area.y, area.width, area.height);
let bounds = areaRect.copy();
let clones = layout.windows;
let direction = 0;
let directions = [];
let rects = [];
for (let i = 0; i < clones.length; i++) {
// save rectangles into 4-dimensional arrays representing two corners of the rectangular: [left_x, top_y, right_x, bottom_y]
let rect = clones[i].metaWindow.get_frame_rect();
rects[i] = new Rect(rect.x, rect.y, rect.width, rect.height);
bounds = bounds.union(rects[i]);
// This is used when the window is on the edge of the screen to try to use as much screen real estate as possible.
directions[i] = direction;
direction++;
if (direction === 4)
direction = 0;
}
let loopCounter = 0;
let overlap;
do {
overlap = false;
for (let i = 0; i < rects.length; i++) {
for (let j = 0; j < rects.length; j++) {
let adjustments = [-1, -1, 1, 1]
.map(v => (v *= WINDOW_PLACEMENT_NATURAL_GAPS));
let iAdjusted = rects[i].adjusted(...adjustments);
let jAdjusted = rects[j].adjusted(...adjustments);
if (i !== j && iAdjusted.overlap(jAdjusted)) {
loopCounter++;
overlap = true;
// TODO: something like a Point2D would be nicer here:
// Determine pushing direction
let iCenter = rects[i].center();
let jCenter = rects[j].center();
let diff = [jCenter[0] - iCenter[0], jCenter[1] - iCenter[1]];
// Prevent dividing by zero and non-movement
if (diff[0] === 0 && diff[1] === 0)
diff[0] = 1;
// Try to keep screen/workspace aspect ratio
if (bounds.height / bounds.width > areaRect.height / areaRect.width)
diff[0] *= 2;
else
diff[1] *= 2;
// Approximate a vector of between 10px and 20px in magnitude in the same direction
let length = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
diff[0] = diff[0] * WINDOW_PLACEMENT_NATURAL_ACCURACY / length;
diff[1] = diff[1] * WINDOW_PLACEMENT_NATURAL_ACCURACY / length;
// Move both windows apart
rects[i].translate(-diff[0], -diff[1]);
rects[j].translate(diff[0], diff[1]);
if (this._settings.get_boolean('use-more-screen')) {
// Try to keep the bounding rect the same aspect as the screen so that more
// screen real estate is utilised. We do this by splitting the screen into nine
// equal sections, if the window center is in any of the corner sections pull the
// window towards the outer corner. If it is in any of the other edge sections
// alternate between each corner on that edge. We don't want to determine it
// randomly as it will not produce consistant locations when using the filter.
// Only move one window so we don't cause large amounts of unnecessary zooming
// in some situations. We need to do this even when expanding later just in case
// all windows are the same size.
// (We are using an old bounding rect for this, hopefully it doesn't matter)
let xSection = Math.round((rects[i].x - bounds.x) / (bounds.width / 3));
let ySection = Math.round((rects[i].y - bounds.y) / (bounds.height / 3));
iCenter = rects[i].center();
diff[0] = 0;
diff[1] = 0;
if (xSection !== 1 || ySection !== 1) { // Remove this if you want the center to pull as well
if (xSection === 1)
xSection = directions[i] / 2 ? 2 : 0;
if (ySection === 1)
ySection = directions[i] % 2 ? 2 : 0;
}
if (xSection === 0 && ySection === 0) {
diff[0] = bounds.x - iCenter[0];
diff[1] = bounds.y - iCenter[1];
}
if (xSection === 2 && ySection === 0) {
diff[0] = bounds.x + bounds.width - iCenter[0];
diff[1] = bounds.y - iCenter[1];
}
if (xSection === 2 && ySection === 2) {
diff[0] = bounds.x + bounds.width - iCenter[0];
diff[1] = bounds.y + bounds.height - iCenter[1];
}
if (xSection === 0 && ySection === 2) {
diff[0] = bounds.x - iCenter[0];
diff[1] = bounds.y + bounds.height - iCenter[1];
}
if (diff[0] !== 0 || diff[1] !== 0) {
length = Math.sqrt(diff[0] * diff[0] + diff[1] * diff[1]);
diff[0] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2; // /2 to make it less influencing than the normal center-move above
diff[1] *= WINDOW_PLACEMENT_NATURAL_ACCURACY / length / 2;
rects[i].translate(diff[0], diff[1]);
}
}
// Update bounding rect
bounds = bounds.union(rects[i]);
bounds = bounds.union(rects[j]);
}
}
}
} while (overlap && loopCounter < WINDOW_PLACEMENT_NATURAL_MAX_TRANSLATIONS);
// Work out scaling by getting the most top-left and most bottom-right window coords.
let scale;
scale = Math.min(
areaRect.width / bounds.width,
areaRect.height / bounds.height,
1.0);
// Make bounding rect fill the screen size for later steps
bounds.x -= (areaRect.width - bounds.width * scale) / 2;
bounds.y -= (areaRect.height - bounds.height * scale) / 2;
bounds.width = areaRect.width / scale;
bounds.height = areaRect.height / scale;
// Move all windows back onto the screen and set their scale
for (let i = 0; i < rects.length; i++)
rects[i].translate(-bounds.x, -bounds.y);
// rescale to workspace
let slots = [];
for (let i = 0; i < rects.length; i++) {
rects[i].x = rects[i].x * scale + areaRect.x;
rects[i].y = rects[i].y * scale + areaRect.y;
slots.push([rects[i].x, rects[i].y, scale, clones[i]]);
}
return slots;
}
}
let winInjections, workspaceInjections;
function resetState() {
winInjections = { };
workspaceInjections = { };
}
function enable() {
resetState();
let settings = ExtensionUtils.getSettings();
workspaceInjections['_getBestLayout'] = Workspace.Workspace.prototype._getBestLayout;
Workspace.Workspace.prototype._getBestLayout = function (windows) {
let strategy = new NaturalLayoutStrategy(settings);
let layout = { strategy };
strategy.computeLayout(windows, layout);
return layout;
};
// position window titles on top of windows in overlay
winInjections['relayout'] = Workspace.WindowOverlay.prototype.relayout;
Workspace.WindowOverlay.prototype.relayout = function (animate) {
if (settings.get_boolean('window-captions-on-top')) {
let [, , , cloneHeight] = this._windowClone.slot;
this.title.translation_y = -cloneHeight;
}
winInjections['relayout'].call(this, animate);
};
}
function removeInjection(object, injection, name) {
if (injection[name] === undefined)
delete object[name];
else
object[name] = injection[name];
}
function disable() {
var i;
for (i in workspaceInjections)
removeInjection(Workspace.Workspace.prototype, workspaceInjections, i);
for (i in winInjections)
removeInjection(Workspace.WindowOverlay.prototype, winInjections, i);
global.stage.queue_relayout();
resetState();
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')

View File

@@ -1,11 +0,0 @@
{
"uuid": "@uuid@",
"extension-id": "@extension_id@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Native Window Placement",
"description": "Arrange windows in overview in a more compact way.",
"shell-version": [ "@shell_current@" ],
"url": "@url@",
"original-authors": [ "wepmaschda@gmx.de" ]
}

View File

@@ -1,16 +0,0 @@
<schemalist gettext-domain="gnome-shell-extensions">
<schema id="org.gnome.shell.extensions.native-window-placement" path="/org/gnome/shell/extensions/native-window-placement/">
<key name="use-more-screen" type="b">
<default>true</default>
<summary>Use more screen for windows</summary>
<description>Try to use more screen for placing window thumbnails by adapting to screen aspect ratio, and consolidating
them further to reduce the bounding box. This setting applies only with the natural placement strategy.</description>
</key>
<key name="window-captions-on-top" type="b">
<default>true</default>
<summary>Place window captions on top</summary>
<description>If true, place window captions on top the respective thumbnail, overriding shell default of placing it at
the bottom. Changing this setting requires restarting the shell to have any effect.</description>
</key>
</schema>
</schemalist>

View File

@@ -1,9 +0,0 @@
.window-caption {
-shell-caption-spacing: 13px; /* current caption height is 26px => set it to half of it. TODO: better solution needed */
}
.window-picker {
-horizontal-spacing: 32px;
-vertical-spacing: 32px;
padding: 64px 32px;
}

View File

@@ -1,147 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/* exported init enable disable */
const { Clutter, GObject, St } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = x => x;
const Me = ExtensionUtils.getCurrentExtension();
const PlaceDisplay = Me.imports.placeDisplay;
const PLACE_ICON_SIZE = 16;
var PlaceMenuItem = GObject.registerClass(
class PlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(info) {
super._init();
this._info = info;
this._icon = new St.Icon({
gicon: info.icon,
icon_size: PLACE_ICON_SIZE,
});
this.add_child(this._icon);
this._label = new St.Label({ text: info.name, x_expand: true });
this.add_child(this._label);
if (info.isRemovable()) {
this._ejectIcon = new St.Icon({
icon_name: 'media-eject-symbolic',
style_class: 'popup-menu-icon',
});
this._ejectButton = new St.Button({ child: this._ejectIcon });
this._ejectButton.connect('clicked', info.eject.bind(info));
this.add_child(this._ejectButton);
}
this._changedId = info.connect('changed',
this._propertiesChanged.bind(this));
}
destroy() {
if (this._changedId) {
this._info.disconnect(this._changedId);
this._changedId = 0;
}
super.destroy();
}
activate(event) {
this._info.launch(event.get_time());
super.activate(event);
}
_propertiesChanged(info) {
this._icon.gicon = info.icon;
this._label.text = info.name;
}
});
const SECTIONS = [
'special',
'devices',
'bookmarks',
'network',
];
let PlacesMenu = GObject.registerClass(
class PlacesMenu extends PanelMenu.Button {
_init() {
super._init(0.0, _('Places'));
let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
let label = new St.Label({
text: _('Places'),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
hbox.add_child(label);
hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.add_actor(hbox);
this.placesManager = new PlaceDisplay.PlacesManager();
this._sections = { };
for (let i = 0; i < SECTIONS.length; i++) {
let id = SECTIONS[i];
this._sections[id] = new PopupMenu.PopupMenuSection();
this.placesManager.connect(`${id}-updated`, () => {
this._redisplay(id);
});
this._create(id);
this.menu.addMenuItem(this._sections[id]);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
}
}
_onDestroy() {
this.placesManager.destroy();
super._onDestroy();
}
_redisplay(id) {
this._sections[id].removeAll();
this._create(id);
}
_create(id) {
let places = this.placesManager.get(id);
for (let i = 0; i < places.length; i++)
this._sections[id].addMenuItem(new PlaceMenuItem(places[i]));
this._sections[id].actor.visible = places.length > 0;
}
});
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
function enable() {
_indicator = new PlacesMenu();
let pos = Main.sessionMode.panel.left.indexOf('appMenu');
if ('apps-menu' in Main.panel.statusArea)
pos++;
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
}
function disable() {
_indicator.destroy();
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_sources += files('placeDisplay.js')

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Places Status Indicator",
"description": "Add a menu for quickly navigating places in the system.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1,570 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { Gio, GLib, Shell } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;
const N_ = x => x;
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
const Hostname1Iface = '<node> \
<interface name="org.freedesktop.hostname1"> \
<property name="PrettyHostname" type="s" access="read" /> \
</interface> \
</node>';
const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface);
class PlaceInfo {
constructor(...params) {
this._init(...params);
}
_init(kind, file, name, icon) {
this.kind = kind;
this.file = file;
this.name = name || this._getFileName();
this.icon = icon ? new Gio.ThemedIcon({ name: icon }) : this.getIcon();
}
destroy() {
}
isRemovable() {
return false;
}
async _ensureMountAndLaunch(context, tryMount) {
try {
await this._launchDefaultForUri(this.file.get_uri(), context, null);
} catch (err) {
if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) {
Main.notifyError(_('Failed to launch “%s”').format(this.name), err.message);
return;
}
let source = {
get_icon: () => this.icon,
};
let op = new ShellMountOperation.ShellMountOperation(source);
try {
await this._mountEnclosingVolume(0, op.mountOp, null);
if (tryMount)
this._ensureMountAndLaunch(context, false);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
Main.notifyError(_('Failed to mount volume for “%s”').format(this.name), e.message);
} finally {
op.close();
}
}
}
launch(timestamp) {
let launchContext = global.create_app_launch_context(timestamp, -1);
this._ensureMountAndLaunch(launchContext, true);
}
getIcon() {
this.file.query_info_async('standard::symbolic-icon',
Gio.FileQueryInfoFlags.NONE,
0,
null,
(file, result) => {
try {
let info = file.query_info_finish(result);
this.icon = info.get_symbolic_icon();
this.emit('changed');
} catch (e) {
if (e instanceof Gio.IOErrorEnum)
return;
throw e;
}
});
// return a generic icon for this kind for now, until we have the
// icon from the query info above
switch (this.kind) {
case 'network':
return new Gio.ThemedIcon({ name: 'folder-remote-symbolic' });
case 'devices':
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
case 'special':
case 'bookmarks':
default:
if (!this.file.is_native())
return new Gio.ThemedIcon({ name: 'folder-remote-symbolic' });
else
return new Gio.ThemedIcon({ name: 'folder-symbolic' });
}
}
_getFileName() {
try {
let info = this.file.query_info('standard::display-name', 0, null);
return info.get_display_name();
} catch (e) {
if (e instanceof Gio.IOErrorEnum)
return this.file.get_basename();
throw e;
}
}
_launchDefaultForUri(uri, context, cancel) {
return new Promise((resolve, reject) => {
Gio.AppInfo.launch_default_for_uri_async(uri, context, cancel, (o, res) => {
try {
Gio.AppInfo.launch_default_for_uri_finish(res);
resolve();
} catch (e) {
reject(e);
}
});
});
}
_mountEnclosingVolume(flags, mountOp, cancel) {
return new Promise((resolve, reject) => {
this.file.mount_enclosing_volume(flags, mountOp, cancel, (o, res) => {
try {
this.file.mount_enclosing_volume_finish(res);
resolve();
} catch (e) {
reject(e);
}
});
});
}
}
Signals.addSignalMethods(PlaceInfo.prototype);
class RootInfo extends PlaceInfo {
_init() {
super._init('devices', Gio.File.new_for_path('/'), _('Computer'));
let busName = 'org.freedesktop.hostname1';
let objPath = '/org/freedesktop/hostname1';
new Hostname1(Gio.DBus.system, busName, objPath, (obj, error) => {
if (error)
return;
this._proxy = obj;
this._proxy.connect('g-properties-changed',
this._propertiesChanged.bind(this));
this._propertiesChanged(obj);
});
}
getIcon() {
return new Gio.ThemedIcon({ name: 'drive-harddisk-symbolic' });
}
_propertiesChanged(proxy) {
// GDBusProxy will emit a g-properties-changed when hostname1 goes down
// ignore it
if (proxy.g_name_owner) {
this.name = proxy.PrettyHostname || _('Computer');
this.emit('changed');
}
}
destroy() {
if (this._proxy) {
this._proxy.run_dispose();
this._proxy = null;
}
super.destroy();
}
}
class PlaceDeviceInfo extends PlaceInfo {
_init(kind, mount) {
this._mount = mount;
super._init(kind, mount.get_root(), mount.get_name());
}
getIcon() {
return this._mount.get_symbolic_icon();
}
isRemovable() {
return this._mount.can_eject();
}
eject() {
let unmountArgs = [
Gio.MountUnmountFlags.NONE,
new ShellMountOperation.ShellMountOperation(this._mount).mountOp,
null, // Gio.Cancellable
];
if (this._mount.can_eject()) {
this._mount.eject_with_operation(...unmountArgs,
this._ejectFinish.bind(this));
} else {
this._mount.unmount_with_operation(...unmountArgs,
this._unmountFinish.bind(this));
}
}
_ejectFinish(mount, result) {
try {
mount.eject_with_operation_finish(result);
} catch (e) {
this._reportFailure(e);
}
}
_unmountFinish(mount, result) {
try {
mount.unmount_with_operation_finish(result);
} catch (e) {
this._reportFailure(e);
}
}
_reportFailure(exception) {
let msg = _('Ejecting drive “%s” failed:').format(this._mount.get_name());
Main.notifyError(msg, exception.message);
}
}
class PlaceVolumeInfo extends PlaceInfo {
_init(kind, volume) {
this._volume = volume;
super._init(kind, volume.get_activation_root(), volume.get_name());
}
launch(timestamp) {
if (this.file) {
super.launch(timestamp);
return;
}
this._volume.mount(0, null, null, (volume, result) => {
volume.mount_finish(result);
let mount = volume.get_mount();
this.file = mount.get_root();
super.launch(timestamp);
});
}
getIcon() {
return this._volume.get_symbolic_icon();
}
}
const DEFAULT_DIRECTORIES = [
GLib.UserDirectory.DIRECTORY_DOCUMENTS,
GLib.UserDirectory.DIRECTORY_PICTURES,
GLib.UserDirectory.DIRECTORY_MUSIC,
GLib.UserDirectory.DIRECTORY_DOWNLOAD,
GLib.UserDirectory.DIRECTORY_VIDEOS,
];
var PlacesManager = class {
constructor() {
this._places = {
special: [],
devices: [],
bookmarks: [],
network: [],
};
this._settings = new Gio.Settings({ schema_id: BACKGROUND_SCHEMA });
this._showDesktopIconsChangedId = this._settings.connect(
'changed::show-desktop-icons', this._updateSpecials.bind(this));
this._updateSpecials();
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
this._volumeMonitor = Gio.VolumeMonitor.get();
this._connectVolumeMonitorSignals();
this._updateMounts();
this._bookmarksFile = this._findBookmarksFile();
this._bookmarkTimeoutId = 0;
this._monitor = null;
if (this._bookmarksFile) {
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._monitor.connect('changed', () => {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
this._bookmarkTimeoutId = GLib.timeout_add(
GLib.PRIORITY_DEFAULT, 100, () => {
this._bookmarkTimeoutId = 0;
this._reloadBookmarks();
return false;
});
});
this._reloadBookmarks();
}
}
_connectVolumeMonitorSignals() {
const signals = [
'volume-added',
'volume-removed',
'volume-changed',
'mount-added',
'mount-removed',
'mount-changed',
'drive-connected',
'drive-disconnected',
'drive-changed',
];
this._volumeMonitorSignals = [];
let func = this._updateMounts.bind(this);
for (let i = 0; i < signals.length; i++) {
let id = this._volumeMonitor.connect(signals[i], func);
this._volumeMonitorSignals.push(id);
}
}
destroy() {
if (this._settings)
this._settings.disconnect(this._showDesktopIconsChangedId);
this._settings = null;
for (let i = 0; i < this._volumeMonitorSignals.length; i++)
this._volumeMonitor.disconnect(this._volumeMonitorSignals[i]);
if (this._monitor)
this._monitor.cancel();
if (this._bookmarkTimeoutId)
GLib.source_remove(this._bookmarkTimeoutId);
}
_updateSpecials() {
this._places.special.forEach(p => p.destroy());
this._places.special = [];
let homePath = GLib.get_home_dir();
this._places.special.push(new PlaceInfo(
'special',
Gio.File.new_for_path(homePath),
_('Home')));
let specials = [];
let dirs = DEFAULT_DIRECTORIES.slice();
if (this._settings.get_boolean('show-desktop-icons'))
dirs.push(GLib.UserDirectory.DIRECTORY_DESKTOP);
for (let i = 0; i < dirs.length; i++) {
let specialPath = GLib.get_user_special_dir(dirs[i]);
if (!specialPath || specialPath === homePath)
continue;
let file = Gio.File.new_for_path(specialPath), info;
try {
info = new PlaceInfo('special', file);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
continue;
throw e;
}
specials.push(info);
}
specials.sort((a, b) => GLib.utf8_collate(a.name, b.name));
this._places.special = this._places.special.concat(specials);
this.emit('special-updated');
}
_updateMounts() {
let networkMounts = [];
let networkVolumes = [];
this._places.devices.forEach(p => p.destroy());
this._places.devices = [];
this._places.network.forEach(p => p.destroy());
this._places.network = [];
/* Add standard places */
this._places.devices.push(new RootInfo());
this._places.network.push(new PlaceInfo(
'network',
Gio.File.new_for_uri('network:///'),
_('Browse Network'),
'network-workgroup-symbolic'));
/* first go through all connected drives */
let drives = this._volumeMonitor.get_connected_drives();
for (let i = 0; i < drives.length; i++) {
let volumes = drives[i].get_volumes();
for (let j = 0; j < volumes.length; j++) {
let identifier = volumes[j].get_identifier('class');
if (identifier && identifier.includes('network')) {
networkVolumes.push(volumes[j]);
} else {
let mount = volumes[j].get_mount();
if (mount)
this._addMount('devices', mount);
}
}
}
/* add all volumes that is not associated with a drive */
let volumes = this._volumeMonitor.get_volumes();
for (let i = 0; i < volumes.length; i++) {
if (volumes[i].get_drive())
continue;
let identifier = volumes[i].get_identifier('class');
if (identifier && identifier.includes('network')) {
networkVolumes.push(volumes[i]);
} else {
let mount = volumes[i].get_mount();
if (mount)
this._addMount('devices', mount);
}
}
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
let mounts = this._volumeMonitor.get_mounts();
for (let i = 0; i < mounts.length; i++) {
if (mounts[i].is_shadowed())
continue;
if (mounts[i].get_volume())
continue;
let root = mounts[i].get_default_location();
if (!root.is_native()) {
networkMounts.push(mounts[i]);
continue;
}
this._addMount('devices', mounts[i]);
}
for (let i = 0; i < networkVolumes.length; i++) {
let mount = networkVolumes[i].get_mount();
if (mount) {
networkMounts.push(mount);
continue;
}
this._addVolume('network', networkVolumes[i]);
}
for (let i = 0; i < networkMounts.length; i++)
this._addMount('network', networkMounts[i]);
this.emit('devices-updated');
this.emit('network-updated');
}
_findBookmarksFile() {
let paths = [
GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']),
GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']),
];
for (let i = 0; i < paths.length; i++) {
if (GLib.file_test(paths[i], GLib.FileTest.EXISTS))
return Gio.File.new_for_path(paths[i]);
}
return null;
}
_reloadBookmarks() {
this._bookmarks = [];
let content = Shell.get_file_contents_utf8_sync(this._bookmarksFile.get_path());
let lines = content.split('\n');
let bookmarks = [];
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
let components = line.split(' ');
let [bookmark] = components;
if (!bookmark)
continue;
let file = Gio.File.new_for_uri(bookmark);
if (file.is_native() && !file.query_exists(null))
continue;
let duplicate = false;
for (let j = 0; j < this._places.special.length; j++) {
if (file.equal(this._places.special[j].file)) {
duplicate = true;
break;
}
}
if (duplicate)
continue;
for (let j = 0; j < bookmarks.length; j++) {
if (file.equal(bookmarks[j].file)) {
duplicate = true;
break;
}
}
if (duplicate)
continue;
let label = null;
if (components.length > 1)
label = components.slice(1).join(' ');
bookmarks.push(new PlaceInfo('bookmarks', file, label));
}
this._places.bookmarks = bookmarks;
this.emit('bookmarks-updated');
}
_addMount(kind, mount) {
let devItem;
try {
devItem = new PlaceDeviceInfo(kind, mount);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
return;
throw e;
}
this._places[kind].push(devItem);
}
_addVolume(kind, volume) {
let volItem;
try {
volItem = new PlaceVolumeInfo(kind, volume);
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
return;
throw e;
}
this._places[kind].push(volItem);
}
get(kind) {
return this._places[kind];
}
};
Signals.addSignalMethods(PlacesManager.prototype);

View File

@@ -1 +0,0 @@
/* none used*/

View File

@@ -1,154 +0,0 @@
/* exported enable disable */
/* Screenshot Window Sizer for Gnome Shell
*
* Copyright (c) 2013 Owen Taylor <otaylor@redhat.com>
* Copyright (c) 2013 Richard Hughes <richard@hughsie.com>
*
* 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.
*/
const { Clutter, Meta, Shell, St } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const MESSAGE_FADE_TIME = 2000;
let text;
function hideMessage() {
text.destroy();
text = null;
}
function flashMessage(message) {
if (!text) {
text = new St.Label({ style_class: 'screenshot-sizer-message' });
Main.uiGroup.add_actor(text);
}
text.remove_all_transitions();
text.text = message;
text.opacity = 255;
let monitor = Main.layoutManager.primaryMonitor;
text.set_position(
monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
text.ease({
opacity: 0,
duration: MESSAGE_FADE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: hideMessage,
});
}
let SIZES = [
[624, 351],
[800, 450],
[1024, 576],
[1200, 675],
[1600, 900],
[360, 654], // Phone portrait maximized
[720, 360], // Phone landscape fullscreen
];
function cycleScreenshotSizes(display, window, binding) {
// Probably this isn't useful with 5 sizes, but you can decrease instead
// of increase by holding down shift.
let modifiers = binding.get_modifiers();
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) !== 0;
// Unmaximize first
if (window.get_maximized() !== 0)
window.unmaximize(Meta.MaximizeFlags.BOTH);
let workArea = window.get_work_area_current_monitor();
let outerRect = window.get_frame_rect();
// Double both axes if on a hidpi display
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let scaledSizes = SIZES.map(size => size.map(wh => wh * scaleFactor))
.filter(([w, h]) => w <= workArea.width && h <= workArea.height);
// Find the nearest 16:9 size for the current window size
let nearestIndex;
let nearestError;
for (let i = 0; i < scaledSizes.length; i++) {
let [width, height] = scaledSizes[i];
// ignore sizes bigger than the workArea
if (width > workArea.width || height > workArea.height)
continue;
// get the best initial window size
let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
if (nearestIndex === undefined || error < nearestError) {
nearestIndex = i;
nearestError = error;
}
}
// get the next size up or down from ideal
let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length;
let [newWidth, newHeight] = scaledSizes[newIndex];
// Push the window onscreen if it would be resized offscreen
let newX = outerRect.x;
let newY = outerRect.y;
if (newX + newWidth > workArea.x + workArea.width)
newX = Math.max(workArea.x + workArea.width - newWidth);
if (newY + newHeight > workArea.y + workArea.height)
newY = Math.max(workArea.y + workArea.height - newHeight);
window.move_resize_frame(true, newX, newY, newWidth, newHeight);
let newOuterRect = window.get_frame_rect();
let message = '%d×%d'.format(
newOuterRect.width / scaleFactor,
newOuterRect.height / scaleFactor);
// The new size might have been constrained by geometry hints (e.g. for
// a terminal) - in that case, include the actual ratio to the message
// we flash
let actualNumerator = 9 * newOuterRect.width / newOuterRect.height;
if (Math.abs(actualNumerator - 16) > 0.01)
message += ' (%.2f:9)'.format(actualNumerator);
flashMessage(message);
}
function enable() {
Main.wm.addKeybinding(
'cycle-screenshot-sizes',
ExtensionUtils.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW,
Shell.ActionMode.NORMAL,
cycleScreenshotSizes);
Main.wm.addKeybinding(
'cycle-screenshot-sizes-backward',
ExtensionUtils.getSettings(),
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.IS_REVERSED,
Shell.ActionMode.NORMAL,
cycleScreenshotSizes);
}
function disable() {
Main.wm.removeKeybinding('cycle-screenshot-sizes');
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward');
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Screenshot Window Sizer",
"description": "Resize windows for GNOME Software screenshots",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="gnome-shell-extensions">
<schema id="org.gnome.shell.extensions.screenshot-window-sizer"
path="/org/gnome/shell/extensions/screenshot-window-sizer/">
<key type="as" name="cycle-screenshot-sizes">
<default><![CDATA[['<Alt><Control>s']]]></default>
<summary>Cycle Screenshot Sizes</summary>
</key>
<key type="as" name="cycle-screenshot-sizes-backward">
<default><![CDATA[['<Shift><Alt><Control>s']]]></default>
<summary>Cycle Screenshot Sizes Backward</summary>
</key>
</schema>
</schemalist>

View File

@@ -0,0 +1,14 @@
EXTENSION_ID = user-theme
include ../../extension.mk
gschemas_in = org.gnome.shell.extensions.user-theme.gschema.xml.in
@INTLTOOL_XML_NOMERGE_RULE@
gsettings_SCHEMAS = $(gschemas_in:.xml.in=.xml)
@GSETTINGS_RULES@
CLEANFILES += $(gschemas_in:.xml.in=.valid) $(gsettings_SCHEMAS)
EXTRA_DIST += $(gschemas_in)

View File

@@ -1,62 +1,57 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// Load shell theme from ~/.local/share/themes/name/gnome-shell
/* exported init */
// Load shell theme from ~/.themes/name/gnome-shell
const { Gio, GLib } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const SETTINGS_SCHEMA = 'org.gnome.shell.extensions.user-theme';
const SETTINGS_KEY = 'name';
class ThemeManager {
constructor() {
this._settings = ExtensionUtils.getSettings();
}
function ThemeManager() {
this._init();
}
enable() {
this._changedId = this._settings.connect(`changed::${SETTINGS_KEY}`, this._changeTheme.bind(this));
ThemeManager.prototype = {
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._settings.connect('changed::'+SETTINGS_KEY, Lang.bind(this, this._changeTheme));
this._changeTheme();
}
},
disable() {
if (this._changedId) {
this._settings.disconnect(this._changedId);
this._changedId = 0;
_changeTheme: function() {
let _stylesheet = null;
let _themeName = this._settings.get_string(SETTINGS_KEY);
if (_themeName) {
let _userCssStylesheet = GLib.get_home_dir() + '/.themes/' + _themeName + '/gnome-shell/gnome-shell.css';
file = Gio.file_new_for_path(_userCssStylesheet);
if (file.query_exists(null))
_stylesheet = _userCssStylesheet;
else {
let sysdirs = GLib.get_system_data_dirs();
for (let i = 0; i < sysdirs.length; i++) {
_userCssStylesheet = sysdirs[i] + '/themes/' + _themeName + '/gnome-shell/gnome-shell.css';
file = Gio.file_new_for_path(_userCssStylesheet);
if (file.query_exists(null)) {
_stylesheet = _userCssStylesheet;
break;
}
}
}
}
Main.setThemeStylesheet(null);
Main.loadTheme();
}
_changeTheme() {
let stylesheet = null;
let themeName = this._settings.get_string(SETTINGS_KEY);
if (themeName) {
let stylesheetPaths = [
[GLib.get_home_dir(), '.themes'],
[GLib.get_user_data_dir(), 'themes'],
...GLib.get_system_data_dirs().map(dir => [dir, 'themes']),
].map(themeDir => GLib.build_filenamev([
...themeDir, themeName, 'gnome-shell', 'gnome-shell.css',
]));
stylesheet = stylesheetPaths.find(path => {
let file = Gio.file_new_for_path(path);
return file.query_exists(null);
});
}
if (stylesheet)
global.log(`loading user theme: ${stylesheet}`);
if (_stylesheet)
global.log('loading user theme: ' + _stylesheet);
else
global.log('loading default theme (Adwaita)');
Main.setThemeStylesheet(stylesheet);
Main.setThemeStylesheet(_stylesheet);
Main.loadTheme();
}
}
function init() {
return new ThemeManager();
function main(metadata) {
new ThemeManager();
}

View File

@@ -1,7 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')

View File

@@ -1,11 +1,9 @@
{
"uuid": "@uuid@",
"extension-id": "@extension_id@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "User Themes",
"description": "Load shell themes from user directory.",
"shell-version": [ "@shell_current@" ],
"description": "Load shell themes from user directory",
"shell-version": [ "3.0" ],
"localedir": "@LOCALEDIR@",
"original-authors": [ "john.stowers@gmail.com" ],
"url": "@url@"
}

View File

@@ -2,8 +2,8 @@
<schema id="org.gnome.shell.extensions.user-theme" path="/org/gnome/shell/extensions/user-theme/">
<key name="name" type="s">
<default>""</default>
<summary>Theme name</summary>
<description>The name of the theme, to be loaded from ~/.themes/name/gnome-shell</description>
<_summary>Theme name</_summary>
<_description>The name of the theme, to be loaded from ~/.themes/name/gnome-shell</_description>
</key>
</schema>
</schemalist>

View File

@@ -1 +0,0 @@
/* none used */

View File

@@ -1,68 +0,0 @@
@import url("stylesheet.css");
#panel.bottom-panel {
border-top-width: 1px;
border-bottom-width: 0px;
height: 2.25em ;
padding: 2px;
}
.bottom-panel .window-button > StWidget,
.bottom-panel .window-picker-toggle > StWidget {
color: #2e3436;
background-color: #eee;
border-radius: 3px;
padding: 3px 6px 1px;
box-shadow: none;
text-shadow: none;
border: 1px solid rgba(0,0,0,0.2);
}
.bottom-panel .window-button > StWidget {
-st-natural-width: 18.7em;
max-width: 18.75em;
}
.bottom-panel .window-button:hover > StWidget,
.bottom-panel .window-picker-toggle:hover > StWidget {
background-color: #f9f9f9;
}
.bottom-panel .window-button:active > StWidget,
.bottom-panel .window-button:focus > StWidget {
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
}
.bottom-panel .window-button.focused > StWidget,
.bottom-panel .window-picker-toggle:checked > StWidget {
background-color: #ccc;
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
}
.bottom-panel .window-button.focused:hover > StWidget {
background-color: #e9e9e9;
}
.bottom-panel .window-button.minimized > StWidget {
color: #888;
box-shadow: none;
}
/* workspace switcher */
.window-list-workspace-indicator .workspace {
background-color: #ddd;
}
.window-list-workspace-indicator .workspace.active {
background-color: #ccc;
}
.window-list-window-preview {
background-color: #ededed;
border: 1px solid #ccc;
}
.window-list-window-preview.active {
background-color: #f6f5f4;
border: 2px solid #888;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
extension_data += configure_file(
input: metadata_name + '.in',
output: metadata_name,
configuration: metadata_conf
)
extension_sources += files('prefs.js', 'windowPicker.js', 'workspaceIndicator.js')
extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
if classic_mode_enabled
extension_data += files('classic.css')
endif

View File

@@ -1,10 +0,0 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Window List",
"description": "Display a window list at the bottom of the screen.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
"shell-version": [ "@shell_current@" ],
"url": "@url@"
}

Some files were not shown because too many files have changed in this diff Show More