Compare commits
298 Commits
shell-tool
...
message-tr
Author | SHA1 | Date | |
---|---|---|---|
8fc9d0c8ba | |||
803a204604 | |||
a02e6d30f7 | |||
a424bbbabf | |||
4ab513ca77 | |||
09653fbaf6 | |||
b1791951cb | |||
af1a3b11f5 | |||
ef49ada575 | |||
6c3b8e2add | |||
3658f8a8b4 | |||
a2cae50e0e | |||
9423cc7fae | |||
767fe0ebc2 | |||
edce0e8feb | |||
554549338a | |||
350e958383 | |||
0e5c83bd2a | |||
6e1a791273 | |||
2435c603ec | |||
44cc42484f | |||
44712c274e | |||
1c4c3afb27 | |||
e326202477 | |||
45f4292259 | |||
288eae91e2 | |||
f4c05deb2d | |||
1cd9a15ac0 | |||
d1edefdc39 | |||
2cb4dfb4a4 | |||
c0ff0066e6 | |||
55fbb9d0af | |||
c4a49b4de2 | |||
aa9d3515a1 | |||
4394bc3e40 | |||
6b1c3d323b | |||
b7646d18ae | |||
f5f92b2e79 | |||
14df7cd62c | |||
c2fe9e57b1 | |||
5a3c3efbeb | |||
a8d18ac18a | |||
949f67469c | |||
a442dfea14 | |||
42757a0c87 | |||
81b7c0170d | |||
d624db18c5 | |||
b03fa1ebf7 | |||
478dba8502 | |||
20294f2c92 | |||
74418f2129 | |||
3b4e2202f7 | |||
3b5c468cbf | |||
34003a1f6b | |||
a4405be71c | |||
b6cc9c7ff6 | |||
368d484dee | |||
7b0526dc5c | |||
236a9b184f | |||
6b816fa7dc | |||
e2ac769fd1 | |||
dd8f05c81d | |||
f0e3b87330 | |||
1d2dc09ede | |||
73cd9513bd | |||
907fc2f067 | |||
2f1ca7bf28 | |||
93f3412f70 | |||
f815844eb4 | |||
b0cb8fb85a | |||
e9787c0f1e | |||
efc291ac5b | |||
1cc78fdf60 | |||
8c59bc71b0 | |||
7b9f5b7643 | |||
0bc578230f | |||
bb366f8fbe | |||
b0a0ee297c | |||
b48b21e578 | |||
e823a3b554 | |||
8ac97fe1a0 | |||
2c914374e0 | |||
1526500079 | |||
9ba5ca0193 | |||
3529b8c915 | |||
4e8206d60e | |||
a5edc78fac | |||
cdd508e12d | |||
931cbc6ae3 | |||
ab809faaf0 | |||
8334b063f1 | |||
21f15246a6 | |||
050e710770 | |||
bb63d513fd | |||
985d707788 | |||
9ddebf0480 | |||
8c05003acf | |||
7c796b2d2a | |||
48f8e4943d | |||
fec2ea5e9c | |||
4014313910 | |||
06cf6c51f3 | |||
426d7bc515 | |||
bf68f9f0cf | |||
ce90dda76f | |||
66cab3b8ed | |||
bc255a525f | |||
d51384fb86 | |||
1626e8c09a | |||
263d738ef6 | |||
8040ad61c6 | |||
057f0effed | |||
bf7b166237 | |||
ae744bf206 | |||
af35dea019 | |||
1b75ae0184 | |||
07a8d5ed2d | |||
2f27f61a1f | |||
585bfe5b5a | |||
5ee72d807d | |||
fcac7ac0b3 | |||
dcd7762253 | |||
869e744670 | |||
626f679947 | |||
4f456b9689 | |||
e2e513ff08 | |||
37a948e4eb | |||
b8d9319c32 | |||
f549269934 | |||
dc232d4631 | |||
930a0b52f3 | |||
f14d7f891b | |||
e5b12619ef | |||
242c2bce04 | |||
64373fe77e | |||
f106ee7182 | |||
56d2691c31 | |||
f883e32f26 | |||
c985c3cf78 | |||
01138d55ca | |||
2103a4da24 | |||
627f86a9dc | |||
baf823c466 | |||
94bd6f1718 | |||
c8b1afbad6 | |||
395f45c570 | |||
3ebb0f149d | |||
6d9aadfa4d | |||
22e26bea8f | |||
d9ff1f2ec1 | |||
10991d363d | |||
0c37a5bd6f | |||
369e0c2c6f | |||
17c8173640 | |||
8fd00ac6cc | |||
4f4e54cbf7 | |||
16cb878f4e | |||
d5adfa6445 | |||
8e9549c6f1 | |||
7239eb2f90 | |||
5b769138b6 | |||
81d579aca0 | |||
7548f2d71e | |||
bda7d3994b | |||
a336ed79ae | |||
416fbc775e | |||
e941e8088b | |||
77cf9ae077 | |||
358175e998 | |||
7f5c600133 | |||
56bcdd41fd | |||
cd33dc7f22 | |||
2eef17bcdc | |||
79a614240b | |||
7c954898a2 | |||
497c74f4d8 | |||
51c41aed90 | |||
d705c1bb52 | |||
d9df7c1b1e | |||
49eb687713 | |||
7bec02dcb1 | |||
38c06ca837 | |||
6623ca1287 | |||
d1bdd6f11d | |||
39edf54aa9 | |||
aa9c585562 | |||
8b813060f0 | |||
ca263dade5 | |||
b8ef9cdf4b | |||
aa92c6fdf5 | |||
3af8130fff | |||
ab0460ad59 | |||
a27c29f4fc | |||
212d5c1954 | |||
8929f43ad9 | |||
a26b0b60d1 | |||
60dbb19c2d | |||
3703a86354 | |||
992b43f914 | |||
ff39edd1ee | |||
a81a16801d | |||
4c6f770dea | |||
c0b01c0210 | |||
400326e549 | |||
d7af6d40e3 | |||
90ebcd32e3 | |||
53d0581377 | |||
439daf828f | |||
d120d03de9 | |||
53fbabe2ca | |||
4bdd40911f | |||
ff4ac0d02e | |||
45dd342cc0 | |||
e5efecd2bd | |||
3bf88b8988 | |||
4ddc1118bb | |||
fd1e7b2a0f | |||
aed1e67add | |||
dc99e8ffcd | |||
6a8b50cb00 | |||
edb50d5dc7 | |||
2f6c951997 | |||
64cd51667d | |||
caa08f27fa | |||
26015ef16d | |||
4798ad5107 | |||
9b05304c2d | |||
795feca393 | |||
31663dcd83 | |||
2f2df61093 | |||
3b8d53060d | |||
c5ce405859 | |||
b3a5fc72fb | |||
8a2cc11cc0 | |||
cbb3a3aec8 | |||
e382da9708 | |||
ab1fbbde92 | |||
5f5266ca60 | |||
68e8b14b8b | |||
abdd8b330c | |||
eb8176deeb | |||
956f89f377 | |||
1c69380923 | |||
d2bc7b200e | |||
afb3b1e718 | |||
061a2cfbfb | |||
243824ab80 | |||
90ddad7ba1 | |||
20b29ff48f | |||
96f4d318c5 | |||
6f7da264ba | |||
640e45c12a | |||
04e28cd7c4 | |||
2cc41c6726 | |||
03a45b665c | |||
5a42179a96 | |||
af3ec56ca1 | |||
289b19aa31 | |||
91eb613d69 | |||
1c7c53d19f | |||
d4304495c6 | |||
b77b205d37 | |||
a15205e6c4 | |||
28dbf7a06e | |||
1ec8e9eb6b | |||
fa09f7a6da | |||
d67e54d3ee | |||
d263c12e2e | |||
4d55ccff39 | |||
2a0adc0fc8 | |||
076e902b2c | |||
8c72623da3 | |||
1fd25573e5 | |||
6527dbc8b7 | |||
3c646ec516 | |||
a9fd350396 | |||
e91e8e993d | |||
276d9a9302 | |||
6b95864076 | |||
d4c577a299 | |||
b90fc1e194 | |||
55497899dd | |||
e37790fdf9 | |||
83402957bb | |||
58325fca76 | |||
8b6962f3bf | |||
c1f91def74 | |||
529f74c0e5 | |||
459a3b18f2 | |||
ac2be7f0d1 | |||
f6b80d5ed4 | |||
d291e568fd | |||
2b78d5bd5d | |||
c9d9846759 | |||
6baafaa530 | |||
c2af05f753 | |||
97df305a6d | |||
fee385ba35 |
4
.gitignore
vendored
@ -38,5 +38,9 @@ src/gnomeshell-taskpanel
|
|||||||
src/gnome-shell
|
src/gnome-shell
|
||||||
src/test-recorder
|
src/test-recorder
|
||||||
src/test-recorder.ogg
|
src/test-recorder.ogg
|
||||||
|
src/test-theme
|
||||||
stamp-h1
|
stamp-h1
|
||||||
|
tests/run-test.sh
|
||||||
xmldocs.make
|
xmldocs.make
|
||||||
|
*~
|
||||||
|
*.patch
|
||||||
|
17
Makefile.am
@ -1,13 +1,22 @@
|
|||||||
SUBDIRS = data js src po
|
SUBDIRS = data js src tests po
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
.project \
|
.project \
|
||||||
.settings
|
.settings \
|
||||||
|
autogen.sh
|
||||||
|
|
||||||
|
# These are files checked into Git that we don't want to distribute
|
||||||
|
DIST_EXCLUDE = \
|
||||||
|
.gitignore \
|
||||||
|
gnome-shell.doap \
|
||||||
|
MAINTAINERS \
|
||||||
|
tools/build/*
|
||||||
|
|
||||||
distcheck-hook:
|
distcheck-hook:
|
||||||
@echo "Checking disted javascript against files in git"
|
@echo "Checking disted files against files in git"
|
||||||
@failed=false; \
|
@failed=false; \
|
||||||
for f in `cd $(srcdir) && git ls-files js` ; do \
|
exclude=`(for p in $(DIST_EXCLUDE) ; do echo --exclude=$$p ; done)`; \
|
||||||
|
for f in `cd $(srcdir) && git ls-files $$exclude` ; do \
|
||||||
if ! test -e $(distdir)/$$f ; then \
|
if ! test -e $(distdir)/$$f ; then \
|
||||||
echo File missing from distribution: $$f ; \
|
echo File missing from distribution: $$f ; \
|
||||||
failed=true ; \
|
failed=true ; \
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
AC_INIT(gnome-shell, 2.27.3)
|
AC_INIT(gnome-shell, 2.28.1)
|
||||||
|
|
||||||
AC_CONFIG_AUX_DIR(config)
|
AC_CONFIG_AUX_DIR(config)
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
|
|||||||
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
|
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
|
||||||
gobject-introspection-1.0 >= 0.6.5)
|
gobject-introspection-1.0 >= 0.6.5)
|
||||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||||
|
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
|
||||||
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
|
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
|
||||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
|
||||||
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
|
||||||
@ -128,5 +129,6 @@ AC_OUTPUT([
|
|||||||
js/misc/Makefile
|
js/misc/Makefile
|
||||||
js/ui/Makefile
|
js/ui/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
tests/Makefile
|
||||||
po/Makefile.in
|
po/Makefile.in
|
||||||
])
|
])
|
||||||
|
@ -12,18 +12,27 @@ gnome-shell.desktop.in: gnome-shell.desktop.in.in
|
|||||||
gnome-shell.desktop: gnome-shell.desktop.in
|
gnome-shell.desktop: gnome-shell.desktop.in
|
||||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||||
|
|
||||||
imagedir = $(pkgdatadir)/images
|
imagesdir = $(pkgdatadir)/images
|
||||||
|
dist_images_DATA = \
|
||||||
dist_image_DATA = \
|
add-workspace.svg \
|
||||||
add-workspace.svg \
|
app-well-glow.png \
|
||||||
app-well-glow.png \
|
close-black.svg \
|
||||||
back.svg \
|
magnifier.svg \
|
||||||
close.svg \
|
|
||||||
close-black.svg \
|
|
||||||
info.svg \
|
|
||||||
magnifier.svg \
|
|
||||||
remove-workspace.svg
|
remove-workspace.svg
|
||||||
|
|
||||||
|
themedir = $(pkgdatadir)/theme
|
||||||
|
dist_theme_DATA = \
|
||||||
|
theme/gnome-shell.css \
|
||||||
|
theme/close.svg \
|
||||||
|
theme/close-window.svg \
|
||||||
|
theme/scroll-button-down.png \
|
||||||
|
theme/scroll-button-down-hover.png \
|
||||||
|
theme/scroll-button-up.png \
|
||||||
|
theme/scroll-button-up-hover.png \
|
||||||
|
theme/scroll-vhandle.png \
|
||||||
|
theme/section-back.svg \
|
||||||
|
theme/section-more.svg
|
||||||
|
|
||||||
schemadir = @GCONF_SCHEMA_FILE_DIR@
|
schemadir = @GCONF_SCHEMA_FILE_DIR@
|
||||||
schema_DATA = gnome-shell.schemas
|
schema_DATA = gnome-shell.schemas
|
||||||
|
|
||||||
|
@ -88,6 +88,21 @@
|
|||||||
</locale>
|
</locale>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<schema>
|
||||||
|
<key>/schemas/desktop/gnome/shell/disabled_extensions</key>
|
||||||
|
<applyto>/desktop/gnome/shell/disabled_extensions</applyto>
|
||||||
|
<owner>gnome-shell</owner>
|
||||||
|
<type>list</type>
|
||||||
|
<list_type>string</list_type>
|
||||||
|
<default>[]</default>
|
||||||
|
<locale name="C">
|
||||||
|
<short>Uuids of extensions to disable</short>
|
||||||
|
<long>
|
||||||
|
GNOME Shell extensions have a uuid property; this key lists extensions which should not be loaded.
|
||||||
|
</long>
|
||||||
|
</locale>
|
||||||
|
</schema>
|
||||||
|
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|
||||||
</gconfschemafile>
|
</gconfschemafile>
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
|
||||||
<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"
|
|
||||||
version="1.1"
|
|
||||||
id="Foreground"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
width="16px"
|
|
||||||
height="16px"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
enable-background="new 0 0 16 16"
|
|
||||||
xml:space="preserve"
|
|
||||||
sodipodi:version="0.32"
|
|
||||||
inkscape:version="0.46"
|
|
||||||
sodipodi:docname="info_16.svg"
|
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
|
||||||
id="metadata2389"><rdf:RDF><cc:Work
|
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
|
||||||
id="defs2387"><linearGradient
|
|
||||||
id="linearGradient3710"><stop
|
|
||||||
style="stop-color:#c4c4c4;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop3712" /><stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop3714" /></linearGradient><inkscape:perspective
|
|
||||||
sodipodi:type="inkscape:persp3d"
|
|
||||||
inkscape:vp_x="0 : 8 : 1"
|
|
||||||
inkscape:vp_y="0 : 1000 : 0"
|
|
||||||
inkscape:vp_z="16 : 8 : 1"
|
|
||||||
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
|
||||||
id="perspective2391" /><linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient3710"
|
|
||||||
id="linearGradient3716"
|
|
||||||
x1="7.9066148"
|
|
||||||
y1="15.937743"
|
|
||||||
x2="7.9377432"
|
|
||||||
y2="0.031128405"
|
|
||||||
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
|
|
||||||
inkscape:window-height="713"
|
|
||||||
inkscape:window-width="722"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
guidetolerance="10.0"
|
|
||||||
gridtolerance="10.0"
|
|
||||||
objecttolerance="10.0"
|
|
||||||
borderopacity="1.0"
|
|
||||||
bordercolor="#666666"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
id="base"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="32.125"
|
|
||||||
inkscape:cx="8"
|
|
||||||
inkscape:cy="8.154146"
|
|
||||||
inkscape:window-x="20"
|
|
||||||
inkscape:window-y="20"
|
|
||||||
inkscape:current-layer="Foreground" />
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M7,3h2v2H7V3z M5.5,12H7V8H5.5V7H9v5h1.5v1h-5V12z M0,8c0-4.418,3.582-8,8-8 s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
|
|
||||||
id="path2384"
|
|
||||||
style="fill-opacity:1;fill:url(#linearGradient3716)" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.5 KiB |
76
data/theme/close-window.svg
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||||
|
<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="Foreground"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="22"
|
||||||
|
height="22"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
enable-background="new 0 0 16 16"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.46"
|
||||||
|
sodipodi:docname="close-window.svg"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||||
|
id="metadata2399"><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><defs
|
||||||
|
id="defs2397"><linearGradient
|
||||||
|
id="linearGradient3173"><stop
|
||||||
|
style="stop-color:#c4c4c4;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3175" /><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3177" /></linearGradient><inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 8 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="16 : 8 : 1"
|
||||||
|
inkscape:persp3d-origin="8 : 5.3333333 : 1"
|
||||||
|
id="perspective2401" /></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="999"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
guidetolerance="10.0"
|
||||||
|
gridtolerance="10.0"
|
||||||
|
objecttolerance="10.0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#000000"
|
||||||
|
id="base"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="25.648691"
|
||||||
|
inkscape:cx="8.8097603"
|
||||||
|
inkscape:cy="9.0472789"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="26"
|
||||||
|
inkscape:current-layer="Foreground"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true" />
|
||||||
|
|
||||||
|
<g
|
||||||
|
id="g3175"><path
|
||||||
|
sodipodi:nodetypes="csssc"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.59217799;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path2394"
|
||||||
|
d="M 0.83987936,8.0425327 C 0.83987936,4.0805265 4.0712155,0.86823453 8.0567103,0.86823453 C 12.042205,0.86823453 15.273542,4.0805265 15.273542,8.0425327 C 15.273542,12.004539 12.042205,15.216831 8.0567103,15.216831 C 4.0712155,15.216831 0.83987936,12.004539 0.83987936,8.0425327 z"
|
||||||
|
clip-rule="evenodd" /><g
|
||||||
|
id="g3172"><path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 5.4242673,5.3313047 L 10.515414,10.421272 L 10.714004,10.646491"
|
||||||
|
id="path3152" /></g></g><path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 5.4402527,10.650392 L 10.688082,5.3573033"
|
||||||
|
id="path3154"
|
||||||
|
sodipodi:nodetypes="cc" /></svg>
|
After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
538
data/theme/gnome-shell.css
Normal file
@ -0,0 +1,538 @@
|
|||||||
|
/* Copyright 2009, Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Portions adapted from Mx's data/style/default.css
|
||||||
|
* Copyright 2009 Intel Corporation
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU Lesser General Public License,
|
||||||
|
* version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.shell-link {
|
||||||
|
color: #0000ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-link:hover {
|
||||||
|
color: #0000e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
StScrollBar
|
||||||
|
{
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
StScrollView
|
||||||
|
{
|
||||||
|
scrollbar-width: 16px;
|
||||||
|
scrollbar-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
StButton#up-stepper
|
||||||
|
{
|
||||||
|
border-image: url("scroll-button-up.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StButton#up-stepper:hover,
|
||||||
|
StButton#up-stepper:active
|
||||||
|
{
|
||||||
|
border-image: url("scroll-button-up-hover.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StButton#down-stepper
|
||||||
|
{
|
||||||
|
border-image: url("scroll-button-down.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StButton#down-stepper:hover,
|
||||||
|
StButton#down-stepper:active
|
||||||
|
{
|
||||||
|
border-image: url("scroll-button-down-hover.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StScrollBar StButton#vhandle
|
||||||
|
{
|
||||||
|
border-image: url("scroll-vhandle.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StScrollBar StButton#vhandle:hover
|
||||||
|
{
|
||||||
|
border-image: url("scroll-vhandle.png") 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
StTooltip {
|
||||||
|
border: 1px solid rgba(79,111,173,0.9);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 4px;
|
||||||
|
background-color: rgba(79,111,173,0.9);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Panel */
|
||||||
|
|
||||||
|
#panel {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: #161616;
|
||||||
|
background-gradient-end: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panelLeft, #panelCenter, #panelRight {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panelLeft {
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panelRight {
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:pressed {
|
||||||
|
background-color: rgba(50,76,111,0.98);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#appMenu {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-menu-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button {
|
||||||
|
padding: 4px 12px 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font: 16px sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:active, .panel-button:checked {
|
||||||
|
background-color: #314a6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panelStatus {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlay */
|
||||||
|
|
||||||
|
.workspaces {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-caption {
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
border: 1px solid rgba(128,128,128,0.40);
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
-shell-caption-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-close {
|
||||||
|
background-image: url("close-window.svg");
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
-shell-close-overlap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dash */
|
||||||
|
|
||||||
|
#dash {
|
||||||
|
color: #5f5f5f;
|
||||||
|
background-color: rgba(0,0,0,0.75);
|
||||||
|
padding: 0px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dashSections {
|
||||||
|
spacing: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchEntry {
|
||||||
|
padding: 4px;
|
||||||
|
border-bottom: 1px solid #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchEntry:active {
|
||||||
|
background-color: #c4c4c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-section {
|
||||||
|
spacing: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
border: 1px solid #262626;
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: #161616;
|
||||||
|
background-gradient-end: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header-inner {
|
||||||
|
border: 1px solid #000000;
|
||||||
|
padding: 0px 4px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-text-content {
|
||||||
|
padding: 4px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header-back {
|
||||||
|
padding: 0px 4px 0px 0px;
|
||||||
|
border-right: 1px solid #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header-back-image {
|
||||||
|
background-image: url("section-back.svg");
|
||||||
|
width: 12px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-count {
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-section-content {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
spacing: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-link {
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-link-expander {
|
||||||
|
background-image: url("section-more.svg");
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-pane {
|
||||||
|
background-color: rgba(0,0,0,0.95);
|
||||||
|
border: 1px solid #262626;
|
||||||
|
padding: 4px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-pane-close {
|
||||||
|
background-image: url("close.svg");
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-section-header {
|
||||||
|
padding: 6px 0px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-section-results {
|
||||||
|
color: #ffffff;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-section-list-results {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-result-content {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dash-search-result-content:selected {
|
||||||
|
padding: 1px;
|
||||||
|
border: 1px solid #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GenericDisplay */
|
||||||
|
|
||||||
|
.generic-display-container {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-item {
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 14px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-item:selected {
|
||||||
|
background-color: rgba(79,111,173,0.66);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-item-text {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-item-description {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #bababa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-details {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generic-display-details-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apps */
|
||||||
|
|
||||||
|
#dashAppWell {
|
||||||
|
spacing: 2px;
|
||||||
|
-shell-grid-item-size: 74px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-app {
|
||||||
|
border: 1px solid #080808;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 2px;
|
||||||
|
width: 74px;
|
||||||
|
height: 74px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-app:hover {
|
||||||
|
border: 1px solid #202020;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-app:active {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
border: 1px solid #5f5f5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-app-glow {
|
||||||
|
-shell-glow-extend-vertical: 3px;
|
||||||
|
-shell-glow-shrink-horizontal: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-menu {
|
||||||
|
border: 1px solid #5f5f5f;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
background-color: rgba(0,0,0,0.9);
|
||||||
|
color: #ffffff;
|
||||||
|
-shell-arrow-width: 12px;
|
||||||
|
-shell-menu-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-menu-item:hover {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-well-menu-separator {
|
||||||
|
padding-top: 1px;
|
||||||
|
border-bottom: 1px solid #5f5f5f;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Places */
|
||||||
|
|
||||||
|
.places-actions {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#placesDevices {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LookingGlass */
|
||||||
|
|
||||||
|
#LookingGlassDialog
|
||||||
|
{
|
||||||
|
background-color: rgba(0,0,0,0.85);
|
||||||
|
spacing: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid rgba(0,0,172,0.85);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
color: #88ff66;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog > #Toolbar
|
||||||
|
{
|
||||||
|
border: 1px solid grey;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog .labels {
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog .notebook-tab {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog .notebook-tab:selected {
|
||||||
|
border: 1px solid #88ff66;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog StLabel
|
||||||
|
{
|
||||||
|
color: #88ff66;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog StEntry
|
||||||
|
{
|
||||||
|
color: #88ff66;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LookingGlassDialog StBoxLayout#EvalBox
|
||||||
|
{
|
||||||
|
padding: 4px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lookingGlassExtensions {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-extension-list {
|
||||||
|
padding: 4px;
|
||||||
|
spacing: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-extension {
|
||||||
|
border: 1px solid #6f6f6f;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-extension-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg-extension-actions {
|
||||||
|
spacing: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar popup */
|
||||||
|
|
||||||
|
#calendarPopup {
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(0,0,0,0.9);
|
||||||
|
border: 1px solid rgba(128,128,128,0.45);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#calendarPopup .calendar {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar {
|
||||||
|
spacing-rows: 5px;
|
||||||
|
spacing-columns: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-change-month {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-change-month:hover {
|
||||||
|
background: #314a6c;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-change-month:active {
|
||||||
|
background: #213050;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
padding: 1px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-today {
|
||||||
|
font-weight: bold;
|
||||||
|
background: #ffffff;
|
||||||
|
color: black;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-other-month-day {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Tray */
|
||||||
|
#message-tray {
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: rgba(0,0,0,0.01);
|
||||||
|
background-gradient-end: rgba(0,0,0,0.95);
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification {
|
||||||
|
border-radius: 5px;
|
||||||
|
background: rgba(0,0,0,0.9);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 10px;
|
||||||
|
spacing: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#summary-mode {
|
||||||
|
spacing: 10px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* App Switcher */
|
||||||
|
.switcher-list {
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
border: 1px solid rgba(128,128,128,0.40);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
font: 12px sans-serif;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .item-box {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .thumbnail-box {
|
||||||
|
padding: 2px;
|
||||||
|
spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .thumbnail {
|
||||||
|
width: 256px;
|
||||||
|
height: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .outlined-item-box {
|
||||||
|
padding: 6px;
|
||||||
|
border: 2px solid rgba(85,85,85,1.0);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .selected-item-box {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255,255,255,0.33);
|
||||||
|
}
|
||||||
|
|
||||||
|
.switcher-list .separator {
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(255,255,255,0.33);
|
||||||
|
}
|
||||||
|
|
BIN
data/theme/scroll-button-down-hover.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
data/theme/scroll-button-down.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
data/theme/scroll-button-up-hover.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
data/theme/scroll-button-up.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
data/theme/scroll-vhandle.png
Normal file
After Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
87
data/theme/section-more.svg
Executable file
@ -0,0 +1,87 @@
|
|||||||
|
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="5.8600588"
|
||||||
|
height="9"
|
||||||
|
id="svg3647"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.46+devel"
|
||||||
|
sodipodi:docname="New document 6">
|
||||||
|
<defs
|
||||||
|
id="defs3649">
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 526.18109 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||||
|
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||||
|
id="perspective3655" />
|
||||||
|
<inkscape:perspective
|
||||||
|
id="perspective3603"
|
||||||
|
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||||
|
inkscape:vp_z="1 : 0.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_x="0 : 0.5 : 1"
|
||||||
|
sodipodi:type="inkscape:persp3d" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="112.21575"
|
||||||
|
inkscape:cy="-32.642856"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="609"
|
||||||
|
inkscape:window-height="501"
|
||||||
|
inkscape:window-x="164"
|
||||||
|
inkscape:window-y="26"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3652">
|
||||||
|
<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(-262.78425,-490.71933)">
|
||||||
|
<path
|
||||||
|
transform="matrix(0,0.98149546,-0.71467449,0,506.02358,412.28296)"
|
||||||
|
d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:arg2="1.5707963"
|
||||||
|
sodipodi:arg1="0.52359878"
|
||||||
|
sodipodi:r2="2.5"
|
||||||
|
sodipodi:r1="5"
|
||||||
|
sodipodi:cy="337.5"
|
||||||
|
sodipodi:cx="84.5"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
id="path5497-5"
|
||||||
|
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||||
|
sodipodi:type="star" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -1,4 +1,6 @@
|
|||||||
jsmiscdir = $(pkgdatadir)/js/misc
|
jsmiscdir = $(pkgdatadir)/js/misc
|
||||||
|
|
||||||
dist_jsmisc_DATA = \
|
dist_jsmisc_DATA = \
|
||||||
docInfo.js
|
docInfo.js \
|
||||||
|
format.js \
|
||||||
|
params.js
|
||||||
|
@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
|
|||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const Search = imports.ui.search;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const THUMBNAIL_ICON_MARGIN = 2;
|
const THUMBNAIL_ICON_MARGIN = 2;
|
||||||
@ -17,69 +18,43 @@ function DocInfo(recentInfo) {
|
|||||||
|
|
||||||
DocInfo.prototype = {
|
DocInfo.prototype = {
|
||||||
_init : function(recentInfo) {
|
_init : function(recentInfo) {
|
||||||
this._recentInfo = recentInfo;
|
this.recentInfo = recentInfo;
|
||||||
// We actually used get_modified() instead of get_visited()
|
// We actually used get_modified() instead of get_visited()
|
||||||
// here, as GtkRecentInfo doesn't updated get_visited()
|
// here, as GtkRecentInfo doesn't updated get_visited()
|
||||||
// correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
// correctly. See http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
||||||
this.timestamp = recentInfo.get_modified().getTime() / 1000;
|
this.timestamp = recentInfo.get_modified().getTime() / 1000;
|
||||||
this.name = recentInfo.get_display_name();
|
this.name = recentInfo.get_display_name();
|
||||||
|
this._lowerName = this.name.toLowerCase();
|
||||||
this.uri = recentInfo.get_uri();
|
this.uri = recentInfo.get_uri();
|
||||||
this.mimeType = recentInfo.get_mime_type();
|
this.mimeType = recentInfo.get_mime_type();
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon : function(size) {
|
createIcon : function(size) {
|
||||||
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this._recentInfo);
|
return Shell.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
|
||||||
},
|
},
|
||||||
|
|
||||||
launch : function() {
|
launch : function() {
|
||||||
// While using Gio.app_info_launch_default_for_uri() would be
|
Shell.DocSystem.get_default().open(this.recentInfo);
|
||||||
// shorter in terms of lines of code, we are not doing so
|
|
||||||
// because that would duplicate the work of retrieving the
|
|
||||||
// mime type.
|
|
||||||
|
|
||||||
let appInfo = Gio.app_info_get_default_for_type(this.mimeType, true);
|
|
||||||
|
|
||||||
if (appInfo != null) {
|
|
||||||
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
|
|
||||||
} else {
|
|
||||||
log("Failed to get default application info for mime type " + this.mimeType +
|
|
||||||
". Will try to use the last application that registered the document.");
|
|
||||||
let appName = this._recentInfo.last_application();
|
|
||||||
let [success, appExec, count, time] = this._recentInfo.get_application_info(appName);
|
|
||||||
if (success) {
|
|
||||||
log("Will open a document with the following command: " + appExec);
|
|
||||||
// TODO: Change this once better support for creating
|
|
||||||
// GAppInfo is added to GtkRecentInfo, as right now
|
|
||||||
// this relies on the fact that the file uri is
|
|
||||||
// already a part of appExec, so we don't supply any
|
|
||||||
// files to appInfo.launch().
|
|
||||||
|
|
||||||
// The 'command line' passed to
|
|
||||||
// create_from_command_line is allowed to contain
|
|
||||||
// '%<something>' macros that are expanded to file
|
|
||||||
// name / icon name, etc, so we need to escape % as %%
|
|
||||||
appExec = appExec.replace(/%/g, "%%");
|
|
||||||
|
|
||||||
let appInfo = Gio.app_info_create_from_commandline(appExec, null, 0, null);
|
|
||||||
|
|
||||||
// The point of passing an app launch context to
|
|
||||||
// launch() is mostly to get startup notification and
|
|
||||||
// associated benefits like the app appearing on the
|
|
||||||
// right desktop; but it doesn't really work for now
|
|
||||||
// because with the way we create the appInfo we
|
|
||||||
// aren't reading the application's desktop file, and
|
|
||||||
// thus don't find the StartupNotify=true in it. So,
|
|
||||||
// despite passing the app launch context, no startup
|
|
||||||
// notification occurs.
|
|
||||||
appInfo.launch([], Main.createAppLaunchContext());
|
|
||||||
} else {
|
|
||||||
log("Failed to get application info for " + this.uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
exists : function() {
|
matchTerms: function(terms) {
|
||||||
return this._recentInfo.exists();
|
let mtype = Search.MatchType.NONE;
|
||||||
|
for (let i = 0; i < terms.length; i++) {
|
||||||
|
let term = terms[i];
|
||||||
|
let idx = this._lowerName.indexOf(term);
|
||||||
|
if (idx == 0) {
|
||||||
|
if (mtype != Search.MatchType.NONE)
|
||||||
|
return Search.MatchType.MULTIPLE;
|
||||||
|
mtype = Search.MatchType.PREFIX;
|
||||||
|
} else if (idx > 0) {
|
||||||
|
if (mtype != Search.MatchType.NONE)
|
||||||
|
return Search.MatchType.MULTIPLE;
|
||||||
|
mtype = Search.MatchType.SUBSTRING;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mtype;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,50 +66,86 @@ function getDocManager() {
|
|||||||
return docManagerInstance;
|
return docManagerInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
|
||||||
|
* which conform to the GenericDisplay item API.
|
||||||
|
*/
|
||||||
function DocManager() {
|
function DocManager() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
DocManager.prototype = {
|
DocManager.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._recentManager = Gtk.RecentManager.get_default();
|
this._docSystem = Shell.DocSystem.get_default();
|
||||||
this._items = {};
|
this._infosByTimestamp = [];
|
||||||
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
this._infosByUri = {};
|
||||||
this._reload();
|
this._docSystem.connect('changed', Lang.bind(this, this._reload));
|
||||||
this.emit('changed');
|
|
||||||
}));
|
|
||||||
this._reload();
|
this._reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
_reload: function() {
|
_reload: function() {
|
||||||
let docs = this._recentManager.get_items();
|
let docs = this._docSystem.get_all();
|
||||||
let newItems = {};
|
this._infosByTimestamp = [];
|
||||||
|
this._infosByUri = {};
|
||||||
for (let i = 0; i < docs.length; i++) {
|
for (let i = 0; i < docs.length; i++) {
|
||||||
let recentInfo = docs[i];
|
let recentInfo = docs[i];
|
||||||
if (!recentInfo.exists())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let docInfo = new DocInfo(recentInfo);
|
let docInfo = new DocInfo(recentInfo);
|
||||||
|
this._infosByTimestamp.push(docInfo);
|
||||||
// we use GtkRecentInfo URI as an item Id
|
this._infosByUri[docInfo.uri] = docInfo;
|
||||||
newItems[docInfo.uri] = docInfo;
|
|
||||||
}
|
}
|
||||||
let deleted = {};
|
this.emit('changed');
|
||||||
for (var uri in this._items) {
|
|
||||||
if (!(uri in newItems))
|
|
||||||
deleted[uri] = this._items[uri];
|
|
||||||
}
|
|
||||||
/* If we'd cached any thumbnail references that no longer exist,
|
|
||||||
dump them here */
|
|
||||||
let texCache = Shell.TextureCache.get_default();
|
|
||||||
for (var uri in deleted) {
|
|
||||||
texCache.evict_recent_thumbnail(this._items[uri]);
|
|
||||||
}
|
|
||||||
this._items = newItems;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getItems: function() {
|
getTimestampOrderedInfos: function() {
|
||||||
return this._items;
|
return this._infosByTimestamp;
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfosByUri: function() {
|
||||||
|
return this._infosByUri;
|
||||||
|
},
|
||||||
|
|
||||||
|
lookupByUri: function(uri) {
|
||||||
|
return this._infosByUri[uri];
|
||||||
|
},
|
||||||
|
|
||||||
|
queueExistenceCheck: function(count) {
|
||||||
|
return this._docSystem.queue_existence_check(count);
|
||||||
|
},
|
||||||
|
|
||||||
|
initialSearch: function(terms) {
|
||||||
|
let multipleMatches = [];
|
||||||
|
let prefixMatches = [];
|
||||||
|
let substringMatches = [];
|
||||||
|
for (let i = 0; i < this._infosByTimestamp.length; i++) {
|
||||||
|
let item = this._infosByTimestamp[i];
|
||||||
|
let mtype = item.matchTerms(terms);
|
||||||
|
if (mtype == Search.MatchType.MULTIPLE)
|
||||||
|
multipleMatches.push(item.uri);
|
||||||
|
else if (mtype == Search.MatchType.PREFIX)
|
||||||
|
prefixMatches.push(item.uri);
|
||||||
|
else if (mtype == Search.MatchType.SUBSTRING)
|
||||||
|
substringMatches.push(item.uri);
|
||||||
|
}
|
||||||
|
return multipleMatches.concat(prefixMatches.concat(substringMatches));
|
||||||
|
},
|
||||||
|
|
||||||
|
subsearch: function(previousResults, terms) {
|
||||||
|
let multipleMatches = [];
|
||||||
|
let prefixMatches = [];
|
||||||
|
let substringMatches = [];
|
||||||
|
for (let i = 0; i < previousResults.length; i++) {
|
||||||
|
let uri = previousResults[i];
|
||||||
|
let item = this._infosByUri[uri];
|
||||||
|
let mtype = item.matchTerms(terms);
|
||||||
|
if (mtype == Search.MatchType.MULTIPLE)
|
||||||
|
multipleMatches.push(uri);
|
||||||
|
else if (mtype == Search.MatchType.PREFIX)
|
||||||
|
prefixMatches.push(uri);
|
||||||
|
else if (mtype == Search.MatchType.SUBSTRING)
|
||||||
|
substringMatches.push(uri);
|
||||||
|
}
|
||||||
|
return multipleMatches.concat(prefixMatches.concat(substringMatches));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
js/misc/format.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is intended to extend the String object and provide
|
||||||
|
* an String.format API for string formatting.
|
||||||
|
* It has to be set up using String.prototype.format = Format.format;
|
||||||
|
* Usage:
|
||||||
|
* "somestring %s %d".format('hello', 5);
|
||||||
|
* It supports %s, %d and %f, for %f it also support precisions like
|
||||||
|
* "%.2f".format(1.526)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function format() {
|
||||||
|
let str = this;
|
||||||
|
let i = 0;
|
||||||
|
let args = arguments;
|
||||||
|
|
||||||
|
return str.replace(/%(?:\.([0-9]+))?(.)/g, function (str, precisionGroup, genericGroup) {
|
||||||
|
|
||||||
|
if (precisionGroup != '' && genericGroup != 'f')
|
||||||
|
throw new Error("Precision can only be specified for 'f'");
|
||||||
|
|
||||||
|
switch (genericGroup) {
|
||||||
|
case '%':
|
||||||
|
return '%';
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
return args[i++].toString();
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
return parseInt(args[i++]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
if (precisionGroup == '')
|
||||||
|
return parseFloat(args[i++]);
|
||||||
|
else
|
||||||
|
return parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported conversion character %' + genericGroup);
|
||||||
|
}
|
||||||
|
return ""; // Suppress warning
|
||||||
|
});
|
||||||
|
}
|
33
js/misc/params.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
// parse:
|
||||||
|
// @params: caller-provided parameter object, or %null
|
||||||
|
// @default: function-provided defaults object
|
||||||
|
// @allowExtras: whether or not to allow properties not in @default
|
||||||
|
//
|
||||||
|
// Examines @params and fills in default values from @defaults for
|
||||||
|
// any properties in @defaults that don't appear in @params. If
|
||||||
|
// @allowExtras is not %true, it will throw an error if @params
|
||||||
|
// contains any properties that aren't in @defaults.
|
||||||
|
//
|
||||||
|
// If @params is %null, this returns @defaults.
|
||||||
|
//
|
||||||
|
// Return value: the updated params
|
||||||
|
function parse(params, defaults, allowExtras) {
|
||||||
|
if (!params)
|
||||||
|
return defaults;
|
||||||
|
|
||||||
|
if (!allowExtras) {
|
||||||
|
for (let prop in params) {
|
||||||
|
if (!(prop in defaults))
|
||||||
|
throw new Error('Unrecognized parameter "' + prop + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let prop in defaults) {
|
||||||
|
if (!(prop in params))
|
||||||
|
params[prop] = defaults[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
@ -3,23 +3,29 @@ jsuidir = $(pkgdatadir)/js/ui
|
|||||||
dist_jsui_DATA = \
|
dist_jsui_DATA = \
|
||||||
altTab.js \
|
altTab.js \
|
||||||
appDisplay.js \
|
appDisplay.js \
|
||||||
appIcon.js \
|
appFavorites.js \
|
||||||
button.js \
|
calendar.js \
|
||||||
chrome.js \
|
chrome.js \
|
||||||
dash.js \
|
dash.js \
|
||||||
dnd.js \
|
dnd.js \
|
||||||
docDisplay.js \
|
docDisplay.js \
|
||||||
|
environment.js \
|
||||||
|
extensionSystem.js \
|
||||||
genericDisplay.js \
|
genericDisplay.js \
|
||||||
lightbox.js \
|
lightbox.js \
|
||||||
link.js \
|
link.js \
|
||||||
lookingGlass.js \
|
lookingGlass.js \
|
||||||
main.js \
|
main.js \
|
||||||
|
messageTray.js \
|
||||||
|
notificationDaemon.js \
|
||||||
overview.js \
|
overview.js \
|
||||||
panel.js \
|
panel.js \
|
||||||
places.js \
|
placeDisplay.js \
|
||||||
runDialog.js \
|
runDialog.js \
|
||||||
|
search.js \
|
||||||
shellDBus.js \
|
shellDBus.js \
|
||||||
sidebar.js \
|
sidebar.js \
|
||||||
|
statusMenu.js \
|
||||||
tweener.js \
|
tweener.js \
|
||||||
widget.js \
|
widget.js \
|
||||||
widgetBox.js \
|
widgetBox.js \
|
||||||
|
849
js/ui/altTab.js
@ -4,24 +4,35 @@ const Big = imports.gi.Big;
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const AppIcon = imports.ui.appIcon;
|
|
||||||
const Lightbox = imports.ui.lightbox;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const POPUP_BG_COLOR = new Clutter.Color();
|
const POPUP_ARROW_COLOR = new Clutter.Color();
|
||||||
POPUP_BG_COLOR.from_pixel(0x00000080);
|
POPUP_ARROW_COLOR.from_pixel(0xffffffff);
|
||||||
const POPUP_APPICON_BORDER_COLOR = new Clutter.Color();
|
const POPUP_UNFOCUSED_ARROW_COLOR = new Clutter.Color();
|
||||||
POPUP_APPICON_BORDER_COLOR.from_pixel(0xffffffff);
|
POPUP_UNFOCUSED_ARROW_COLOR.from_pixel(0x808080ff);
|
||||||
|
const TRANSPARENT_COLOR = new Clutter.Color();
|
||||||
|
TRANSPARENT_COLOR.from_pixel(0x00000000);
|
||||||
|
|
||||||
const POPUP_GRID_SPACING = 8;
|
const POPUP_APPICON_SIZE = 96;
|
||||||
const POPUP_ICON_SIZE = 48;
|
const POPUP_LIST_SPACING = 8;
|
||||||
const POPUP_NUM_COLUMNS = 5;
|
|
||||||
|
|
||||||
const POPUP_POINTER_SELECTION_THRESHOLD = 3;
|
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
|
||||||
|
|
||||||
|
const THUMBNAIL_SIZE = 256;
|
||||||
|
const THUMBNAIL_POPUP_TIME = 500; // milliseconds
|
||||||
|
const THUMBNAIL_FADE_TIME = 0.2; // seconds
|
||||||
|
|
||||||
|
function mod(a, b) {
|
||||||
|
return (a + b) % b;
|
||||||
|
}
|
||||||
|
|
||||||
function AltTabPopup() {
|
function AltTabPopup() {
|
||||||
this._init();
|
this._init();
|
||||||
@ -29,61 +40,31 @@ function AltTabPopup() {
|
|||||||
|
|
||||||
AltTabPopup.prototype = {
|
AltTabPopup.prototype = {
|
||||||
_init : function() {
|
_init : function() {
|
||||||
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
|
this.actor = new Clutter.Group({ reactive: true,
|
||||||
corner_radius: POPUP_GRID_SPACING,
|
x: 0,
|
||||||
padding: POPUP_GRID_SPACING,
|
y: 0,
|
||||||
spacing: POPUP_GRID_SPACING,
|
width: global.screen_width,
|
||||||
orientation: Big.BoxOrientation.VERTICAL,
|
height: global.screen_height });
|
||||||
reactive: true });
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
// Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently
|
|
||||||
// just implemented using a chain of Big.Box.
|
|
||||||
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
|
||||||
orientation: Big.BoxOrientation.VERTICAL });
|
|
||||||
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
x_align: Big.BoxAlignment.CENTER });
|
|
||||||
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
|
|
||||||
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
this._icons = [];
|
|
||||||
this._currentWindows = [];
|
|
||||||
this._haveModal = false;
|
this._haveModal = false;
|
||||||
this._selected = 0;
|
|
||||||
this._highlightedWindow = null;
|
this._currentApp = 0;
|
||||||
this._toplevels = global.window_group.get_children();
|
this._currentWindow = 0;
|
||||||
|
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();
|
||||||
|
|
||||||
global.stage.add_actor(this.actor);
|
global.stage.add_actor(this.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_addIcon : function(appIcon) {
|
show : function(backward) {
|
||||||
appIcon.connect('activate', Lang.bind(this, this._appClicked));
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
appIcon.connect('activate-window', Lang.bind(this, this._windowClicked));
|
let apps = tracker.get_running_apps ("");
|
||||||
appIcon.connect('highlight-window', Lang.bind(this, this._windowHovered));
|
|
||||||
appIcon.connect('menu-popped-up', Lang.bind(this, this._menuPoppedUp));
|
|
||||||
appIcon.connect('menu-popped-down', Lang.bind(this, this._menuPoppedDown));
|
|
||||||
|
|
||||||
appIcon.actor.connect('enter-event', Lang.bind(this, this._iconEntered));
|
|
||||||
|
|
||||||
// FIXME?
|
|
||||||
appIcon.actor.border = 2;
|
|
||||||
appIcon.highlight_border_color = POPUP_APPICON_BORDER_COLOR;
|
|
||||||
|
|
||||||
this._icons.push(appIcon);
|
|
||||||
this._currentWindows.push(appIcon.windows[0]);
|
|
||||||
|
|
||||||
// Add it to the grid
|
|
||||||
if (!this._gridRow || this._gridRow.get_children().length == POPUP_NUM_COLUMNS) {
|
|
||||||
this._gridRow = new Big.Box({ spacing: POPUP_GRID_SPACING,
|
|
||||||
orientation: Big.BoxOrientation.HORIZONTAL });
|
|
||||||
this._grid.append(this._gridRow, Big.BoxPackFlags.NONE);
|
|
||||||
}
|
|
||||||
this._gridRow.append(appIcon.actor, Big.BoxPackFlags.NONE);
|
|
||||||
},
|
|
||||||
|
|
||||||
show : function(initialSelection) {
|
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
|
||||||
let apps = appMonitor.get_running_apps ("");
|
|
||||||
|
|
||||||
if (!apps.length)
|
if (!apps.length)
|
||||||
return false;
|
return false;
|
||||||
@ -95,36 +76,52 @@ AltTabPopup.prototype = {
|
|||||||
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||||
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||||
|
|
||||||
this._motionEventId = this.actor.connect('motion-event', Lang.bind(this, this._mouseMoved));
|
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
|
||||||
this._mouseActive = false;
|
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
|
||||||
this._mouseMovement = 0;
|
|
||||||
|
|
||||||
// Contruct the AppIcons, sort by time, add to the popup
|
this._appSwitcher = new AppSwitcher(apps);
|
||||||
let icons = [];
|
this.actor.add_actor(this._appSwitcher.actor);
|
||||||
for (let i = 0; i < apps.length; i++)
|
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||||
icons.push(new AppIcon.AppIcon(apps[i], AppIcon.MenuType.BELOW));
|
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
|
||||||
icons.sort(Lang.bind(this, this._sortAppIcon));
|
|
||||||
for (let i = 0; i < icons.length; i++)
|
|
||||||
this._addIcon(icons[i]);
|
|
||||||
|
|
||||||
// Need to specify explicit width and height because the
|
let primary = global.get_primary_monitor();
|
||||||
// window_group may not actually cover the whole screen
|
this._appSwitcher.actor.x = primary.x + Math.floor((primary.width - this._appSwitcher.actor.width) / 2);
|
||||||
this._lightbox = new Lightbox.Lightbox(global.window_group,
|
this._appSwitcher.actor.y = primary.y + Math.floor((primary.height - this._appSwitcher.actor.height) / 2);
|
||||||
global.screen_width,
|
|
||||||
global.screen_height);
|
|
||||||
|
|
||||||
this.actor.show_all();
|
this._appIcons = this._appSwitcher.icons;
|
||||||
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
|
|
||||||
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
|
|
||||||
|
|
||||||
this._updateSelection(initialSelection);
|
// Make the initial selection
|
||||||
|
if (this._appIcons.length == 1) {
|
||||||
|
if (!backward && this._appIcons[0].cachedWindows.length > 1) {
|
||||||
|
// For compatibility with the multi-app case below
|
||||||
|
this._select(0, 1, true);
|
||||||
|
} else
|
||||||
|
this._select(0);
|
||||||
|
} else if (backward) {
|
||||||
|
this._select(this._appIcons.length - 1);
|
||||||
|
} else {
|
||||||
|
let firstWindows = this._appIcons[0].cachedWindows;
|
||||||
|
if (firstWindows.length > 1) {
|
||||||
|
let curAppNextWindow = firstWindows[1];
|
||||||
|
let nextAppWindow = this._appIcons[1].cachedWindows[0];
|
||||||
|
|
||||||
|
// If the next window of the current app is more-recently-used
|
||||||
|
// than the first window of the next app, then select it.
|
||||||
|
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
|
||||||
|
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
|
||||||
|
this._select(0, 1, true);
|
||||||
|
else
|
||||||
|
this._select(1);
|
||||||
|
} else
|
||||||
|
this._select(1);
|
||||||
|
}
|
||||||
|
|
||||||
// There's a race condition; if the user released Alt before
|
// There's a race condition; if the user released Alt before
|
||||||
// we got the grab, then we won't be notified. (See
|
// we got the grab, then we won't be notified. (See
|
||||||
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
|
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
|
||||||
// details.) So we check now. (Have to do this after calling
|
// details.) So we check now. (Have to do this after updating
|
||||||
// _updateSelection.)
|
// selection.)
|
||||||
let [screen, x, y, mods] = Gdk.Display.get_default().get_pointer();
|
let mods = global.get_modifier_keys();
|
||||||
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
||||||
this._finish();
|
this._finish();
|
||||||
return false;
|
return false;
|
||||||
@ -133,40 +130,59 @@ AltTabPopup.prototype = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_hasVisibleWindows : function(appIcon) {
|
_nextApp : function() {
|
||||||
for (let i = 0; i < appIcon.windows.length; i++) {
|
return mod(this._currentApp + 1, this._appIcons.length);
|
||||||
if (appIcon.windows[i].showing_on_its_workspace())
|
},
|
||||||
return true;
|
_previousApp : function() {
|
||||||
}
|
return mod(this._currentApp - 1, this._appIcons.length);
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_sortAppIcon : function(appIcon1, appIcon2) {
|
_nextWindow : function() {
|
||||||
let vis1 = this._hasVisibleWindows(appIcon1);
|
return mod(this._currentWindow + 1,
|
||||||
let vis2 = this._hasVisibleWindows(appIcon2);
|
this._appIcons[this._currentApp].cachedWindows.length);
|
||||||
|
},
|
||||||
if (vis1 && !vis2) {
|
_previousWindow : function() {
|
||||||
return -1;
|
return mod(this._currentWindow - 1,
|
||||||
} else if (vis2 && !vis1) {
|
this._appIcons[this._currentApp].cachedWindows.length);
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
// The app's most-recently-used window is first
|
|
||||||
// in its list
|
|
||||||
return (appIcon2.windows[0].get_user_time() -
|
|
||||||
appIcon1.windows[0].get_user_time());
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_keyPressEvent : function(actor, event) {
|
_keyPressEvent : function(actor, event) {
|
||||||
let keysym = event.get_key_symbol();
|
let keysym = event.get_key_symbol();
|
||||||
let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
|
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||||
|
|
||||||
if (keysym == Clutter.Tab)
|
this._disableHover();
|
||||||
this._updateSelection(backwards ? -1 : 1);
|
|
||||||
else if (keysym == Clutter.grave)
|
// The WASD stuff is for debugging in Xephyr, where the arrow
|
||||||
this._updateWindowSelection(backwards ? -1 : 1);
|
// keys aren't mapped correctly
|
||||||
|
|
||||||
|
if (keysym == Clutter.grave)
|
||||||
|
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
|
||||||
else if (keysym == Clutter.Escape)
|
else if (keysym == Clutter.Escape)
|
||||||
this.destroy();
|
this.destroy();
|
||||||
|
else if (this._thumbnailsFocused) {
|
||||||
|
if (keysym == Clutter.Tab) {
|
||||||
|
if (shift && this._currentWindow == 0)
|
||||||
|
this._select(this._previousApp());
|
||||||
|
else if (!shift && this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||||
|
this._select(this._nextApp());
|
||||||
|
else
|
||||||
|
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
|
||||||
|
} else if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||||
|
this._select(this._currentApp, this._previousWindow());
|
||||||
|
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||||
|
this._select(this._currentApp, this._nextWindow());
|
||||||
|
else if (keysym == Clutter.Up || keysym == Clutter.w)
|
||||||
|
this._select(this._currentApp, null, true);
|
||||||
|
} else {
|
||||||
|
if (keysym == Clutter.Tab)
|
||||||
|
this._select(shift ? this._previousApp() : this._nextApp());
|
||||||
|
else if (keysym == Clutter.Left || keysym == Clutter.a)
|
||||||
|
this._select(this._previousApp());
|
||||||
|
else if (keysym == Clutter.Right || keysym == Clutter.d)
|
||||||
|
this._select(this._nextApp());
|
||||||
|
else if (keysym == Clutter.Down || keysym == Clutter.s)
|
||||||
|
this._select(this._currentApp, this._currentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -180,48 +196,88 @@ AltTabPopup.prototype = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_appClicked : function(icon) {
|
_onScroll : function(actor, event) {
|
||||||
Main.activateWindow(icon.windows[0]);
|
let direction = event.get_scroll_direction();
|
||||||
this.destroy();
|
if (direction == Clutter.ScrollDirection.UP) {
|
||||||
},
|
if (this._thumbnailsFocused) {
|
||||||
|
if (this._currentWindow == 0)
|
||||||
_windowClicked : function(icon, window) {
|
this._select(this._previousApp());
|
||||||
if (window)
|
else
|
||||||
Main.activateWindow(window);
|
this._select(this._currentApp, this._previousWindow());
|
||||||
this.destroy();
|
} else {
|
||||||
},
|
let nwindows = this._appIcons[this._currentApp].cachedWindows.length;
|
||||||
|
if (nwindows > 1)
|
||||||
_windowHovered : function(icon, window) {
|
this._select(this._currentApp, nwindows - 1);
|
||||||
if (window)
|
else
|
||||||
this._highlightWindow(window);
|
this._select(this._previousApp());
|
||||||
},
|
}
|
||||||
|
} else if (direction == Clutter.ScrollDirection.DOWN) {
|
||||||
_mouseMoved : function(actor, event) {
|
if (this._thumbnailsFocused) {
|
||||||
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
|
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||||
return;
|
this._select(this._nextApp());
|
||||||
|
else
|
||||||
this.actor.disconnect(this._motionEventId);
|
this._select(this._currentApp, this._nextWindow());
|
||||||
this._mouseActive = true;
|
} else {
|
||||||
|
let nwindows = this._appIcons[this._currentApp].cachedWindows.length;
|
||||||
actor = event.get_source();
|
if (nwindows > 1)
|
||||||
while (actor) {
|
this._select(this._currentApp, 0);
|
||||||
if (actor._delegate instanceof AppIcon.AppIcon) {
|
else
|
||||||
this._iconEntered(actor, event);
|
this._select(this._nextApp());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
actor = actor.get_parent();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_iconEntered : function(actor, event) {
|
_clickedOutside : function(actor, event) {
|
||||||
let index = this._icons.indexOf(actor._delegate);
|
this.destroy();
|
||||||
if (this._mouseActive)
|
},
|
||||||
this._updateSelection(index - this._selected);
|
|
||||||
|
_appActivated : function(appSwitcher, n) {
|
||||||
|
// If the user clicks on the selected app, activate the
|
||||||
|
// selected window; otherwise (eg, they click on an app while
|
||||||
|
// !mouseActive) activate the first window of the clicked-on
|
||||||
|
// app.
|
||||||
|
let window = (n == this._currentApp) ? this._currentWindow : 0;
|
||||||
|
Main.activateWindow(this._appIcons[n].cachedWindows[window]);
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_appEntered : function(appSwitcher, n) {
|
||||||
|
if (!this._mouseActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._select(n);
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowActivated : function(thumbnailList, n) {
|
||||||
|
Main.activateWindow(this._appIcons[this._currentApp].cachedWindows[n]);
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_windowEntered : function(thumbnailList, n) {
|
||||||
|
if (!this._mouseActive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._select(this._currentApp, n);
|
||||||
|
},
|
||||||
|
|
||||||
|
_disableHover : function() {
|
||||||
|
this._mouseActive = false;
|
||||||
|
|
||||||
|
if (this._motionTimeoutId != 0)
|
||||||
|
Mainloop.source_remove(this._motionTimeoutId);
|
||||||
|
|
||||||
|
this._motionTimeoutId = Mainloop.timeout_add(DISABLE_HOVER_TIMEOUT, Lang.bind(this, this._mouseTimedOut));
|
||||||
|
},
|
||||||
|
|
||||||
|
_mouseTimedOut : function() {
|
||||||
|
this._motionTimeoutId = 0;
|
||||||
|
this._mouseActive = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_finish : function() {
|
_finish : function() {
|
||||||
if (this._highlightedWindow)
|
let app = this._appIcons[this._currentApp];
|
||||||
Main.activateWindow(this._highlightedWindow);
|
let window = app.cachedWindows[this._currentWindow];
|
||||||
|
Main.activateWindow(window);
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -233,55 +289,486 @@ AltTabPopup.prototype = {
|
|||||||
if (this._haveModal)
|
if (this._haveModal)
|
||||||
Main.popModal(this.actor);
|
Main.popModal(this.actor);
|
||||||
|
|
||||||
if (this._lightbox)
|
|
||||||
this._lightbox.destroy();
|
|
||||||
|
|
||||||
if (this._keyPressEventId)
|
if (this._keyPressEventId)
|
||||||
global.stage.disconnect(this._keyPressEventId);
|
global.stage.disconnect(this._keyPressEventId);
|
||||||
if (this._keyReleaseEventId)
|
if (this._keyReleaseEventId)
|
||||||
global.stage.disconnect(this._keyReleaseEventId);
|
global.stage.disconnect(this._keyReleaseEventId);
|
||||||
|
|
||||||
|
if (this._motionTimeoutId != 0)
|
||||||
|
Mainloop.source_remove(this._motionTimeoutId);
|
||||||
|
if (this._thumbnailTimeoutId != 0)
|
||||||
|
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateSelection : function(delta) {
|
/**
|
||||||
this._icons[this._selected].setHighlight(false);
|
* _select:
|
||||||
if (delta != 0 && this._selectedMenu)
|
* @app: index of the app to select
|
||||||
this._selectedMenu.popdown();
|
* @window: (optional) index of which of @app's windows to select
|
||||||
|
* @forceAppFocus: optional flag, see below
|
||||||
this._selected = (this._selected + this._icons.length + delta) % this._icons.length;
|
*
|
||||||
this._icons[this._selected].setHighlight(true);
|
* Selects the indicated @app, and optional @window, and sets
|
||||||
|
* this._thumbnailsFocused appropriately to indicate whether the
|
||||||
this._highlightWindow(this._currentWindows[this._selected]);
|
* arrow keys should act on the app list or the thumbnail list.
|
||||||
},
|
*
|
||||||
|
* If @app is specified and @window is unspecified or %null, then
|
||||||
_menuPoppedUp : function(icon, menu) {
|
* the app is highlighted (ie, given a light background), and the
|
||||||
this._selectedMenu = menu;
|
* current thumbnail list, if any, is destroyed. If @app has
|
||||||
},
|
* multiple windows, and @forceAppFocus is not %true, then a
|
||||||
|
* timeout is started to open a thumbnail list.
|
||||||
_menuPoppedDown : function(icon, menu) {
|
*
|
||||||
this._selectedMenu = null;
|
* If @app and @window are specified (and @forceAppFocus is not),
|
||||||
},
|
* then @app will be outlined, a thumbnail list will be created
|
||||||
|
* and focused (if it hasn't been already), and the @window'th
|
||||||
_updateWindowSelection : function(delta) {
|
* window in it will be highlighted.
|
||||||
let icon = this._icons[this._selected];
|
*
|
||||||
|
* If @app and @window are specified and @forceAppFocus is %true,
|
||||||
if (!this._selectedMenu)
|
* then @app will be highlighted, and @window outlined, and the
|
||||||
icon.popupMenu();
|
* app list will have the keyboard focus.
|
||||||
if (!this._selectedMenu)
|
*/
|
||||||
return;
|
_select : function(app, window, forceAppFocus) {
|
||||||
|
if (app != this._currentApp || window == null) {
|
||||||
let next = 0;
|
if (this._thumbnails)
|
||||||
for (let i = 0; i < icon.windows.length; i++) {
|
this._destroyThumbnails();
|
||||||
if (icon.windows[i] == this._highlightedWindow) {
|
}
|
||||||
next = (i + icon.windows.length + delta) % icon.windows.length;
|
|
||||||
break;
|
if (this._thumbnailTimeoutId != 0) {
|
||||||
}
|
Mainloop.source_remove(this._thumbnailTimeoutId);
|
||||||
|
this._thumbnailTimeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._thumbnailsFocused = (window != null) && !forceAppFocus;
|
||||||
|
|
||||||
|
this._currentApp = app;
|
||||||
|
this._currentWindow = window ? window : 0;
|
||||||
|
this._appSwitcher.highlight(app, this._thumbnailsFocused);
|
||||||
|
|
||||||
|
if (window != null) {
|
||||||
|
if (!this._thumbnails)
|
||||||
|
this._createThumbnails();
|
||||||
|
this._currentWindow = window;
|
||||||
|
this._thumbnails.highlight(window, forceAppFocus);
|
||||||
|
} else if (this._appIcons[this._currentApp].cachedWindows.length > 1 &&
|
||||||
|
!forceAppFocus) {
|
||||||
|
this._thumbnailTimeoutId = Mainloop.timeout_add (
|
||||||
|
THUMBNAIL_POPUP_TIME,
|
||||||
|
Lang.bind(this, function () {
|
||||||
|
this._select(this._currentApp, 0, true);
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
this._selectedMenu.selectWindow(icon.windows[next]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_highlightWindow : function(metaWin) {
|
_destroyThumbnails : function() {
|
||||||
this._highlightedWindow = metaWin;
|
Tweener.addTween(this._thumbnails.actor,
|
||||||
this._currentWindows[this._selected] = metaWin;
|
{ opacity: 0,
|
||||||
this._lightbox.highlight(this._highlightedWindow.get_compositor_private());
|
time: THUMBNAIL_FADE_TIME,
|
||||||
|
transition: "easeOutQuad",
|
||||||
|
onComplete: function() { this.destroy(); }
|
||||||
|
});
|
||||||
|
this._thumbnails = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createThumbnails : function() {
|
||||||
|
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].cachedWindows);
|
||||||
|
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
|
||||||
|
this._thumbnails.connect('item-entered', Lang.bind(this, this._windowEntered));
|
||||||
|
|
||||||
|
this.actor.add_actor(this._thumbnails.actor);
|
||||||
|
|
||||||
|
let thumbnailCenter;
|
||||||
|
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
|
||||||
|
// Center the thumbnails under the corresponding AppIcon.
|
||||||
|
// If this is being called when the switcher is first
|
||||||
|
// being brought up, then nothing will have been assigned
|
||||||
|
// an allocation yet, and the get_transformed_position()
|
||||||
|
// call will return 0,0.
|
||||||
|
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
|
||||||
|
// Calling clutter_actor_get_allocation_box() would force
|
||||||
|
// it to properly allocate itself, but we can't call that
|
||||||
|
// because it has an out-caller-allocates arg. So we use
|
||||||
|
// clutter_stage_get_actor_at_pos(), which will force a
|
||||||
|
// reallocation as a side effect.
|
||||||
|
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
|
||||||
|
|
||||||
|
let icon = this._appIcons[this._currentApp].actor;
|
||||||
|
let [stageX, stageY] = icon.get_transformed_position();
|
||||||
|
thumbnailCenter = stageX + icon.width / 2;
|
||||||
|
} else {
|
||||||
|
// Center the thumbnails on the monitor
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
|
thumbnailCenter = primary.x + primary.width / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
|
||||||
|
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
|
||||||
|
|
||||||
|
this._thumbnails.actor.opacity = 0;
|
||||||
|
Tweener.addTween(this._thumbnails.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: THUMBNAIL_FADE_TIME,
|
||||||
|
transition: "easeOutQuad"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function SwitcherList(squareItems) {
|
||||||
|
this._init(squareItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitcherList.prototype = {
|
||||||
|
_init : function(squareItems) {
|
||||||
|
this.actor = new St.Bin({ style_class: 'switcher-list' });
|
||||||
|
|
||||||
|
// Here we use a GenericContainer so that we can force all the
|
||||||
|
// children except the separator to have the same width.
|
||||||
|
this._list = new Shell.GenericContainer();
|
||||||
|
this._list.spacing = POPUP_LIST_SPACING;
|
||||||
|
|
||||||
|
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
|
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
|
this._list.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
|
||||||
|
this.actor.add_actor(this._list);
|
||||||
|
|
||||||
|
this._items = [];
|
||||||
|
this._highlighted = -1;
|
||||||
|
this._separator = null;
|
||||||
|
this._squareItems = squareItems;
|
||||||
|
},
|
||||||
|
|
||||||
|
addItem : function(item) {
|
||||||
|
let bbox = new St.Clickable({ style_class: 'item-box',
|
||||||
|
reactive: true });
|
||||||
|
|
||||||
|
bbox.set_child(item);
|
||||||
|
this._list.add_actor(bbox);
|
||||||
|
|
||||||
|
let n = this._items.length;
|
||||||
|
bbox.connect('clicked', Lang.bind(this, function () {
|
||||||
|
this._itemActivated(n);
|
||||||
|
}));
|
||||||
|
bbox.connect('enter-event', Lang.bind(this, function () {
|
||||||
|
this._itemEntered(n);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._items.push(bbox);
|
||||||
|
},
|
||||||
|
|
||||||
|
addSeparator: function () {
|
||||||
|
let box = new St.Bin({ style_class: 'separator' })
|
||||||
|
this._separator = box;
|
||||||
|
this._list.add_actor(box);
|
||||||
|
},
|
||||||
|
|
||||||
|
highlight: function(index, justOutline) {
|
||||||
|
if (this._highlighted != -1)
|
||||||
|
this._items[this._highlighted].style_class = 'item-box';
|
||||||
|
|
||||||
|
this._highlighted = index;
|
||||||
|
|
||||||
|
if (this._highlighted != -1) {
|
||||||
|
if (justOutline)
|
||||||
|
this._items[this._highlighted].style_class = 'outlined-item-box';
|
||||||
|
else
|
||||||
|
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_itemActivated: function(n) {
|
||||||
|
this.emit('item-activated', n);
|
||||||
|
},
|
||||||
|
|
||||||
|
_itemEntered: function(n) {
|
||||||
|
this.emit('item-entered', n);
|
||||||
|
},
|
||||||
|
|
||||||
|
_maxChildWidth: function (forHeight) {
|
||||||
|
let maxChildMin = 0;
|
||||||
|
let maxChildNat = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
|
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
|
||||||
|
maxChildMin = Math.max(childMin, maxChildMin);
|
||||||
|
maxChildNat = Math.max(childNat, maxChildNat);
|
||||||
|
|
||||||
|
if (this._squareItems) {
|
||||||
|
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||||
|
maxChildMin = Math.max(childMin, maxChildMin);
|
||||||
|
maxChildNat = Math.max(childNat, maxChildNat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [maxChildMin, maxChildNat];
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||||
|
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
|
||||||
|
|
||||||
|
let separatorWidth = 0;
|
||||||
|
if (this._separator) {
|
||||||
|
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
|
||||||
|
separatorWidth = sepNat + this._list.spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||||
|
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
|
||||||
|
alloc.nat_size = this._items.length * maxChildNat + separatorWidth + totalSpacing;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
|
let maxChildMin = 0;
|
||||||
|
let maxChildNat = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
|
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
|
||||||
|
maxChildMin = Math.max(childMin, maxChildMin);
|
||||||
|
maxChildNat = Math.max(childNat, maxChildNat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._squareItems) {
|
||||||
|
let [childMin, childNat] = this._maxChildWidth(-1);
|
||||||
|
maxChildMin = Math.max(childMin, maxChildMin);
|
||||||
|
maxChildNat = Math.max(childNat, maxChildNat);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc.min_size = maxChildMin;
|
||||||
|
alloc.nat_size = maxChildNat;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function (actor, box, flags) {
|
||||||
|
let childHeight = box.y2 - box.y1;
|
||||||
|
|
||||||
|
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
|
||||||
|
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||||
|
|
||||||
|
let separatorWidth = 0;
|
||||||
|
if (this._separator) {
|
||||||
|
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
|
||||||
|
separatorWidth = sepNat;
|
||||||
|
totalSpacing += this._list.spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
|
||||||
|
|
||||||
|
let x = 0;
|
||||||
|
let children = this._list.get_children();
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
if (this._items.indexOf(children[i]) != -1) {
|
||||||
|
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
|
||||||
|
let vSpacing = (childHeight - childNat) / 2;
|
||||||
|
childBox.x1 = x;
|
||||||
|
childBox.y1 = vSpacing;
|
||||||
|
childBox.x2 = x + childWidth;
|
||||||
|
childBox.y2 = childBox.y1 + childNat;
|
||||||
|
children[i].allocate(childBox, flags);
|
||||||
|
|
||||||
|
x += this._list.spacing + childWidth;
|
||||||
|
} else if (children[i] == this._separator) {
|
||||||
|
// We want the separator to be more compact than the rest.
|
||||||
|
childBox.x1 = x;
|
||||||
|
childBox.y1 = 0;
|
||||||
|
childBox.x2 = x + separatorWidth;
|
||||||
|
childBox.y2 = childHeight;
|
||||||
|
children[i].allocate(childBox, flags);
|
||||||
|
x += this._list.spacing + separatorWidth;
|
||||||
|
} else {
|
||||||
|
// Something else, eg, AppSwitcher's arrows;
|
||||||
|
// we don't allocate it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Signals.addSignalMethods(SwitcherList.prototype);
|
||||||
|
|
||||||
|
function AppIcon(app) {
|
||||||
|
this._init(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppIcon.prototype = {
|
||||||
|
_init: function(app) {
|
||||||
|
this.app = app;
|
||||||
|
this.actor = new St.BoxLayout({ style_class: "alt-tab-app",
|
||||||
|
vertical: true });
|
||||||
|
this._icon = this.app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||||
|
this.actor.add(this._icon, { x_fill: false, y_fill: false } );
|
||||||
|
this._label = new St.Label({ text: this.app.get_name() });
|
||||||
|
this.actor.add(this._label, { x_fill: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppSwitcher(apps) {
|
||||||
|
this._init(apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppSwitcher.prototype = {
|
||||||
|
__proto__ : SwitcherList.prototype,
|
||||||
|
|
||||||
|
_init : function(apps) {
|
||||||
|
SwitcherList.prototype._init.call(this, true);
|
||||||
|
|
||||||
|
// Construct the AppIcons, sort by time, add to the popup
|
||||||
|
let activeWorkspace = global.screen.get_active_workspace();
|
||||||
|
let workspaceIcons = [];
|
||||||
|
let otherIcons = [];
|
||||||
|
for (let i = 0; i < apps.length; i++) {
|
||||||
|
let appIcon = new AppIcon(apps[i]);
|
||||||
|
// Cache the window list now; we don't handle dynamic changes here,
|
||||||
|
// and we don't want to be continually retrieving it
|
||||||
|
appIcon.cachedWindows = appIcon.app.get_windows();
|
||||||
|
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
|
||||||
|
workspaceIcons.push(appIcon);
|
||||||
|
else
|
||||||
|
otherIcons.push(appIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||||
|
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
|
||||||
|
|
||||||
|
this.icons = [];
|
||||||
|
this._arrows = [];
|
||||||
|
for (let i = 0; i < workspaceIcons.length; i++)
|
||||||
|
this._addIcon(workspaceIcons[i]);
|
||||||
|
if (workspaceIcons.length > 0 && otherIcons.length > 0)
|
||||||
|
this.addSeparator();
|
||||||
|
for (let i = 0; i < otherIcons.length; i++)
|
||||||
|
this._addIcon(otherIcons[i]);
|
||||||
|
|
||||||
|
this._curApp = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function (actor, box, flags) {
|
||||||
|
// Allocate the main list items
|
||||||
|
SwitcherList.prototype._allocate.call(this, actor, box, flags);
|
||||||
|
|
||||||
|
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
|
||||||
|
let arrowWidth = arrowHeight * 2;
|
||||||
|
|
||||||
|
// Now allocate each arrow underneath its item
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
for (let i = 0; i < this._items.length; i++) {
|
||||||
|
let itemBox = this._items[i].allocation;
|
||||||
|
childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
|
||||||
|
childBox.x2 = childBox.x1 + arrowWidth;
|
||||||
|
childBox.y1 = itemBox.y2 + arrowHeight;
|
||||||
|
childBox.y2 = childBox.y1 + arrowHeight;
|
||||||
|
this._arrows[i].allocate(childBox, flags);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// We override SwitcherList's highlight() method to also deal with
|
||||||
|
// the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
|
||||||
|
// will hide their arrows by default, but show them when their
|
||||||
|
// thumbnails are visible (ie, when the app icon is supposed to be
|
||||||
|
// in justOutline mode). Apps with multiple windows will normally
|
||||||
|
// show a dim arrow, but show a bright arrow when they are
|
||||||
|
// highlighted; their redraw handler will use the right color
|
||||||
|
// based on this._curApp; we just need to do a queue_relayout() to
|
||||||
|
// force it to redraw. (queue_redraw() doesn't work because
|
||||||
|
// ShellDrawingArea only redraws on allocate.)
|
||||||
|
highlight : function(n, justOutline) {
|
||||||
|
if (this._curApp != -1) {
|
||||||
|
if (this.icons[this._curApp].cachedWindows.length == 1)
|
||||||
|
this._arrows[this._curApp].hide();
|
||||||
|
else
|
||||||
|
this._arrows[this._curApp].queue_relayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitcherList.prototype.highlight.call(this, n, justOutline);
|
||||||
|
this._curApp = n;
|
||||||
|
|
||||||
|
if (this._curApp != -1) {
|
||||||
|
if (justOutline && this.icons[this._curApp].cachedWindows.length == 1)
|
||||||
|
this._arrows[this._curApp].show();
|
||||||
|
else
|
||||||
|
this._arrows[this._curApp].queue_relayout();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_addIcon : function(appIcon) {
|
||||||
|
this.icons.push(appIcon);
|
||||||
|
this.addItem(appIcon.actor);
|
||||||
|
|
||||||
|
let n = this._arrows.length;
|
||||||
|
let arrow = new St.DrawingArea();
|
||||||
|
arrow.connect('redraw', Lang.bind(this,
|
||||||
|
function (area, texture) {
|
||||||
|
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
|
||||||
|
TRANSPARENT_COLOR,
|
||||||
|
this._curApp == n ? POPUP_ARROW_COLOR : POPUP_UNFOCUSED_ARROW_COLOR);
|
||||||
|
}));
|
||||||
|
this._list.add_actor(arrow);
|
||||||
|
this._arrows.push(arrow);
|
||||||
|
|
||||||
|
if (appIcon.cachedWindows.length == 1)
|
||||||
|
arrow.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_hasWindowsOnWorkspace: function(appIcon, workspace) {
|
||||||
|
let windows = appIcon.cachedWindows;
|
||||||
|
for (let i = 0; i < windows.length; i++) {
|
||||||
|
if (windows[i].get_workspace() == workspace)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sortAppIcon : function(appIcon1, appIcon2) {
|
||||||
|
return appIcon1.app.compare(appIcon2.app);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function ThumbnailList(windows) {
|
||||||
|
this._init(windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThumbnailList.prototype = {
|
||||||
|
__proto__ : SwitcherList.prototype,
|
||||||
|
|
||||||
|
_init : function(windows) {
|
||||||
|
SwitcherList.prototype._init.call(this);
|
||||||
|
|
||||||
|
let activeWorkspace = global.screen.get_active_workspace();
|
||||||
|
|
||||||
|
// We fake the value of "separatorAdded" when the app has no window
|
||||||
|
// on the current workspace, to avoid displaying a useless separator in
|
||||||
|
// that case.
|
||||||
|
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
|
||||||
|
|
||||||
|
for (let i = 0; i < windows.length; i++) {
|
||||||
|
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
|
||||||
|
this.addSeparator();
|
||||||
|
separatorAdded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mutterWindow = windows[i].get_compositor_private();
|
||||||
|
let windowTexture = mutterWindow.get_texture ();
|
||||||
|
let [width, height] = windowTexture.get_size();
|
||||||
|
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
|
||||||
|
|
||||||
|
let box = new St.BoxLayout({ style_class: "thumbnail-box",
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
let bin = new St.Bin({ style_class: "thumbnail" });
|
||||||
|
let clone = new Clutter.Clone ({ source: windowTexture,
|
||||||
|
reactive: true,
|
||||||
|
width: width * scale,
|
||||||
|
height: height * scale });
|
||||||
|
|
||||||
|
bin.add_actor(clone);
|
||||||
|
box.add_actor(bin);
|
||||||
|
|
||||||
|
let title = windows[i].get_title();
|
||||||
|
if (title) {
|
||||||
|
let name = new St.Label({ text: title });
|
||||||
|
// St.Label doesn't support text-align so use a Bin
|
||||||
|
let bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||||
|
bin.add_actor(name);
|
||||||
|
box.add_actor(bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addItem(box);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
1318
js/ui/appDisplay.js
90
js/ui/appFavorites.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
|
||||||
|
function AppFavorites() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AppFavorites.prototype = {
|
||||||
|
FAVORITE_APPS_KEY: 'favorite_apps',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._favorites = {};
|
||||||
|
this._gconf = Shell.GConf.get_default();
|
||||||
|
this._gconf.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
|
||||||
|
this._reload();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFavsChanged: function() {
|
||||||
|
this._reload();
|
||||||
|
this.emit('changed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_reload: function() {
|
||||||
|
let ids = Shell.GConf.get_default().get_string_list('favorite_apps');
|
||||||
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
let apps = ids.map(function (id) {
|
||||||
|
return appSys.get_app(id);
|
||||||
|
}).filter(function (app) {
|
||||||
|
return app != null;
|
||||||
|
});
|
||||||
|
this._favorites = {};
|
||||||
|
for (let i = 0; i < apps.length; i++) {
|
||||||
|
let app = apps[i];
|
||||||
|
this._favorites[app.get_id()] = app;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getIds: function() {
|
||||||
|
let ret = [];
|
||||||
|
for (let id in this._favorites)
|
||||||
|
ret.push(id);
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
getFavoriteMap: function() {
|
||||||
|
return this._favorites;
|
||||||
|
},
|
||||||
|
|
||||||
|
getFavorites: function() {
|
||||||
|
let ret = [];
|
||||||
|
for (let id in this._favorites)
|
||||||
|
ret.push(this._favorites[id]);
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
isFavorite: function(appId) {
|
||||||
|
return appId in this._favorites;
|
||||||
|
},
|
||||||
|
|
||||||
|
addFavorite: function(appId) {
|
||||||
|
if (appId in this._favorites)
|
||||||
|
return;
|
||||||
|
let app = Shell.AppSystem.get_default().get_app(appId);
|
||||||
|
if (!app)
|
||||||
|
return;
|
||||||
|
let ids = this._getIds();
|
||||||
|
ids.push(appId);
|
||||||
|
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
|
||||||
|
this._favorites[appId] = app;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFavorite: function(appId) {
|
||||||
|
if (!appId in this._favorites)
|
||||||
|
return;
|
||||||
|
let ids = this._getIds().filter(function (id) { return id != appId; });
|
||||||
|
this._gconf.set_string_list(this.FAVORITE_APPS_KEY, ids);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(AppFavorites.prototype);
|
||||||
|
|
||||||
|
var appFavoritesInstance = null;
|
||||||
|
function getAppFavorites() {
|
||||||
|
if (appFavoritesInstance == null)
|
||||||
|
appFavoritesInstance = new AppFavorites();
|
||||||
|
return appFavoritesInstance;
|
||||||
|
}
|
191
js/ui/appIcon.js
@ -8,10 +8,12 @@ const Mainloop = imports.mainloop;
|
|||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
const _ = Gettext.gettext;
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
const GenericDisplay = imports.ui.genericDisplay;
|
const GenericDisplay = imports.ui.genericDisplay;
|
||||||
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Workspaces = imports.ui.workspaces;
|
const Workspaces = imports.ui.workspaces;
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ GLOW_COLOR.from_pixel(0x4f6ba4ff);
|
|||||||
const GLOW_PADDING_HORIZONTAL = 3;
|
const GLOW_PADDING_HORIZONTAL = 3;
|
||||||
const GLOW_PADDING_VERTICAL = 3;
|
const GLOW_PADDING_VERTICAL = 3;
|
||||||
|
|
||||||
const APPICON_ICON_SIZE = 48;
|
const APPICON_DEFAULT_ICON_SIZE = 48;
|
||||||
|
|
||||||
const APPICON_PADDING = 1;
|
const APPICON_PADDING = 1;
|
||||||
const APPICON_BORDER_WIDTH = 1;
|
const APPICON_BORDER_WIDTH = 1;
|
||||||
@ -49,14 +51,19 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
|
|||||||
|
|
||||||
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
|
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
|
||||||
|
|
||||||
function AppIcon(appInfo, menuType) {
|
function AppIcon(params) {
|
||||||
this._init(appInfo, menuType || MenuType.NONE);
|
this._init(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppIcon.prototype = {
|
AppIcon.prototype = {
|
||||||
_init : function(appInfo, menuType) {
|
_init : function(params) {
|
||||||
this.appInfo = appInfo;
|
this.app = params.app;
|
||||||
this._menuType = menuType;
|
if (!this.app)
|
||||||
|
throw new Error('AppIcon constructor requires "app" param');
|
||||||
|
|
||||||
|
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
|
||||||
|
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
|
||||||
|
this._showGlow = ('glow' in params) ? params.glow : false;
|
||||||
|
|
||||||
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
|
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
border: APPICON_BORDER_WIDTH,
|
border: APPICON_BORDER_WIDTH,
|
||||||
@ -64,30 +71,25 @@ AppIcon.prototype = {
|
|||||||
padding: APPICON_PADDING,
|
padding: APPICON_PADDING,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
|
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
|
||||||
|
|
||||||
if (menuType != MenuType.NONE) {
|
if (this._menuType != MenuType.NONE) {
|
||||||
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
|
|
||||||
for (let i = 0; i < this.windows.length; i++) {
|
|
||||||
this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
|
|
||||||
}
|
|
||||||
this._resortWindows();
|
|
||||||
|
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
|
||||||
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
|
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
|
||||||
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
|
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
|
||||||
|
|
||||||
this._menuTimeoutId = 0;
|
this._menuTimeoutId = 0;
|
||||||
this._menu = null;
|
this._menu = null;
|
||||||
} else
|
}
|
||||||
this.windows = [];
|
|
||||||
|
|
||||||
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
x_align: Big.BoxAlignment.CENTER,
|
x_align: Big.BoxAlignment.CENTER,
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
y_align: Big.BoxAlignment.CENTER,
|
||||||
width: APPICON_ICON_SIZE,
|
width: this._iconSize,
|
||||||
height: APPICON_ICON_SIZE });
|
height: this._iconSize });
|
||||||
this.icon = appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
this.icon = this.app.create_icon_texture(this._iconSize);
|
||||||
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
|
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
|
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
|
||||||
@ -98,22 +100,21 @@ AppIcon.prototype = {
|
|||||||
nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
|
nameBox.connect('allocate', Lang.bind(this, this._nameBoxAllocate));
|
||||||
this._nameBox = nameBox;
|
this._nameBox = nameBox;
|
||||||
|
|
||||||
this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
this._name = new St.Label({ style_class: "app-icon-label",
|
||||||
font_name: "Sans 12px",
|
text: this.app.get_name() });
|
||||||
line_alignment: Pango.Alignment.CENTER,
|
this._name.clutter_text.line_alignment = Pango.Alignment.CENTER;
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
|
||||||
text: appInfo.get_name() });
|
|
||||||
nameBox.add_actor(this._name);
|
nameBox.add_actor(this._name);
|
||||||
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
if (this._showGlow) {
|
||||||
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||||
for (let i = 0; i < this.windows.length && i < 3; i++) {
|
this._nameBox.add_actor(this._glowBox);
|
||||||
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
this._glowBox.lower(this._name);
|
||||||
glowPath, -1, -1);
|
this._appWindowChangedId = this.app.connect('windows-changed', Lang.bind(this, this._rerenderGlow));
|
||||||
glow.keep_aspect_ratio = false;
|
this._rerenderGlow();
|
||||||
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
} else {
|
||||||
|
this._glowBox = null;
|
||||||
|
this._appWindowChangedId = 0;
|
||||||
}
|
}
|
||||||
this._nameBox.add_actor(this._glowBox);
|
|
||||||
this._glowBox.lower(this._name);
|
|
||||||
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
|
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -158,25 +159,30 @@ AppIcon.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_resortWindows: function() {
|
_onDestroy: function() {
|
||||||
this.windows.sort(function (a, b) {
|
if (this._appWindowChangedId > 0)
|
||||||
let visA = a.showing_on_its_workspace();
|
this.app.disconnect(this._appWindowChangedId);
|
||||||
let visB = b.showing_on_its_workspace();
|
},
|
||||||
|
|
||||||
if (visA && !visB)
|
_rerenderGlow: function() {
|
||||||
return -1;
|
if (!this._showGlow)
|
||||||
else if (visB && !visA)
|
return;
|
||||||
return 1;
|
this._glowBox.get_children().forEach(function (a) { a.destroy(); });
|
||||||
else
|
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
|
||||||
return b.get_user_time() - a.get_user_time();
|
let windows = this.app.get_windows();
|
||||||
});
|
for (let i = 0; i < windows.length && i < 3; i++) {
|
||||||
|
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
||||||
|
glowPath, -1, -1);
|
||||||
|
glow.keep_aspect_ratio = false;
|
||||||
|
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// AppIcon itself is not a draggable, but if you want to make
|
// AppIcon itself is not a draggable, but if you want to make
|
||||||
// a subclass of it draggable, you can use this method to create
|
// a subclass of it draggable, you can use this method to create
|
||||||
// a drag actor
|
// a drag actor
|
||||||
createDragActor: function() {
|
createDragActor: function() {
|
||||||
return this.appInfo.create_icon_texture(APPICON_ICON_SIZE);
|
return this.app.create_icon_texture(this._iconSize);
|
||||||
},
|
},
|
||||||
|
|
||||||
setHighlight: function(highlight) {
|
setHighlight: function(highlight) {
|
||||||
@ -205,14 +211,19 @@ AppIcon.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_updateMenuOnButtonPress: function(actor, event) {
|
_updateMenuOnButtonPress: function(actor, event) {
|
||||||
if (this._menuTimeoutId != 0)
|
let button = event.get_button();
|
||||||
Mainloop.source_remove(this._menuTimeoutId);
|
if (button == 1) {
|
||||||
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
if (this._menuTimeoutId != 0)
|
||||||
Lang.bind(this, this.popupMenu));
|
Mainloop.source_remove(this._menuTimeoutId);
|
||||||
|
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
|
||||||
|
Lang.bind(this, function () { this.popupMenu(button); }));
|
||||||
|
} else if (button == 3) {
|
||||||
|
this.popupMenu(button);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
popupMenu: function() {
|
popupMenu: function(activatingButton) {
|
||||||
if (this._menuTimeoutId != 0) {
|
if (this._menuTimeoutId != 0) {
|
||||||
Mainloop.source_remove(this._menuTimeoutId);
|
Mainloop.source_remove(this._menuTimeoutId);
|
||||||
this._menuTimeoutId = 0;
|
this._menuTimeoutId = 0;
|
||||||
@ -236,7 +247,7 @@ AppIcon.prototype = {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._menu.popup();
|
this._menu.popup(activatingButton);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -296,10 +307,10 @@ AppIconMenu.prototype = {
|
|||||||
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
|
this._windowContainer.connect('leave-event', Lang.bind(this, this._onMenuLeave));
|
||||||
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
|
this._windowContainer.connect('button-release-event', Lang.bind(this, this._onMenuButtonRelease));
|
||||||
|
|
||||||
this._arrow = new Shell.DrawingArea();
|
this._arrow = new St.DrawingArea();
|
||||||
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
|
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
|
||||||
Shell.draw_box_pointer(texture,
|
Shell.draw_box_pointer(texture,
|
||||||
this._type == MenuType.ON_RIGHT ? Clutter.Gravity.WEST : Clutter.Gravity.NORTH,
|
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
|
||||||
source.highlight_border_color,
|
source.highlight_border_color,
|
||||||
APPICON_MENU_BACKGROUND_COLOR);
|
APPICON_MENU_BACKGROUND_COLOR);
|
||||||
}));
|
}));
|
||||||
@ -371,38 +382,52 @@ AppIconMenu.prototype = {
|
|||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
this._windowContainer.remove_all();
|
this._windowContainer.remove_all();
|
||||||
|
|
||||||
let windows = this._source.windows;
|
let windows = this._source.app.get_windows();
|
||||||
|
|
||||||
this._windowContainer.show();
|
this._windowContainer.show();
|
||||||
|
|
||||||
let iconsDiffer = false;
|
let iconsDiffer = false;
|
||||||
let texCache = Shell.TextureCache.get_default();
|
let texCache = Shell.TextureCache.get_default();
|
||||||
let firstIcon = windows[0].mini_icon;
|
if (windows.length > 0) {
|
||||||
for (let i = 1; i < windows.length; i++) {
|
let firstIcon = windows[0].mini_icon;
|
||||||
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
for (let i = 1; i < windows.length; i++) {
|
||||||
iconsDiffer = true;
|
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
|
||||||
break;
|
iconsDiffer = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 activeWorkspace = global.screen.get_active_workspace();
|
||||||
|
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
|
||||||
|
|
||||||
let currentWorkspaceWindows = windows.filter(function (w) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
return w.get_workspace() == activeWorkspace;
|
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
|
||||||
});
|
this._appendSeparator();
|
||||||
let otherWorkspaceWindows = windows.filter(function (w) {
|
separatorShown = true;
|
||||||
return w.get_workspace() != activeWorkspace;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
this._appendWindows(currentWorkspaceWindows, iconsDiffer);
|
let icon = null;
|
||||||
if (currentWorkspaceWindows.length > 0 && otherWorkspaceWindows.length > 0) {
|
if (iconsDiffer)
|
||||||
this._appendSeparator();
|
icon = Shell.TextureCache.get_default().bind_pixbuf_property(windows[i], "mini-icon");
|
||||||
|
|
||||||
|
let box = this._appendMenuItem(icon, windows[i].title);
|
||||||
|
box._window = windows[i];
|
||||||
}
|
}
|
||||||
this._appendWindows(otherWorkspaceWindows, iconsDiffer);
|
|
||||||
|
|
||||||
this._appendSeparator();
|
if (windows.length > 0)
|
||||||
|
this._appendSeparator();
|
||||||
|
|
||||||
this._newWindowMenuItem = this._appendMenuItem(null, _("New Window"));
|
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
|
||||||
|
|
||||||
|
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
|
||||||
|
|
||||||
|
if (windows.length > 0)
|
||||||
|
this._appendSeparator();
|
||||||
|
this._toggleFavoriteMenuItem = this._appendMenuItem(null, isFavorite ? _("Remove from Favorites")
|
||||||
|
: _("Add to Favorites"));
|
||||||
|
|
||||||
this._highlightedItem = null;
|
this._highlightedItem = null;
|
||||||
},
|
},
|
||||||
@ -441,26 +466,13 @@ AppIconMenu.prototype = {
|
|||||||
return box;
|
return box;
|
||||||
},
|
},
|
||||||
|
|
||||||
_appendWindows: function (windows, iconsDiffer) {
|
popup: function(activatingButton) {
|
||||||
for (let i = 0; i < windows.length; i++) {
|
|
||||||
let metaWindow = windows[i];
|
|
||||||
|
|
||||||
let icon = null;
|
|
||||||
if (iconsDiffer) {
|
|
||||||
icon = Shell.TextureCache.get_default().bind_pixbuf_property(metaWindow, "mini-icon");
|
|
||||||
}
|
|
||||||
let box = this._appendMenuItem(icon, metaWindow.title);
|
|
||||||
box._window = metaWindow;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
popup: function() {
|
|
||||||
let [stageX, stageY] = this._source.actor.get_transformed_position();
|
let [stageX, stageY] = this._source.actor.get_transformed_position();
|
||||||
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
|
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
|
||||||
|
|
||||||
this._redisplay();
|
this._redisplay();
|
||||||
|
|
||||||
this._windowContainer.popup(0, Main.currentTime());
|
this._windowContainer.popup(activatingButton, global.get_current_time());
|
||||||
|
|
||||||
this.emit('popup', true);
|
this.emit('popup', true);
|
||||||
|
|
||||||
@ -557,8 +569,15 @@ AppIconMenu.prototype = {
|
|||||||
let metaWindow = child._window;
|
let metaWindow = child._window;
|
||||||
this.emit('activate-window', metaWindow);
|
this.emit('activate-window', metaWindow);
|
||||||
} else if (child == this._newWindowMenuItem) {
|
} else if (child == this._newWindowMenuItem) {
|
||||||
this._source.appInfo.launch();
|
this._source.app.launch();
|
||||||
this.emit('activate-window', null);
|
this.emit('activate-window', null);
|
||||||
|
} 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.popdown();
|
this.popdown();
|
||||||
},
|
},
|
||||||
|
172
js/ui/button.js
@ -1,172 +0,0 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
const Big = imports.gi.Big;
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
const Tweener = imports.ui.tweener;
|
|
||||||
|
|
||||||
const DEFAULT_BUTTON_COLOR = new Clutter.Color();
|
|
||||||
DEFAULT_BUTTON_COLOR.from_pixel(0xeeddcc66);
|
|
||||||
|
|
||||||
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
|
|
||||||
DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaa66);
|
|
||||||
|
|
||||||
const DEFAULT_TEXT_COLOR = new Clutter.Color();
|
|
||||||
DEFAULT_TEXT_COLOR.from_pixel(0x000000ff);
|
|
||||||
|
|
||||||
const DEFAULT_FONT = 'Sans Bold 16px';
|
|
||||||
|
|
||||||
// Padding on the left and right side of the button.
|
|
||||||
const SIDE_PADDING = 14;
|
|
||||||
|
|
||||||
function Button(widget, buttonColor, pressedButtonColor, textColor, font) {
|
|
||||||
this._init(widget, buttonColor, pressedButtonColor, textColor, font);
|
|
||||||
}
|
|
||||||
|
|
||||||
Button.prototype = {
|
|
||||||
_init : function(widgetOrText, buttonColor, pressedButtonColor, textColor, font) {
|
|
||||||
this._buttonColor = buttonColor
|
|
||||||
if (buttonColor == null)
|
|
||||||
this._buttonColor = DEFAULT_BUTTON_COLOR;
|
|
||||||
|
|
||||||
this._pressedButtonColor = pressedButtonColor
|
|
||||||
if (pressedButtonColor == null)
|
|
||||||
this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
|
|
||||||
|
|
||||||
this._textColor = textColor;
|
|
||||||
if (textColor == null)
|
|
||||||
this._textColor = DEFAULT_TEXT_COLOR;
|
|
||||||
|
|
||||||
this._font = font;
|
|
||||||
if (font == null)
|
|
||||||
this._font = DEFAULT_FONT;
|
|
||||||
|
|
||||||
this._isBetweenPressAndRelease = false;
|
|
||||||
this._mouseIsOverButton = false;
|
|
||||||
|
|
||||||
this.actor = new Shell.ButtonBox({ reactive: true,
|
|
||||||
corner_radius: 5,
|
|
||||||
padding_left: SIDE_PADDING,
|
|
||||||
padding_right: SIDE_PADDING,
|
|
||||||
orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
y_align: Big.BoxAlignment.CENTER
|
|
||||||
});
|
|
||||||
if (typeof widgetOrText == 'string') {
|
|
||||||
this._widget = new Clutter.Text({ font_name: this._font,
|
|
||||||
color: this._textColor,
|
|
||||||
text: widgetOrText });
|
|
||||||
} else {
|
|
||||||
this._widget = widgetOrText;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actor.append(this._widget, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
this.actor.connect('notify::hover', Lang.bind(this, this._updateColors));
|
|
||||||
this.actor.connect('notify::pressed', Lang.bind(this, this._updateColors));
|
|
||||||
this.actor.connect('notify::active', Lang.bind(this, this._updateColors));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateColors : function() {
|
|
||||||
if (this.actor.active || this.actor.pressed)
|
|
||||||
this.actor.backgroundColor = this._pressedButtonColor;
|
|
||||||
else if (this.actor.hover)
|
|
||||||
this.actor.backgroundColor = this._buttonColor;
|
|
||||||
else
|
|
||||||
this.actor.backgroundColor = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Signals.addSignalMethods(Button.prototype);
|
|
||||||
|
|
||||||
/* Delay before the icon should appear, in seconds after the pointer has entered the parent */
|
|
||||||
const ANIMATION_TIME = 0.25;
|
|
||||||
|
|
||||||
/* This is an icon button that fades in/out when mouse enters/leaves the parent.
|
|
||||||
* A delay is used before the fading starts. You can force it to be shown if needed.
|
|
||||||
*
|
|
||||||
* parent -- used to show/hide the button depending on mouse entering/leaving it
|
|
||||||
* size -- size in pixels of both the button and the icon it contains
|
|
||||||
* texture -- optional, must be used if the texture for the icon is already created (else, use setIconFromName)
|
|
||||||
*/
|
|
||||||
function IconButton(parent, size, texture) {
|
|
||||||
this._init(parent, size, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
IconButton.prototype = {
|
|
||||||
_init : function(parent, size, texture) {
|
|
||||||
this._size = size;
|
|
||||||
if (texture)
|
|
||||||
this.actor = texture;
|
|
||||||
else
|
|
||||||
this.actor = new Clutter.Texture({ width: this._size, height: this._size });
|
|
||||||
this.actor.set_reactive(true);
|
|
||||||
this.actor.set_opacity(0);
|
|
||||||
parent.connect("enter-event", Lang.bind(this, function(actor, event) {
|
|
||||||
this._shouldHide = false;
|
|
||||||
// Nothing to do if the cursor has come back from a child of the parent actor
|
|
||||||
if (actor.get_children().indexOf(event.get_related()) != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._fadeIn();
|
|
||||||
}));
|
|
||||||
parent.connect("leave-event", Lang.bind(this, function(actor, event) {
|
|
||||||
// Nothing to do if the cursor has merely entered a child of the parent actor
|
|
||||||
if (actor.get_children().indexOf(event.get_related()) != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Remember that we should not be visible to hide the button if forceShow is unset
|
|
||||||
if (this._forceShow) {
|
|
||||||
this._shouldHide = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._fadeOut();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Private methods ///
|
|
||||||
|
|
||||||
setIconFromName : function(iconName) {
|
|
||||||
let iconTheme = Gtk.IconTheme.get_default();
|
|
||||||
let iconInfo = iconTheme.lookup_icon(iconName, this._size, 0);
|
|
||||||
if (!iconInfo)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let iconPath = iconInfo.get_filename();
|
|
||||||
this.actor.set_from_file(iconPath);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Useful if we want to show the button immediately,
|
|
||||||
// e.g. in case the mouse is already in the parent when the button is created
|
|
||||||
show : function() {
|
|
||||||
this.actor.set_opacity(255);
|
|
||||||
},
|
|
||||||
|
|
||||||
// If show is true, prevents the button from fading out
|
|
||||||
forceShow : function(show) {
|
|
||||||
this._forceShow = show;
|
|
||||||
// Hide the button if it should have been hidden under normal conditions
|
|
||||||
if (!this._forceShow && this._shouldHide) {
|
|
||||||
this._fadeOut();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Private methods ///
|
|
||||||
|
|
||||||
_fadeIn : function() {
|
|
||||||
Tweener.removeTweens(this.actor);
|
|
||||||
Tweener.addTween(this.actor, { opacity: 255,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition :"easeInQuad" });
|
|
||||||
},
|
|
||||||
|
|
||||||
_fadeOut : function() {
|
|
||||||
Tweener.removeTweens(this.actor);
|
|
||||||
Tweener.addTween(this.actor, { opacity: 0,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition :"easeOutQuad" });
|
|
||||||
}
|
|
||||||
};
|
|
177
js/ui/calendar.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||||
|
|
||||||
|
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
function _sameDay(dateA, dateB) {
|
||||||
|
return (dateA.getDate() == dateB.getDate() &&
|
||||||
|
dateA.getMonth() == dateB.getMonth() &&
|
||||||
|
dateA.getYear() == dateB.getYear());
|
||||||
|
}
|
||||||
|
|
||||||
|
function Calendar() {
|
||||||
|
this._init();
|
||||||
|
};
|
||||||
|
|
||||||
|
Calendar.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
// FIXME: This is actually the fallback method for GTK+ for the week start;
|
||||||
|
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
|
||||||
|
// should add a C function so we can do the full handling.
|
||||||
|
this._weekStart = NaN;
|
||||||
|
let weekStartString = Gettext_gtk20.gettext("calendar:week_start:0");
|
||||||
|
if (weekStartString.indexOf("calendar:week_start:") == 0) {
|
||||||
|
this._weekStart = parseInt(weekStartString.substring(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
|
||||||
|
log("Translation of 'calendar:week_start:0' in GTK+ is not correct");
|
||||||
|
this.weekStart = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the ordering for month/year in the calendar heading
|
||||||
|
switch (Gettext_gtk20.gettext("calendar:MY")) {
|
||||||
|
case "calendar:MY":
|
||||||
|
this._headerFormat = "%B %Y";
|
||||||
|
break;
|
||||||
|
case "calendar:YM":
|
||||||
|
this._headerFormat = "%Y %B";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log("Translation of 'calendar:MY' in GTK+ is not correct");
|
||||||
|
this._headerFormat = "%B %Y";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start off with the current date
|
||||||
|
this.date = new Date();
|
||||||
|
|
||||||
|
this.actor = new St.Table({ homogeneous: false,
|
||||||
|
style_class: "calendar",
|
||||||
|
reactive: true });
|
||||||
|
|
||||||
|
this.actor.connect('scroll-event',
|
||||||
|
Lang.bind(this, this._onScroll));
|
||||||
|
|
||||||
|
// Top line of the calendar '<| September 2009 |>'
|
||||||
|
this._topBox = new St.BoxLayout();
|
||||||
|
this.actor.add(this._topBox,
|
||||||
|
{ row: 0, col: 0, col_span: 7 });
|
||||||
|
|
||||||
|
let back = new St.Button({ label: "<", style_class: 'calendar-change-month' });
|
||||||
|
this._topBox.add(back);
|
||||||
|
back.connect("clicked", Lang.bind(this, this._prevMonth));
|
||||||
|
|
||||||
|
this._dateLabel = new St.Label();
|
||||||
|
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let forward = new St.Button({ label: ">", style_class: 'calendar-change-month' });
|
||||||
|
this._topBox.add(forward);
|
||||||
|
forward.connect("clicked", Lang.bind(this, this._nextMonth));
|
||||||
|
|
||||||
|
// We need to figure out the abbreviated localized names for the days of the week;
|
||||||
|
// we do this by just getting the next 7 days starting from right now and then putting
|
||||||
|
// them in the right cell in the table. It doesn't matter if we add them in order
|
||||||
|
let iter = new Date(this.date);
|
||||||
|
iter.setSeconds(0); // Leap second protection. Hah!
|
||||||
|
iter.setHours(12);
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
this.actor.add(new St.Label({ text: iter.toLocaleFormat("%a") }),
|
||||||
|
{ row: 1,
|
||||||
|
col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||||
|
x_fill: false, x_align: 1.0 });
|
||||||
|
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All the children after this are days, and get removed when we update the calendar
|
||||||
|
this._firstDayIndex = this.actor.get_children().length;
|
||||||
|
|
||||||
|
this._update();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sets the calendar to show a specific date
|
||||||
|
setDate: function(date) {
|
||||||
|
if (!_sameDay(date, this.date)) {
|
||||||
|
this.date = date;
|
||||||
|
this._update();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onScroll : function(actor, event) {
|
||||||
|
switch (event.get_scroll_direction()) {
|
||||||
|
case Clutter.ScrollDirection.UP:
|
||||||
|
case Clutter.ScrollDirection.LEFT:
|
||||||
|
this._prevMonth();
|
||||||
|
break;
|
||||||
|
case Clutter.ScrollDirection.DOWN:
|
||||||
|
case Clutter.ScrollDirection.RIGHT:
|
||||||
|
this._nextMonth();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_prevMonth: function() {
|
||||||
|
if (this.date.getMonth() == 0) {
|
||||||
|
this.date.setMonth(11);
|
||||||
|
this.date.setFullYear(this.date.getFullYear() - 1);
|
||||||
|
} else {
|
||||||
|
this.date.setMonth(this.date.getMonth() - 1);
|
||||||
|
}
|
||||||
|
this._update();
|
||||||
|
},
|
||||||
|
|
||||||
|
_nextMonth: function() {
|
||||||
|
if (this.date.getMonth() == 11) {
|
||||||
|
this.date.setMonth(0);
|
||||||
|
this.date.setFullYear(this.date.getFullYear() + 1);
|
||||||
|
} else {
|
||||||
|
this.date.setMonth(this.date.getMonth() + 1);
|
||||||
|
}
|
||||||
|
this._update();
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function() {
|
||||||
|
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
|
||||||
|
|
||||||
|
// Remove everything but the topBox and the weekday labels
|
||||||
|
let children = this.actor.get_children();
|
||||||
|
for (let i = this._firstDayIndex; i < children.length; i++)
|
||||||
|
children[i].destroy();
|
||||||
|
|
||||||
|
// Start at the beginning of the week before the start of the month
|
||||||
|
let iter = new Date(this.date);
|
||||||
|
iter.setDate(1);
|
||||||
|
iter.setSeconds(0);
|
||||||
|
iter.setHours(12);
|
||||||
|
iter.setTime(iter.getTime() - (iter.getDay() - this._weekStart) * MSECS_IN_DAY);
|
||||||
|
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
let row = 2;
|
||||||
|
while (true) {
|
||||||
|
let label = new St.Label({ text: iter.getDate().toString() });
|
||||||
|
if (_sameDay(now, iter))
|
||||||
|
label.style_class = "calendar-day calendar-today";
|
||||||
|
else if (iter.getMonth() != this.date.getMonth())
|
||||||
|
label.style_class = "calendar-day calendar-other-month-day";
|
||||||
|
else
|
||||||
|
label.style_class = "calendar-day";
|
||||||
|
this.actor.add(label,
|
||||||
|
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7,
|
||||||
|
x_fill: false, x_align: 1.0 });
|
||||||
|
|
||||||
|
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||||
|
if (iter.getDay() == this._weekStart) {
|
||||||
|
// We stop on the first "first day of the week" after the month we are displaying
|
||||||
|
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
|
||||||
|
break;
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
153
js/ui/chrome.js
@ -7,6 +7,7 @@ const Meta = imports.gi.Meta;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
// This manages the shell "chrome"; the UI that's visible in the
|
// This manages the shell "chrome"; the UI that's visible in the
|
||||||
// normal mode (ie, outside the Overview), that surrounds the main
|
// normal mode (ie, outside the Overview), that surrounds the main
|
||||||
@ -54,7 +55,7 @@ Chrome.prototype = {
|
|||||||
|
|
||||||
// addActor:
|
// addActor:
|
||||||
// @actor: an actor to add to the chrome layer
|
// @actor: an actor to add to the chrome layer
|
||||||
// @shapeActor: optional "shape actor".
|
// @params: (optional) additional params
|
||||||
//
|
//
|
||||||
// Adds @actor to the chrome layer and extends the input region
|
// Adds @actor to the chrome layer and extends the input region
|
||||||
// and window manager struts to include it. (Window manager struts
|
// and window manager struts to include it. (Window manager struts
|
||||||
@ -64,59 +65,45 @@ Chrome.prototype = {
|
|||||||
// in its visibility will affect the input region, but NOT the
|
// in its visibility will affect the input region, but NOT the
|
||||||
// struts.
|
// struts.
|
||||||
//
|
//
|
||||||
// If @shapeActor is provided, it will be used instead of @actor
|
// If %visibleInOverview is %true in @params, @actor will remain
|
||||||
// for the input region/strut shape. (This lets you have things like
|
// visible when the overview is brought up. Otherwise it will
|
||||||
// drop shadows in @actor that don't affect the struts.) It must
|
// automatically be hidden. If %affectsStruts or %affectsInputRegion
|
||||||
// be a child of @actor. Alternatively, you can pass %null for
|
// is %false, the actor will not have the indicated effect.
|
||||||
// @shapeActor to indicate that @actor should not affect the input
|
addActor: function(actor, params) {
|
||||||
// region or struts at all.
|
params = Params.parse(params, { visibleInOverview: false,
|
||||||
addActor: function(actor, shapeActor) {
|
affectsStruts: true,
|
||||||
if (shapeActor === undefined)
|
affectsInputRegion: true });
|
||||||
shapeActor = actor;
|
|
||||||
else if (shapeActor && !this._verifyAncestry(shapeActor, actor))
|
|
||||||
throw new Error('shapeActor is not a descendent of actor');
|
|
||||||
|
|
||||||
this.nonOverviewActor.add_actor(actor);
|
if (params.visibleInOverview)
|
||||||
|
this.actor.add_actor(actor);
|
||||||
if (shapeActor)
|
|
||||||
this._trackActor(shapeActor, true, true);
|
|
||||||
},
|
|
||||||
|
|
||||||
// setVisibleInOverview:
|
|
||||||
// @actor: an actor in the chrome layer
|
|
||||||
// @visible: Overview visibility
|
|
||||||
//
|
|
||||||
// By default, actors in the chrome layer are automatically hidden
|
|
||||||
// when the Overview is shown. This can be used to override that
|
|
||||||
// behavior
|
|
||||||
setVisibleInOverview: function(actor, visible) {
|
|
||||||
if (!this._verifyAncestry(actor, this.actor))
|
|
||||||
throw new Error('actor is not a descendent of the chrome layer');
|
|
||||||
|
|
||||||
if (visible)
|
|
||||||
actor.reparent(this.actor);
|
|
||||||
else
|
else
|
||||||
actor.reparent(this.nonOverviewActor);
|
this.nonOverviewActor.add_actor(actor);
|
||||||
|
|
||||||
|
this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
|
||||||
},
|
},
|
||||||
|
|
||||||
// addInputRegionActor:
|
// trackActor:
|
||||||
// @actor: an actor to add to the stage input region
|
// @actor: a descendant of the chrome to begin tracking
|
||||||
|
// @params: parameters describing how to track @actor
|
||||||
//
|
//
|
||||||
// Adds @actor to the stage input region, as with addActor(), but
|
// Tells the chrome to track @actor, which must be a descendant
|
||||||
// for actors that are already descendants of the chrome layer.
|
// of an actor added via addActor(). This can be used to extend the
|
||||||
addInputRegionActor: function(actor) {
|
// struts or input region to cover specific children.
|
||||||
|
trackActor: function(actor, params) {
|
||||||
if (!this._verifyAncestry(actor, this.actor))
|
if (!this._verifyAncestry(actor, this.actor))
|
||||||
throw new Error('actor is not a descendent of the chrome layer');
|
throw new Error('actor is not a descendent of the chrome layer');
|
||||||
|
|
||||||
this._trackActor(actor, true, false);
|
params = Params.parse(params, { affectsStruts: true,
|
||||||
|
affectsInputRegion: true });
|
||||||
|
this._trackActor(actor, params.affectsInputRegion, params.affectsStruts);
|
||||||
},
|
},
|
||||||
|
|
||||||
// removeInputRegionActor:
|
// untrackActor:
|
||||||
// @actor: an actor previously added to the stage input region
|
// @actor: an actor previously tracked via trackActor()
|
||||||
//
|
//
|
||||||
// Undoes the effect of addInputRegionActor()
|
// Undoes the effect of trackActor()
|
||||||
removeInputRegionActor: function(actor) {
|
untrackActor: function(actor) {
|
||||||
this._untrackActor(actor, true, false);
|
this._untrackActor(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
// removeActor:
|
// removeActor:
|
||||||
@ -128,7 +115,7 @@ Chrome.prototype = {
|
|||||||
this.nonOverviewActor.remove_actor(actor);
|
this.nonOverviewActor.remove_actor(actor);
|
||||||
else
|
else
|
||||||
this.actor.remove_actor(actor);
|
this.actor.remove_actor(actor);
|
||||||
this._untrackActor(actor, true, true);
|
this._untrackActor(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_findActor: function(actor) {
|
_findActor: function(actor) {
|
||||||
@ -142,23 +129,13 @@ Chrome.prototype = {
|
|||||||
|
|
||||||
_trackActor: function(actor, inputRegion, strut) {
|
_trackActor: function(actor, inputRegion, strut) {
|
||||||
let actorData;
|
let actorData;
|
||||||
let i = this._findActor(actor);
|
|
||||||
|
|
||||||
if (i != -1) {
|
if (this._findActor(actor) != -1)
|
||||||
actorData = this._trackedActors[i];
|
throw new Error('trying to re-track existing chrome actor');
|
||||||
if (inputRegion)
|
|
||||||
actorData.inputRegion++;
|
|
||||||
if (strut)
|
|
||||||
actorData.strut++;
|
|
||||||
if (!inputRegion && !strut)
|
|
||||||
actorData.children++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
actorData = { actor: actor,
|
actorData = { actor: actor,
|
||||||
inputRegion: inputRegion ? 1 : 0,
|
inputRegion: inputRegion,
|
||||||
strut: strut ? 1 : 0,
|
strut: strut };
|
||||||
children: 0 };
|
|
||||||
|
|
||||||
actorData.visibleId = actor.connect('notify::visible',
|
actorData.visibleId = actor.connect('notify::visible',
|
||||||
Lang.bind(this, this._queueUpdateRegions));
|
Lang.bind(this, this._queueUpdateRegions));
|
||||||
@ -166,54 +143,31 @@ Chrome.prototype = {
|
|||||||
Lang.bind(this, this._queueUpdateRegions));
|
Lang.bind(this, this._queueUpdateRegions));
|
||||||
actorData.parentSetId = actor.connect('parent-set',
|
actorData.parentSetId = actor.connect('parent-set',
|
||||||
Lang.bind(this, this._actorReparented));
|
Lang.bind(this, this._actorReparented));
|
||||||
|
// Note that destroying actor will unset its parent, so we don't
|
||||||
|
// need to connect to 'destroy' too.
|
||||||
|
|
||||||
this._trackedActors.push(actorData);
|
this._trackedActors.push(actorData);
|
||||||
|
this._queueUpdateRegions();
|
||||||
actor = actor.get_parent();
|
|
||||||
if (actor != this.actor && actor != this.nonOverviewActor)
|
|
||||||
this._trackActor(actor, false, false);
|
|
||||||
|
|
||||||
if (inputRegion || strut)
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_untrackActor: function(actor, inputRegion, strut) {
|
_untrackActor: function(actor) {
|
||||||
let i = this._findActor(actor);
|
let i = this._findActor(actor);
|
||||||
|
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
return;
|
return;
|
||||||
let actorData = this._trackedActors[i];
|
let actorData = this._trackedActors[i];
|
||||||
|
|
||||||
if (inputRegion)
|
this._trackedActors.splice(i, 1);
|
||||||
actorData.inputRegion--;
|
actor.disconnect(actorData.visibleId);
|
||||||
if (strut)
|
actor.disconnect(actorData.allocationId);
|
||||||
actorData.strut--;
|
actor.disconnect(actorData.parentSetId);
|
||||||
if (!inputRegion && !strut)
|
|
||||||
actorData.children--;
|
|
||||||
|
|
||||||
if (actorData.inputRegion <= 0 && actorData.strut <= 0 && actorData.children <= 0) {
|
this._queueUpdateRegions();
|
||||||
this._trackedActors.splice(i, 1);
|
|
||||||
actor.disconnect(actorData.visibleId);
|
|
||||||
actor.disconnect(actorData.allocationId);
|
|
||||||
actor.disconnect(actorData.parentSetId);
|
|
||||||
|
|
||||||
actor = actor.get_parent();
|
|
||||||
if (actor && actor != this.actor && actor != this.nonOverviewActor)
|
|
||||||
this._untrackActor(actor, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputRegion || strut)
|
|
||||||
this._queueUpdateRegions();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_actorReparented: function(actor, oldParent) {
|
_actorReparented: function(actor, oldParent) {
|
||||||
if (this._verifyAncestry(actor, this.actor)) {
|
if (!this._verifyAncestry(actor, this.actor))
|
||||||
let newParent = actor.get_parent();
|
this._untrackActor(actor);
|
||||||
if (newParent != this.actor && newParent != this.nonOverviewActor)
|
|
||||||
this._trackActor(newParent, false, false);
|
|
||||||
}
|
|
||||||
if (oldParent != this.actor && oldParent != this.nonOverviewActor)
|
|
||||||
this._untrackActor(oldParent, false, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_overviewShowing: function() {
|
_overviewShowing: function() {
|
||||||
@ -237,6 +191,7 @@ Chrome.prototype = {
|
|||||||
|
|
||||||
_windowsRestacked: function() {
|
_windowsRestacked: function() {
|
||||||
let windows = global.get_windows();
|
let windows = global.get_windows();
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
|
|
||||||
// The chrome layer should be visible unless there is a window
|
// The chrome layer should be visible unless there is a window
|
||||||
// with layer FULLSCREEN, or a window with layer
|
// with layer FULLSCREEN, or a window with layer
|
||||||
@ -254,17 +209,15 @@ Chrome.prototype = {
|
|||||||
for (let i = windows.length - 1; i > -1; i--) {
|
for (let i = windows.length - 1; i > -1; i--) {
|
||||||
let layer = windows[i].get_meta_window().get_layer();
|
let layer = windows[i].get_meta_window().get_layer();
|
||||||
|
|
||||||
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
|
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT ||
|
||||||
if (windows[i].x <= 0 &&
|
layer == Meta.StackLayer.FULLSCREEN) {
|
||||||
windows[i].x + windows[i].width >= global.screen_width &&
|
if (windows[i].x <= primary.x &&
|
||||||
windows[i].y <= 0 &&
|
windows[i].x + windows[i].width >= primary.x + primary.width &&
|
||||||
windows[i].y + windows[i].height >= global.screen_height) {
|
windows[i].y <= primary.y &&
|
||||||
|
windows[i].y + windows[i].height >= primary.y + primary.height) {
|
||||||
this._obscuredByFullscreen = true;
|
this._obscuredByFullscreen = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (layer == Meta.StackLayer.FULLSCREEN) {
|
|
||||||
this._obscuredByFullscreen = true;
|
|
||||||
break;
|
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
845
js/ui/dash.js
@ -8,13 +8,18 @@ const Lang = imports.lang;
|
|||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
const DocInfo = imports.misc.docInfo;
|
const DocInfo = imports.misc.docInfo;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const GenericDisplay = imports.ui.genericDisplay;
|
const GenericDisplay = imports.ui.genericDisplay;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Search = imports.ui.search;
|
||||||
|
|
||||||
|
const MAX_DASH_DOCS = 50;
|
||||||
const DASH_DOCS_ICON_SIZE = 16;
|
const DASH_DOCS_ICON_SIZE = 16;
|
||||||
|
|
||||||
const DEFAULT_SPACING = 4;
|
const DEFAULT_SPACING = 4;
|
||||||
@ -111,19 +116,19 @@ DocDisplayItem.prototype = {
|
|||||||
/* This class represents a display containing a collection of document items.
|
/* This class represents a display containing a collection of document items.
|
||||||
* The documents are sorted by how recently they were last visited.
|
* The documents are sorted by how recently they were last visited.
|
||||||
*/
|
*/
|
||||||
function DocDisplay() {
|
function DocDisplay(flags) {
|
||||||
this._init();
|
this._init(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
DocDisplay.prototype = {
|
DocDisplay.prototype = {
|
||||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||||
|
|
||||||
_init : function() {
|
_init : function(flags) {
|
||||||
GenericDisplay.GenericDisplay.prototype._init.call(this);
|
GenericDisplay.GenericDisplay.prototype._init.call(this, flags);
|
||||||
// We keep a single timeout callback for updating last visited times
|
// We keep a single timeout callback for updating last visited times
|
||||||
// for all the items in the display. This avoids creating individual
|
// for all the items in the display. This avoids creating individual
|
||||||
// callbacks for each item in the display. So proper time updates
|
// callbacks for each item in the display. So proper time updates
|
||||||
// for individual items and item details depend on the item being
|
// for individual items and item details depend on the item being
|
||||||
// associated with one of the displays.
|
// associated with one of the displays.
|
||||||
this._updateTimeoutTargetTime = -1;
|
this._updateTimeoutTargetTime = -1;
|
||||||
this._updateTimeoutId = 0;
|
this._updateTimeoutId = 0;
|
||||||
@ -136,7 +141,7 @@ DocDisplay.prototype = {
|
|||||||
// but redisplaying right away is cool when we use Zephyr.
|
// but redisplaying right away is cool when we use Zephyr.
|
||||||
// Also, we might be displaying remote documents, like Google Docs, in the future
|
// Also, we might be displaying remote documents, like Google Docs, in the future
|
||||||
// which might be edited by someone else.
|
// which might be edited by someone else.
|
||||||
this._redisplay(false);
|
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.connect('destroy', Lang.bind(this, function (o) {
|
this.connect('destroy', Lang.bind(this, function (o) {
|
||||||
@ -151,7 +156,8 @@ DocDisplay.prototype = {
|
|||||||
_refreshCache : function() {
|
_refreshCache : function() {
|
||||||
if (!this._docsStale)
|
if (!this._docsStale)
|
||||||
return true;
|
return true;
|
||||||
this._allItems = this._docManager.getItems();
|
this._allItems = {};
|
||||||
|
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
||||||
this._docsStale = false;
|
this._docsStale = false;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -176,13 +182,8 @@ DocDisplay.prototype = {
|
|||||||
this._matchedItemKeys = [];
|
this._matchedItemKeys = [];
|
||||||
let docIdsToRemove = [];
|
let docIdsToRemove = [];
|
||||||
for (docId in this._allItems) {
|
for (docId in this._allItems) {
|
||||||
// this._allItems[docId].exists() checks if the resource still exists
|
this._matchedItems[docId] = 1;
|
||||||
if (this._allItems[docId].exists()) {
|
this._matchedItemKeys.push(docId);
|
||||||
this._matchedItems[docId] = 1;
|
|
||||||
this._matchedItemKeys.push(docId);
|
|
||||||
} else {
|
|
||||||
docIdsToRemove.push(docId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (docId in docIdsToRemove) {
|
for (docId in docIdsToRemove) {
|
||||||
@ -274,18 +275,21 @@ DashDocDisplayItem.prototype = {
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
||||||
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||||
let name = new Clutter.Text({ font_name: "Sans 14px",
|
let name = new St.Label({ style_class: 'dash-recent-docs-item',
|
||||||
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
text: docInfo.name });
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
|
||||||
text: docInfo.name });
|
|
||||||
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
let draggable = DND.makeDraggable(this.actor);
|
let draggable = DND.makeDraggable(this.actor);
|
||||||
this.actor._delegate = this;
|
},
|
||||||
|
|
||||||
|
getUri: function() {
|
||||||
|
return this._info.uri;
|
||||||
},
|
},
|
||||||
|
|
||||||
getDragActorSource: function() {
|
getDragActorSource: function() {
|
||||||
@ -317,12 +321,14 @@ DashDocDisplay.prototype = {
|
|||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
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('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||||
|
|
||||||
|
this._actorsByUri = {};
|
||||||
|
|
||||||
this._docManager = DocInfo.getDocManager();
|
this._docManager = DocInfo.getDocManager();
|
||||||
this._docManager.connect('changed', Lang.bind(this, function(mgr) {
|
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
||||||
this._redisplay();
|
this._pendingDocsChange = true;
|
||||||
}));
|
this._checkDocExistence = false;
|
||||||
this._redisplay();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
@ -354,19 +360,19 @@ DashDocDisplay.prototype = {
|
|||||||
// Two columns, where we go vertically down first. So just take
|
// Two columns, where we go vertically down first. So just take
|
||||||
// the height of half of the children as our preferred height.
|
// the height of half of the children as our preferred height.
|
||||||
|
|
||||||
let firstColumnChildren = children.length / 2;
|
let firstColumnChildren = Math.ceil(children.length / 2);
|
||||||
|
|
||||||
alloc.min_size = 0;
|
let natural = 0;
|
||||||
for (let i = 0; i < firstColumnChildren; i++) {
|
for (let i = 0; i < firstColumnChildren; i++) {
|
||||||
let child = children[i];
|
let child = children[i];
|
||||||
let [minSize, naturalSize] = child.get_preferred_height(forWidth);
|
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
||||||
alloc.natural_size += naturalSize;
|
natural += naturalSize;
|
||||||
|
|
||||||
if (i > 0 && i < children.length - 1) {
|
if (i > 0 && i < children.length - 1) {
|
||||||
alloc.min_size += DEFAULT_SPACING;
|
natural += DEFAULT_SPACING;
|
||||||
alloc.natural_size += DEFAULT_SPACING;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
alloc.natural_size = natural;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
_allocate: function(actor, box, flags) {
|
||||||
@ -384,14 +390,16 @@ DashDocDisplay.prototype = {
|
|||||||
// Loop over the children, going vertically down first. When we run
|
// Loop over the children, going vertically down first. When we run
|
||||||
// out of vertical space (our y variable is bigger than box.y2), switch
|
// out of vertical space (our y variable is bigger than box.y2), switch
|
||||||
// to the second column.
|
// to the second column.
|
||||||
for (; i < children.length; i++) {
|
while (i < children.length) {
|
||||||
let child = children[i];
|
let child = children[i];
|
||||||
|
|
||||||
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
||||||
|
|
||||||
if (y + naturalSize > box.y2) {
|
if (y + naturalSize > box.y2) {
|
||||||
// Is this the second column? Ok, break.
|
// Is this the second column, or we're in
|
||||||
if (columnIndex == 1) {
|
// the first column and can't even fit one
|
||||||
|
// item? In that case, break.
|
||||||
|
if (columnIndex == 1 || i == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Set x to the halfway point.
|
// Set x to the halfway point.
|
||||||
@ -399,6 +407,10 @@ DashDocDisplay.prototype = {
|
|||||||
x = x + itemWidth + DEFAULT_SPACING;
|
x = x + itemWidth + DEFAULT_SPACING;
|
||||||
// And y is back to the top.
|
// And y is back to the top.
|
||||||
y = box.y1;
|
y = box.y1;
|
||||||
|
// Retry this same item, now that we're in the second column.
|
||||||
|
// By looping back to the top here, we re-test the size
|
||||||
|
// again for the second column.
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
@ -411,30 +423,53 @@ DashDocDisplay.prototype = {
|
|||||||
|
|
||||||
child.show();
|
child.show();
|
||||||
child.allocate(childBox, flags);
|
child.allocate(childBox, flags);
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything else didn't fit, just hide it.
|
if (this._checkDocExistence) {
|
||||||
for (; i < children.length; i++) {
|
// Now we know how many docs we are displaying, queue a check to see if any of them
|
||||||
children[i].hide();
|
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
|
||||||
|
// we'll now be displaying items we weren't previously, we'll check again to see
|
||||||
|
// if they were deleted, and so forth and so on.
|
||||||
|
// TODO: We should change this to ask for as many as we can fit in the given space:
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
|
||||||
|
this._docManager.queueExistenceCheck(i);
|
||||||
|
this._checkDocExistence = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let skipPaint = [];
|
||||||
|
for (; i < children.length; i++)
|
||||||
|
this.actor.set_skip_paint(children[i], true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDocsChanged: function() {
|
||||||
|
this._checkDocExistence = true;
|
||||||
|
Main.queueDeferredWork(this._workId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
|
// Should be kept alive by the _actorsByUri
|
||||||
this.actor.remove_all();
|
this.actor.remove_all();
|
||||||
|
let docs = this._docManager.getTimestampOrderedInfos();
|
||||||
let docs = this._docManager.getItems();
|
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
|
||||||
let docUrls = [];
|
let doc = docs[i];
|
||||||
for (let url in docs) {
|
let display = this._actorsByUri[doc.uri];
|
||||||
docUrls.push(url);
|
if (display) {
|
||||||
|
this.actor.add_actor(display.actor);
|
||||||
|
} else {
|
||||||
|
let display = new DashDocDisplayItem(doc);
|
||||||
|
this.actor.add_actor(display.actor);
|
||||||
|
this._actorsByUri[doc.uri] = display;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
|
// Any unparented actors must have been deleted
|
||||||
let textureCache = Shell.TextureCache.get_default();
|
for (let uri in this._actorsByUri) {
|
||||||
|
let display = this._actorsByUri[uri];
|
||||||
for (let i = 0; i < docUrls.length; i++) {
|
if (display.actor.get_parent() == null) {
|
||||||
let url = docUrls[i];
|
display.actor.destroy();
|
||||||
let docInfo = docs[url];
|
delete this._actorsByUri[uri];
|
||||||
let display = new DashDocDisplayItem(docInfo);
|
}
|
||||||
this.actor.add_actor(display.actor);
|
|
||||||
}
|
}
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
}
|
}
|
||||||
@ -442,3 +477,41 @@ DashDocDisplay.prototype = {
|
|||||||
|
|
||||||
Signals.addSignalMethods(DashDocDisplay.prototype);
|
Signals.addSignalMethods(DashDocDisplay.prototype);
|
||||||
|
|
||||||
|
function DocSearchProvider() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
DocSearchProvider.prototype = {
|
||||||
|
__proto__: Search.SearchProvider.prototype,
|
||||||
|
|
||||||
|
_init: function(name) {
|
||||||
|
Search.SearchProvider.prototype._init.call(this, _("DOCUMENTS"));
|
||||||
|
this._docManager = DocInfo.getDocManager();
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultMeta: function(resultId) {
|
||||||
|
let docInfo = this._docManager.lookupByUri(resultId);
|
||||||
|
if (!docInfo)
|
||||||
|
return null;
|
||||||
|
return { 'id': resultId,
|
||||||
|
'name': docInfo.name,
|
||||||
|
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
|
||||||
|
},
|
||||||
|
|
||||||
|
activateResult: function(id) {
|
||||||
|
let docInfo = this._docManager.lookupByUri(id);
|
||||||
|
docInfo.launch();
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialResultSet: function(terms) {
|
||||||
|
return this._docManager.initialSearch(terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
|
return this._docManager.subsearch(previousResults, terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
expandSearch: function(terms) {
|
||||||
|
log("TODO expand docs search");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
42
js/ui/environment.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||||
|
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const Format = imports.misc.format;
|
||||||
|
|
||||||
|
// "monkey patch" in some varargs ClutterContainer methods; we need
|
||||||
|
// to do this per-container class since there is no representation
|
||||||
|
// of interfaces in Javascript
|
||||||
|
function _patchContainerClass(containerClass) {
|
||||||
|
// This one is a straightforward mapping of the C method
|
||||||
|
containerClass.prototype.child_set = function(actor, props) {
|
||||||
|
let meta = this.get_child_meta(actor);
|
||||||
|
for (prop in props)
|
||||||
|
meta[prop] = props[prop];
|
||||||
|
};
|
||||||
|
|
||||||
|
// clutter_container_add() actually is a an add-many-actors
|
||||||
|
// method. We conveniently, but somewhat dubiously, take the
|
||||||
|
// this opportunity to make it do something more useful.
|
||||||
|
containerClass.prototype.add = function(actor, props) {
|
||||||
|
this.add_actor(actor);
|
||||||
|
if (props)
|
||||||
|
this.child_set(actor, props);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_patchContainerClass(St.BoxLayout);
|
||||||
|
_patchContainerClass(St.Table);
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Tweener.init();
|
||||||
|
String.prototype.format = Format.format;
|
||||||
|
|
||||||
|
// Set the default direction for St widgets (this needs to be done before any use of St)
|
||||||
|
if (Gettext_gtk20.gettext("default:LTR") == "default:RTL") {
|
||||||
|
St.Widget.set_default_direction(St.TextDirection.RTL);
|
||||||
|
}
|
||||||
|
}
|
158
js/ui/extensionSystem.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const ExtensionState = {
|
||||||
|
ENABLED: 1,
|
||||||
|
DISABLED: 2,
|
||||||
|
ERROR: 3,
|
||||||
|
OUT_OF_DATE: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
const ExtensionType = {
|
||||||
|
SYSTEM: 1,
|
||||||
|
PER_USER: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps uuid -> metadata object
|
||||||
|
const extensionMeta = {};
|
||||||
|
// Maps uuid -> importer object (extension directory tree)
|
||||||
|
const extensions = {};
|
||||||
|
// Array of uuids
|
||||||
|
var disabledExtensions;
|
||||||
|
// GFile for user extensions
|
||||||
|
var userExtensionsDir = null;
|
||||||
|
|
||||||
|
function loadExtension(dir, enabled, type) {
|
||||||
|
let info;
|
||||||
|
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
||||||
|
|
||||||
|
let metadataFile = dir.get_child('metadata.json');
|
||||||
|
if (!metadataFile.query_exists(null)) {
|
||||||
|
global.logError(baseErrorString + 'Missing metadata.json');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [success, metadataContents, len, etag] = metadataFile.load_contents(null);
|
||||||
|
let meta;
|
||||||
|
try {
|
||||||
|
meta = JSON.parse(metadataContents);
|
||||||
|
} catch (e) {
|
||||||
|
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let requiredProperties = ['uuid', 'name', 'description'];
|
||||||
|
for (let i = 0; i < requiredProperties; i++) {
|
||||||
|
let prop = requiredProperties[i];
|
||||||
|
if (!meta[prop]) {
|
||||||
|
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Encourage people to add this
|
||||||
|
if (!meta['url']) {
|
||||||
|
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = dir.get_basename();
|
||||||
|
if (base != meta.uuid) {
|
||||||
|
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionMeta[meta.uuid] = meta;
|
||||||
|
extensionMeta[meta.uuid].type = type;
|
||||||
|
extensionMeta[meta.uuid].path = dir.get_path();
|
||||||
|
if (!enabled) {
|
||||||
|
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to error, we set success as the last step
|
||||||
|
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
|
||||||
|
|
||||||
|
let extensionJs = dir.get_child('extension.js');
|
||||||
|
if (!extensionJs.query_exists(null)) {
|
||||||
|
global.logError(baseErrorString + 'Missing extension.js');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let stylesheetPath = null;
|
||||||
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||||
|
let theme = themeContext.get_theme();
|
||||||
|
let stylesheetFile = dir.get_child('stylesheet.css');
|
||||||
|
if (stylesheetFile.query_exists(null)) {
|
||||||
|
try {
|
||||||
|
theme.load_stylesheet(stylesheetFile.get_path());
|
||||||
|
} catch (e) {
|
||||||
|
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let extensionModule;
|
||||||
|
try {
|
||||||
|
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
|
||||||
|
extensionModule = extensions[meta.uuid].extension;
|
||||||
|
} catch (e) {
|
||||||
|
if (stylesheetPath != null)
|
||||||
|
theme.unload_stylesheet(stylesheetPath);
|
||||||
|
global.logError(baseErrorString + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!extensionModule.main) {
|
||||||
|
global.logError(baseErrorString + 'missing \'main\' function');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
extensionModule.main();
|
||||||
|
} catch (e) {
|
||||||
|
if (stylesheetPath != null)
|
||||||
|
theme.unload_stylesheet(stylesheetPath);
|
||||||
|
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
|
||||||
|
global.log('Loaded extension ' + meta.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
let userConfigPath = GLib.get_user_config_dir();
|
||||||
|
let userExtensionsPath = GLib.build_filenamev([userConfigPath, 'gnome-shell', 'extensions']);
|
||||||
|
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
|
||||||
|
try {
|
||||||
|
userExtensionsDir.make_directory_with_parents(null);
|
||||||
|
} catch (e) {
|
||||||
|
global.logError(""+e);
|
||||||
|
}
|
||||||
|
|
||||||
|
disabledExtensions = Shell.GConf.get_default().get_string_list('disabled_extensions');
|
||||||
|
}
|
||||||
|
|
||||||
|
function _loadExtensionsIn(dir, type) {
|
||||||
|
let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
let file, info;
|
||||||
|
while ((info = fileEnum.next_file(null)) != null) {
|
||||||
|
let fileType = info.get_file_type();
|
||||||
|
if (fileType != Gio.FileType.DIRECTORY)
|
||||||
|
continue;
|
||||||
|
let name = info.get_name();
|
||||||
|
let enabled = disabledExtensions.indexOf(name) < 0;
|
||||||
|
let child = dir.get_child(name);
|
||||||
|
loadExtension(child, enabled, type);
|
||||||
|
}
|
||||||
|
fileEnum.close(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadExtensions() {
|
||||||
|
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
|
||||||
|
let systemDataDirs = GLib.get_system_data_dirs();
|
||||||
|
for (let i = 0; i < systemDataDirs.length; i++) {
|
||||||
|
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
|
||||||
|
let dir = Gio.file_new_for_path(dirPath);
|
||||||
|
if (dir.query_exists(null))
|
||||||
|
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
|
||||||
|
}
|
||||||
|
}
|
@ -11,25 +11,18 @@ const Meta = imports.gi.Meta;
|
|||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Button = imports.ui.button;
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Link = imports.ui.link;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const RedisplayFlags = { NONE: 0,
|
const RedisplayFlags = { NONE: 0,
|
||||||
RESET_CONTROLS: 1 << 0,
|
|
||||||
FULL: 1 << 1,
|
FULL: 1 << 1,
|
||||||
SUBSEARCH: 1 << 2 };
|
SUBSEARCH: 1 << 2,
|
||||||
|
IMMEDIATE: 1 << 3 };
|
||||||
|
|
||||||
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
|
|
||||||
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
|
|
||||||
const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color();
|
const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color();
|
||||||
ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb);
|
ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb);
|
||||||
const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000);
|
|
||||||
const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x4f6fadaa);
|
|
||||||
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
|
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
|
||||||
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
||||||
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
|
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
@ -37,10 +30,7 @@ PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
|
|||||||
|
|
||||||
const DEFAULT_PADDING = 4;
|
const DEFAULT_PADDING = 4;
|
||||||
|
|
||||||
const ITEM_DISPLAY_HEIGHT = 50;
|
|
||||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||||
const ITEM_DISPLAY_PADDING = 1;
|
|
||||||
const ITEM_DISPLAY_PADDING_RIGHT = 2;
|
|
||||||
const DEFAULT_COLUMN_GAP = 6;
|
const DEFAULT_COLUMN_GAP = 6;
|
||||||
|
|
||||||
const PREVIEW_ICON_SIZE = 96;
|
const PREVIEW_ICON_SIZE = 96;
|
||||||
@ -51,8 +41,6 @@ const PREVIEW_BOX_CORNER_RADIUS = 10;
|
|||||||
const PREVIEW_PLACING = 3/4;
|
const PREVIEW_PLACING = 3/4;
|
||||||
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
|
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
|
||||||
|
|
||||||
const INFORMATION_BUTTON_SIZE = 16;
|
|
||||||
|
|
||||||
/* This is a virtual class that represents a single display item containing
|
/* This is a virtual class that represents a single display item containing
|
||||||
* a name, a description, and an icon. It allows selecting an item and represents
|
* a name, a description, and an icon. It allows selecting an item and represents
|
||||||
* it by highlighting it with a different background color than the default.
|
* it by highlighting it with a different background color than the default.
|
||||||
@ -63,12 +51,8 @@ function GenericDisplayItem() {
|
|||||||
|
|
||||||
GenericDisplayItem.prototype = {
|
GenericDisplayItem.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this.actor = new St.BoxLayout({ style_class: "generic-display-item",
|
||||||
spacing: ITEM_DISPLAY_PADDING,
|
reactive: true });
|
||||||
reactive: true,
|
|
||||||
background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
|
||||||
corner_radius: 4,
|
|
||||||
height: ITEM_DISPLAY_HEIGHT });
|
|
||||||
|
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
this.actor.connect('button-release-event',
|
this.actor.connect('button-release-event',
|
||||||
@ -80,47 +64,13 @@ GenericDisplayItem.prototype = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let draggable = DND.makeDraggable(this.actor);
|
let draggable = DND.makeDraggable(this.actor);
|
||||||
draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
|
||||||
|
|
||||||
this._infoContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this._iconBin = new St.Bin();
|
||||||
spacing: DEFAULT_PADDING });
|
this.actor.add(this._iconBin);
|
||||||
this.actor.append(this._infoContent, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
this._iconBox = new Big.Box();
|
this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
|
||||||
this._infoContent.append(this._iconBox, Big.BoxPackFlags.NONE);
|
vertical: true });
|
||||||
|
this.actor.add(this._infoText, { expand: true, y_fill: false });
|
||||||
this._infoText = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
||||||
spacing: DEFAULT_PADDING });
|
|
||||||
this._infoContent.append(this._infoText, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
let infoIconUri = "file://" + global.imagedir + "info.svg";
|
|
||||||
let infoIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
|
|
||||||
infoIconUri,
|
|
||||||
INFORMATION_BUTTON_SIZE,
|
|
||||||
INFORMATION_BUTTON_SIZE);
|
|
||||||
this._informationButton = new Button.IconButton(this.actor, INFORMATION_BUTTON_SIZE, infoIcon);
|
|
||||||
let buttonBox = new Big.Box({ width: INFORMATION_BUTTON_SIZE + 2 * DEFAULT_PADDING,
|
|
||||||
height: INFORMATION_BUTTON_SIZE,
|
|
||||||
padding_left: DEFAULT_PADDING, padding_right: DEFAULT_PADDING,
|
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
|
||||||
buttonBox.append(this._informationButton.actor, Big.BoxPackFlags.NONE);
|
|
||||||
this.actor.append(buttonBox, Big.BoxPackFlags.END);
|
|
||||||
|
|
||||||
// Connecting to the button-press-event for the information button ensures that the actor,
|
|
||||||
// which is a draggable actor, does not get the button-press-event and doesn't initiate
|
|
||||||
// the dragging, which then prevents us from getting the button-release-event for the button.
|
|
||||||
this._informationButton.actor.connect('button-press-event',
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
this._informationButton.actor.connect('button-release-event',
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
// Selects the item by highlighting it and displaying its details
|
|
||||||
this.emit('show-details');
|
|
||||||
return true;
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._name = null;
|
this._name = null;
|
||||||
this._description = null;
|
this._description = null;
|
||||||
@ -152,24 +102,10 @@ GenericDisplayItem.prototype = {
|
|||||||
|
|
||||||
//// Public methods ////
|
//// Public methods ////
|
||||||
|
|
||||||
// Shows the information button when the item was drawn under the mouse pointer.
|
|
||||||
onDrawnUnderPointer: function() {
|
|
||||||
this._informationButton.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Highlights the item by setting a different background color than the default
|
// Highlights the item by setting a different background color than the default
|
||||||
// if isSelected is true, removes the highlighting otherwise.
|
// if isSelected is true, removes the highlighting otherwise.
|
||||||
markSelected: function(isSelected) {
|
markSelected: function(isSelected) {
|
||||||
let color;
|
this.actor.set_style_pseudo_class(isSelected ? "selected" : null);
|
||||||
if (isSelected) {
|
|
||||||
color = ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR;
|
|
||||||
this._informationButton.forceShow(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
color = ITEM_DISPLAY_BACKGROUND_COLOR;
|
|
||||||
this._informationButton.forceShow(false);
|
|
||||||
}
|
|
||||||
this.actor.background_color = color;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -185,19 +121,14 @@ GenericDisplayItem.prototype = {
|
|||||||
spacing: PREVIEW_BOX_SPACING });
|
spacing: PREVIEW_BOX_SPACING });
|
||||||
|
|
||||||
// Inner box with name and description
|
// Inner box with name and description
|
||||||
let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
|
||||||
spacing: PREVIEW_BOX_SPACING });
|
vertical: true });
|
||||||
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
let detailsName = new St.Label({ style_class: 'generic-display-details-name',
|
||||||
font_name: "Sans bold 14px",
|
text: this._name.text });
|
||||||
line_wrap: true,
|
textDetails.add(detailsName);
|
||||||
text: this._name.text });
|
|
||||||
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
let detailsDescription = new St.Label({ text: this._description.text });
|
||||||
font_name: "Sans 14px",
|
textDetails.add(detailsDescription);
|
||||||
line_wrap: true,
|
|
||||||
text: this._description.text });
|
|
||||||
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
this._detailsDescriptions.push(detailsDescription);
|
this._detailsDescriptions.push(detailsDescription);
|
||||||
|
|
||||||
@ -223,7 +154,7 @@ GenericDisplayItem.prototype = {
|
|||||||
|
|
||||||
// Destroys the item.
|
// Destroys the item.
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Pure virtual public methods ////
|
//// Pure virtual public methods ////
|
||||||
@ -260,20 +191,15 @@ GenericDisplayItem.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._icon = this._createIcon();
|
this._icon = this._createIcon();
|
||||||
this._iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
this._iconBin.set_child(this._icon);
|
||||||
|
|
||||||
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
|
this._name = new St.Label({ style_class: "generic-display-item-name",
|
||||||
font_name: "Sans 14px",
|
text: nameText });
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
this._infoText.add(this._name);
|
||||||
text: nameText });
|
|
||||||
this._infoText.append(this._name, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
|
this._description = new St.Label({ style_class: "generic-display-item-description",
|
||||||
font_name: "Sans 12px",
|
text: descriptionText ? descriptionText : "" });
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
this._infoText.add(this._description);
|
||||||
text: descriptionText ? descriptionText : ""
|
|
||||||
});
|
|
||||||
this._infoText.append(this._description, Big.BoxPackFlags.EXPAND);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sets the description text for the item, including the description text
|
// Sets the description text for the item, including the description text
|
||||||
@ -305,42 +231,43 @@ GenericDisplayItem.prototype = {
|
|||||||
// Returns a preview icon for the item.
|
// Returns a preview icon for the item.
|
||||||
_createPreviewIcon: function() {
|
_createPreviewIcon: function() {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
},
|
}
|
||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
// Hides the information button once the item starts being dragged.
|
|
||||||
_onDragBegin : function (draggable, time) {
|
|
||||||
// For some reason, we are not getting leave-event signal when we are dragging an item,
|
|
||||||
// so we should remove the link manually.
|
|
||||||
this._informationButton.actor.hide();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Signals.addSignalMethods(GenericDisplayItem.prototype);
|
Signals.addSignalMethods(GenericDisplayItem.prototype);
|
||||||
|
|
||||||
|
const GenericDisplayFlags = {
|
||||||
|
DISABLE_VSCROLLING: 1 << 0
|
||||||
|
}
|
||||||
|
|
||||||
/* This is a virtual class that represents a display containing a collection of items
|
/* This is a virtual class that represents a display containing a collection of items
|
||||||
* that can be filtered with a search string.
|
* that can be filtered with a search string.
|
||||||
*/
|
*/
|
||||||
function GenericDisplay() {
|
function GenericDisplay(flags) {
|
||||||
this._init();
|
this._init(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericDisplay.prototype = {
|
GenericDisplay.prototype = {
|
||||||
_init : function() {
|
_init : function(flags) {
|
||||||
|
let disableVScrolling = (flags & GenericDisplayFlags.DISABLE_VSCROLLING) != 0;
|
||||||
this._search = '';
|
this._search = '';
|
||||||
this._expanded = false;
|
this._expanded = false;
|
||||||
|
|
||||||
this._maxItemsPerPage = null;
|
if (disableVScrolling) {
|
||||||
this._list = new Shell.OverflowList({ spacing: 6.0,
|
this.actor = this._list = new Shell.OverflowList({ spacing: 6,
|
||||||
item_height: ITEM_DISPLAY_HEIGHT });
|
item_height: 50 });
|
||||||
|
} else {
|
||||||
|
this.actor = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||||
|
this.actor.get_hscroll_bar().hide();
|
||||||
|
this._list = new St.BoxLayout({ style_class: 'generic-display-container',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.add_actor(this._list);
|
||||||
|
}
|
||||||
|
|
||||||
this._list.connect('notify::n-pages', Lang.bind(this, function () {
|
this._pendingRedisplay = RedisplayFlags.NONE;
|
||||||
this._updateDisplayControl(true);
|
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
||||||
}));
|
|
||||||
this._list.connect('notify::page', Lang.bind(this, function () {
|
|
||||||
this._updateDisplayControl(false);
|
|
||||||
}));
|
|
||||||
|
|
||||||
// map<itemId, Object> where Object represents the item info
|
// map<itemId, Object> where Object represents the item info
|
||||||
this._allItems = {};
|
this._allItems = {};
|
||||||
@ -352,13 +279,6 @@ GenericDisplay.prototype = {
|
|||||||
this._displayedItems = {};
|
this._displayedItems = {};
|
||||||
this._openDetailIndex = -1;
|
this._openDetailIndex = -1;
|
||||||
this._selectedIndex = -1;
|
this._selectedIndex = -1;
|
||||||
// These two are public - .actor is the normal "actor subclass" property,
|
|
||||||
// but we also expose a .displayControl actor which is separate.
|
|
||||||
// See also getNavigationArea.
|
|
||||||
this.actor = this._list;
|
|
||||||
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
|
||||||
spacing: 12,
|
|
||||||
orientation: Big.BoxOrientation.HORIZONTAL});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//// Public methods ////
|
//// Public methods ////
|
||||||
@ -366,12 +286,18 @@ GenericDisplay.prototype = {
|
|||||||
// Sets the search string and displays the matching items.
|
// Sets the search string and displays the matching items.
|
||||||
setSearch: function(text) {
|
setSearch: function(text) {
|
||||||
let lowertext = text.toLowerCase();
|
let lowertext = text.toLowerCase();
|
||||||
if (lowertext == this._search)
|
if (lowertext == this._search) {
|
||||||
return;
|
return;
|
||||||
let flags = RedisplayFlags.RESET_CONTROLS;
|
}
|
||||||
|
let flags = RedisplayFlags.IMMEDIATE;
|
||||||
if (this._search != '') {
|
if (this._search != '') {
|
||||||
if (lowertext.indexOf(this._search) == 0)
|
// Because we combine search terms with OR, we have to be sure that no new term
|
||||||
|
// was introduced before deciding that the new search results will be a subset of
|
||||||
|
// the existing search results.
|
||||||
|
if (lowertext.indexOf(this._search) == 0 &&
|
||||||
|
lowertext.split(/\s+/).length == this._search.split(/\s+/).length) {
|
||||||
flags |= RedisplayFlags.SUBSEARCH;
|
flags |= RedisplayFlags.SUBSEARCH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._search = lowertext;
|
this._search = lowertext;
|
||||||
this._redisplay(flags);
|
this._redisplay(flags);
|
||||||
@ -391,7 +317,7 @@ GenericDisplay.prototype = {
|
|||||||
// to the bottom one. Returns true if the selection actually moved up, false if it wrapped
|
// to the bottom one. Returns true if the selection actually moved up, false if it wrapped
|
||||||
// around to the bottom.
|
// around to the bottom.
|
||||||
selectUp: function() {
|
selectUp: function() {
|
||||||
let count = this._list.displayedCount;
|
let count = this._getVisibleCount();
|
||||||
let selectedUp = true;
|
let selectedUp = true;
|
||||||
let prev = this._selectedIndex - 1;
|
let prev = this._selectedIndex - 1;
|
||||||
if (this._selectedIndex <= 0) {
|
if (this._selectedIndex <= 0) {
|
||||||
@ -406,7 +332,7 @@ GenericDisplay.prototype = {
|
|||||||
// to the top one. Returns true if the selection actually moved down, false if it wrapped
|
// to the top one. Returns true if the selection actually moved down, false if it wrapped
|
||||||
// around to the top.
|
// around to the top.
|
||||||
selectDown: function() {
|
selectDown: function() {
|
||||||
let count = this._list.displayedCount;
|
let count = this._getVisibleCount();
|
||||||
let selectedDown = true;
|
let selectedDown = true;
|
||||||
let next = this._selectedIndex + 1;
|
let next = this._selectedIndex + 1;
|
||||||
if (this._selectedIndex == count - 1) {
|
if (this._selectedIndex == count - 1) {
|
||||||
@ -425,7 +351,7 @@ GenericDisplay.prototype = {
|
|||||||
|
|
||||||
// Selects the last item among the displayed items.
|
// Selects the last item among the displayed items.
|
||||||
selectLastItem: function() {
|
selectLastItem: function() {
|
||||||
let count = this._list.displayedCount;
|
let count = this._getVisibleCount();
|
||||||
if (this.hasItems())
|
if (this.hasItems())
|
||||||
this._selectIndex(count - 1);
|
this._selectIndex(count - 1);
|
||||||
},
|
},
|
||||||
@ -462,6 +388,8 @@ GenericDisplay.prototype = {
|
|||||||
resetState: function() {
|
resetState: function() {
|
||||||
this._filterReset();
|
this._filterReset();
|
||||||
this._openDetailIndex = -1;
|
this._openDetailIndex = -1;
|
||||||
|
if (!(this.actor instanceof Shell.OverflowList))
|
||||||
|
this.actor.get_vscroll_bar().get_adjustment().value = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns an actor which acts as a sidebar; this is used for
|
// Returns an actor which acts as a sidebar; this is used for
|
||||||
@ -475,26 +403,18 @@ GenericDisplay.prototype = {
|
|||||||
return item.createDetailsActor();
|
return item.createDetailsActor();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Displays the page specified by the pageNumber argument.
|
|
||||||
displayPage: function(pageNumber) {
|
|
||||||
// Cleanup from the previous selection, but don't unset this._selectedIndex
|
|
||||||
if (this.hasSelected()) {
|
|
||||||
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
|
|
||||||
}
|
|
||||||
this._list.page = pageNumber;
|
|
||||||
},
|
|
||||||
|
|
||||||
//// Protected methods ////
|
//// Protected methods ////
|
||||||
|
|
||||||
_redisplayFull: function() {
|
_recreateDisplayItems: function() {
|
||||||
this._removeAllDisplayItems();
|
this._removeAllDisplayItems();
|
||||||
|
this._setDefaultList();
|
||||||
for (let itemId in this._allItems) {
|
for (let itemId in this._allItems) {
|
||||||
this._addDisplayItem(itemId);
|
this._addDisplayItem(itemId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Creates a display item based on the information associated with itemId
|
// Creates a display item based on the information associated with itemId
|
||||||
// and adds it to the displayed items.
|
// and adds it to the list of displayed items, but does not yet display it.
|
||||||
_addDisplayItem : function(itemId) {
|
_addDisplayItem : function(itemId) {
|
||||||
if (this._displayedItems.hasOwnProperty(itemId)) {
|
if (this._displayedItems.hasOwnProperty(itemId)) {
|
||||||
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
|
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
|
||||||
@ -508,14 +428,14 @@ GenericDisplay.prototype = {
|
|||||||
Lang.bind(this,
|
Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
// update the selection
|
// update the selection
|
||||||
this._selectIndex(this._list.get_actor_index(displayItem.actor));
|
this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
|
||||||
this.activateSelected();
|
this.activateSelected();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
displayItem.connect('show-details',
|
displayItem.connect('show-details',
|
||||||
Lang.bind(this,
|
Lang.bind(this,
|
||||||
function() {
|
function() {
|
||||||
let index = this._list.get_actor_index(displayItem.actor);
|
let index = this._list.get_children().indexOf(displayItem.actor);
|
||||||
/* Close the details pane if already open */
|
/* Close the details pane if already open */
|
||||||
if (index == this._openDetailIndex) {
|
if (index == this._openDetailIndex) {
|
||||||
this._openDetailIndex = -1;
|
this._openDetailIndex = -1;
|
||||||
@ -525,15 +445,15 @@ GenericDisplay.prototype = {
|
|||||||
this.emit('show-details', index);
|
this.emit('show-details', index);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._list.add_actor(displayItem.actor);
|
|
||||||
this._displayedItems[itemId] = displayItem;
|
this._displayedItems[itemId] = displayItem;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Removes an item identifed by the itemId from the displayed items.
|
// Removes an item identifed by the itemId from the displayed items.
|
||||||
_removeDisplayItem: function(itemId) {
|
_removeDisplayItem: function(itemId) {
|
||||||
let count = this._list.displayedCount;
|
let children = this._list.get_children();
|
||||||
|
let count = children.length;
|
||||||
let displayItem = this._displayedItems[itemId];
|
let displayItem = this._displayedItems[itemId];
|
||||||
let displayItemIndex = this._list.get_actor_index(displayItem.actor);
|
let displayItemIndex = children.indexOf(displayItem.actor);
|
||||||
|
|
||||||
if (this.hasSelected() && count == 1) {
|
if (this.hasSelected() && count == 1) {
|
||||||
this.unsetSelected();
|
this.unsetSelected();
|
||||||
@ -628,62 +548,51 @@ GenericDisplay.prototype = {
|
|||||||
/*
|
/*
|
||||||
* Updates the displayed items, applying the search string if one exists.
|
* Updates the displayed items, applying the search string if one exists.
|
||||||
* @flags: Flags controlling redisplay behavior as follows:
|
* @flags: Flags controlling redisplay behavior as follows:
|
||||||
* RESET_CONTROLS - indicates if the page selection should be reset when displaying the matching results.
|
|
||||||
* We reset the page selection when the change in results was initiated by the user by
|
|
||||||
* entering a different search criteria or by viewing the results list in a different
|
|
||||||
* size mode, but we keep the page selection the same if the results got updated on
|
|
||||||
* their own while the user was browsing through the result pages.
|
|
||||||
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
||||||
* one, which implies we only need to re-search through previous results.
|
* one, which implies we only need to re-search through previous results.
|
||||||
|
* FULL - Indicates that we need recreate all displayed items.
|
||||||
|
* IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful
|
||||||
|
* if you want to get the number of matched items and show/hide a section based on
|
||||||
|
* that number.
|
||||||
*/
|
*/
|
||||||
_redisplay: function(flags) {
|
_redisplay: function(flags) {
|
||||||
let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0;
|
let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
|
||||||
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) > 0;
|
if (!immediate && !this.actor.mapped) {
|
||||||
let fullReload = (flags & RedisplayFlags.FULL) > 0;
|
this._pendingRedisplay |= flags;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
|
||||||
|
let fullReload = (flags & RedisplayFlags.FULL) != 0;
|
||||||
|
|
||||||
let hadSelected = this.hasSelected();
|
let hadSelected = this.hasSelected();
|
||||||
|
this.unsetSelected();
|
||||||
|
|
||||||
if (!this._initialLoadComplete || !this._refreshCache())
|
if (!this._initialLoadComplete)
|
||||||
fullReload = true;
|
fullReload = true;
|
||||||
|
|
||||||
|
if (!this._refreshCache())
|
||||||
|
fullReload = true;
|
||||||
|
|
||||||
if (fullReload) {
|
if (fullReload) {
|
||||||
|
this._recreateDisplayItems();
|
||||||
this._initialLoadComplete = true;
|
this._initialLoadComplete = true;
|
||||||
this._redisplayFull();
|
}
|
||||||
} if (isSubSearch) {
|
|
||||||
|
if (isSubSearch) {
|
||||||
this._redisplaySubSearch();
|
this._redisplaySubSearch();
|
||||||
} else {
|
} else {
|
||||||
this._redisplayReordering();
|
this._redisplayReordering();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resetPage)
|
|
||||||
this._list.page = 0;
|
|
||||||
|
|
||||||
if (hadSelected) {
|
if (hadSelected) {
|
||||||
this._selectedIndex = -1;
|
this._selectedIndex = -1;
|
||||||
this.selectFirstItem();
|
this.selectFirstItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
Mainloop.idle_add(Lang.bind(this, this._checkInformationIcon),
|
|
||||||
Meta.PRIORITY_BEFORE_REDRAW);
|
|
||||||
|
|
||||||
this.emit('redisplayed');
|
this.emit('redisplayed');
|
||||||
},
|
},
|
||||||
|
|
||||||
// Check if the pointer is over one of the items and display the information button if it is.
|
|
||||||
// We want to do this between finishing our changes to the display and the point where
|
|
||||||
// the display is redrawn.
|
|
||||||
_checkInformationIcon: function() {
|
|
||||||
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
|
|
||||||
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
|
||||||
x, y);
|
|
||||||
if (actor != null) {
|
|
||||||
let item = this._findDisplayedByActor(actor);
|
|
||||||
if (item != null) {
|
|
||||||
item.onDrawnUnderPointer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
//// Pure virtual protected methods ////
|
//// Pure virtual protected methods ////
|
||||||
|
|
||||||
// Performs the steps needed to have the latest information about the items.
|
// Performs the steps needed to have the latest information about the items.
|
||||||
@ -756,59 +665,14 @@ GenericDisplay.prototype = {
|
|||||||
return matchScores;
|
return matchScores;
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
|
||||||
* Updates the display control to reflect the matched items set and the page selected.
|
|
||||||
*
|
|
||||||
* resetDisplayControl - indicates if the display control should be re-created because
|
|
||||||
* the results or the space allocated for them changed. If it's false,
|
|
||||||
* the existing display control is used and only the page links are
|
|
||||||
* updated to reflect the current page selection.
|
|
||||||
*/
|
|
||||||
_updateDisplayControl: function(resetDisplayControl) {
|
|
||||||
if (resetDisplayControl) {
|
|
||||||
this.displayControl.remove_all();
|
|
||||||
let nPages = this._list.n_pages;
|
|
||||||
// Don't show the page indicator if there is only one page.
|
|
||||||
if (nPages == 1)
|
|
||||||
return;
|
|
||||||
let pageNumber = this._list.page;
|
|
||||||
for (let i = 0; i < nPages; i++) {
|
|
||||||
let pageControl = new Link.Link({ color: (i == pageNumber) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
|
|
||||||
font_name: "Sans Bold 16px",
|
|
||||||
text: (i+1) + "",
|
|
||||||
reactive: (i == pageNumber) ? false : true});
|
|
||||||
this.displayControl.append(pageControl.actor, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
// we use pageNumberLocalScope to get the page number right in the callback function
|
|
||||||
let pageNumberLocalScope = i;
|
|
||||||
pageControl.connect('clicked',
|
|
||||||
Lang.bind(this,
|
|
||||||
function(o, event) {
|
|
||||||
this.displayPage(pageNumberLocalScope);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let pageControlActors = this.displayControl.get_children();
|
|
||||||
for (let i = 0; i < pageControlActors.length; i++) {
|
|
||||||
let pageControlActor = pageControlActors[i];
|
|
||||||
if (i == this._list.page) {
|
|
||||||
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
|
|
||||||
pageControlActor.reactive = false;
|
|
||||||
} else {
|
|
||||||
pageControlActor.color = ITEM_DISPLAY_DESCRIPTION_COLOR;
|
|
||||||
pageControlActor.reactive = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.hasSelected()) {
|
|
||||||
this.selectFirstItem();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Returns a display item based on its index in the ordering of the
|
// Returns a display item based on its index in the ordering of the
|
||||||
// display children.
|
// display children.
|
||||||
_findDisplayedByIndex: function(index) {
|
_findDisplayedByIndex: function(index) {
|
||||||
let actor = this._list.get_displayed_actor(index);
|
let actor;
|
||||||
|
if (this.actor instanceof Shell.OverflowList)
|
||||||
|
actor = this.actor.get_displayed_actor(index);
|
||||||
|
else
|
||||||
|
actor = this._list.get_children()[index];
|
||||||
return this._findDisplayedByActor(actor);
|
return this._findDisplayedByActor(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -840,6 +704,20 @@ GenericDisplay.prototype = {
|
|||||||
let item = this._findDisplayedByIndex(index);
|
let item = this._findDisplayedByIndex(index);
|
||||||
item.markSelected(true);
|
item.markSelected(true);
|
||||||
this.emit('selected');
|
this.emit('selected');
|
||||||
|
},
|
||||||
|
|
||||||
|
_getVisibleCount: function() {
|
||||||
|
if (this.actor instanceof Shell.OverflowList)
|
||||||
|
return this._list.displayed_count;
|
||||||
|
return this._list.get_n_children();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMappedNotify: function () {
|
||||||
|
let mapped = this.actor.mapped;
|
||||||
|
if (mapped && this._pendingRedisplay > RedisplayFlags.NONE)
|
||||||
|
this._redisplay(this._pendingRedisplay);
|
||||||
|
|
||||||
|
this._pendingRedisplay = RedisplayFlags.NONE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,78 +3,21 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
// Link is a clickable link. Right now it just handles properly capturing
|
|
||||||
// press and release events and short-circuiting the button handling in
|
|
||||||
// ClutterText, but more features like different colors for hover/pressed states
|
|
||||||
// or a different mouse cursor could be implemented.
|
|
||||||
//
|
|
||||||
// The properties passed in are forwarded to the Clutter.Text() constructor,
|
|
||||||
// so can include, 'text', 'font_name', etc.
|
|
||||||
function Link(props) {
|
function Link(props) {
|
||||||
this._init(props);
|
this._init(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
Link.prototype = {
|
Link.prototype = {
|
||||||
_init : function(props) {
|
_init : function(props) {
|
||||||
let realProps = { reactive: true };
|
let realProps = { reactive: true,
|
||||||
|
style_class: 'shell-link' };
|
||||||
// The user can pass in reactive: false to override the above and get
|
// The user can pass in reactive: false to override the above and get
|
||||||
// a non-reactive link (a link to the current page, perhaps)
|
// a non-reactive link (a link to the current page, perhaps)
|
||||||
Lang.copyProperties(props, realProps);
|
Lang.copyProperties(props, realProps);
|
||||||
|
|
||||||
this.actor = new Clutter.Text(realProps);
|
this.actor = new St.Button(realProps);
|
||||||
this.actor._delegate = this;
|
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
|
||||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
|
|
||||||
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
|
|
||||||
this.actor.connect('leave-event', Lang.bind(this, this._onLeave));
|
|
||||||
|
|
||||||
this._buttonDown = false;
|
|
||||||
this._havePointer = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Update the text of the link
|
|
||||||
setText : function(text) {
|
|
||||||
this.actor.text = text;
|
|
||||||
},
|
|
||||||
|
|
||||||
// We want to react on buttonDown, but if we override button-release-event for
|
|
||||||
// ClutterText, but not button-press-event, we get a stuck grab. Tracking
|
|
||||||
// buttonDown and doing the grab isn't really necessary, but doing it makes
|
|
||||||
// the behavior perfectly correct if the user clicks on one actor, drags
|
|
||||||
// to another and releases - that should not trigger either actor.
|
|
||||||
_onButtonPress : function(actor, event) {
|
|
||||||
this._buttonDown = true;
|
|
||||||
this._havePointer = true; // Hack to work around poor enter/leave tracking in Clutter
|
|
||||||
Clutter.grab_pointer(actor);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onButtonRelease : function(actor, event) {
|
|
||||||
if (this._buttonDown) {
|
|
||||||
this._buttonDown = false;
|
|
||||||
Clutter.ungrab_pointer(actor);
|
|
||||||
|
|
||||||
if (this._havePointer)
|
|
||||||
this.emit('clicked');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onEnter : function(actor, event) {
|
|
||||||
if (event.get_source() == actor)
|
|
||||||
this._havePointer = true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onLeave : function(actor, event) {
|
|
||||||
if (event.get_source() == actor)
|
|
||||||
this._havePointer = false;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,26 +4,20 @@ const Big = imports.gi.Big;
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const Link = imports.ui.link;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const LG_BORDER_COLOR = new Clutter.Color();
|
/* Imports...feel free to add here as needed */
|
||||||
LG_BORDER_COLOR.from_pixel(0x0000aca0);
|
|
||||||
const LG_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
LG_BACKGROUND_COLOR.from_pixel(0x000000d5);
|
|
||||||
const GREY = new Clutter.Color();
|
|
||||||
GREY.from_pixel(0xAFAFAFFF);
|
|
||||||
const MATRIX_GREEN = new Clutter.Color();
|
|
||||||
MATRIX_GREEN.from_pixel(0x88ff66ff);
|
|
||||||
// FIXME pull from GConf
|
|
||||||
const MATRIX_FONT = 'Monospace 10';
|
|
||||||
|
|
||||||
/* Imports...feel free to add here as needed */
|
|
||||||
var commandHeader = "const Clutter = imports.gi.Clutter; " +
|
var commandHeader = "const Clutter = imports.gi.Clutter; " +
|
||||||
"const GLib = imports.gi.GLib; " +
|
"const GLib = imports.gi.GLib; " +
|
||||||
"const Gtk = imports.gi.Gtk; " +
|
"const Gtk = imports.gi.Gtk; " +
|
||||||
@ -47,32 +41,41 @@ function Notebook() {
|
|||||||
|
|
||||||
Notebook.prototype = {
|
Notebook.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new Big.Box();
|
this.actor = new St.BoxLayout({ vertical: true });
|
||||||
|
|
||||||
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this.tabControls = new St.BoxLayout({ style_class: "labels" });
|
||||||
spacing: 4, padding: 2 });
|
|
||||||
|
|
||||||
this._selectedIndex = -1;
|
this._selectedIndex = -1;
|
||||||
this._tabs = [];
|
this._tabs = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
appendPage: function(name, child) {
|
appendPage: function(name, child) {
|
||||||
let labelOuterBox = new Big.Box({ padding: 2 });
|
let labelBox = new St.BoxLayout({ style_class: "notebook-tab" });
|
||||||
let labelBox = new Big.Box({ padding: 2, border_color: MATRIX_GREEN,
|
let label = new St.Button({ label: name });
|
||||||
reactive: true });
|
label.connect('clicked', Lang.bind(this, function () {
|
||||||
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
|
|
||||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
|
||||||
font_name: MATRIX_FONT,
|
|
||||||
text: name });
|
|
||||||
labelBox.connect('button-press-event', Lang.bind(this, function () {
|
|
||||||
this.selectChild(child);
|
this.selectChild(child);
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
labelBox.append(label, Big.BoxPackFlags.EXPAND);
|
labelBox.add(label, { expand: true });
|
||||||
this._tabs.push([child, labelBox]);
|
this.tabControls.add(labelBox);
|
||||||
child.hide();
|
|
||||||
this.actor.append(child, Big.BoxPackFlags.EXPAND);
|
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
|
||||||
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
|
scrollview.get_hscroll_bar().hide();
|
||||||
|
scrollview.add_actor(child);
|
||||||
|
|
||||||
|
let tabData = { child: child,
|
||||||
|
labelBox: labelBox,
|
||||||
|
label: label,
|
||||||
|
scrollView: scrollview,
|
||||||
|
_scrollToBottom: false };
|
||||||
|
this._tabs.push(tabData);
|
||||||
|
scrollview.hide();
|
||||||
|
this.actor.add(scrollview, { expand: true });
|
||||||
|
|
||||||
|
let vAdjust = scrollview.vscroll.adjustment;
|
||||||
|
vAdjust.connect('changed', Lang.bind(this, function () { this._onAdjustScopeChanged(tabData); }));
|
||||||
|
vAdjust.connect('notify::value', Lang.bind(this, function() { this._onAdjustValueChanged(tabData); }));
|
||||||
|
|
||||||
if (this._selectedIndex == -1)
|
if (this._selectedIndex == -1)
|
||||||
this.selectIndex(0);
|
this.selectIndex(0);
|
||||||
},
|
},
|
||||||
@ -80,10 +83,9 @@ Notebook.prototype = {
|
|||||||
_unselect: function() {
|
_unselect: function() {
|
||||||
if (this._selectedIndex < 0)
|
if (this._selectedIndex < 0)
|
||||||
return;
|
return;
|
||||||
let [child, labelBox] = this._tabs[this._selectedIndex];
|
let tabData = this._tabs[this._selectedIndex];
|
||||||
labelBox.padding = 2;
|
tabData.labelBox.set_style_pseudo_class(null);
|
||||||
labelBox.border = 0;
|
tabData.scrollView.hide();
|
||||||
child.hide();
|
|
||||||
this._selectedIndex = -1;
|
this._selectedIndex = -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -95,12 +97,11 @@ Notebook.prototype = {
|
|||||||
this.emit('selection', null);
|
this.emit('selection', null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [child, labelBox] = this._tabs[index];
|
let tabData = this._tabs[index];
|
||||||
labelBox.padding = 1;
|
tabData.labelBox.set_style_pseudo_class('selected');
|
||||||
labelBox.border = 1;
|
tabData.scrollView.show();
|
||||||
child.show();
|
|
||||||
this._selectedIndex = index;
|
this._selectedIndex = index;
|
||||||
this.emit('selection', child);
|
this.emit('selection', tabData.child);
|
||||||
},
|
},
|
||||||
|
|
||||||
selectChild: function(child) {
|
selectChild: function(child) {
|
||||||
@ -108,13 +109,32 @@ Notebook.prototype = {
|
|||||||
this.selectIndex(-1);
|
this.selectIndex(-1);
|
||||||
else {
|
else {
|
||||||
for (let i = 0; i < this._tabs.length; i++) {
|
for (let i = 0; i < this._tabs.length; i++) {
|
||||||
let [tabChild, labelBox] = this._tabs[i];
|
let tabData = this._tabs[i];
|
||||||
if (tabChild == child) {
|
if (tabData.child == child) {
|
||||||
this.selectIndex(i);
|
this.selectIndex(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToBottom: function(index) {
|
||||||
|
let tabData = this._tabs[index];
|
||||||
|
tabData._scrollToBottom = true;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAdjustValueChanged: function (tabData) {
|
||||||
|
let vAdjust = tabData.scrollView.vscroll.adjustment;
|
||||||
|
if (vAdjust.value < (vAdjust.upper - vAdjust.lower - 0.5))
|
||||||
|
tabData._scrolltoBottom = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAdjustScopeChanged: function (tabData) {
|
||||||
|
if (!tabData._scrollToBottom)
|
||||||
|
return;
|
||||||
|
let vAdjust = tabData.scrollView.vscroll.adjustment;
|
||||||
|
vAdjust.value = vAdjust.upper - vAdjust.page_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Signals.addSignalMethods(Notebook.prototype);
|
Signals.addSignalMethods(Notebook.prototype);
|
||||||
@ -130,20 +150,19 @@ Result.prototype = {
|
|||||||
|
|
||||||
this.actor = new Big.Box();
|
this.actor = new Big.Box();
|
||||||
|
|
||||||
let cmdTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
let cmdTxt = new St.Label({ text: command });
|
||||||
font_name: MATRIX_FONT,
|
cmdTxt.ellipsize = Pango.EllipsizeMode.END;
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
|
||||||
text: command });
|
|
||||||
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
|
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
|
||||||
let resultTxt = new Clutter.Text({ color: MATRIX_GREEN,
|
let resultTxt = new St.Label({ text: "r(" + index + ") = " + o });
|
||||||
font_name: MATRIX_FONT,
|
resultTxt.ellipsize = Pango.EllipsizeMode.END;
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
|
||||||
text: "r(" + index + ") = " + o });
|
|
||||||
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
|
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
|
||||||
let line = new Big.Box({ border_color: GREY,
|
let line = new Clutter.Rectangle({ name: "Separator",
|
||||||
border_bottom: 1,
|
height: 1 });
|
||||||
height: 8 });
|
let padBin = new St.Bin({ name: "Separator", x_fill: true, y_fill: true });
|
||||||
this.actor.append(line, Big.BoxPackFlags.NONE);
|
padBin.add_actor(line);
|
||||||
|
this.actor.append(padBin, Big.BoxPackFlags.NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,17 +177,14 @@ ActorHierarchy.prototype = {
|
|||||||
|
|
||||||
this._parentList = [];
|
this._parentList = [];
|
||||||
|
|
||||||
this.actor = new Big.Box({ spacing: 4,
|
this.actor = new St.BoxLayout({ name: "ActorHierarchy", vertical: true });
|
||||||
border: 1,
|
|
||||||
padding: 4,
|
|
||||||
border_color: GREY });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setTarget: function(actor) {
|
setTarget: function(actor) {
|
||||||
this._previousTarget = this._target;
|
this._previousTarget = this._target;
|
||||||
this.target = actor;
|
this.target = actor;
|
||||||
|
|
||||||
this.actor.remove_all();
|
this.actor.get_children().forEach(function (child) { child.destroy(); });
|
||||||
|
|
||||||
if (!(actor instanceof Clutter.Actor))
|
if (!(actor instanceof Clutter.Actor))
|
||||||
return;
|
return;
|
||||||
@ -181,11 +197,9 @@ ActorHierarchy.prototype = {
|
|||||||
while ((parent = parent.get_parent()) != null) {
|
while ((parent = parent.get_parent()) != null) {
|
||||||
this._parentList.push(parent);
|
this._parentList.push(parent);
|
||||||
|
|
||||||
let link = new Clutter.Text({ color: MATRIX_GREEN,
|
let link = new St.Label({ reactive: true,
|
||||||
font_name: MATRIX_FONT,
|
text: "" + parent });
|
||||||
reactive: true,
|
this.actor.add_actor(link);
|
||||||
text: "" + parent });
|
|
||||||
this.actor.append(link, Big.BoxPackFlags.IF_FITS);
|
|
||||||
let parentTarget = parent;
|
let parentTarget = parent;
|
||||||
link.connect('button-press-event', Lang.bind(this, function () {
|
link.connect('button-press-event', Lang.bind(this, function () {
|
||||||
this._selectByActor(parentTarget);
|
this._selectByActor(parentTarget);
|
||||||
@ -214,16 +228,13 @@ PropertyInspector.prototype = {
|
|||||||
|
|
||||||
this._parentList = [];
|
this._parentList = [];
|
||||||
|
|
||||||
this.actor = new Big.Box({ spacing: 4,
|
this.actor = new St.BoxLayout({ name: "PropertyInspector", vertical: true });
|
||||||
border: 1,
|
|
||||||
padding: 4,
|
|
||||||
border_color: GREY });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setTarget: function(actor) {
|
setTarget: function(actor) {
|
||||||
this.target = actor;
|
this.target = actor;
|
||||||
|
|
||||||
this.actor.remove_all();
|
this.actor.get_children().forEach(function (child) { child.destroy(); });
|
||||||
|
|
||||||
for (let propName in actor) {
|
for (let propName in actor) {
|
||||||
let valueStr;
|
let valueStr;
|
||||||
@ -233,11 +244,9 @@ PropertyInspector.prototype = {
|
|||||||
valueStr = '<error>';
|
valueStr = '<error>';
|
||||||
}
|
}
|
||||||
let propText = propName + ": " + valueStr;
|
let propText = propName + ": " + valueStr;
|
||||||
let propDisplay = new Clutter.Text({ color: MATRIX_GREEN,
|
let propDisplay = new St.Label({ reactive: true,
|
||||||
font_name: MATRIX_FONT,
|
text: propText });
|
||||||
reactive: true,
|
this.actor.add_actor(propDisplay);
|
||||||
text: propText });
|
|
||||||
this.actor.append(propDisplay, Big.BoxPackFlags.IF_FITS);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,20 +258,17 @@ function Inspector() {
|
|||||||
Inspector.prototype = {
|
Inspector.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
let width = 150;
|
let width = 150;
|
||||||
let eventHandler = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
let primary = global.get_primary_monitor();
|
||||||
border: 1,
|
let eventHandler = new St.BoxLayout({ name: "LookingGlassDialog",
|
||||||
border_color: LG_BORDER_COLOR,
|
vertical: false,
|
||||||
corner_radius: 4,
|
y: primary.y + Math.floor(primary.height / 2),
|
||||||
y: global.stage.height/2,
|
reactive: true });
|
||||||
reactive: true
|
|
||||||
});
|
|
||||||
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
|
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
|
||||||
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
|
eventHandler.x = primary.x + Math.floor((primary.width - eventHandler.width) / 2);
|
||||||
}));
|
}));
|
||||||
global.stage.add_actor(eventHandler);
|
global.stage.add_actor(eventHandler);
|
||||||
let displayText = new Clutter.Text({ color: MATRIX_GREEN,
|
let displayText = new St.Label();
|
||||||
font_name: MATRIX_FONT, text: '' });
|
eventHandler.add(displayText, { expand: true });
|
||||||
eventHandler.append(displayText, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
let borderPaintTarget = null;
|
let borderPaintTarget = null;
|
||||||
let borderPaintId = null;
|
let borderPaintId = null;
|
||||||
@ -302,6 +308,135 @@ Inspector.prototype = {
|
|||||||
|
|
||||||
Signals.addSignalMethods(Inspector.prototype);
|
Signals.addSignalMethods(Inspector.prototype);
|
||||||
|
|
||||||
|
function ErrorLog() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorLog.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout();
|
||||||
|
this.text = new St.Label();
|
||||||
|
this.actor.add(this.text);
|
||||||
|
this.text.clutter_text.line_wrap = true;
|
||||||
|
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
||||||
|
},
|
||||||
|
|
||||||
|
_formatTime: function(d){
|
||||||
|
function pad(n) { return n < 10 ? '0' + n : n };
|
||||||
|
return d.getUTCFullYear()+'-'
|
||||||
|
+ pad(d.getUTCMonth()+1)+'-'
|
||||||
|
+ pad(d.getUTCDate())+'T'
|
||||||
|
+ pad(d.getUTCHours())+':'
|
||||||
|
+ pad(d.getUTCMinutes())+':'
|
||||||
|
+ pad(d.getUTCSeconds())+'Z'
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderText: function() {
|
||||||
|
if (!this.actor.mapped)
|
||||||
|
return;
|
||||||
|
let text = this.text.text;
|
||||||
|
let stack = Main._getAndClearErrorStack();
|
||||||
|
for (let i = 0; i < stack.length; i++) {
|
||||||
|
let logItem = stack[i];
|
||||||
|
text += logItem.category + " t=" + this._formatTime(new Date(logItem.timestamp)) + " " + logItem.message + "\n";
|
||||||
|
}
|
||||||
|
this.text.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Extensions() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Extensions.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
|
name: 'lookingGlassExtensions' });
|
||||||
|
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||||
|
text: _("No extensions installed") });
|
||||||
|
this._extensionsList = new St.BoxLayout({ vertical: true,
|
||||||
|
style_class: 'lg-extensions-list' });
|
||||||
|
this.actor.add(this._extensionsList);
|
||||||
|
this._loadExtensionList();
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadExtensionList: function() {
|
||||||
|
let extensions = ExtensionSystem.extensionMeta;
|
||||||
|
let totalExtensions = 0;
|
||||||
|
for (let uuid in extensions) {
|
||||||
|
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
|
||||||
|
this._extensionsList.add(extensionDisplay);
|
||||||
|
totalExtensions++;
|
||||||
|
}
|
||||||
|
if (totalExtensions == 0) {
|
||||||
|
this._extensionsList.add(this._noExtensions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onViewSource: function (actor) {
|
||||||
|
let meta = actor._extensionMeta;
|
||||||
|
let file = Gio.file_new_for_path(meta.path);
|
||||||
|
let uri = file.get_uri();
|
||||||
|
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
||||||
|
Main.lookingGlass.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWebPage: function (actor) {
|
||||||
|
let meta = actor._extensionMeta;
|
||||||
|
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
|
||||||
|
Main.lookingGlass.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
_stateToString: function(extensionState) {
|
||||||
|
switch (extensionState) {
|
||||||
|
case ExtensionSystem.ExtensionState.ENABLED:
|
||||||
|
return _("Enabled");
|
||||||
|
case ExtensionSystem.ExtensionState.DISABLED:
|
||||||
|
return _("Disabled");
|
||||||
|
case ExtensionSystem.ExtensionState.ERROR:
|
||||||
|
return _("Error");
|
||||||
|
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
|
||||||
|
return _("Out of date");
|
||||||
|
}
|
||||||
|
return "Unknown"; // Not translated, shouldn't appear
|
||||||
|
},
|
||||||
|
|
||||||
|
_createExtensionDisplay: function(meta) {
|
||||||
|
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
|
||||||
|
let name = new St.Label({ style_class: 'lg-extension-name',
|
||||||
|
text: meta.name });
|
||||||
|
box.add(name, { expand: true });
|
||||||
|
let description = new St.Label({ style_class: 'lg-extension-description',
|
||||||
|
text: meta.description });
|
||||||
|
box.add(description, { expand: true });
|
||||||
|
|
||||||
|
let metaBox = new St.BoxLayout();
|
||||||
|
box.add(metaBox);
|
||||||
|
let stateString = this._stateToString(meta.state);
|
||||||
|
let state = new St.Label({ style_class: 'lg-extension-state',
|
||||||
|
text: this._stateToString(meta.state) });
|
||||||
|
|
||||||
|
let actionsContainer = new St.Bin({ x_align: St.Align.END });
|
||||||
|
metaBox.add(actionsContainer);
|
||||||
|
let actionsBox = new St.BoxLayout({ style_class: 'lg-extension-actions' });
|
||||||
|
actionsContainer.set_child(actionsBox);
|
||||||
|
|
||||||
|
let viewsource = new Link.Link({ label: _("View Source") });
|
||||||
|
viewsource.actor._extensionMeta = meta;
|
||||||
|
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
|
||||||
|
actionsBox.add(viewsource.actor);
|
||||||
|
|
||||||
|
if (meta.url) {
|
||||||
|
let webpage = new Link.Link({ label: _("Web Page") });
|
||||||
|
webpage.actor._extensionMeta = meta;
|
||||||
|
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
|
||||||
|
actionsBox.add(webpage.actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function LookingGlass() {
|
function LookingGlass() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
@ -314,6 +449,10 @@ LookingGlass.prototype = {
|
|||||||
this._savedText = null;
|
this._savedText = null;
|
||||||
this._historyNavIndex = -1;
|
this._historyNavIndex = -1;
|
||||||
this._history = [];
|
this._history = [];
|
||||||
|
this._borderPaintTarget = null;
|
||||||
|
this._borderPaintId = 0;
|
||||||
|
this._borderDestroyId = 0;
|
||||||
|
|
||||||
this._readHistory();
|
this._readHistory();
|
||||||
|
|
||||||
this._open = false;
|
this._open = false;
|
||||||
@ -321,29 +460,26 @@ LookingGlass.prototype = {
|
|||||||
this._offset = 0;
|
this._offset = 0;
|
||||||
this._results = [];
|
this._results = [];
|
||||||
|
|
||||||
// TODO replace with scrolling or something better
|
// Sort of magic, but...eh.
|
||||||
this._maxItems = 10;
|
this._maxItems = 150;
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ name: "LookingGlassDialog",
|
||||||
|
vertical: true,
|
||||||
|
visible: false });
|
||||||
|
|
||||||
|
let gconf = Shell.GConf.get_default();
|
||||||
|
gconf.watch_directory("/desktop/gnome/interface");
|
||||||
|
gconf.connect("changed::/desktop/gnome/interface/monospace_font_name",
|
||||||
|
Lang.bind(this, this._updateFont));
|
||||||
|
this._updateFont();
|
||||||
|
|
||||||
this.actor = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
|
|
||||||
border: 1,
|
|
||||||
border_color: LG_BORDER_COLOR,
|
|
||||||
corner_radius: 4,
|
|
||||||
padding_top: 8,
|
|
||||||
padding_left: 4,
|
|
||||||
padding_right: 4,
|
|
||||||
padding_bottom: 4,
|
|
||||||
spacing: 4,
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
global.stage.add_actor(this.actor);
|
global.stage.add_actor(this.actor);
|
||||||
|
|
||||||
let toolbar = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
let toolbar = new St.BoxLayout({ name: "Toolbar" });
|
||||||
border: 1, border_color: GREY,
|
this.actor.add_actor(toolbar);
|
||||||
corner_radius: 4 });
|
|
||||||
this.actor.append(toolbar, Big.BoxPackFlags.NONE);
|
|
||||||
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
|
||||||
24);
|
24);
|
||||||
toolbar.append(inspectIcon, Big.BoxPackFlags.NONE);
|
toolbar.add_actor(inspectIcon);
|
||||||
inspectIcon.reactive = true;
|
inspectIcon.reactive = true;
|
||||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||||
let inspector = new Inspector();
|
let inspector = new Inspector();
|
||||||
@ -361,31 +497,27 @@ LookingGlass.prototype = {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let notebook = new Notebook();
|
let notebook = new Notebook();
|
||||||
this.actor.append(notebook.actor, Big.BoxPackFlags.EXPAND);
|
this._notebook = notebook;
|
||||||
toolbar.append(notebook.tabControls, Big.BoxPackFlags.END);
|
this.actor.add(notebook.actor, { expand: true });
|
||||||
|
|
||||||
this._evalBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let emptyBox = new St.Bin();
|
||||||
spacing: 4 });
|
toolbar.add(emptyBox, { expand: true });
|
||||||
|
toolbar.add_actor(notebook.tabControls);
|
||||||
|
|
||||||
|
this._evalBox = new St.BoxLayout({ name: "EvalBox", vertical: true });
|
||||||
notebook.appendPage('Evaluator', this._evalBox);
|
notebook.appendPage('Evaluator', this._evalBox);
|
||||||
|
|
||||||
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
spacing: 4 });
|
spacing: 4 });
|
||||||
this._evalBox.append(this._resultsArea, Big.BoxPackFlags.EXPAND);
|
this._evalBox.add(this._resultsArea, { expand: true });
|
||||||
|
|
||||||
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
|
||||||
this._evalBox.append(entryArea, Big.BoxPackFlags.NONE);
|
this._evalBox.add_actor(entryArea);
|
||||||
|
|
||||||
let label = new Clutter.Text({ color: MATRIX_GREEN,
|
let label = new St.Label({ text: 'js>>> ' });
|
||||||
font_name: MATRIX_FONT,
|
|
||||||
text: 'js>>> ' });
|
|
||||||
entryArea.append(label, Big.BoxPackFlags.NONE);
|
entryArea.append(label, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._entry = new Clutter.Text({ color: MATRIX_GREEN,
|
this._entry = new St.Entry();
|
||||||
font_name: MATRIX_FONT,
|
|
||||||
editable: true,
|
|
||||||
activatable: true,
|
|
||||||
singleLineMode: true,
|
|
||||||
text: ''});
|
|
||||||
/* unmapping the edit box will un-focus it, undo that */
|
/* unmapping the edit box will un-focus it, undo that */
|
||||||
notebook.connect('selection', Lang.bind(this, function (nb, child) {
|
notebook.connect('selection', Lang.bind(this, function (nb, child) {
|
||||||
if (child == this._evalBox)
|
if (child == this._evalBox)
|
||||||
@ -403,7 +535,13 @@ LookingGlass.prototype = {
|
|||||||
notebook.selectIndex(0);
|
notebook.selectIndex(0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._entry.connect('activate', Lang.bind(this, function (o, e) {
|
this._errorLog = new ErrorLog();
|
||||||
|
notebook.appendPage('Errors', this._errorLog.actor);
|
||||||
|
|
||||||
|
this._extensions = new Extensions();
|
||||||
|
notebook.appendPage('Extensions', this._extensions.actor);
|
||||||
|
|
||||||
|
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
||||||
let text = o.get_text();
|
let text = o.get_text();
|
||||||
// Ensure we don't get newlines in the command; the history file is
|
// Ensure we don't get newlines in the command; the history file is
|
||||||
// newline-separated.
|
// newline-separated.
|
||||||
@ -416,12 +554,9 @@ LookingGlass.prototype = {
|
|||||||
this._historyNavIndex = -1;
|
this._historyNavIndex = -1;
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||||
let symbol = e.get_key_symbol();
|
let symbol = e.get_key_symbol();
|
||||||
if (symbol == Clutter.Escape) {
|
if (symbol == Clutter.Up) {
|
||||||
this.close();
|
|
||||||
return true;
|
|
||||||
} else if (symbol == Clutter.Up) {
|
|
||||||
if (this._historyNavIndex >= this._history.length - 1)
|
if (this._historyNavIndex >= this._history.length - 1)
|
||||||
return true;
|
return true;
|
||||||
this._historyNavIndex++;
|
this._historyNavIndex++;
|
||||||
@ -446,6 +581,19 @@ LookingGlass.prototype = {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateFont: function() {
|
||||||
|
let gconf = Shell.GConf.get_default();
|
||||||
|
let fontName = gconf.get_string("/desktop/gnome/interface/monospace_font_name");
|
||||||
|
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
||||||
|
let fontDesc = Pango.Font.description_from_string(fontName);
|
||||||
|
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
||||||
|
// monospace font to be bold/oblique/etc. Could easily be added here.
|
||||||
|
this.actor.style =
|
||||||
|
'font-size: ' + fontDesc.get_size() / 1024. + (fontDesc.get_size_is_absolute() ? 'px' : 'pt') + ';'
|
||||||
|
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||||
|
},
|
||||||
|
|
||||||
_readHistory: function () {
|
_readHistory: function () {
|
||||||
if (!this._historyFile.query_exists(null))
|
if (!this._historyFile.query_exists(null))
|
||||||
return;
|
return;
|
||||||
@ -475,6 +623,18 @@ LookingGlass.prototype = {
|
|||||||
this._results.push(result);
|
this._results.push(result);
|
||||||
this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE);
|
this._resultsArea.append(result.actor, Big.BoxPackFlags.NONE);
|
||||||
this._propInspector.setTarget(obj);
|
this._propInspector.setTarget(obj);
|
||||||
|
if (this._borderPaintTarget != null) {
|
||||||
|
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||||
|
this._borderPaintTarget = null;
|
||||||
|
}
|
||||||
|
if (obj instanceof Clutter.Actor) {
|
||||||
|
this._borderPaintTarget = obj;
|
||||||
|
this._borderPaintId = Shell.add_hook_paint_red_border(obj);
|
||||||
|
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
|
||||||
|
this._borderDestroyId = 0;
|
||||||
|
this._borderPaintTarget = null;
|
||||||
|
}));
|
||||||
|
}
|
||||||
let children = this._resultsArea.get_children();
|
let children = this._resultsArea.get_children();
|
||||||
if (children.length > this._maxItems) {
|
if (children.length > this._maxItems) {
|
||||||
this._results.shift();
|
this._results.shift();
|
||||||
@ -482,6 +642,9 @@ LookingGlass.prototype = {
|
|||||||
this._offset++;
|
this._offset++;
|
||||||
}
|
}
|
||||||
this._it = obj;
|
this._it = obj;
|
||||||
|
|
||||||
|
// Scroll to bottom
|
||||||
|
this._notebook.scrollToBottom(0);
|
||||||
},
|
},
|
||||||
|
|
||||||
_evaluate : function(command) {
|
_evaluate : function(command) {
|
||||||
@ -518,11 +681,11 @@ LookingGlass.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_resizeTo: function(actor) {
|
_resizeTo: function(actor) {
|
||||||
let stage = global.stage;
|
let primary = global.get_primary_monitor();
|
||||||
let myWidth = stage.width * 0.7;
|
let myWidth = primary.width * 0.7;
|
||||||
let myHeight = stage.height * 0.7;
|
let myHeight = primary.height * 0.7;
|
||||||
let [srcX, srcY] = actor.get_transformed_position();
|
let [srcX, srcY] = actor.get_transformed_position();
|
||||||
this.actor.x = srcX + (stage.width-myWidth)/2;
|
this.actor.x = srcX + (primary.width - myWidth) / 2;
|
||||||
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
|
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
|
||||||
this._targetY = this._hiddenY + myHeight;
|
this._targetY = this._hiddenY + myHeight;
|
||||||
this.actor.y = this._hiddenY;
|
this.actor.y = this._hiddenY;
|
||||||
@ -538,6 +701,16 @@ LookingGlass.prototype = {
|
|||||||
this._resizeTo(actor);
|
this._resizeTo(actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Handle key events which are relevant for all tabs of the LookingGlass
|
||||||
|
_globalKeyPressEvent : function(actor, event) {
|
||||||
|
let symbol = event.get_key_symbol();
|
||||||
|
if (symbol == Clutter.Escape) {
|
||||||
|
this.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
open : function() {
|
open : function() {
|
||||||
if (this._open)
|
if (this._open)
|
||||||
return;
|
return;
|
||||||
@ -545,6 +718,9 @@ LookingGlass.prototype = {
|
|||||||
if (!Main.pushModal(this.actor))
|
if (!Main.pushModal(this.actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._keyPressEventId = global.stage.connect('key-press-event',
|
||||||
|
Lang.bind(this, this._globalKeyPressEvent));
|
||||||
|
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
this.actor.lower(Main.chrome.actor);
|
this.actor.lower(Main.chrome.actor);
|
||||||
this._open = true;
|
this._open = true;
|
||||||
@ -563,10 +739,19 @@ LookingGlass.prototype = {
|
|||||||
if (!this._open)
|
if (!this._open)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this._keyPressEventId)
|
||||||
|
global.stage.disconnect(this._keyPressEventId);
|
||||||
|
|
||||||
this._historyNavIndex = -1;
|
this._historyNavIndex = -1;
|
||||||
this._open = false;
|
this._open = false;
|
||||||
Tweener.removeTweens(this.actor);
|
Tweener.removeTweens(this.actor);
|
||||||
|
|
||||||
|
if (this._borderPaintTarget != null) {
|
||||||
|
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||||
|
this._borderPaintTarget.disconnect(this._borderDestroyId);
|
||||||
|
this._borderPaintTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
Main.popModal(this.actor);
|
Main.popModal(this.actor);
|
||||||
|
|
||||||
Tweener.addTween(this.actor, { time: 0.5,
|
Tweener.addTween(this.actor, { time: 0.5,
|
||||||
|
253
js/ui/main.js
@ -10,15 +10,20 @@ const Mainloop = imports.mainloop;
|
|||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Chrome = imports.ui.chrome;
|
const Chrome = imports.ui.chrome;
|
||||||
|
const Environment = imports.ui.environment;
|
||||||
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
|
const PlaceDisplay = imports.ui.placeDisplay;
|
||||||
const RunDialog = imports.ui.runDialog;
|
const RunDialog = imports.ui.runDialog;
|
||||||
const LookingGlass = imports.ui.lookingGlass;
|
const LookingGlass = imports.ui.lookingGlass;
|
||||||
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
const Sidebar = imports.ui.sidebar;
|
const Sidebar = imports.ui.sidebar;
|
||||||
const Tweener = imports.ui.tweener;
|
|
||||||
const WindowManager = imports.ui.windowManager;
|
const WindowManager = imports.ui.windowManager;
|
||||||
|
|
||||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
@ -27,14 +32,20 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
|||||||
let chrome = null;
|
let chrome = null;
|
||||||
let panel = null;
|
let panel = null;
|
||||||
let sidebar = null;
|
let sidebar = null;
|
||||||
|
let placesManager = null;
|
||||||
let overview = null;
|
let overview = null;
|
||||||
let runDialog = null;
|
let runDialog = null;
|
||||||
let lookingGlass = null;
|
let lookingGlass = null;
|
||||||
let wm = null;
|
let wm = null;
|
||||||
|
let notificationDaemon = null;
|
||||||
|
let notificationPopup = null;
|
||||||
|
let messageTray = null;
|
||||||
let recorder = null;
|
let recorder = null;
|
||||||
let shellDBusService = null;
|
let shellDBusService = null;
|
||||||
let modalCount = 0;
|
let modalCount = 0;
|
||||||
let modalActorFocusStack = [];
|
let modalActorFocusStack = [];
|
||||||
|
let _errorLogStack = [];
|
||||||
|
let _startDate;
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
// Add a binding for "global" in the global JS namespace; (gjs
|
// Add a binding for "global" in the global JS namespace; (gjs
|
||||||
@ -42,6 +53,12 @@ function start() {
|
|||||||
// called "window".)
|
// called "window".)
|
||||||
window.global = Shell.Global.get();
|
window.global = Shell.Global.get();
|
||||||
|
|
||||||
|
// Now monkey patch utility functions into the global proxy;
|
||||||
|
// This is easier and faster than indirecting down into global
|
||||||
|
// if we want to call back up into JS.
|
||||||
|
global.logError = _logError;
|
||||||
|
global.log = _logDebug;
|
||||||
|
|
||||||
Gio.DesktopAppInfo.set_desktop_env("GNOME");
|
Gio.DesktopAppInfo.set_desktop_env("GNOME");
|
||||||
|
|
||||||
global.grab_dbus_service();
|
global.grab_dbus_service();
|
||||||
@ -52,17 +69,18 @@ function start() {
|
|||||||
// back into sync ones.
|
// back into sync ones.
|
||||||
DBus.session.flush();
|
DBus.session.flush();
|
||||||
|
|
||||||
Tweener.init();
|
Environment.init();
|
||||||
|
|
||||||
// Ensure ShellAppMonitor is initialized; this will
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||||
// also initialize ShellAppSystem first. ShellAppSystem
|
// also initialize ShellAppSystem first. ShellAppSystem
|
||||||
// needs to load all the .desktop files, and ShellAppMonitor
|
// needs to load all the .desktop files, and ShellWindowTracker
|
||||||
// will use those to associate with windows. Right now
|
// will use those to associate with windows. Right now
|
||||||
// the Monitor doesn't listen for installed app changes
|
// the Monitor doesn't listen for installed app changes
|
||||||
// and recalculate application associations, so to avoid
|
// and recalculate application associations, so to avoid
|
||||||
// races for now we initialize it here. It's better to
|
// races for now we initialize it here. It's better to
|
||||||
// be predictable anyways.
|
// be predictable anyways.
|
||||||
Shell.AppMonitor.get_default();
|
Shell.WindowTracker.get_default();
|
||||||
|
Shell.AppUsage.get_default();
|
||||||
|
|
||||||
// The background color really only matters if there is no desktop
|
// The background color really only matters if there is no desktop
|
||||||
// window (say, nautilus) running. We set it mostly so things look good
|
// window (say, nautilus) running. We set it mostly so things look good
|
||||||
@ -75,6 +93,11 @@ function start() {
|
|||||||
for (let i = 0; i < children.length; i++)
|
for (let i = 0; i < children.length; i++)
|
||||||
children[i].destroy();
|
children[i].destroy();
|
||||||
|
|
||||||
|
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||||
|
let stylesheetPath = global.datadir + "/theme/gnome-shell.css";
|
||||||
|
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
|
||||||
|
themeContext.set_theme (theme);
|
||||||
|
|
||||||
global.connect('panel-run-dialog', function(panel) {
|
global.connect('panel-run-dialog', function(panel) {
|
||||||
// Make sure not more than one run dialog is shown.
|
// Make sure not more than one run dialog is shown.
|
||||||
getRunDialog().open();
|
getRunDialog().open();
|
||||||
@ -89,11 +112,17 @@ function start() {
|
|||||||
getRunDialog().open();
|
getRunDialog().open();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
placesManager = new PlaceDisplay.PlacesManager();
|
||||||
overview = new Overview.Overview();
|
overview = new Overview.Overview();
|
||||||
chrome = new Chrome.Chrome();
|
chrome = new Chrome.Chrome();
|
||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
sidebar = new Sidebar.Sidebar();
|
sidebar = new Sidebar.Sidebar();
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
||||||
|
notificationPopup = new MessageTray.Notification();
|
||||||
|
messageTray = new MessageTray.MessageTray();
|
||||||
|
|
||||||
|
_startDate = new Date();
|
||||||
|
|
||||||
global.screen.connect('toggle-recording', function() {
|
global.screen.connect('toggle-recording', function() {
|
||||||
if (recorder == null) {
|
if (recorder == null) {
|
||||||
@ -109,6 +138,9 @@ function start() {
|
|||||||
|
|
||||||
_relayout();
|
_relayout();
|
||||||
|
|
||||||
|
ExtensionSystem.init();
|
||||||
|
ExtensionSystem.loadExtensions();
|
||||||
|
|
||||||
panel.startupAnimation();
|
panel.startupAnimation();
|
||||||
|
|
||||||
let display = global.screen.get_display();
|
let display = global.screen.get_display();
|
||||||
@ -117,11 +149,56 @@ function start() {
|
|||||||
|
|
||||||
global.stage.connect('captured-event', _globalKeyPressHandler);
|
global.stage.connect('captured-event', _globalKeyPressHandler);
|
||||||
|
|
||||||
|
_log('info', 'loaded at ' + _startDate);
|
||||||
|
|
||||||
Mainloop.idle_add(_removeUnusedWorkspaces);
|
Mainloop.idle_add(_removeUnusedWorkspaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _log:
|
||||||
|
* @category: string message type ('info', 'error')
|
||||||
|
* @msg: A message string
|
||||||
|
* ...: Any further arguments are converted into JSON notation,
|
||||||
|
* and appended to the log message, separated by spaces.
|
||||||
|
*
|
||||||
|
* Log a message into the LookingGlass error
|
||||||
|
* stream. This is primarily intended for use by the
|
||||||
|
* extension system as well as debugging.
|
||||||
|
*/
|
||||||
|
function _log(category, msg) {
|
||||||
|
let text = msg;
|
||||||
|
if (arguments.length > 2) {
|
||||||
|
text += ': ';
|
||||||
|
for (let i = 2; i < arguments.length; i++) {
|
||||||
|
text += JSON.stringify(arguments[i]);
|
||||||
|
if (i < arguments.length - 1)
|
||||||
|
text += " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_errorLogStack.push({timestamp: new Date().getTime(),
|
||||||
|
category: category,
|
||||||
|
message: text });
|
||||||
|
}
|
||||||
|
|
||||||
|
function _logError(msg) {
|
||||||
|
return _log('error', msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _logDebug(msg) {
|
||||||
|
return _log('debug', msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by the error display in lookingGlass.js
|
||||||
|
function _getAndClearErrorStack() {
|
||||||
|
let errors = _errorLogStack;
|
||||||
|
_errorLogStack = [];
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
function _relayout() {
|
function _relayout() {
|
||||||
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
|
let primary = global.get_primary_monitor();
|
||||||
|
panel.actor.set_position(primary.x, primary.y);
|
||||||
|
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
|
||||||
overview.relayout();
|
overview.relayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +270,7 @@ function _globalKeyPressHandler(actor, event) {
|
|||||||
overview.hide();
|
overview.hide();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (symbol == Clutter.F2 && (event.get_state() & Clutter.ModifierType.MOD1_MASK)) {
|
} else if (symbol == Clutter.F2 && (Shell.get_event_state(event) & Clutter.ModifierType.MOD1_MASK)) {
|
||||||
getRunDialog().open();
|
getRunDialog().open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,7 +305,7 @@ function _findModal(actor) {
|
|||||||
*/
|
*/
|
||||||
function pushModal(actor) {
|
function pushModal(actor) {
|
||||||
if (modalCount == 0) {
|
if (modalCount == 0) {
|
||||||
if (!global.begin_modal(currentTime())) {
|
if (!global.begin_modal(global.get_current_time())) {
|
||||||
log("pushModal: invocation of begin_modal failed");
|
log("pushModal: invocation of begin_modal failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -281,7 +358,7 @@ function popModal(actor) {
|
|||||||
if (modalCount > 0)
|
if (modalCount > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
global.end_modal(currentTime());
|
global.end_modal(global.get_current_time());
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,45 +377,6 @@ function getRunDialog() {
|
|||||||
return runDialog;
|
return runDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAppLaunchContext() {
|
|
||||||
let context = new Gdk.AppLaunchContext();
|
|
||||||
context.set_timestamp(currentTime());
|
|
||||||
|
|
||||||
// Make sure that the app is opened on the current workspace even if
|
|
||||||
// the user switches before it starts
|
|
||||||
context.set_desktop(global.screen.get_active_workspace_index());
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* currentTime:
|
|
||||||
*
|
|
||||||
* Gets the current X server time from the current Clutter, Gdk, or X
|
|
||||||
* event. If called from outside an event handler, this may return
|
|
||||||
* %Clutter.CURRENT_TIME (aka 0), or it may return a slightly
|
|
||||||
* out-of-date timestamp.
|
|
||||||
*/
|
|
||||||
function currentTime() {
|
|
||||||
// meta_display_get_current_time() will return the correct time
|
|
||||||
// when handling an X or Gdk event, but will return CurrentTime
|
|
||||||
// from some Clutter event callbacks.
|
|
||||||
//
|
|
||||||
// clutter_get_current_event_time() will return the correct time
|
|
||||||
// from a Clutter event callback, but may return an out-of-date
|
|
||||||
// timestamp if called at other times.
|
|
||||||
//
|
|
||||||
// So we try meta_display_get_current_time() first, since we
|
|
||||||
// can recognize a "wrong" answer from that, and then fall back
|
|
||||||
// to clutter_get_current_event_time().
|
|
||||||
|
|
||||||
let time = global.screen.get_display().get_current_time();
|
|
||||||
if (time != Clutter.CURRENT_TIME)
|
|
||||||
return time;
|
|
||||||
|
|
||||||
return Clutter.get_current_event_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* activateWindow:
|
* activateWindow:
|
||||||
* @window: the Meta.Window to activate
|
* @window: the Meta.Window to activate
|
||||||
@ -351,7 +389,7 @@ function activateWindow(window, time) {
|
|||||||
let windowWorkspaceNum = window.get_workspace().index();
|
let windowWorkspaceNum = window.get_workspace().index();
|
||||||
|
|
||||||
if (!time)
|
if (!time)
|
||||||
time = currentTime();
|
time = global.get_current_time();
|
||||||
|
|
||||||
if (windowWorkspaceNum != activeWorkspaceNum) {
|
if (windowWorkspaceNum != activeWorkspaceNum) {
|
||||||
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
|
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
|
||||||
@ -360,3 +398,120 @@ function activateWindow(window, time) {
|
|||||||
window.activate(time);
|
window.activate(time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - replace this timeout with some system to guess when the user might
|
||||||
|
// be e.g. just reading the screen and not likely to interact.
|
||||||
|
const DEFERRED_TIMEOUT_SECONDS = 20;
|
||||||
|
var _deferredWorkData = {};
|
||||||
|
// Work scheduled for some point in the future
|
||||||
|
var _deferredWorkQueue = [];
|
||||||
|
// Work we need to process before the next redraw
|
||||||
|
var _beforeRedrawQueue = [];
|
||||||
|
// Counter to assign work ids
|
||||||
|
var _deferredWorkSequence = 0;
|
||||||
|
var _deferredTimeoutId = 0;
|
||||||
|
|
||||||
|
function _runDeferredWork(workId) {
|
||||||
|
if (!_deferredWorkData[workId])
|
||||||
|
return;
|
||||||
|
let index = _deferredWorkQueue.indexOf(workId);
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_deferredWorkQueue.splice(index, 1);
|
||||||
|
_deferredWorkData[workId].callback();
|
||||||
|
if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
|
||||||
|
Mainloop.source_remove(_deferredTimeoutId);
|
||||||
|
_deferredTimeoutId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _runAllDeferredWork() {
|
||||||
|
while (_deferredWorkQueue.length > 0)
|
||||||
|
_runDeferredWork(_deferredWorkQueue[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _runBeforeRedrawQueue() {
|
||||||
|
for (let i = 0; i < _beforeRedrawQueue.length; i++) {
|
||||||
|
let workId = _beforeRedrawQueue[i];
|
||||||
|
_runDeferredWork(workId);
|
||||||
|
}
|
||||||
|
_beforeRedrawQueue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _queueBeforeRedraw(workId) {
|
||||||
|
_beforeRedrawQueue.push(workId);
|
||||||
|
if (_beforeRedrawQueue.length == 1) {
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
|
||||||
|
_runBeforeRedrawQueue();
|
||||||
|
return false;
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initializeDeferredWork:
|
||||||
|
* @actor: A #ClutterActor
|
||||||
|
* @callback: Function to invoke to perform work
|
||||||
|
*
|
||||||
|
* This function sets up a callback to be invoked when either the
|
||||||
|
* given actor is mapped, or after some period of time when the machine
|
||||||
|
* is idle. This is useful if your actor isn't always visible on the
|
||||||
|
* screen (for example, all actors in the overview), and you don't want
|
||||||
|
* to consume resources updating if the actor isn't actually going to be
|
||||||
|
* displaying to the user.
|
||||||
|
*
|
||||||
|
* Note that queueDeferredWork is called by default immediately on
|
||||||
|
* initialization as well, under the assumption that new actors
|
||||||
|
* will need it.
|
||||||
|
*
|
||||||
|
* Returns: A string work identifer
|
||||||
|
*/
|
||||||
|
function initializeDeferredWork(actor, callback, props) {
|
||||||
|
// Turn into a string so we can use as an object property
|
||||||
|
let workId = "" + (++_deferredWorkSequence);
|
||||||
|
_deferredWorkData[workId] = { 'actor': actor,
|
||||||
|
'callback': callback };
|
||||||
|
actor.connect('notify::mapped', function () {
|
||||||
|
if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
|
||||||
|
return;
|
||||||
|
_queueBeforeRedraw(workId);
|
||||||
|
});
|
||||||
|
actor.connect('destroy', function() {
|
||||||
|
let index = _deferredWorkQueue.indexOf(workId);
|
||||||
|
if (index >= 0)
|
||||||
|
_deferredWorkQueue.splice(index, 1);
|
||||||
|
delete _deferredWorkData[workId];
|
||||||
|
});
|
||||||
|
queueDeferredWork(workId);
|
||||||
|
return workId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* queueDeferredWork:
|
||||||
|
* @workId: work identifier
|
||||||
|
*
|
||||||
|
* Ensure that the work identified by @workId will be
|
||||||
|
* run on map or timeout. You should call this function
|
||||||
|
* for example when data being displayed by the actor has
|
||||||
|
* changed.
|
||||||
|
*/
|
||||||
|
function queueDeferredWork(workId) {
|
||||||
|
let data = _deferredWorkData[workId];
|
||||||
|
if (!data) {
|
||||||
|
global.logError("invalid work id ", workId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_deferredWorkQueue.indexOf(workId) < 0)
|
||||||
|
_deferredWorkQueue.push(workId);
|
||||||
|
if (data.actor.mapped) {
|
||||||
|
_queueBeforeRedraw(workId);
|
||||||
|
return;
|
||||||
|
} else if (_deferredTimeoutId == 0) {
|
||||||
|
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
||||||
|
_runAllDeferredWork();
|
||||||
|
_deferredTimeoutId = 0;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
271
js/ui/messageTray.js
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const ANIMATION_TIME = 0.2;
|
||||||
|
const NOTIFICATION_TIMEOUT = 4;
|
||||||
|
|
||||||
|
const MESSAGE_TRAY_TIMEOUT = 0.2;
|
||||||
|
|
||||||
|
const ICON_SIZE = 24;
|
||||||
|
|
||||||
|
function Notification(icon, text) {
|
||||||
|
this._init(icon, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification.prototype = {
|
||||||
|
_init: function(icon, text) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function NotificationBox() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationBox.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ name: 'notification' });
|
||||||
|
|
||||||
|
this._iconBox = new St.Bin();
|
||||||
|
this.actor.add(this._iconBox);
|
||||||
|
|
||||||
|
this._textBox = new Shell.GenericContainer();
|
||||||
|
this._textBox.connect('get-preferred-width', Lang.bind(this, this._textBoxGetPreferredWidth));
|
||||||
|
this._textBox.connect('get-preferred-height', Lang.bind(this, this._textBoxGetPreferredHeight));
|
||||||
|
this._textBox.connect('allocate', Lang.bind(this, this._textBoxAllocate));
|
||||||
|
this.actor.add(this._textBox, { expand: true, x_fill: false, y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
this._text = new St.Label();
|
||||||
|
this._text.clutter_text.line_wrap = true;
|
||||||
|
this._text.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
this._textBox.add_actor(this._text);
|
||||||
|
},
|
||||||
|
|
||||||
|
_textBoxGetPreferredWidth: function (actor, forHeight, alloc) {
|
||||||
|
let [min, nat] = this._text.get_preferred_width(forHeight);
|
||||||
|
|
||||||
|
alloc.min_size = alloc.nat_size = Math.min(nat, global.screen_width / 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
_textBoxGetPreferredHeight: function (actor, forWidth, alloc) {
|
||||||
|
// St.BoxLayout passes -1 for @forWidth, which isn't what we want.
|
||||||
|
let prefWidth = {};
|
||||||
|
this._textBoxGetPreferredWidth(this._textBox, -1, prefWidth);
|
||||||
|
[alloc.min_size, alloc.nat_size] = this._text.get_preferred_height(prefWidth.nat_size);
|
||||||
|
log('for width ' + prefWidth.nat_size + ', height ' + alloc.nat_size);
|
||||||
|
},
|
||||||
|
|
||||||
|
_textBoxAllocate: function (actor, box, flags) {
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
childBox.x1 = childBox.y1 = 0;
|
||||||
|
childBox.x2 = box.x2 - box.x1;
|
||||||
|
childBox.y2 = box.y2 - box.y1;
|
||||||
|
this._text.allocate(childBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
setContent: function(notification) {
|
||||||
|
this._iconBox.child = notification.icon;
|
||||||
|
|
||||||
|
// Support <b>, <i>, and <u>, escape anything else
|
||||||
|
// so it displays as raw markup.
|
||||||
|
let markup = notification.text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, "<$1");
|
||||||
|
this._text.clutter_text.set_markup(markup);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Source(id, createIcon) {
|
||||||
|
this._init(id, createIcon);
|
||||||
|
}
|
||||||
|
|
||||||
|
Source.prototype = {
|
||||||
|
_init: function(id, createIcon) {
|
||||||
|
this.id = id;
|
||||||
|
if (createIcon)
|
||||||
|
this.createIcon = createIcon;
|
||||||
|
},
|
||||||
|
|
||||||
|
// This can be overridden by a subclass, or by the createIcon
|
||||||
|
// parameter to _init()
|
||||||
|
createIcon: function(size) {
|
||||||
|
throw new Error('no implementation of createIcon in ' + this);
|
||||||
|
},
|
||||||
|
|
||||||
|
notify: function(text) {
|
||||||
|
Main.messageTray.showNotification(new Notification(this.createIcon(ICON_SIZE), text));
|
||||||
|
},
|
||||||
|
|
||||||
|
clicked: function() {
|
||||||
|
this.emit('clicked');
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.emit('destroy');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(Source.prototype);
|
||||||
|
|
||||||
|
function MessageTray() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageTray.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ name: 'message-tray',
|
||||||
|
reactive: true });
|
||||||
|
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
|
this.actor.x = 0;
|
||||||
|
this.actor.y = primary.height - 1;
|
||||||
|
|
||||||
|
this.actor.width = primary.width;
|
||||||
|
|
||||||
|
this._summaryBin = new St.Bin({ x_align: St.Align.END });
|
||||||
|
this.actor.add(this._summaryBin, { expand: true });
|
||||||
|
this._summaryBin.hide();
|
||||||
|
|
||||||
|
this._notificationBox = new NotificationBox();
|
||||||
|
this._notificationQueue = [];
|
||||||
|
this.actor.add(this._notificationBox.actor);
|
||||||
|
this._notificationBox.actor.hide();
|
||||||
|
|
||||||
|
Main.chrome.addActor(this.actor, { affectsStruts: false });
|
||||||
|
|
||||||
|
this.actor.connect('enter-event',
|
||||||
|
Lang.bind(this, this._onMessageTrayEntered));
|
||||||
|
this.actor.connect('leave-event',
|
||||||
|
Lang.bind(this, this._onMessageTrayLeft));
|
||||||
|
this._isShowing = false;
|
||||||
|
this.actor.show();
|
||||||
|
|
||||||
|
this._summary = new St.BoxLayout({ name: 'summary-mode' });
|
||||||
|
this._summaryBin.child = this._summary;
|
||||||
|
|
||||||
|
this._sources = {};
|
||||||
|
this._icons = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
contains: function(source) {
|
||||||
|
return this._sources.hasOwnProperty(source.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
add: function(source) {
|
||||||
|
if (this.contains(source)) {
|
||||||
|
log('Trying to re-add source ' + source.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconBox = new St.Bin({ reactive: true });
|
||||||
|
iconBox.child = source.createIcon(ICON_SIZE);
|
||||||
|
this._summary.insert_actor(iconBox, 0);
|
||||||
|
this._icons[source.id] = iconBox;
|
||||||
|
this._sources[source.id] = source;
|
||||||
|
|
||||||
|
iconBox.connect('button-release-event', Lang.bind(this,
|
||||||
|
function () {
|
||||||
|
source.clicked();
|
||||||
|
}));
|
||||||
|
|
||||||
|
source.connect('destroy', Lang.bind(this,
|
||||||
|
function () {
|
||||||
|
this.remove(source);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function(source) {
|
||||||
|
if (!this.contains(source))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._summary.remove_actor(this._icons[source.id]);
|
||||||
|
delete this._icons[source.id];
|
||||||
|
delete this._sources[source.id];
|
||||||
|
},
|
||||||
|
|
||||||
|
getSource: function(id) {
|
||||||
|
return this._sources[id];
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMessageTrayEntered: function() {
|
||||||
|
// Don't hide the message tray after a timeout if the user has moved the mouse over it.
|
||||||
|
// We might have a timeout in place if the user moved the mouse away from the message tray for a very short period of time
|
||||||
|
// or if we are showing a notification.
|
||||||
|
if (this._hideTimeoutId > 0)
|
||||||
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
|
||||||
|
if (this._isShowing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the message tray was not already showing, we'll show it in the summary mode.
|
||||||
|
this._summaryBin.show();
|
||||||
|
this._show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMessageTrayLeft: function() {
|
||||||
|
if (!this._isShowing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We wait just a little before hiding the message tray in case the user will quickly move the mouse back over it.
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add(MESSAGE_TRAY_TIMEOUT * 1000, Lang.bind(this, this._hide));
|
||||||
|
},
|
||||||
|
|
||||||
|
_show: function() {
|
||||||
|
this._isShowing = true;
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ y: primary.height - this.actor.height,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: "easeOutQuad"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_hide: function() {
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
|
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
|
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ y: primary.height - 1,
|
||||||
|
time: ANIMATION_TIME,
|
||||||
|
transition: "easeOutQuad",
|
||||||
|
onComplete: this._hideComplete,
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideComplete: function() {
|
||||||
|
this._isShowing = false;
|
||||||
|
this._summaryBin.hide();
|
||||||
|
this._notificationBox.actor.hide();
|
||||||
|
if (this._notificationQueue.length > 0)
|
||||||
|
this.showNotification(this._notificationQueue.shift());
|
||||||
|
},
|
||||||
|
|
||||||
|
showNotification: function(notification) {
|
||||||
|
if (this._isShowing) {
|
||||||
|
this._notificationQueue.push(notification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._notificationBox.setContent(notification);
|
||||||
|
|
||||||
|
this._notificationBox.actor.x = Math.round((this.actor.width - this._notificationBox.actor.width) / 2);
|
||||||
|
this._notificationBox.actor.show();
|
||||||
|
|
||||||
|
// Because we set up the timeout before we do the animation, we add ANIMATION_TIME to NOTIFICATION_TIMEOUT, so that
|
||||||
|
// NOTIFICATION_TIMEOUT represents the time the notifiation is fully shown.
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add((NOTIFICATION_TIMEOUT + ANIMATION_TIME) * 1000, Lang.bind(this, this._hide));
|
||||||
|
|
||||||
|
this._show();
|
||||||
|
}
|
||||||
|
};
|
218
js/ui/notificationDaemon.js
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
let nextNotificationId = 1;
|
||||||
|
|
||||||
|
const NotificationDaemonIface = {
|
||||||
|
name: 'org.freedesktop.Notifications',
|
||||||
|
methods: [{ name: 'Notify',
|
||||||
|
inSignature: 'susssasa{sv}i',
|
||||||
|
outSignature: 'u'
|
||||||
|
},
|
||||||
|
{ name: 'CloseNotification',
|
||||||
|
inSignature: 'u',
|
||||||
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'GetCapabilities',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'as'
|
||||||
|
},
|
||||||
|
{ name: 'GetServerInformation',
|
||||||
|
inSignature: '',
|
||||||
|
outSignature: 'ssss'
|
||||||
|
}],
|
||||||
|
signals: [{ name: 'NotificationClosed',
|
||||||
|
inSignature: 'uu' },
|
||||||
|
{ name: 'ActionInvoked',
|
||||||
|
inSignature: 'us' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const NotificationClosedReason = {
|
||||||
|
EXPIRED: 1,
|
||||||
|
DISMISSED: 2,
|
||||||
|
APP_CLOSED: 3,
|
||||||
|
UNDEFINED: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
const Urgency = {
|
||||||
|
LOW: 0,
|
||||||
|
NORMAL: 1,
|
||||||
|
CRITICAL: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
function NotificationDaemon() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationDaemon.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
DBus.session.exportObject('/org/freedesktop/Notifications', this);
|
||||||
|
|
||||||
|
this._everAcquiredName = false;
|
||||||
|
DBus.session.acquire_name('org.freedesktop.Notifications',
|
||||||
|
// We pass MANY_INSTANCES so that if
|
||||||
|
// notification-daemon is running, we'll
|
||||||
|
// get queued behind it and then get the
|
||||||
|
// name after killing it below
|
||||||
|
DBus.MANY_INSTANCES,
|
||||||
|
Lang.bind(this, this._acquiredName),
|
||||||
|
Lang.bind(this, this._lostName));
|
||||||
|
},
|
||||||
|
|
||||||
|
_acquiredName: function() {
|
||||||
|
this._everAcquiredName = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_lostName: function() {
|
||||||
|
if (this._everAcquiredName)
|
||||||
|
log('Lost name org.freedesktop.Notifications!');
|
||||||
|
else if (GLib.getenv('GNOME_SHELL_NO_REPLACE'))
|
||||||
|
log('Failed to acquire org.freedesktop.Notifications');
|
||||||
|
else {
|
||||||
|
log('Failed to acquire org.freedesktop.Notifications; trying again');
|
||||||
|
|
||||||
|
// kill the notification-daemon. pkill is more portable
|
||||||
|
// than killall, but on Linux at least it won't match if
|
||||||
|
// you pass more than 15 characters of the process name...
|
||||||
|
// However, if you use the "-f" flag to match the entire
|
||||||
|
// command line, it will work, but we have to be careful
|
||||||
|
// in that case that we don't match "gedit
|
||||||
|
// notification-daemon.c" or whatever...
|
||||||
|
let p = new Shell.Process({ args: ['pkill', '-f',
|
||||||
|
'^([^ ]*/)?(notification-daemon|notify-osd)$']});
|
||||||
|
p.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_sourceId: function(id) {
|
||||||
|
return 'notification-' + id;
|
||||||
|
},
|
||||||
|
|
||||||
|
Notify: function(appName, replacesId, icon, summary, body,
|
||||||
|
actions, hints, timeout) {
|
||||||
|
let id, source = null;
|
||||||
|
|
||||||
|
if (replacesId != 0) {
|
||||||
|
id = replacesId;
|
||||||
|
source = Main.messageTray.getSource(this._sourceId(id));
|
||||||
|
// source may be null if the current source was destroyed
|
||||||
|
// right as the client sent the new notification
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source == null) {
|
||||||
|
id = nextNotificationId++;
|
||||||
|
|
||||||
|
source = new Source(this._sourceId(id), icon, hints);
|
||||||
|
Main.messageTray.add(source);
|
||||||
|
|
||||||
|
source.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
source.destroy();
|
||||||
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = GLib.markup_escape_text(summary, -1);
|
||||||
|
if (body)
|
||||||
|
source.notify('<b>' + summary + '</b>: ' + body);
|
||||||
|
else
|
||||||
|
source.notify('<b>' + summary + '</b>');
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
|
||||||
|
CloseNotification: function(id) {
|
||||||
|
let source = Main.messageTray.getSource(this._sourceId(id));
|
||||||
|
if (source)
|
||||||
|
source.destroy();
|
||||||
|
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
|
||||||
|
},
|
||||||
|
|
||||||
|
GetCapabilities: function() {
|
||||||
|
return [
|
||||||
|
// 'actions',
|
||||||
|
'body',
|
||||||
|
// 'body-hyperlinks',
|
||||||
|
// 'body-images',
|
||||||
|
'body-markup',
|
||||||
|
// 'icon-multi',
|
||||||
|
'icon-static'
|
||||||
|
// 'sound',
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
GetServerInformation: function() {
|
||||||
|
return [
|
||||||
|
'GNOME Shell',
|
||||||
|
'GNOME',
|
||||||
|
'0.1', // FIXME, get this from somewhere
|
||||||
|
'1.0'
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
_emitNotificationClosed: function(id, reason) {
|
||||||
|
DBus.session.emit_signal('/org/freedesktop/Notifications',
|
||||||
|
'org.freedesktop.Notifications',
|
||||||
|
'NotificationClosed', 'uu',
|
||||||
|
[id, reason]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
|
||||||
|
|
||||||
|
function Source(sourceId, icon, hints) {
|
||||||
|
this._init(sourceId, icon, hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
Source.prototype = {
|
||||||
|
__proto__: MessageTray.Source.prototype,
|
||||||
|
|
||||||
|
_init: function(sourceId, icon, hints) {
|
||||||
|
MessageTray.Source.prototype._init.call(this, sourceId);
|
||||||
|
|
||||||
|
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||||
|
|
||||||
|
this._icon = icon;
|
||||||
|
this._iconData = hints.icon_data;
|
||||||
|
this._urgency = hints.urgency;
|
||||||
|
},
|
||||||
|
|
||||||
|
createIcon: function(size) {
|
||||||
|
let textureCache = Shell.TextureCache.get_default();
|
||||||
|
|
||||||
|
if (this._icon) {
|
||||||
|
if (this._icon.substr(0, 7) == 'file://')
|
||||||
|
return textureCache.load_uri_async(this._icon, size, size);
|
||||||
|
else if (this._icon[0] == '/') {
|
||||||
|
let uri = GLib.filename_to_uri(this._icon, null);
|
||||||
|
return textureCache.load_uri_async(uri, size, size);
|
||||||
|
} else
|
||||||
|
return textureCache.load_icon_name(this._icon, size);
|
||||||
|
} else if (this._iconData) {
|
||||||
|
let [width, height, rowStride, hasAlpha,
|
||||||
|
bitsPerSample, nChannels, data] = this._iconData;
|
||||||
|
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
||||||
|
width, height, rowStride, size);
|
||||||
|
} else {
|
||||||
|
let stockIcon;
|
||||||
|
switch (this._urgency) {
|
||||||
|
case Urgency.LOW:
|
||||||
|
case Urgency.NORMAL:
|
||||||
|
stockIcon = 'gtk-dialog-info';
|
||||||
|
break;
|
||||||
|
case Urgency.CRITICAL:
|
||||||
|
stockIcon = 'gtk-dialog-error';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return textureCache.load_icon_name(stockIcon, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -12,7 +12,6 @@ const Lang = imports.lang;
|
|||||||
const AppDisplay = imports.ui.appDisplay;
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
const DocDisplay = imports.ui.docDisplay;
|
const DocDisplay = imports.ui.docDisplay;
|
||||||
const GenericDisplay = imports.ui.genericDisplay;
|
const GenericDisplay = imports.ui.genericDisplay;
|
||||||
const Link = imports.ui.link;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Dash = imports.ui.dash;
|
const Dash = imports.ui.dash;
|
||||||
@ -123,8 +122,7 @@ Overview.prototype = {
|
|||||||
|
|
||||||
// Container to hold popup pane chrome.
|
// Container to hold popup pane chrome.
|
||||||
this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this._paneContainer = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
spacing: 6
|
spacing: 6 });
|
||||||
});
|
|
||||||
// Note here we explicitly don't set the paneContainer to be reactive yet; that's done
|
// Note here we explicitly don't set the paneContainer to be reactive yet; that's done
|
||||||
// inside the notify::visible handler on panes.
|
// inside the notify::visible handler on panes.
|
||||||
this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) {
|
this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) {
|
||||||
@ -142,63 +140,68 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_recalculateGridSizes: function () {
|
_recalculateGridSizes: function () {
|
||||||
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
let primary = global.get_primary_monitor();
|
||||||
(global.screen_height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
|
||||||
|
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
|
||||||
|
|
||||||
// We divide the screen into an imaginary grid which helps us determine the layout of
|
// We divide the screen into an imaginary grid which helps us determine the layout of
|
||||||
// different visual components.
|
// different visual components.
|
||||||
if (wideScreen) {
|
if (wideScreen) {
|
||||||
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
|
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN);
|
||||||
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
|
displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN);
|
||||||
} else {
|
} else {
|
||||||
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
|
displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN);
|
||||||
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
|
displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
relayout: function () {
|
relayout: function () {
|
||||||
let screenHeight = global.screen_height;
|
let primary = global.get_primary_monitor();
|
||||||
let screenWidth = global.screen_width;
|
|
||||||
|
this._group.set_position(primary.x, primary.y);
|
||||||
|
|
||||||
let contentY = Panel.PANEL_HEIGHT;
|
let contentY = Panel.PANEL_HEIGHT;
|
||||||
let contentHeight = screenHeight - contentY;
|
let contentHeight = primary.height - contentY;
|
||||||
|
|
||||||
this._coverPane.set_position(0, contentY);
|
this._coverPane.set_position(0, contentY);
|
||||||
this._coverPane.set_size(screenWidth, contentHeight);
|
this._coverPane.set_size(primary.width, contentHeight);
|
||||||
|
|
||||||
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
|
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||||
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
|
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
|
||||||
|
|
||||||
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
|
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
|
||||||
- WORKSPACE_GRID_PADDING * 2;
|
- WORKSPACE_GRID_PADDING * 2;
|
||||||
// We scale the vertical padding by (screenHeight / screenWidth)
|
// We scale the vertical padding by (primary.height / primary.width)
|
||||||
// so that the workspace preserves its aspect ratio.
|
// so that the workspace preserves its aspect ratio.
|
||||||
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
|
this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed
|
||||||
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
|
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2);
|
||||||
|
|
||||||
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
|
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
|
||||||
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
|
this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width));
|
||||||
|
|
||||||
this._dash.actor.set_position(0, contentY);
|
this._dash.actor.set_position(0, contentY);
|
||||||
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
|
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
|
||||||
this._dash.searchArea.height = this._workspacesY - contentY;
|
this._dash.searchArea.height = this._workspacesY - contentY;
|
||||||
this._dash.sectionArea.height = this._workspacesHeight;
|
this._dash.sectionArea.height = this._workspacesHeight;
|
||||||
|
this._dash.searchResults.actor.height = this._workspacesHeight;
|
||||||
|
|
||||||
// place the 'Add Workspace' button in the bottom row of the grid
|
// place the 'Add Workspace' button in the bottom row of the grid
|
||||||
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
|
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
|
||||||
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
|
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
|
||||||
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
|
this._addButtonY = primary.height - Math.floor(displayGridRowHeight * 4/5);
|
||||||
|
|
||||||
this._backOver.set_position(0, contentY);
|
// The parent (this._group) is positioned at the top left of the primary monitor
|
||||||
this._backOver.set_size(global.screen_width, contentHeight);
|
// while this._backOver occupies the entire screen.
|
||||||
|
this._backOver.set_position(- primary.x, - primary.y);
|
||||||
|
this._backOver.set_size(global.screen_width, global.screen_height);
|
||||||
|
|
||||||
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
|
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
|
||||||
contentY);
|
this._workspacesY);
|
||||||
// Dynamic width
|
// Dynamic width
|
||||||
this._paneContainer.height = contentHeight;
|
this._paneContainer.height = this._workspacesHeight;
|
||||||
|
|
||||||
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
|
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
|
||||||
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
|
this._transparentBackground.set_size(primary.width - this._paneContainer.x,
|
||||||
this._paneContainer.height);
|
this._paneContainer.height);
|
||||||
|
|
||||||
if (this._activeDisplayPane != null)
|
if (this._activeDisplayPane != null)
|
||||||
@ -224,6 +227,7 @@ Overview.prototype = {
|
|||||||
this._activeDisplayPane.close();
|
this._activeDisplayPane.close();
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
this._workspaces.actor.opacity = 64;
|
||||||
} else if (pane == this._activeDisplayPane) {
|
} else if (pane == this._activeDisplayPane) {
|
||||||
this._activeDisplayPane = null;
|
this._activeDisplayPane = null;
|
||||||
if (backgroundEventId != null) {
|
if (backgroundEventId != null) {
|
||||||
@ -232,6 +236,7 @@ Overview.prototype = {
|
|||||||
}
|
}
|
||||||
this._transparentBackground.lower_bottom();
|
this._transparentBackground.lower_bottom();
|
||||||
this._paneContainer.lower_bottom();
|
this._paneContainer.lower_bottom();
|
||||||
|
this._workspaces.actor.opacity = 255;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -243,7 +248,7 @@ Overview.prototype = {
|
|||||||
// This allows the user to place the item on any workspace.
|
// This allows the user to place the item on any workspace.
|
||||||
handleDragOver : function(source, actor, x, y, time) {
|
handleDragOver : function(source, actor, x, y, time) {
|
||||||
if (source instanceof GenericDisplay.GenericDisplayItem
|
if (source instanceof GenericDisplay.GenericDisplayItem
|
||||||
|| source instanceof AppDisplay.BaseWellItem) {
|
|| source instanceof AppDisplay.AppIcon) {
|
||||||
if (this._activeDisplayPane != null)
|
if (this._activeDisplayPane != null)
|
||||||
this._activeDisplayPane.close();
|
this._activeDisplayPane.close();
|
||||||
return true;
|
return true;
|
||||||
@ -322,9 +327,10 @@ Overview.prototype = {
|
|||||||
// The opposite transition is used in hide().
|
// The opposite transition is used in hide().
|
||||||
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
|
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
|
||||||
[this._group.x, this._group.y] = this.getZoomedInPosition();
|
[this._group.x, this._group.y] = this.getZoomedInPosition();
|
||||||
|
let primary = global.get_primary_monitor();
|
||||||
Tweener.addTween(this._group,
|
Tweener.addTween(this._group,
|
||||||
{ x: 0,
|
{ x: primary.x,
|
||||||
y: 0,
|
y: primary.y,
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
@ -355,6 +361,10 @@ Overview.prototype = {
|
|||||||
this._activeDisplayPane.close();
|
this._activeDisplayPane.close();
|
||||||
this._workspaces.hide();
|
this._workspaces.hide();
|
||||||
|
|
||||||
|
this._addButton.actor.destroy();
|
||||||
|
this._addButton.actor = null;
|
||||||
|
this._addButton = null;
|
||||||
|
|
||||||
// Create a zoom in effect by transforming the Overview group so that
|
// Create a zoom in effect by transforming the Overview group so that
|
||||||
// the active workspace fills up the whole screen. The opposite
|
// the active workspace fills up the whole screen. The opposite
|
||||||
// transition is used in show().
|
// transition is used in show().
|
||||||
@ -447,7 +457,7 @@ Overview.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_addNewWorkspace: function() {
|
_addNewWorkspace: function() {
|
||||||
global.screen.append_new_workspace(false, Main.currentTime());
|
global.screen.append_new_workspace(false, global.get_current_time());
|
||||||
},
|
},
|
||||||
|
|
||||||
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
_acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) {
|
||||||
|
345
js/ui/panel.js
@ -7,45 +7,22 @@ const Lang = imports.lang;
|
|||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
const _ = Gettext.gettext;
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
const Button = imports.ui.button;
|
const Calendar = imports.ui.calendar;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const StatusMenu = imports.ui.statusMenu;
|
||||||
|
|
||||||
const PANEL_HEIGHT = 26;
|
const PANEL_HEIGHT = 26;
|
||||||
const TRAY_HEIGHT = PANEL_HEIGHT - 1;
|
|
||||||
|
|
||||||
const DEFAULT_PADDING = 4;
|
const DEFAULT_PADDING = 4;
|
||||||
|
|
||||||
const PANEL_ICON_SIZE = 24;
|
const PANEL_ICON_SIZE = 24;
|
||||||
|
|
||||||
const BACKGROUND_TOP = new Clutter.Color();
|
|
||||||
BACKGROUND_TOP.from_pixel(0x161616ff);
|
|
||||||
const BACKGROUND_BOTTOM = new Clutter.Color();
|
|
||||||
BACKGROUND_BOTTOM.from_pixel(0x000000ff);
|
|
||||||
|
|
||||||
const PANEL_FOREGROUND_COLOR = new Clutter.Color();
|
|
||||||
PANEL_FOREGROUND_COLOR.from_pixel(0xffffffff);
|
|
||||||
const SN_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
SN_BACKGROUND_COLOR.from_pixel(0xffff00a0);
|
|
||||||
|
|
||||||
const TRANSPARENT_COLOR = new Clutter.Color();
|
|
||||||
TRANSPARENT_COLOR.from_pixel(0x00000000);
|
|
||||||
|
|
||||||
// Don't make the mouse hover effect visible to the user for a menu feel.
|
|
||||||
const PANEL_BUTTON_COLOR = new Clutter.Color();
|
|
||||||
PANEL_BUTTON_COLOR.from_pixel(0x00000000);
|
|
||||||
|
|
||||||
// Lighten pressed buttons; darkening has no effect on a black background.
|
|
||||||
const PRESSED_BUTTON_BACKGROUND_COLOR = new Clutter.Color();
|
|
||||||
PRESSED_BUTTON_BACKGROUND_COLOR.from_pixel(0x324c6ffa);
|
|
||||||
|
|
||||||
const DEFAULT_FONT = 'Sans 16px';
|
|
||||||
|
|
||||||
const TRAY_PADDING = 0;
|
|
||||||
// See comments around _recomputeTraySize
|
// See comments around _recomputeTraySize
|
||||||
const TRAY_SPACING = 14;
|
const TRAY_SPACING = 14;
|
||||||
const TRAY_SPACING_MIN = 8;
|
const TRAY_SPACING_MIN = 8;
|
||||||
@ -53,12 +30,21 @@ const TRAY_SPACING_MIN = 8;
|
|||||||
// Used for the tray icon container with gtk pre-2.16, which doesn't
|
// Used for the tray icon container with gtk pre-2.16, which doesn't
|
||||||
// fully support tray icon transparency
|
// fully support tray icon transparency
|
||||||
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
|
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
|
||||||
TRAY_BACKGROUND_COLOR.from_pixel(0xefefefff);
|
TRAY_BACKGROUND_COLOR.from_pixel(0x0b0b0bff);
|
||||||
const TRAY_BORDER_COLOR = new Clutter.Color();
|
const TRAY_BORDER_COLOR = new Clutter.Color();
|
||||||
TRAY_BORDER_COLOR.from_pixel(0x00000033);
|
TRAY_BORDER_COLOR.from_pixel(0x00000033);
|
||||||
const TRAY_CORNER_RADIUS = 5;
|
const TRAY_CORNER_RADIUS = 5;
|
||||||
const TRAY_BORDER_WIDTH = 0;
|
const TRAY_BORDER_WIDTH = 0;
|
||||||
|
|
||||||
|
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||||
|
|
||||||
|
const STANDARD_TRAY_ICON_ORDER = ['keyboard', 'volume', 'bluetooth', 'network', 'battery']
|
||||||
|
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||||
|
'bluetooth-applet': 'bluetooth',
|
||||||
|
'gnome-volume-control-applet': 'volume',
|
||||||
|
'nm-applet': 'network',
|
||||||
|
'gnome-power-manager': 'battery'
|
||||||
|
};
|
||||||
|
|
||||||
function AppPanelMenu() {
|
function AppPanelMenu() {
|
||||||
this._init();
|
this._init();
|
||||||
@ -72,24 +58,14 @@ AppPanelMenu.prototype = {
|
|||||||
this._activeSequence = null;
|
this._activeSequence = null;
|
||||||
this._startupSequences = {};
|
this._startupSequences = {};
|
||||||
|
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this.actor = new St.BoxLayout({ name: 'appMenu' });
|
||||||
spacing: DEFAULT_PADDING,
|
this._iconBox = new St.Bin({ name: 'appMenuIcon' });
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
this.actor.add(this._iconBox);
|
||||||
this._iconBox = new Big.Box({ width: PANEL_ICON_SIZE, height: PANEL_ICON_SIZE,
|
this._label = new St.Label();
|
||||||
x_align: Big.BoxAlignment.CENTER,
|
this.actor.add(this._label, { expand: true, y_fill: false });
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
|
||||||
this.actor.append(this._iconBox, Big.BoxPackFlags.NONE);
|
|
||||||
let labelBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
|
||||||
this._label = new Clutter.Text({ font_name: DEFAULT_FONT,
|
|
||||||
color: PANEL_FOREGROUND_COLOR,
|
|
||||||
text: "" });
|
|
||||||
labelBox.append(this._label, Big.BoxPackFlags.EXPAND);
|
|
||||||
this.actor.append(labelBox, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
this._startupBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this._startupBox = new St.BoxLayout();
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
this.actor.add(this._startupBox);
|
||||||
this.actor.append(this._startupBox, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
this.actor.opacity = 255;
|
this.actor.opacity = 255;
|
||||||
@ -98,34 +74,26 @@ AppPanelMenu.prototype = {
|
|||||||
this.actor.opacity = 192;
|
this.actor.opacity = 192;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._metaDisplay.connect('notify::focus-window', Lang.bind(this, this._sync));
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
tracker.connect('notify::focus-app', Lang.bind(this, this._sync));
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
tracker.connect('startup-sequence-changed', Lang.bind(this, this._sync));
|
||||||
appMonitor.connect('startup-sequence-changed', Lang.bind(this, this._sync));
|
// For now just resync on all running state changes; this is mainly to handle
|
||||||
// For now just resync on application add/remove; this is mainly to handle
|
|
||||||
// cases where the focused window's application changes without the focus
|
// cases where the focused window's application changes without the focus
|
||||||
// changing. An example case is how we map Firefox based on the window
|
// changing. An example case is how we map Firefox based on the window
|
||||||
// title which is a dynamic property.
|
// title which is a dynamic property.
|
||||||
appMonitor.connect('app-added', Lang.bind(this, this._sync));
|
tracker.connect('app-running-changed', Lang.bind(this, this._sync));
|
||||||
appMonitor.connect('app-removed', Lang.bind(this, this._sync));
|
|
||||||
|
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
_sync: function() {
|
_sync: function() {
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
|
||||||
let focusWindow = this._metaDisplay.get_focus_window();
|
let focusedApp = tracker.focus_app;
|
||||||
let focusedApp;
|
|
||||||
if (focusWindow == null) {
|
|
||||||
focusedApp = null;
|
|
||||||
} else {
|
|
||||||
focusedApp = appMonitor.get_window_app(focusWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastSequence = null;
|
let lastSequence = null;
|
||||||
if (focusedApp == null) {
|
if (focusedApp == null) {
|
||||||
let sequences = appMonitor.get_startup_sequences();
|
let sequences = tracker.get_startup_sequences();
|
||||||
if (sequences.length > 0)
|
if (sequences.length > 0)
|
||||||
lastSequence = sequences[sequences.length - 1];
|
lastSequence = sequences[sequences.length - 1];
|
||||||
}
|
}
|
||||||
@ -141,19 +109,22 @@ AppPanelMenu.prototype = {
|
|||||||
this._focusedApp = focusedApp;
|
this._focusedApp = focusedApp;
|
||||||
this._activeSequence = lastSequence;
|
this._activeSequence = lastSequence;
|
||||||
|
|
||||||
this._iconBox.remove_all();
|
if (this._iconBox.child != null)
|
||||||
|
this._iconBox.child.destroy();
|
||||||
this._iconBox.hide();
|
this._iconBox.hide();
|
||||||
this._label.text = '';
|
this._label.set_text('');
|
||||||
if (this._focusedApp != null) {
|
if (this._focusedApp != null) {
|
||||||
let icon = focusedApp.create_icon_texture(PANEL_ICON_SIZE);
|
let icon = this._focusedApp.create_icon_texture(PANEL_ICON_SIZE);
|
||||||
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
|
this._iconBox.set_child(icon);
|
||||||
this._iconBox.show();
|
this._iconBox.show();
|
||||||
this._label.text = focusedApp.get_name();
|
let appName = this._focusedApp.get_name();
|
||||||
|
// Use _set_text to work around http://bugzilla.openedhand.com/show_bug.cgi?id=1851
|
||||||
|
this._label.set_text(appName);
|
||||||
} else if (this._activeSequence != null) {
|
} else if (this._activeSequence != null) {
|
||||||
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
|
let icon = this._activeSequence.create_icon(PANEL_ICON_SIZE);
|
||||||
this._iconBox.append(icon, Big.BoxPackFlags.NONE);
|
this._iconBox.set_child(icon);
|
||||||
this._iconBox.show();
|
this._iconBox.show();
|
||||||
this._label.text = this._activeSequence.get_name();
|
this._label.set_text(this._activeSequence.get_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
@ -169,30 +140,17 @@ function Panel() {
|
|||||||
Panel.prototype = {
|
Panel.prototype = {
|
||||||
_init : function() {
|
_init : function() {
|
||||||
|
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL
|
this.actor = new St.BoxLayout({ name: 'panel' });
|
||||||
});
|
this.actor._delegate = this;
|
||||||
let backgroundGradient = Shell.create_vertical_gradient(BACKGROUND_TOP,
|
|
||||||
BACKGROUND_BOTTOM);
|
|
||||||
this.actor.connect('notify::allocation', Lang.bind(this, function () {
|
|
||||||
let [width, height] = this.actor.get_size();
|
|
||||||
backgroundGradient.set_size(width, height);
|
|
||||||
}));
|
|
||||||
this.actor.add_actor(backgroundGradient);
|
|
||||||
|
|
||||||
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||||||
spacing: DEFAULT_PADDING,
|
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||||
padding_right: DEFAULT_PADDING });
|
|
||||||
this._centerBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
|
||||||
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
|
||||||
padding_left: DEFAULT_PADDING });
|
|
||||||
|
|
||||||
/* This box container ensures that the centerBox is positioned in the *absolute*
|
/* This box container ensures that the centerBox is positioned in the *absolute*
|
||||||
* center, but can be pushed aside if necessary. */
|
* center, but can be pushed aside if necessary. */
|
||||||
this._boxContainer = new Shell.GenericContainer();
|
this._boxContainer = new Shell.GenericContainer();
|
||||||
this.actor.append(this._boxContainer, Big.BoxPackFlags.EXPAND);
|
this.actor.add(this._boxContainer, { expand: true });
|
||||||
this._boxContainer.add_actor(this._leftBox);
|
this._boxContainer.add_actor(this._leftBox);
|
||||||
this._boxContainer.add_actor(this._centerBox);
|
this._boxContainer.add_actor(this._centerBox);
|
||||||
this._boxContainer.add_actor(this._rightBox);
|
this._boxContainer.add_actor(this._rightBox);
|
||||||
@ -264,13 +222,16 @@ Panel.prototype = {
|
|||||||
this._rightBox.allocate(childBox, flags);
|
this._rightBox.allocate(childBox, flags);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/* left side */
|
/* Button on the left side of the panel. */
|
||||||
|
/* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
|
||||||
|
let label = new St.Label({ text: _("Activities") });
|
||||||
|
this.button = new St.Clickable({ name: 'panelActivities',
|
||||||
|
style_class: 'panel-button',
|
||||||
|
reactive: true });
|
||||||
|
this.button.set_child(label);
|
||||||
|
this.button.height = PANEL_HEIGHT;
|
||||||
|
|
||||||
this.button = new Button.Button(_("Activities"), PANEL_BUTTON_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR,
|
this._leftBox.add(this.button);
|
||||||
PANEL_FOREGROUND_COLOR, DEFAULT_FONT);
|
|
||||||
this.button.actor.height = PANEL_HEIGHT;
|
|
||||||
|
|
||||||
this._leftBox.append(this.button.actor, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
// We use this flag to mark the case where the user has entered the
|
// We use this flag to mark the case where the user has entered the
|
||||||
// hot corner and has not left both the hot corner and a surrounding
|
// hot corner and has not left both the hot corner and a surrounding
|
||||||
@ -278,16 +239,22 @@ Panel.prototype = {
|
|||||||
// multiple times due to an accidental jitter.
|
// multiple times due to an accidental jitter.
|
||||||
this._hotCornerEntered = false;
|
this._hotCornerEntered = false;
|
||||||
|
|
||||||
this._hotCornerEnvirons = new Clutter.Rectangle({ width: 3,
|
this._hotCornerEnvirons = new Clutter.Rectangle({ x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 3,
|
||||||
height: 3,
|
height: 3,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
|
|
||||||
this._hotCorner = new Clutter.Rectangle({ width: 1,
|
this._hotCorner = new Clutter.Rectangle({ x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
|
|
||||||
|
this._hotCornerActivationTime = 0;
|
||||||
|
|
||||||
this._hotCornerEnvirons.connect('leave-event',
|
this._hotCornerEnvirons.connect('leave-event',
|
||||||
Lang.bind(this, this._onHotCornerEnvironsLeft));
|
Lang.bind(this, this._onHotCornerEnvironsLeft));
|
||||||
// Clicking on the hot corner environs should result in the same bahavior
|
// Clicking on the hot corner environs should result in the same bahavior
|
||||||
@ -305,29 +272,35 @@ Panel.prototype = {
|
|||||||
this._hotCorner.connect('leave-event',
|
this._hotCorner.connect('leave-event',
|
||||||
Lang.bind(this, this._onHotCornerLeft));
|
Lang.bind(this, this._onHotCornerLeft));
|
||||||
|
|
||||||
this._leftBox.append(this._hotCornerEnvirons, Big.BoxPackFlags.FIXED);
|
this._leftBox.add(this._hotCornerEnvirons);
|
||||||
this._leftBox.append(this._hotCorner, Big.BoxPackFlags.FIXED);
|
this._leftBox.add(this._hotCorner);
|
||||||
|
|
||||||
let appMenu = new AppPanelMenu();
|
let appMenu = new AppPanelMenu();
|
||||||
this._leftBox.append(appMenu.actor, Big.BoxPackFlags.NONE);
|
this._leftBox.add(appMenu.actor);
|
||||||
|
|
||||||
/* center */
|
/* center */
|
||||||
|
|
||||||
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
|
let clockButton = new St.Button({ style_class: "panel-button",
|
||||||
color: PANEL_FOREGROUND_COLOR,
|
toggle_mode: true,
|
||||||
text: "" });
|
x_fill: true,
|
||||||
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
|
y_fill: true });
|
||||||
|
this._centerBox.add(clockButton, { y_fill: false });
|
||||||
|
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
|
||||||
|
|
||||||
|
this._clock = new St.Label();
|
||||||
|
clockButton.set_child(this._clock);
|
||||||
|
|
||||||
|
this._calendarPopup = null;
|
||||||
|
|
||||||
/* right */
|
/* right */
|
||||||
|
|
||||||
// The tray icons live in trayBox within trayContainer.
|
// The tray icons live in trayBox within trayContainer.
|
||||||
// The trayBox is hidden when there are no tray icons.
|
// The trayBox is hidden when there are no tray icons.
|
||||||
let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let trayContainer = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
y_align: Big.BoxAlignment.START });
|
y_align: Big.BoxAlignment.CENTER });
|
||||||
this._rightBox.append(trayContainer, Big.BoxPackFlags.NONE);
|
this._rightBox.add(trayContainer);
|
||||||
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
let trayBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
height: TRAY_HEIGHT,
|
height: PANEL_ICON_SIZE,
|
||||||
padding: TRAY_PADDING,
|
|
||||||
spacing: TRAY_SPACING });
|
spacing: TRAY_SPACING });
|
||||||
this._trayBox = trayBox;
|
this._trayBox = trayBox;
|
||||||
|
|
||||||
@ -345,14 +318,7 @@ Panel.prototype = {
|
|||||||
trayContainer.append(trayBox, Big.BoxPackFlags.NONE);
|
trayContainer.append(trayBox, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
|
this._traymanager = new Shell.TrayManager({ bg_color: TRAY_BACKGROUND_COLOR });
|
||||||
this._traymanager.connect('tray-icon-added',
|
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||||
Lang.bind(this, function(o, icon) {
|
|
||||||
trayBox.append(icon, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
// Make sure the trayBox is shown.
|
|
||||||
trayBox.show();
|
|
||||||
this._recomputeTraySize();
|
|
||||||
}));
|
|
||||||
this._traymanager.connect('tray-icon-removed',
|
this._traymanager.connect('tray-icon-removed',
|
||||||
Lang.bind(this, function(o, icon) {
|
Lang.bind(this, function(o, icon) {
|
||||||
trayBox.remove_actor(icon);
|
trayBox.remove_actor(icon);
|
||||||
@ -363,58 +329,48 @@ Panel.prototype = {
|
|||||||
}));
|
}));
|
||||||
this._traymanager.manage_stage(global.stage);
|
this._traymanager.manage_stage(global.stage);
|
||||||
|
|
||||||
let statusbox = new Big.Box();
|
let statusmenu = this._statusmenu = new StatusMenu.StatusMenu();
|
||||||
let statusmenu = this._statusmenu = new Shell.StatusMenu();
|
let statusbutton = new St.Clickable({ name: 'panelStatus',
|
||||||
statusmenu.get_icon().hide();
|
style_class: 'panel-button',
|
||||||
statusmenu.get_name().fontName = DEFAULT_FONT;
|
reactive: true });
|
||||||
statusmenu.get_name().color = PANEL_FOREGROUND_COLOR;
|
statusbutton.set_child(statusmenu.actor);
|
||||||
statusbox.append(this._statusmenu, Big.BoxPackFlags.NONE);
|
statusbutton.height = PANEL_HEIGHT;
|
||||||
let statusbutton = new Button.Button(statusbox,
|
statusbutton.connect('clicked', function (b, event) {
|
||||||
PANEL_BUTTON_COLOR,
|
statusmenu.toggle(event);
|
||||||
PRESSED_BUTTON_BACKGROUND_COLOR,
|
// The statusmenu might not pop up if it couldn't get a pointer grab
|
||||||
PANEL_FOREGROUND_COLOR);
|
if (statusmenu.isActive())
|
||||||
statusbutton.actor.height = PANEL_HEIGHT;
|
statusbutton.active = true;
|
||||||
statusbutton.actor.connect('button-press-event', function (b, e) {
|
return true;
|
||||||
if (e.get_button() == 1 && e.get_click_count() == 1) {
|
|
||||||
statusmenu.toggle(e);
|
|
||||||
// The statusmenu might not pop up if it couldn't get a pointer grab
|
|
||||||
if (statusmenu.is_active())
|
|
||||||
statusbutton.actor.active = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this._rightBox.append(statusbutton.actor, Big.BoxPackFlags.NONE);
|
this._rightBox.add(statusbutton);
|
||||||
// We get a deactivated event when the popup disappears
|
// We get a deactivated event when the popup disappears
|
||||||
this._statusmenu.connect('deactivated', function (sm) {
|
this._statusmenu.connect('deactivated', function (sm) {
|
||||||
statusbutton.actor.active = false;
|
statusbutton.active = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
|
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
|
||||||
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
|
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
|
||||||
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
||||||
// to switch to.
|
// to switch to.
|
||||||
this.button.actor.connect('button-press-event', function(b, e) {
|
this.button.connect('clicked', Lang.bind(this, function(b, event) {
|
||||||
if (e.get_button() == 1 && e.get_click_count() == 1) {
|
if (!Main.overview.animationInProgress) {
|
||||||
Main.overview.toggle();
|
this._maybeToggleOverviewOnClick();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
|
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
|
||||||
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
|
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
|
||||||
// and to be released when it is exited regardless of how it was triggered.
|
// and to be released when it is exited regardless of how it was triggered.
|
||||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||||
this.button.actor.active = true;
|
this.button.active = true;
|
||||||
}));
|
}));
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
this.button.actor.active = false;
|
this.button.active = false;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Main.chrome.addActor(this.actor);
|
Main.chrome.addActor(this.actor, { visibleInOverview: true });
|
||||||
Main.chrome.setVisibleInOverview(this.actor, true);
|
|
||||||
|
|
||||||
// Start the clock
|
// Start the clock
|
||||||
this._updateClock();
|
this._updateClock();
|
||||||
@ -429,6 +385,38 @@ Panel.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onTrayIconAdded: function(o, icon, wmClass) {
|
||||||
|
let role = STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass];
|
||||||
|
if (!role) {
|
||||||
|
// Unknown icons go first in undefined order
|
||||||
|
this._trayBox.prepend(icon, Big.BoxPackFlags.NONE);
|
||||||
|
} else {
|
||||||
|
icon._role = role;
|
||||||
|
// Figure out the index in our well-known order for this icon
|
||||||
|
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
|
||||||
|
icon._rolePosition = position;
|
||||||
|
let children = this._trayBox.get_children();
|
||||||
|
let i;
|
||||||
|
// Walk children backwards, until we find one that isn't
|
||||||
|
// well-known, or one where we should follow
|
||||||
|
for (i = children.length - 1; i >= 0; i--) {
|
||||||
|
let rolePosition = children[i]._rolePosition;
|
||||||
|
if (!rolePosition || position > rolePosition) {
|
||||||
|
this._trayBox.insert_after(icon, children[i], Big.BoxPackFlags.NONE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == -1) {
|
||||||
|
// If we didn't find a position, we must be first
|
||||||
|
this._trayBox.prepend(icon, Big.BoxPackFlags.NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the trayBox is shown.
|
||||||
|
this._trayBox.show();
|
||||||
|
this._recomputeTraySize();
|
||||||
|
},
|
||||||
|
|
||||||
// By default, tray icons have a spacing of TRAY_SPACING. However this
|
// By default, tray icons have a spacing of TRAY_SPACING. However this
|
||||||
// starts to fail if we have too many as can sadly happen; just jump down
|
// starts to fail if we have too many as can sadly happen; just jump down
|
||||||
// to a spacing of 8 if we're over 6.
|
// to a spacing of 8 if we're over 6.
|
||||||
@ -454,10 +442,21 @@ Panel.prototype = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_toggleCalendar: function(clockButton) {
|
||||||
|
if (clockButton.checked) {
|
||||||
|
if (this._calendarPopup == null)
|
||||||
|
this._calendarPopup = new CalendarPopup();
|
||||||
|
this._calendarPopup.show();
|
||||||
|
} else {
|
||||||
|
this._calendarPopup.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onHotCornerEntered : function() {
|
_onHotCornerEntered : function() {
|
||||||
if (!this._hotCornerEntered) {
|
if (!this._hotCornerEntered) {
|
||||||
this._hotCornerEntered = true;
|
this._hotCornerEntered = true;
|
||||||
if (!Main.overview.animationInProgress) {
|
if (!Main.overview.animationInProgress) {
|
||||||
|
this._hotCornerActivationTime = Date.now() / 1000;
|
||||||
Main.overview.toggle();
|
Main.overview.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,7 +465,7 @@ Panel.prototype = {
|
|||||||
|
|
||||||
_onHotCornerClicked : function() {
|
_onHotCornerClicked : function() {
|
||||||
if (!Main.overview.animationInProgress) {
|
if (!Main.overview.animationInProgress) {
|
||||||
Main.overview.toggle();
|
this._maybeToggleOverviewOnClick();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -483,5 +482,61 @@ Panel.prototype = {
|
|||||||
this._hotCornerEntered = false;
|
this._hotCornerEntered = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
|
||||||
|
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
|
||||||
|
// and clicked the Activities button.
|
||||||
|
_maybeToggleOverviewOnClick: function() {
|
||||||
|
if (this._hotCornerActivationTime == 0 || Date.now() / 1000 - this._hotCornerActivationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
||||||
|
Main.overview.toggle();
|
||||||
|
this._hotCornerActivationTime = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function CalendarPopup() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CalendarPopup.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
let panelActor = Main.panel.actor;
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ name: 'calendarPopup' });
|
||||||
|
|
||||||
|
this.calendar = new Calendar.Calendar();
|
||||||
|
this.actor.add(this.calendar.actor);
|
||||||
|
|
||||||
|
Main.chrome.addActor(this.actor, { visibleInOverview: true,
|
||||||
|
affectsStruts: false });
|
||||||
|
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
let panelActor = Main.panel.actor;
|
||||||
|
|
||||||
|
// Reset the calendar to today's date
|
||||||
|
this.calendar.setDate(new Date());
|
||||||
|
|
||||||
|
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
|
||||||
|
this.actor.lower(panelActor);
|
||||||
|
this.actor.show();
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ y: panelActor.y + panelActor.height,
|
||||||
|
time: 0.2,
|
||||||
|
transition: "easeOutQuad"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
let panelActor = Main.panel.actor;
|
||||||
|
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ y: panelActor.y + panelActor.height - this.actor.height,
|
||||||
|
time: 0.2,
|
||||||
|
transition: "easeOutQuad",
|
||||||
|
onComplete: function() { this.actor.hide(); },
|
||||||
|
onCompleteScope: this
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
574
js/ui/placeDisplay.js
Normal file
@ -0,0 +1,574 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Big = imports.gi.Big;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Pango = imports.gi.Pango;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const DND = imports.ui.dnd;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Search = imports.ui.search;
|
||||||
|
|
||||||
|
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||||
|
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||||
|
|
||||||
|
const PLACES_ICON_SIZE = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a place object, which is most normally a bookmark entry,
|
||||||
|
* a mount/volume, or a special place like the Home Folder, Computer, and Network.
|
||||||
|
*
|
||||||
|
* @name: String title
|
||||||
|
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
|
||||||
|
* @launch: A JavaScript callback to launch the entry
|
||||||
|
*/
|
||||||
|
function PlaceInfo(id, name, iconFactory, launch) {
|
||||||
|
this._init(id, name, iconFactory, launch);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaceInfo.prototype = {
|
||||||
|
_init: function(id, name, iconFactory, launch) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this._lowerName = name.toLowerCase();
|
||||||
|
this.iconFactory = iconFactory;
|
||||||
|
this.launch = launch;
|
||||||
|
},
|
||||||
|
|
||||||
|
matchTerms: function(terms) {
|
||||||
|
let mtype = Search.MatchType.NONE;
|
||||||
|
for (let i = 0; i < terms.length; i++) {
|
||||||
|
let term = terms[i];
|
||||||
|
let idx = this._lowerName.indexOf(term);
|
||||||
|
if (idx == 0)
|
||||||
|
return Search.MatchType.PREFIX;
|
||||||
|
else if (idx > 0)
|
||||||
|
mtype = Search.MatchType.SUBSTRING;
|
||||||
|
}
|
||||||
|
return mtype;
|
||||||
|
},
|
||||||
|
|
||||||
|
isRemovable: function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlaceDeviceInfo(mount) {
|
||||||
|
this._init(mount);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaceDeviceInfo.prototype = {
|
||||||
|
__proto__: PlaceInfo.prototype,
|
||||||
|
|
||||||
|
_init: function(mount) {
|
||||||
|
this._mount = mount;
|
||||||
|
this.name = mount.get_name();
|
||||||
|
this._lowerName = this.name.toLowerCase();
|
||||||
|
this.id = "mount:" + mount.get_root().get_uri();
|
||||||
|
},
|
||||||
|
|
||||||
|
iconFactory: function(size) {
|
||||||
|
let icon = this._mount.get_icon();
|
||||||
|
return Shell.TextureCache.get_default().load_gicon(icon, size);
|
||||||
|
},
|
||||||
|
|
||||||
|
launch: function() {
|
||||||
|
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||||
|
global.create_app_launch_context());
|
||||||
|
},
|
||||||
|
|
||||||
|
isRemovable: function() {
|
||||||
|
return this._mount.can_unmount();
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function() {
|
||||||
|
if (!this.isRemovable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._mount.unmount(0, null, Lang.bind(this, this._removeFinish), null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeFinish: function(o, res, data) {
|
||||||
|
this._mount.unmount_finish(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function PlacesManager() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacesManager.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
let gconf = Shell.GConf.get_default();
|
||||||
|
gconf.watch_directory(NAUTILUS_PREFS_DIR);
|
||||||
|
|
||||||
|
this._defaultPlaces = [];
|
||||||
|
this._mounts = [];
|
||||||
|
this._bookmarks = [];
|
||||||
|
this._isDesktopHome = false;
|
||||||
|
|
||||||
|
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||||
|
let homeUri = homeFile.get_uri();
|
||||||
|
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
||||||
|
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
||||||
|
this._home = new PlaceInfo('special:home', homeLabel,
|
||||||
|
function(size) {
|
||||||
|
return Shell.TextureCache.get_default().load_gicon(homeIcon, size);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
|
||||||
|
});
|
||||||
|
|
||||||
|
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||||
|
let desktopFile = Gio.file_new_for_path (desktopPath);
|
||||||
|
let desktopUri = desktopFile.get_uri();
|
||||||
|
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
||||||
|
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
||||||
|
this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
|
||||||
|
function(size) {
|
||||||
|
return Shell.TextureCache.get_default().load_gicon(desktopIcon, size);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
|
||||||
|
});
|
||||||
|
|
||||||
|
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||||
|
function (size) {
|
||||||
|
return Shell.TextureCache.get_default().load_icon_name("applications-internet", size);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||||
|
});
|
||||||
|
|
||||||
|
let networkApp = null;
|
||||||
|
try {
|
||||||
|
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
|
||||||
|
} catch(e) {
|
||||||
|
try {
|
||||||
|
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
|
||||||
|
} catch(e) {
|
||||||
|
log("Cannot create \"Network\" item, .desktop file not found or corrupt.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkApp != null) {
|
||||||
|
this._network = new PlaceInfo('special:network', networkApp.get_name(),
|
||||||
|
function(size) {
|
||||||
|
return networkApp.create_icon_texture(size);
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
networkApp.launch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._defaultPlaces.push(this._home);
|
||||||
|
|
||||||
|
if (!this._isDesktopHome)
|
||||||
|
this._defaultPlaces.push(this._desktopMenu);
|
||||||
|
|
||||||
|
if (this._network)
|
||||||
|
this._defaultPlaces.push(this._network);
|
||||||
|
|
||||||
|
this._defaultPlaces.push(this._connect);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
||||||
|
*/
|
||||||
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
||||||
|
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
||||||
|
this._updateDevices();
|
||||||
|
|
||||||
|
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
||||||
|
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
||||||
|
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||||
|
this._bookmarkTimeoutId = 0;
|
||||||
|
monitor.connect('changed', Lang.bind(this, function () {
|
||||||
|
if (this._bookmarkTimeoutId > 0)
|
||||||
|
return;
|
||||||
|
/* Defensive event compression */
|
||||||
|
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
||||||
|
this._bookmarkTimeoutId = 0;
|
||||||
|
this._reloadBookmarks();
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._reloadBookmarks();
|
||||||
|
this._updateDesktopMenuVisibility();
|
||||||
|
|
||||||
|
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDevices: function() {
|
||||||
|
this._mounts = [];
|
||||||
|
|
||||||
|
/* 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 mount = volumes[j].get_mount();
|
||||||
|
if(mount != null) {
|
||||||
|
this._addMount(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() != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let mount = volumes[i].get_mount();
|
||||||
|
if(mount != null) {
|
||||||
|
this._addMount(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;
|
||||||
|
|
||||||
|
this._addMount(mounts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We emit two signals, one for a generic 'all places' update
|
||||||
|
* and the other for one specific to mounts. We do this because
|
||||||
|
* clients like PlaceDisplay may only care about places in general
|
||||||
|
* being updated while clients like DashPlaceDisplay care which
|
||||||
|
* specific type of place got updated.
|
||||||
|
*/
|
||||||
|
this.emit('mounts-updated');
|
||||||
|
this.emit('places-updated');
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadBookmarks: function() {
|
||||||
|
|
||||||
|
this._bookmarks = [];
|
||||||
|
|
||||||
|
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let bookmarks = bookmarksContent.split('\n');
|
||||||
|
|
||||||
|
let bookmarksToLabel = {};
|
||||||
|
let bookmarksOrder = [];
|
||||||
|
for (let i = 0; i < bookmarks.length; i++) {
|
||||||
|
let bookmarkLine = bookmarks[i];
|
||||||
|
let components = bookmarkLine.split(' ');
|
||||||
|
let bookmark = components[0];
|
||||||
|
if (bookmark in bookmarksToLabel)
|
||||||
|
continue;
|
||||||
|
let label = null;
|
||||||
|
if (components.length > 1)
|
||||||
|
label = components.slice(1).join(' ');
|
||||||
|
bookmarksToLabel[bookmark] = label;
|
||||||
|
bookmarksOrder.push(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < bookmarksOrder.length; i++) {
|
||||||
|
let bookmark = bookmarksOrder[i];
|
||||||
|
let label = bookmarksToLabel[bookmark];
|
||||||
|
let file = Gio.file_new_for_uri(bookmark);
|
||||||
|
if (!file.query_exists(null))
|
||||||
|
continue;
|
||||||
|
if (label == null)
|
||||||
|
label = Shell.util_get_label_for_uri(bookmark);
|
||||||
|
if (label == null)
|
||||||
|
continue;
|
||||||
|
let icon = Shell.util_get_icon_for_uri(bookmark);
|
||||||
|
|
||||||
|
let item = new PlaceInfo('bookmark:' + bookmark, label,
|
||||||
|
function(size) {
|
||||||
|
return Shell.TextureCache.get_default().load_gicon(icon, size);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
|
||||||
|
});
|
||||||
|
this._bookmarks.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See comment in _updateDevices for explanation why there are two signals. */
|
||||||
|
this.emit('bookmarks-updated');
|
||||||
|
this.emit('places-updated');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDesktopMenuVisibility: function() {
|
||||||
|
let gconf = Shell.GConf.get_default();
|
||||||
|
this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
||||||
|
|
||||||
|
/* See comment in _updateDevices for explanation why there are two signals. */
|
||||||
|
this.emit('defaults-updated');
|
||||||
|
this.emit('places-updated');
|
||||||
|
},
|
||||||
|
|
||||||
|
_addMount: function(mount) {
|
||||||
|
let devItem = new PlaceDeviceInfo(mount);
|
||||||
|
this._mounts.push(devItem);
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllPlaces: function () {
|
||||||
|
return this.getDefaultPlaces().concat(this.getBookmarks(), this.getMounts());
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultPlaces: function () {
|
||||||
|
return this._defaultPlaces;
|
||||||
|
},
|
||||||
|
|
||||||
|
getBookmarks: function () {
|
||||||
|
return this._bookmarks;
|
||||||
|
},
|
||||||
|
|
||||||
|
getMounts: function () {
|
||||||
|
return this._mounts;
|
||||||
|
},
|
||||||
|
|
||||||
|
_lookupById: function(sourceArray, id) {
|
||||||
|
for (let i = 0; i < sourceArray.length; i++) {
|
||||||
|
let place = sourceArray[i];
|
||||||
|
if (place.id == id)
|
||||||
|
return place;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
lookupPlaceById: function(id) {
|
||||||
|
let colonIdx = id.indexOf(':');
|
||||||
|
let type = id.substring(0, colonIdx);
|
||||||
|
let sourceArray = null;
|
||||||
|
if (type == 'special')
|
||||||
|
sourceArray = this._defaultPlaces;
|
||||||
|
else if (type == 'mount')
|
||||||
|
sourceArray = this._mounts;
|
||||||
|
else if (type == 'bookmark')
|
||||||
|
sourceArray = this._bookmarks;
|
||||||
|
return this._lookupById(sourceArray, id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Signals.addSignalMethods(PlacesManager.prototype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An entry in the places menu.
|
||||||
|
* @info The corresponding PlaceInfo to populate this entry.
|
||||||
|
*/
|
||||||
|
function DashPlaceDisplayItem(info) {
|
||||||
|
this._init(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
DashPlaceDisplayItem.prototype = {
|
||||||
|
_init: function(info) {
|
||||||
|
this.name = info.name;
|
||||||
|
this._info = info;
|
||||||
|
this._icon = info.iconFactory(PLACES_ICON_SIZE);
|
||||||
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
|
spacing: 4 });
|
||||||
|
let text = new St.Button({ style_class: 'places-item',
|
||||||
|
label: info.name,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
text.connect('clicked', Lang.bind(this, this._onClicked));
|
||||||
|
let iconBox = new St.Bin({ child: this._icon, reactive: true });
|
||||||
|
iconBox.connect('button-release-event',
|
||||||
|
Lang.bind(this, this._onClicked));
|
||||||
|
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||||
|
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
|
if (info.isRemovable()) {
|
||||||
|
let removeIcon = Shell.TextureCache.get_default().load_icon_name ('media-eject', PLACES_ICON_SIZE);
|
||||||
|
let removeIconBox = new St.Button({ child: removeIcon,
|
||||||
|
reactive: true });
|
||||||
|
this.actor.append(removeIconBox, Big.BoxPackFlags.NONE);
|
||||||
|
removeIconBox.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._info.remove();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actor._delegate = this;
|
||||||
|
let draggable = DND.makeDraggable(this.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function(b) {
|
||||||
|
this._info.launch();
|
||||||
|
Main.overview.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
getDragActorSource: function() {
|
||||||
|
return this._icon;
|
||||||
|
},
|
||||||
|
|
||||||
|
getDragActor: function(stageX, stageY) {
|
||||||
|
return this._info.iconFactory(PLACES_ICON_SIZE);
|
||||||
|
},
|
||||||
|
|
||||||
|
//// Drag and drop methods ////
|
||||||
|
|
||||||
|
shellWorkspaceLaunch: function() {
|
||||||
|
this._info.launch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function DashPlaceDisplay() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
DashPlaceDisplay.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
|
||||||
|
// Places is divided semi-arbitrarily into left and right; a grid would
|
||||||
|
// look better in that there would be an even number of items left+right,
|
||||||
|
// but it seems like we want some sort of differentiation between actions
|
||||||
|
// like "Connect to server..." and regular folders
|
||||||
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
|
spacing: 4 });
|
||||||
|
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||||
|
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
|
||||||
|
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
||||||
|
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
|
// Subdivide left into actions and devices
|
||||||
|
this._actionsBox = new St.BoxLayout({ style_class: 'places-actions',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._devBox = new St.BoxLayout({ style_class: 'places-actions',
|
||||||
|
name: 'placesDevices',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._dirsBox = new St.BoxLayout({ style_class: 'places-actions',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
|
||||||
|
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
|
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
|
Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults));
|
||||||
|
Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks));
|
||||||
|
Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts));
|
||||||
|
|
||||||
|
this._updateDefaults();
|
||||||
|
this._updateMounts();
|
||||||
|
this._updateBookmarks();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDefaults: function() {
|
||||||
|
this._actionsBox.destroy_children();
|
||||||
|
|
||||||
|
let places = Main.placesManager.getDefaultPlaces();
|
||||||
|
for (let i = 0; i < places.length; i++)
|
||||||
|
this._actionsBox.add(new DashPlaceDisplayItem(places[i]).actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateMounts: function() {
|
||||||
|
this._devBox.destroy_children();
|
||||||
|
|
||||||
|
let places = Main.placesManager.getMounts();
|
||||||
|
for (let i = 0; i < places.length; i++)
|
||||||
|
this._devBox.add(new DashPlaceDisplayItem(places[i]).actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBookmarks: function() {
|
||||||
|
this._dirsBox.destroy_children();
|
||||||
|
|
||||||
|
let places = Main.placesManager.getBookmarks();
|
||||||
|
for (let i = 0; i < places.length; i ++)
|
||||||
|
this._dirsBox.add(new DashPlaceDisplayItem(places[i]).actor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Signals.addSignalMethods(DashPlaceDisplay.prototype);
|
||||||
|
|
||||||
|
function PlaceSearchProvider() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaceSearchProvider.prototype = {
|
||||||
|
__proto__: Search.SearchProvider.prototype,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
Search.SearchProvider.prototype._init.call(this, _("PLACES"));
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultMeta: function(resultId) {
|
||||||
|
let placeInfo = Main.placesManager.lookupPlaceById(resultId);
|
||||||
|
if (!placeInfo)
|
||||||
|
return null;
|
||||||
|
return { 'id': resultId,
|
||||||
|
'name': placeInfo.name,
|
||||||
|
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
|
||||||
|
},
|
||||||
|
|
||||||
|
activateResult: function(id) {
|
||||||
|
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
||||||
|
placeInfo.launch();
|
||||||
|
},
|
||||||
|
|
||||||
|
_compareResultMeta: function (idA, idB) {
|
||||||
|
let infoA = Main.placesManager.lookupPlaceById(idA);
|
||||||
|
let infoB = Main.placesManager.lookupPlaceById(idB);
|
||||||
|
return infoA.name.localeCompare(infoB.name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_searchPlaces: function(places, terms) {
|
||||||
|
let multipleResults = [];
|
||||||
|
let prefixResults = [];
|
||||||
|
let substringResults = [];
|
||||||
|
|
||||||
|
terms = terms.map(String.toLowerCase);
|
||||||
|
|
||||||
|
for (let i = 0; i < places.length; i++) {
|
||||||
|
let place = places[i];
|
||||||
|
let mtype = place.matchTerms(terms);
|
||||||
|
if (mtype == Search.MatchType.MULTIPLE)
|
||||||
|
multipleResults.push(place.id);
|
||||||
|
else if (mtype == Search.MatchType.PREFIX)
|
||||||
|
prefixResults.push(place.id);
|
||||||
|
else if (mtype == Search.MatchType.SUBSTRING)
|
||||||
|
substringResults.push(place.id);
|
||||||
|
}
|
||||||
|
multipleResults.sort(this._compareResultMeta);
|
||||||
|
prefixResults.sort(this._compareResultMeta);
|
||||||
|
substringResults.sort(this._compareResultMeta);
|
||||||
|
return multipleResults.concat(prefixResults.concat(substringResults));
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialResultSet: function(terms) {
|
||||||
|
let places = Main.placesManager.getAllPlaces();
|
||||||
|
return this._searchPlaces(places, terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubsearchResultSet: function(previousResults, terms) {
|
||||||
|
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
|
||||||
|
return this._searchPlaces(places, terms);
|
||||||
|
}
|
||||||
|
}
|
278
js/ui/places.js
@ -1,278 +0,0 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
||||||
|
|
||||||
const Big = imports.gi.Big;
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const Pango = imports.gi.Pango;
|
|
||||||
const GLib = imports.gi.GLib;
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Mainloop = imports.mainloop;
|
|
||||||
const Signals = imports.signals;
|
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const GenericDisplay = imports.ui.genericDisplay;
|
|
||||||
|
|
||||||
const PLACES_VSPACING = 8;
|
|
||||||
const PLACES_ICON_SIZE = 16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An entry in the places menu.
|
|
||||||
* @name: String title
|
|
||||||
* @iconFactory: A JavaScript callback which will create an icon texture
|
|
||||||
* @onActivate: A JavaScript callback to launch the entry
|
|
||||||
*/
|
|
||||||
function PlaceDisplay(name, iconFactory, onActivate) {
|
|
||||||
this._init(name, iconFactory, onActivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaceDisplay.prototype = {
|
|
||||||
_init : function(name, iconFactory, onActivate) {
|
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
reactive: true,
|
|
||||||
spacing: 4 });
|
|
||||||
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
|
|
||||||
onActivate(this);
|
|
||||||
Main.overview.hide();
|
|
||||||
}));
|
|
||||||
let text = new Clutter.Text({ font_name: "Sans 14px",
|
|
||||||
ellipsize: Pango.EllipsizeMode.END,
|
|
||||||
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
|
||||||
text: name });
|
|
||||||
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
|
||||||
this._icon = iconFactory();
|
|
||||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
|
||||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
|
||||||
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
this._iconFactory = iconFactory;
|
|
||||||
this._onActivate = onActivate;
|
|
||||||
|
|
||||||
this.actor._delegate = this;
|
|
||||||
let draggable = DND.makeDraggable(this.actor);
|
|
||||||
},
|
|
||||||
|
|
||||||
getDragActorSource: function() {
|
|
||||||
return this._icon;
|
|
||||||
},
|
|
||||||
|
|
||||||
getDragActor: function(stageX, stageY) {
|
|
||||||
return this._iconFactory();
|
|
||||||
},
|
|
||||||
|
|
||||||
//// Drag and drop methods ////
|
|
||||||
|
|
||||||
shellWorkspaceLaunch : function() {
|
|
||||||
this._onActivate();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Signals.addSignalMethods(PlaceDisplay.prototype);
|
|
||||||
|
|
||||||
function Places() {
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Places.prototype = {
|
|
||||||
_init : function() {
|
|
||||||
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
||||||
spacing: 4 });
|
|
||||||
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
||||||
spacing: PLACES_VSPACING });
|
|
||||||
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
|
|
||||||
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
||||||
spacing: PLACES_VSPACING });
|
|
||||||
|
|
||||||
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
||||||
spacing: PLACES_VSPACING });
|
|
||||||
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
|
|
||||||
|
|
||||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
|
||||||
let homeUri = homeFile.get_uri();
|
|
||||||
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
|
||||||
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
|
||||||
let home = new PlaceDisplay(homeLabel,
|
|
||||||
function() {
|
|
||||||
return Shell.TextureCache.get_default().load_gicon(homeIcon, PLACES_ICON_SIZE);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
|
||||||
});
|
|
||||||
|
|
||||||
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
this._menuBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
||||||
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
|
||||||
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
|
||||||
this._updateDevices();
|
|
||||||
|
|
||||||
let networkApp = null;
|
|
||||||
try {
|
|
||||||
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
|
|
||||||
} catch(e) {
|
|
||||||
try {
|
|
||||||
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
|
|
||||||
} catch(e) {
|
|
||||||
log("Cannot create \"Network\" item, .desktop file not found or corrupt.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkApp != null) {
|
|
||||||
let network = new PlaceDisplay(networkApp.get_name(),
|
|
||||||
function() {
|
|
||||||
return networkApp.create_icon_texture(PLACES_ICON_SIZE);
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
networkApp.launch();
|
|
||||||
});
|
|
||||||
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
let connect = new PlaceDisplay('Connect to...',
|
|
||||||
function () {
|
|
||||||
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
|
||||||
});
|
|
||||||
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
|
||||||
|
|
||||||
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
|
||||||
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
|
||||||
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
monitor.connect('changed', Lang.bind(this, function () {
|
|
||||||
if (this._bookmarkTimeoutId > 0)
|
|
||||||
return;
|
|
||||||
/* Defensive event compression */
|
|
||||||
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
|
||||||
this._bookmarkTimeoutId = 0;
|
|
||||||
this._reloadBookmarks();
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._reloadBookmarks();
|
|
||||||
},
|
|
||||||
|
|
||||||
_reloadBookmarks: function() {
|
|
||||||
|
|
||||||
this._dirsBox.remove_all();
|
|
||||||
|
|
||||||
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
|
||||||
return;
|
|
||||||
|
|
||||||
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let bookmarks = bookmarksContent.split('\n');
|
|
||||||
|
|
||||||
let bookmarksToLabel = {};
|
|
||||||
let bookmarksOrder = [];
|
|
||||||
for (let i = 0; i < bookmarks.length; i++) {
|
|
||||||
let bookmarkLine = bookmarks[i];
|
|
||||||
let components = bookmarkLine.split(' ');
|
|
||||||
let bookmark = components[0];
|
|
||||||
if (bookmark in bookmarksToLabel)
|
|
||||||
continue;
|
|
||||||
let label = null;
|
|
||||||
if (components.length > 1)
|
|
||||||
label = components.slice(1).join(' ');
|
|
||||||
bookmarksToLabel[bookmark] = label;
|
|
||||||
bookmarksOrder.push(bookmark);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < bookmarksOrder.length; i++) {
|
|
||||||
let bookmark = bookmarksOrder[i];
|
|
||||||
let label = bookmarksToLabel[bookmark];
|
|
||||||
let file = Gio.file_new_for_uri(bookmark);
|
|
||||||
if (!file.query_exists(null))
|
|
||||||
continue;
|
|
||||||
if (label == null)
|
|
||||||
label = Shell.util_get_label_for_uri(bookmark);
|
|
||||||
if (label == null)
|
|
||||||
continue;
|
|
||||||
let icon = Shell.util_get_icon_for_uri(bookmark);
|
|
||||||
|
|
||||||
let item = new PlaceDisplay(label,
|
|
||||||
function() {
|
|
||||||
return Shell.TextureCache.get_default().load_gicon(icon, PLACES_ICON_SIZE);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext());
|
|
||||||
});
|
|
||||||
this._dirsBox.append(item.actor, Big.BoxPackFlags.NONE);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateDevices: function() {
|
|
||||||
this._devBox.remove_all();
|
|
||||||
|
|
||||||
/* 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 mount = volumes[j].get_mount();
|
|
||||||
if(mount != null) {
|
|
||||||
this._addMount(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() != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let mount = volumes[i].get_mount();
|
|
||||||
if(mount != null) {
|
|
||||||
this._addMount(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;
|
|
||||||
|
|
||||||
this._addMount(mounts[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_addMount: function(mount) {
|
|
||||||
let mountLabel = mount.get_name();
|
|
||||||
let mountIcon = mount.get_icon();
|
|
||||||
let root = mount.get_root();
|
|
||||||
let mountUri = root.get_uri();
|
|
||||||
let devItem = new PlaceDisplay(mountLabel,
|
|
||||||
function() {
|
|
||||||
return Shell.TextureCache.get_default().load_gicon(mountIcon, PLACES_ICON_SIZE);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
|
|
||||||
});
|
|
||||||
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
Signals.addSignalMethods(Places.prototype);
|
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const Big = imports.gi.Big;
|
const Big = imports.gi.Big;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
@ -24,6 +25,144 @@ const DIALOG_WIDTH = 320;
|
|||||||
const DIALOG_PADDING = 6;
|
const DIALOG_PADDING = 6;
|
||||||
const ICON_SIZE = 24;
|
const ICON_SIZE = 24;
|
||||||
const ICON_BOX_SIZE = 36;
|
const ICON_BOX_SIZE = 36;
|
||||||
|
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
|
||||||
|
|
||||||
|
function CommandCompleter() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandCompleter.prototype = {
|
||||||
|
_init : function() {
|
||||||
|
this._changedCount = 0;
|
||||||
|
this._paths = GLib.getenv('PATH').split(':');
|
||||||
|
this._valid = false;
|
||||||
|
this._updateInProgress = false;
|
||||||
|
this._childs = new Array(this._paths.length);
|
||||||
|
this._monitors = new Array(this._paths.length);
|
||||||
|
for (let i = 0; i < this._paths.length; i++) {
|
||||||
|
this._childs[i] = [];
|
||||||
|
let file = Gio.file_new_for_path(this._paths[i]);
|
||||||
|
let info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
|
||||||
|
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this._paths[i] = file.get_path();
|
||||||
|
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
|
||||||
|
if (this._monitors[i] != null) {
|
||||||
|
this._monitors[i].connect("changed", Lang.bind(this, this._onChanged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._update(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGetEnumerateComplete : function(obj, res) {
|
||||||
|
this._enumerator = obj.enumerate_children_finish(res);
|
||||||
|
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNextFileComplete : function(obj, res) {
|
||||||
|
let files = obj.next_files_finish(res);
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
this._childs[this._i].push(files[i].get_name());
|
||||||
|
}
|
||||||
|
if (files.length) {
|
||||||
|
this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete), null);
|
||||||
|
} else {
|
||||||
|
this._enumerator.close(null);
|
||||||
|
this._enumerator = null;
|
||||||
|
this._update(this._i + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update : function() {
|
||||||
|
if (this._valid)
|
||||||
|
return;
|
||||||
|
this._update(0);
|
||||||
|
},
|
||||||
|
|
||||||
|
_update : function(i) {
|
||||||
|
if (i == 0 && this._updateInProgress)
|
||||||
|
return;
|
||||||
|
this._updateInProgress = true;
|
||||||
|
this._changedCount = 0;
|
||||||
|
this._i = i;
|
||||||
|
if (i >= this._paths.length) {
|
||||||
|
this._valid = true;
|
||||||
|
this._updateInProgress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let file = Gio.file_new_for_path(this._paths[i]);
|
||||||
|
this._childs[this._i] = [];
|
||||||
|
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete), null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onChanged : function(m, f, of, type) {
|
||||||
|
if (!this._valid)
|
||||||
|
return;
|
||||||
|
let path = f.get_parent().get_path();
|
||||||
|
let k = undefined;
|
||||||
|
for (let i = 0; i < this._paths.length; i++) {
|
||||||
|
if (this._paths[i] == path)
|
||||||
|
k = i;
|
||||||
|
}
|
||||||
|
if (k === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == Gio.FileMonitorEvent.CREATED) {
|
||||||
|
this._childs[k].push(f.get_basename());
|
||||||
|
}
|
||||||
|
if (type == Gio.FileMonitorEvent.DELETED) {
|
||||||
|
this._changedCount++;
|
||||||
|
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
|
||||||
|
this._valid = false;
|
||||||
|
}
|
||||||
|
let name = f.get_basename();
|
||||||
|
this._childs[k] = this._childs[k].filter(function(e) {
|
||||||
|
return e != name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
|
||||||
|
this._childs[k] = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCompletion: function(text) {
|
||||||
|
let common = "";
|
||||||
|
let notInit = true;
|
||||||
|
if (!this._valid) {
|
||||||
|
this._update(0);
|
||||||
|
return common;
|
||||||
|
}
|
||||||
|
function _getCommon(s1, s2) {
|
||||||
|
let k = 0;
|
||||||
|
for (; k < s1.length && k < s2.length; k++) {
|
||||||
|
if (s1[k] != s2[k])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (k == 0)
|
||||||
|
return "";
|
||||||
|
return s1.substr(0, k);
|
||||||
|
}
|
||||||
|
function _hasPrefix(s1, prefix) {
|
||||||
|
return s1.indexOf(prefix) == 0;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this._childs.length; i++) {
|
||||||
|
for (let k = 0; k < this._childs[i].length; k++) {
|
||||||
|
if (!_hasPrefix(this._childs[i][k], text))
|
||||||
|
continue;
|
||||||
|
if (notInit) {
|
||||||
|
common = this._childs[i][k];
|
||||||
|
notInit = false;
|
||||||
|
}
|
||||||
|
common = _getCommon(common, this._childs[i][k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (common.length)
|
||||||
|
return common.substr(text.length);
|
||||||
|
return common;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function RunDialog() {
|
function RunDialog() {
|
||||||
this._init();
|
this._init();
|
||||||
@ -60,24 +199,26 @@ RunDialog.prototype = {
|
|||||||
|
|
||||||
// All actors are inside _group. We create it initially
|
// All actors are inside _group. We create it initially
|
||||||
// hidden then show it in show()
|
// hidden then show it in show()
|
||||||
this._group = new Clutter.Group({ visible: false });
|
this._group = new Clutter.Group({ visible: false,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: global.screen_width,
|
||||||
|
height: global.screen_height });
|
||||||
global.stage.add_actor(this._group);
|
global.stage.add_actor(this._group);
|
||||||
|
|
||||||
this._lightbox = new Lightbox.Lightbox(this._group);
|
let lightbox = new Lightbox.Lightbox(this._group);
|
||||||
|
|
||||||
let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
this._boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
||||||
x_align: Big.BoxAlignment.CENTER,
|
x_align: Big.BoxAlignment.CENTER,
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
y_align: Big.BoxAlignment.CENTER });
|
||||||
width: global.screen_width,
|
|
||||||
height: global.screen_height });
|
|
||||||
|
|
||||||
this._group.add_actor(boxH);
|
this._group.add_actor(this._boxH);
|
||||||
this._lightbox.highlight(boxH);
|
lightbox.highlight(this._boxH);
|
||||||
|
|
||||||
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
y_align: Big.BoxAlignment.CENTER });
|
y_align: Big.BoxAlignment.CENTER });
|
||||||
|
|
||||||
boxH.append(boxV, Big.BoxPackFlags.NONE);
|
this._boxH.append(boxV, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
|
|
||||||
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
||||||
@ -87,7 +228,7 @@ RunDialog.prototype = {
|
|||||||
padding: DIALOG_PADDING,
|
padding: DIALOG_PADDING,
|
||||||
width: DIALOG_WIDTH });
|
width: DIALOG_WIDTH });
|
||||||
|
|
||||||
boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
this._boxH.append(dialogBox, Big.BoxPackFlags.NONE);
|
||||||
|
|
||||||
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
|
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
|
||||||
font_name: '18px Sans',
|
font_name: '18px Sans',
|
||||||
@ -135,17 +276,57 @@ RunDialog.prototype = {
|
|||||||
this.close();
|
this.close();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._pathCompleter = new Gio.FilenameCompleter();
|
||||||
|
this._commandCompleter = new CommandCompleter();
|
||||||
|
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||||
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||||
let symbol = e.get_key_symbol();
|
let symbol = e.get_key_symbol();
|
||||||
if (symbol == Clutter.Escape) {
|
if (symbol == Clutter.Escape) {
|
||||||
this.close();
|
this.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (symbol == Clutter.slash) {
|
||||||
|
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
|
||||||
|
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
|
||||||
|
let text = o.get_text().concat('/a');
|
||||||
|
let prefix;
|
||||||
|
if (text.lastIndexOf(' ') == -1)
|
||||||
|
prefix = text;
|
||||||
|
else
|
||||||
|
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
||||||
|
this._getCompletion(prefix);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (symbol == Clutter.Tab) {
|
||||||
|
let text = o.get_text();
|
||||||
|
let prefix;
|
||||||
|
if (text.lastIndexOf(' ') == -1)
|
||||||
|
prefix = text;
|
||||||
|
else
|
||||||
|
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
||||||
|
let postfix = this._getCompletion(prefix);
|
||||||
|
if (postfix != null && postfix.length > 0) {
|
||||||
|
o.insert_text(postfix, -1);
|
||||||
|
o.set_cursor_position(text.length + postfix.length);
|
||||||
|
if (postfix[postfix.length - 1] == '/')
|
||||||
|
this._getCompletion(text + postfix + 'a');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getCompletion : function(text) {
|
||||||
|
if (text.indexOf('/') != -1) {
|
||||||
|
return this._pathCompleter.get_completion_suffix(text);
|
||||||
|
} else {
|
||||||
|
return this._commandCompleter.getCompletion(text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_run : function(command) {
|
_run : function(command) {
|
||||||
|
this._commandError = false;
|
||||||
let f;
|
let f;
|
||||||
if (this._enableInternalCommands)
|
if (this._enableInternalCommands)
|
||||||
f = this._internalCommands[command];
|
f = this._internalCommands[command];
|
||||||
@ -155,7 +336,6 @@ RunDialog.prototype = {
|
|||||||
f();
|
f();
|
||||||
} else if (command) {
|
} else if (command) {
|
||||||
try {
|
try {
|
||||||
this._commandError = false;
|
|
||||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||||
let p = new Shell.Process({'args' : args});
|
let p = new Shell.Process({'args' : args});
|
||||||
p.run();
|
p.run();
|
||||||
@ -168,7 +348,7 @@ RunDialog.prototype = {
|
|||||||
* We are only interested in the actual error, so parse that out.
|
* We are only interested in the actual error, so parse that out.
|
||||||
*/
|
*/
|
||||||
let m = /.+\((.+)\)/.exec(e);
|
let m = /.+\((.+)\)/.exec(e);
|
||||||
let errorStr = "Execution of '" + command + "' failed:\n" + m[1];
|
let errorStr = _("Execution of '%s' failed:").format(command) + "\n" + m[1];
|
||||||
this._errorMessage.set_text(errorStr);
|
this._errorMessage.set_text(errorStr);
|
||||||
this._errorBox.show();
|
this._errorBox.show();
|
||||||
}
|
}
|
||||||
@ -182,6 +362,12 @@ RunDialog.prototype = {
|
|||||||
if (!Main.pushModal(this._group))
|
if (!Main.pushModal(this._group))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Position the dialog on the current monitor
|
||||||
|
let monitor = global.get_focus_monitor();
|
||||||
|
|
||||||
|
this._boxH.set_position(monitor.x, monitor.y);
|
||||||
|
this._boxH.set_size(monitor.width, monitor.height);
|
||||||
|
|
||||||
this._isOpen = true;
|
this._isOpen = true;
|
||||||
this._group.show();
|
this._group.show();
|
||||||
|
|
||||||
@ -198,7 +384,7 @@ RunDialog.prototype = {
|
|||||||
this._commandError = false;
|
this._commandError = false;
|
||||||
|
|
||||||
this._group.hide();
|
this._group.hide();
|
||||||
this._entry.text = '';
|
this._entry.set_text('');
|
||||||
|
|
||||||
Main.popModal(this._group);
|
Main.popModal(this._group);
|
||||||
}
|
}
|
||||||
|
272
js/ui/search.js
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const RESULT_ICON_SIZE = 24;
|
||||||
|
|
||||||
|
// Not currently referenced by the search API, but
|
||||||
|
// this enumeration can be useful for provider
|
||||||
|
// implementations.
|
||||||
|
const MatchType = {
|
||||||
|
NONE: 0,
|
||||||
|
MULTIPLE: 1,
|
||||||
|
PREFIX: 2,
|
||||||
|
SUBSTRING: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
function SearchResultDisplay(provider) {
|
||||||
|
this._init(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResultDisplay.prototype = {
|
||||||
|
_init: function(provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
this.actor = null;
|
||||||
|
this.selectionIndex = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* renderResults:
|
||||||
|
* @results: List of identifier strings
|
||||||
|
* @terms: List of search term strings
|
||||||
|
*
|
||||||
|
* Display the given search matches which resulted
|
||||||
|
* from the given terms. It's expected that not
|
||||||
|
* all results will fit in the space for the container
|
||||||
|
* actor; in this case, show as many as makes sense
|
||||||
|
* for your result type.
|
||||||
|
*
|
||||||
|
* The terms are useful for search match highlighting.
|
||||||
|
*/
|
||||||
|
renderResults: function(results, terms) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear:
|
||||||
|
* Remove all results from this display and reset the selection index.
|
||||||
|
*/
|
||||||
|
clear: function() {
|
||||||
|
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
|
||||||
|
this.selectionIndex = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getSelectionIndex:
|
||||||
|
*
|
||||||
|
* Returns the index of the selected actor, or -1 if none.
|
||||||
|
*/
|
||||||
|
getSelectionIndex: function() {
|
||||||
|
return this.selectionIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getVisibleResultCount:
|
||||||
|
*
|
||||||
|
* Returns: The number of actors visible.
|
||||||
|
*/
|
||||||
|
getVisibleResultCount: function() {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* selectIndex:
|
||||||
|
* @index: Integer index
|
||||||
|
*
|
||||||
|
* Move selection to the given index.
|
||||||
|
* Return true if successful, false if no more results
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
selectIndex: function() {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SearchProvider:
|
||||||
|
*
|
||||||
|
* Subclass this object to add a new result type
|
||||||
|
* to the search system, then call registerProvider()
|
||||||
|
* in SearchSystem with an instance.
|
||||||
|
*/
|
||||||
|
function SearchProvider(title) {
|
||||||
|
this._init(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchProvider.prototype = {
|
||||||
|
_init: function(title) {
|
||||||
|
this.title = title;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getInitialResultSet:
|
||||||
|
* @terms: Array of search terms, treated as logical OR
|
||||||
|
*
|
||||||
|
* Called when the user first begins a search (most likely
|
||||||
|
* therefore a single term of length one or two), or when
|
||||||
|
* a new term is added.
|
||||||
|
*
|
||||||
|
* Should return an array of result identifier strings representing
|
||||||
|
* items which match the given search terms. This
|
||||||
|
* is expected to be a substring match on the metadata for a given
|
||||||
|
* item. Ordering of returned results is up to the discretion of the provider,
|
||||||
|
* but you should follow these heruistics:
|
||||||
|
*
|
||||||
|
* * Put items which match multiple search terms before single matches
|
||||||
|
* * Put items which match on a prefix before non-prefix substring matches
|
||||||
|
*
|
||||||
|
* This function should be fast; do not perform unindexed full-text searches
|
||||||
|
* or network queries.
|
||||||
|
*/
|
||||||
|
getInitialResultSet: function(terms) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getSubsearchResultSet:
|
||||||
|
* @previousResults: Array of item identifiers
|
||||||
|
* @newTerms: Updated search terms
|
||||||
|
*
|
||||||
|
* Called when a search is performed which is a "subsearch" of
|
||||||
|
* the previous search; i.e. when every search term has exactly
|
||||||
|
* one corresponding term in the previous search which is a prefix
|
||||||
|
* of the new term.
|
||||||
|
*
|
||||||
|
* This allows search providers to only search through the previous
|
||||||
|
* result set, rather than possibly performing a full re-query.
|
||||||
|
*/
|
||||||
|
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getResultInfo:
|
||||||
|
* @id: Result identifier string
|
||||||
|
*
|
||||||
|
* Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
|
||||||
|
* properties which describe the given search result.
|
||||||
|
*/
|
||||||
|
getResultMeta: function(id) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createResultContainer:
|
||||||
|
*
|
||||||
|
* Search providers may optionally override this to render their
|
||||||
|
* results in a custom fashion. The default implementation
|
||||||
|
* will create a vertical list.
|
||||||
|
*
|
||||||
|
* Returns: An instance of SearchResultDisplay.
|
||||||
|
*/
|
||||||
|
createResultContainerActor: function() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createResultActor:
|
||||||
|
* @resultMeta: Object with result metadata
|
||||||
|
* @terms: Array of search terms, should be used for highlighting
|
||||||
|
*
|
||||||
|
* Search providers may optionally override this to render a
|
||||||
|
* particular serch result in a custom fashion. The default
|
||||||
|
* implementation will show the icon next to the name.
|
||||||
|
*
|
||||||
|
* The actor should be an instance of St.Widget, with the style class
|
||||||
|
* 'dash-search-result-content'.
|
||||||
|
*/
|
||||||
|
createResultActor: function(resultMeta, terms) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* activateResult:
|
||||||
|
* @id: Result identifier string
|
||||||
|
*
|
||||||
|
* Called when the user chooses a given result.
|
||||||
|
*/
|
||||||
|
activateResult: function(id) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* expandSearch:
|
||||||
|
*
|
||||||
|
* Called when the user clicks on the header for this
|
||||||
|
* search section. Should typically launch an external program
|
||||||
|
* displaying search results for that item type.
|
||||||
|
*/
|
||||||
|
expandSearch: function(terms) {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(SearchProvider.prototype);
|
||||||
|
|
||||||
|
function SearchSystem() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchSystem.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._providers = [];
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
registerProvider: function (provider) {
|
||||||
|
this._providers.push(provider);
|
||||||
|
},
|
||||||
|
|
||||||
|
getProviders: function() {
|
||||||
|
return this._providers;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTerms: function() {
|
||||||
|
return this._previousTerms;
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this._previousTerms = [];
|
||||||
|
this._previousResults = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSearch: function(searchString) {
|
||||||
|
searchString = searchString.replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||||
|
if (searchString == '')
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let terms = searchString.split(/\s+/);
|
||||||
|
let isSubSearch = terms.length == this._previousTerms.length;
|
||||||
|
if (isSubSearch) {
|
||||||
|
for (let i = 0; i < terms.length; i++) {
|
||||||
|
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
|
||||||
|
isSubSearch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = [];
|
||||||
|
if (isSubSearch) {
|
||||||
|
for (let i = 0; i < this._previousResults.length; i++) {
|
||||||
|
let [provider, previousResults] = this._previousResults[i];
|
||||||
|
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
|
||||||
|
if (providerResults.length > 0)
|
||||||
|
results.push([provider, providerResults]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < this._providers.length; i++) {
|
||||||
|
let provider = this._providers[i];
|
||||||
|
let providerResults = provider.getInitialResultSet(terms);
|
||||||
|
if (providerResults.length > 0)
|
||||||
|
results.push([provider, providerResults]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._previousTerms = terms;
|
||||||
|
this._previousResults = results;
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Signals.addSignalMethods(SearchSystem.prototype);
|
@ -48,6 +48,9 @@ GnomeShell.prototype = {
|
|||||||
let success;
|
let success;
|
||||||
try {
|
try {
|
||||||
returnValue = JSON.stringify(eval(code));
|
returnValue = JSON.stringify(eval(code));
|
||||||
|
// A hack; DBus doesn't have null/undefined
|
||||||
|
if (returnValue == undefined)
|
||||||
|
returnValue = "";
|
||||||
success = true;
|
success = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
returnValue = JSON.stringify(e);
|
returnValue = JSON.stringify(e);
|
||||||
|
@ -69,6 +69,8 @@ Sidebar.prototype = {
|
|||||||
Lang.bind(this, this._expandedChanged));
|
Lang.bind(this, this._expandedChanged));
|
||||||
this._gconf.connect('changed::sidebar/visible',
|
this._gconf.connect('changed::sidebar/visible',
|
||||||
Lang.bind(this, this._visibleChanged));
|
Lang.bind(this, this._visibleChanged));
|
||||||
|
|
||||||
|
this._adjustPosition();
|
||||||
},
|
},
|
||||||
|
|
||||||
addWidget: function(widget) {
|
addWidget: function(widget) {
|
||||||
@ -82,6 +84,14 @@ Sidebar.prototype = {
|
|||||||
|
|
||||||
this.box.append(widgetBox.actor, Big.BoxPackFlags.NONE);
|
this.box.append(widgetBox.actor, Big.BoxPackFlags.NONE);
|
||||||
this._widgets.push(widgetBox);
|
this._widgets.push(widgetBox);
|
||||||
|
this._adjustPosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
_adjustPosition: function() {
|
||||||
|
let primary=global.get_primary_monitor();
|
||||||
|
|
||||||
|
this.actor.y = Math.max(primary.y + Panel.PANEL_HEIGHT,primary.height/2 - this.actor.height/2);
|
||||||
|
this.actor.x = primary.x;
|
||||||
},
|
},
|
||||||
|
|
||||||
_visibleChanged: function() {
|
_visibleChanged: function() {
|
||||||
|
297
js/ui/statusMenu.js
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const DBus = imports.dbus;
|
||||||
|
const Gdm = imports.gi.Gdm;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const Panel = imports.ui.panel;
|
||||||
|
|
||||||
|
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||||
|
//
|
||||||
|
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||||
|
// Copyright (C) 2008,2009 Red Hat, Inc.
|
||||||
|
|
||||||
|
const SIDEBAR_VISIBLE_KEY = 'sidebar/visible';
|
||||||
|
|
||||||
|
function StatusMenu() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusMenu.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
this._gdm = Gdm.UserManager.ref_default();
|
||||||
|
this._user = this._gdm.get_user(GLib.get_user_name());
|
||||||
|
this._presence = new GnomeSessionPresence();
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ name: 'StatusMenu' });
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
|
||||||
|
this._iconBox = new St.Bin();
|
||||||
|
this.actor.add(this._iconBox, { y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let textureCache = Shell.TextureCache.get_default();
|
||||||
|
// FIXME: these icons are all wrong (likewise in createSubMenu)
|
||||||
|
this._availableIcon = textureCache.load_icon_name('gtk-yes', 16);
|
||||||
|
this._busyIcon = textureCache.load_icon_name('gtk-no', 16);
|
||||||
|
this._invisibleIcon = textureCache.load_icon_name('gtk-close', 16);
|
||||||
|
this._idleIcon = textureCache.load_icon_name('gtk-media-pause', 16);
|
||||||
|
|
||||||
|
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
|
||||||
|
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
|
||||||
|
|
||||||
|
this._name = new St.Label({ text: this._user.get_real_name() });
|
||||||
|
this.actor.add(this._name, { expand: true, y_align: St.Align.MIDDLE });
|
||||||
|
this._userNameChangedId = this._user.connect('notify::display-name', Lang.bind(this, this._updateUserName));
|
||||||
|
|
||||||
|
this._createSubMenu();
|
||||||
|
this._gdm.connect('users-loaded', Lang.bind(this, this._updateSwitchUser));
|
||||||
|
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
|
||||||
|
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this._user.disconnect(this._userNameChangedId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateUserName: function() {
|
||||||
|
this._name.set_text(this._user.get_real_name());
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSwitchUser: function() {
|
||||||
|
let users = this._gdm.list_users();
|
||||||
|
if (users.length > 1)
|
||||||
|
this._loginScreenItem.show();
|
||||||
|
else
|
||||||
|
this._loginScreenItem.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePresenceIcon: function(presence, status) {
|
||||||
|
if (status == GnomeSessionPresenceStatus.AVAILABLE)
|
||||||
|
this._iconBox.child = this._availableIcon;
|
||||||
|
else if (status == GnomeSessionPresenceStatus.BUSY)
|
||||||
|
this._iconBox.child = this._busyIcon;
|
||||||
|
else if (status == GnomeSessionPresenceStatus.INVISIBLE)
|
||||||
|
this._iconBox.child = this._invisibleIcon;
|
||||||
|
else
|
||||||
|
this._iconBox.child = this._idleIcon;
|
||||||
|
},
|
||||||
|
|
||||||
|
// The menu
|
||||||
|
|
||||||
|
_createImageMenuItem: function(label, iconName, forceIcon) {
|
||||||
|
let image = new Gtk.Image();
|
||||||
|
let item = new Gtk.ImageMenuItem({ label: label,
|
||||||
|
image: image,
|
||||||
|
always_show_image: forceIcon == true });
|
||||||
|
item.connect('style-set', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
image.set_from_icon_name(iconName, Gtk.IconSize.MENU);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return item;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createSubMenu: function() {
|
||||||
|
this._menu = new Gtk.Menu();
|
||||||
|
this._menu.connect('deactivate', Lang.bind(this, function() { this.emit('deactivated'); }));
|
||||||
|
|
||||||
|
let item;
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Available'), 'gtk-yes', true);
|
||||||
|
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.AVAILABLE));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Busy'), 'gtk-no', true);
|
||||||
|
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.BUSY));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Invisible'), 'gtk-close', true);
|
||||||
|
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSessionPresenceStatus.INVISIBLE));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = new Gtk.SeparatorMenuItem();
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Account Information...'), 'user-info');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onAccountInformationActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
let gconf = Shell.GConf.get_default();
|
||||||
|
item = new Gtk.CheckMenuItem({ label: _('Sidebar'),
|
||||||
|
active: gconf.get_boolean(SIDEBAR_VISIBLE_KEY) });
|
||||||
|
item.connect('activate', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
gconf.set_boolean(SIDEBAR_VISIBLE_KEY, this._sidebarItem.active);
|
||||||
|
}));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
this._sidebarItem = item;
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('System Preferences...'), 'preferences-desktop');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = new Gtk.SeparatorMenuItem();
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Lock Screen'), 'system-lock-screen');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Switch User'), 'system-users');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
this._loginScreenItem = item;
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Log Out...'), 'system-log-out');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
|
||||||
|
item = this._createImageMenuItem(_('Shut Down...'), 'system-shutdown');
|
||||||
|
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||||
|
this._menu.append(item);
|
||||||
|
item.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setPresenceStatus: function(item, status) {
|
||||||
|
this._presence.setStatus(status);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAccountInformationActivate: function() {
|
||||||
|
this._spawn(['gnome-about-me']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPreferencesActivate: function() {
|
||||||
|
this._spawn(['gnome-control-center']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLockScreenActivate: function() {
|
||||||
|
this._spawn(['gnome-screensaver-command', '--lock']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLoginScreenActivate: function() {
|
||||||
|
this._gdm.goto_login_session();
|
||||||
|
this._onLockScreenActivate();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onQuitSessionActivate: function() {
|
||||||
|
this._spawn(['gnome-session-save', '--logout-dialog']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShutDownActivate: function() {
|
||||||
|
this._spawn(['gnome-session-save', '--shutdown-dialog']);
|
||||||
|
},
|
||||||
|
|
||||||
|
_spawn: function(args) {
|
||||||
|
// FIXME: once Shell.Process gets support for signalling
|
||||||
|
// errors we should pop up an error dialog or something here
|
||||||
|
// on failure
|
||||||
|
let p = new Shell.Process({'args' : args});
|
||||||
|
p.run();
|
||||||
|
},
|
||||||
|
|
||||||
|
// shell_status_menu_toggle:
|
||||||
|
// @event: event causing the toggle
|
||||||
|
//
|
||||||
|
// If the menu is not currently up, pops it up. Otherwise, hides it.
|
||||||
|
// Popping up may fail if another grab is already active; check with
|
||||||
|
// isActive().
|
||||||
|
toggle: function(event) {
|
||||||
|
if (this._menu.visible)
|
||||||
|
this._menu.popdown();
|
||||||
|
else {
|
||||||
|
// We don't want to overgrab a Mutter grab with the grab
|
||||||
|
// that GTK+ uses on menus.
|
||||||
|
if (global.display_is_grabbed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [menuWidth, menuHeight] = this._menu.get_size_request ();
|
||||||
|
|
||||||
|
let panel;
|
||||||
|
for (panel = this.actor; panel; panel = panel.get_parent()) {
|
||||||
|
if (panel._delegate instanceof Panel.Panel)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [panelX, panelY] = panel.get_transformed_position();
|
||||||
|
let [panelWidth, panelHeight] = panel.get_transformed_size();
|
||||||
|
|
||||||
|
let menuX = Math.round(panelX + panelWidth - menuWidth);
|
||||||
|
let menuY = Math.round(panelY + panelHeight);
|
||||||
|
|
||||||
|
Shell.popup_menu(this._menu, event.get_button(), event.get_time(),
|
||||||
|
menuX, menuY);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// isActive:
|
||||||
|
//
|
||||||
|
// Gets whether the menu is currently popped up
|
||||||
|
//
|
||||||
|
// Return value: %true if the menu is currently popped up
|
||||||
|
isActive: function() {
|
||||||
|
return this._menu.visible;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Signals.addSignalMethods(StatusMenu.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
const GnomeSessionPresenceIface = {
|
||||||
|
name: 'org.gnome.SessionManager.Presence',
|
||||||
|
methods: [{ name: 'SetStatus',
|
||||||
|
inSignature: 'u' }],
|
||||||
|
properties: [{ name: 'status',
|
||||||
|
signature: 'u',
|
||||||
|
access: 'readwrite' }],
|
||||||
|
signals: [{ name: 'StatusChanged',
|
||||||
|
inSignature: 'u' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const GnomeSessionPresenceStatus = {
|
||||||
|
AVAILABLE: 0,
|
||||||
|
INVISIBLE: 1,
|
||||||
|
BUSY: 2,
|
||||||
|
IDLE: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
function GnomeSessionPresence() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeSessionPresence.prototype = {
|
||||||
|
_init: function() {
|
||||||
|
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', this);
|
||||||
|
this.connect('StatusChanged', Lang.bind(this, function (proxy, status) { this.status = status; }));
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatus: function(callback) {
|
||||||
|
this.GetRemote('status', Lang.bind(this,
|
||||||
|
function(status, ex) {
|
||||||
|
if (!ex)
|
||||||
|
callback(this, status);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
setStatus: function(status) {
|
||||||
|
this.SetStatusRemote(status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DBus.proxifyPrototype(GnomeSessionPresence.prototype, GnomeSessionPresenceIface);
|
||||||
|
|
@ -12,6 +12,7 @@ const Signals = imports.signals;
|
|||||||
const Gettext = imports.gettext.domain('gnome-shell');
|
const Gettext = imports.gettext.domain('gnome-shell');
|
||||||
const _ = Gettext.gettext;
|
const _ = Gettext.gettext;
|
||||||
|
|
||||||
|
const AppFavorites = imports.ui.appFavorites;
|
||||||
const DocInfo = imports.misc.docInfo;
|
const DocInfo = imports.misc.docInfo;
|
||||||
|
|
||||||
const COLLAPSED_WIDTH = 24;
|
const COLLAPSED_WIDTH = 24;
|
||||||
@ -318,12 +319,9 @@ AppsWidget.prototype = {
|
|||||||
this.collapsedActor = new Big.Box({ spacing: 2});
|
this.collapsedActor = new Big.Box({ spacing: 2});
|
||||||
|
|
||||||
let appSystem = Shell.AppSystem.get_default();
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
let apps = appSystem.get_favorites();
|
let apps = AppFavorites.getAppFavorites().getFavorites();
|
||||||
for (let i = 0; i < apps.length; i++) {
|
for (let i = 0; i < apps.length; i++) {
|
||||||
let app = appSystem.lookup_cached_app(apps[i]);
|
this.addItem(new AppsWidgetInfo(apps[i]));
|
||||||
if (!app)
|
|
||||||
continue;
|
|
||||||
this.addItem(new AppsWidgetInfo(app));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -356,8 +354,7 @@ RecentDocsWidget.prototype = {
|
|||||||
for (i = 0; i < docs.length; i++) {
|
for (i = 0; i < docs.length; i++) {
|
||||||
let docInfo = new DocInfo.DocInfo (docs[i]);
|
let docInfo = new DocInfo.DocInfo (docs[i]);
|
||||||
|
|
||||||
if (docInfo.exists())
|
items.push(docInfo);
|
||||||
items.push(docInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
||||||
|
@ -331,7 +331,7 @@ WidgetBox.prototype = {
|
|||||||
onCompleteScope: this });
|
onCompleteScope: this });
|
||||||
this.state = this._widget.state = Widget.STATE_POPPING_OUT;
|
this.state = this._widget.state = Widget.STATE_POPPING_OUT;
|
||||||
|
|
||||||
Main.chrome.addInputRegionActor(this._hbox);
|
Main.chrome.trackActor(this._hbox, { affectsStruts: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
_popOutComplete: function() {
|
_popOutComplete: function() {
|
||||||
@ -370,7 +370,7 @@ WidgetBox.prototype = {
|
|||||||
this._egroup.hide();
|
this._egroup.hide();
|
||||||
this._ebox.x = 0;
|
this._ebox.x = 0;
|
||||||
|
|
||||||
Main.chrome.removeInputRegionActor(this._hbox);
|
Main.chrome.untrackActor(this._hbox);
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
|
@ -274,7 +274,7 @@ WindowManager.prototype = {
|
|||||||
_startAppSwitcher : function(shellwm, binding, window, backwards) {
|
_startAppSwitcher : function(shellwm, binding, window, backwards) {
|
||||||
let tabPopup = new AltTab.AltTabPopup();
|
let tabPopup = new AltTab.AltTabPopup();
|
||||||
|
|
||||||
if (!tabPopup.show(backwards ? -1 : 1))
|
if (!tabPopup.show(backwards))
|
||||||
tabPopup.destroy();
|
tabPopup.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
const Big = imports.gi.Big;
|
const Big = imports.gi.Big;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
|
const Gdk = imports.gi.Gdk;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const St = imports.gi.St;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
@ -20,10 +22,6 @@ const Tweener = imports.ui.tweener;
|
|||||||
|
|
||||||
const FOCUS_ANIMATION_TIME = 0.15;
|
const FOCUS_ANIMATION_TIME = 0.15;
|
||||||
|
|
||||||
const WINDOWCLONE_BG_COLOR = new Clutter.Color();
|
|
||||||
WINDOWCLONE_BG_COLOR.from_pixel(0x000000f0);
|
|
||||||
const WINDOWCLONE_TITLE_COLOR = new Clutter.Color();
|
|
||||||
WINDOWCLONE_TITLE_COLOR.from_pixel(0xffffffff);
|
|
||||||
const FRAME_COLOR = new Clutter.Color();
|
const FRAME_COLOR = new Clutter.Color();
|
||||||
FRAME_COLOR.from_pixel(0xffffffff);
|
FRAME_COLOR.from_pixel(0xffffffff);
|
||||||
|
|
||||||
@ -111,7 +109,7 @@ WindowClone.prototype = {
|
|||||||
this.origX = realWindow.x;
|
this.origX = realWindow.x;
|
||||||
this.origY = realWindow.y;
|
this.origY = realWindow.y;
|
||||||
|
|
||||||
this._title = null;
|
this._stackAbove = null;
|
||||||
|
|
||||||
this.actor.connect('button-release-event',
|
this.actor.connect('button-release-event',
|
||||||
Lang.bind(this, this._onButtonRelease));
|
Lang.bind(this, this._onButtonRelease));
|
||||||
@ -129,24 +127,20 @@ WindowClone.prototype = {
|
|||||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
this._inDrag = false;
|
this._inDrag = false;
|
||||||
|
|
||||||
|
this._zooming = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
setVisibleWithChrome: function(visible) {
|
setStackAbove: function (actor) {
|
||||||
if (visible) {
|
this._stackAbove = actor;
|
||||||
this.actor.show();
|
if (this._inDrag || this._zooming)
|
||||||
if (this._title)
|
// We'll fix up the stack after the drag/zooming
|
||||||
this._title.show();
|
return;
|
||||||
} else {
|
this.actor.raise(this._stackAbove);
|
||||||
this.actor.hide();
|
|
||||||
if (this._title)
|
|
||||||
this._title.hide();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
if (this._title)
|
|
||||||
this._title.destroy();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEnter: function (actor, event) {
|
_onEnter: function (actor, event) {
|
||||||
@ -156,9 +150,6 @@ WindowClone.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._havePointer = true;
|
this._havePointer = true;
|
||||||
|
|
||||||
actor.raise_top();
|
|
||||||
this._updateTitle();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onLeave: function (actor, event) {
|
_onLeave: function (actor, event) {
|
||||||
@ -168,7 +159,6 @@ WindowClone.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._havePointer = false;
|
this._havePointer = false;
|
||||||
this._updateTitle();
|
|
||||||
|
|
||||||
if (this._zoomStep)
|
if (this._zoomStep)
|
||||||
this._zoomEnd();
|
this._zoomEnd();
|
||||||
@ -206,6 +196,9 @@ WindowClone.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_zoomStart : function () {
|
_zoomStart : function () {
|
||||||
|
this._zooming = true;
|
||||||
|
this.emit('zoom-start');
|
||||||
|
|
||||||
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
|
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
|
||||||
|
|
||||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||||
@ -232,13 +225,15 @@ WindowClone.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_zoomEnd : function () {
|
_zoomEnd : function () {
|
||||||
|
this._zooming = false;
|
||||||
|
this.emit('zoom-end');
|
||||||
|
|
||||||
this.actor.reparent(this._origParent);
|
this.actor.reparent(this._origParent);
|
||||||
|
this.actor.raise(this._stackAbove);
|
||||||
|
|
||||||
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
||||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
|
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
|
||||||
|
|
||||||
this._adjustTitle();
|
|
||||||
|
|
||||||
this._zoomLightbox.destroy();
|
this._zoomLightbox.destroy();
|
||||||
Main.overview.disconnect(this._hideEventId);
|
Main.overview.disconnect(this._hideEventId);
|
||||||
|
|
||||||
@ -257,7 +252,6 @@ WindowClone.prototype = {
|
|||||||
|
|
||||||
_onDragBegin : function (draggable, time) {
|
_onDragBegin : function (draggable, time) {
|
||||||
this._inDrag = true;
|
this._inDrag = true;
|
||||||
this._updateTitle();
|
|
||||||
this.emit('drag-begin');
|
this.emit('drag-begin');
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -272,88 +266,13 @@ WindowClone.prototype = {
|
|||||||
// mysteriously present
|
// mysteriously present
|
||||||
this._havePointer = false;
|
this._havePointer = false;
|
||||||
|
|
||||||
|
// We may not have a parent if DnD completed successfully, in
|
||||||
|
// which case our clone will shortly be destroyed and replaced
|
||||||
|
// with a new one on the target workspace.
|
||||||
|
if (this.actor.get_parent() != null)
|
||||||
|
this.actor.raise(this._stackAbove);
|
||||||
|
|
||||||
this.emit('drag-end');
|
this.emit('drag-end');
|
||||||
},
|
|
||||||
|
|
||||||
// Called by Tweener
|
|
||||||
onAnimationStart : function () {
|
|
||||||
this._updateTitle();
|
|
||||||
},
|
|
||||||
|
|
||||||
// Called by Tweener
|
|
||||||
onAnimationComplete : function () {
|
|
||||||
this._updateTitle();
|
|
||||||
this.actor.raise(this.stackAbove);
|
|
||||||
},
|
|
||||||
|
|
||||||
_createTitle : function () {
|
|
||||||
let window = this.realWindow;
|
|
||||||
|
|
||||||
let box = new Big.Box({ background_color : WINDOWCLONE_BG_COLOR,
|
|
||||||
y_align: Big.BoxAlignment.CENTER,
|
|
||||||
corner_radius: 5,
|
|
||||||
padding: 4,
|
|
||||||
spacing: 4,
|
|
||||||
orientation: Big.BoxOrientation.HORIZONTAL });
|
|
||||||
|
|
||||||
let title = new Clutter.Text({ color: WINDOWCLONE_TITLE_COLOR,
|
|
||||||
font_name: "Sans 12",
|
|
||||||
text: this.metaWindow.title,
|
|
||||||
ellipsize: Pango.EllipsizeMode.END
|
|
||||||
});
|
|
||||||
box.append(title, Big.BoxPackFlags.EXPAND);
|
|
||||||
// Get and cache the expected width (just the icon), with spacing, plus title
|
|
||||||
box.fullWidth = box.width;
|
|
||||||
box.hide(); // Hidden by default, show on mouseover
|
|
||||||
this._title = box;
|
|
||||||
|
|
||||||
// Make the title a sibling of the window
|
|
||||||
this.actor.get_parent().add_actor(box);
|
|
||||||
},
|
|
||||||
|
|
||||||
_adjustTitle : function () {
|
|
||||||
let title = this._title;
|
|
||||||
if (!title)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let [cloneScreenWidth, cloneScreenHeight] = this.actor.get_transformed_size();
|
|
||||||
let [titleScreenWidth, titleScreenHeight] = title.get_transformed_size();
|
|
||||||
|
|
||||||
// Titles are supposed to be "full-size", so adjust its
|
|
||||||
// scale to counteract the scaling of its ancestor actors.
|
|
||||||
title.set_scale(title.width / titleScreenWidth * title.scale_x,
|
|
||||||
title.height / titleScreenHeight * title.scale_y);
|
|
||||||
|
|
||||||
title.width = Math.min(title.fullWidth, cloneScreenWidth);
|
|
||||||
let xoff = ((cloneScreenWidth - title.width) / 2) * title.scale_x;
|
|
||||||
title.set_position(this.actor.x + xoff, this.actor.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
_showTitle : function () {
|
|
||||||
if (!this._title)
|
|
||||||
this._createTitle();
|
|
||||||
|
|
||||||
this._adjustTitle();
|
|
||||||
this._title.show();
|
|
||||||
this._title.raise(this.actor);
|
|
||||||
},
|
|
||||||
|
|
||||||
_hideTitle : function () {
|
|
||||||
if (!this._title)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._title.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateTitle : function () {
|
|
||||||
let shouldShow = (this._havePointer &&
|
|
||||||
!this._inDrag &&
|
|
||||||
!Tweener.isTweening(this.actor));
|
|
||||||
|
|
||||||
if (shouldShow)
|
|
||||||
this._showTitle();
|
|
||||||
else
|
|
||||||
this._hideTitle();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -388,6 +307,212 @@ DesktopClone.prototype = {
|
|||||||
Signals.addSignalMethods(DesktopClone.prototype);
|
Signals.addSignalMethods(DesktopClone.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @windowClone: Corresponding window clone
|
||||||
|
* @parentActor: The actor which will be the parent of all overlay items
|
||||||
|
* such as app icon and window caption
|
||||||
|
*/
|
||||||
|
function WindowOverlay(windowClone, parentActor) {
|
||||||
|
this._init(windowClone, parentActor);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowOverlay.prototype = {
|
||||||
|
_init : function(windowClone, parentActor) {
|
||||||
|
let metaWindow = windowClone.metaWindow;
|
||||||
|
|
||||||
|
this._windowClone = windowClone;
|
||||||
|
this._parentActor = parentActor;
|
||||||
|
|
||||||
|
let title = new St.Label({ style_class: "window-caption",
|
||||||
|
text : metaWindow.title });
|
||||||
|
title.connect('style-changed',
|
||||||
|
Lang.bind(this, this._onStyleChanged));
|
||||||
|
title.clutter_text.ellipsize = Pango.EllipsizeMode.END;
|
||||||
|
title._spacing = 0;
|
||||||
|
|
||||||
|
let button = new St.Bin({ style_class: "window-close",
|
||||||
|
reactive: true });
|
||||||
|
button.connect('style-changed',
|
||||||
|
Lang.bind(this, this._onStyleChanged));
|
||||||
|
button._overlap = 0;
|
||||||
|
|
||||||
|
windowClone.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
windowClone.actor.connect('enter-event',
|
||||||
|
Lang.bind(this, this._onEnter));
|
||||||
|
windowClone.actor.connect('leave-event',
|
||||||
|
Lang.bind(this, this._onLeave));
|
||||||
|
|
||||||
|
this._idleToggleCloseId = 0;
|
||||||
|
button.connect('button-release-event',
|
||||||
|
Lang.bind(this, this._closeWindow));
|
||||||
|
|
||||||
|
this._windowAddedId = 0;
|
||||||
|
windowClone.connect('zoom-start', Lang.bind(this, this.hide));
|
||||||
|
windowClone.connect('zoom-end', Lang.bind(this, this.show));
|
||||||
|
|
||||||
|
button.hide();
|
||||||
|
|
||||||
|
this.title = title;
|
||||||
|
this.closeButton = button;
|
||||||
|
|
||||||
|
parentActor.add_actor(this.title);
|
||||||
|
parentActor.add_actor(this.closeButton);
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.closeButton.hide();
|
||||||
|
this.title.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
|
||||||
|
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
||||||
|
x, y);
|
||||||
|
if (actor == this._windowClone.actor) {
|
||||||
|
this.closeButton.show();
|
||||||
|
}
|
||||||
|
this.title.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeIn: function() {
|
||||||
|
this.title.opacity = 0;
|
||||||
|
this.title.show();
|
||||||
|
this.title.raise_top();
|
||||||
|
Tweener.addTween(this.title,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: Overview.ANIMATION_TIME,
|
||||||
|
transition: "easeOutQuad" });
|
||||||
|
},
|
||||||
|
|
||||||
|
chromeWidth: function () {
|
||||||
|
return this.closeButton.width - this.closeButton._overlap;
|
||||||
|
},
|
||||||
|
|
||||||
|
chromeHeight: function () {
|
||||||
|
return this.closeButton.height - this.closeButton._overlap +
|
||||||
|
this.title.height + this.title._spacing;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @cloneX: x position of windowClone
|
||||||
|
* @cloneY: y position of windowClone
|
||||||
|
* @cloneWidth: width of windowClone
|
||||||
|
* @cloneHeight height of windowClone
|
||||||
|
*/
|
||||||
|
// These parameters are not the values retrieved with
|
||||||
|
// get_transformed_position() and get_transformed_size(),
|
||||||
|
// as windowClone might be moving.
|
||||||
|
// See Workspace._fadeInWindowOverlay
|
||||||
|
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight) {
|
||||||
|
let button = this.closeButton;
|
||||||
|
let title = this.title;
|
||||||
|
|
||||||
|
let buttonX = cloneX + cloneWidth - button._overlap;
|
||||||
|
let buttonY = cloneY - button.height + button._overlap;
|
||||||
|
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
|
||||||
|
|
||||||
|
if (!title.fullWidth)
|
||||||
|
title.fullWidth = title.width;
|
||||||
|
title.width = Math.min(title.fullWidth, cloneWidth);
|
||||||
|
|
||||||
|
let titleX = cloneX + (cloneWidth - title.width) / 2;
|
||||||
|
let titleY = cloneY + cloneHeight + title._spacing;
|
||||||
|
title.set_position(Math.floor(titleX), Math.floor(titleY));
|
||||||
|
},
|
||||||
|
|
||||||
|
_closeWindow: function(actor, event) {
|
||||||
|
let metaWindow = this._windowClone.metaWindow;
|
||||||
|
this._workspace = metaWindow.get_workspace();
|
||||||
|
|
||||||
|
this._windowAddedId = this._workspace.connect('window-added',
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onWindowAdded));
|
||||||
|
|
||||||
|
metaWindow.delete(event.get_time());
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowAdded: function(workspace, win) {
|
||||||
|
let metaWindow = this._windowClone.metaWindow;
|
||||||
|
|
||||||
|
if (win.get_transient_for() == metaWindow) {
|
||||||
|
workspace.disconnect(this._windowAddedId);
|
||||||
|
this._windowAddedId = 0;
|
||||||
|
|
||||||
|
// use an idle handler to avoid mapping problems -
|
||||||
|
// see comment in Workspace._windowAdded
|
||||||
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._windowClone.emit('selected');
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
if (this._windowAddedId > 0) {
|
||||||
|
this._workspace.disconnect(this._windowAddedId);
|
||||||
|
this._windowAddedId = 0;
|
||||||
|
}
|
||||||
|
if (this._idleToggleCloseId > 0) {
|
||||||
|
Mainloop.source_remove(this._idleToggleCloseId);
|
||||||
|
this._idleToggleCloseId = 0;
|
||||||
|
}
|
||||||
|
this.title.destroy();
|
||||||
|
this.closeButton.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onEnter: function() {
|
||||||
|
this.closeButton.raise_top();
|
||||||
|
this.closeButton.show();
|
||||||
|
this.emit('show-close-button');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLeave: function() {
|
||||||
|
if (this._idleToggleCloseId == 0)
|
||||||
|
this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton));
|
||||||
|
},
|
||||||
|
|
||||||
|
_idleToggleCloseButton: function() {
|
||||||
|
this._idleToggleCloseId = 0;
|
||||||
|
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
|
||||||
|
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
||||||
|
x, y);
|
||||||
|
if (actor != this._windowClone.actor && actor != this.closeButton) {
|
||||||
|
this.closeButton.hide();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
hideCloseButton: function() {
|
||||||
|
if (this._idleToggleCloseId > 0) {
|
||||||
|
Mainloop.source_remove(this._idleToggleCloseId);
|
||||||
|
this._idleToggleCloseId = 0;
|
||||||
|
}
|
||||||
|
this.closeButton.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStyleChanged: function() {
|
||||||
|
let titleNode = this.title.get_theme_node();
|
||||||
|
|
||||||
|
let [success, len] = titleNode.get_length('-shell-caption-spacing',
|
||||||
|
false);
|
||||||
|
if (success)
|
||||||
|
this.title._spacing = len;
|
||||||
|
|
||||||
|
let closeNode = this.closeButton.get_theme_node();
|
||||||
|
|
||||||
|
[success, len] = closeNode.get_length('-shell-close-overlap',
|
||||||
|
false);
|
||||||
|
if (success)
|
||||||
|
this.closeButton._overlap = len;
|
||||||
|
|
||||||
|
this._parentActor.queue_relayout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Signals.addSignalMethods(WindowOverlay.prototype);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @workspaceNum: Workspace index
|
* @workspaceNum: Workspace index
|
||||||
* @parentActor: The actor which will be the parent of this workspace;
|
* @parentActor: The actor which will be the parent of this workspace;
|
||||||
@ -439,7 +564,7 @@ Workspace.prototype = {
|
|||||||
// Create clones for remaining windows that should be
|
// Create clones for remaining windows that should be
|
||||||
// visible in the Overview
|
// visible in the Overview
|
||||||
this._windows = [this._desktop];
|
this._windows = [this._desktop];
|
||||||
this._windowIcons = [ null ];
|
this._windowOverlays = [ null ];
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
if (this._isOverviewWindow(windows[i])) {
|
if (this._isOverviewWindow(windows[i])) {
|
||||||
this._addWindowClone(windows[i]);
|
this._addWindowClone(windows[i]);
|
||||||
@ -554,6 +679,9 @@ Workspace.prototype = {
|
|||||||
this._lightbox.destroy();
|
this._lightbox.destroy();
|
||||||
this._lightbox = null;
|
this._lightbox = null;
|
||||||
}
|
}
|
||||||
|
if (this._frame) {
|
||||||
|
this._frame.set_opacity(showLightbox ? 150 : 255);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -618,16 +746,47 @@ Workspace.prototype = {
|
|||||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_isCloneVisible: function(clone) {
|
||||||
|
return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _getVisibleClones:
|
||||||
|
*
|
||||||
|
* Returns a list WindowClone objects where the clone isn't filtered
|
||||||
|
* out by any application filter. The clone for the desktop is excluded.
|
||||||
|
* The returned array will always be newly allocated; it is not in any
|
||||||
|
* defined order, and thus it's convenient to call .sort() with your
|
||||||
|
* choice of sorting function.
|
||||||
|
*/
|
||||||
|
_getVisibleClones: function() {
|
||||||
|
let visible = [];
|
||||||
|
|
||||||
|
for (let i = 1; i < this._windows.length; i++) {
|
||||||
|
let clone = this._windows[i];
|
||||||
|
|
||||||
|
if (!this._isCloneVisible(clone))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
visible.push(clone);
|
||||||
|
}
|
||||||
|
return visible;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getVisibleWindows: function() {
|
||||||
|
return this._getVisibleClones().map(function (clone) { return clone.metaWindow; });
|
||||||
|
},
|
||||||
|
|
||||||
_resetCloneVisibility: function () {
|
_resetCloneVisibility: function () {
|
||||||
for (let i = 1; i < this._windows.length; i++) {
|
for (let i = 1; i < this._windows.length; i++) {
|
||||||
let clone = this._windows[i];
|
let clone = this._windows[i];
|
||||||
let icon = this._windowIcons[i];
|
let overlay = this._windowOverlays[i];
|
||||||
|
|
||||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) {
|
if (!this._isCloneVisible(clone)) {
|
||||||
clone.setVisibleWithChrome(false);
|
clone.actor.hide();
|
||||||
icon.hide();
|
overlay.hide();
|
||||||
} else {
|
} else {
|
||||||
clone.setVisibleWithChrome(true);
|
clone.actor.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -773,7 +932,6 @@ Workspace.prototype = {
|
|||||||
* fashion by the order in which the windows were created.
|
* fashion by the order in which the windows were created.
|
||||||
*/
|
*/
|
||||||
_orderWindowsByMotionAndStartup: function(windows, slots) {
|
_orderWindowsByMotionAndStartup: function(windows, slots) {
|
||||||
let appMonitor = Shell.AppMonitor.get_default();
|
|
||||||
windows.sort(function(w1, w2) {
|
windows.sort(function(w1, w2) {
|
||||||
return w2.get_stable_sequence() - w1.get_stable_sequence();
|
return w2.get_stable_sequence() - w1.get_stable_sequence();
|
||||||
});
|
});
|
||||||
@ -834,14 +992,17 @@ Workspace.prototype = {
|
|||||||
let rect = new Meta.Rectangle();
|
let rect = new Meta.Rectangle();
|
||||||
metaWindow.get_outer_rect(rect);
|
metaWindow.get_outer_rect(rect);
|
||||||
|
|
||||||
let desiredWidth = global.screen_width * fraction;
|
let chromeHeight = this._windowOverlays[1].chromeHeight() / this.scale;
|
||||||
let desiredHeight = global.screen_height * fraction;
|
let chromeWidth = this._windowOverlays[1].chromeWidth() / this.scale;
|
||||||
let scale = Math.min(desiredWidth / rect.width,
|
|
||||||
desiredHeight / rect.height,
|
let desiredWidth = (global.screen_width - chromeWidth) * fraction;
|
||||||
|
let desiredHeight = (global.screen_height - chromeHeight) * fraction;
|
||||||
|
let scale = Math.min(desiredWidth / (rect.width + chromeWidth),
|
||||||
|
desiredHeight / (rect.height + chromeHeight),
|
||||||
1.0 / this.scale);
|
1.0 / this.scale);
|
||||||
|
|
||||||
let x = xCenter - 0.5 * scale * rect.width;
|
let x = xCenter - 0.5 * scale * (rect.width + chromeWidth);
|
||||||
let y = yCenter - 0.5 * scale * rect.height;
|
let y = yCenter - 0.5 * scale * (rect.height + chromeHeight);
|
||||||
|
|
||||||
return [x, y, scale];
|
return [x, y, scale];
|
||||||
},
|
},
|
||||||
@ -853,34 +1014,22 @@ Workspace.prototype = {
|
|||||||
positionWindows : function(workspaceZooming) {
|
positionWindows : function(workspaceZooming) {
|
||||||
let totalVisible = 0;
|
let totalVisible = 0;
|
||||||
|
|
||||||
let visibleWindows = [];
|
let visibleWindows = this._getVisibleWindows();
|
||||||
|
|
||||||
for (let i = 1; i < this._windows.length; i++) {
|
|
||||||
let clone = this._windows[i];
|
|
||||||
|
|
||||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
visibleWindows.push(clone.metaWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Start the animations
|
||||||
let slots = this._computeAllWindowSlots(visibleWindows.length);
|
let slots = this._computeAllWindowSlots(visibleWindows.length);
|
||||||
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
|
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
|
||||||
|
|
||||||
let previousWindow = this._windows[0];
|
|
||||||
for (let i = 0; i < visibleWindows.length; i++) {
|
for (let i = 0; i < visibleWindows.length; i++) {
|
||||||
let slot = slots[i];
|
let slot = slots[i];
|
||||||
let metaWindow = visibleWindows[i];
|
let metaWindow = visibleWindows[i];
|
||||||
let mainIndex = this._lookupIndex(metaWindow);
|
let mainIndex = this._lookupIndex(metaWindow);
|
||||||
let clone = metaWindow._delegate;
|
let clone = metaWindow._delegate;
|
||||||
let icon = this._windowIcons[mainIndex];
|
let overlay = this._windowOverlays[mainIndex];
|
||||||
|
|
||||||
clone.stackAbove = previousWindow.actor;
|
|
||||||
previousWindow = clone;
|
|
||||||
|
|
||||||
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
|
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
|
||||||
|
|
||||||
icon.hide();
|
overlay.hide();
|
||||||
Tweener.addTween(clone.actor,
|
Tweener.addTween(clone.actor,
|
||||||
{ x: x,
|
{ x: x,
|
||||||
y: y,
|
y: y,
|
||||||
@ -890,55 +1039,62 @@ Workspace.prototype = {
|
|||||||
time: Overview.ANIMATION_TIME,
|
time: Overview.ANIMATION_TIME,
|
||||||
transition: "easeOutQuad",
|
transition: "easeOutQuad",
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this._fadeInWindowIcon(clone, icon);
|
this._fadeInWindowOverlay(clone, overlay);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInWindowIcon: function (clone, icon) {
|
syncStacking: function(stackIndices) {
|
||||||
icon.opacity = 0;
|
let desktopClone = this._windows[0];
|
||||||
icon.show();
|
|
||||||
|
let visibleClones = this._getVisibleClones();
|
||||||
|
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||||
|
|
||||||
|
for (let i = 0; i < visibleClones.length; i++) {
|
||||||
|
let clone = visibleClones[i];
|
||||||
|
let metaWindow = clone.metaWindow;
|
||||||
|
if (i == 0) {
|
||||||
|
clone.setStackAbove(desktopClone.actor);
|
||||||
|
} else {
|
||||||
|
let previousClone = visibleClones[i - 1];
|
||||||
|
clone.setStackAbove(previousClone.actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeInWindowOverlay: function(clone, overlay) {
|
||||||
// This is a little messy and complicated because when we
|
// This is a little messy and complicated because when we
|
||||||
// start the fade-in we may not have done the final positioning
|
// start the fade-in we may not have done the final positioning
|
||||||
// of the workspaces. (Tweener doesn't necessarily finish
|
// of the workspaces. (Tweener doesn't necessarily finish
|
||||||
// all animations before calling onComplete callbacks.)
|
// all animations before calling onComplete callbacks.)
|
||||||
// So we need to manually compute where the window will
|
// So we need to manually compute where the window will
|
||||||
// be after the workspace animation finishes.
|
// be after the workspace animation finishes.
|
||||||
let [parentX, parentY] = icon.get_parent().get_position();
|
|
||||||
let [cloneX, cloneY] = clone.actor.get_position();
|
let [cloneX, cloneY] = clone.actor.get_position();
|
||||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||||
cloneX = this.gridX + this.scale * cloneX;
|
cloneX = this.gridX + this.scale * cloneX;
|
||||||
cloneY = this.gridY + this.scale * cloneY;
|
cloneY = this.gridY + this.scale * cloneY;
|
||||||
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
|
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
|
||||||
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
|
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
|
||||||
// Note we only round the first part, because we're still going to be
|
|
||||||
// positioned relative to the parent. By subtracting a possibly
|
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
|
||||||
// non-integral parent X/Y we cancel it out.
|
overlay.fadeIn();
|
||||||
let x = Math.round(cloneX + cloneWidth - icon.width) - parentX;
|
|
||||||
let y = Math.round(cloneY + cloneHeight - icon.height) - parentY;
|
|
||||||
icon.set_position(x, y);
|
|
||||||
icon.raise(this.actor);
|
|
||||||
Tweener.addTween(icon,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: Overview.ANIMATION_TIME,
|
|
||||||
transition: "easeOutQuad" });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_fadeInAllIcons: function () {
|
_fadeInAllOverlays: function() {
|
||||||
for (let i = 1; i < this._windows.length; i++) {
|
for (let i = 1; i < this._windows.length; i++) {
|
||||||
let clone = this._windows[i];
|
let clone = this._windows[i];
|
||||||
let icon = this._windowIcons[i];
|
let overlay = this._windowOverlays[i];
|
||||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
||||||
continue;
|
continue;
|
||||||
this._fadeInWindowIcon(clone, icon);
|
this._fadeInWindowOverlay(clone, overlay);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_hideAllIcons: function () {
|
_hideAllOverlays: function() {
|
||||||
for (let i = 1; i < this._windows.length; i++) {
|
for (let i = 1; i< this._windows.length; i++) {
|
||||||
let icon = this._windowIcons[i];
|
let overlay = this._windowOverlays[i];
|
||||||
icon.hide();
|
overlay.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -952,10 +1108,9 @@ Workspace.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
let clone = this._windows[index];
|
let clone = this._windows[index];
|
||||||
let icon = this._windowIcons[index];
|
|
||||||
|
|
||||||
this._windows.splice(index, 1);
|
this._windows.splice(index, 1);
|
||||||
this._windowIcons.splice(index, 1);
|
this._windowOverlays.splice(index, 1);
|
||||||
|
|
||||||
// If metaWin.get_compositor_private() returned non-NULL, that
|
// If metaWin.get_compositor_private() returned non-NULL, that
|
||||||
// means the window still exists (and is just being moved to
|
// means the window still exists (and is just being moved to
|
||||||
@ -973,7 +1128,6 @@ Workspace.prototype = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
clone.destroy();
|
clone.destroy();
|
||||||
icon.destroy();
|
|
||||||
|
|
||||||
this.positionWindows(false);
|
this.positionWindows(false);
|
||||||
this.updateRemovable();
|
this.updateRemovable();
|
||||||
@ -1043,7 +1197,7 @@ Workspace.prototype = {
|
|||||||
zoomFromOverview : function() {
|
zoomFromOverview : function() {
|
||||||
this.leavingOverview = true;
|
this.leavingOverview = true;
|
||||||
|
|
||||||
this._hideAllIcons();
|
this._hideAllOverlays();
|
||||||
|
|
||||||
Main.overview.connect('hidden', Lang.bind(this,
|
Main.overview.connect('hidden', Lang.bind(this,
|
||||||
this._doneLeavingOverview));
|
this._doneLeavingOverview));
|
||||||
@ -1080,7 +1234,7 @@ Workspace.prototype = {
|
|||||||
// Animates grid shrinking/expanding when a row or column
|
// Animates grid shrinking/expanding when a row or column
|
||||||
// of workspaces is added or removed
|
// of workspaces is added or removed
|
||||||
resizeToGrid : function (oldScale) {
|
resizeToGrid : function (oldScale) {
|
||||||
this._hideAllIcons();
|
this._hideAllOverlays();
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ x: this.gridX,
|
{ x: this.gridX,
|
||||||
y: this.gridY,
|
y: this.gridY,
|
||||||
@ -1088,7 +1242,7 @@ Workspace.prototype = {
|
|||||||
scale_y: this.scale,
|
scale_y: this.scale,
|
||||||
time: Overview.ANIMATION_TIME,
|
time: Overview.ANIMATION_TIME,
|
||||||
transition: "easeOutQuad",
|
transition: "easeOutQuad",
|
||||||
onComplete: Lang.bind(this, this._fadeInAllIcons)
|
onComplete: Lang.bind(this, this._fadeInAllOverlays)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1117,7 +1271,7 @@ Workspace.prototype = {
|
|||||||
slideOut : function(onComplete) {
|
slideOut : function(onComplete) {
|
||||||
let destX = this.actor.x, destY = this.actor.y;
|
let destX = this.actor.x, destY = this.actor.y;
|
||||||
|
|
||||||
this._hideAllIcons();
|
this._hideAllOverlays();
|
||||||
|
|
||||||
if (this.gridCol > this.gridRow)
|
if (this.gridCol > this.gridRow)
|
||||||
destX = global.screen_width;
|
destX = global.screen_width;
|
||||||
@ -1162,55 +1316,45 @@ Workspace.prototype = {
|
|||||||
|
|
||||||
// Tests if @win should be shown in the Overview
|
// Tests if @win should be shown in the Overview
|
||||||
_isOverviewWindow : function (win) {
|
_isOverviewWindow : function (win) {
|
||||||
let appMon = Shell.AppMonitor.get_default()
|
let tracker = Shell.WindowTracker.get_default()
|
||||||
return appMon.is_window_usage_tracked(win.get_meta_window());
|
return tracker.is_window_interesting(win.get_meta_window());
|
||||||
},
|
|
||||||
|
|
||||||
_createWindowIcon: function(window) {
|
|
||||||
let appSys = Shell.AppSystem.get_default();
|
|
||||||
let appMon = Shell.AppMonitor.get_default()
|
|
||||||
let appInfo = appMon.get_window_app(window.metaWindow);
|
|
||||||
let iconTexture = null;
|
|
||||||
// The design is application based, so prefer the application
|
|
||||||
// icon here if we have it. FIXME - should move this fallback code
|
|
||||||
// into ShellAppMonitor.
|
|
||||||
if (appInfo) {
|
|
||||||
iconTexture = appInfo.create_icon_texture(48);
|
|
||||||
} else {
|
|
||||||
let icon = window.metaWindow.icon;
|
|
||||||
iconTexture = new Clutter.Texture({ width: 48,
|
|
||||||
height: 48,
|
|
||||||
keep_aspect_ratio: true });
|
|
||||||
Shell.clutter_texture_set_from_pixbuf(iconTexture, icon);
|
|
||||||
}
|
|
||||||
return iconTexture;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Create a clone of a (non-desktop) window and add it to the window list
|
// Create a clone of a (non-desktop) window and add it to the window list
|
||||||
_addWindowClone : function(win) {
|
_addWindowClone : function(win) {
|
||||||
let icon = this._createWindowIcon(win);
|
|
||||||
this.parentActor.add_actor(icon);
|
|
||||||
|
|
||||||
let clone = new WindowClone(win);
|
let clone = new WindowClone(win);
|
||||||
|
let overlay = new WindowOverlay(clone, this.parentActor);
|
||||||
|
|
||||||
clone.connect('selected',
|
clone.connect('selected',
|
||||||
Lang.bind(this, this._onCloneSelected));
|
Lang.bind(this, this._onCloneSelected));
|
||||||
clone.connect('drag-begin',
|
clone.connect('drag-begin',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
icon.hide();
|
overlay.hide();
|
||||||
}));
|
}));
|
||||||
clone.connect('drag-end',
|
clone.connect('drag-end',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
icon.show();
|
overlay.show();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.actor.add_actor(clone.actor);
|
this.actor.add_actor(clone.actor);
|
||||||
|
|
||||||
|
overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose));
|
||||||
|
|
||||||
this._windows.push(clone);
|
this._windows.push(clone);
|
||||||
this._windowIcons.push(icon);
|
this._windowOverlays.push(overlay);
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onShowOverlayClose: function (windowOverlay) {
|
||||||
|
for (let i = 1; i < this._windowOverlays.length; i++) {
|
||||||
|
let overlay = this._windowOverlays[i];
|
||||||
|
if (overlay == windowOverlay)
|
||||||
|
continue;
|
||||||
|
overlay.hideCloseButton();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_computeWindowSlot : function(windowIndex, numberOfWindows) {
|
_computeWindowSlot : function(windowIndex, numberOfWindows) {
|
||||||
if (numberOfWindows in POSITIONS)
|
if (numberOfWindows in POSITIONS)
|
||||||
return POSITIONS[numberOfWindows][windowIndex];
|
return POSITIONS[numberOfWindows][windowIndex];
|
||||||
@ -1286,7 +1430,10 @@ function Workspaces(width, height, x, y) {
|
|||||||
|
|
||||||
Workspaces.prototype = {
|
Workspaces.prototype = {
|
||||||
_init : function(width, height, x, y) {
|
_init : function(width, height, x, y) {
|
||||||
this.actor = new Clutter.Group();
|
this.actor = new St.Bin({ style_class: "workspaces" });
|
||||||
|
this._actor = new Clutter.Group();
|
||||||
|
|
||||||
|
this.actor.add_actor(this._actor);
|
||||||
|
|
||||||
this._width = width;
|
this._width = width;
|
||||||
this._height = height;
|
this._height = height;
|
||||||
@ -1320,10 +1467,12 @@ Workspaces.prototype = {
|
|||||||
// workspaces have been created. This cannot be done first because
|
// workspaces have been created. This cannot be done first because
|
||||||
// window movement depends on the Workspaces object being accessible
|
// window movement depends on the Workspaces object being accessible
|
||||||
// as an Overview member.
|
// as an Overview member.
|
||||||
Main.overview.connect('showing',
|
this._overviewShowingId =
|
||||||
Lang.bind(this, function() {
|
Main.overview.connect('showing',
|
||||||
for (let w = 0; w < this._workspaces.length; w++)
|
Lang.bind(this, function() {
|
||||||
this._workspaces[w].zoomToOverview();
|
this._onRestacked();
|
||||||
|
for (let w = 0; w < this._workspaces.length; w++)
|
||||||
|
this._workspaces[w].zoomToOverview();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Track changes to the number of workspaces
|
// Track changes to the number of workspaces
|
||||||
@ -1333,6 +1482,9 @@ Workspaces.prototype = {
|
|||||||
this._switchWorkspaceNotifyId =
|
this._switchWorkspaceNotifyId =
|
||||||
global.window_manager.connect('switch-workspace',
|
global.window_manager.connect('switch-workspace',
|
||||||
Lang.bind(this, this._activeWorkspaceChanged));
|
Lang.bind(this, this._activeWorkspaceChanged));
|
||||||
|
this._restackedNotifyId =
|
||||||
|
global.screen.connect('restacked',
|
||||||
|
Lang.bind(this, this._onRestacked));
|
||||||
},
|
},
|
||||||
|
|
||||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||||
@ -1395,10 +1547,11 @@ Workspaces.prototype = {
|
|||||||
|
|
||||||
this._windowSelectionAppId = appId;
|
this._windowSelectionAppId = appId;
|
||||||
|
|
||||||
let appSys = Shell.AppMonitor.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
let showOnlyWindows = {};
|
let showOnlyWindows = {};
|
||||||
let windows = appSys.get_windows_for_app(appId);
|
let app = appSys.get_app(appId);
|
||||||
|
let windows = app.get_windows();
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
showOnlyWindows[windows[i]] = 1;
|
showOnlyWindows[windows[i]] = 1;
|
||||||
}
|
}
|
||||||
@ -1422,9 +1575,6 @@ Workspaces.prototype = {
|
|||||||
this._clearApplicationWindowSelection(false);
|
this._clearApplicationWindowSelection(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let clone = this._lookupCloneForMetaWindow (metaWindow);
|
|
||||||
clone.actor.raise_top();
|
|
||||||
|
|
||||||
Main.activateWindow(metaWindow, time);
|
Main.activateWindow(metaWindow, time);
|
||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
},
|
},
|
||||||
@ -1448,8 +1598,10 @@ Workspaces.prototype = {
|
|||||||
this.actor.destroy();
|
this.actor.destroy();
|
||||||
this.actor = null;
|
this.actor = null;
|
||||||
|
|
||||||
|
Main.overview.disconnect(this._overviewShowingId);
|
||||||
global.screen.disconnect(this._nWorkspacesNotifyId);
|
global.screen.disconnect(this._nWorkspacesNotifyId);
|
||||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
|
global.screen.disconnect(this._restackedNotifyId);
|
||||||
},
|
},
|
||||||
|
|
||||||
getScale : function() {
|
getScale : function() {
|
||||||
@ -1590,9 +1742,22 @@ Workspaces.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_addWorkspaceActor : function(workspaceNum) {
|
_addWorkspaceActor : function(workspaceNum) {
|
||||||
let workspace = new Workspace(workspaceNum, this.actor);
|
let workspace = new Workspace(workspaceNum, this._actor);
|
||||||
this._workspaces[workspaceNum] = workspace;
|
this._workspaces[workspaceNum] = workspace;
|
||||||
this.actor.add_actor(workspace.actor);
|
this._actor.add_actor(workspace.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRestacked: function() {
|
||||||
|
let stack = global.get_windows();
|
||||||
|
let stackIndices = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < stack.length; i++) {
|
||||||
|
// Use the stable sequence for an integer to use as a hash key
|
||||||
|
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._workspaces.length; i++)
|
||||||
|
this._workspaces[i].syncStacking(stackIndices);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Handles a drop onto the (+) button; assumes the new workspace
|
// Handles a drop onto the (+) button; assumes the new workspace
|
||||||
|
@ -3,11 +3,14 @@ ca
|
|||||||
cs
|
cs
|
||||||
da
|
da
|
||||||
de
|
de
|
||||||
|
el
|
||||||
en_GB
|
en_GB
|
||||||
es
|
es
|
||||||
|
fi
|
||||||
fr
|
fr
|
||||||
ga
|
ga
|
||||||
gl
|
gl
|
||||||
|
he
|
||||||
hu
|
hu
|
||||||
it
|
it
|
||||||
ko
|
ko
|
||||||
@ -16,6 +19,8 @@ nl
|
|||||||
pa
|
pa
|
||||||
pl
|
pl
|
||||||
pt_BR
|
pt_BR
|
||||||
|
ro
|
||||||
|
ru
|
||||||
sl
|
sl
|
||||||
sv
|
sv
|
||||||
tr
|
tr
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
data/gnome-shell.desktop.in.in
|
data/gnome-shell.desktop.in.in
|
||||||
js/ui/panel.js
|
js/ui/appDisplay.js
|
||||||
|
js/ui/appIcon.js
|
||||||
js/ui/dash.js
|
js/ui/dash.js
|
||||||
js/ui/overview.js
|
js/ui/overview.js
|
||||||
|
js/ui/panel.js
|
||||||
|
js/ui/placeDisplay.js
|
||||||
js/ui/runDialog.js
|
js/ui/runDialog.js
|
||||||
|
js/ui/widget.js
|
||||||
src/gdmuser/gdm-user.c
|
src/gdmuser/gdm-user.c
|
||||||
src/shell-global.c
|
src/shell-global.c
|
||||||
src/shell-status-menu.c
|
|
||||||
src/shell-uri-util.c
|
src/shell-uri-util.c
|
||||||
|
100
po/ar.po
@ -6,8 +6,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: HEAD\n"
|
"Project-Id-Version: HEAD\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-09-25 04:22+0200\n"
|
"POT-Creation-Date: 2009-11-08 20:55+0200\n"
|
||||||
"PO-Revision-Date: 2009-09-25 04:21+0300\n"
|
"PO-Revision-Date: 2009-11-08 20:55+0300\n"
|
||||||
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
|
||||||
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
"Language-Team: Arabic <doc@arabeyes.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -16,7 +16,7 @@ msgstr ""
|
|||||||
"Language: ar\n"
|
"Language: ar\n"
|
||||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||||
"X-Generator: Virtaal 0.4.0\n"
|
"X-Generator: Virtaal 0.4.1\n"
|
||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
msgid "GNOME Shell"
|
msgid "GNOME Shell"
|
||||||
@ -26,63 +26,106 @@ msgstr "صدفة جنوم"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "إدارة النوافذ وإطلاق التطبيقات"
|
msgstr "إدارة النوافذ وإطلاق التطبيقات"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:332
|
||||||
#: ../js/ui/panel.js:269
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "متكرر"
|
||||||
msgstr "الأنشطة"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appDisplay.js:867
|
||||||
#: ../js/ui/panel.js:452
|
msgid "Drag here to add favorites"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "اسحب إلى هنا ليضاف إلى المفضّلة"
|
||||||
msgstr "%A %Ol:%OM %p"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appIcon.js:426
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "نافذة جديدة"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "أزِل من المفضّلة"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:431
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "أضِف إلى المفضّلة"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:267
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "ابحث..."
|
msgstr "ابحث..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
#: ../js/ui/dash.js:376
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "استعرض"
|
msgstr "المزيد"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
#: ../js/ui/dash.js:532
|
||||||
msgid "(see all)"
|
msgid "(see all)"
|
||||||
msgstr "(انظر الكل)"
|
msgstr "(انظر الكل)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
#: ../js/ui/dash.js:711 ../js/ui/dash.js:773
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "التطبيقات"
|
msgstr "التطبيقات"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:731
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "الأماكن"
|
msgstr "الأماكن"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
#: ../js/ui/dash.js:738 ../js/ui/dash.js:783
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "المستندات الحديثة"
|
msgstr "المستندات الحديثة"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:947
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "نتائج البحث"
|
msgstr "نتائج البحث"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#: ../js/ui/dash.js:778
|
||||||
msgid "PREFERENCES"
|
msgid "PREFERENCES"
|
||||||
msgstr "التفضيلات"
|
msgstr "التفضيلات"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:95
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "الأنشطة"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%A %Ol:%OM %p"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "اتّصل ب..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "من فضلك اكتب أمرا:"
|
msgstr "من فضلك اكتب أمرا:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "فشل تنفيذ '%s':"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%OH:%OM"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "التطبيقات"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "المستندات الحديثة"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "منذ أقل من دقيقة"
|
msgstr "منذ أقل من دقيقة"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
@ -93,7 +136,7 @@ msgstr[3] "منذ %d دقائق"
|
|||||||
msgstr[4] "منذ %d دقيقة"
|
msgstr[4] "منذ %d دقيقة"
|
||||||
msgstr[5] "منذ %d دقيقة"
|
msgstr[5] "منذ %d دقيقة"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:827
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
@ -104,7 +147,7 @@ msgstr[3] "منذ %d ساعات"
|
|||||||
msgstr[4] "منذ %d ساعة"
|
msgstr[4] "منذ %d ساعة"
|
||||||
msgstr[5] "منذ %d ساعة"
|
msgstr[5] "منذ %d ساعة"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:830
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
@ -115,7 +158,7 @@ msgstr[3] "منذ %d أيام"
|
|||||||
msgstr[4] "منذ %d يوما"
|
msgstr[4] "منذ %d يوما"
|
||||||
msgstr[5] "منذ %d يوم"
|
msgstr[5] "منذ %d يوم"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:833
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -199,3 +242,6 @@ msgstr "ابحث"
|
|||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "استعرض"
|
||||||
|
105
po/ca.po
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: HEAD\n"
|
"Project-Id-Version: HEAD\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-08-30 18:53+0200\n"
|
"POT-Creation-Date: 2009-10-10 20:19+0200\n"
|
||||||
"PO-Revision-Date: 2009-08-30 18:57+0100\n"
|
"PO-Revision-Date: 2009-10-10 20:22+0100\n"
|
||||||
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
|
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -24,82 +24,126 @@ msgstr "GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Gestió de finestres i execució d'aplicacions"
|
msgstr "Gestió de finestres i execució d'aplicacions"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:335
|
||||||
#: ../js/ui/panel.js:271
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "Freqüent"
|
||||||
msgstr "Activitats"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:462
|
||||||
#: ../js/ui/panel.js:454
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Finestra nova"
|
||||||
msgstr "%a %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:256
|
#: ../js/ui/appIcon.js:475
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Elimina dels preferits"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:476
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Afegeix als preferits"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Cerca..."
|
msgstr "Cerca..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:374
|
#: ../js/ui/dash.js:400
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "Navega"
|
msgstr "Més"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:451
|
#: ../js/ui/dash.js:543
|
||||||
msgid "(see all)"
|
msgid "(see all)"
|
||||||
msgstr "(mostra tot)"
|
msgstr "(mostra tot)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:633
|
#: ../js/ui/dash.js:763
|
||||||
#: ../js/ui/dash.js:681
|
#: ../js/ui/dash.js:825
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "APLICACIONS"
|
msgstr "APLICACIONS"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:653
|
#: ../js/ui/dash.js:783
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "LLOCS"
|
msgstr "LLOCS"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:660
|
#: ../js/ui/dash.js:790
|
||||||
#: ../js/ui/dash.js:692
|
#: ../js/ui/dash.js:835
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "DOCUMENTS RECENTS"
|
msgstr "DOCUMENTS RECENTS"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:679
|
#: ../js/ui/dash.js:815
|
||||||
|
#: ../js/ui/dash.js:955
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "RESULTATS DE LA CERCA"
|
msgstr "RESULTATS DE LA CERCA"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:82
|
#: ../js/ui/dash.js:830
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "PREFERÈNCIES"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:272
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Activitats"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:464
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Connecta a..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Introduïu una ordre:"
|
msgstr "Introduïu una ordre:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:840
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "No s'ha pogut executar «%s»:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:162
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:316
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplicacions"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:341
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documents recents"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Fa menys d'un minut"
|
msgstr "Fa menys d'un minut"
|
||||||
|
|
||||||
#: ../src/shell-global.c:843
|
#: ../src/shell-global.c:815
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "Fa %d minut"
|
msgstr[0] "Fa %d minut"
|
||||||
msgstr[1] "Fa %d minuts"
|
msgstr[1] "Fa %d minuts"
|
||||||
|
|
||||||
#: ../src/shell-global.c:846
|
#: ../src/shell-global.c:818
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "Fa %d hora"
|
msgstr[0] "Fa %d hora"
|
||||||
msgstr[1] "Fa %d hores"
|
msgstr[1] "Fa %d hores"
|
||||||
|
|
||||||
#: ../src/shell-global.c:849
|
#: ../src/shell-global.c:821
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "Fa %d dia"
|
msgstr[0] "Fa %d dia"
|
||||||
msgstr[1] "Fa %d dies"
|
msgstr[1] "Fa %d dies"
|
||||||
|
|
||||||
#: ../src/shell-global.c:852
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -180,10 +224,3 @@ msgstr "Cerca"
|
|||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
#~ msgid "Find apps or documents"
|
|
||||||
#~ msgstr "Cerca aplicacions o documents"
|
|
||||||
#~ msgid "Manager"
|
|
||||||
#~ msgstr "Gestor"
|
|
||||||
#~ msgid "The user manager object this user is controlled by."
|
|
||||||
#~ msgstr "L'objecte gestor d'usuaris que controla a aquest usuari."
|
|
||||||
|
|
||||||
|
103
po/cs.po
@ -7,14 +7,15 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell\n"
|
"Project-Id-Version: gnome-shell\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-09-22 13:36+0200\n"
|
"POT-Creation-Date: 2009-11-05 18:03+0100\n"
|
||||||
"PO-Revision-Date: 2009-09-22 13:37+0200\n"
|
"PO-Revision-Date: 2009-11-05 18:03+0100\n"
|
||||||
"Last-Translator: Andre Klapper <ak-47@gmx.net>, 2009\n"
|
"Last-Translator: Petr Kovar <pknbe@volny.cz>\n"
|
||||||
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
|
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||||
|
"X-Generator: Lokalize 1.0\n"
|
||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
msgid "GNOME Shell"
|
msgid "GNOME Shell"
|
||||||
@ -24,63 +25,106 @@ msgstr "Prostředí GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Správa oken a spouštění aplikací"
|
msgstr "Správa oken a spouštění aplikací"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:332
|
||||||
#: ../js/ui/panel.js:269
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "Časté"
|
||||||
msgstr "Činnosti"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appDisplay.js:867
|
||||||
#: ../js/ui/panel.js:452
|
msgid "Drag here to add favorites"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Oblíbené přidáte přetažením sem"
|
||||||
msgstr "%a, %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appIcon.js:426
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Nové okno"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Odstranit z oblíbených"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:431
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Přidat mezi oblíbené"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:267
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Najít..."
|
msgstr "Najít..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
#: ../js/ui/dash.js:376
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "Procházet"
|
msgstr "Více"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
#: ../js/ui/dash.js:532
|
||||||
msgid "(see all)"
|
msgid "(see all)"
|
||||||
msgstr "(zobrazit vše)"
|
msgstr "(zobrazit vše)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
#: ../js/ui/dash.js:711 ../js/ui/dash.js:773
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "APLIKACE"
|
msgstr "APLIKACE"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:731
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "MÍSTA"
|
msgstr "MÍSTA"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
#: ../js/ui/dash.js:738 ../js/ui/dash.js:783
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "NEDÁVNÉ DOKUMENTY"
|
msgstr "NEDÁVNÉ DOKUMENTY"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:947
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "VÝSLEDKY HLEDÁNÍ"
|
msgstr "VÝSLEDKY HLEDÁNÍ"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#: ../js/ui/dash.js:778
|
||||||
msgid "PREFERENCES"
|
msgid "PREFERENCES"
|
||||||
msgstr "NASTAVENÍ"
|
msgstr "PŘEDVOLBY"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:101
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Činnosti"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a, %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Připojit se k..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Zadejte prosím příkaz:"
|
msgstr "Zadejte prosím příkaz:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Vykonání \"%s\" selhalo:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplikace"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Nedávné dokumenty"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Před méně než minutou"
|
msgstr "Před méně než minutou"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
@ -88,7 +132,7 @@ msgstr[0] "Před %d minutou"
|
|||||||
msgstr[1] "Před %d minutami"
|
msgstr[1] "Před %d minutami"
|
||||||
msgstr[2] "Před %d minutami"
|
msgstr[2] "Před %d minutami"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:827
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
@ -96,7 +140,7 @@ msgstr[0] "Před %d hodinou"
|
|||||||
msgstr[1] "Před %d hodinami"
|
msgstr[1] "Před %d hodinami"
|
||||||
msgstr[2] "Před %d hodinami"
|
msgstr[2] "Před %d hodinami"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:830
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
@ -104,7 +148,7 @@ msgstr[0] "Před %d dnem"
|
|||||||
msgstr[1] "Před %d dny"
|
msgstr[1] "Před %d dny"
|
||||||
msgstr[2] "Před %d dny"
|
msgstr[2] "Před %d dny"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:833
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -186,6 +230,9 @@ msgstr "Hledat"
|
|||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Procházet"
|
||||||
|
|
||||||
#~ msgid "Manager"
|
#~ msgid "Manager"
|
||||||
#~ msgstr "Správce"
|
#~ msgstr "Správce"
|
||||||
|
|
||||||
|
96
po/da.po
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell\n"
|
"Project-Id-Version: gnome-shell\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-09-04 01:26+0200\n"
|
"POT-Creation-Date: 2009-10-21 22:51+0200\n"
|
||||||
"PO-Revision-Date: 2009-09-01 21:48+0200\n"
|
"PO-Revision-Date: 2009-10-18 17:32+0200\n"
|
||||||
"Last-Translator: Kris Thomsen <lakristho@gmail.com>\n"
|
"Last-Translator: Kris Thomsen <lakristho@gmail.com>\n"
|
||||||
"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
|
"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -24,80 +24,123 @@ msgstr "Skal til GNOME"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Vinduehåndtering og åbning af programmer"
|
msgstr "Vinduehåndtering og åbning af programmer"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:332
|
||||||
#: ../js/ui/panel.js:271
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "Ofte"
|
||||||
msgstr "Aktiviteter"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:410
|
||||||
#: ../js/ui/panel.js:454
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Nyt vindue"
|
||||||
msgstr "%a %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:256
|
#: ../js/ui/appIcon.js:414
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Fjern fra favoritter"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:415
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Tilføj til favoritter"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Find..."
|
msgstr "Find..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:374
|
#: ../js/ui/dash.js:400
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "Gennemse"
|
msgstr "Mere"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:511
|
#: ../js/ui/dash.js:543
|
||||||
msgid "(see all)"
|
msgid "(see all)"
|
||||||
msgstr "(se alle)"
|
msgstr "(se alle)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:705 ../js/ui/dash.js:761 ../js/ui/dash.js:893
|
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "PROGRAMMER"
|
msgstr "PROGRAMMER"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:725
|
#: ../js/ui/dash.js:745
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "STEDER"
|
msgstr "STEDER"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:732 ../js/ui/dash.js:773 ../js/ui/dash.js:867
|
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "SENESTE DOKUMENTER"
|
msgstr "SENESTE DOKUMENTER"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:751 ../js/ui/dash.js:856 ../js/ui/dash.js:882
|
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "SØGERESULTATER"
|
msgstr "SØGERESULTATER"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:82
|
#: ../js/ui/dash.js:792
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "INDSTILLINGER"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Aktiviteter"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Forbind til..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Indtast en kommando:"
|
msgstr "Indtast en kommando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:840
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Kørsel af \"%s\" mislykkedes:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Programmer"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Seneste dokumenter"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Mindre end et minut siden"
|
msgstr "Mindre end et minut siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:843
|
#: ../src/shell-global.c:815
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d minut siden"
|
msgstr[0] "%d minut siden"
|
||||||
msgstr[1] "%d minutter siden"
|
msgstr[1] "%d minutter siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:846
|
#: ../src/shell-global.c:818
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d time siden"
|
msgstr[0] "%d time siden"
|
||||||
msgstr[1] "%d timer siden"
|
msgstr[1] "%d timer siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:849
|
#: ../src/shell-global.c:821
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d dag siden"
|
msgstr[0] "%d dag siden"
|
||||||
msgstr[1] "%d dage siden"
|
msgstr[1] "%d dage siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:852
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -178,6 +221,9 @@ msgstr "Søg"
|
|||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Gennemse"
|
||||||
|
|
||||||
#~ msgid "Find apps or documents"
|
#~ msgid "Find apps or documents"
|
||||||
#~ msgstr "Find programmer eller dokumenter"
|
#~ msgstr "Find programmer eller dokumenter"
|
||||||
|
|
||||||
|
139
po/de.po
@ -4,18 +4,21 @@
|
|||||||
#
|
#
|
||||||
# Hendrik Brandt <heb@gnome-de.org>, 2009.
|
# Hendrik Brandt <heb@gnome-de.org>, 2009.
|
||||||
# Hendrik Richter <hendrikr@gnome.org>, 2009.
|
# Hendrik Richter <hendrikr@gnome.org>, 2009.
|
||||||
|
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009.
|
||||||
|
# Mario Blättermann <mariobl@gnome.org>, 2009.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: HEAD\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"POT-Creation-Date: 2009-08-20 16:47+0200\n"
|
"shell&component=general\n"
|
||||||
"PO-Revision-Date: 2009-08-20 16:49+0200\n"
|
"POT-Creation-Date: 2009-10-16 20:05+0000\n"
|
||||||
"Last-Translator: Hendrik Richter <hendrikr@gnome.org>\n"
|
"PO-Revision-Date: 2009-10-17 23:37+0200\n"
|
||||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
|
||||||
|
"Language-Team: German <gnome-de@gnome.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: UTF-8\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
@ -26,79 +29,123 @@ msgstr "GNOME-Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Fenster verwalten und Anwendungen starten"
|
msgstr "Fenster verwalten und Anwendungen starten"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:335
|
||||||
#: ../js/ui/panel.js:266
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "Häufig"
|
||||||
msgstr "Aktivitäten"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:407
|
||||||
#: ../js/ui/panel.js:433
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Neues Fenster"
|
||||||
msgstr "%a %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:251
|
#: ../js/ui/appIcon.js:420
|
||||||
msgid "Find apps or documents"
|
msgid "Remove from Favorites"
|
||||||
msgstr "Anwendungen oder Dokumente suchen"
|
msgstr "Aus Favoriten entfernen"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:369
|
#: ../js/ui/appIcon.js:421
|
||||||
msgid "Browse"
|
msgid "Add to Favorites"
|
||||||
msgstr "Durchsuchen"
|
msgstr "Zu Favoriten hinzufügen"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Suchen …"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:400
|
||||||
|
msgid "More"
|
||||||
|
msgstr "Mehr"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(alle sehen)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
|
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "ANWENDUNGEN"
|
msgstr "ANWENDUNGEN"
|
||||||
|
|
||||||
#. **** Documents ****
|
|
||||||
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
|
|
||||||
msgid "RECENT DOCUMENTS"
|
|
||||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:598
|
#: ../js/ui/dash.js:745
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "ORTE"
|
msgstr "ORTE"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:75
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "SUCHERGEBNISSE"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:792
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "EINSTELLUNGEN"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:273
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Aktivitäten"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:465
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Verbinden mit …"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Bitte geben Sie einen Befehl ein:"
|
msgstr "Bitte geben Sie einen Befehl ein:"
|
||||||
|
|
||||||
#: ../src/gdmuser/gdm-user.c:243
|
#: ../js/ui/runDialog.js:173
|
||||||
msgid "Manager"
|
#, c-format
|
||||||
msgstr "Verwaltung"
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Ausführung von »%s« ist gescheitert:"
|
||||||
|
|
||||||
#: ../src/gdmuser/gdm-user.c:244
|
#. Translators: This is a time format.
|
||||||
msgid "The user manager object this user is controlled by."
|
#: ../js/ui/widget.js:162
|
||||||
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
#: ../src/shell-global.c:841
|
#: ../js/ui/widget.js:316
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Anwendungen"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:341
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Zuletzt geöffnete Dokumente"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Vor weniger als einer Minute"
|
msgstr "Vor weniger als einer Minute"
|
||||||
|
|
||||||
#: ../src/shell-global.c:844
|
#: ../src/shell-global.c:815
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "Vor %d Minute"
|
msgstr[0] "Vor %d Minute"
|
||||||
msgstr[1] "Vor %d Minuten"
|
msgstr[1] "Vor %d Minuten"
|
||||||
|
|
||||||
#: ../src/shell-global.c:847
|
#: ../src/shell-global.c:818
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "Vor %d Stunde"
|
msgstr[0] "Vor %d Stunde"
|
||||||
msgstr[1] "Vor %d Stunden"
|
msgstr[1] "Vor %d Stunden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:850
|
#: ../src/shell-global.c:821
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "Vor %d Tag"
|
msgstr[0] "Vor %d Tag"
|
||||||
msgstr[1] "Vor %d Tagen"
|
msgstr[1] "Vor %d Tagen"
|
||||||
|
|
||||||
#: ../src/shell-global.c:853
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -180,3 +227,15 @@ msgstr "Suchen"
|
|||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Find apps or documents"
|
||||||
|
#~ msgstr "Anwendungen oder Dokumente suchen"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Durchsuchen"
|
||||||
|
|
||||||
|
#~ msgid "Manager"
|
||||||
|
#~ msgstr "Verwaltung"
|
||||||
|
|
||||||
|
#~ msgid "The user manager object this user is controlled by."
|
||||||
|
#~ msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
|
||||||
|
186
po/el.po
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
# translation of gnome-shell.po.master.po to Greek
|
||||||
|
# Greek translation for gnome-shell.
|
||||||
|
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
|
#
|
||||||
|
# Jennie Petoumenou <epetoumenou@gmail.com>, 2009.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnome-shell.po.master\n"
|
||||||
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
|
||||||
|
"POT-Creation-Date: 2009-10-04 08:03+0000\n"
|
||||||
|
"PO-Revision-Date: 2009-10-04 10:25+0200\n"
|
||||||
|
"Last-Translator: Jennie Petoumenou <epetoumenou@gmail.com>\n"
|
||||||
|
"Language-Team: Greek <<team AT BLOCKSPAM gnome DOT gr>>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: KBabel 1.11.4\n"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
|
msgid "GNOME Shell"
|
||||||
|
msgstr "Κέλυφος GNOME"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
|
msgid "Window management and application launching"
|
||||||
|
msgstr "Διαχείριση παραθύρων και εκκίνηση εφαρμογών"
|
||||||
|
|
||||||
|
#. left side
|
||||||
|
#: ../js/ui/panel.js:271
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Δραστηριότητες"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:461
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Εύρεση..."
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:400
|
||||||
|
msgid "More"
|
||||||
|
msgstr "Περισσότερα"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(εμφάνιση όλων)"
|
||||||
|
|
||||||
|
#. **** Applications ****
|
||||||
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "ΕΦΑΡΜΟΓΕΣ"
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:783
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "ΤΟΠΟΘΕΣΙΕΣ"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "ΠΡΟΣΦΑΤΑ ΕΓΓΡΑΦΑ"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "ΑΠΟΤΕΛΕΣΜΑΤΑ ΑΝΑΖΗΤΗΣΗΣ"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:830
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "ΠΡΟΤΙΜΗΣΕΙΣ"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
|
msgid "Please enter a command:"
|
||||||
|
msgstr "Παρακαλώ εισάγετε μία εντολή:"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "Λιγότερο από ένα λεπτό πριν"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:815
|
||||||
|
#, c-format
|
||||||
|
msgid "%d minute ago"
|
||||||
|
msgid_plural "%d minutes ago"
|
||||||
|
msgstr[0] "%d λεπτό πριν"
|
||||||
|
msgstr[1] "%d λεπτά πριν"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:818
|
||||||
|
#, c-format
|
||||||
|
msgid "%d hour ago"
|
||||||
|
msgid_plural "%d hours ago"
|
||||||
|
msgstr[0] "%d ώρα πριν"
|
||||||
|
msgstr[1] "%d ώρες πριν"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
|
#, c-format
|
||||||
|
msgid "%d day ago"
|
||||||
|
msgid_plural "%d days ago"
|
||||||
|
msgstr[0] "%d ημέρα πριν"
|
||||||
|
msgstr[1] "%d ημέρες πριν"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:824
|
||||||
|
#, c-format
|
||||||
|
msgid "%d week ago"
|
||||||
|
msgid_plural "%d weeks ago"
|
||||||
|
msgstr[0] "%d εβδομάδα πριν"
|
||||||
|
msgstr[1] "%d εβδομάδες πριν"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:156
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Άγνωστο"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:212
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't lock screen: %s"
|
||||||
|
msgstr "Αδύνατο το κλείδωμα της οθόνης: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:227
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
msgstr "Δεν είναι δυνατή η προσωρινή ρύθμιση της προστασίας οθόνης σε κενή οθόνη: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't logout: %s"
|
||||||
|
msgstr "Αδύνατη η αποσύνδεση: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:492
|
||||||
|
msgid "Account Information..."
|
||||||
|
msgstr "Πληροφορίες λογαριασμού..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:502
|
||||||
|
msgid "Sidebar"
|
||||||
|
msgstr "Πλευρική στήλη"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:510
|
||||||
|
msgid "System Preferences..."
|
||||||
|
msgstr "Προστιμήσεις συστήματος..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:525
|
||||||
|
msgid "Lock Screen"
|
||||||
|
msgstr "Κλείδωμα οθόνης"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:535
|
||||||
|
msgid "Switch User"
|
||||||
|
msgstr "Αλλαγή χρήστη"
|
||||||
|
|
||||||
|
#. Only show switch user if there are other users
|
||||||
|
#. Log Out
|
||||||
|
#: ../src/shell-status-menu.c:546
|
||||||
|
msgid "Log Out..."
|
||||||
|
msgstr "Αποσύνδεση..."
|
||||||
|
|
||||||
|
#. Shut down
|
||||||
|
#: ../src/shell-status-menu.c:557
|
||||||
|
msgid "Shut Down..."
|
||||||
|
msgstr "Τερματισμός..."
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:87
|
||||||
|
msgid "Home Folder"
|
||||||
|
msgstr "Προσωπικός φάκελος"
|
||||||
|
|
||||||
|
#. Translators: this is the same string as the one found in
|
||||||
|
#. * nautilus
|
||||||
|
#: ../src/shell-uri-util.c:102
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "Σύστημα αρχείων"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:248
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Αναζήτηση"
|
||||||
|
|
||||||
|
#. Translators: the first string is the name of a gvfs
|
||||||
|
#. * method, and the second string is a path. For
|
||||||
|
#. * example, "Trash: some-directory". It means that the
|
||||||
|
#. * directory called "some-directory" is in the trash.
|
||||||
|
#.
|
||||||
|
#: ../src/shell-uri-util.c:298
|
||||||
|
#, c-format
|
||||||
|
msgid "%1$s: %2$s"
|
||||||
|
msgstr "%1$s: %2$s"
|
||||||
|
|
216
po/es.po
@ -8,13 +8,13 @@ msgstr ""
|
|||||||
"Project-Id-Version: gnome-shell master\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"shell&component=general\n"
|
"shell&component=general\n"
|
||||||
"POT-Creation-Date: 2009-09-27 16:05+0000\n"
|
"POT-Creation-Date: 2009-12-18 15:06+0000\n"
|
||||||
"PO-Revision-Date: 2009-09-28 21:58+0200\n"
|
"PO-Revision-Date: 2009-12-19 14:41+0100\n"
|
||||||
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
|
"Last-Translator: Jorge González <jorgegonz@svn.gnome.org>\n"
|
||||||
"Language-Team: Español <gnome-es-list@gnome.org>\n"
|
"Language-Team: Español <gnome-es-list@gnome.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: UTF-8\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
@ -25,153 +25,132 @@ msgstr "GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Gestión de ventanas e inicio de aplicaciones"
|
msgstr "Gestión de ventanas e inicio de aplicaciones"
|
||||||
|
|
||||||
#. left side
|
#. **** Applications ****
|
||||||
#: ../js/ui/panel.js:269
|
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:852
|
||||||
msgid "Activities"
|
msgid "APPLICATIONS"
|
||||||
msgstr "Actividades"
|
msgstr "APLICACIONES"
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appDisplay.js:276
|
||||||
#: ../js/ui/panel.js:452
|
msgid "PREFERENCES"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "PREFERENCIAS"
|
||||||
msgstr "%a %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Ventana nueva"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Quitar de los favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Añadir a los favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:1064
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Arrastrar aquí para añadir a los favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:240
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Buscar…"
|
msgstr "Buscar…"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
#: ../js/ui/dash.js:437
|
||||||
msgid "More"
|
#| msgid "Search"
|
||||||
msgstr "Más"
|
msgid "Searching..."
|
||||||
|
msgstr "Buscando…"
|
||||||
#: ../js/ui/dash.js:543
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(ver todo)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
|
||||||
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
|
||||||
msgid "APPLICATIONS"
|
|
||||||
msgstr "APLICACIONES"
|
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:783
|
#: ../js/ui/dash.js:872 ../js/ui/placeDisplay.js:471
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "LUGARES"
|
msgstr "LUGARES"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
#: ../js/ui/dash.js:879
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "DOCUMENTOS RECIENTES"
|
msgstr "DOCUMENTOS RECIENTES"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. Button on the left side of the panel.
|
||||||
#: ../js/ui/dash.js:815 ../js/ui/dash.js:958
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
msgid "SEARCH RESULTS"
|
#: ../js/ui/panel.js:227
|
||||||
msgstr "RESULTADOS DE LA BÚSQUEDA"
|
msgid "Activities"
|
||||||
|
msgstr "Actividades"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:830
|
#. Translators: This is a time format.
|
||||||
msgid "PREFERENCES"
|
#: ../js/ui/panel.js:440
|
||||||
msgstr "PREFERENCIAS"
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H:%M"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:94
|
#: ../js/ui/placeDisplay.js:99
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Conectar a…"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Introduzca un comando:"
|
msgstr "Introduzca un comando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Falló la ejecución de «%s»:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplicaciones"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documentos recientes"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:826
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Hace menos de un minuto"
|
msgstr "Hace menos de un minuto"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:829
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "Hace %d minuto"
|
msgstr[0] "Hace %d minuto"
|
||||||
msgstr[1] "Hace %d minutos"
|
msgstr[1] "Hace %d minutos"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:832
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "Hace %d hora"
|
msgstr[0] "Hace %d hora"
|
||||||
msgstr[1] "Hace %d horas"
|
msgstr[1] "Hace %d horas"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:835
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "Hace %d día"
|
msgstr[0] "Hace %d día"
|
||||||
msgstr[1] "Hace %d días"
|
msgstr[1] "Hace %d días"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:838
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
msgstr[0] "Hace %d semana"
|
msgstr[0] "Hace %d semana"
|
||||||
msgstr[1] "Hace %d semanas"
|
msgstr[1] "Hace %d semanas"
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:156
|
#: ../src/shell-uri-util.c:89
|
||||||
msgid "Unknown"
|
|
||||||
msgstr "Desconocido"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:212
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't lock screen: %s"
|
|
||||||
msgstr "No se puede bloquear la pantalla: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:227
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
|
||||||
msgstr ""
|
|
||||||
"No se puede establecer temporalmente el salvapantallas a oscurecer pantalla: "
|
|
||||||
"%s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:351
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't logout: %s"
|
|
||||||
msgstr "No se puede salir de la sesión: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:492
|
|
||||||
msgid "Account Information..."
|
|
||||||
msgstr "Información de la cuenta…"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:502
|
|
||||||
msgid "Sidebar"
|
|
||||||
msgstr "Barra lateral"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:510
|
|
||||||
msgid "System Preferences..."
|
|
||||||
msgstr "Preferencias del sistema…"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:525
|
|
||||||
msgid "Lock Screen"
|
|
||||||
msgstr "Bloquear la pantalla"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:535
|
|
||||||
msgid "Switch User"
|
|
||||||
msgstr "Cambiar de usuario"
|
|
||||||
|
|
||||||
#. Only show switch user if there are other users
|
|
||||||
#. Log Out
|
|
||||||
#: ../src/shell-status-menu.c:546
|
|
||||||
msgid "Log Out..."
|
|
||||||
msgstr "Salir…"
|
|
||||||
|
|
||||||
#. Shut down
|
|
||||||
#: ../src/shell-status-menu.c:557
|
|
||||||
msgid "Shut Down..."
|
|
||||||
msgstr "Apagar…"
|
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:87
|
|
||||||
msgid "Home Folder"
|
msgid "Home Folder"
|
||||||
msgstr "Carpeta personal"
|
msgstr "Carpeta personal"
|
||||||
|
|
||||||
#. Translators: this is the same string as the one found in
|
#. Translators: this is the same string as the one found in
|
||||||
#. * nautilus
|
#. * nautilus
|
||||||
#: ../src/shell-uri-util.c:102
|
#: ../src/shell-uri-util.c:104
|
||||||
msgid "File System"
|
msgid "File System"
|
||||||
msgstr "Sistema de archivos"
|
msgstr "Sistema de archivos"
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:248
|
#: ../src/shell-uri-util.c:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Buscar"
|
msgstr "Buscar"
|
||||||
|
|
||||||
@ -180,11 +159,58 @@ msgstr "Buscar"
|
|||||||
#. * example, "Trash: some-directory". It means that the
|
#. * example, "Trash: some-directory". It means that the
|
||||||
#. * directory called "some-directory" is in the trash.
|
#. * directory called "some-directory" is in the trash.
|
||||||
#.
|
#.
|
||||||
#: ../src/shell-uri-util.c:298
|
#: ../src/shell-uri-util.c:300
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Frequent"
|
||||||
|
#~ msgstr "Frecuentes"
|
||||||
|
|
||||||
|
#~ msgid "More"
|
||||||
|
#~ msgstr "Más"
|
||||||
|
|
||||||
|
#~ msgid "(see all)"
|
||||||
|
#~ msgstr "(ver todo)"
|
||||||
|
|
||||||
|
#~ msgid "SEARCH RESULTS"
|
||||||
|
#~ msgstr "RESULTADOS DE LA BÚSQUEDA"
|
||||||
|
|
||||||
|
#~ msgid "Unknown"
|
||||||
|
#~ msgstr "Desconocido"
|
||||||
|
|
||||||
|
#~ msgid "Can't lock screen: %s"
|
||||||
|
#~ msgstr "No se puede bloquear la pantalla: %s"
|
||||||
|
|
||||||
|
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "No se puede establecer temporalmente el salvapantallas a oscurecer "
|
||||||
|
#~ "pantalla: %s"
|
||||||
|
|
||||||
|
#~ msgid "Can't logout: %s"
|
||||||
|
#~ msgstr "No se puede salir de la sesión: %s"
|
||||||
|
|
||||||
|
#~ msgid "Account Information..."
|
||||||
|
#~ msgstr "Información de la cuenta…"
|
||||||
|
|
||||||
|
#~ msgid "Sidebar"
|
||||||
|
#~ msgstr "Barra lateral"
|
||||||
|
|
||||||
|
#~ msgid "System Preferences..."
|
||||||
|
#~ msgstr "Preferencias del sistema…"
|
||||||
|
|
||||||
|
#~ msgid "Lock Screen"
|
||||||
|
#~ msgstr "Bloquear la pantalla"
|
||||||
|
|
||||||
|
#~ msgid "Switch User"
|
||||||
|
#~ msgstr "Cambiar de usuario"
|
||||||
|
|
||||||
|
#~ msgid "Log Out..."
|
||||||
|
#~ msgstr "Salir…"
|
||||||
|
|
||||||
|
#~ msgid "Shut Down..."
|
||||||
|
#~ msgstr "Apagar…"
|
||||||
|
|
||||||
#~ msgid "Browse"
|
#~ msgid "Browse"
|
||||||
#~ msgstr "Examine"
|
#~ msgstr "Examine"
|
||||||
|
|
||||||
|
226
po/fi.po
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
# gnome-shell Finnish translation
|
||||||
|
# Copyright (C) 2009 Timo Jyrinki
|
||||||
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
|
# Timo Jyrinki <timo.jyrinki@iki.fi>, 2009.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnome-shell\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2009-11-04 11:16+0200\n"
|
||||||
|
"PO-Revision-Date: 2009-11-04 11:16+0200\n"
|
||||||
|
"Last-Translator: Timo Jyrinki <timo.jyrinki@iki.fi>\n"
|
||||||
|
"Language-Team: Finnish <gnome-fi-laatu@lists.sourceforge.net>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
|
msgid "GNOME Shell"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
|
msgid "Window management and application launching"
|
||||||
|
msgstr "Ikkunanhallinta ja sovelluksien käynnistäminen"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:332
|
||||||
|
msgid "Frequent"
|
||||||
|
msgstr "Usein käytetyt"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:867
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Raahaa tähän lisätäksesi suosikkeihin"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:426
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Uusi ikkuna"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Poista suosikeista"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:431
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Lisää suosikkeihin"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Etsi..."
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:400
|
||||||
|
msgid "More"
|
||||||
|
msgstr "Lisää"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(näytä kaikki)"
|
||||||
|
|
||||||
|
#. **** Applications ****
|
||||||
|
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "SOVELLUKSET"
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:745
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "SIJAINNIT"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "VIIMEISIMMÄT ASIAKIRJAT"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "HAKUTULOKSET"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:792
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "ASETUKSET"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Toiminnot"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %I.%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Yhdistä..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
|
msgid "Please enter a command:"
|
||||||
|
msgstr "Syötä komento:"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%I.%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Sovellukset"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Viimeisimmät asiakirjat"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "Alle minuutti sitten"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:824
|
||||||
|
#, c-format
|
||||||
|
msgid "%d minute ago"
|
||||||
|
msgid_plural "%d minutes ago"
|
||||||
|
msgstr[0] "%d minuutti sitten"
|
||||||
|
msgstr[1] "%d minuuttia sitten"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:827
|
||||||
|
#, c-format
|
||||||
|
msgid "%d hour ago"
|
||||||
|
msgid_plural "%d hours ago"
|
||||||
|
msgstr[0] "%d tunti sitten"
|
||||||
|
msgstr[1] "%d tuntia sitten"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:830
|
||||||
|
#, c-format
|
||||||
|
msgid "%d day ago"
|
||||||
|
msgid_plural "%d days ago"
|
||||||
|
msgstr[0] "%d päivä sitten"
|
||||||
|
msgstr[1] "%d päivää sitten"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:833
|
||||||
|
#, c-format
|
||||||
|
msgid "%d week ago"
|
||||||
|
msgid_plural "%d weeks ago"
|
||||||
|
msgstr[0] "%d viikko sitten"
|
||||||
|
msgstr[1] "%d viikkoa sitten"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:156
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Tuntematon"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:212
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't lock screen: %s"
|
||||||
|
msgstr "Näyttöä ei voi lukita: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:227
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't logout: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:492
|
||||||
|
msgid "Account Information..."
|
||||||
|
msgstr "Käyttäjätilin tiedot..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:502
|
||||||
|
msgid "Sidebar"
|
||||||
|
msgstr "Sivupalkki"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:510
|
||||||
|
msgid "System Preferences..."
|
||||||
|
msgstr "Järjestelmän asetukset"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:525
|
||||||
|
msgid "Lock Screen"
|
||||||
|
msgstr "Lukitse näyttö"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:535
|
||||||
|
msgid "Switch User"
|
||||||
|
msgstr "Vaihda käyttäjää"
|
||||||
|
|
||||||
|
#. Only show switch user if there are other users
|
||||||
|
#. Log Out
|
||||||
|
#: ../src/shell-status-menu.c:546
|
||||||
|
msgid "Log Out..."
|
||||||
|
msgstr "Kirjaudu ulos..."
|
||||||
|
|
||||||
|
#. Shut down
|
||||||
|
#: ../src/shell-status-menu.c:557
|
||||||
|
msgid "Shut Down..."
|
||||||
|
msgstr "Sammuta..."
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:87
|
||||||
|
msgid "Home Folder"
|
||||||
|
msgstr "Kotikansio"
|
||||||
|
|
||||||
|
#. Translators: this is the same string as the one found in
|
||||||
|
#. * nautilus
|
||||||
|
#: ../src/shell-uri-util.c:102
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "Tiedostojärjestelmä"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:248
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Haku"
|
||||||
|
|
||||||
|
#. Translators: the first string is the name of a gvfs
|
||||||
|
#. * method, and the second string is a path. For
|
||||||
|
#. * example, "Trash: some-directory". It means that the
|
||||||
|
#. * directory called "some-directory" is in the trash.
|
||||||
|
#.
|
||||||
|
#: ../src/shell-uri-util.c:298
|
||||||
|
#, c-format
|
||||||
|
msgid "%1$s: %2$s"
|
||||||
|
msgstr "%1$s: %2$s"
|
102
po/fr.po
@ -6,13 +6,13 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: HEAD\n"
|
"Project-Id-Version: gnome-shell master fr\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"shell&component=general\n"
|
"shell&component=general\n"
|
||||||
"POT-Creation-Date: 2009-09-09 21:30+0000\n"
|
"POT-Creation-Date: 2009-11-13 17:44+0000\n"
|
||||||
"PO-Revision-Date: 2009-09-11 21:40+0200\n"
|
"PO-Revision-Date: 2009-12-05 16:43+0100\n"
|
||||||
"Last-Translator: Mathieu Bridon <bochecha@fedoraproject.org>\n"
|
"Last-Translator: Pablo Martin-Gomez <pablo.martin-gomez@laposte.net>\n"
|
||||||
"Language-Team: GNOME French Team\n"
|
"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
@ -26,80 +26,115 @@ msgstr "GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Gestion des fenêtres et lancement des applications"
|
msgstr "Gestion des fenêtres et lancement des applications"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:696
|
||||||
#: ../js/ui/panel.js:269
|
msgid "Drag here to add favorites"
|
||||||
msgid "Activities"
|
msgstr "Glisser ici pour ajouter aux favoris"
|
||||||
msgstr "Activités"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:425
|
||||||
#: ../js/ui/panel.js:452
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Nouvelle fenêtre"
|
||||||
msgstr "%a %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:255
|
#: ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Enlever des favoris"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Ajouter aux favoris"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:237
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Rechercher..."
|
msgstr "Rechercher..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:372
|
|
||||||
msgid "Browse"
|
|
||||||
msgstr "Parcourir"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:508
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(tout afficher)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:700 ../js/ui/dash.js:756 ../js/ui/dash.js:887
|
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "APPLICATIONS"
|
msgstr "APPLICATIONS"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:720
|
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "RACCOURCIS"
|
msgstr "RACCOURCIS"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:727 ../js/ui/dash.js:768 ../js/ui/dash.js:861
|
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "DOCUMENTS RÉCENTS"
|
msgstr "DOCUMENTS RÉCENTS"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:746 ../js/ui/dash.js:850 ../js/ui/dash.js:876
|
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "RÉSULTATS DE LA RECHERCHE"
|
msgstr "RÉSULTATS DE LA RECHERCHE"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:90
|
#: ../js/ui/dash.js:723
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "PRÉFÉRENCES"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Activités"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/placeDisplay.js:84
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Connexion à..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Veuillez saisir une commande :"
|
msgstr "Veuillez saisir une commande :"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Exécution de « %s » impossible :"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Applications"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documents récents"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Il y a moins d'une minute"
|
msgstr "Il y a moins d'une minute"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "Il y a %d minute"
|
msgstr[0] "Il y a %d minute"
|
||||||
msgstr[1] "Il y a %d minutes"
|
msgstr[1] "Il y a %d minutes"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:827
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "Il y a %d heure"
|
msgstr[0] "Il y a %d heure"
|
||||||
msgstr[1] "Il y a %d heures"
|
msgstr[1] "Il y a %d heures"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:830
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "Il y a %d jour"
|
msgstr[0] "Il y a %d jour"
|
||||||
msgstr[1] "Il y a %d jours"
|
msgstr[1] "Il y a %d jours"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:833
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -180,3 +215,6 @@ msgstr "Recherche"
|
|||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s : %2$s"
|
msgstr "%1$s : %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Parcourir"
|
||||||
|
240
po/gl.po
@ -2,19 +2,22 @@
|
|||||||
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the gnome-shell package.
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
# Fran Diéguez <fran.dieguez@mabishu.com>, 2009.
|
# Fran Diéguez <fran.dieguez@mabishu.com>, 2009.
|
||||||
#
|
# Anton Meixome <certima@certima.net>, 2009.
|
||||||
|
# Antón Méixome <meixome@certima.net>, 2009.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell master\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-09-15 23:06+0200\n"
|
"POT-Creation-Date: 2009-12-28 17:07+0100\n"
|
||||||
"PO-Revision-Date: 2009-09-10 22:32+0100\n"
|
"PO-Revision-Date: 2009-12-28 08:11+0100\n"
|
||||||
"Last-Translator: Fran Diéguez <fran.dieguez@glug.es>\n"
|
"Last-Translator: Antón Méixome <meixome@certima.net>\n"
|
||||||
"Language-Team: Galician <gnome@mancomun.org>\n"
|
"Language-Team: Galician Proxecto Trasno <proxecto@trasno.net>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Language: gl\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Virtaal 0.4.0\n"
|
||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:1
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
msgid "GNOME Shell"
|
msgid "GNOME Shell"
|
||||||
@ -22,154 +25,133 @@ msgstr "GNOME Shell"
|
|||||||
|
|
||||||
#: ../data/gnome-shell.desktop.in.in.h:2
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Xestor de xanelas e lanzado de aplicativos"
|
msgstr "Xestor de xanelas e lanzamento de aplicativos"
|
||||||
|
|
||||||
#. left side
|
#. **** Applications ****
|
||||||
#: ../js/ui/panel.js:269
|
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
|
||||||
msgid "Activities"
|
msgid "APPLICATIONS"
|
||||||
msgstr "Actividades"
|
msgstr "APLICATIVOS"
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appDisplay.js:276
|
||||||
#: ../js/ui/panel.js:452
|
msgid "PREFERENCES"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "PREFERENCIAS"
|
||||||
msgstr "%a %l:%M %p"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Xanela nova"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Eliminar de Favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Engadir a Favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:1064
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Arrastra aquí para engadir favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:240
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Buscar..."
|
msgstr "Buscar..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
#: ../js/ui/dash.js:437
|
||||||
msgid "Browse"
|
msgid "Searching..."
|
||||||
msgstr "Explorar"
|
msgstr "Buscando..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(ver todos)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
|
||||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
|
||||||
msgid "APPLICATIONS"
|
|
||||||
msgstr "APLICATIVOS"
|
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "LUGARES"
|
msgstr "LUGARES"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
#: ../js/ui/dash.js:885
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "DOCUMENTOS RECENTES"
|
msgstr "DOCUMENTOS RECENTES"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. Button on the left side of the panel.
|
||||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
msgid "SEARCH RESULTS"
|
#: ../js/ui/panel.js:227
|
||||||
msgstr "RESULTADOS DA BÚSQUEDA"
|
msgid "Activities"
|
||||||
|
msgstr "Actividades"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#. Translators: This is a time format.
|
||||||
msgid "PREFERENCES"
|
#: ../js/ui/panel.js:440
|
||||||
msgstr ""
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:96
|
#: ../js/ui/placeDisplay.js:144
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Conectar con..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:235
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Insira unha orde:"
|
msgstr "Insira unha orde:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:351
|
||||||
msgid "Less than a minute ago"
|
#, c-format
|
||||||
msgstr "Menos de un minuto"
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Fallou a execución de %s"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplicativos"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documentos recentes"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:890
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "Hai menos dun minuto"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:893
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "fai %d minuto"
|
msgstr[0] "hai %d minuto"
|
||||||
msgstr[1] "fai %d minutos"
|
msgstr[1] "hai %d minutos"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:896
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "fai %d hora"
|
msgstr[0] "hai %d hora"
|
||||||
msgstr[1] "fai %d horas"
|
msgstr[1] "hai %d horas"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:899
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "fai %d día"
|
msgstr[0] "hai %d día"
|
||||||
msgstr[1] "fai %d días"
|
msgstr[1] "hai %d días"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:902
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
msgstr[0] "fai %d semana"
|
msgstr[0] "hai %d semana"
|
||||||
msgstr[1] "fai %d semanas"
|
msgstr[1] "hai %d semanas"
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:156
|
#: ../src/shell-uri-util.c:89
|
||||||
msgid "Unknown"
|
|
||||||
msgstr "Descoñecido"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:212
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't lock screen: %s"
|
|
||||||
msgstr "Non foi posíbel bloquear a pantalla: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:227
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
|
||||||
msgstr ""
|
|
||||||
"Non foi posíbel establecer o salvapantallas a unha pantalla en branco: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:351
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't logout: %s"
|
|
||||||
msgstr "Non foi posíbel pechar a sesión: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:492
|
|
||||||
msgid "Account Information..."
|
|
||||||
msgstr "Información da conta..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:502
|
|
||||||
msgid "Sidebar"
|
|
||||||
msgstr "Barra lateral"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:510
|
|
||||||
msgid "System Preferences..."
|
|
||||||
msgstr "Preferenzas do sistema..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:525
|
|
||||||
msgid "Lock Screen"
|
|
||||||
msgstr "Bloquear pantalla"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:535
|
|
||||||
msgid "Switch User"
|
|
||||||
msgstr "Cambiar de usuario"
|
|
||||||
|
|
||||||
#. Only show switch user if there are other users
|
|
||||||
#. Log Out
|
|
||||||
#: ../src/shell-status-menu.c:546
|
|
||||||
msgid "Log Out..."
|
|
||||||
msgstr "Pechar sesión..."
|
|
||||||
|
|
||||||
#. Shut down
|
|
||||||
#: ../src/shell-status-menu.c:557
|
|
||||||
msgid "Shut Down..."
|
|
||||||
msgstr "Apagar..."
|
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:87
|
|
||||||
msgid "Home Folder"
|
msgid "Home Folder"
|
||||||
msgstr "Cartafol persoal"
|
msgstr "Cartafol persoal"
|
||||||
|
|
||||||
#. Translators: this is the same string as the one found in
|
#. Translators: this is the same string as the one found in
|
||||||
#. * nautilus
|
#. * nautilus
|
||||||
#: ../src/shell-uri-util.c:102
|
#: ../src/shell-uri-util.c:104
|
||||||
msgid "File System"
|
msgid "File System"
|
||||||
msgstr "Sistema de ficheiros"
|
msgstr "Sistema de ficheiros"
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:248
|
#: ../src/shell-uri-util.c:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Buscar"
|
msgstr "Buscar"
|
||||||
|
|
||||||
@ -178,11 +160,55 @@ msgstr "Buscar"
|
|||||||
#. * example, "Trash: some-directory". It means that the
|
#. * example, "Trash: some-directory". It means that the
|
||||||
#. * directory called "some-directory" is in the trash.
|
#. * directory called "some-directory" is in the trash.
|
||||||
#.
|
#.
|
||||||
#: ../src/shell-uri-util.c:298
|
#: ../src/shell-uri-util.c:300
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "SEARCH RESULTS"
|
||||||
|
#~ msgstr "RESULTADOS DA BUSCA"
|
||||||
|
|
||||||
|
#~ msgid "Unknown"
|
||||||
|
#~ msgstr "Descoñecido"
|
||||||
|
|
||||||
|
#~ msgid "Can't lock screen: %s"
|
||||||
|
#~ msgstr "Non foi posíbel bloquear a pantalla: %s"
|
||||||
|
|
||||||
|
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Non foi posíbel estabelecer temporalmente o salvapantallas a unha "
|
||||||
|
#~ "pantalla en branco: %s"
|
||||||
|
|
||||||
|
#~ msgid "Can't logout: %s"
|
||||||
|
#~ msgstr "Non foi posíbel pechar a sesión: %s"
|
||||||
|
|
||||||
|
#~ msgid "Account Information..."
|
||||||
|
#~ msgstr "Información da conta..."
|
||||||
|
|
||||||
|
#~ msgid "Sidebar"
|
||||||
|
#~ msgstr "Barra lateral"
|
||||||
|
|
||||||
|
#~ msgid "System Preferences..."
|
||||||
|
#~ msgstr "Preferencias do sistema..."
|
||||||
|
|
||||||
|
#~ msgid "Lock Screen"
|
||||||
|
#~ msgstr "Bloquear pantalla"
|
||||||
|
|
||||||
|
#~ msgid "Switch User"
|
||||||
|
#~ msgstr "Cambiar de usuario"
|
||||||
|
|
||||||
|
#~ msgid "Log Out..."
|
||||||
|
#~ msgstr "Saír da sesión..."
|
||||||
|
|
||||||
|
#~ msgid "Shut Down..."
|
||||||
|
#~ msgstr "Apagar..."
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Explorar"
|
||||||
|
|
||||||
|
#~ msgid "(see all)"
|
||||||
|
#~ msgstr "(ver todos)"
|
||||||
|
|
||||||
#~ msgid "Find apps or documents"
|
#~ msgid "Find apps or documents"
|
||||||
#~ msgstr "Atopar aplicativos ou documentos"
|
#~ msgstr "Atopar aplicativos ou documentos"
|
||||||
|
|
||||||
|
215
po/he.po
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
# Hebrew translation for gnome-shell.
|
||||||
|
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
|
# liel <lielft@gmail.com>, 2009.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
|
"shell&component=general\n"
|
||||||
|
"POT-Creation-Date: 2009-11-13 17:44+0000\n"
|
||||||
|
"PO-Revision-Date: 2009-11-28 17:33+0200\n"
|
||||||
|
"Last-Translator: Liel Fridman <lielft@gmail.com>\n"
|
||||||
|
"Language-Team: Hebrew <he@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
|
msgid "GNOME Shell"
|
||||||
|
msgstr "מעטפת GNOME"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
|
msgid "Window management and application launching"
|
||||||
|
msgstr "ניהול חלונות והרצת יישומים"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:696
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "יש לגרור פריטים לכאן כדי להוסיף מועדפים"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:425
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "חלון חדש"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "הסר מהמועדפים"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "הוסף למועדפים"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:237
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "חפש..."
|
||||||
|
|
||||||
|
#. **** Applications ****
|
||||||
|
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "יישומים"
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "מקומות"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "מסמכים אחרונים"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "תוצאות חיפוש"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:723
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "העדפות"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "פעילויות"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
|
#: ../js/ui/placeDisplay.js:84
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "התחבר אל..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
|
msgid "Please enter a command:"
|
||||||
|
msgstr "נא להזין פקודה:"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "ההרצה של '%s' נכשלה:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "יישומים"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "מסמכים אחרונים"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "לפני פחות מדקה"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:824
|
||||||
|
#, c-format
|
||||||
|
msgid "%d minute ago"
|
||||||
|
msgid_plural "%d minutes ago"
|
||||||
|
msgstr[0] "לפני דקה"
|
||||||
|
msgstr[1] "לפני %d דקות"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:827
|
||||||
|
#, c-format
|
||||||
|
msgid "%d hour ago"
|
||||||
|
msgid_plural "%d hours ago"
|
||||||
|
msgstr[0] "לפני שעה"
|
||||||
|
msgstr[1] "לפני %d שעות"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:830
|
||||||
|
#, c-format
|
||||||
|
msgid "%d day ago"
|
||||||
|
msgid_plural "%d days ago"
|
||||||
|
msgstr[0] "לפני יום"
|
||||||
|
msgstr[1] "לפני %d ימים"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:833
|
||||||
|
#, c-format
|
||||||
|
msgid "%d week ago"
|
||||||
|
msgid_plural "%d weeks ago"
|
||||||
|
msgstr[0] "לפני שבוע"
|
||||||
|
msgstr[1] "לפני %d שבועות"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:156
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "לא ידוע"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:212
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't lock screen: %s"
|
||||||
|
msgstr "לא ניתן לנעול את המסך: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:227
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
msgstr "לא ניתן זמנית לקבוע שומר מסך כמסך שחור: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't logout: %s"
|
||||||
|
msgstr "לא ניתן להתנתק: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:492
|
||||||
|
msgid "Account Information..."
|
||||||
|
msgstr "מידע על המשתמש..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:502
|
||||||
|
msgid "Sidebar"
|
||||||
|
msgstr "סרגל צד"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:510
|
||||||
|
msgid "System Preferences..."
|
||||||
|
msgstr "העדפות מערכת..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:525
|
||||||
|
msgid "Lock Screen"
|
||||||
|
msgstr "נעילת המסך"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:535
|
||||||
|
msgid "Switch User"
|
||||||
|
msgstr "החלף משתמש"
|
||||||
|
|
||||||
|
#. Only show switch user if there are other users
|
||||||
|
#. Log Out
|
||||||
|
#: ../src/shell-status-menu.c:546
|
||||||
|
msgid "Log Out..."
|
||||||
|
msgstr "ניתוק..."
|
||||||
|
|
||||||
|
#. Shut down
|
||||||
|
#: ../src/shell-status-menu.c:557
|
||||||
|
msgid "Shut Down..."
|
||||||
|
msgstr "כיבוי..."
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:87
|
||||||
|
msgid "Home Folder"
|
||||||
|
msgstr "תיקיית הבית"
|
||||||
|
|
||||||
|
#. Translators: this is the same string as the one found in
|
||||||
|
#. * nautilus
|
||||||
|
#: ../src/shell-uri-util.c:102
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "מערכת הקבצים"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:248
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "חפש"
|
||||||
|
|
||||||
|
#. Translators: the first string is the name of a gvfs
|
||||||
|
#. * method, and the second string is a path. For
|
||||||
|
#. * example, "Trash: some-directory". It means that the
|
||||||
|
#. * directory called "some-directory" is in the trash.
|
||||||
|
#.
|
||||||
|
#: ../src/shell-uri-util.c:298
|
||||||
|
#, c-format
|
||||||
|
msgid "%1$s: %2$s"
|
||||||
|
msgstr "%1$s: %2$s"
|
240
po/it.po
@ -6,10 +6,9 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell\n"
|
"Project-Id-Version: gnome-shell\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"shell&component=general\n"
|
"POT-Creation-Date: 2009-12-28 21:58+0100\n"
|
||||||
"POT-Creation-Date: 2009-09-05 00:19+0000\n"
|
"PO-Revision-Date: 2009-12-28 21:59+0100\n"
|
||||||
"PO-Revision-Date: 2009-09-06 18:31+0200\n"
|
|
||||||
"Last-Translator: Milo Casagrande <milo@ubuntu.com>\n"
|
"Last-Translator: Milo Casagrande <milo@ubuntu.com>\n"
|
||||||
"Language-Team: Italian <tp@lists.linux.it>\n"
|
"Language-Team: Italian <tp@lists.linux.it>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -25,151 +24,132 @@ msgstr "GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Gestione finestre e avvio applicazioni"
|
msgstr "Gestione finestre e avvio applicazioni"
|
||||||
|
|
||||||
#. left side
|
#. **** Applications ****
|
||||||
#: ../js/ui/panel.js:271
|
#: ../js/ui/appDisplay.js:252 ../js/ui/dash.js:858
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "Applicazioni"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:276
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "Preferenze"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:707 ../js/ui/appIcon.js:425
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Nuova finestra"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:711 ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Rimuovi dai preferiti"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:712 ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Aggiungi ai preferiti"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:1064
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Trascinare qui per aggiungere ai preferiti"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:240
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Trova..."
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:437
|
||||||
|
msgid "Searching..."
|
||||||
|
msgstr "Ricerca..."
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:878 ../js/ui/placeDisplay.js:519
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "Risorse"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:885
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "Documenti recenti"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:227
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Attività"
|
msgstr "Attività"
|
||||||
|
|
||||||
# (ndt) proviamo col k, se non funge, sappiamo il perché...
|
# (ndt) proviamo col k, se non funge, sappiamo il perché...
|
||||||
#. Translators: This is a time format.
|
#. Translators: This is a time format.
|
||||||
#: ../js/ui/panel.js:454
|
#: ../js/ui/panel.js:440
|
||||||
msgid "%a %l:%M %p"
|
msgid "%a %l:%M %p"
|
||||||
msgstr "%a %k.%M"
|
msgstr "%a %k.%M"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:256
|
#: ../js/ui/placeDisplay.js:144
|
||||||
msgid "Find..."
|
msgid "Connect to..."
|
||||||
msgstr "Trova..."
|
msgstr "Connetti a..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:374
|
#: ../js/ui/runDialog.js:235
|
||||||
msgid "Browse"
|
|
||||||
msgstr "Esplora"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:511
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(vedi tutto)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
|
||||||
#: ../js/ui/dash.js:705 ../js/ui/dash.js:761 ../js/ui/dash.js:893
|
|
||||||
msgid "APPLICATIONS"
|
|
||||||
msgstr "Applicazioni"
|
|
||||||
|
|
||||||
#. **** Places ****
|
|
||||||
#. Translators: This is in the sense of locations for documents,
|
|
||||||
#. network locations, etc.
|
|
||||||
#: ../js/ui/dash.js:725
|
|
||||||
msgid "PLACES"
|
|
||||||
msgstr "Risorse"
|
|
||||||
|
|
||||||
#. **** Documents ****
|
|
||||||
#: ../js/ui/dash.js:732 ../js/ui/dash.js:773 ../js/ui/dash.js:867
|
|
||||||
msgid "RECENT DOCUMENTS"
|
|
||||||
msgstr "Documenti recenti"
|
|
||||||
|
|
||||||
#. **** Search Results ****
|
|
||||||
#: ../js/ui/dash.js:751 ../js/ui/dash.js:856 ../js/ui/dash.js:882
|
|
||||||
msgid "SEARCH RESULTS"
|
|
||||||
msgstr "Risultati ricerca"
|
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:82
|
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Inserire un comando:"
|
msgstr "Inserire un comando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:840
|
#: ../js/ui/runDialog.js:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Esecuzione di «%s» non riuscita:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%k.%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Applicazioni"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documenti recenti"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:890
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Meno di un minuto fa"
|
msgstr "Meno di un minuto fa"
|
||||||
|
|
||||||
#: ../src/shell-global.c:843
|
#: ../src/shell-global.c:893
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d minuto fa"
|
msgstr[0] "%d minuto fa"
|
||||||
msgstr[1] "%d minuti fa"
|
msgstr[1] "%d minuti fa"
|
||||||
|
|
||||||
#: ../src/shell-global.c:846
|
#: ../src/shell-global.c:896
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d ora fa"
|
msgstr[0] "%d ora fa"
|
||||||
msgstr[1] "%d ore fa"
|
msgstr[1] "%d ore fa"
|
||||||
|
|
||||||
#: ../src/shell-global.c:849
|
#: ../src/shell-global.c:899
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d giorno fa"
|
msgstr[0] "%d giorno fa"
|
||||||
msgstr[1] "%d giorni fa"
|
msgstr[1] "%d giorni fa"
|
||||||
|
|
||||||
#: ../src/shell-global.c:852
|
#: ../src/shell-global.c:902
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
msgstr[0] "%d settimana fa"
|
msgstr[0] "%d settimana fa"
|
||||||
msgstr[1] "%d settimane fa"
|
msgstr[1] "%d settimane fa"
|
||||||
|
|
||||||
# (ndt) valutare se vada al femminile
|
#: ../src/shell-uri-util.c:89
|
||||||
#: ../src/shell-status-menu.c:156
|
|
||||||
msgid "Unknown"
|
|
||||||
msgstr "Sconosciuto"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:212
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't lock screen: %s"
|
|
||||||
msgstr "Impossibile bloccare lo schermo: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:227
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
|
||||||
msgstr ""
|
|
||||||
"Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:351
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't logout: %s"
|
|
||||||
msgstr "Impossibile terminare la sessione: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:492
|
|
||||||
msgid "Account Information..."
|
|
||||||
msgstr "Informazioni account..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:502
|
|
||||||
msgid "Sidebar"
|
|
||||||
msgstr "Barra laterale"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:510
|
|
||||||
msgid "System Preferences..."
|
|
||||||
msgstr "Preferenze di sistema..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:525
|
|
||||||
msgid "Lock Screen"
|
|
||||||
msgstr "Blocca schermo"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:535
|
|
||||||
msgid "Switch User"
|
|
||||||
msgstr "Cambia utente"
|
|
||||||
|
|
||||||
#. Only show switch user if there are other users
|
|
||||||
#. Log Out
|
|
||||||
#: ../src/shell-status-menu.c:546
|
|
||||||
msgid "Log Out..."
|
|
||||||
msgstr "Termina sessione..."
|
|
||||||
|
|
||||||
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
|
|
||||||
#. Shut down
|
|
||||||
#: ../src/shell-status-menu.c:557
|
|
||||||
msgid "Shut Down..."
|
|
||||||
msgstr "Spegni..."
|
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:87
|
|
||||||
msgid "Home Folder"
|
msgid "Home Folder"
|
||||||
msgstr "Cartella home"
|
msgstr "Cartella home"
|
||||||
|
|
||||||
#. Translators: this is the same string as the one found in
|
#. Translators: this is the same string as the one found in
|
||||||
#. * nautilus
|
#. * nautilus
|
||||||
#: ../src/shell-uri-util.c:102
|
#: ../src/shell-uri-util.c:104
|
||||||
msgid "File System"
|
msgid "File System"
|
||||||
msgstr "File system"
|
msgstr "File system"
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:248
|
#: ../src/shell-uri-util.c:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Cerca"
|
msgstr "Cerca"
|
||||||
|
|
||||||
@ -179,19 +159,55 @@ msgstr "Cerca"
|
|||||||
#. * example, "Trash: some-directory". It means that the
|
#. * example, "Trash: some-directory". It means that the
|
||||||
#. * directory called "some-directory" is in the trash.
|
#. * directory called "some-directory" is in the trash.
|
||||||
#.
|
#.
|
||||||
#: ../src/shell-uri-util.c:298
|
#: ../src/shell-uri-util.c:300
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
# (ndt) è da valutare se è troppo lunga, è in una casella di ricerca
|
#~ msgid "Frequent"
|
||||||
#~ msgid "Find apps or documents"
|
#~ msgstr "Frequente"
|
||||||
#~ msgstr "Trova programmi e documenti"
|
|
||||||
|
|
||||||
# (ndt) no idea...
|
#~ msgid "More"
|
||||||
#~ msgid "Manager"
|
#~ msgstr "Altro"
|
||||||
#~ msgstr "Manager"
|
|
||||||
|
|
||||||
# (ndt) no idea...
|
#~ msgid "(see all)"
|
||||||
#~ msgid "The user manager object this user is controlled by."
|
#~ msgstr "(vedi tutto)"
|
||||||
#~ msgstr "L'oggetto user manager che controlla questo utente."
|
|
||||||
|
#~ msgid "SEARCH RESULTS"
|
||||||
|
#~ msgstr "Risultati ricerca"
|
||||||
|
|
||||||
|
# (ndt) valutare se vada al femminile
|
||||||
|
#~ msgid "Unknown"
|
||||||
|
#~ msgstr "Sconosciuto"
|
||||||
|
|
||||||
|
#~ msgid "Can't lock screen: %s"
|
||||||
|
#~ msgstr "Impossibile bloccare lo schermo: %s"
|
||||||
|
|
||||||
|
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Impossibile impostare temporaneamente il salva schermo a schermo nero: %s "
|
||||||
|
|
||||||
|
#~ msgid "Can't logout: %s"
|
||||||
|
#~ msgstr "Impossibile terminare la sessione: %s"
|
||||||
|
|
||||||
|
#~ msgid "Account Information..."
|
||||||
|
#~ msgstr "Informazioni account..."
|
||||||
|
|
||||||
|
#~ msgid "Sidebar"
|
||||||
|
#~ msgstr "Barra laterale"
|
||||||
|
|
||||||
|
#~ msgid "System Preferences..."
|
||||||
|
#~ msgstr "Preferenze di sistema..."
|
||||||
|
|
||||||
|
#~ msgid "Lock Screen"
|
||||||
|
#~ msgstr "Blocca schermo"
|
||||||
|
|
||||||
|
#~ msgid "Switch User"
|
||||||
|
#~ msgstr "Cambia utente"
|
||||||
|
|
||||||
|
#~ msgid "Log Out..."
|
||||||
|
#~ msgstr "Termina sessione..."
|
||||||
|
|
||||||
|
# (ndt) da valutare... pare che ora anche Windows usi 'Arresta...'...
|
||||||
|
#~ msgid "Shut Down..."
|
||||||
|
#~ msgstr "Spegni..."
|
||||||
|
59
po/nb.po
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell 0.4\n"
|
"Project-Id-Version: gnome-shell 0.4\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-08-21 12:36+0200\n"
|
"POT-Creation-Date: 2009-10-03 10:47+0200\n"
|
||||||
"PO-Revision-Date: 2009-08-21 12:44+0200\n"
|
"PO-Revision-Date: 2009-10-03 10:48+0200\n"
|
||||||
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
|
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
|
||||||
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -25,70 +25,83 @@ msgid "Window management and application launching"
|
|||||||
msgstr "Vindushåndtering og oppstart av programmer"
|
msgstr "Vindushåndtering og oppstart av programmer"
|
||||||
|
|
||||||
#. left side
|
#. left side
|
||||||
#: ../js/ui/panel.js:266
|
#: ../js/ui/panel.js:271
|
||||||
msgid "Activities"
|
msgid "Activities"
|
||||||
msgstr "Aktiviteter"
|
msgstr "Aktiviteter"
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#. Translators: This is a time format.
|
||||||
#: ../js/ui/panel.js:433
|
#: ../js/ui/panel.js:461
|
||||||
msgid "%a %l:%M %p"
|
msgid "%a %l:%M %p"
|
||||||
msgstr "%a %l:%M"
|
msgstr "%a %l:%M"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:250
|
#: ../js/ui/dash.js:283
|
||||||
msgid "Find apps or documents"
|
msgid "Find..."
|
||||||
msgstr "Finn programmer eller dokumenter"
|
msgstr "Finn..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:368
|
#: ../js/ui/dash.js:400
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "Bla gjennom"
|
msgstr "Mer"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(se alle)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "PROGRAMMER"
|
msgstr "PROGRAMMER"
|
||||||
|
|
||||||
#. **** Documents ****
|
|
||||||
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
|
|
||||||
msgid "RECENT DOCUMENTS"
|
|
||||||
msgstr "SISTE DOKUMENTER"
|
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:597
|
#: ../js/ui/dash.js:783
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "STEDER"
|
msgstr "STEDER"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:75
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "SISTE DOKUMENTER"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "SØKERESULTATER"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:830
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "BRUKERVALG"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Oppgi en kommando:"
|
msgstr "Oppgi en kommando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:841
|
#: ../src/shell-global.c:812
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Mindre enn ett minutt siden"
|
msgstr "Mindre enn ett minutt siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:844
|
#: ../src/shell-global.c:815
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d minutt siden"
|
msgstr[0] "%d minutt siden"
|
||||||
msgstr[1] "%d minutter siden"
|
msgstr[1] "%d minutter siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:847
|
#: ../src/shell-global.c:818
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d time siden"
|
msgstr[0] "%d time siden"
|
||||||
msgstr[1] "%d timer siden"
|
msgstr[1] "%d timer siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:850
|
#: ../src/shell-global.c:821
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d dag siden"
|
msgstr[0] "%d dag siden"
|
||||||
msgstr[1] "%d dager siden"
|
msgstr[1] "%d dager siden"
|
||||||
|
|
||||||
#: ../src/shell-global.c:853
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
|
95
po/pa.po
@ -8,8 +8,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: gnome-shell master\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
|
||||||
"cgi?product=gnome-shell&component=general\n"
|
"cgi?product=gnome-shell&component=general\n"
|
||||||
"POT-Creation-Date: 2009-08-31 22:31+0000\n"
|
"POT-Creation-Date: 2009-10-09 22:10+0000\n"
|
||||||
"PO-Revision-Date: 2009-09-01 06:51+0530\n"
|
"PO-Revision-Date: 2009-10-15 06:18+0530\n"
|
||||||
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
|
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
|
||||||
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
|
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -26,80 +26,123 @@ msgstr "ਗਨੋਮ ਸ਼ੈਲ"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "ਵਿੰਡੋ ਪਰਬੰਧ ਅਤੇ ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਓ"
|
msgstr "ਵਿੰਡੋ ਪਰਬੰਧ ਅਤੇ ਐਪਲੀਕੇਸ਼ਨ ਚਲਾਓ"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:335
|
||||||
#: ../js/ui/panel.js:271
|
msgid "Frequent"
|
||||||
msgid "Activities"
|
msgstr "ਅਕਸਰ"
|
||||||
msgstr "ਸਰਗਰਮੀਆਂ"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:462
|
||||||
#: ../js/ui/panel.js:454
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "ਨਵੀਂ ਵਿੰਡੋ"
|
||||||
msgstr "%a %l:%M %p"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:256
|
#: ../js/ui/appIcon.js:475
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "ਪਸੰਦ ਵਿੱਚੋਂ ਹਟਾਓ"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:476
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "ਪਸੰਦ 'ਚ ਸ਼ਾਮਲ ਕਰੋ"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "ਖੋਜ..."
|
msgstr "ਖੋਜ..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:374
|
#: ../js/ui/dash.js:400
|
||||||
msgid "Browse"
|
msgid "More"
|
||||||
msgstr "ਝਲਕ"
|
msgstr "ਹੋਰ"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:451
|
#: ../js/ui/dash.js:543
|
||||||
msgid "(see all)"
|
msgid "(see all)"
|
||||||
msgstr "(ਸਭ ਵੇਖੋ)"
|
msgstr "(ਸਭ ਵੇਖੋ)"
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:633 ../js/ui/dash.js:681
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
|
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:653
|
#: ../js/ui/dash.js:783
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "ਥਾਵਾਂ"
|
msgstr "ਥਾਵਾਂ"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:660 ../js/ui/dash.js:692
|
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
|
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:679
|
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "ਖੋਜ ਨਤੀਜੇ"
|
msgstr "ਖੋਜ ਨਤੀਜੇ"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:82
|
#: ../js/ui/dash.js:830
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "ਪਸੰਦ"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:272
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "ਸਰਗਰਮੀਆਂ"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:464
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "...ਨਾਲ ਕੁਨੈਕਟ ਕਰੋ"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "ਕਮਾਂਡ ਦਿਓ ਜੀ:"
|
msgstr "ਕਮਾਂਡ ਦਿਓ ਜੀ:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:840
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "'%s' ਚਲਾਉਣ ਲਈ ਫੇਲ੍ਹ:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:162
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:316
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "ਐਪਲੀਕੇਸ਼ਨ"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:341
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "ਤਾਜ਼ਾ ਡੌਕੂਮੈਂਟ"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
|
msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਚਿਰ ਪਹਿਲਾਂ"
|
||||||
|
|
||||||
#: ../src/shell-global.c:843
|
#: ../src/shell-global.c:815
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
|
msgstr[0] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
|
||||||
msgstr[1] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
|
msgstr[1] "%d ਮਿੰਟ ਪਹਿਲਾਂ"
|
||||||
|
|
||||||
#: ../src/shell-global.c:846
|
#: ../src/shell-global.c:818
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d ਘੰਟਾ ਪਹਿਲਾਂ"
|
msgstr[0] "%d ਘੰਟਾ ਪਹਿਲਾਂ"
|
||||||
msgstr[1] "%d ਘੰਟੇ ਪਹਿਲਾਂ"
|
msgstr[1] "%d ਘੰਟੇ ਪਹਿਲਾਂ"
|
||||||
|
|
||||||
#: ../src/shell-global.c:849
|
#: ../src/shell-global.c:821
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d ਦਿਨ ਪਹਿਲਾਂ"
|
msgstr[0] "%d ਦਿਨ ਪਹਿਲਾਂ"
|
||||||
msgstr[1] "%d ਦਿਨ ਪਹਿਲਾਂ"
|
msgstr[1] "%d ਦਿਨ ਪਹਿਲਾਂ"
|
||||||
|
|
||||||
#: ../src/shell-global.c:852
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -180,3 +223,5 @@ msgstr "ਖੋਜ"
|
|||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "ਝਲਕ"
|
||||||
|
104
po/pt_BR.po
@ -7,10 +7,11 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||||
"POT-Creation-Date: 2009-09-21 08:39-0300\n"
|
"shell&component=general\n"
|
||||||
"PO-Revision-Date: 2009-09-20 08:41-0300\n"
|
"POT-Creation-Date: 2009-11-12 21:33+0000\n"
|
||||||
"Last-Translator: Rodrigo Flores <mail@rodrigoflores.org>\n"
|
"PO-Revision-Date: 2009-11-14 11:53-0200\n"
|
||||||
|
"Last-Translator: Amanda Magalhães <amandinhakee@gmail.com>\n"
|
||||||
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
|
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@ -25,84 +26,115 @@ msgstr "GNOME Shell"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Gerenciamento de janelas e lançador de aplicações"
|
msgstr "Gerenciamento de janelas e lançador de aplicações"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:696
|
||||||
#: ../js/ui/panel.js:269
|
msgid "Drag here to add favorites"
|
||||||
msgid "Activities"
|
msgstr "Arraste até aqui para adicionar aos favoritos"
|
||||||
msgstr "Atividades"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appIcon.js:425
|
||||||
#: ../js/ui/panel.js:452
|
msgid "New Window"
|
||||||
msgid "%a %l:%M %p"
|
msgstr "Nova janela"
|
||||||
msgstr "%a %l:%M %p"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Remover dos Favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Adicionar aos Favoritos"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:237
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Encontre..."
|
msgstr "Procurar..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
|
||||||
msgid "Browse"
|
|
||||||
msgstr "Navegar"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(veja todos)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
|
#: ../js/ui/dash.js:656 ../js/ui/dash.js:718
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "APLICATIVOS"
|
msgstr "APLICATIVOS"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:676 ../js/ui/dash.js:733
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "LOCAIS"
|
msgstr "LOCAIS"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
|
#: ../js/ui/dash.js:683 ../js/ui/dash.js:728
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "DOCUMENTOS RECENTES"
|
msgstr "DOCUMENTOS RECENTES"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
|
#: ../js/ui/dash.js:708 ../js/ui/dash.js:898
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "RESULTADOS DA BUSCA"
|
msgstr "RESULTADOS DA BUSCA"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#: ../js/ui/dash.js:723
|
||||||
msgid "PREFERENCES"
|
msgid "PREFERENCES"
|
||||||
msgstr "PREFERÊNCIAS"
|
msgstr "PREFERÊNCIAS"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:101
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Atividades"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
|
#: ../js/ui/placeDisplay.js:84
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Conectar ao..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Por favor digite um comando:"
|
msgstr "Por favor digite um comando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "A execução de \"%s\" falhou:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplicações"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documentos Recentes"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Menos de um minuto atrás"
|
msgstr "Menos de um minuto atrás"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:824
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d minuto atrás"
|
msgstr[0] "%d minuto atrás"
|
||||||
msgstr[1] "%d minutos atrás"
|
msgstr[1] "%d minutos atrás"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:827
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d hora atrás"
|
msgstr[0] "%d hora atrás"
|
||||||
msgstr[1] "%d horas atrás"
|
msgstr[1] "%d horas atrás"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:830
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d dia atrás"
|
msgstr[0] "%d dia atrás"
|
||||||
msgstr[1] "%d dias atrás"
|
msgstr[1] "%d dias atrás"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:833
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -183,6 +215,12 @@ msgstr "Procurar"
|
|||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Navegar"
|
||||||
|
|
||||||
|
#~ msgid "(see all)"
|
||||||
|
#~ msgstr "(veja todos)"
|
||||||
|
|
||||||
#~ msgid "Find apps or documents"
|
#~ msgid "Find apps or documents"
|
||||||
#~ msgstr "Localizar aplicativos ou documentos"
|
#~ msgstr "Localizar aplicativos ou documentos"
|
||||||
|
|
||||||
|
232
po/ro.po
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
# Romanian translation for gnome-shell.
|
||||||
|
# Copyright (C) 2009 gnome-shell's COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
|
# Lucian Adrian Grijincu <lucian.grijincu@gmail.com>, 2009.
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2009-10-28 22:25+0200\n"
|
||||||
|
"PO-Revision-Date: 2009-10-28 22:33+0200\n"
|
||||||
|
"Last-Translator: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>\n"
|
||||||
|
"Language-Team: Romanian <gnomero-list@lists.sourceforge.net>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
|
||||||
|
"20)) ? 1 : 2);;\n"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
|
msgid "GNOME Shell"
|
||||||
|
msgstr "GNOME Shell"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
|
msgid "Window management and application launching"
|
||||||
|
msgstr "Administrare de ferestre și lansare de aplicații"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:332
|
||||||
|
msgid "Frequent"
|
||||||
|
msgstr "Frecvent"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:867
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Adăugați aici favorite cu mausul"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:426
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Fereastră nouă"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Șterge din favorite"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:431
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Adaugă la favorite"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Caută..."
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:400
|
||||||
|
msgid "More"
|
||||||
|
msgstr "Mai multe"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(arată tot)"
|
||||||
|
|
||||||
|
#. **** Applications ****
|
||||||
|
#: ../js/ui/dash.js:725 ../js/ui/dash.js:787
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "APPLICAȚII"
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:745
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "LOCAȚII"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:752 ../js/ui/dash.js:797
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "DOCUMENTE RECENTE"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:777 ../js/ui/dash.js:961
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "REZULTATELE CĂUTĂRII"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:792
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "PREFERINȚE"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:274
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Activități"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:491
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %l:%M %p"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Conectare la..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
|
msgid "Please enter a command:"
|
||||||
|
msgstr "Introduceți o comandă:"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Execuția comenzii „%s” a eșuat:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Aplicații"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Documente recente"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "În ultimul minut"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:824
|
||||||
|
#, c-format
|
||||||
|
msgid "%d minute ago"
|
||||||
|
msgid_plural "%d minutes ago"
|
||||||
|
msgstr[0] "acum un minut"
|
||||||
|
msgstr[1] "acum %d minute"
|
||||||
|
msgstr[2] "acum %d de minute"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:827
|
||||||
|
#, c-format
|
||||||
|
msgid "%d hour ago"
|
||||||
|
msgid_plural "%d hours ago"
|
||||||
|
msgstr[0] "acum o oră"
|
||||||
|
msgstr[1] "acum %d ore"
|
||||||
|
msgstr[2] "acum %d de ore"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:830
|
||||||
|
#, c-format
|
||||||
|
msgid "%d day ago"
|
||||||
|
msgid_plural "%d days ago"
|
||||||
|
msgstr[0] "acum o zi"
|
||||||
|
msgstr[1] "acum %d zile"
|
||||||
|
msgstr[2] "acum %d de zile"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:833
|
||||||
|
#, c-format
|
||||||
|
msgid "%d week ago"
|
||||||
|
msgid_plural "%d weeks ago"
|
||||||
|
msgstr[0] "acum o săptămână"
|
||||||
|
msgstr[1] "acum %d săptămâni"
|
||||||
|
msgstr[2] "acum %d de săptămâni"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:156
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Necunoscut"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:212
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't lock screen: %s"
|
||||||
|
msgstr "Nu s-a putut bloca ecranul: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:227
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
msgstr "Nu s-a putut folosi temporar un ecran gol pentru economizorul de "
|
||||||
|
"ecran: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't logout: %s"
|
||||||
|
msgstr "Nu se poate ieși din sesiune: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:492
|
||||||
|
msgid "Account Information..."
|
||||||
|
msgstr "Informații despre cont..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:502
|
||||||
|
msgid "Sidebar"
|
||||||
|
msgstr "Bară laterală"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:510
|
||||||
|
msgid "System Preferences..."
|
||||||
|
msgstr "Preferințe de sistem..."
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:525
|
||||||
|
msgid "Lock Screen"
|
||||||
|
msgstr "Blocare ecran"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:535
|
||||||
|
msgid "Switch User"
|
||||||
|
msgstr "Alt utilizator"
|
||||||
|
|
||||||
|
#. Only show switch user if there are other users
|
||||||
|
#. Log Out
|
||||||
|
#: ../src/shell-status-menu.c:546
|
||||||
|
msgid "Log Out..."
|
||||||
|
msgstr "Ieșire..."
|
||||||
|
|
||||||
|
#. Shut down
|
||||||
|
#: ../src/shell-status-menu.c:557
|
||||||
|
msgid "Shut Down..."
|
||||||
|
msgstr "Oprire..."
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:87
|
||||||
|
msgid "Home Folder"
|
||||||
|
msgstr "Dosar personal"
|
||||||
|
|
||||||
|
#. Translators: this is the same string as the one found in
|
||||||
|
#. * nautilus
|
||||||
|
#: ../src/shell-uri-util.c:102
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "Sistem de fișiere"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:248
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Caută"
|
||||||
|
|
||||||
|
#. Translators: the first string is the name of a gvfs
|
||||||
|
#. * method, and the second string is a path. For
|
||||||
|
#. * example, "Trash: some-directory". It means that the
|
||||||
|
#. * directory called "some-directory" is in the trash.
|
||||||
|
#.
|
||||||
|
#: ../src/shell-uri-util.c:298
|
||||||
|
#, c-format
|
||||||
|
msgid "%1$s: %2$s"
|
||||||
|
msgstr "%1$s: %2$s"
|
||||||
|
|
233
po/ru.po
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
#
|
||||||
|
# Andrey Korzinev <fellrond@gmail.com>, 2009.
|
||||||
|
# Sergey V. Kovylov <serejka@gmail.com>, 2009.
|
||||||
|
# Marina Zhurakhinskaya <marinaz@redhat.com>, 2009.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: gnome-shell\n"
|
||||||
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
|
||||||
|
"POT-Creation-Date: 2009-10-09 00:27-0400\n"
|
||||||
|
"PO-Revision-Date: 2009-10-09 00:27-0400\n"
|
||||||
|
"Last-Translator: Marina Zhurakhinskaya <marinaz@redhat.com>\n"
|
||||||
|
"Language-Team: Russian <gnome-cyr@gnome.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
|
||||||
|
"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:1
|
||||||
|
msgid "GNOME Shell"
|
||||||
|
msgstr "GNOME Shell"
|
||||||
|
|
||||||
|
#: ../data/gnome-shell.desktop.in.in.h:2
|
||||||
|
msgid "Window management and application launching"
|
||||||
|
msgstr "Управление окнами и запуск приложений"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:335
|
||||||
|
msgid "Frequent"
|
||||||
|
msgstr "Часто используемые"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:462
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Новое окно"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:475
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Удалить из избранного"
|
||||||
|
|
||||||
|
#: ../js/ui/appIcon.js:476
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Добавить в избранное"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:283
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Найти…"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:400
|
||||||
|
msgid "More"
|
||||||
|
msgstr "Ещё"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:543
|
||||||
|
#: "(увидеть все)" does not fit when it is next to "НЕДАВНИЕ ДОКУМЕНТЫ", so I'm just using "(все)"
|
||||||
|
msgid "(see all)"
|
||||||
|
msgstr "(все)"
|
||||||
|
|
||||||
|
#. **** Applications ****
|
||||||
|
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
|
||||||
|
msgid "APPLICATIONS"
|
||||||
|
msgstr "ПРИЛОЖЕНИЯ"
|
||||||
|
|
||||||
|
#. **** Places ****
|
||||||
|
#. Translators: This is in the sense of locations for documents,
|
||||||
|
#. network locations, etc.
|
||||||
|
#: ../js/ui/dash.js:783
|
||||||
|
msgid "PLACES"
|
||||||
|
msgstr "ПАПКИ И РЕСУРСЫ"
|
||||||
|
|
||||||
|
#. **** Documents ****
|
||||||
|
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
|
||||||
|
msgid "RECENT DOCUMENTS"
|
||||||
|
msgstr "НЕДАВНИЕ ДОКУМЕНТЫ"
|
||||||
|
|
||||||
|
#. **** Search Results ****
|
||||||
|
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
|
||||||
|
msgid "SEARCH RESULTS"
|
||||||
|
msgstr "РЕЗУЛЬТАТЫ ПОИСКА"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:830
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "НАСТРОЙКИ"
|
||||||
|
|
||||||
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:272
|
||||||
|
#. Another word that was considered was "Обзор", but it was decided that it doesn't give a full sense of the actions
|
||||||
|
#. possible when in the overview mode.
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Действия"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:464
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a, %H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/places.js:178
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Соединиться с…"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
|
msgid "Please enter a command:"
|
||||||
|
msgstr "Введите команду:"
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Не удалось выполнить «%s»:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:162
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:316
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Приложения"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:341
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Недавние документы"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:812
|
||||||
|
msgid "Less than a minute ago"
|
||||||
|
msgstr "Менее минуты назад"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:815
|
||||||
|
#, c-format
|
||||||
|
msgid "%d minute ago"
|
||||||
|
msgid_plural "%d minutes ago"
|
||||||
|
msgstr[0] "%d минуту назад"
|
||||||
|
msgstr[1] "%d минуты назад"
|
||||||
|
msgstr[2] "%d минут назад"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:818
|
||||||
|
#, c-format
|
||||||
|
msgid "%d hour ago"
|
||||||
|
msgid_plural "%d hours ago"
|
||||||
|
msgstr[0] "%d час назад"
|
||||||
|
msgstr[1] "%d часа назад"
|
||||||
|
msgstr[2] "%d часов назад"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:821
|
||||||
|
#, c-format
|
||||||
|
msgid "%d day ago"
|
||||||
|
msgid_plural "%d days ago"
|
||||||
|
msgstr[0] "%d день назад"
|
||||||
|
msgstr[1] "%d дня назад"
|
||||||
|
msgstr[2] "%d дней назад"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:824
|
||||||
|
#, c-format
|
||||||
|
msgid "%d week ago"
|
||||||
|
msgid_plural "%d weeks ago"
|
||||||
|
msgstr[0] "%d неделю назад"
|
||||||
|
msgstr[1] "%d недели назад"
|
||||||
|
msgstr[2] "%d недель назад"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:156
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "Неизвестно"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:212
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't lock screen: %s"
|
||||||
|
msgstr "Не удалось заблокировать экран: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:227
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
msgstr "Не удалось временно установить пустую экранную заставку: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Can't logout: %s"
|
||||||
|
msgstr "Не удалось завершить сеанс пользователя: %s"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:492
|
||||||
|
msgid "Account Information..."
|
||||||
|
msgstr "Информация о пользователе…"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:502
|
||||||
|
msgid "Sidebar"
|
||||||
|
msgstr "Боковая панель"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:510
|
||||||
|
msgid "System Preferences..."
|
||||||
|
msgstr "Системные настройки…"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:525
|
||||||
|
msgid "Lock Screen"
|
||||||
|
msgstr "Заблокировать экран"
|
||||||
|
|
||||||
|
#: ../src/shell-status-menu.c:535
|
||||||
|
msgid "Switch User"
|
||||||
|
msgstr "Сменить пользователя"
|
||||||
|
|
||||||
|
#. Only show switch user if there are other users
|
||||||
|
#. Log Out
|
||||||
|
#: ../src/shell-status-menu.c:546
|
||||||
|
msgid "Log Out..."
|
||||||
|
msgstr "Завершить сеанс…"
|
||||||
|
|
||||||
|
#. Shut down
|
||||||
|
#: ../src/shell-status-menu.c:557
|
||||||
|
msgid "Shut Down..."
|
||||||
|
msgstr "Выключить…"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:87
|
||||||
|
msgid "Home Folder"
|
||||||
|
msgstr "Домашняя папка"
|
||||||
|
|
||||||
|
#. Translators: this is the same string as the one found in
|
||||||
|
#. * nautilus
|
||||||
|
#: ../src/shell-uri-util.c:102
|
||||||
|
msgid "File System"
|
||||||
|
msgstr "Файловая система"
|
||||||
|
|
||||||
|
#: ../src/shell-uri-util.c:248
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Поиск"
|
||||||
|
|
||||||
|
#. Translators: the first string is the name of a gvfs
|
||||||
|
#. * method, and the second string is a path. For
|
||||||
|
#. * example, "Trash: some-directory". It means that the
|
||||||
|
#. * directory called "some-directory" is in the trash.
|
||||||
|
#.
|
||||||
|
#: ../src/shell-uri-util.c:298
|
||||||
|
#, c-format
|
||||||
|
msgid "%1$s: %2$s"
|
||||||
|
msgstr "%1$s: %2$s"
|
222
po/sl.po
@ -1,15 +1,17 @@
|
|||||||
# Slovenian translation for gnome-shell.
|
# Slovenian translation of gnome-shell package.
|
||||||
|
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||||
# This file is distributed under the same license as the gnome-shell package.
|
# This file is distributed under the same license as the gnome-shell package.
|
||||||
# Matej Urbančič <mateju@svn.gnome.org>, 2009.
|
#
|
||||||
|
# Matej Urbančič <mateju@svn.gnome.org>, 2009 - 2010.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell master\n"
|
"Project-Id-Version: gnome-shell master\n"
|
||||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
|
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n"
|
||||||
"POT-Creation-Date: 2009-09-21 22:38+0000\n"
|
"POT-Creation-Date: 2010-01-05 01:21+0000\n"
|
||||||
"PO-Revision-Date: 2009-09-22 10:36+0100\n"
|
"PO-Revision-Date: 2010-01-05 09:09+0100\n"
|
||||||
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
|
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
|
||||||
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
|
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
@ -27,66 +29,98 @@ msgstr "Gnome lupina"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Upravljanje oken in zaganjanje programov"
|
msgstr "Upravljanje oken in zaganjanje programov"
|
||||||
|
|
||||||
#. left side
|
|
||||||
#: ../js/ui/panel.js:269
|
|
||||||
msgid "Activities"
|
|
||||||
msgstr "Dejavnosti"
|
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
|
||||||
#: ../js/ui/panel.js:452
|
|
||||||
msgid "%a %l:%M %p"
|
|
||||||
msgstr "%a, %H:%M"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
|
||||||
msgid "Find..."
|
|
||||||
msgstr "Poišči ..."
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
|
||||||
msgid "Browse"
|
|
||||||
msgstr "Prebrskaj"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(poglej vse)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:753
|
#: ../js/ui/appDisplay.js:252
|
||||||
#: ../js/ui/dash.js:809
|
#: ../js/ui/dash.js:858
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "Programi"
|
msgstr "Programi"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:276
|
||||||
|
msgid "PREFERENCES"
|
||||||
|
msgstr "Možnosti"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:707
|
||||||
|
#: ../js/ui/appIcon.js:425
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr "Novo okno"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:711
|
||||||
|
#: ../js/ui/appIcon.js:429
|
||||||
|
msgid "Remove from Favorites"
|
||||||
|
msgstr "Odstrani iz priljubljenih"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:712
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Dodaj med priljubljene"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:1064
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "S potegom na to mesto se izbor doda med priljubljene"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:240
|
||||||
|
msgid "Find..."
|
||||||
|
msgstr "Najdi ..."
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:437
|
||||||
|
msgid "Searching..."
|
||||||
|
msgstr "Iskanje ..."
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:878
|
||||||
|
#: ../js/ui/placeDisplay.js:519
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "Mesta"
|
msgstr "Mesta"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780
|
#: ../js/ui/dash.js:885
|
||||||
#: ../js/ui/dash.js:819
|
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "Nedavni dokumenti"
|
msgstr "Nedavni dokumenti"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. Button on the left side of the panel.
|
||||||
#: ../js/ui/dash.js:799
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
#: ../js/ui/dash.js:931
|
#: ../js/ui/panel.js:227
|
||||||
msgid "SEARCH RESULTS"
|
msgid "Activities"
|
||||||
msgstr "Rezultati iskanja"
|
msgstr "Dejavnosti"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#. Translators: This is a time format.
|
||||||
msgid "PREFERENCES"
|
#: ../js/ui/panel.js:440
|
||||||
msgstr "Lastnosti"
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a, %H:%M"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:101
|
#: ../js/ui/placeDisplay.js:144
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Povezava z ..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:235
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Prosim, vnesite ukaz:"
|
msgstr "Vnos ukaza:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:351
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Izvajanje '%s' je spodletelo:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H:%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Programi"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Nedavni dokumenti"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:890
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Pred manj kot eno minuto"
|
msgstr "Pred manj kot eno minuto"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:893
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
@ -95,7 +129,7 @@ msgstr[1] "Pred %d minuto"
|
|||||||
msgstr[2] "Pred %d minutama"
|
msgstr[2] "Pred %d minutama"
|
||||||
msgstr[3] "Pred %d minutami"
|
msgstr[3] "Pred %d minutami"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:896
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
@ -104,7 +138,7 @@ msgstr[1] "Pred %d uro"
|
|||||||
msgstr[2] "Pred %d urama"
|
msgstr[2] "Pred %d urama"
|
||||||
msgstr[3] "Pred %d urami"
|
msgstr[3] "Pred %d urami"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:899
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
@ -113,7 +147,7 @@ msgstr[1] "Pred %d dnevom"
|
|||||||
msgstr[2] "Pred %d dnevoma"
|
msgstr[2] "Pred %d dnevoma"
|
||||||
msgstr[3] "Pred %d dnevi"
|
msgstr[3] "Pred %d dnevi"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:902
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
@ -122,77 +156,61 @@ msgstr[1] "Pred %d tednom"
|
|||||||
msgstr[2] "Pred %d tednoma"
|
msgstr[2] "Pred %d tednoma"
|
||||||
msgstr[3] "Pred %d tedni"
|
msgstr[3] "Pred %d tedni"
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:156
|
#: ../src/shell-uri-util.c:89
|
||||||
msgid "Unknown"
|
|
||||||
msgstr "Neznano"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:212
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't lock screen: %s"
|
|
||||||
msgstr "Ni mogoče zakleniti zaslona: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:227
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
|
||||||
msgstr "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:351
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't logout: %s"
|
|
||||||
msgstr "Ni se mogoče odjaviti: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:492
|
|
||||||
msgid "Account Information..."
|
|
||||||
msgstr "Podrobnosti računa ..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:502
|
|
||||||
msgid "Sidebar"
|
|
||||||
msgstr "_Stranska vrstica"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:510
|
|
||||||
msgid "System Preferences..."
|
|
||||||
msgstr "Sistemske lastnosti ..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:525
|
|
||||||
msgid "Lock Screen"
|
|
||||||
msgstr "Zakleni zaslon"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:535
|
|
||||||
msgid "Switch User"
|
|
||||||
msgstr "Preklop uporabnika"
|
|
||||||
|
|
||||||
#. Only show switch user if there are other users
|
|
||||||
#. Log Out
|
|
||||||
#: ../src/shell-status-menu.c:546
|
|
||||||
msgid "Log Out..."
|
|
||||||
msgstr "Odjava ..."
|
|
||||||
|
|
||||||
#. Shut down
|
|
||||||
#: ../src/shell-status-menu.c:557
|
|
||||||
msgid "Shut Down..."
|
|
||||||
msgstr "Izklop ..."
|
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:87
|
|
||||||
msgid "Home Folder"
|
msgid "Home Folder"
|
||||||
msgstr "Domača mapa"
|
msgstr "Domača mapa"
|
||||||
|
|
||||||
#. Translators: this is the same string as the one found in
|
#. Translators: this is the same string as the one found in
|
||||||
#. * nautilus
|
#. * nautilus
|
||||||
#: ../src/shell-uri-util.c:102
|
#: ../src/shell-uri-util.c:104
|
||||||
msgid "File System"
|
msgid "File System"
|
||||||
msgstr "Datotečni sistem"
|
msgstr "Datotečni sistem"
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:248
|
#: ../src/shell-uri-util.c:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Iskanje"
|
msgstr "Poišči"
|
||||||
|
|
||||||
#. Translators: the first string is the name of a gvfs
|
#. Translators: the first string is the name of a gvfs
|
||||||
#. * method, and the second string is a path. For
|
#. * method, and the second string is a path. For
|
||||||
#. * example, "Trash: some-directory". It means that the
|
#. * example, "Trash: some-directory". It means that the
|
||||||
#. * directory called "some-directory" is in the trash.
|
#. * directory called "some-directory" is in the trash.
|
||||||
#.
|
#.
|
||||||
#: ../src/shell-uri-util.c:298
|
#: ../src/shell-uri-util.c:300
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "SEARCH RESULTS"
|
||||||
|
#~ msgstr "Rezultati iskanja"
|
||||||
|
#~ msgid "Unknown"
|
||||||
|
#~ msgstr "Neznano"
|
||||||
|
#~ msgid "Can't lock screen: %s"
|
||||||
|
#~ msgstr "Ni mogoče zakleniti zaslona: %s"
|
||||||
|
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "Ni mogoče začasno nastaviti črnega zaslona za ohranjevalnik zaslona: %s"
|
||||||
|
#~ msgid "Can't logout: %s"
|
||||||
|
#~ msgstr "Ni se mogoče odjaviti: %s"
|
||||||
|
#~ msgid "Account Information..."
|
||||||
|
#~ msgstr "Podrobnosti računa ..."
|
||||||
|
#~ msgid "Sidebar"
|
||||||
|
#~ msgstr "Stranska vrstica"
|
||||||
|
#~ msgid "System Preferences..."
|
||||||
|
#~ msgstr "Sistemske možnosti ..."
|
||||||
|
#~ msgid "Lock Screen"
|
||||||
|
#~ msgstr "Zakleni zaslon"
|
||||||
|
#~ msgid "Switch User"
|
||||||
|
#~ msgstr "Preklop uporabnika"
|
||||||
|
#~ msgid "Log Out..."
|
||||||
|
#~ msgstr "Odjava ..."
|
||||||
|
#~ msgid "Shut Down..."
|
||||||
|
#~ msgstr "Izklopi ..."
|
||||||
|
#~ msgid "Frequent"
|
||||||
|
#~ msgstr "Pogosto"
|
||||||
|
#~ msgid "More"
|
||||||
|
#~ msgstr "Več"
|
||||||
|
#~ msgid "(see all)"
|
||||||
|
#~ msgstr "(poglej vse)"
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Prebrskaj"
|
||||||
|
|
||||||
|
185
po/sv.po
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gnome-shell\n"
|
"Project-Id-Version: gnome-shell\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2009-09-18 13:02+0200\n"
|
"POT-Creation-Date: 2009-12-01 23:52+0100\n"
|
||||||
"PO-Revision-Date: 2009-09-18 13:02+0100\n"
|
"PO-Revision-Date: 2009-12-01 23:53+0100\n"
|
||||||
"Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
|
"Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
|
||||||
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
|
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -24,154 +24,139 @@ msgstr "GNOME-skal"
|
|||||||
msgid "Window management and application launching"
|
msgid "Window management and application launching"
|
||||||
msgstr "Fönsterhantering och programstarter"
|
msgstr "Fönsterhantering och programstarter"
|
||||||
|
|
||||||
#. left side
|
#: ../js/ui/appDisplay.js:580
|
||||||
#: ../js/ui/panel.js:269
|
#: ../js/ui/appIcon.js:425
|
||||||
msgid "Activities"
|
msgid "New Window"
|
||||||
msgstr "Aktiviteter"
|
msgstr "Nytt fönster"
|
||||||
|
|
||||||
#. Translators: This is a time format.
|
#: ../js/ui/appDisplay.js:584
|
||||||
#: ../js/ui/panel.js:452
|
#: ../js/ui/appIcon.js:429
|
||||||
msgid "%a %l:%M %p"
|
msgid "Remove from Favorites"
|
||||||
msgstr "%a %H.%M"
|
msgstr "Ta bort från favoriter"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:283
|
#: ../js/ui/appDisplay.js:585
|
||||||
|
#: ../js/ui/appIcon.js:430
|
||||||
|
msgid "Add to Favorites"
|
||||||
|
msgstr "Lägg till som favorit"
|
||||||
|
|
||||||
|
#: ../js/ui/appDisplay.js:1029
|
||||||
|
msgid "Drag here to add favorites"
|
||||||
|
msgstr "Dra hit för att lägga till favorit"
|
||||||
|
|
||||||
|
#: ../js/ui/dash.js:236
|
||||||
msgid "Find..."
|
msgid "Find..."
|
||||||
msgstr "Sök..."
|
msgstr "Sök..."
|
||||||
|
|
||||||
#: ../js/ui/dash.js:400
|
|
||||||
msgid "Browse"
|
|
||||||
msgstr "Bläddra"
|
|
||||||
|
|
||||||
#: ../js/ui/dash.js:536
|
|
||||||
msgid "(see all)"
|
|
||||||
msgstr "(se alla)"
|
|
||||||
|
|
||||||
#. **** Applications ****
|
#. **** Applications ****
|
||||||
#: ../js/ui/dash.js:753
|
#: ../js/ui/dash.js:620
|
||||||
#: ../js/ui/dash.js:809
|
#: ../js/ui/dash.js:682
|
||||||
msgid "APPLICATIONS"
|
msgid "APPLICATIONS"
|
||||||
msgstr "PROGRAM"
|
msgstr "PROGRAM"
|
||||||
|
|
||||||
#. **** Places ****
|
#. **** Places ****
|
||||||
#. Translators: This is in the sense of locations for documents,
|
#. Translators: This is in the sense of locations for documents,
|
||||||
#. network locations, etc.
|
#. network locations, etc.
|
||||||
#: ../js/ui/dash.js:773
|
#: ../js/ui/dash.js:640
|
||||||
|
#: ../js/ui/dash.js:697
|
||||||
msgid "PLACES"
|
msgid "PLACES"
|
||||||
msgstr "PLATSER"
|
msgstr "PLATSER"
|
||||||
|
|
||||||
#. **** Documents ****
|
#. **** Documents ****
|
||||||
#: ../js/ui/dash.js:780
|
#: ../js/ui/dash.js:647
|
||||||
#: ../js/ui/dash.js:819
|
#: ../js/ui/dash.js:692
|
||||||
msgid "RECENT DOCUMENTS"
|
msgid "RECENT DOCUMENTS"
|
||||||
msgstr "SENASTE DOKUMENT"
|
msgstr "SENASTE DOKUMENT"
|
||||||
|
|
||||||
#. **** Search Results ****
|
#. **** Search Results ****
|
||||||
#: ../js/ui/dash.js:799
|
#: ../js/ui/dash.js:672
|
||||||
#: ../js/ui/dash.js:931
|
#: ../js/ui/dash.js:862
|
||||||
msgid "SEARCH RESULTS"
|
msgid "SEARCH RESULTS"
|
||||||
msgstr "SÖKRESULTAT"
|
msgstr "SÖKRESULTAT"
|
||||||
|
|
||||||
#: ../js/ui/dash.js:814
|
#: ../js/ui/dash.js:687
|
||||||
msgid "PREFERENCES"
|
msgid "PREFERENCES"
|
||||||
msgstr "INSTÄLLNINGAR"
|
msgstr "INSTÄLLNINGAR"
|
||||||
|
|
||||||
#: ../js/ui/runDialog.js:101
|
#. Button on the left side of the panel.
|
||||||
|
#. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview".
|
||||||
|
#: ../js/ui/panel.js:227
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Aktiviteter"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/panel.js:440
|
||||||
|
msgid "%a %l:%M %p"
|
||||||
|
msgstr "%a %H.%M"
|
||||||
|
|
||||||
|
#: ../js/ui/placeDisplay.js:84
|
||||||
|
msgid "Connect to..."
|
||||||
|
msgstr "Anslut till..."
|
||||||
|
|
||||||
|
#: ../js/ui/runDialog.js:96
|
||||||
msgid "Please enter a command:"
|
msgid "Please enter a command:"
|
||||||
msgstr "Ange ett kommando:"
|
msgstr "Ange ett kommando:"
|
||||||
|
|
||||||
#: ../src/shell-global.c:799
|
#: ../js/ui/runDialog.js:173
|
||||||
|
#, c-format
|
||||||
|
msgid "Execution of '%s' failed:"
|
||||||
|
msgstr "Körning av \"%s\" misslyckades:"
|
||||||
|
|
||||||
|
#. Translators: This is a time format.
|
||||||
|
#: ../js/ui/widget.js:163
|
||||||
|
msgid "%H:%M"
|
||||||
|
msgstr "%H.%M"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:317
|
||||||
|
msgid "Applications"
|
||||||
|
msgstr "Program"
|
||||||
|
|
||||||
|
#: ../js/ui/widget.js:339
|
||||||
|
msgid "Recent Documents"
|
||||||
|
msgstr "Senaste dokument"
|
||||||
|
|
||||||
|
#: ../src/shell-global.c:826
|
||||||
msgid "Less than a minute ago"
|
msgid "Less than a minute ago"
|
||||||
msgstr "Mindre än en minut sedan"
|
msgstr "Mindre än en minut sedan"
|
||||||
|
|
||||||
#: ../src/shell-global.c:802
|
#: ../src/shell-global.c:829
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d minute ago"
|
msgid "%d minute ago"
|
||||||
msgid_plural "%d minutes ago"
|
msgid_plural "%d minutes ago"
|
||||||
msgstr[0] "%d minut sedan"
|
msgstr[0] "%d minut sedan"
|
||||||
msgstr[1] "%d minuter sedan"
|
msgstr[1] "%d minuter sedan"
|
||||||
|
|
||||||
#: ../src/shell-global.c:805
|
#: ../src/shell-global.c:832
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d hour ago"
|
msgid "%d hour ago"
|
||||||
msgid_plural "%d hours ago"
|
msgid_plural "%d hours ago"
|
||||||
msgstr[0] "%d timme sedan"
|
msgstr[0] "%d timme sedan"
|
||||||
msgstr[1] "%d timmar sedan"
|
msgstr[1] "%d timmar sedan"
|
||||||
|
|
||||||
#: ../src/shell-global.c:808
|
#: ../src/shell-global.c:835
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d day ago"
|
msgid "%d day ago"
|
||||||
msgid_plural "%d days ago"
|
msgid_plural "%d days ago"
|
||||||
msgstr[0] "%d dag sedan"
|
msgstr[0] "%d dag sedan"
|
||||||
msgstr[1] "%d dagar sedan"
|
msgstr[1] "%d dagar sedan"
|
||||||
|
|
||||||
#: ../src/shell-global.c:811
|
#: ../src/shell-global.c:838
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%d week ago"
|
msgid "%d week ago"
|
||||||
msgid_plural "%d weeks ago"
|
msgid_plural "%d weeks ago"
|
||||||
msgstr[0] "%d vecka sedan"
|
msgstr[0] "%d vecka sedan"
|
||||||
msgstr[1] "%d veckor sedan"
|
msgstr[1] "%d veckor sedan"
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:156
|
#: ../src/shell-uri-util.c:89
|
||||||
msgid "Unknown"
|
|
||||||
msgstr "Okänt"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:212
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't lock screen: %s"
|
|
||||||
msgstr "Kan inte låsa skärmen: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:227
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't temporarily set screensaver to blank screen: %s"
|
|
||||||
msgstr "Kan inte temporärt ställa in skärmsläckaren till blank skärm: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:351
|
|
||||||
#, c-format
|
|
||||||
msgid "Can't logout: %s"
|
|
||||||
msgstr "Kan inte logga ut: %s"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:492
|
|
||||||
msgid "Account Information..."
|
|
||||||
msgstr "Kontoinformation..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:502
|
|
||||||
msgid "Sidebar"
|
|
||||||
msgstr "Sidopanel"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:510
|
|
||||||
msgid "System Preferences..."
|
|
||||||
msgstr "Systeminställningar..."
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:525
|
|
||||||
msgid "Lock Screen"
|
|
||||||
msgstr "Lås skärmen"
|
|
||||||
|
|
||||||
#: ../src/shell-status-menu.c:535
|
|
||||||
msgid "Switch User"
|
|
||||||
msgstr "Växla användare"
|
|
||||||
|
|
||||||
#. Only show switch user if there are other users
|
|
||||||
#. Log Out
|
|
||||||
#: ../src/shell-status-menu.c:546
|
|
||||||
msgid "Log Out..."
|
|
||||||
msgstr "Logga ut..."
|
|
||||||
|
|
||||||
#. Shut down
|
|
||||||
#: ../src/shell-status-menu.c:557
|
|
||||||
msgid "Shut Down..."
|
|
||||||
msgstr "Stäng av..."
|
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:87
|
|
||||||
msgid "Home Folder"
|
msgid "Home Folder"
|
||||||
msgstr "Hemmapp"
|
msgstr "Hemmapp"
|
||||||
|
|
||||||
#. Translators: this is the same string as the one found in
|
#. Translators: this is the same string as the one found in
|
||||||
#. * nautilus
|
#. * nautilus
|
||||||
#: ../src/shell-uri-util.c:102
|
#: ../src/shell-uri-util.c:104
|
||||||
msgid "File System"
|
msgid "File System"
|
||||||
msgstr "Filsystem"
|
msgstr "Filsystem"
|
||||||
|
|
||||||
#: ../src/shell-uri-util.c:248
|
#: ../src/shell-uri-util.c:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Sök"
|
msgstr "Sök"
|
||||||
|
|
||||||
@ -180,11 +165,37 @@ msgstr "Sök"
|
|||||||
#. * example, "Trash: some-directory". It means that the
|
#. * example, "Trash: some-directory". It means that the
|
||||||
#. * directory called "some-directory" is in the trash.
|
#. * directory called "some-directory" is in the trash.
|
||||||
#.
|
#.
|
||||||
#: ../src/shell-uri-util.c:298
|
#: ../src/shell-uri-util.c:300
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "%1$s: %2$s"
|
msgid "%1$s: %2$s"
|
||||||
msgstr "%1$s: %2$s"
|
msgstr "%1$s: %2$s"
|
||||||
|
|
||||||
|
#~ msgid "(see all)"
|
||||||
|
#~ msgstr "(se alla)"
|
||||||
|
#~ msgid "Unknown"
|
||||||
|
#~ msgstr "Okänt"
|
||||||
|
#~ msgid "Can't lock screen: %s"
|
||||||
|
#~ msgstr "Kan inte låsa skärmen: %s"
|
||||||
|
#~ msgid "Can't temporarily set screensaver to blank screen: %s"
|
||||||
|
#~ msgstr "Kan inte temporärt ställa in skärmsläckaren till blank skärm: %s"
|
||||||
|
#~ msgid "Can't logout: %s"
|
||||||
|
#~ msgstr "Kan inte logga ut: %s"
|
||||||
|
#~ msgid "Account Information..."
|
||||||
|
#~ msgstr "Kontoinformation..."
|
||||||
|
#~ msgid "Sidebar"
|
||||||
|
#~ msgstr "Sidopanel"
|
||||||
|
#~ msgid "System Preferences..."
|
||||||
|
#~ msgstr "Systeminställningar..."
|
||||||
|
#~ msgid "Lock Screen"
|
||||||
|
#~ msgstr "Lås skärmen"
|
||||||
|
#~ msgid "Switch User"
|
||||||
|
#~ msgstr "Växla användare"
|
||||||
|
#~ msgid "Log Out..."
|
||||||
|
#~ msgstr "Logga ut..."
|
||||||
|
#~ msgid "Shut Down..."
|
||||||
|
#~ msgstr "Stäng av..."
|
||||||
|
#~ msgid "Browse"
|
||||||
|
#~ msgstr "Bläddra"
|
||||||
#~ msgid "Find apps or documents"
|
#~ msgid "Find apps or documents"
|
||||||
#~ msgstr "Hitta program eller dokument"
|
#~ msgstr "Hitta program eller dokument"
|
||||||
#~ msgid "DOCUMENTS"
|
#~ msgid "DOCUMENTS"
|
||||||
|
154
src/Makefile-st.am
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
st_cflags = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-DPREFIX=\""$(prefix)"\" \
|
||||||
|
-DLIBDIR=\""$(libdir)"\" \
|
||||||
|
-DG_DISABLE_DEPRECATED \
|
||||||
|
-DG_LOG_DOMAIN=\"St\" \
|
||||||
|
-DST_COMPILATION \
|
||||||
|
-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" \
|
||||||
|
$(ST_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
st_built_sources = \
|
||||||
|
st-enum-types.h \
|
||||||
|
st-enum-types.c \
|
||||||
|
st-marshal.h \
|
||||||
|
st-marshal.c
|
||||||
|
|
||||||
|
BUILT_SOURCES += $(st_built_sources)
|
||||||
|
|
||||||
|
EXTRA_DIST += \
|
||||||
|
st/test-theme.css \
|
||||||
|
st/st-marshal.list \
|
||||||
|
st/st-enum-types.h.in \
|
||||||
|
st/st-enum-types.c.in
|
||||||
|
|
||||||
|
CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h
|
||||||
|
|
||||||
|
st-marshal.h: stamp-st-marshal.h
|
||||||
|
@true
|
||||||
|
stamp-st-marshal.h: Makefile st/st-marshal.list
|
||||||
|
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
|
||||||
|
--prefix=_st_marshal \
|
||||||
|
--header \
|
||||||
|
$(srcdir)/st/st-marshal.list > $@.tmp && \
|
||||||
|
(cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \
|
||||||
|
rm -f $@.tmp && \
|
||||||
|
echo timestamp > $(@F)
|
||||||
|
|
||||||
|
st-marshal.c: Makefile st/st-marshal.list
|
||||||
|
$(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \
|
||||||
|
$(GLIB_GENMARSHAL) \
|
||||||
|
--prefix=_st_marshal \
|
||||||
|
--body \
|
||||||
|
$(srcdir)/st/st-marshal.list ) > $@.tmp && \
|
||||||
|
cp -f $@.tmp st-marshal.c && \
|
||||||
|
rm -f $@.tmp
|
||||||
|
|
||||||
|
st-enum-types.h: stamp-st-enum-types.h Makefile
|
||||||
|
@true
|
||||||
|
stamp-st-enum-types.h: $(source_h) st/st-enum-types.h.in
|
||||||
|
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||||
|
$(GLIB_MKENUMS) \
|
||||||
|
--template st/st-enum-types.h.in \
|
||||||
|
$(st_source_h) ) >> $@.tmp && \
|
||||||
|
(cmp -s $@.tmp st-enum-types.h || cp $@.tmp st-enum-types.h) && \
|
||||||
|
rm -f $@.tmp && \
|
||||||
|
echo timestamp > $(@F)
|
||||||
|
|
||||||
|
st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in
|
||||||
|
$(AM_V_GEN) ( cd $(srcdir) && \
|
||||||
|
$(GLIB_MKENUMS) \
|
||||||
|
--template st/st-enum-types.c.in \
|
||||||
|
$(st_source_h) ) >> $@.tmp && \
|
||||||
|
cp $@.tmp $@ && \
|
||||||
|
rm -f $@.tmp
|
||||||
|
|
||||||
|
# please, keep this sorted alphabetically
|
||||||
|
st_source_h = \
|
||||||
|
st/st-adjustment.h \
|
||||||
|
st/st-bin.h \
|
||||||
|
st/st-border-image.h \
|
||||||
|
st/st-box-layout.h \
|
||||||
|
st/st-box-layout-child.h \
|
||||||
|
st/st-button.h \
|
||||||
|
st/st-clickable.h \
|
||||||
|
st/st-clipboard.h \
|
||||||
|
st/st-drawing-area.h \
|
||||||
|
st/st-entry.h \
|
||||||
|
st/st-im-text.h \
|
||||||
|
st/st-label.h \
|
||||||
|
st/st-overflow-box.h \
|
||||||
|
st/st-private.h \
|
||||||
|
st/st-scrollable.h \
|
||||||
|
st/st-scroll-bar.h \
|
||||||
|
st/st-scroll-view.h \
|
||||||
|
st/st-subtexture.h \
|
||||||
|
st/st-table.h \
|
||||||
|
st/st-table-child.h \
|
||||||
|
st/st-table-private.h \
|
||||||
|
st/st-texture-cache.h \
|
||||||
|
st/st-texture-frame.h \
|
||||||
|
st/st-theme.h \
|
||||||
|
st/st-theme-context.h \
|
||||||
|
st/st-theme-node.h \
|
||||||
|
st/st-theme-private.h \
|
||||||
|
st/st-tooltip.h \
|
||||||
|
st/st-types.h \
|
||||||
|
st/st-widget.h \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
st_source_private_h = \
|
||||||
|
st/st-private.h \
|
||||||
|
st/st-table-private.h \
|
||||||
|
st/st-theme-private.h
|
||||||
|
|
||||||
|
# please, keep this sorted alphabetically
|
||||||
|
st_source_c = \
|
||||||
|
st/st-adjustment.c \
|
||||||
|
st/st-bin.c \
|
||||||
|
st/st-border-image.c \
|
||||||
|
st/st-box-layout.c \
|
||||||
|
st/st-box-layout-child.c \
|
||||||
|
st/st-button.c \
|
||||||
|
st/st-clickable.c \
|
||||||
|
st/st-clipboard.c \
|
||||||
|
st/st-drawing-area.c \
|
||||||
|
st/st-entry.c \
|
||||||
|
st/st-im-text.c \
|
||||||
|
st/st-label.c \
|
||||||
|
st/st-overflow-box.c \
|
||||||
|
st/st-private.c \
|
||||||
|
st/st-scrollable.c \
|
||||||
|
st/st-scroll-bar.c \
|
||||||
|
st/st-scroll-view.c \
|
||||||
|
st/st-subtexture.c \
|
||||||
|
st/st-table.c \
|
||||||
|
st/st-table-child.c \
|
||||||
|
st/st-texture-cache.c \
|
||||||
|
st/st-texture-frame.c \
|
||||||
|
st/st-theme.c \
|
||||||
|
st/st-theme-context.c \
|
||||||
|
st/st-theme-node.c \
|
||||||
|
st/st-tooltip.c \
|
||||||
|
st/st-widget.c \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES += libst-1.0.la
|
||||||
|
|
||||||
|
libst_1_0_la_LIBADD = $(ST_LIBS)
|
||||||
|
libst_1_0_la_SOURCES = \
|
||||||
|
$(st_source_c) \
|
||||||
|
$(st_source_private_c) \
|
||||||
|
$(st_source_h) \
|
||||||
|
$(st_built_sources) \
|
||||||
|
$(NULL)
|
||||||
|
libst_1_0_la_CPPFLAGS = $(st_cflags)
|
||||||
|
libst_1_0_la_LDFLAGS = $(LDADD)
|
||||||
|
|
||||||
|
noinst_PROGRAMS += test-theme
|
||||||
|
|
||||||
|
test_theme_CPPFLAGS = $(st_cflags)
|
||||||
|
test_theme_LDADD = libst-1.0.la
|
||||||
|
|
||||||
|
test_theme_SOURCES = st/test-theme.c
|
@ -4,6 +4,7 @@ CLEANFILES =
|
|||||||
EXTRA_DIST =
|
EXTRA_DIST =
|
||||||
libexec_PROGRAMS =
|
libexec_PROGRAMS =
|
||||||
noinst_LTLIBRARIES =
|
noinst_LTLIBRARIES =
|
||||||
|
noinst_PROGRAMS =
|
||||||
|
|
||||||
.AUTOPARALLEL:
|
.AUTOPARALLEL:
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ EXTRA_DIST += gnome-shell.in
|
|||||||
|
|
||||||
include Makefile-big.am
|
include Makefile-big.am
|
||||||
include Makefile-gdmuser.am
|
include Makefile-gdmuser.am
|
||||||
|
include Makefile-st.am
|
||||||
include Makefile-tray.am
|
include Makefile-tray.am
|
||||||
|
|
||||||
gnome_shell_cflags = \
|
gnome_shell_cflags = \
|
||||||
@ -52,18 +54,19 @@ CLEANFILES += $(SHELL_STAMP_FILES)
|
|||||||
libgnome_shell_la_SOURCES = \
|
libgnome_shell_la_SOURCES = \
|
||||||
$(shell_built_sources) \
|
$(shell_built_sources) \
|
||||||
gnome-shell-plugin.c \
|
gnome-shell-plugin.c \
|
||||||
shell-app-monitor.c \
|
shell-app.c \
|
||||||
shell-app-monitor.h \
|
shell-app.h \
|
||||||
|
shell-app-private.h \
|
||||||
shell-app-system.c \
|
shell-app-system.c \
|
||||||
shell-app-system.h \
|
shell-app-system.h \
|
||||||
|
shell-app-usage.c \
|
||||||
|
shell-app-usage.h \
|
||||||
shell-arrow.c \
|
shell-arrow.c \
|
||||||
shell-arrow.h \
|
shell-arrow.h \
|
||||||
shell-button-box.c \
|
shell-doc-system.c \
|
||||||
shell-button-box.h \
|
shell-doc-system.h \
|
||||||
shell-drawing.c \
|
shell-drawing.c \
|
||||||
shell-drawing.h \
|
shell-drawing.h \
|
||||||
shell-drawing-area.c \
|
|
||||||
shell-drawing-area.h \
|
|
||||||
shell-embedded-window.c \
|
shell-embedded-window.c \
|
||||||
shell-embedded-window.h \
|
shell-embedded-window.h \
|
||||||
shell-embedded-window-private.h \
|
shell-embedded-window-private.h \
|
||||||
@ -81,8 +84,7 @@ libgnome_shell_la_SOURCES = \
|
|||||||
shell-process.h \
|
shell-process.h \
|
||||||
shell-global.c \
|
shell-global.c \
|
||||||
shell-global.h \
|
shell-global.h \
|
||||||
shell-status-menu.c \
|
shell-global-private.h \
|
||||||
shell-status-menu.h \
|
|
||||||
shell-stack.c \
|
shell-stack.c \
|
||||||
shell-stack.h \
|
shell-stack.h \
|
||||||
shell-tray-manager.c \
|
shell-tray-manager.c \
|
||||||
@ -91,11 +93,14 @@ libgnome_shell_la_SOURCES = \
|
|||||||
shell-texture-cache.h \
|
shell-texture-cache.h \
|
||||||
shell-uri-util.c \
|
shell-uri-util.c \
|
||||||
shell-uri-util.h \
|
shell-uri-util.h \
|
||||||
|
shell-window-tracker.c \
|
||||||
|
shell-window-tracker.h \
|
||||||
shell-wm.c \
|
shell-wm.c \
|
||||||
shell-wm.h
|
shell-wm.h
|
||||||
|
|
||||||
non_gir_sources = \
|
non_gir_sources = \
|
||||||
shell-embedded-window-private.h
|
shell-embedded-window-private.h \
|
||||||
|
shell-global-private.h
|
||||||
|
|
||||||
shell_recorder_sources = \
|
shell_recorder_sources = \
|
||||||
shell-recorder.c \
|
shell-recorder.c \
|
||||||
@ -112,7 +117,7 @@ if BUILD_RECORDER
|
|||||||
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
|
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
|
||||||
non_gir_sources += $(shell_recorder_non_gir_sources)
|
non_gir_sources += $(shell_recorder_non_gir_sources)
|
||||||
|
|
||||||
noinst_PROGRAMS = test-recorder
|
noinst_PROGRAMS += test-recorder
|
||||||
|
|
||||||
test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
|
test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
|
||||||
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
|
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
|
||||||
@ -150,23 +155,25 @@ libgnome_shell_la_LIBADD = \
|
|||||||
$(MUTTER_PLUGIN_LIBS) \
|
$(MUTTER_PLUGIN_LIBS) \
|
||||||
$(LIBGNOMEUI_LIBS) \
|
$(LIBGNOMEUI_LIBS) \
|
||||||
libbig-1.0.la \
|
libbig-1.0.la \
|
||||||
|
libst-1.0.la \
|
||||||
libgdmuser-1.0.la \
|
libgdmuser-1.0.la \
|
||||||
libtray.la
|
libtray.la
|
||||||
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
|
||||||
|
|
||||||
typelibdir = $(pkglibdir)
|
typelibdir = $(pkglibdir)
|
||||||
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
|
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib Gdm-1.0.typelib
|
||||||
|
|
||||||
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
|
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile
|
||||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||||
--namespace=Shell \
|
--namespace=Shell \
|
||||||
--nsversion=0.1 \
|
--nsversion=0.1 \
|
||||||
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
|
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
|
||||||
--include=Clutter-1.0 \
|
--include=Clutter-1.0 \
|
||||||
--include=Meta-2.27 \
|
--include=Meta-2.28 \
|
||||||
--libtool="$(LIBTOOL)" \
|
--libtool="$(LIBTOOL)" \
|
||||||
--add-include-path=$(builddir) \
|
--add-include-path=$(builddir) \
|
||||||
--include=Big-1.0 \
|
--include=Big-1.0 \
|
||||||
|
--include=St-1.0 \
|
||||||
--program=mutter \
|
--program=mutter \
|
||||||
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||||
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
|
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
|
||||||
@ -177,14 +184,14 @@ CLEANFILES += Shell-0.1.gir
|
|||||||
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
|
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
|
||||||
# (not the fake library, since we've already done the rewriting)
|
# (not the fake library, since we've already done the rewriting)
|
||||||
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
|
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
|
||||||
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
|
$(AM_V_GEN) \
|
||||||
$(G_IR_COMPILER) \
|
$(G_IR_COMPILER) \
|
||||||
--includedir=. \
|
--includedir=. \
|
||||||
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
|
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
|
||||||
Shell-0.1.gir -o $@
|
Shell-0.1.gir -o $@
|
||||||
CLEANFILES += Shell-0.1.typelib
|
CLEANFILES += Shell-0.1.typelib
|
||||||
|
|
||||||
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
|
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
|
||||||
$(AM_V_GEN) $(G_IR_SCANNER) \
|
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||||
--namespace=Big \
|
--namespace=Big \
|
||||||
--nsversion=1.0 \
|
--nsversion=1.0 \
|
||||||
@ -201,6 +208,47 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
|
|||||||
CLEANFILES += Big-1.0.gir
|
CLEANFILES += Big-1.0.gir
|
||||||
|
|
||||||
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
|
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
|
||||||
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
|
$(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
|
||||||
$(G_IR_COMPILER) Big-1.0.gir -o $@
|
|
||||||
CLEANFILES += Big-1.0.typelib
|
CLEANFILES += Big-1.0.typelib
|
||||||
|
|
||||||
|
St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
|
||||||
|
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||||
|
--namespace=St \
|
||||||
|
--nsversion=1.0 \
|
||||||
|
--include=Clutter-1.0 \
|
||||||
|
--add-include-path=$(builddir) \
|
||||||
|
--libtool="$(LIBTOOL)" \
|
||||||
|
--program=mutter \
|
||||||
|
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
|
||||||
|
-DST_COMPILATION \
|
||||||
|
$(addprefix $(srcdir)/,$(st_source_h)) \
|
||||||
|
$(addprefix $(srcdir)/,$(st_source_c)) \
|
||||||
|
$(srcdir)/st-enum-types.h \
|
||||||
|
$(st_cflags) \
|
||||||
|
-o $@
|
||||||
|
CLEANFILES += St-1.0.gir
|
||||||
|
|
||||||
|
St-1.0.typelib: St-1.0.gir
|
||||||
|
$(AM_V_GEN) $(G_IR_COMPILER) \
|
||||||
|
$< -o $@
|
||||||
|
|
||||||
|
CLEANFILES += St-1.0.typelib
|
||||||
|
|
||||||
|
Gdm-1.0.gir: $(mutter) $(G_IR_SCANNER) libgdmuser-1.0.la Makefile
|
||||||
|
$(AM_V_GEN) $(G_IR_SCANNER) \
|
||||||
|
--namespace=Gdm \
|
||||||
|
--nsversion=1.0 \
|
||||||
|
--include=GObject-2.0 \
|
||||||
|
--include=GdkPixbuf-2.0 \
|
||||||
|
--libtool="$(LIBTOOL)" \
|
||||||
|
--library=libgdmuser-1.0.la \
|
||||||
|
$(addprefix $(srcdir)/,$(gdmuser_source_h)) \
|
||||||
|
$(addprefix $(srcdir)/,$(gdmuser_source_c)) \
|
||||||
|
$(gdmuser_cflags) \
|
||||||
|
-o $@
|
||||||
|
CLEANFILES += Gdm-1.0.gir
|
||||||
|
|
||||||
|
Gdm-1.0.typelib: libbig-1.0.la Gdm-1.0.gir
|
||||||
|
$(AM_V_GEN) $(G_IR_COMPILER) Gdm-1.0.gir -o $@
|
||||||
|
CLEANFILES += Gdm-1.0.typelib
|
||||||
|
|
||||||
|
@ -268,6 +268,52 @@ corner_get(guint radius,
|
|||||||
return corner;
|
return corner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* To match the CSS specification, we want the border to look like it was
|
||||||
|
* drawn over the background. But actually drawing the border over the
|
||||||
|
* background will produce slightly bad antialiasing at the edges, so
|
||||||
|
* compute the effective border color instead.
|
||||||
|
*/
|
||||||
|
#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
|
||||||
|
#define MULT(c,a) NORM(c*a)
|
||||||
|
|
||||||
|
static void
|
||||||
|
premultiply (ClutterColor *color)
|
||||||
|
{
|
||||||
|
guint t;
|
||||||
|
color->red = MULT (color->red, color->alpha);
|
||||||
|
color->green = MULT (color->green, color->alpha);
|
||||||
|
color->blue = MULT (color->blue, color->alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unpremultiply (ClutterColor *color)
|
||||||
|
{
|
||||||
|
if (color->alpha != 0) {
|
||||||
|
color->red = (color->red * 255 + 127) / color->alpha;
|
||||||
|
color->green = (color->green * 255 + 127) / color->alpha;
|
||||||
|
color->blue = (color->blue * 255 + 127) / color->alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
over (const ClutterColor *source,
|
||||||
|
const ClutterColor *destination,
|
||||||
|
ClutterColor *result)
|
||||||
|
{
|
||||||
|
guint t;
|
||||||
|
ClutterColor src = *source;
|
||||||
|
ClutterColor dst = *destination;
|
||||||
|
premultiply (&src);
|
||||||
|
premultiply (&dst);
|
||||||
|
|
||||||
|
result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
|
||||||
|
result->red = src.red + NORM ((255 - src.alpha) * dst.red);
|
||||||
|
result->green = src.green + NORM ((255 - src.alpha) * dst.green);
|
||||||
|
result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue);
|
||||||
|
|
||||||
|
unpremultiply (result);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
big_rectangle_update_corners(BigRectangle *rectangle)
|
big_rectangle_update_corners(BigRectangle *rectangle)
|
||||||
{
|
{
|
||||||
@ -278,6 +324,7 @@ big_rectangle_update_corners(BigRectangle *rectangle)
|
|||||||
if (rectangle->radius != 0) {
|
if (rectangle->radius != 0) {
|
||||||
ClutterColor *color;
|
ClutterColor *color;
|
||||||
ClutterColor *border_color;
|
ClutterColor *border_color;
|
||||||
|
ClutterColor effective_border;
|
||||||
guint border_width;
|
guint border_width;
|
||||||
|
|
||||||
g_object_get(rectangle,
|
g_object_get(rectangle,
|
||||||
@ -286,10 +333,12 @@ big_rectangle_update_corners(BigRectangle *rectangle)
|
|||||||
"color", &color,
|
"color", &color,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
over (border_color, color, &effective_border);
|
||||||
|
|
||||||
corner = corner_get(rectangle->radius,
|
corner = corner_get(rectangle->radius,
|
||||||
color,
|
color,
|
||||||
border_width,
|
border_width,
|
||||||
border_color);
|
&effective_border);
|
||||||
|
|
||||||
clutter_color_free(border_color);
|
clutter_color_free(border_color);
|
||||||
clutter_color_free(color);
|
clutter_color_free(color);
|
||||||
@ -329,12 +378,10 @@ big_rectangle_paint(ClutterActor *actor)
|
|||||||
|
|
||||||
rectangle = BIG_RECTANGLE(actor);
|
rectangle = BIG_RECTANGLE(actor);
|
||||||
|
|
||||||
if (rectangle->radius == 0) {
|
/* We can't chain up, even when we the radius is 0, because of the different
|
||||||
/* In that case we are no different than our parent class,
|
* interpretation of the border/background relationship here than for
|
||||||
* so don't bother */
|
* ClutterRectangle.
|
||||||
CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
|
*/
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rectangle->corners_dirty)
|
if (rectangle->corners_dirty)
|
||||||
big_rectangle_update_corners(rectangle);
|
big_rectangle_update_corners(rectangle);
|
||||||
@ -345,6 +392,9 @@ big_rectangle_paint(ClutterActor *actor)
|
|||||||
"color", &color,
|
"color", &color,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
if (border_color->alpha == 0 && color->alpha == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
actor_opacity = clutter_actor_get_paint_opacity (actor);
|
actor_opacity = clutter_actor_get_paint_opacity (actor);
|
||||||
|
|
||||||
clutter_actor_get_allocation_box(actor, &box);
|
clutter_actor_get_allocation_box(actor, &box);
|
||||||
@ -358,6 +408,11 @@ big_rectangle_paint(ClutterActor *actor)
|
|||||||
|
|
||||||
radius = rectangle->radius;
|
radius = rectangle->radius;
|
||||||
|
|
||||||
|
/* Optimization; if the border is transparent, it just looks like part of
|
||||||
|
* the background */
|
||||||
|
if (radius == 0 && border_color->alpha == 0)
|
||||||
|
border_width = 0;
|
||||||
|
|
||||||
max = MAX(border_width, radius);
|
max = MAX(border_width, radius);
|
||||||
|
|
||||||
if (radius != 0) {
|
if (radius != 0) {
|
||||||
@ -393,33 +448,54 @@ big_rectangle_paint(ClutterActor *actor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (border_width != 0) {
|
if (border_width != 0) {
|
||||||
|
ClutterColor effective_border;
|
||||||
|
over (border_color, color, &effective_border);
|
||||||
|
|
||||||
if (!rectangle->border_material)
|
if (!rectangle->border_material)
|
||||||
rectangle->border_material = cogl_material_new ();
|
rectangle->border_material = cogl_material_new ();
|
||||||
|
|
||||||
cogl_color_set_from_4ub(&tmp_color,
|
cogl_color_set_from_4ub(&tmp_color,
|
||||||
border_color->red,
|
effective_border.red,
|
||||||
border_color->green,
|
effective_border.green,
|
||||||
border_color->blue,
|
effective_border.blue,
|
||||||
actor_opacity * border_color->alpha / 255);
|
actor_opacity * effective_border.alpha / 255);
|
||||||
cogl_color_premultiply (&tmp_color);
|
cogl_color_premultiply (&tmp_color);
|
||||||
cogl_material_set_color(rectangle->border_material, &tmp_color);
|
cogl_material_set_color(rectangle->border_material, &tmp_color);
|
||||||
cogl_set_source(rectangle->border_material);
|
cogl_set_source(rectangle->border_material);
|
||||||
|
|
||||||
/* NORTH */
|
if (radius > 0) { /* skip corners */
|
||||||
cogl_rectangle(max, 0,
|
/* NORTH */
|
||||||
width - max, border_width);
|
cogl_rectangle(max, 0,
|
||||||
|
width - max, border_width);
|
||||||
|
|
||||||
/* EAST */
|
/* EAST */
|
||||||
cogl_rectangle(width - border_width, max,
|
cogl_rectangle(width - border_width, max,
|
||||||
width, height - max);
|
width, height - max);
|
||||||
|
|
||||||
/* SOUTH */
|
/* SOUTH */
|
||||||
cogl_rectangle(max, height - border_width,
|
cogl_rectangle(max, height - border_width,
|
||||||
width - max, height);
|
width - max, height);
|
||||||
|
|
||||||
/* WEST */
|
/* WEST */
|
||||||
cogl_rectangle(0, max,
|
cogl_rectangle(0, max,
|
||||||
border_width, height - max);
|
border_width, height - max);
|
||||||
|
} else { /* include corners */
|
||||||
|
/* NORTH */
|
||||||
|
cogl_rectangle(0, 0,
|
||||||
|
width, border_width);
|
||||||
|
|
||||||
|
/* EAST */
|
||||||
|
cogl_rectangle(width - border_width, border_width,
|
||||||
|
width, height - border_width);
|
||||||
|
|
||||||
|
/* SOUTH */
|
||||||
|
cogl_rectangle(0, height - border_width,
|
||||||
|
width, height);
|
||||||
|
|
||||||
|
/* WEST */
|
||||||
|
cogl_rectangle(0, border_width,
|
||||||
|
border_width, height - border_width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rectangle->background_material)
|
if (!rectangle->background_material)
|
||||||
@ -455,6 +531,7 @@ big_rectangle_paint(ClutterActor *actor)
|
|||||||
cogl_rectangle(border_width, max,
|
cogl_rectangle(border_width, max,
|
||||||
width - border_width, height - max);
|
width - border_width, height - max);
|
||||||
|
|
||||||
|
out:
|
||||||
clutter_color_free(border_color);
|
clutter_color_free(border_color);
|
||||||
clutter_color_free(color);
|
clutter_color_free(color);
|
||||||
}
|
}
|
||||||
|
@ -998,7 +998,7 @@ gdm_user_manager_get_user (GdmUserManager *manager,
|
|||||||
|
|
||||||
GdmUser *
|
GdmUser *
|
||||||
gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
|
gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
|
||||||
uid_t uid)
|
gulong uid)
|
||||||
{
|
{
|
||||||
GdmUser *user;
|
GdmUser *user;
|
||||||
struct passwd *pwent;
|
struct passwd *pwent;
|
||||||
|
@ -75,7 +75,7 @@ GSList * gdm_user_manager_list_users (GdmUserManager *mana
|
|||||||
GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
|
GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
|
||||||
const char *user_name);
|
const char *user_name);
|
||||||
GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
|
GdmUser * gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
|
||||||
uid_t uid);
|
gulong uid);
|
||||||
|
|
||||||
gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
|
gboolean gdm_user_manager_activate_user_session (GdmUserManager *manager,
|
||||||
GdmUser *user);
|
GdmUser *user);
|
||||||
|
@ -516,7 +516,7 @@ _gdm_user_icon_changed (GdmUser *user)
|
|||||||
* Since: 1.0
|
* Since: 1.0
|
||||||
**/
|
**/
|
||||||
|
|
||||||
uid_t
|
gulong
|
||||||
gdm_user_get_uid (GdmUser *user)
|
gdm_user_get_uid (GdmUser *user)
|
||||||
{
|
{
|
||||||
g_return_val_if_fail (GDM_IS_USER (user), -1);
|
g_return_val_if_fail (GDM_IS_USER (user), -1);
|
||||||
|
@ -39,7 +39,7 @@ typedef struct _GdmUser GdmUser;
|
|||||||
|
|
||||||
GType gdm_user_get_type (void) G_GNUC_CONST;
|
GType gdm_user_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
uid_t gdm_user_get_uid (GdmUser *user);
|
gulong gdm_user_get_uid (GdmUser *user);
|
||||||
G_CONST_RETURN char *gdm_user_get_user_name (GdmUser *user);
|
G_CONST_RETURN char *gdm_user_get_user_name (GdmUser *user);
|
||||||
G_CONST_RETURN char *gdm_user_get_real_name (GdmUser *user);
|
G_CONST_RETURN char *gdm_user_get_real_name (GdmUser *user);
|
||||||
G_CONST_RETURN char *gdm_user_get_home_directory (GdmUser *user);
|
G_CONST_RETURN char *gdm_user_get_home_directory (GdmUser *user);
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
* 02111-1307, USA.
|
* 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define MUTTER_BUILDING_PLUGIN 1
|
#define MUTTER_BUILDING_PLUGIN 1
|
||||||
#include <mutter-plugin.h>
|
#include <mutter-plugin.h>
|
||||||
|
|
||||||
@ -40,7 +42,7 @@
|
|||||||
|
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
|
||||||
#include "shell-global.h"
|
#include "shell-global-private.h"
|
||||||
#include "shell-wm.h"
|
#include "shell-wm.h"
|
||||||
|
|
||||||
static void gnome_shell_plugin_constructed (GObject *object);
|
static void gnome_shell_plugin_constructed (GObject *object);
|
||||||
@ -192,6 +194,8 @@ gnome_shell_plugin_constructed (GObject *object)
|
|||||||
shell_plugin->gjs_context = gjs_context_new_with_search_path(search_path);
|
shell_plugin->gjs_context = gjs_context_new_with_search_path(search_path);
|
||||||
g_strfreev(search_path);
|
g_strfreev(search_path);
|
||||||
|
|
||||||
|
_shell_global_set_gjs_context (shell_global_get (), shell_plugin->gjs_context);
|
||||||
|
|
||||||
if (!gjs_context_eval (shell_plugin->gjs_context,
|
if (!gjs_context_eval (shell_plugin->gjs_context,
|
||||||
"const Main = imports.ui.main; Main.start();",
|
"const Main = imports.ui.main; Main.start();",
|
||||||
-1,
|
-1,
|
||||||
|
112
src/gnome-shell.in
Normal file → Executable file
@ -53,9 +53,7 @@ def start_xephyr():
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# Start some windows in our session.
|
# Start some windows in our session.
|
||||||
subprocess.Popen(["xterm", "-geometry", "+30+30"])
|
subprocess.Popen(["gnome-terminal"])
|
||||||
subprocess.Popen(["xlogo", "-geometry", "-0-0"])
|
|
||||||
subprocess.Popen(["xeyes", "-geometry", "-0+30"])
|
|
||||||
|
|
||||||
return xephyr;
|
return xephyr;
|
||||||
|
|
||||||
@ -212,6 +210,10 @@ parser.add_option("", "--geometry", metavar="GEOMETRY",
|
|||||||
default="1024x768");
|
default="1024x768");
|
||||||
parser.add_option("-w", "--wide", action="store_true",
|
parser.add_option("-w", "--wide", action="store_true",
|
||||||
help="Use widescreen (1280x800) with Xephyr")
|
help="Use widescreen (1280x800) with Xephyr")
|
||||||
|
parser.add_option("", "--eval-file", metavar="EVAL_FILE",
|
||||||
|
help="Evaluate the contents of the given JavaScript file")
|
||||||
|
parser.add_option("", "--create-extension", action="store_true",
|
||||||
|
help="Create a new GNOME Shell extension")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
@ -219,6 +221,105 @@ if args:
|
|||||||
parser.print_usage()
|
parser.print_usage()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if options.create_extension:
|
||||||
|
import json
|
||||||
|
|
||||||
|
print
|
||||||
|
print '''Name should be a very short (ideally descriptive) string.
|
||||||
|
Examples are: "Click To Focus", "Adblock", "Shell Window Shrinker".
|
||||||
|
'''
|
||||||
|
name = raw_input('Name: ').strip()
|
||||||
|
print
|
||||||
|
print '''Description is a single-sentence explanation of what your extension does.
|
||||||
|
Examples are: "Make windows visible on click", "Block advertisement popups"
|
||||||
|
"Animate windows shrinking on minimize"
|
||||||
|
'''
|
||||||
|
description = raw_input('Description: ').strip()
|
||||||
|
underifier = re.compile('[^A-Za-z]')
|
||||||
|
sample_uuid = underifier.sub('_', name)
|
||||||
|
# TODO use evolution data server
|
||||||
|
hostname = subprocess.Popen(['hostname'], stdout=subprocess.PIPE).communicate()[0].strip()
|
||||||
|
sample_uuid = sample_uuid + '@' + hostname
|
||||||
|
|
||||||
|
print
|
||||||
|
print '''Uuid is a globally-unique identifier for your extension.
|
||||||
|
This should be in the format of an email address (foo.bar@extensions.example.com), but
|
||||||
|
need not be an actual email address, though it's a good idea to base the uuid on your
|
||||||
|
email address. For example, if your email address is janedoe@example.com, you might
|
||||||
|
use an extension title clicktofocus@janedoe.example.com.'''
|
||||||
|
uuid = raw_input('Uuid [%s]: ' % (sample_uuid, )).strip()
|
||||||
|
if uuid == '':
|
||||||
|
uuid = sample_uuid
|
||||||
|
|
||||||
|
extension_path = os.path.join(os.path.expanduser('~/.config'), 'gnome-shell', 'extensions', uuid)
|
||||||
|
if os.path.exists(extension_path):
|
||||||
|
print "Extension path %r already exists" % (extension_path, )
|
||||||
|
sys.exit(0)
|
||||||
|
os.makedirs(extension_path)
|
||||||
|
meta = { 'name': name,
|
||||||
|
'description': description,
|
||||||
|
'uuid': uuid }
|
||||||
|
f = open(os.path.join(extension_path, 'metadata.json'), 'w')
|
||||||
|
try:
|
||||||
|
json.dump(meta, f)
|
||||||
|
except AttributeError:
|
||||||
|
# For Python versions older than 2.6, try using the json-py module
|
||||||
|
f.write(json.write(meta) + '\n')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
extensionjs_path = os.path.join(extension_path, 'extension.js')
|
||||||
|
f = open(extensionjs_path, 'w')
|
||||||
|
f.write('''// Sample extension code, makes clicking on the panel show a message
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
Main.panel.actor.reactive = true;
|
||||||
|
Main.panel.actor.connect('button-release-event', _showHello);
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open(os.path.join(extension_path, 'stylesheet.css'), 'w')
|
||||||
|
f.write('''/* Example stylesheet */
|
||||||
|
.helloworld-label {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: rgba(10,10,10,0.7);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
''')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
subprocess.Popen(['gnome-open', extensionjs_path])
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if options.eval_file:
|
||||||
|
import dbus
|
||||||
|
|
||||||
|
f = open(options.eval_file)
|
||||||
|
contents = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
session = dbus.SessionBus()
|
||||||
|
shell = session.get_object('org.gnome.Shell', '/org/gnome/Shell')
|
||||||
|
shell = dbus.Interface(shell, 'org.gnome.Shell')
|
||||||
|
result = shell.Eval(contents)
|
||||||
|
print result
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
if options.debug_command:
|
if options.debug_command:
|
||||||
options.debug = True
|
options.debug = True
|
||||||
elif options.debug:
|
elif options.debug:
|
||||||
@ -256,8 +357,9 @@ try:
|
|||||||
shell = None
|
shell = None
|
||||||
if options.xephyr:
|
if options.xephyr:
|
||||||
xephyr = start_xephyr()
|
xephyr = start_xephyr()
|
||||||
# This makes us not grab the org.gnome.Panel name
|
# This makes us not grab the org.gnome.Panel or
|
||||||
os.environ['GNOME_SHELL_NO_REPLACE_PANEL'] = '1'
|
# org.freedesktop.Notifications D-Bus names
|
||||||
|
os.environ['GNOME_SHELL_NO_REPLACE'] = '1'
|
||||||
shell = start_shell()
|
shell = start_shell()
|
||||||
else:
|
else:
|
||||||
xephyr = None
|
xephyr = None
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
||||||
|
|
||||||
#include <mutter-plugin.h>
|
|
||||||
|
|
||||||
ClutterActor *
|
|
||||||
mutter_plugin_get_overlay_group (MutterPlugin *plugin)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClutterActor *
|
|
||||||
mutter_plugin_get_stage (MutterPlugin *plugin)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GList *
|
|
||||||
mutter_plugin_get_windows (MutterPlugin *plugin)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mutter_plugin_query_screen_size (MutterPlugin *plugin,
|
|
||||||
int *width,
|
|
||||||
int *height)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
|
|
||||||
gint x, gint y, gint width, gint height)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaScreen *
|
|
||||||
mutter_plugin_get_screen (MutterPlugin *plugin)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClutterActor *
|
|
||||||
mutter_plugin_get_window_group (MutterPlugin *plugin)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Display *
|
|
||||||
meta_display_get_xdisplay (MetaDisplay *display)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaDisplay *
|
|
||||||
meta_screen_get_display (MetaScreen *display)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window
|
|
||||||
meta_screen_get_xroot (MetaScreen *display)
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
||||||
#ifndef __SHELL_APP_MONITOR_H__
|
|
||||||
#define __SHELL_APP_MONITOR_H__
|
|
||||||
|
|
||||||
#include <glib-object.h>
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include "window.h"
|
|
||||||
#include "shell-app-system.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This object provides monitoring of system application directories (.desktop files)
|
|
||||||
* and activity-based statistics about applications usage
|
|
||||||
*/
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _ShellAppMonitor ShellAppMonitor;
|
|
||||||
typedef struct _ShellAppMonitorClass ShellAppMonitorClass;
|
|
||||||
typedef struct _ShellAppMonitorPrivate ShellAppMonitorPrivate;
|
|
||||||
|
|
||||||
#define SHELL_TYPE_APP_MONITOR (shell_app_monitor_get_type ())
|
|
||||||
#define SHELL_APP_MONITOR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP_MONITOR, ShellAppMonitor))
|
|
||||||
#define SHELL_APP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP_MONITOR, ShellAppMonitorClass))
|
|
||||||
#define SHELL_IS_APP_MONITOR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP_MONITOR))
|
|
||||||
#define SHELL_IS_APP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP_MONITOR))
|
|
||||||
#define SHELL_APP_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP_MONITOR, ShellAppMonitorClass))
|
|
||||||
|
|
||||||
struct _ShellAppMonitorClass
|
|
||||||
{
|
|
||||||
GObjectClass parent_class;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
GType shell_app_monitor_get_type (void) G_GNUC_CONST;
|
|
||||||
|
|
||||||
ShellAppMonitor* shell_app_monitor_get_default(void);
|
|
||||||
|
|
||||||
ShellAppInfo *shell_app_monitor_get_window_app (ShellAppMonitor *monitor, MetaWindow *metawin);
|
|
||||||
|
|
||||||
GList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
|
|
||||||
const char *context,
|
|
||||||
gint number);
|
|
||||||
|
|
||||||
GSList *shell_app_monitor_get_windows_for_app (ShellAppMonitor *monitor, const char *appid);
|
|
||||||
|
|
||||||
gboolean shell_app_monitor_is_window_usage_tracked (MetaWindow *window);
|
|
||||||
|
|
||||||
/* Get whatever's running right now */
|
|
||||||
GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor, const char *context);
|
|
||||||
|
|
||||||
GSList *shell_app_monitor_get_startup_sequences (ShellAppMonitor *monitor);
|
|
||||||
|
|
||||||
/* Hidden typedef for SnStartupSequence */
|
|
||||||
typedef struct _ShellStartupSequence ShellStartupSequence;
|
|
||||||
#define SHELL_TYPE_STARTUP_SEQUENCE (shell_startup_sequence_get_type ())
|
|
||||||
GType shell_startup_sequence_get_type (void);
|
|
||||||
|
|
||||||
const char *shell_startup_sequence_get_id (ShellStartupSequence *sequence);
|
|
||||||
const char *shell_startup_sequence_get_name (ShellStartupSequence *sequence);
|
|
||||||
gboolean shell_startup_sequence_get_completed (ShellStartupSequence *sequence);
|
|
||||||
ClutterActor *shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* __SHELL_APP_MONITOR_H__ */
|
|
22
src/shell-app-private.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifndef __SHELL_APP_PRIVATE_H__
|
||||||
|
#define __SHELL_APP_PRIVATE_H__
|
||||||
|
|
||||||
|
#include "shell-app.h"
|
||||||
|
#include "shell-app-system.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
ShellAppInfo *_shell_app_get_info (ShellApp *app);
|
||||||
|
|
||||||
|
ShellApp* _shell_app_new_for_window (MetaWindow *window);
|
||||||
|
|
||||||
|
ShellApp* _shell_app_new (ShellAppInfo *appinfo);
|
||||||
|
|
||||||
|
void _shell_app_add_window (ShellApp *app, MetaWindow *window);
|
||||||
|
|
||||||
|
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SHELL_APP_PRIVATE_H__ */
|
@ -1,5 +1,7 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "shell-app-system.h"
|
#include "shell-app-system.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -10,6 +12,7 @@
|
|||||||
#include <gconf/gconf-client.h>
|
#include <gconf/gconf-client.h>
|
||||||
#include <clutter/clutter.h>
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include "shell-app-private.h"
|
||||||
#include "shell-global.h"
|
#include "shell-global.h"
|
||||||
#include "shell-texture-cache.h"
|
#include "shell-texture-cache.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
@ -33,7 +36,6 @@ enum {
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
INSTALLED_CHANGED,
|
INSTALLED_CHANGED,
|
||||||
FAVORITES_CHANGED,
|
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,14 +45,12 @@ struct _ShellAppSystemPrivate {
|
|||||||
GMenuTree *apps_tree;
|
GMenuTree *apps_tree;
|
||||||
GMenuTree *settings_tree;
|
GMenuTree *settings_tree;
|
||||||
|
|
||||||
|
GHashTable *app_id_to_info;
|
||||||
GHashTable *app_id_to_app;
|
GHashTable *app_id_to_app;
|
||||||
|
|
||||||
GSList *cached_app_menus; /* ShellAppMenuEntry */
|
GSList *cached_flattened_apps; /* ShellAppInfo */
|
||||||
|
|
||||||
GSList *cached_settings; /* ShellAppInfo */
|
GSList *cached_settings; /* ShellAppInfo */
|
||||||
|
|
||||||
GList *cached_favorites; /* utf8 */
|
|
||||||
|
|
||||||
gint app_monitor_id;
|
gint app_monitor_id;
|
||||||
|
|
||||||
guint app_change_timeout_id;
|
guint app_change_timeout_id;
|
||||||
@ -60,8 +60,6 @@ static void shell_app_system_finalize (GObject *object);
|
|||||||
static gboolean on_tree_changed (gpointer user_data);
|
static gboolean on_tree_changed (gpointer user_data);
|
||||||
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
|
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
|
||||||
static void reread_menus (ShellAppSystem *self);
|
static void reread_menus (ShellAppSystem *self);
|
||||||
static void on_favorite_apps_changed (GConfClient *client, guint id, GConfEntry *entry, gpointer user_data);
|
|
||||||
static void reread_favorite_apps (ShellAppSystem *system);
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
|
||||||
|
|
||||||
@ -82,6 +80,10 @@ struct _ShellAppInfo {
|
|||||||
*/
|
*/
|
||||||
guint refcount;
|
guint refcount;
|
||||||
|
|
||||||
|
char *casefolded_name;
|
||||||
|
char *name_collation_key;
|
||||||
|
char *casefolded_description;
|
||||||
|
|
||||||
GMenuTreeItem *entry;
|
GMenuTreeItem *entry;
|
||||||
|
|
||||||
GKeyFile *keyfile;
|
GKeyFile *keyfile;
|
||||||
@ -103,6 +105,11 @@ shell_app_info_unref (ShellAppInfo *info)
|
|||||||
{
|
{
|
||||||
if (--info->refcount > 0)
|
if (--info->refcount > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
g_free (info->casefolded_name);
|
||||||
|
g_free (info->name_collation_key);
|
||||||
|
g_free (info->casefolded_description);
|
||||||
|
|
||||||
switch (info->type)
|
switch (info->type)
|
||||||
{
|
{
|
||||||
case SHELL_APP_INFO_TYPE_ENTRY:
|
case SHELL_APP_INFO_TYPE_ENTRY:
|
||||||
@ -128,7 +135,7 @@ shell_app_info_new_from_tree_item (GMenuTreeItem *item)
|
|||||||
if (!item)
|
if (!item)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||||
info->type = SHELL_APP_INFO_TYPE_ENTRY;
|
info->type = SHELL_APP_INFO_TYPE_ENTRY;
|
||||||
info->refcount = 1;
|
info->refcount = 1;
|
||||||
info->entry = gmenu_tree_item_ref (item);
|
info->entry = gmenu_tree_item_ref (item);
|
||||||
@ -140,7 +147,7 @@ shell_app_info_new_from_window (MetaWindow *window)
|
|||||||
{
|
{
|
||||||
ShellAppInfo *info;
|
ShellAppInfo *info;
|
||||||
|
|
||||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||||
info->type = SHELL_APP_INFO_TYPE_WINDOW;
|
info->type = SHELL_APP_INFO_TYPE_WINDOW;
|
||||||
info->refcount = 1;
|
info->refcount = 1;
|
||||||
info->window = g_object_ref (window);
|
info->window = g_object_ref (window);
|
||||||
@ -158,7 +165,7 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
|
|||||||
{
|
{
|
||||||
ShellAppInfo *info;
|
ShellAppInfo *info;
|
||||||
|
|
||||||
info = g_slice_alloc (sizeof (ShellAppInfo));
|
info = g_slice_alloc0 (sizeof (ShellAppInfo));
|
||||||
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
|
info->type = SHELL_APP_INFO_TYPE_DESKTOP_FILE;
|
||||||
info->refcount = 1;
|
info->refcount = 1;
|
||||||
info->keyfile = keyfile;
|
info->keyfile = keyfile;
|
||||||
@ -166,29 +173,6 @@ shell_app_info_new_from_keyfile_take_ownership (GKeyFile *keyfile,
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gpointer
|
|
||||||
shell_app_menu_entry_copy (gpointer entryp)
|
|
||||||
{
|
|
||||||
ShellAppMenuEntry *entry;
|
|
||||||
ShellAppMenuEntry *copy;
|
|
||||||
entry = entryp;
|
|
||||||
copy = g_new0 (ShellAppMenuEntry, 1);
|
|
||||||
copy->name = g_strdup (entry->name);
|
|
||||||
copy->id = g_strdup (entry->id);
|
|
||||||
copy->icon = g_strdup (entry->icon);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_app_menu_entry_free (gpointer entryp)
|
|
||||||
{
|
|
||||||
ShellAppMenuEntry *entry = entryp;
|
|
||||||
g_free (entry->name);
|
|
||||||
g_free (entry->id);
|
|
||||||
g_free (entry->icon);
|
|
||||||
g_free (entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class = (GObjectClass *)klass;
|
GObjectClass *gobject_class = (GObjectClass *)klass;
|
||||||
@ -203,14 +187,6 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
|
|||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
g_cclosure_marshal_VOID__VOID,
|
g_cclosure_marshal_VOID__VOID,
|
||||||
G_TYPE_NONE, 0);
|
G_TYPE_NONE, 0);
|
||||||
signals[FAVORITES_CHANGED] =
|
|
||||||
g_signal_new ("favorites-changed",
|
|
||||||
SHELL_TYPE_APP_SYSTEM,
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (ShellAppSystemClass, favorites_changed),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE, 0);
|
|
||||||
|
|
||||||
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
|
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
|
||||||
}
|
}
|
||||||
@ -226,8 +202,11 @@ shell_app_system_init (ShellAppSystem *self)
|
|||||||
ShellAppSystemPrivate);
|
ShellAppSystemPrivate);
|
||||||
|
|
||||||
/* The key is owned by the value */
|
/* The key is owned by the value */
|
||||||
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
|
priv->app_id_to_info = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
NULL, (GDestroyNotify) shell_app_info_unref);
|
NULL, (GDestroyNotify) shell_app_info_unref);
|
||||||
|
|
||||||
|
/* Key is owned by info */
|
||||||
|
priv->app_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
|
|
||||||
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
/* For now, we want to pick up Evince, Nautilus, etc. We'll
|
||||||
* handle NODISPLAY semantics at a higher level or investigate them
|
* handle NODISPLAY semantics at a higher level or investigate them
|
||||||
@ -244,10 +223,6 @@ shell_app_system_init (ShellAppSystem *self)
|
|||||||
reread_menus (self);
|
reread_menus (self);
|
||||||
|
|
||||||
client = gconf_client_get_default ();
|
client = gconf_client_get_default ();
|
||||||
|
|
||||||
self->priv->app_monitor_id = gconf_client_notify_add (client, SHELL_APP_FAVORITES_KEY,
|
|
||||||
on_favorite_apps_changed, self, NULL, NULL);
|
|
||||||
reread_favorite_apps (self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -262,69 +237,23 @@ shell_app_system_finalize (GObject *object)
|
|||||||
gmenu_tree_unref (priv->apps_tree);
|
gmenu_tree_unref (priv->apps_tree);
|
||||||
gmenu_tree_unref (priv->settings_tree);
|
gmenu_tree_unref (priv->settings_tree);
|
||||||
|
|
||||||
|
g_hash_table_destroy (priv->app_id_to_info);
|
||||||
g_hash_table_destroy (priv->app_id_to_app);
|
g_hash_table_destroy (priv->app_id_to_app);
|
||||||
|
|
||||||
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
|
g_slist_foreach (priv->cached_flattened_apps, (GFunc)shell_app_info_unref, NULL);
|
||||||
g_slist_free (priv->cached_app_menus);
|
g_slist_free (priv->cached_flattened_apps);
|
||||||
priv->cached_app_menus = NULL;
|
priv->cached_flattened_apps = NULL;
|
||||||
|
|
||||||
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
|
g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
|
||||||
g_slist_free (priv->cached_settings);
|
g_slist_free (priv->cached_settings);
|
||||||
priv->cached_settings = NULL;
|
priv->cached_settings = NULL;
|
||||||
|
|
||||||
g_list_free (priv->cached_favorites);
|
|
||||||
|
|
||||||
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
|
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
|
|
||||||
{
|
|
||||||
GMenuTreeDirectory *trunk;
|
|
||||||
GSList *entries;
|
|
||||||
GSList *iter;
|
|
||||||
|
|
||||||
trunk = gmenu_tree_get_root_directory (tree);
|
|
||||||
entries = gmenu_tree_directory_get_contents (trunk);
|
|
||||||
|
|
||||||
g_slist_foreach (*cache, (GFunc)shell_app_menu_entry_free, NULL);
|
|
||||||
g_slist_free (*cache);
|
|
||||||
*cache = NULL;
|
|
||||||
|
|
||||||
for (iter = entries; iter; iter = iter->next)
|
|
||||||
{
|
|
||||||
GMenuTreeItem *item = iter->data;
|
|
||||||
|
|
||||||
switch (gmenu_tree_item_get_type (item))
|
|
||||||
{
|
|
||||||
case GMENU_TREE_ITEM_DIRECTORY:
|
|
||||||
{
|
|
||||||
GMenuTreeDirectory *dir = iter->data;
|
|
||||||
ShellAppMenuEntry *shell_entry = g_new0 (ShellAppMenuEntry, 1);
|
|
||||||
shell_entry->name = g_strdup (gmenu_tree_directory_get_name (dir));
|
|
||||||
shell_entry->id = g_strdup (gmenu_tree_directory_get_menu_id (dir));
|
|
||||||
shell_entry->icon = g_strdup (gmenu_tree_directory_get_icon (dir));
|
|
||||||
|
|
||||||
*cache = g_slist_prepend (*cache, shell_entry);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gmenu_tree_item_unref (item);
|
|
||||||
}
|
|
||||||
*cache = g_slist_reverse (*cache);
|
|
||||||
|
|
||||||
g_slist_free (entries);
|
|
||||||
gmenu_tree_item_unref (trunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSList *
|
static GSList *
|
||||||
gather_entries_recurse (ShellAppSystem *monitor,
|
gather_entries_recurse (ShellAppSystem *monitor,
|
||||||
GSList *apps,
|
GSList *apps,
|
||||||
|
GHashTable *unique,
|
||||||
GMenuTreeDirectory *root)
|
GMenuTreeDirectory *root)
|
||||||
{
|
{
|
||||||
GSList *contents;
|
GSList *contents;
|
||||||
@ -340,13 +269,17 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
|||||||
case GMENU_TREE_ITEM_ENTRY:
|
case GMENU_TREE_ITEM_ENTRY:
|
||||||
{
|
{
|
||||||
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
|
ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
|
||||||
apps = g_slist_prepend (apps, app);
|
if (!g_hash_table_lookup (unique, shell_app_info_get_id (app)))
|
||||||
|
{
|
||||||
|
apps = g_slist_prepend (apps, app);
|
||||||
|
g_hash_table_insert (unique, (char*)shell_app_info_get_id (app), app);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GMENU_TREE_ITEM_DIRECTORY:
|
case GMENU_TREE_ITEM_DIRECTORY:
|
||||||
{
|
{
|
||||||
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
|
||||||
apps = gather_entries_recurse (monitor, apps, dir);
|
apps = gather_entries_recurse (monitor, apps, unique, dir);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -363,6 +296,7 @@ gather_entries_recurse (ShellAppSystem *monitor,
|
|||||||
static void
|
static void
|
||||||
reread_entries (ShellAppSystem *self,
|
reread_entries (ShellAppSystem *self,
|
||||||
GSList **cache,
|
GSList **cache,
|
||||||
|
GHashTable *unique,
|
||||||
GMenuTree *tree)
|
GMenuTree *tree)
|
||||||
{
|
{
|
||||||
GMenuTreeDirectory *trunk;
|
GMenuTreeDirectory *trunk;
|
||||||
@ -373,54 +307,51 @@ reread_entries (ShellAppSystem *self,
|
|||||||
g_slist_free (*cache);
|
g_slist_free (*cache);
|
||||||
*cache = NULL;
|
*cache = NULL;
|
||||||
|
|
||||||
*cache = gather_entries_recurse (self, *cache, trunk);
|
*cache = gather_entries_recurse (self, *cache, unique, trunk);
|
||||||
|
|
||||||
gmenu_tree_item_unref (trunk);
|
gmenu_tree_item_unref (trunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cache_by_id (ShellAppSystem *self, GSList *apps, gboolean ref)
|
cache_by_id (ShellAppSystem *self, GSList *apps)
|
||||||
{
|
{
|
||||||
GSList *iter;
|
GSList *iter;
|
||||||
|
|
||||||
for (iter = apps; iter; iter = iter->next)
|
for (iter = apps; iter; iter = iter->next)
|
||||||
{
|
{
|
||||||
ShellAppInfo *info = iter->data;
|
ShellAppInfo *info = iter->data;
|
||||||
if (ref)
|
shell_app_info_ref (info);
|
||||||
shell_app_info_ref (info);
|
|
||||||
/* the name is owned by the info itself */
|
/* the name is owned by the info itself */
|
||||||
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (info),
|
g_hash_table_replace (self->priv->app_id_to_info, (char*)shell_app_info_get_id (info),
|
||||||
info);
|
info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reread_menus (ShellAppSystem *self)
|
reread_menus (ShellAppSystem *self)
|
||||||
{
|
{
|
||||||
GSList *apps;
|
GHashTable *unique = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
GMenuTreeDirectory *trunk;
|
|
||||||
|
|
||||||
reread_directories (self, &(self->priv->cached_app_menus), self->priv->apps_tree);
|
reread_entries (self, &(self->priv->cached_flattened_apps), unique, self->priv->apps_tree);
|
||||||
|
g_hash_table_remove_all (unique);
|
||||||
|
reread_entries (self, &(self->priv->cached_settings), unique, self->priv->settings_tree);
|
||||||
|
g_hash_table_destroy (unique);
|
||||||
|
|
||||||
reread_entries (self, &(self->priv->cached_settings), self->priv->settings_tree);
|
g_hash_table_remove_all (self->priv->app_id_to_info);
|
||||||
|
|
||||||
/* Now loop over applications.menu and settings.menu, inserting each by desktop file
|
cache_by_id (self, self->priv->cached_flattened_apps);
|
||||||
* ID into a hash */
|
cache_by_id (self, self->priv->cached_settings);
|
||||||
g_hash_table_remove_all (self->priv->app_id_to_app);
|
|
||||||
trunk = gmenu_tree_get_root_directory (self->priv->apps_tree);
|
|
||||||
apps = gather_entries_recurse (self, NULL, trunk);
|
|
||||||
gmenu_tree_item_unref (trunk);
|
|
||||||
cache_by_id (self, apps, FALSE);
|
|
||||||
g_slist_free (apps);
|
|
||||||
cache_by_id (self, self->priv->cached_settings, TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
on_tree_changed (gpointer user_data)
|
on_tree_changed (gpointer user_data)
|
||||||
{
|
{
|
||||||
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
|
||||||
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
|
||||||
reread_menus (self);
|
reread_menus (self);
|
||||||
|
|
||||||
|
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
|
||||||
|
|
||||||
self->priv->app_change_timeout_id = 0;
|
self->priv->app_change_timeout_id = 0;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -450,63 +381,6 @@ on_tree_changed_cb (GMenuTree *monitor, gpointer user_data)
|
|||||||
self, NULL);
|
self, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GList *
|
|
||||||
convert_gconf_value_string_list_to_list_uniquify (GConfValue *value )
|
|
||||||
{
|
|
||||||
GSList *list;
|
|
||||||
GSList *tmp;
|
|
||||||
GList *result = NULL;
|
|
||||||
GHashTable *tmp_table = g_hash_table_new (g_str_hash, g_str_equal);
|
|
||||||
|
|
||||||
list = gconf_value_get_list (value);
|
|
||||||
|
|
||||||
for (tmp = list ; tmp; tmp = tmp->next)
|
|
||||||
{
|
|
||||||
GConfValue *value = tmp->data;
|
|
||||||
char *str = g_strdup (gconf_value_get_string (value));
|
|
||||||
if (!str)
|
|
||||||
continue;
|
|
||||||
if (g_hash_table_lookup (tmp_table, str))
|
|
||||||
{
|
|
||||||
g_free (str);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
g_hash_table_insert (tmp_table, str, GUINT_TO_POINTER(1));
|
|
||||||
result = g_list_prepend (result, str);
|
|
||||||
}
|
|
||||||
g_hash_table_destroy (tmp_table);
|
|
||||||
return g_list_reverse (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
reread_favorite_apps (ShellAppSystem *system)
|
|
||||||
{
|
|
||||||
GConfClient *client = gconf_client_get_default ();
|
|
||||||
GConfValue *val;
|
|
||||||
|
|
||||||
val = gconf_client_get (client, SHELL_APP_FAVORITES_KEY, NULL);
|
|
||||||
|
|
||||||
if (!(val && val->type == GCONF_VALUE_LIST && gconf_value_get_list_type (val) == GCONF_VALUE_STRING))
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_list_foreach (system->priv->cached_favorites, (GFunc) g_free, NULL);
|
|
||||||
g_list_free (system->priv->cached_favorites);
|
|
||||||
system->priv->cached_favorites = convert_gconf_value_string_list_to_list_uniquify (val);
|
|
||||||
|
|
||||||
gconf_value_free (val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
on_favorite_apps_changed (GConfClient *client,
|
|
||||||
guint id,
|
|
||||||
GConfEntry *entry,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
ShellAppSystem *system = SHELL_APP_SYSTEM (user_data);
|
|
||||||
reread_favorite_apps (system);
|
|
||||||
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
GType
|
GType
|
||||||
shell_app_info_get_type (void)
|
shell_app_info_get_type (void)
|
||||||
{
|
{
|
||||||
@ -520,58 +394,19 @@ shell_app_info_get_type (void)
|
|||||||
return gtype;
|
return gtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
GType
|
|
||||||
shell_app_menu_entry_get_type (void)
|
|
||||||
{
|
|
||||||
static GType gtype = G_TYPE_INVALID;
|
|
||||||
if (gtype == G_TYPE_INVALID)
|
|
||||||
{
|
|
||||||
gtype = g_boxed_type_register_static ("ShellAppMenuEntry",
|
|
||||||
shell_app_menu_entry_copy,
|
|
||||||
shell_app_menu_entry_free);
|
|
||||||
}
|
|
||||||
return gtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_app_system_get_applications_for_menu:
|
* shell_app_system_get_flattened_apps:
|
||||||
*
|
*
|
||||||
* Traverses a toplevel menu, and returns all items under it. Nested items
|
* Traverses a toplevel menu, and returns all items under it. Nested items
|
||||||
* are flattened.
|
* are flattened. This value is computed on initial call and cached thereafter
|
||||||
|
* until the set of installed applications changes.
|
||||||
*
|
*
|
||||||
* Return value: (transfer full) (element-type ShellAppInfo): List of applications
|
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
|
||||||
*/
|
*/
|
||||||
GSList *
|
GSList *
|
||||||
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
|
shell_app_system_get_flattened_apps (ShellAppSystem *self)
|
||||||
const char *menu)
|
|
||||||
{
|
{
|
||||||
char *path;
|
return self->priv->cached_flattened_apps;
|
||||||
GMenuTreeDirectory *menu_entry;
|
|
||||||
GSList *apps;
|
|
||||||
|
|
||||||
path = g_strdup_printf ("/%s", menu);
|
|
||||||
menu_entry = gmenu_tree_get_directory_from_path (monitor->priv->apps_tree, path);
|
|
||||||
g_free (path);
|
|
||||||
g_assert (menu_entry != NULL);
|
|
||||||
|
|
||||||
apps = gather_entries_recurse (monitor, NULL, menu_entry);
|
|
||||||
|
|
||||||
gmenu_tree_item_unref (menu_entry);
|
|
||||||
|
|
||||||
return apps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_app_system_get_menus:
|
|
||||||
*
|
|
||||||
* Returns a list of toplevel #ShellAppMenuEntry items
|
|
||||||
*
|
|
||||||
* Return value: (transfer none) (element-type AppMenuEntry): List of toplevel menus
|
|
||||||
*/
|
|
||||||
GSList *
|
|
||||||
shell_app_system_get_menus (ShellAppSystem *monitor)
|
|
||||||
{
|
|
||||||
return monitor->priv->cached_app_menus;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -603,93 +438,70 @@ shell_app_system_get_default ()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
typedef struct {
|
||||||
* shell_app_system_get_favorites:
|
ShellAppSystem *appsys;
|
||||||
*
|
ShellAppInfo *info;
|
||||||
* Return the list of applications which have been explicitly added to the
|
} ShellAppRef;
|
||||||
* favorites.
|
|
||||||
*
|
|
||||||
* Return value: (transfer none) (element-type utf8): List of favorite application ids
|
|
||||||
*/
|
|
||||||
GList *
|
|
||||||
shell_app_system_get_favorites (ShellAppSystem *system)
|
|
||||||
{
|
|
||||||
return system->priv->cached_favorites;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_gconf_value_string_list (GConfValue *val, GList *items)
|
shell_app_system_on_app_weakref (gpointer data,
|
||||||
|
GObject *location)
|
||||||
{
|
{
|
||||||
GList *iter;
|
ShellAppRef *ref = data;
|
||||||
GSList *tmp = NULL;
|
|
||||||
|
|
||||||
for (iter = items; iter; iter = iter->next)
|
g_hash_table_remove (ref->appsys->priv->app_id_to_app, shell_app_info_get_id (ref->info));
|
||||||
{
|
shell_app_info_unref (ref->info);
|
||||||
const char *str = iter->data;
|
g_free (ref);
|
||||||
GConfValue *strval = gconf_value_new (GCONF_VALUE_STRING);
|
|
||||||
gconf_value_set_string (strval, str);
|
|
||||||
tmp = g_slist_prepend (tmp, strval);
|
|
||||||
}
|
|
||||||
tmp = g_slist_reverse (tmp);
|
|
||||||
|
|
||||||
gconf_value_set_list (val, tmp);
|
|
||||||
g_slist_free (tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
shell_app_system_add_favorite (ShellAppSystem *system, const char *id)
|
|
||||||
{
|
|
||||||
GConfClient *client = gconf_client_get_default ();
|
|
||||||
GConfValue *val;
|
|
||||||
GList *iter;
|
|
||||||
|
|
||||||
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
|
|
||||||
if (iter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
val = gconf_value_new (GCONF_VALUE_LIST);
|
|
||||||
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
|
|
||||||
|
|
||||||
system->priv->cached_favorites = g_list_append (system->priv->cached_favorites, g_strdup (id));
|
|
||||||
|
|
||||||
set_gconf_value_string_list (val, system->priv->cached_favorites);
|
|
||||||
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
|
|
||||||
{
|
|
||||||
GConfClient *client = gconf_client_get_default ();
|
|
||||||
GConfValue *val;
|
|
||||||
GList *iter;
|
|
||||||
|
|
||||||
iter = g_list_find_custom (system->priv->cached_favorites, id, (GCompareFunc)strcmp);
|
|
||||||
if (!iter)
|
|
||||||
return;
|
|
||||||
g_free (iter->data);
|
|
||||||
system->priv->cached_favorites = g_list_delete_link (system->priv->cached_favorites, iter);
|
|
||||||
|
|
||||||
val = gconf_value_new (GCONF_VALUE_LIST);
|
|
||||||
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
|
|
||||||
|
|
||||||
set_gconf_value_string_list (val, system->priv->cached_favorites);
|
|
||||||
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_app_system_lookup_app:
|
* shell_app_system_get_app:
|
||||||
*
|
*
|
||||||
* Return value: (transfer full): The #ShellAppInfo for id, or %NULL if none
|
* Find or create a #ShellApp corresponding to an id; if already cached
|
||||||
|
* elsewhere in memory, return that instance. Otherwise, create a new
|
||||||
|
* one.
|
||||||
|
*
|
||||||
|
* Return value: (transfer full): The #ShellApp for id, or %NULL if none
|
||||||
*/
|
*/
|
||||||
ShellAppInfo *
|
ShellApp *
|
||||||
shell_app_system_lookup_cached_app (ShellAppSystem *self, const char *id)
|
shell_app_system_get_app (ShellAppSystem *self,
|
||||||
|
const char *id)
|
||||||
{
|
{
|
||||||
ShellAppInfo *info;
|
ShellAppInfo *info;
|
||||||
|
ShellApp *app;
|
||||||
|
|
||||||
info = g_hash_table_lookup (self->priv->app_id_to_app, id);
|
app = g_hash_table_lookup (self->priv->app_id_to_app, id);
|
||||||
if (info)
|
if (app)
|
||||||
shell_app_info_ref (info);
|
return g_object_ref (app);
|
||||||
return info;
|
|
||||||
|
info = g_hash_table_lookup (self->priv->app_id_to_info, id);
|
||||||
|
if (!info)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
app = _shell_app_new (info);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ShellAppSystem ensures we have a unique instance of
|
||||||
|
* apps per id.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_shell_app_system_register_app (ShellAppSystem *self,
|
||||||
|
ShellApp *app)
|
||||||
|
{
|
||||||
|
const char *id;
|
||||||
|
ShellAppRef *ref;
|
||||||
|
|
||||||
|
id = shell_app_get_id (app);
|
||||||
|
|
||||||
|
g_return_if_fail (g_hash_table_lookup (self->priv->app_id_to_app, id) == NULL);
|
||||||
|
|
||||||
|
ref = g_new0 (ShellAppRef, 1);
|
||||||
|
ref->appsys = self;
|
||||||
|
ref->info = shell_app_info_ref (_shell_app_get_info (app));
|
||||||
|
g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (ref->info), app);
|
||||||
|
g_object_weak_ref (G_OBJECT (app), shell_app_system_on_app_weakref, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellAppInfo *
|
ShellAppInfo *
|
||||||
@ -753,16 +565,16 @@ shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
|
|||||||
* heuristically determined application identifier
|
* heuristically determined application identifier
|
||||||
* string, or %NULL if none.
|
* string, or %NULL if none.
|
||||||
*
|
*
|
||||||
* Returns: (transfer full): A #ShellAppInfo for name
|
* Returns: (transfer full): A #ShellApp for name
|
||||||
*/
|
*/
|
||||||
ShellAppInfo *
|
ShellApp *
|
||||||
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
ShellAppInfo *result;
|
ShellApp *result;
|
||||||
char **vendor_prefixes;
|
char **vendor_prefixes;
|
||||||
|
|
||||||
result = shell_app_system_lookup_cached_app (system, name);
|
result = shell_app_system_get_app (system, name);
|
||||||
if (result != NULL)
|
if (result != NULL)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -770,7 +582,7 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
|||||||
*vendor_prefixes; vendor_prefixes++)
|
*vendor_prefixes; vendor_prefixes++)
|
||||||
{
|
{
|
||||||
char *tmpid = g_strjoin (NULL, *vendor_prefixes, "-", name, NULL);
|
char *tmpid = g_strjoin (NULL, *vendor_prefixes, "-", name, NULL);
|
||||||
result = shell_app_system_lookup_cached_app (system, tmpid);
|
result = shell_app_system_get_app (system, tmpid);
|
||||||
g_free (tmpid);
|
g_free (tmpid);
|
||||||
if (result != NULL)
|
if (result != NULL)
|
||||||
return result;
|
return result;
|
||||||
@ -779,6 +591,253 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MATCH_NONE,
|
||||||
|
MATCH_MULTIPLE, /* Matches multiple terms */
|
||||||
|
MATCH_PREFIX, /* Strict prefix */
|
||||||
|
MATCH_SUBSTRING /* Not prefix, substring */
|
||||||
|
} ShellAppInfoSearchMatch;
|
||||||
|
|
||||||
|
static char *
|
||||||
|
normalize_and_casefold (const char *str)
|
||||||
|
{
|
||||||
|
char *normalized, *result;
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
|
||||||
|
result = g_utf8_casefold (normalized, -1);
|
||||||
|
g_free (normalized);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_info_init_search_data (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const char *comment;
|
||||||
|
|
||||||
|
g_assert (info->type == SHELL_APP_INFO_TYPE_ENTRY);
|
||||||
|
|
||||||
|
name = gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry);
|
||||||
|
info->casefolded_name = normalize_and_casefold (name);
|
||||||
|
|
||||||
|
comment = gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry);
|
||||||
|
info->casefolded_description = normalize_and_casefold (comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShellAppInfoSearchMatch
|
||||||
|
shell_app_info_match_terms (ShellAppInfo *info,
|
||||||
|
GSList *terms)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
ShellAppInfoSearchMatch match;
|
||||||
|
|
||||||
|
if (G_UNLIKELY(!info->casefolded_name))
|
||||||
|
shell_app_info_init_search_data (info);
|
||||||
|
|
||||||
|
match = MATCH_NONE;
|
||||||
|
for (iter = terms; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
const char *term = iter->data;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
p = strstr (info->casefolded_name, term);
|
||||||
|
if (p == info->casefolded_name)
|
||||||
|
{
|
||||||
|
if (match != MATCH_NONE)
|
||||||
|
return MATCH_MULTIPLE;
|
||||||
|
else
|
||||||
|
match = MATCH_PREFIX;
|
||||||
|
}
|
||||||
|
else if (p != NULL)
|
||||||
|
match = MATCH_SUBSTRING;
|
||||||
|
|
||||||
|
if (!info->casefolded_description)
|
||||||
|
continue;
|
||||||
|
p = strstr (info->casefolded_description, term);
|
||||||
|
if (p != NULL)
|
||||||
|
match = MATCH_SUBSTRING;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
shell_app_info_compare (gconstpointer a,
|
||||||
|
gconstpointer b,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
ShellAppSystem *system = data;
|
||||||
|
const char *id_a = a;
|
||||||
|
const char *id_b = b;
|
||||||
|
ShellAppInfo *info_a = g_hash_table_lookup (system->priv->app_id_to_info, id_a);
|
||||||
|
ShellAppInfo *info_b = g_hash_table_lookup (system->priv->app_id_to_info, id_b);
|
||||||
|
|
||||||
|
if (!info_a->name_collation_key)
|
||||||
|
info_a->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_a->entry), -1);
|
||||||
|
if (!info_b->name_collation_key)
|
||||||
|
info_b->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_b->entry), -1);
|
||||||
|
|
||||||
|
return strcmp (info_a->name_collation_key, info_b->name_collation_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *
|
||||||
|
sort_and_concat_results (ShellAppSystem *system,
|
||||||
|
GSList *multiple_matches,
|
||||||
|
GSList *prefix_matches,
|
||||||
|
GSList *substring_matches)
|
||||||
|
{
|
||||||
|
multiple_matches = g_slist_sort_with_data (multiple_matches, shell_app_info_compare, system);
|
||||||
|
prefix_matches = g_slist_sort_with_data (prefix_matches, shell_app_info_compare, system);
|
||||||
|
substring_matches = g_slist_sort_with_data (substring_matches, shell_app_info_compare, system);
|
||||||
|
return g_slist_concat (multiple_matches, g_slist_concat (prefix_matches, substring_matches));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* normalize_terms:
|
||||||
|
* @terms: (element-type utf8): Input search terms
|
||||||
|
*
|
||||||
|
* Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
|
||||||
|
*/
|
||||||
|
static GSList *
|
||||||
|
normalize_terms (GSList *terms)
|
||||||
|
{
|
||||||
|
GSList *normalized_terms = NULL;
|
||||||
|
GSList *iter;
|
||||||
|
for (iter = terms; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
const char *term = iter->data;
|
||||||
|
normalized_terms = g_slist_prepend (normalized_terms, normalize_and_casefold (term));
|
||||||
|
}
|
||||||
|
return normalized_terms;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
shell_app_system_do_match (ShellAppSystem *system,
|
||||||
|
ShellAppInfo *info,
|
||||||
|
GSList *terms,
|
||||||
|
GSList **multiple_results,
|
||||||
|
GSList **prefix_results,
|
||||||
|
GSList **substring_results)
|
||||||
|
{
|
||||||
|
const char *id = shell_app_info_get_id (info);
|
||||||
|
ShellAppInfoSearchMatch match;
|
||||||
|
|
||||||
|
if (shell_app_info_get_is_nodisplay (info))
|
||||||
|
return;
|
||||||
|
|
||||||
|
match = shell_app_info_match_terms (info, terms);
|
||||||
|
switch (match)
|
||||||
|
{
|
||||||
|
case MATCH_NONE:
|
||||||
|
break;
|
||||||
|
case MATCH_MULTIPLE:
|
||||||
|
*multiple_results = g_slist_prepend (*multiple_results, (char *) id);
|
||||||
|
break;
|
||||||
|
case MATCH_PREFIX:
|
||||||
|
*prefix_results = g_slist_prepend (*prefix_results, (char *) id);
|
||||||
|
break;
|
||||||
|
case MATCH_SUBSTRING:
|
||||||
|
*substring_results = g_slist_prepend (*substring_results, (char *) id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *
|
||||||
|
shell_app_system_initial_search_internal (ShellAppSystem *self,
|
||||||
|
GSList *terms,
|
||||||
|
GSList *source)
|
||||||
|
{
|
||||||
|
GSList *multiple_results = NULL;
|
||||||
|
GSList *prefix_results = NULL;
|
||||||
|
GSList *substring_results = NULL;
|
||||||
|
GSList *iter;
|
||||||
|
GSList *normalized_terms = normalize_terms (terms);
|
||||||
|
|
||||||
|
for (iter = source; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
ShellAppInfo *info = iter->data;
|
||||||
|
|
||||||
|
shell_app_system_do_match (self, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
|
||||||
|
}
|
||||||
|
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
|
||||||
|
g_slist_free (normalized_terms);
|
||||||
|
|
||||||
|
return sort_and_concat_results (self, multiple_results, prefix_results, substring_results);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_system_initial_search:
|
||||||
|
* @self: A #ShellAppSystem
|
||||||
|
* @prefs: %TRUE iff we should search preferences instead of apps
|
||||||
|
* @terms: (element-type utf8): List of terms, logical OR
|
||||||
|
*
|
||||||
|
* Search through applications for the given search terms. Note that returned
|
||||||
|
* strings are only valid until a return to the main loop.
|
||||||
|
*
|
||||||
|
* Returns: (transfer container) (element-type utf8): List of application identifiers
|
||||||
|
*/
|
||||||
|
GSList *
|
||||||
|
shell_app_system_initial_search (ShellAppSystem *self,
|
||||||
|
gboolean prefs,
|
||||||
|
GSList *terms)
|
||||||
|
{
|
||||||
|
return shell_app_system_initial_search_internal (self, terms,
|
||||||
|
prefs ? self->priv->cached_settings : self->priv->cached_flattened_apps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_system_subsearch:
|
||||||
|
* @self: A #ShellAppSystem
|
||||||
|
* @prefs: %TRUE iff we should search preferences instead of apps
|
||||||
|
* @previous_results: (element-type utf8): List of previous results
|
||||||
|
* @terms: (element-type utf8): List of terms, logical OR
|
||||||
|
*
|
||||||
|
* Search through a previous result set; for more information, see
|
||||||
|
* js/ui/search.js. Note the value of @prefs must be
|
||||||
|
* the same as passed to shell_app_system_initial_search(). Note that returned
|
||||||
|
* strings are only valid until a return to the main loop.
|
||||||
|
*
|
||||||
|
* Returns: (transfer container) (element-type utf8): List of application identifiers
|
||||||
|
*/
|
||||||
|
GSList *
|
||||||
|
shell_app_system_subsearch (ShellAppSystem *system,
|
||||||
|
gboolean prefs,
|
||||||
|
GSList *previous_results,
|
||||||
|
GSList *terms)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
GSList *multiple_results = NULL;
|
||||||
|
GSList *prefix_results = NULL;
|
||||||
|
GSList *substring_results = NULL;
|
||||||
|
GSList *normalized_terms = normalize_terms (terms);
|
||||||
|
|
||||||
|
/* Note prefs is deliberately ignored; both apps and prefs are in app_id_to_app,
|
||||||
|
* but we have the parameter for consistency and in case in the future
|
||||||
|
* they're not in the same data structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (iter = previous_results; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
const char *id = iter->data;
|
||||||
|
ShellAppInfo *info;
|
||||||
|
|
||||||
|
info = g_hash_table_lookup (system->priv->app_id_to_info, id);
|
||||||
|
if (!info)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
shell_app_system_do_match (system, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
|
||||||
|
}
|
||||||
|
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
|
||||||
|
g_slist_free (normalized_terms);
|
||||||
|
|
||||||
|
/* Note that a shorter term might have matched as a prefix, but
|
||||||
|
when extended only as a substring, so we have to redo the
|
||||||
|
sort rather than reusing the existing ordering */
|
||||||
|
return sort_and_concat_results (system, multiple_results, prefix_results, substring_results);
|
||||||
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
shell_app_info_get_id (ShellAppInfo *info)
|
shell_app_info_get_id (ShellAppInfo *info)
|
||||||
{
|
{
|
||||||
@ -810,6 +869,8 @@ shell_app_info_get_name (ShellAppInfo *info)
|
|||||||
{
|
{
|
||||||
char *title;
|
char *title;
|
||||||
g_object_get (info->window, "title", &title, NULL);
|
g_object_get (info->window, "title", &title, NULL);
|
||||||
|
if (!title)
|
||||||
|
title = g_strdup ("");
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
#ifndef __SHELL_APP_SYSTEM_H__
|
#ifndef __SHELL_APP_SYSTEM_H__
|
||||||
#define __SHELL_APP_SYSTEM_H__
|
#define __SHELL_APP_SYSTEM_H__
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <clutter/clutter.h>
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include "shell-app.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
|
#define SHELL_TYPE_APP_SYSTEM (shell_app_system_get_type ())
|
||||||
@ -35,18 +37,6 @@ struct _ShellAppSystemClass
|
|||||||
GType shell_app_system_get_type (void) G_GNUC_CONST;
|
GType shell_app_system_get_type (void) G_GNUC_CONST;
|
||||||
ShellAppSystem* shell_app_system_get_default(void);
|
ShellAppSystem* shell_app_system_get_default(void);
|
||||||
|
|
||||||
GSList *shell_app_system_get_applications_for_menu (ShellAppSystem *system, const char *menu);
|
|
||||||
|
|
||||||
typedef struct _ShellAppMenuEntry ShellAppMenuEntry;
|
|
||||||
|
|
||||||
struct _ShellAppMenuEntry {
|
|
||||||
char *name;
|
|
||||||
char *id;
|
|
||||||
char *icon;
|
|
||||||
};
|
|
||||||
|
|
||||||
GType shell_app_menu_entry_get_type (void);
|
|
||||||
|
|
||||||
typedef struct _ShellAppInfo ShellAppInfo;
|
typedef struct _ShellAppInfo ShellAppInfo;
|
||||||
|
|
||||||
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
|
#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
|
||||||
@ -75,20 +65,25 @@ gboolean shell_app_info_launch (ShellAppInfo *info,
|
|||||||
|
|
||||||
ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
|
ShellAppInfo *shell_app_system_load_from_desktop_file (ShellAppSystem *system, const char *filename, GError **error);
|
||||||
|
|
||||||
ShellAppInfo *shell_app_system_lookup_cached_app (ShellAppSystem *system, const char *id);
|
ShellApp *shell_app_system_get_app (ShellAppSystem *system, const char *id);
|
||||||
|
|
||||||
ShellAppInfo *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
|
void _shell_app_system_register_app (ShellAppSystem *self, ShellApp *app);
|
||||||
|
|
||||||
|
ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *system, const char *id);
|
||||||
|
|
||||||
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window);
|
ShellAppInfo *shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window);
|
||||||
|
|
||||||
GSList *shell_app_system_get_menus (ShellAppSystem *system);
|
GSList *shell_app_system_get_flattened_apps (ShellAppSystem *system);
|
||||||
|
|
||||||
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
|
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
|
||||||
|
|
||||||
GList *shell_app_system_get_favorites (ShellAppSystem *system);
|
GSList *shell_app_system_initial_search (ShellAppSystem *system,
|
||||||
|
gboolean prefs,
|
||||||
|
GSList *terms);
|
||||||
|
|
||||||
void shell_app_system_add_favorite (ShellAppSystem *system, const char *id);
|
GSList *shell_app_system_subsearch (ShellAppSystem *system,
|
||||||
|
gboolean prefs,
|
||||||
void shell_app_system_remove_favorite (ShellAppSystem *system, const char *id);
|
GSList *previous_results,
|
||||||
|
GSList *terms);
|
||||||
|
|
||||||
#endif /* __SHELL_APP_SYSTEM_H__ */
|
#endif /* __SHELL_APP_SYSTEM_H__ */
|
||||||
|
973
src/shell-app-usage.c
Normal file
@ -0,0 +1,973 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <gdk/gdkx.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <gconf/gconf.h>
|
||||||
|
#include <gconf/gconf-client.h>
|
||||||
|
#include <dbus/dbus-glib.h>
|
||||||
|
|
||||||
|
#include "shell-app-usage.h"
|
||||||
|
#include "shell-window-tracker.h"
|
||||||
|
#include "shell-global.h"
|
||||||
|
#include "shell-marshal.h"
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
|
#include "window.h"
|
||||||
|
#include "group.h"
|
||||||
|
|
||||||
|
/* This file includes modified code from
|
||||||
|
* desktop-data-engine/engine-dbus/hippo-application-monitor.c
|
||||||
|
* in the functions collecting application usage data.
|
||||||
|
* Written by Owen Taylor, originally licensed under LGPL 2.1.
|
||||||
|
* Copyright Red Hat, Inc. 2006-2008
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:shell-app-usage
|
||||||
|
* @short_description: Track application usage/state data
|
||||||
|
*
|
||||||
|
* This class maintains some usage and state statistics for
|
||||||
|
* applications by keeping track of the approximate time an application's
|
||||||
|
* windows are focused, as well as the last workspace it was seen on.
|
||||||
|
* This time tracking is implemented by watching for focus notifications,
|
||||||
|
* and computing a time delta between them. Also we watch the
|
||||||
|
* GNOME Session "StatusChanged" signal which by default is emitted after 5
|
||||||
|
* minutes to signify idle.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define APP_MONITOR_GCONF_DIR SHELL_GCONF_DIR"/app_monitor"
|
||||||
|
#define ENABLE_MONITORING_KEY APP_MONITOR_GCONF_DIR"/enable_monitoring"
|
||||||
|
|
||||||
|
#define FOCUS_TIME_MIN_SECONDS 7 /* Need 7 continuous seconds of focus */
|
||||||
|
|
||||||
|
#define USAGE_CLEAN_DAYS 7 /* If after 7 days we haven't seen an app, purge it */
|
||||||
|
|
||||||
|
/* Data is saved to file SHELL_CONFIG_DIR/DATA_FILENAME */
|
||||||
|
#define DATA_FILENAME "application_state"
|
||||||
|
|
||||||
|
#define IDLE_TIME_TRANSITION_SECONDS 30 /* If we transition to idle, only count
|
||||||
|
* this many seconds of usage */
|
||||||
|
|
||||||
|
/* The ranking algorithm we use is: every time an app score reaches SCORE_MAX,
|
||||||
|
* divide all scores by 2. Scores are raised by 1 unit every SAVE_APPS_TIMEOUT
|
||||||
|
* seconds. This mechanism allows the list to update relatively fast when
|
||||||
|
* a new app is used intensively.
|
||||||
|
* To keep the list clean, and avoid being Big Brother, apps that have not been
|
||||||
|
* seen for a week and whose score is below SCORE_MIN are removed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* How often we save internally app data, in seconds */
|
||||||
|
#define SAVE_APPS_TIMEOUT_SECONDS (5 * 60)
|
||||||
|
|
||||||
|
/* With this value, an app goes from bottom to top of the
|
||||||
|
* usage list in 50 hours of use */
|
||||||
|
#define SCORE_MAX (3600 * 50 / FOCUS_TIME_MIN_SECONDS)
|
||||||
|
|
||||||
|
/* If an app's score in lower than this and the app has not been used in a week,
|
||||||
|
* remove it */
|
||||||
|
#define SCORE_MIN (SCORE_MAX >> 3)
|
||||||
|
|
||||||
|
/* http://www.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Presence */
|
||||||
|
#define GNOME_SESSION_STATUS_IDLE 3
|
||||||
|
|
||||||
|
typedef struct UsageData UsageData;
|
||||||
|
|
||||||
|
struct _ShellAppUsage
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
GFile *configfile;
|
||||||
|
DBusGProxy *session_proxy;
|
||||||
|
GdkDisplay *display;
|
||||||
|
GConfClient *gconf_client;
|
||||||
|
gulong last_idle;
|
||||||
|
guint idle_focus_change_id;
|
||||||
|
guint save_id;
|
||||||
|
guint gconf_notify;
|
||||||
|
gboolean currently_idle;
|
||||||
|
gboolean enable_monitoring;
|
||||||
|
|
||||||
|
GSList *previously_running;
|
||||||
|
|
||||||
|
long watch_start_time;
|
||||||
|
ShellApp *watched_app;
|
||||||
|
|
||||||
|
/* <char *context, GHashTable<char *appid, UsageData *usage>> */
|
||||||
|
GHashTable *app_usages_for_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
/* Represents an application record for a given context */
|
||||||
|
struct UsageData
|
||||||
|
{
|
||||||
|
/* Whether the application we're tracking is "transient", see
|
||||||
|
* shell_app_info_is_transient.
|
||||||
|
*/
|
||||||
|
gboolean transient;
|
||||||
|
|
||||||
|
gdouble score; /* Based on the number of times we'e seen the app and normalized */
|
||||||
|
long last_seen; /* Used to clear old apps we've only seen a few times */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void shell_app_usage_finalize (GObject *object);
|
||||||
|
|
||||||
|
static void on_session_status_changed (DBusGProxy *proxy, guint status, ShellAppUsage *self);
|
||||||
|
static void on_focus_app_changed (ShellWindowTracker *tracker, GParamSpec *spec, ShellAppUsage *self);
|
||||||
|
static void ensure_queued_save (ShellAppUsage *self);
|
||||||
|
static UsageData * get_app_usage_for_context_and_id (ShellAppUsage *self,
|
||||||
|
const char *context,
|
||||||
|
const char *appid);
|
||||||
|
|
||||||
|
static gboolean idle_save_application_usage (gpointer data);
|
||||||
|
|
||||||
|
static void restore_from_file (ShellAppUsage *self);
|
||||||
|
|
||||||
|
static void update_enable_monitoring (ShellAppUsage *self);
|
||||||
|
|
||||||
|
static void on_enable_monitoring_key_changed (GConfClient *client,
|
||||||
|
guint connexion_id,
|
||||||
|
GConfEntry *entry,
|
||||||
|
gpointer self);
|
||||||
|
|
||||||
|
static long
|
||||||
|
get_time (void)
|
||||||
|
{
|
||||||
|
GTimeVal tv;
|
||||||
|
g_get_current_time (&tv);
|
||||||
|
return tv.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_class_init (ShellAppUsageClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = shell_app_usage_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GHashTable *
|
||||||
|
get_usages_for_context (ShellAppUsage *self,
|
||||||
|
const char *context)
|
||||||
|
{
|
||||||
|
GHashTable *context_usages;
|
||||||
|
|
||||||
|
context_usages = g_hash_table_lookup (self->app_usages_for_context, context);
|
||||||
|
if (context_usages == NULL)
|
||||||
|
{
|
||||||
|
context_usages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||||
|
g_hash_table_insert (self->app_usages_for_context, g_strdup (context),
|
||||||
|
context_usages);
|
||||||
|
}
|
||||||
|
return context_usages;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UsageData *
|
||||||
|
get_app_usage_for_context_and_id (ShellAppUsage *self,
|
||||||
|
const char *context,
|
||||||
|
const char *appid)
|
||||||
|
{
|
||||||
|
UsageData *usage;
|
||||||
|
GHashTable *context_usages;
|
||||||
|
|
||||||
|
context_usages = get_usages_for_context (self, context);
|
||||||
|
|
||||||
|
usage = g_hash_table_lookup (context_usages, appid);
|
||||||
|
if (usage)
|
||||||
|
return usage;
|
||||||
|
|
||||||
|
usage = g_new0 (UsageData, 1);
|
||||||
|
g_hash_table_insert (context_usages, g_strdup (appid), usage);
|
||||||
|
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UsageData *
|
||||||
|
get_usage_for_app (ShellAppUsage *self,
|
||||||
|
ShellApp *app)
|
||||||
|
{
|
||||||
|
const char *context;
|
||||||
|
|
||||||
|
context = _shell_window_tracker_get_app_context (shell_window_tracker_get_default (), app);
|
||||||
|
|
||||||
|
return get_app_usage_for_context_and_id (self, context, shell_app_get_id (app));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gboolean in_context;
|
||||||
|
GHashTableIter context_iter;
|
||||||
|
const char *context_id;
|
||||||
|
GHashTableIter usage_iter;
|
||||||
|
} UsageIterator;
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage_iterator_init (ShellAppUsage *self,
|
||||||
|
UsageIterator *iter)
|
||||||
|
{
|
||||||
|
iter->in_context = FALSE;
|
||||||
|
g_hash_table_iter_init (&(iter->context_iter), self->app_usages_for_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
usage_iterator_next (ShellAppUsage *self,
|
||||||
|
UsageIterator *iter,
|
||||||
|
const char **context,
|
||||||
|
const char **id,
|
||||||
|
UsageData **usage)
|
||||||
|
{
|
||||||
|
gpointer key, value;
|
||||||
|
gboolean next_context;
|
||||||
|
|
||||||
|
if (!iter->in_context)
|
||||||
|
next_context = TRUE;
|
||||||
|
else if (!g_hash_table_iter_next (&(iter->usage_iter), &key, &value))
|
||||||
|
next_context = TRUE;
|
||||||
|
else
|
||||||
|
next_context = FALSE;
|
||||||
|
|
||||||
|
while (next_context)
|
||||||
|
{
|
||||||
|
GHashTable *app_usages;
|
||||||
|
|
||||||
|
if (!g_hash_table_iter_next (&(iter->context_iter), &key, &value))
|
||||||
|
return FALSE;
|
||||||
|
iter->in_context = TRUE;
|
||||||
|
iter->context_id = key;
|
||||||
|
app_usages = value;
|
||||||
|
g_hash_table_iter_init (&(iter->usage_iter), app_usages);
|
||||||
|
|
||||||
|
next_context = !g_hash_table_iter_next (&(iter->usage_iter), &key, &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
*context = iter->context_id;
|
||||||
|
*id = key;
|
||||||
|
*usage = value;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage_iterator_remove (ShellAppUsage *self,
|
||||||
|
UsageIterator *iter)
|
||||||
|
{
|
||||||
|
g_assert (iter->in_context);
|
||||||
|
|
||||||
|
g_hash_table_iter_remove (&(iter->usage_iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Limit the score to a certain level so that most used apps can change */
|
||||||
|
static void
|
||||||
|
normalize_usage (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
UsageIterator iter;
|
||||||
|
const char *context;
|
||||||
|
const char *id;
|
||||||
|
UsageData *usage;
|
||||||
|
|
||||||
|
usage_iterator_init (self, &iter);
|
||||||
|
|
||||||
|
while (usage_iterator_next (self, &iter, &context, &id, &usage))
|
||||||
|
{
|
||||||
|
usage->score /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
increment_usage_for_app_at_time (ShellAppUsage *self,
|
||||||
|
ShellApp *app,
|
||||||
|
long time)
|
||||||
|
{
|
||||||
|
UsageData *usage;
|
||||||
|
guint elapsed;
|
||||||
|
guint usage_count;
|
||||||
|
|
||||||
|
usage = get_usage_for_app (self, app);
|
||||||
|
|
||||||
|
usage->last_seen = time;
|
||||||
|
|
||||||
|
elapsed = time - self->watch_start_time;
|
||||||
|
usage_count = elapsed / FOCUS_TIME_MIN_SECONDS;
|
||||||
|
if (usage_count > 0)
|
||||||
|
{
|
||||||
|
usage->score += usage_count;
|
||||||
|
if (usage->score > SCORE_MAX)
|
||||||
|
normalize_usage (self);
|
||||||
|
ensure_queued_save (self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
increment_usage_for_app (ShellAppUsage *self,
|
||||||
|
ShellApp *app)
|
||||||
|
{
|
||||||
|
long curtime = get_time ();
|
||||||
|
increment_usage_for_app_at_time (self, app, curtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_app_running_changed (ShellWindowTracker *tracker,
|
||||||
|
ShellApp *app,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellAppUsage *self = SHELL_APP_USAGE (user_data);
|
||||||
|
UsageData *usage;
|
||||||
|
gboolean running;
|
||||||
|
|
||||||
|
if (shell_app_is_transient (app))
|
||||||
|
return;
|
||||||
|
|
||||||
|
usage = get_usage_for_app (self, app);
|
||||||
|
|
||||||
|
running = shell_app_get_n_windows (app) > 0;
|
||||||
|
|
||||||
|
usage->last_seen = get_time ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_focus_app_changed (ShellWindowTracker *tracker,
|
||||||
|
GParamSpec *spec,
|
||||||
|
ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
if (self->watched_app != NULL)
|
||||||
|
increment_usage_for_app (self, self->watched_app);
|
||||||
|
|
||||||
|
if (self->watched_app)
|
||||||
|
g_object_unref (self->watched_app);
|
||||||
|
|
||||||
|
g_object_get (tracker, "focus-app", &(self->watched_app), NULL);
|
||||||
|
self->watch_start_time = get_time ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_session_status_changed (DBusGProxy *proxy,
|
||||||
|
guint status,
|
||||||
|
ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
gboolean idle;
|
||||||
|
|
||||||
|
idle = (status >= GNOME_SESSION_STATUS_IDLE);
|
||||||
|
if (self->currently_idle == idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->currently_idle = idle;
|
||||||
|
if (idle)
|
||||||
|
{
|
||||||
|
long end_time;
|
||||||
|
|
||||||
|
/* The GNOME Session signal we watch is 5 minutes, but that's a long
|
||||||
|
* time for this purpose. Instead, just add a base 30 seconds.
|
||||||
|
*/
|
||||||
|
if (self->watched_app)
|
||||||
|
{
|
||||||
|
end_time = self->watch_start_time + IDLE_TIME_TRANSITION_SECONDS;
|
||||||
|
increment_usage_for_app_at_time (self, self->watched_app, end_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Transitioning to !idle, reset the start time */
|
||||||
|
self->watch_start_time = get_time ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_init (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
char *shell_config_dir, *path;
|
||||||
|
DBusGConnection *session_bus;
|
||||||
|
ShellWindowTracker *tracker;
|
||||||
|
|
||||||
|
self->app_usages_for_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
|
||||||
|
|
||||||
|
tracker = shell_window_tracker_get_default ();
|
||||||
|
g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);
|
||||||
|
g_signal_connect (tracker, "app-running-changed", G_CALLBACK (on_app_running_changed), self);
|
||||||
|
|
||||||
|
session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
|
||||||
|
self->session_proxy = dbus_g_proxy_new_for_name (session_bus, "org.gnome.SessionManager",
|
||||||
|
"/org/gnome/SessionManager/Presence",
|
||||||
|
"org.gnome.SessionManager");
|
||||||
|
dbus_g_proxy_add_signal (self->session_proxy, "StatusChanged",
|
||||||
|
G_TYPE_UINT, G_TYPE_INVALID, G_TYPE_INVALID);
|
||||||
|
dbus_g_proxy_connect_signal (self->session_proxy, "StatusChanged",
|
||||||
|
G_CALLBACK (on_session_status_changed), self, NULL);
|
||||||
|
|
||||||
|
self->last_idle = 0;
|
||||||
|
self->currently_idle = FALSE;
|
||||||
|
self->enable_monitoring = FALSE;
|
||||||
|
|
||||||
|
g_object_get (shell_global_get(), "configdir", &shell_config_dir, NULL),
|
||||||
|
path = g_build_filename (shell_config_dir, DATA_FILENAME, NULL);
|
||||||
|
g_free (shell_config_dir);
|
||||||
|
self->configfile = g_file_new_for_path (path);
|
||||||
|
g_free (path);
|
||||||
|
restore_from_file (self);
|
||||||
|
|
||||||
|
self->gconf_client = gconf_client_get_default ();
|
||||||
|
gconf_client_add_dir (self->gconf_client, APP_MONITOR_GCONF_DIR,
|
||||||
|
GCONF_CLIENT_PRELOAD_NONE, NULL);
|
||||||
|
self->gconf_notify =
|
||||||
|
gconf_client_notify_add (self->gconf_client, ENABLE_MONITORING_KEY,
|
||||||
|
on_enable_monitoring_key_changed, self, NULL, NULL);
|
||||||
|
update_enable_monitoring (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
ShellAppUsage *self = SHELL_APP_USAGE (object);
|
||||||
|
|
||||||
|
if (self->save_id > 0)
|
||||||
|
g_source_remove (self->save_id);
|
||||||
|
gconf_client_notify_remove (self->gconf_client, self->gconf_notify);
|
||||||
|
g_object_unref (self->gconf_client);
|
||||||
|
|
||||||
|
g_object_unref (self->configfile);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (shell_app_usage_parent_class)->finalize(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ShellAppUsage *usage;
|
||||||
|
GHashTable *context_usages;
|
||||||
|
} SortAppsByUsageData;
|
||||||
|
|
||||||
|
static int
|
||||||
|
sort_apps_by_usage (gconstpointer a,
|
||||||
|
gconstpointer b,
|
||||||
|
gpointer datap)
|
||||||
|
{
|
||||||
|
SortAppsByUsageData *data = datap;
|
||||||
|
ShellApp *app_a, *app_b;
|
||||||
|
UsageData *usage_a, *usage_b;
|
||||||
|
|
||||||
|
app_a = (ShellApp*)a;
|
||||||
|
app_b = (ShellApp*)b;
|
||||||
|
|
||||||
|
usage_a = g_hash_table_lookup (data->context_usages, shell_app_get_id (app_a));
|
||||||
|
usage_b = g_hash_table_lookup (data->context_usages, shell_app_get_id (app_b));
|
||||||
|
|
||||||
|
return usage_b->score - usage_a->score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_usage_get_most_used:
|
||||||
|
* @usage: the usage instance to request
|
||||||
|
* @context: Activity identifier
|
||||||
|
* @max_count: how many applications are requested. Note that the actual
|
||||||
|
* list size may be less, or NULL if not enough applications are registered.
|
||||||
|
*
|
||||||
|
* Get a list of most popular applications for a given context.
|
||||||
|
*
|
||||||
|
* Returns: (element-type ShellApp) (transfer full): List of applications
|
||||||
|
*/
|
||||||
|
GSList *
|
||||||
|
shell_app_usage_get_most_used (ShellAppUsage *self,
|
||||||
|
const char *context,
|
||||||
|
gint max_count)
|
||||||
|
{
|
||||||
|
GSList *apps;
|
||||||
|
GList *appids, *iter;
|
||||||
|
GHashTable *usages;
|
||||||
|
ShellAppSystem *appsys;
|
||||||
|
SortAppsByUsageData data;
|
||||||
|
|
||||||
|
usages = g_hash_table_lookup (self->app_usages_for_context, context);
|
||||||
|
if (usages == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
appsys = shell_app_system_get_default ();
|
||||||
|
|
||||||
|
appids = g_hash_table_get_keys (usages);
|
||||||
|
apps = NULL;
|
||||||
|
for (iter = appids; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
const char *appid = iter->data;
|
||||||
|
ShellApp *app;
|
||||||
|
|
||||||
|
app = shell_app_system_get_app (appsys, appid);
|
||||||
|
if (!app)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
apps = g_slist_prepend (apps, g_object_ref (app));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (appids);
|
||||||
|
|
||||||
|
data.usage = self;
|
||||||
|
data.context_usages = usages;
|
||||||
|
apps = g_slist_sort_with_data (apps, sort_apps_by_usage, &data);
|
||||||
|
|
||||||
|
return apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ensure_queued_save (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
if (self->save_id != 0)
|
||||||
|
return;
|
||||||
|
self->save_id = g_timeout_add_seconds (SAVE_APPS_TIMEOUT_SECONDS, idle_save_application_usage, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to sort highest scores at the top */
|
||||||
|
static gint
|
||||||
|
usage_sort_apps (gconstpointer data1,
|
||||||
|
gconstpointer data2)
|
||||||
|
{
|
||||||
|
const UsageData *u1 = data1;
|
||||||
|
const UsageData *u2 = data2;
|
||||||
|
|
||||||
|
if (u1->score > u2->score)
|
||||||
|
return -1;
|
||||||
|
else if (u1->score == u2->score)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up apps we see rarely.
|
||||||
|
* The logic behind this is that if an app was seen less than SCORE_MIN times
|
||||||
|
* and not seen for a week, it can probably be forgotten about.
|
||||||
|
* This should much reduce the size of the list and avoid 'pollution'. */
|
||||||
|
static gboolean
|
||||||
|
idle_clean_usage (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
UsageIterator iter;
|
||||||
|
const char *context;
|
||||||
|
const char *id;
|
||||||
|
UsageData *usage;
|
||||||
|
long current_time;
|
||||||
|
long week_ago;
|
||||||
|
|
||||||
|
current_time = get_time ();
|
||||||
|
week_ago = current_time - (7 * 24 * 60 * 60);
|
||||||
|
|
||||||
|
usage_iterator_init (self, &iter);
|
||||||
|
|
||||||
|
while (usage_iterator_next (self, &iter, &context, &id, &usage))
|
||||||
|
{
|
||||||
|
if ((usage->score < SCORE_MIN) &&
|
||||||
|
(usage->last_seen < week_ago))
|
||||||
|
usage_iterator_remove (self, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
write_escaped (GDataOutputStream *stream,
|
||||||
|
const char *str,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gboolean ret;
|
||||||
|
char *quoted = g_markup_escape_text (str, -1);
|
||||||
|
ret = g_data_output_stream_put_string (stream, quoted, NULL, error);
|
||||||
|
g_free (quoted);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
write_attribute_string (GDataOutputStream *stream,
|
||||||
|
const char *elt_name,
|
||||||
|
const char *str,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
char *elt;
|
||||||
|
|
||||||
|
elt = g_strdup_printf (" %s=\"", elt_name);
|
||||||
|
ret = g_data_output_stream_put_string (stream, elt, NULL, error);
|
||||||
|
g_free (elt);
|
||||||
|
if (!ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = write_escaped (stream, str, error);
|
||||||
|
if (!ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = g_data_output_stream_put_string (stream, "\"", NULL, error);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
write_attribute_uint (GDataOutputStream *stream,
|
||||||
|
const char *elt_name,
|
||||||
|
guint value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gboolean ret;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = g_strdup_printf ("%u", value);
|
||||||
|
ret = write_attribute_string (stream, elt_name, buf, error);
|
||||||
|
g_free (buf);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
write_attribute_double (GDataOutputStream *stream,
|
||||||
|
const char *elt_name,
|
||||||
|
double value,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
g_ascii_dtostr (buf, sizeof (buf), value);
|
||||||
|
ret = write_attribute_string (stream, elt_name, buf, error);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save app data lists to file */
|
||||||
|
static gboolean
|
||||||
|
idle_save_application_usage (gpointer data)
|
||||||
|
{
|
||||||
|
ShellAppUsage *self = SHELL_APP_USAGE (data);
|
||||||
|
UsageIterator iter;
|
||||||
|
const char *current_context;
|
||||||
|
const char *context;
|
||||||
|
const char *id;
|
||||||
|
UsageData *usage;
|
||||||
|
GFileOutputStream *output;
|
||||||
|
GOutputStream *buffered_output;
|
||||||
|
GDataOutputStream *data_output;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
self->save_id = 0;
|
||||||
|
|
||||||
|
/* Parent directory is already created by shell-global */
|
||||||
|
output = g_file_replace (self->configfile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
|
||||||
|
if (!output)
|
||||||
|
{
|
||||||
|
g_debug ("Could not save applications usage data: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
buffered_output = g_buffered_output_stream_new (G_OUTPUT_STREAM (output));
|
||||||
|
g_object_unref (output);
|
||||||
|
data_output = g_data_output_stream_new (G_OUTPUT_STREAM (buffered_output));
|
||||||
|
g_object_unref (buffered_output);
|
||||||
|
|
||||||
|
if (!g_data_output_stream_put_string (data_output, "<?xml version=\"1.0\"?>\n<application-state>\n", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
usage_iterator_init (self, &iter);
|
||||||
|
|
||||||
|
current_context = NULL;
|
||||||
|
while (usage_iterator_next (self, &iter, &context, &id, &usage))
|
||||||
|
{
|
||||||
|
ShellApp *app;
|
||||||
|
|
||||||
|
app = shell_app_system_get_app (shell_app_system_get_default(), id);
|
||||||
|
|
||||||
|
if (!app)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (context != current_context)
|
||||||
|
{
|
||||||
|
if (current_context != NULL)
|
||||||
|
{
|
||||||
|
if (!g_data_output_stream_put_string (data_output, " </context>", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
current_context = context;
|
||||||
|
if (!g_data_output_stream_put_string (data_output, " <context", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
if (!write_attribute_string (data_output, "id", context, &error))
|
||||||
|
goto out;
|
||||||
|
if (!g_data_output_stream_put_string (data_output, ">\n", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!g_data_output_stream_put_string (data_output, " <application", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
if (!write_attribute_string (data_output, "id", id, &error))
|
||||||
|
goto out;
|
||||||
|
if (!write_attribute_uint (data_output, "open-window-count", shell_app_get_n_windows (app), &error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!write_attribute_double (data_output, "score", usage->score, &error))
|
||||||
|
goto out;
|
||||||
|
if (!write_attribute_uint (data_output, "last-seen", usage->last_seen, &error))
|
||||||
|
goto out;
|
||||||
|
if (!g_data_output_stream_put_string (data_output, "/>\n", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (current_context != NULL)
|
||||||
|
{
|
||||||
|
if (!g_data_output_stream_put_string (data_output, " </context>\n", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!g_data_output_stream_put_string (data_output, "</application-state>\n", NULL, &error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!error)
|
||||||
|
g_output_stream_close_async (G_OUTPUT_STREAM (data_output), 0, NULL, NULL, NULL);
|
||||||
|
g_object_unref (data_output);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_debug ("Could not save applications usage data: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ShellAppUsage *self;
|
||||||
|
char *context;
|
||||||
|
} ParseData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_start_element_handler (GMarkupParseContext *context,
|
||||||
|
const gchar *element_name,
|
||||||
|
const gchar **attribute_names,
|
||||||
|
const gchar **attribute_values,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
ParseData *data = user_data;
|
||||||
|
|
||||||
|
if (strcmp (element_name, "application-state") == 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (strcmp (element_name, "context") == 0)
|
||||||
|
{
|
||||||
|
char *context = NULL;
|
||||||
|
const char **attribute;
|
||||||
|
const char **value;
|
||||||
|
|
||||||
|
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
|
||||||
|
{
|
||||||
|
if (strcmp (*attribute, "id") == 0)
|
||||||
|
context = g_strdup (*value);
|
||||||
|
}
|
||||||
|
if (context < 0)
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_MARKUP_ERROR,
|
||||||
|
G_MARKUP_ERROR_PARSE,
|
||||||
|
"Missing attribute id on <%s> element",
|
||||||
|
element_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->context = context;
|
||||||
|
}
|
||||||
|
else if (strcmp (element_name, "application") == 0)
|
||||||
|
{
|
||||||
|
const char **attribute;
|
||||||
|
const char **value;
|
||||||
|
UsageData *usage;
|
||||||
|
char *appid = NULL;
|
||||||
|
GHashTable *usage_table;
|
||||||
|
|
||||||
|
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
|
||||||
|
{
|
||||||
|
if (strcmp (*attribute, "id") == 0)
|
||||||
|
appid = g_strdup (*value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appid)
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_MARKUP_ERROR,
|
||||||
|
G_MARKUP_ERROR_PARSE,
|
||||||
|
"Missing attribute id on <%s> element",
|
||||||
|
element_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage_table = get_usages_for_context (data->self, data->context);
|
||||||
|
|
||||||
|
usage = g_new0 (UsageData, 1);
|
||||||
|
g_hash_table_insert (usage_table, appid, usage);
|
||||||
|
|
||||||
|
for (attribute = attribute_names, value = attribute_values; *attribute; attribute++, value++)
|
||||||
|
{
|
||||||
|
if (strcmp (*attribute, "open-window-count") == 0)
|
||||||
|
{
|
||||||
|
guint count = strtoul (*value, NULL, 10);
|
||||||
|
if (count > 0)
|
||||||
|
data->self->previously_running = g_slist_prepend (data->self->previously_running,
|
||||||
|
usage);
|
||||||
|
}
|
||||||
|
else if (strcmp (*attribute, "score") == 0)
|
||||||
|
{
|
||||||
|
usage->score = g_ascii_strtod (*value, NULL);
|
||||||
|
}
|
||||||
|
else if (strcmp (*attribute, "last-seen") == 0)
|
||||||
|
{
|
||||||
|
usage->last_seen = (guint) g_ascii_strtoull (*value, NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_MARKUP_ERROR,
|
||||||
|
G_MARKUP_ERROR_PARSE,
|
||||||
|
"Unknown element <%s>",
|
||||||
|
element_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_end_element_handler (GMarkupParseContext *context,
|
||||||
|
const gchar *element_name,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
ParseData *data = user_data;
|
||||||
|
|
||||||
|
if (strcmp (element_name, "context") == 0)
|
||||||
|
{
|
||||||
|
g_free (data->context);
|
||||||
|
data->context = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_usage_text_handler (GMarkupParseContext *context,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
/* do nothing, very very fast */
|
||||||
|
}
|
||||||
|
|
||||||
|
static GMarkupParser app_state_parse_funcs =
|
||||||
|
{
|
||||||
|
shell_app_usage_start_element_handler,
|
||||||
|
shell_app_usage_end_element_handler,
|
||||||
|
shell_app_usage_text_handler,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Load data about apps usage from file */
|
||||||
|
static void
|
||||||
|
restore_from_file (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
GFileInputStream *input;
|
||||||
|
ParseData parse_data;
|
||||||
|
GMarkupParseContext *parse_context;
|
||||||
|
GError *error = NULL;
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
input = g_file_read (self->configfile, NULL, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
if (error->code != G_IO_ERROR_NOT_FOUND)
|
||||||
|
g_warning ("Could not load applications usage data: %s", error->message);
|
||||||
|
|
||||||
|
g_error_free (error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (&parse_data, 0, sizeof (ParseData));
|
||||||
|
parse_data.self = self;
|
||||||
|
parse_data.context = NULL;
|
||||||
|
parse_context = g_markup_parse_context_new (&app_state_parse_funcs, 0, &parse_data, NULL);
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
gssize count = g_input_stream_read ((GInputStream*) input, buf, sizeof(buf), NULL, &error);
|
||||||
|
if (count <= 0)
|
||||||
|
goto out;
|
||||||
|
if (!g_markup_parse_context_parse (parse_context, buf, count, &error))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
g_free (parse_data.context);
|
||||||
|
g_markup_parse_context_free (parse_context);
|
||||||
|
g_input_stream_close ((GInputStream*)input, NULL, NULL);
|
||||||
|
g_object_unref (input);
|
||||||
|
|
||||||
|
idle_clean_usage (self);
|
||||||
|
self->previously_running = g_slist_sort (self->previously_running, usage_sort_apps);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
g_warning ("Could not load applications usage data: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable or disable the timers, depending on the value of ENABLE_MONITORING_KEY
|
||||||
|
* and taking care of the previous state. If selfing is disabled, we still
|
||||||
|
* report apps usage based on (possibly) saved data, but don't collect data.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
update_enable_monitoring (ShellAppUsage *self)
|
||||||
|
{
|
||||||
|
GConfValue *value;
|
||||||
|
gboolean enable;
|
||||||
|
|
||||||
|
value = gconf_client_get (self->gconf_client, ENABLE_MONITORING_KEY, NULL);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
enable = gconf_value_get_bool (value);
|
||||||
|
gconf_value_free (value);
|
||||||
|
}
|
||||||
|
else /* Schema is not present, set default value by hand to avoid getting FALSE */
|
||||||
|
enable = TRUE;
|
||||||
|
|
||||||
|
/* Be sure not to start the timers if they were already set */
|
||||||
|
if (enable && !self->enable_monitoring)
|
||||||
|
{
|
||||||
|
on_focus_app_changed (shell_window_tracker_get_default (), NULL, self);
|
||||||
|
}
|
||||||
|
/* ...and don't try to stop them if they were not running */
|
||||||
|
else if (!enable && self->enable_monitoring)
|
||||||
|
{
|
||||||
|
if (self->watched_app)
|
||||||
|
g_object_unref (self->watched_app);
|
||||||
|
self->watched_app = NULL;
|
||||||
|
if (self->save_id)
|
||||||
|
{
|
||||||
|
g_source_remove (self->save_id);
|
||||||
|
self->save_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->enable_monitoring = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called when the ENABLE_MONITORING_KEY boolean has changed */
|
||||||
|
static void
|
||||||
|
on_enable_monitoring_key_changed (GConfClient *client,
|
||||||
|
guint connexion_id,
|
||||||
|
GConfEntry *entry,
|
||||||
|
gpointer self)
|
||||||
|
{
|
||||||
|
update_enable_monitoring ((ShellAppUsage *) self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_usage_get_default:
|
||||||
|
*
|
||||||
|
* Return Value: (transfer none): The global #ShellAppUsage instance
|
||||||
|
*/
|
||||||
|
ShellAppUsage *
|
||||||
|
shell_app_usage_get_default ()
|
||||||
|
{
|
||||||
|
static ShellAppUsage *instance;
|
||||||
|
|
||||||
|
if (instance == NULL)
|
||||||
|
instance = g_object_new (SHELL_TYPE_APP_USAGE, NULL);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
36
src/shell-app-usage.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifndef __SHELL_APP_USAGE_H__
|
||||||
|
#define __SHELL_APP_USAGE_H__
|
||||||
|
|
||||||
|
#include "shell-app.h"
|
||||||
|
#include "shell-window-tracker.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _ShellAppUsage ShellAppUsage;
|
||||||
|
typedef struct _ShellAppUsageClass ShellAppUsageClass;
|
||||||
|
typedef struct _ShellAppUsagePrivate ShellAppUsagePrivate;
|
||||||
|
|
||||||
|
#define SHELL_TYPE_APP_USAGE (shell_app_usage_get_type ())
|
||||||
|
#define SHELL_APP_USAGE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP_USAGE, ShellAppUsage))
|
||||||
|
#define SHELL_APP_USAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP_USAGE, ShellAppUsageClass))
|
||||||
|
#define SHELL_IS_APP_USAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP_USAGE))
|
||||||
|
#define SHELL_IS_APP_USAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP_USAGE))
|
||||||
|
#define SHELL_APP_USAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP_USAGE, ShellAppUsageClass))
|
||||||
|
|
||||||
|
struct _ShellAppUsageClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType shell_app_usage_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
ShellAppUsage* shell_app_usage_get_default(void);
|
||||||
|
|
||||||
|
GSList *shell_app_usage_get_most_used (ShellAppUsage *monitor,
|
||||||
|
const char *context,
|
||||||
|
gint number);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SHELL_APP_USAGE_H__ */
|
379
src/shell-app.c
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "shell-app-private.h"
|
||||||
|
#include "shell-global.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:shell-app
|
||||||
|
* @short_description: Object representing an application
|
||||||
|
*
|
||||||
|
* This object wraps a #ShellAppInfo, providing methods and signals
|
||||||
|
* primarily useful for running applications.
|
||||||
|
*/
|
||||||
|
struct _ShellApp
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
ShellAppInfo *info;
|
||||||
|
|
||||||
|
guint workspace_switch_id;
|
||||||
|
|
||||||
|
gboolean window_sort_stale;
|
||||||
|
GSList *windows;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
WINDOWS_CHANGED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
const char *
|
||||||
|
shell_app_get_id (ShellApp *app)
|
||||||
|
{
|
||||||
|
return shell_app_info_get_id (app->info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_create_icon_texture:
|
||||||
|
*
|
||||||
|
* Look up the icon for this application, and create a #ClutterTexture
|
||||||
|
* for it at the given size.
|
||||||
|
*
|
||||||
|
* Return value: (transfer none): A floating #ClutterActor
|
||||||
|
*/
|
||||||
|
ClutterActor *
|
||||||
|
shell_app_create_icon_texture (ShellApp *app,
|
||||||
|
float size)
|
||||||
|
{
|
||||||
|
return shell_app_info_create_icon_texture (app->info, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
shell_app_get_name (ShellApp *app)
|
||||||
|
{
|
||||||
|
return shell_app_info_get_name (app->info);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
shell_app_get_description (ShellApp *app)
|
||||||
|
{
|
||||||
|
return shell_app_info_get_description (app->info);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_app_is_transient (ShellApp *app)
|
||||||
|
{
|
||||||
|
return shell_app_info_is_transient (app->info);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_app_launch (ShellApp *self,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return shell_app_info_launch (self->info, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _shell_app_get_info:
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): Associated app info
|
||||||
|
*/
|
||||||
|
ShellAppInfo *
|
||||||
|
_shell_app_get_info (ShellApp *app)
|
||||||
|
{
|
||||||
|
return app->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ShellApp *app;
|
||||||
|
MetaWorkspace *active_workspace;
|
||||||
|
} CompareWindowsData;
|
||||||
|
|
||||||
|
static int
|
||||||
|
shell_app_compare_windows (gconstpointer a,
|
||||||
|
gconstpointer b,
|
||||||
|
gpointer datap)
|
||||||
|
{
|
||||||
|
MetaWindow *win_a = (gpointer)a;
|
||||||
|
MetaWindow *win_b = (gpointer)b;
|
||||||
|
CompareWindowsData *data = datap;
|
||||||
|
gboolean ws_a, ws_b;
|
||||||
|
gboolean vis_a, vis_b;
|
||||||
|
|
||||||
|
ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
|
||||||
|
ws_b = meta_window_get_workspace (win_b) == data->active_workspace;
|
||||||
|
|
||||||
|
if (ws_a && !ws_b)
|
||||||
|
return -1;
|
||||||
|
else if (!ws_a && ws_b)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
vis_a = meta_window_showing_on_its_workspace (win_a);
|
||||||
|
vis_b = meta_window_showing_on_its_workspace (win_b);
|
||||||
|
|
||||||
|
if (vis_a && !vis_b)
|
||||||
|
return -1;
|
||||||
|
else if (!vis_a && vis_b)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_get_windows:
|
||||||
|
* @app:
|
||||||
|
*
|
||||||
|
* Get the toplevel, interesting windows which are associated with this
|
||||||
|
* application. The returned list will be sorted first by whether
|
||||||
|
* they're on the active workspace, then by whether they're visible,
|
||||||
|
* and finally by the time the user last interacted with them.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (element-type MetaWindow): List of windows
|
||||||
|
*/
|
||||||
|
GSList *
|
||||||
|
shell_app_get_windows (ShellApp *app)
|
||||||
|
{
|
||||||
|
if (app->window_sort_stale)
|
||||||
|
{
|
||||||
|
CompareWindowsData data;
|
||||||
|
data.app = app;
|
||||||
|
data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ()));
|
||||||
|
app->windows = g_slist_sort_with_data (app->windows, shell_app_compare_windows, &data);
|
||||||
|
app->window_sort_stale = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return app->windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint
|
||||||
|
shell_app_get_n_windows (ShellApp *app)
|
||||||
|
{
|
||||||
|
return g_slist_length (app->windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
shell_app_has_visible_windows (ShellApp *app)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
for (iter = app->windows; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
MetaWindow *window = iter->data;
|
||||||
|
|
||||||
|
if (meta_window_showing_on_its_workspace (window))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_app_is_on_workspace (ShellApp *app,
|
||||||
|
MetaWorkspace *workspace)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
|
||||||
|
for (iter = app->windows; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
if (meta_window_get_workspace (iter->data) == workspace)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_app_compare:
|
||||||
|
* @app:
|
||||||
|
* @other: A #ShellApp
|
||||||
|
*
|
||||||
|
* Compare one #ShellApp instance to another, in the following way:
|
||||||
|
* - If one of them has visible windows and the other does not, the one
|
||||||
|
* with visible windows is first.
|
||||||
|
* - If one has no windows at all (i.e. it's not running) and the other
|
||||||
|
* does, the one with windows is first.
|
||||||
|
* - Finally, the application which the user interacted with most recently
|
||||||
|
* compares earlier.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
shell_app_compare (ShellApp *app,
|
||||||
|
ShellApp *other)
|
||||||
|
{
|
||||||
|
gboolean vis_app, vis_other;
|
||||||
|
GSList *windows_app, *windows_other;
|
||||||
|
|
||||||
|
vis_app = shell_app_has_visible_windows (app);
|
||||||
|
vis_other = shell_app_has_visible_windows (other);
|
||||||
|
|
||||||
|
if (vis_app && !vis_other)
|
||||||
|
return -1;
|
||||||
|
else if (!vis_app && vis_other)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (app->windows && !other->windows)
|
||||||
|
return -1;
|
||||||
|
else if (!app->windows && other->windows)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
windows_app = shell_app_get_windows (app);
|
||||||
|
windows_other = shell_app_get_windows (other);
|
||||||
|
|
||||||
|
return meta_window_get_user_time (windows_other->data) - meta_window_get_user_time (windows_app->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellApp *
|
||||||
|
_shell_app_new_for_window (MetaWindow *window)
|
||||||
|
{
|
||||||
|
ShellApp *app;
|
||||||
|
|
||||||
|
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||||
|
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
|
||||||
|
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
||||||
|
_shell_app_add_window (app, window);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellApp *
|
||||||
|
_shell_app_new (ShellAppInfo *info)
|
||||||
|
{
|
||||||
|
ShellApp *app;
|
||||||
|
|
||||||
|
app = g_object_new (SHELL_TYPE_APP, NULL);
|
||||||
|
app->info = shell_app_info_ref (info);
|
||||||
|
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_on_unmanaged (MetaWindow *window,
|
||||||
|
ShellApp *app)
|
||||||
|
{
|
||||||
|
_shell_app_remove_window (app, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_on_user_time_changed (MetaWindow *window,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
ShellApp *app)
|
||||||
|
{
|
||||||
|
/* Ideally we don't want to emit windows-changed if the sort order
|
||||||
|
* isn't actually changing. This check catches most of those.
|
||||||
|
*/
|
||||||
|
if (window != app->windows->data)
|
||||||
|
{
|
||||||
|
app->window_sort_stale = TRUE;
|
||||||
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_on_ws_switch (MetaScreen *screen,
|
||||||
|
int from,
|
||||||
|
int to,
|
||||||
|
MetaMotionDirection direction,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
ShellApp *self = SHELL_APP (data);
|
||||||
|
self->window_sort_stale = TRUE;
|
||||||
|
g_signal_emit (self, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_shell_app_add_window (ShellApp *app,
|
||||||
|
MetaWindow *window)
|
||||||
|
{
|
||||||
|
if (g_slist_find (app->windows, window))
|
||||||
|
return;
|
||||||
|
|
||||||
|
app->windows = g_slist_prepend (app->windows, g_object_ref (window));
|
||||||
|
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
|
||||||
|
g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
|
||||||
|
app->window_sort_stale = TRUE;
|
||||||
|
|
||||||
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
|
|
||||||
|
if (app->workspace_switch_id == 0)
|
||||||
|
{
|
||||||
|
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
|
||||||
|
|
||||||
|
app->workspace_switch_id =
|
||||||
|
g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
disconnect_workspace_switch (ShellApp *app)
|
||||||
|
{
|
||||||
|
MetaScreen *screen;
|
||||||
|
|
||||||
|
if (app->workspace_switch_id == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
screen = shell_global_get_screen (shell_global_get ());
|
||||||
|
g_signal_handler_disconnect (screen, app->workspace_switch_id);
|
||||||
|
app->workspace_switch_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_shell_app_remove_window (ShellApp *app,
|
||||||
|
MetaWindow *window)
|
||||||
|
{
|
||||||
|
if (!g_slist_find (app->windows, window))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app);
|
||||||
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
|
||||||
|
g_object_unref (window);
|
||||||
|
app->windows = g_slist_remove (app->windows, window);
|
||||||
|
|
||||||
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
|
|
||||||
|
if (app->windows == NULL)
|
||||||
|
disconnect_workspace_switch (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_init (ShellApp *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
ShellApp *app = SHELL_APP (object);
|
||||||
|
|
||||||
|
if (app->info)
|
||||||
|
{
|
||||||
|
shell_app_info_unref (app->info);
|
||||||
|
app->info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (app->windows)
|
||||||
|
_shell_app_remove_window (app, app->windows->data);
|
||||||
|
|
||||||
|
disconnect_workspace_switch (app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_app_class_init(ShellAppClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->dispose = shell_app_dispose;
|
||||||
|
|
||||||
|
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
|
||||||
|
SHELL_TYPE_APP,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0,
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
}
|
48
src/shell-app.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifndef __SHELL_APP_H__
|
||||||
|
#define __SHELL_APP_H__
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef struct _ShellApp ShellApp;
|
||||||
|
typedef struct _ShellAppClass ShellAppClass;
|
||||||
|
typedef struct _ShellAppPrivate ShellAppPrivate;
|
||||||
|
|
||||||
|
#define SHELL_TYPE_APP (shell_app_get_type ())
|
||||||
|
#define SHELL_APP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_APP, ShellApp))
|
||||||
|
#define SHELL_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_APP, ShellAppClass))
|
||||||
|
#define SHELL_IS_APP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_APP))
|
||||||
|
#define SHELL_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_APP))
|
||||||
|
#define SHELL_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_APP, ShellAppClass))
|
||||||
|
|
||||||
|
struct _ShellAppClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
GType shell_app_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
const char *shell_app_get_id (ShellApp *app);
|
||||||
|
|
||||||
|
ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
|
||||||
|
char *shell_app_get_name (ShellApp *app);
|
||||||
|
char *shell_app_get_description (ShellApp *app);
|
||||||
|
gboolean shell_app_is_transient (ShellApp *app);
|
||||||
|
gboolean shell_app_launch (ShellApp *info, GError **error);
|
||||||
|
|
||||||
|
guint shell_app_get_n_windows (ShellApp *app);
|
||||||
|
|
||||||
|
GSList *shell_app_get_windows (ShellApp *app);
|
||||||
|
|
||||||
|
gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
|
||||||
|
|
||||||
|
int shell_app_compare (ShellApp *app, ShellApp *other);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __SHELL_APP_H__ */
|
@ -1,5 +1,7 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "shell-arrow.h"
|
#include "shell-arrow.h"
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
#include <clutter/clutter.h>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
#ifndef __SHELL_ARROW_H__
|
#ifndef __SHELL_ARROW_H__
|
||||||
#define __SHELL_ARROW_H__
|
#define __SHELL_ARROW_H__
|
||||||
|
|
||||||
|
@ -1,317 +0,0 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:shell-button-box
|
|
||||||
* @short_description: A box with properties useful for implementing buttons
|
|
||||||
*
|
|
||||||
* A #BigBox subclass which translates lower-level Clutter button events
|
|
||||||
* into higher level properties which are useful for implementing "button-like"
|
|
||||||
* actors.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shell-button-box.h"
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(ShellButtonBox, shell_button_box, BIG_TYPE_BOX);
|
|
||||||
|
|
||||||
struct _ShellButtonBoxPrivate {
|
|
||||||
gboolean active;
|
|
||||||
gboolean held;
|
|
||||||
gboolean hover;
|
|
||||||
gboolean pressed;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Signals */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
ACTIVATE,
|
|
||||||
LAST_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
PROP_0,
|
|
||||||
|
|
||||||
PROP_ACTIVE,
|
|
||||||
PROP_HOVER,
|
|
||||||
PROP_PRESSED,
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint shell_button_box_signals [LAST_SIGNAL] = { 0 };
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_active (ShellButtonBox *box,
|
|
||||||
gboolean active)
|
|
||||||
{
|
|
||||||
if (box->priv->active == active)
|
|
||||||
return;
|
|
||||||
box->priv->active = active;
|
|
||||||
g_object_notify (G_OBJECT (box), "active");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_hover (ShellButtonBox *box,
|
|
||||||
gboolean hover)
|
|
||||||
{
|
|
||||||
if (box->priv->hover == hover)
|
|
||||||
return;
|
|
||||||
box->priv->hover = hover;
|
|
||||||
g_object_notify (G_OBJECT (box), "hover");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_pressed (ShellButtonBox *box,
|
|
||||||
gboolean pressed)
|
|
||||||
{
|
|
||||||
if (box->priv->pressed == pressed)
|
|
||||||
return;
|
|
||||||
box->priv->pressed = pressed;
|
|
||||||
g_object_notify (G_OBJECT (box), "pressed");
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
shell_button_box_contains (ShellButtonBox *box,
|
|
||||||
ClutterActor *actor)
|
|
||||||
{
|
|
||||||
while (actor != NULL && actor != (ClutterActor*)box)
|
|
||||||
{
|
|
||||||
actor = clutter_actor_get_parent (actor);
|
|
||||||
}
|
|
||||||
return actor != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
shell_button_box_enter_event (ClutterActor *actor,
|
|
||||||
ClutterCrossingEvent *event)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
|
|
||||||
|
|
||||||
if (shell_button_box_contains (box, event->related))
|
|
||||||
return TRUE;
|
|
||||||
if (!shell_button_box_contains (box, event->source))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
g_object_freeze_notify (G_OBJECT (actor));
|
|
||||||
|
|
||||||
if (box->priv->held)
|
|
||||||
set_pressed (box, TRUE);
|
|
||||||
set_hover (box, TRUE);
|
|
||||||
|
|
||||||
g_object_thaw_notify (G_OBJECT (actor));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
shell_button_box_leave_event (ClutterActor *actor,
|
|
||||||
ClutterCrossingEvent *event)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
|
|
||||||
|
|
||||||
if (shell_button_box_contains (box, event->related))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
set_hover (box, FALSE);
|
|
||||||
set_pressed (box, FALSE);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
shell_button_box_button_press_event (ClutterActor *actor,
|
|
||||||
ClutterButtonEvent *event)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
|
|
||||||
|
|
||||||
if (event->button != 1 || event->click_count != 1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (box->priv->held)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (!shell_button_box_contains (box, event->source))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
box->priv->held = TRUE;
|
|
||||||
clutter_grab_pointer (CLUTTER_ACTOR (box));
|
|
||||||
|
|
||||||
set_pressed (box, TRUE);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
shell_button_box_button_release_event (ClutterActor *actor,
|
|
||||||
ClutterButtonEvent *event)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (actor);
|
|
||||||
|
|
||||||
if (event->button != 1 || event->click_count != 1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!box->priv->held)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
box->priv->held = FALSE;
|
|
||||||
clutter_ungrab_pointer ();
|
|
||||||
|
|
||||||
if (!shell_button_box_contains (box, event->source))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
set_pressed (box, FALSE);
|
|
||||||
|
|
||||||
g_signal_emit (G_OBJECT (box), shell_button_box_signals[ACTIVATE], 0, event);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_button_box_fake_release:
|
|
||||||
* @box:
|
|
||||||
*
|
|
||||||
* If this button box is holding a pointer grab, this function will
|
|
||||||
* will ungrab it, and reset the pressed state. The effect is
|
|
||||||
* similar to if the user had released the mouse button, but without
|
|
||||||
* emitting the activate signal.
|
|
||||||
*
|
|
||||||
* This function is useful if for example you want to do something after the user
|
|
||||||
* is holding the mouse button for a given period of time, breaking the
|
|
||||||
* grab.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
shell_button_box_fake_release (ShellButtonBox *box)
|
|
||||||
{
|
|
||||||
if (!box->priv->held)
|
|
||||||
return;
|
|
||||||
|
|
||||||
box->priv->held = FALSE;
|
|
||||||
clutter_ungrab_pointer ();
|
|
||||||
|
|
||||||
set_pressed (box, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_button_box_set_property(GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_ACTIVE:
|
|
||||||
set_active (box, g_value_get_boolean (value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_button_box_get_property(GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
ShellButtonBox *box = SHELL_BUTTON_BOX (object);
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_ACTIVE:
|
|
||||||
g_value_set_boolean (value, box->priv->active);
|
|
||||||
break;
|
|
||||||
case PROP_PRESSED:
|
|
||||||
g_value_set_boolean (value, box->priv->pressed);
|
|
||||||
break;
|
|
||||||
case PROP_HOVER:
|
|
||||||
g_value_set_boolean (value, box->priv->hover);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_button_box_class_init (ShellButtonBoxClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
||||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
||||||
|
|
||||||
gobject_class->get_property = shell_button_box_get_property;
|
|
||||||
gobject_class->set_property = shell_button_box_set_property;
|
|
||||||
|
|
||||||
actor_class->enter_event = shell_button_box_enter_event;
|
|
||||||
actor_class->leave_event = shell_button_box_leave_event;
|
|
||||||
actor_class->button_press_event = shell_button_box_button_press_event;
|
|
||||||
actor_class->button_release_event = shell_button_box_button_release_event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShellButtonBox::activate
|
|
||||||
* @box: The #ShellButtonBox
|
|
||||||
* @event: Release event which triggered the activation
|
|
||||||
*
|
|
||||||
* This signal is emitted when the button should take the action
|
|
||||||
* associated with button click+release.
|
|
||||||
*/
|
|
||||||
shell_button_box_signals[ACTIVATE] =
|
|
||||||
g_signal_new ("activate",
|
|
||||||
G_TYPE_FROM_CLASS (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
0,
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE, 1, CLUTTER_TYPE_EVENT);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShellButtonBox:active
|
|
||||||
*
|
|
||||||
* The property allows the button to be used as a "toggle button"; it's up to the
|
|
||||||
* application to update the active property in response to the activate signal;
|
|
||||||
* it doesn't happen automatically.
|
|
||||||
*/
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_ACTIVE,
|
|
||||||
g_param_spec_boolean ("active",
|
|
||||||
"Active",
|
|
||||||
"Whether the button persistently active",
|
|
||||||
FALSE,
|
|
||||||
G_PARAM_READWRITE));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShellButtonBox:hover
|
|
||||||
*
|
|
||||||
* This property tracks whether the mouse is over the button; note this
|
|
||||||
* state is independent of whether the button is pressed.
|
|
||||||
*/
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_HOVER,
|
|
||||||
g_param_spec_boolean ("hover",
|
|
||||||
"Hovering state",
|
|
||||||
"Whether the mouse is over the button",
|
|
||||||
FALSE,
|
|
||||||
G_PARAM_READABLE));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ShellButtonBox:pressed
|
|
||||||
*
|
|
||||||
* This property tracks whether the button should have a "pressed in"
|
|
||||||
* effect.
|
|
||||||
*/
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_PRESSED,
|
|
||||||
g_param_spec_boolean ("pressed",
|
|
||||||
"Pressed state",
|
|
||||||
"Whether the button is currently pressed",
|
|
||||||
FALSE,
|
|
||||||
G_PARAM_READABLE));
|
|
||||||
|
|
||||||
g_type_class_add_private (gobject_class, sizeof (ShellButtonBoxPrivate));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_button_box_init (ShellButtonBox *self)
|
|
||||||
{
|
|
||||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_BUTTON_BOX,
|
|
||||||
ShellButtonBoxPrivate);
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
#ifndef __SHELL_BUTTON_BOX_H__
|
|
||||||
#define __SHELL_BUTTON_BOX_H__
|
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
|
||||||
#include "big/box.h"
|
|
||||||
|
|
||||||
#define SHELL_TYPE_BUTTON_BOX (shell_button_box_get_type ())
|
|
||||||
#define SHELL_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBox))
|
|
||||||
#define SHELL_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
|
|
||||||
#define SHELL_IS_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_BUTTON_BOX))
|
|
||||||
#define SHELL_IS_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_BUTTON_BOX))
|
|
||||||
#define SHELL_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_BUTTON_BOX, ShellButtonBoxClass))
|
|
||||||
|
|
||||||
typedef struct _ShellButtonBox ShellButtonBox;
|
|
||||||
typedef struct _ShellButtonBoxClass ShellButtonBoxClass;
|
|
||||||
|
|
||||||
typedef struct _ShellButtonBoxPrivate ShellButtonBoxPrivate;
|
|
||||||
|
|
||||||
struct _ShellButtonBox
|
|
||||||
{
|
|
||||||
BigBox parent;
|
|
||||||
|
|
||||||
ShellButtonBoxPrivate *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _ShellButtonBoxClass
|
|
||||||
{
|
|
||||||
BigBoxClass parent_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
GType shell_button_box_get_type (void) G_GNUC_CONST;
|
|
||||||
|
|
||||||
void shell_button_box_fake_release (ShellButtonBox *box);
|
|
||||||
|
|
||||||
#endif /* __SHELL_BUTTON_BOX_H__ */
|
|
367
src/shell-doc-system.c
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "shell-doc-system.h"
|
||||||
|
|
||||||
|
#include "shell-global.h"
|
||||||
|
#include "shell-texture-cache.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:shell-doc-system
|
||||||
|
* @short_description: Track recently used documents
|
||||||
|
*
|
||||||
|
* Wraps #GtkRecentManager, caching recently used document information, and adds
|
||||||
|
* APIs for asynchronous queries.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
CHANGED,
|
||||||
|
DELETED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
struct _ShellDocSystemPrivate {
|
||||||
|
GtkRecentManager *manager;
|
||||||
|
GHashTable *infos_by_uri;
|
||||||
|
GSList *infos_by_timestamp;
|
||||||
|
|
||||||
|
guint idle_recent_changed_id;
|
||||||
|
|
||||||
|
GHashTable *deleted_infos;
|
||||||
|
guint idle_emit_deleted_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(ShellDocSystem, shell_doc_system, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_doc_system_get_all:
|
||||||
|
* @self: A #ShellDocSystem
|
||||||
|
*
|
||||||
|
* Returns the currently cached set of recent files. Recent files are read initially
|
||||||
|
* from the underlying #GtkRecentManager, and updated when it changes.
|
||||||
|
* This function does not perform I/O.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (element-type GtkRecentInfo): Cached recent file infos
|
||||||
|
*/
|
||||||
|
GSList *
|
||||||
|
shell_doc_system_get_all (ShellDocSystem *self)
|
||||||
|
{
|
||||||
|
return self->priv->infos_by_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @self: A #ShellDocSystem
|
||||||
|
* @uri: Url
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): Recent file info corresponding to given @uri
|
||||||
|
*/
|
||||||
|
GtkRecentInfo *
|
||||||
|
shell_doc_system_lookup_by_uri (ShellDocSystem *self,
|
||||||
|
const char *uri)
|
||||||
|
{
|
||||||
|
return g_hash_table_lookup (self->priv->infos_by_uri, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
shell_doc_system_idle_emit_deleted (gpointer data)
|
||||||
|
{
|
||||||
|
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
|
||||||
|
self->priv->idle_emit_deleted_id = 0;
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, self->priv->deleted_infos);
|
||||||
|
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||||
|
{
|
||||||
|
GtkRecentInfo *info = key;
|
||||||
|
g_signal_emit (self, signals[DELETED], 0, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_emit (self, signals[CHANGED], 0);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ShellDocSystem *self;
|
||||||
|
GtkRecentInfo *info;
|
||||||
|
} ShellDocSystemRecentQueryData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_recent_file_query_result (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
ShellDocSystemRecentQueryData *data = user_data;
|
||||||
|
ShellDocSystem *self = data->self;
|
||||||
|
GError *error = NULL;
|
||||||
|
GFileInfo *fileinfo;
|
||||||
|
|
||||||
|
fileinfo = g_file_query_info_finish (G_FILE (source), result, &error);
|
||||||
|
if (fileinfo)
|
||||||
|
g_object_unref (fileinfo);
|
||||||
|
/* This is a strict error check; we don't want to cause recent files to
|
||||||
|
* vanish for anything potentially transient.
|
||||||
|
*/
|
||||||
|
if (error != NULL && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)
|
||||||
|
{
|
||||||
|
self->priv->infos_by_timestamp = g_slist_remove (self->priv->infos_by_timestamp, data->info);
|
||||||
|
g_hash_table_remove (self->priv->infos_by_uri, gtk_recent_info_get_uri (data->info));
|
||||||
|
|
||||||
|
g_hash_table_insert (self->priv->deleted_infos, gtk_recent_info_ref (data->info), NULL);
|
||||||
|
|
||||||
|
if (self->priv->idle_emit_deleted_id == 0)
|
||||||
|
self->priv->idle_emit_deleted_id = g_timeout_add (0, shell_doc_system_idle_emit_deleted, self);
|
||||||
|
}
|
||||||
|
g_clear_error (&error);
|
||||||
|
|
||||||
|
gtk_recent_info_unref (data->info);
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_doc_system_queue_existence_check:
|
||||||
|
* @self: A #ShellDocSystem
|
||||||
|
* @n_items: Count of items to check for existence, starting from most recent
|
||||||
|
*
|
||||||
|
* Asynchronously start a check of a number of recent file for existence;
|
||||||
|
* any deleted files will be emitted from the #ShellDocSystem::deleted
|
||||||
|
* signal. Note that this function ignores non-local files; they
|
||||||
|
* will simply always appear to exist (until they are removed from
|
||||||
|
* the recent file list manually).
|
||||||
|
*
|
||||||
|
* The intent of this function is to be called after a #ShellDocSystem::changed
|
||||||
|
* signal has been emitted, and a display has shown a subset of those files.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
shell_doc_system_queue_existence_check (ShellDocSystem *self,
|
||||||
|
guint n_items)
|
||||||
|
{
|
||||||
|
GSList *iter;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0, iter = self->priv->infos_by_timestamp; i < n_items && iter; i++, iter = iter->next)
|
||||||
|
{
|
||||||
|
GtkRecentInfo *info = iter->data;
|
||||||
|
const char *uri;
|
||||||
|
GFile *file;
|
||||||
|
ShellDocSystemRecentQueryData *data;
|
||||||
|
|
||||||
|
if (!gtk_recent_info_is_local (info))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
data = g_new0 (ShellDocSystemRecentQueryData, 1);
|
||||||
|
data->self = self;
|
||||||
|
data->info = gtk_recent_info_ref (info);
|
||||||
|
|
||||||
|
uri = gtk_recent_info_get_uri (info);
|
||||||
|
file = g_file_new_for_uri (uri);
|
||||||
|
|
||||||
|
g_file_query_info_async (file, "standard::type", G_FILE_QUERY_INFO_NONE,
|
||||||
|
G_PRIORITY_DEFAULT, NULL, on_recent_file_query_result, data);
|
||||||
|
g_object_unref (file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sort_infos_by_timestamp_descending (gconstpointer a,
|
||||||
|
gconstpointer b)
|
||||||
|
{
|
||||||
|
GtkRecentInfo *info_a = (GtkRecentInfo*)a;
|
||||||
|
GtkRecentInfo *info_b = (GtkRecentInfo*)b;
|
||||||
|
time_t modified_a, modified_b;
|
||||||
|
|
||||||
|
modified_a = gtk_recent_info_get_modified (info_a);
|
||||||
|
modified_b = gtk_recent_info_get_modified (info_b);
|
||||||
|
|
||||||
|
return modified_b - modified_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
idle_handle_recent_changed (gpointer data)
|
||||||
|
{
|
||||||
|
ShellDocSystem *self = SHELL_DOC_SYSTEM (data);
|
||||||
|
GList *items, *iter;
|
||||||
|
|
||||||
|
self->priv->idle_recent_changed_id = 0;
|
||||||
|
|
||||||
|
g_hash_table_remove_all (self->priv->deleted_infos);
|
||||||
|
g_hash_table_remove_all (self->priv->infos_by_uri);
|
||||||
|
g_slist_free (self->priv->infos_by_timestamp);
|
||||||
|
self->priv->infos_by_timestamp = NULL;
|
||||||
|
|
||||||
|
items = gtk_recent_manager_get_items (self->priv->manager);
|
||||||
|
for (iter = items; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
GtkRecentInfo *info = iter->data;
|
||||||
|
const char *uri = gtk_recent_info_get_uri (info);
|
||||||
|
|
||||||
|
/* uri is owned by the info */
|
||||||
|
g_hash_table_insert (self->priv->infos_by_uri, (char*) uri, info);
|
||||||
|
|
||||||
|
self->priv->infos_by_timestamp = g_slist_prepend (self->priv->infos_by_timestamp, info);
|
||||||
|
}
|
||||||
|
g_list_free (items);
|
||||||
|
|
||||||
|
self->priv->infos_by_timestamp = g_slist_sort (self->priv->infos_by_timestamp, sort_infos_by_timestamp_descending);
|
||||||
|
|
||||||
|
g_signal_emit (self, signals[CHANGED], 0);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_doc_system_on_recent_changed (GtkRecentManager *manager,
|
||||||
|
ShellDocSystem *self)
|
||||||
|
{
|
||||||
|
if (self->priv->idle_recent_changed_id != 0)
|
||||||
|
return;
|
||||||
|
self->priv->idle_recent_changed_id = g_timeout_add (0, idle_handle_recent_changed, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_doc_system_open:
|
||||||
|
* @system: A #ShellDocSystem
|
||||||
|
* @info: A #GtkRecentInfo
|
||||||
|
*
|
||||||
|
* Launch the default application associated with the mime type of
|
||||||
|
* @info, using its uri.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
shell_doc_system_open (ShellDocSystem *system,
|
||||||
|
GtkRecentInfo *info)
|
||||||
|
{
|
||||||
|
GFile *file;
|
||||||
|
GAppInfo *app_info;
|
||||||
|
gboolean needs_uri;
|
||||||
|
|
||||||
|
file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
|
||||||
|
needs_uri = g_file_get_path (file) == NULL;
|
||||||
|
g_object_unref (file);
|
||||||
|
|
||||||
|
app_info = g_app_info_get_default_for_type (gtk_recent_info_get_mime_type (info), needs_uri);
|
||||||
|
if (app_info != NULL)
|
||||||
|
{
|
||||||
|
GList *uris;
|
||||||
|
uris = g_list_prepend (NULL, (gpointer)gtk_recent_info_get_uri (info));
|
||||||
|
g_app_info_launch_uris (app_info, uris, shell_global_create_app_launch_context (shell_global_get ()), NULL);
|
||||||
|
g_list_free (uris);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *app_name;
|
||||||
|
#if GTK_MINOR_VERSION >= 18
|
||||||
|
const char *app_exec;
|
||||||
|
#else
|
||||||
|
char *app_exec;
|
||||||
|
#endif
|
||||||
|
char *app_exec_quoted;
|
||||||
|
guint count;
|
||||||
|
time_t time;
|
||||||
|
|
||||||
|
app_name = gtk_recent_info_last_application (info);
|
||||||
|
if (gtk_recent_info_get_application_info (info, app_name, &app_exec, &count, &time))
|
||||||
|
{
|
||||||
|
GRegex *regex;
|
||||||
|
GAppLaunchContext *context;
|
||||||
|
|
||||||
|
/* TODO: Change this once better support for creating
|
||||||
|
GAppInfo is added to GtkRecentInfo, as right now
|
||||||
|
this relies on the fact that the file uri is
|
||||||
|
already a part of appExec, so we don't supply any
|
||||||
|
files to app_info.launch().
|
||||||
|
|
||||||
|
The 'command line' passed to
|
||||||
|
create_from_command_line is allowed to contain
|
||||||
|
'%<something>' macros that are expanded to file
|
||||||
|
name / icon name, etc, so we need to escape % as %%
|
||||||
|
*/
|
||||||
|
|
||||||
|
regex = g_regex_new ("%", 0, 0, NULL);
|
||||||
|
app_exec_quoted = g_regex_replace (regex, app_exec, -1, 0, "%%", 0, NULL);
|
||||||
|
g_regex_unref (regex);
|
||||||
|
|
||||||
|
app_info = g_app_info_create_from_commandline (app_exec, NULL, 0, NULL);
|
||||||
|
|
||||||
|
/* The point of passing an app launch context to
|
||||||
|
launch() is mostly to get startup notification and
|
||||||
|
associated benefits like the app appearing on the
|
||||||
|
right desktop; but it doesn't really work for now
|
||||||
|
because with the way we create the appInfo we
|
||||||
|
aren't reading the application's desktop file, and
|
||||||
|
thus don't find the StartupNotify=true in it. So,
|
||||||
|
despite passing the app launch context, no startup
|
||||||
|
notification occurs.
|
||||||
|
*/
|
||||||
|
context = shell_global_create_app_launch_context (shell_global_get ());
|
||||||
|
g_app_info_launch (app_info, NULL, context, NULL);
|
||||||
|
g_object_unref (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (app_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_doc_system_class_init(ShellDocSystemClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = (GObjectClass *)klass;
|
||||||
|
|
||||||
|
signals[CHANGED] =
|
||||||
|
g_signal_new ("changed",
|
||||||
|
SHELL_TYPE_DOC_SYSTEM,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0,
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
|
||||||
|
signals[DELETED] =
|
||||||
|
g_signal_new ("deleted",
|
||||||
|
SHELL_TYPE_DOC_SYSTEM,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
0,
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__BOXED,
|
||||||
|
G_TYPE_NONE, 1, GTK_TYPE_RECENT_INFO);
|
||||||
|
|
||||||
|
g_type_class_add_private (gobject_class, sizeof (ShellDocSystemPrivate));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_doc_system_init (ShellDocSystem *self)
|
||||||
|
{
|
||||||
|
ShellDocSystemPrivate *priv;
|
||||||
|
|
||||||
|
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||||
|
SHELL_TYPE_DOC_SYSTEM,
|
||||||
|
ShellDocSystemPrivate);
|
||||||
|
self->priv->manager = gtk_recent_manager_get_default ();
|
||||||
|
|
||||||
|
self->priv->deleted_infos = g_hash_table_new_full (NULL, NULL, (GDestroyNotify)gtk_recent_info_unref, NULL);
|
||||||
|
self->priv->infos_by_uri = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)gtk_recent_info_unref);
|
||||||
|
|
||||||
|
g_signal_connect (self->priv->manager, "changed", G_CALLBACK(shell_doc_system_on_recent_changed), self);
|
||||||
|
shell_doc_system_on_recent_changed (self->priv->manager, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_doc_system_get_default:
|
||||||
|
*
|
||||||
|
* Return Value: (transfer none): The global #ShellDocSystem singleton
|
||||||
|
*/
|
||||||
|
ShellDocSystem *
|
||||||
|
shell_doc_system_get_default ()
|
||||||
|
{
|
||||||
|
static ShellDocSystem *instance = NULL;
|
||||||
|
|
||||||
|
if (instance == NULL)
|
||||||
|
instance = g_object_new (SHELL_TYPE_DOC_SYSTEM, NULL);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
46
src/shell-doc-system.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifndef __SHELL_DOC_SYSTEM_H__
|
||||||
|
#define __SHELL_DOC_SYSTEM_H__
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#define SHELL_TYPE_DOC_SYSTEM (shell_doc_system_get_type ())
|
||||||
|
#define SHELL_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystem))
|
||||||
|
#define SHELL_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
|
||||||
|
#define SHELL_IS_DOC_SYSTEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DOC_SYSTEM))
|
||||||
|
#define SHELL_IS_DOC_SYSTEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DOC_SYSTEM))
|
||||||
|
#define SHELL_DOC_SYSTEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DOC_SYSTEM, ShellDocSystemClass))
|
||||||
|
|
||||||
|
typedef struct _ShellDocSystem ShellDocSystem;
|
||||||
|
typedef struct _ShellDocSystemClass ShellDocSystemClass;
|
||||||
|
typedef struct _ShellDocSystemPrivate ShellDocSystemPrivate;
|
||||||
|
|
||||||
|
struct _ShellDocSystem
|
||||||
|
{
|
||||||
|
GObject parent;
|
||||||
|
|
||||||
|
ShellDocSystemPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ShellDocSystemClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType shell_doc_system_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
ShellDocSystem* shell_doc_system_get_default (void);
|
||||||
|
|
||||||
|
GSList *shell_doc_system_get_all (ShellDocSystem *system);
|
||||||
|
|
||||||
|
GtkRecentInfo *shell_doc_system_lookup_by_uri (ShellDocSystem *system,
|
||||||
|
const char *uri);
|
||||||
|
|
||||||
|
void shell_doc_system_queue_existence_check (ShellDocSystem *system,
|
||||||
|
guint n_recent);
|
||||||
|
|
||||||
|
void shell_doc_system_open (ShellDocSystem *system,
|
||||||
|
GtkRecentInfo *info);
|
||||||
|
|
||||||
|
#endif /* __SHELL_DOC_SYSTEM_H__ */
|
@ -1,102 +0,0 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:shell-drawing-area
|
|
||||||
* @short_description: A dynamically-sized Cairo drawing area
|
|
||||||
*
|
|
||||||
* #ShellDrawingArea is similar to #ClutterCairoTexture in that
|
|
||||||
* it allows drawing via Cairo; the primary difference is that
|
|
||||||
* it is dynamically sized. To use, connect to the @redraw
|
|
||||||
* signal, and inside the signal handler, call
|
|
||||||
* clutter_cairo_texture_create() to begin drawing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "shell-drawing-area.h"
|
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <cairo.h>
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(ShellDrawingArea, shell_drawing_area, CLUTTER_TYPE_GROUP);
|
|
||||||
|
|
||||||
struct _ShellDrawingAreaPrivate {
|
|
||||||
ClutterCairoTexture *texture;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Signals */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
REDRAW,
|
|
||||||
LAST_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint shell_drawing_area_signals [LAST_SIGNAL] = { 0 };
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_drawing_area_allocate (ClutterActor *self,
|
|
||||||
const ClutterActorBox *box,
|
|
||||||
ClutterAllocationFlags flags)
|
|
||||||
{
|
|
||||||
ShellDrawingArea *area = SHELL_DRAWING_AREA (self);
|
|
||||||
int width = box->x2 - box->x1;
|
|
||||||
int height = box->y2 - box->y1;
|
|
||||||
ClutterActorBox child_box;
|
|
||||||
|
|
||||||
/* Chain up directly to ClutterActor to set actor->allocation. We explicitly skip our parent class
|
|
||||||
* ClutterGroup here because we want to override the allocate function. */
|
|
||||||
(CLUTTER_ACTOR_CLASS (g_type_class_peek (clutter_actor_get_type ())))->allocate (self, box, flags);
|
|
||||||
|
|
||||||
child_box.x1 = 0;
|
|
||||||
child_box.x2 = width;
|
|
||||||
child_box.y1 = 0;
|
|
||||||
child_box.y2 = height;
|
|
||||||
|
|
||||||
clutter_actor_allocate (CLUTTER_ACTOR (area->priv->texture), &child_box, flags);
|
|
||||||
if (width > 0 && height > 0)
|
|
||||||
{
|
|
||||||
clutter_cairo_texture_set_surface_size (area->priv->texture,
|
|
||||||
width, height);
|
|
||||||
g_signal_emit (G_OBJECT (self), shell_drawing_area_signals[REDRAW], 0,
|
|
||||||
area->priv->texture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_drawing_area_class_init (ShellDrawingAreaClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
||||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
||||||
|
|
||||||
actor_class->allocate = shell_drawing_area_allocate;
|
|
||||||
|
|
||||||
shell_drawing_area_signals[REDRAW] =
|
|
||||||
g_signal_new ("redraw",
|
|
||||||
G_TYPE_FROM_CLASS (klass),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET (ShellDrawingAreaClass, redraw),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__OBJECT,
|
|
||||||
G_TYPE_NONE, 1, G_TYPE_OBJECT);
|
|
||||||
|
|
||||||
g_type_class_add_private (gobject_class, sizeof (ShellDrawingAreaPrivate));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
shell_drawing_area_init (ShellDrawingArea *area)
|
|
||||||
{
|
|
||||||
area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_DRAWING_AREA,
|
|
||||||
ShellDrawingAreaPrivate);
|
|
||||||
area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
|
|
||||||
clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_drawing_area_get_texture:
|
|
||||||
*
|
|
||||||
* Return Value: (transfer none):
|
|
||||||
*/
|
|
||||||
ClutterCairoTexture *
|
|
||||||
shell_drawing_area_get_texture (ShellDrawingArea *area)
|
|
||||||
{
|
|
||||||
return area->priv->texture;
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#ifndef __SHELL_DRAWING_AREA_H__
|
|
||||||
#define __SHELL_DRAWING_AREA_H__
|
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
|
|
||||||
#define SHELL_TYPE_DRAWING_AREA (shell_drawing_area_get_type ())
|
|
||||||
#define SHELL_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingArea))
|
|
||||||
#define SHELL_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
|
|
||||||
#define SHELL_IS_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_DRAWING_AREA))
|
|
||||||
#define SHELL_IS_DRAWING_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_DRAWING_AREA))
|
|
||||||
#define SHELL_DRAWING_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_DRAWING_AREA, ShellDrawingAreaClass))
|
|
||||||
|
|
||||||
typedef struct _ShellDrawingArea ShellDrawingArea;
|
|
||||||
typedef struct _ShellDrawingAreaClass ShellDrawingAreaClass;
|
|
||||||
|
|
||||||
typedef struct _ShellDrawingAreaPrivate ShellDrawingAreaPrivate;
|
|
||||||
|
|
||||||
struct _ShellDrawingArea
|
|
||||||
{
|
|
||||||
ClutterGroup parent;
|
|
||||||
|
|
||||||
ShellDrawingAreaPrivate *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _ShellDrawingAreaClass
|
|
||||||
{
|
|
||||||
ClutterGroupClass parent_class;
|
|
||||||
|
|
||||||
void (*redraw) (ShellDrawingArea *area, ClutterCairoTexture *texture);
|
|
||||||
};
|
|
||||||
|
|
||||||
GType shell_drawing_area_get_type (void) G_GNUC_CONST;
|
|
||||||
|
|
||||||
ClutterCairoTexture *shell_drawing_area_get_texture (ShellDrawingArea *area);
|
|
||||||
|
|
||||||
#endif /* __SHELL_DRAWING_AREA_H__ */
|
|
@ -1,104 +1,10 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#include "shell-drawing.h"
|
#include "shell-drawing.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_create_vertical_gradient:
|
|
||||||
* @top: the color at the top
|
|
||||||
* @bottom: the color at the bottom
|
|
||||||
*
|
|
||||||
* Creates a vertical gradient actor.
|
|
||||||
*
|
|
||||||
* Return value: (transfer none): a #ClutterCairoTexture actor with the
|
|
||||||
* gradient. The texture actor is floating, hence (transfer none).
|
|
||||||
*/
|
|
||||||
ClutterCairoTexture *
|
|
||||||
shell_create_vertical_gradient (ClutterColor *top,
|
|
||||||
ClutterColor *bottom)
|
|
||||||
{
|
|
||||||
ClutterCairoTexture *texture;
|
|
||||||
cairo_t *cr;
|
|
||||||
cairo_pattern_t *pattern;
|
|
||||||
|
|
||||||
/* Draw the gradient on an 8x8 pixel texture. Because the gradient is drawn
|
|
||||||
* from the uppermost to the lowermost row, after stretching 1/16 of the
|
|
||||||
* texture height has the top color and 1/16 has the bottom color. The 8
|
|
||||||
* pixel width is chosen for reasons related to graphics hardware internals.
|
|
||||||
*/
|
|
||||||
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 8));
|
|
||||||
cr = clutter_cairo_texture_create (texture);
|
|
||||||
|
|
||||||
pattern = cairo_pattern_create_linear (0, 0, 0, 8);
|
|
||||||
cairo_pattern_add_color_stop_rgba (pattern, 0,
|
|
||||||
top->red / 255.,
|
|
||||||
top->green / 255.,
|
|
||||||
top->blue / 255.,
|
|
||||||
top->alpha / 255.);
|
|
||||||
cairo_pattern_add_color_stop_rgba (pattern, 1,
|
|
||||||
bottom->red / 255.,
|
|
||||||
bottom->green / 255.,
|
|
||||||
bottom->blue / 255.,
|
|
||||||
bottom->alpha / 255.);
|
|
||||||
|
|
||||||
cairo_set_source (cr, pattern);
|
|
||||||
cairo_paint (cr);
|
|
||||||
|
|
||||||
cairo_pattern_destroy (pattern);
|
|
||||||
cairo_destroy (cr);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_create_horizontal_gradient:
|
|
||||||
* @left: the color on the left
|
|
||||||
* @right: the color on the right
|
|
||||||
*
|
|
||||||
* Creates a horizontal gradient actor.
|
|
||||||
*
|
|
||||||
* Return value: (transfer none): a #ClutterCairoTexture actor with the
|
|
||||||
* gradient. The texture actor is floating, hence (transfer none).
|
|
||||||
*/
|
|
||||||
ClutterCairoTexture *
|
|
||||||
shell_create_horizontal_gradient (ClutterColor *left,
|
|
||||||
ClutterColor *right)
|
|
||||||
{
|
|
||||||
ClutterCairoTexture *texture;
|
|
||||||
cairo_t *cr;
|
|
||||||
cairo_pattern_t *pattern;
|
|
||||||
|
|
||||||
/* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
|
|
||||||
* from the left to the right column, after stretching 1/16 of the
|
|
||||||
* texture width has the left side color and 1/16 has the right side color.
|
|
||||||
* There is no reason to use the 8 pixel height that would be similar to the
|
|
||||||
* reason we are using the 8 pixel width for the vertical gradient, so we
|
|
||||||
* are just using the 1 pixel height instead.
|
|
||||||
*/
|
|
||||||
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
|
|
||||||
cr = clutter_cairo_texture_create (texture);
|
|
||||||
|
|
||||||
pattern = cairo_pattern_create_linear (0, 0, 8, 0);
|
|
||||||
cairo_pattern_add_color_stop_rgba (pattern, 0,
|
|
||||||
left->red / 255.,
|
|
||||||
left->green / 255.,
|
|
||||||
left->blue / 255.,
|
|
||||||
left->alpha / 255.);
|
|
||||||
cairo_pattern_add_color_stop_rgba (pattern, 1,
|
|
||||||
right->red / 255.,
|
|
||||||
right->green / 255.,
|
|
||||||
right->blue / 255.,
|
|
||||||
right->alpha / 255.);
|
|
||||||
|
|
||||||
cairo_set_source (cr, pattern);
|
|
||||||
cairo_paint (cr);
|
|
||||||
|
|
||||||
cairo_pattern_destroy (pattern);
|
|
||||||
cairo_destroy (cr);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
shell_draw_clock (ClutterCairoTexture *texture,
|
shell_draw_clock (ClutterCairoTexture *texture,
|
||||||
int hour,
|
int hour,
|
||||||
@ -147,17 +53,14 @@ shell_draw_clock (ClutterCairoTexture *texture,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
shell_draw_box_pointer (ClutterCairoTexture *texture,
|
||||||
ClutterGravity pointing_towards,
|
ShellPointerDirection direction,
|
||||||
ClutterColor *border_color,
|
ClutterColor *border_color,
|
||||||
ClutterColor *background_color)
|
ClutterColor *background_color)
|
||||||
{
|
{
|
||||||
guint width, height;
|
guint width, height;
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
|
|
||||||
g_return_if_fail (pointing_towards == CLUTTER_GRAVITY_NORTH ||
|
|
||||||
pointing_towards == CLUTTER_GRAVITY_WEST);
|
|
||||||
|
|
||||||
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
clutter_cairo_texture_get_surface_size (texture, &width, &height);
|
||||||
|
|
||||||
clutter_cairo_texture_clear (texture);
|
clutter_cairo_texture_clear (texture);
|
||||||
@ -167,17 +70,34 @@ shell_draw_box_pointer (ClutterCairoTexture *texture,
|
|||||||
|
|
||||||
clutter_cairo_set_source_color (cr, border_color);
|
clutter_cairo_set_source_color (cr, border_color);
|
||||||
|
|
||||||
if (pointing_towards == CLUTTER_GRAVITY_WEST)
|
switch (direction)
|
||||||
{
|
|
||||||
cairo_move_to (cr, width, 0);
|
|
||||||
cairo_line_to (cr, 0, floor (height * 0.5));
|
|
||||||
cairo_line_to (cr, width, height);
|
|
||||||
}
|
|
||||||
else /* CLUTTER_GRAVITY_NORTH */
|
|
||||||
{
|
{
|
||||||
|
case SHELL_POINTER_UP:
|
||||||
cairo_move_to (cr, 0, height);
|
cairo_move_to (cr, 0, height);
|
||||||
cairo_line_to (cr, floor (width * 0.5), 0);
|
cairo_line_to (cr, floor (width * 0.5), 0);
|
||||||
cairo_line_to (cr, width, height);
|
cairo_line_to (cr, width, height);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHELL_POINTER_DOWN:
|
||||||
|
cairo_move_to (cr, width, 0);
|
||||||
|
cairo_line_to (cr, floor (width * 0.5), height);
|
||||||
|
cairo_line_to (cr, 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHELL_POINTER_LEFT:
|
||||||
|
cairo_move_to (cr, width, height);
|
||||||
|
cairo_line_to (cr, 0, floor (height * 0.5));
|
||||||
|
cairo_line_to (cr, width, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHELL_POINTER_RIGHT:
|
||||||
|
cairo_move_to (cr, 0, 0);
|
||||||
|
cairo_line_to (cr, width, floor (height * 0.5));
|
||||||
|
cairo_line_to (cr, 0, height);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_stroke_preserve (cr);
|
cairo_stroke_preserve (cr);
|
||||||
|