Compare commits
606 Commits
overview-r
...
2.91.90
Author | SHA1 | Date | |
---|---|---|---|
cd2c0565ad | |||
43469de2fe | |||
d1eb2b2b02 | |||
682b7ea0b5 | |||
e4d9b11685 | |||
89cbb9766b | |||
ca4b385916 | |||
26f4e44d6c | |||
610c2b5987 | |||
3c66455112 | |||
867c9a19ae | |||
1496c85bb6 | |||
998ef7d861 | |||
86b925a294 | |||
1fcfadd53c | |||
192d3a94ed | |||
5683bb9b8e | |||
1cf4cb3de5 | |||
d1548aeee1 | |||
fb800f3d8b | |||
21cc6a07f5 | |||
8a22ea948f | |||
b5cea80b54 | |||
5fc4d1751c | |||
75dbe4fdb9 | |||
c692fa4687 | |||
f34ce9271c | |||
6e236546ea | |||
3fd908d92c | |||
a4d3a57a1c | |||
0064ca3582 | |||
9c4cee7875 | |||
4a21dc954a | |||
526384320e | |||
977f45cdb9 | |||
0a94c01f83 | |||
473dad0c3e | |||
a5aa7d4bd6 | |||
4dec1bc846 | |||
9925264410 | |||
51bfbca2f1 | |||
a3a6650e66 | |||
2b90be77b3 | |||
794c986b10 | |||
bffe796413 | |||
6b4c497800 | |||
063038bc41 | |||
932a95fa25 | |||
9ce439e406 | |||
ca2bffcdb0 | |||
01097ac954 | |||
45fe44da85 | |||
89cc6807e0 | |||
9af03194b9 | |||
3056c772c2 | |||
136ed3623b | |||
4bedbca66f | |||
30da70a09e | |||
cf3c631cde | |||
530604d1ad | |||
edc0cd36c5 | |||
5261304231 | |||
a1c0b85819 | |||
1b4cff7cdc | |||
ef983480c0 | |||
bff027c1f7 | |||
1473a66862 | |||
53516733f7 | |||
e9a45190e4 | |||
649ed3332f | |||
7598fd4581 | |||
aaf94ea8f9 | |||
1a639f0b17 | |||
1bc1a0dbf0 | |||
537ce60599 | |||
0ee5a05e00 | |||
bdd805a3ee | |||
43961aaca5 | |||
0ef3f999d2 | |||
fb8f3f19f7 | |||
85ecd1864f | |||
630f0f0ac8 | |||
ae5131f902 | |||
493e82e37d | |||
804b51fa95 | |||
4f23f32fc1 | |||
3631983de5 | |||
a1e019b41a | |||
a80ed030ad | |||
1779f662b1 | |||
07bde8248e | |||
bb70be31c0 | |||
6b429b7f50 | |||
0ae44f4015 | |||
f1c279765b | |||
f39e693324 | |||
e3e16586b8 | |||
8f3376ce62 | |||
140022aaf2 | |||
ccd2fec890 | |||
02bcce07bd | |||
dc51a84cec | |||
2b91ef7833 | |||
0285e62516 | |||
4ef1923573 | |||
370c596fbf | |||
e97c15e01e | |||
69d3aad080 | |||
2fd5371de7 | |||
b750dde05b | |||
9f310b6773 | |||
067e3f2075 | |||
1938a5bcb0 | |||
36a624aafd | |||
8c80a58fa4 | |||
740a946e72 | |||
1aba99336a | |||
37d307c80e | |||
294490f77b | |||
1bc805206b | |||
a047132a2f | |||
c2ae95f912 | |||
ed6af523cb | |||
1224e959b6 | |||
688b9697c8 | |||
0c0e2cc689 | |||
f211681d6c | |||
eaa7b83979 | |||
1c320d1f8a | |||
216be33fb9 | |||
b23989871e | |||
fb5a0f8fa5 | |||
83883fafbb | |||
09717aae58 | |||
03729a71f4 | |||
6bf2dd9138 | |||
7810db6b9f | |||
4c2d9ca16e | |||
b0efe684fc | |||
2f3e47b586 | |||
1d77914316 | |||
aad3560ccb | |||
69d25c975c | |||
64c4bb86c6 | |||
eadd3691a6 | |||
20aec4bb55 | |||
01b646169c | |||
e5130877e7 | |||
75f771d736 | |||
914e5d30c7 | |||
2d716041f1 | |||
cd56de85d0 | |||
47e66ffc01 | |||
b4ec342d06 | |||
0e23ec394e | |||
7d77802ba1 | |||
ce91d85bc0 | |||
aba6a85c56 | |||
475c36048b | |||
ea4c68bd8e | |||
ac3c9f8475 | |||
de3ae87199 | |||
e752aae669 | |||
8f7d5cde90 | |||
d90c98130e | |||
af3883905b | |||
a4e53953a9 | |||
165f4e38b7 | |||
7372308270 | |||
c239d3bb1b | |||
940f06fb32 | |||
026f598c37 | |||
d33958ceee | |||
9bbf293898 | |||
d6020f1402 | |||
29e97a5f88 | |||
2b84554d91 | |||
a0584b9c30 | |||
5aab878e75 | |||
4d474e2f9c | |||
8cf9b5e5d2 | |||
a52ec8f286 | |||
739399eb2e | |||
ef8c9e0abb | |||
c705b64d67 | |||
70dd9c9d3d | |||
4450385458 | |||
1f154f6638 | |||
881a1d5cbc | |||
11064c0e35 | |||
7496a7ff3b | |||
fb71250dce | |||
0d32017ffc | |||
1b8bfda3b4 | |||
f038ce1c69 | |||
e6938ed06c | |||
2d5133ad51 | |||
6514bc845f | |||
eda12542c0 | |||
7c2aee3ac0 | |||
3c593fc056 | |||
9748cc264a | |||
295d286161 | |||
6011dd6bc9 | |||
5eb28e162f | |||
2792ad1cf4 | |||
95e6eae23b | |||
9ec2d5d609 | |||
a138f59cb0 | |||
d3e223c217 | |||
5437a3be4b | |||
c86a977564 | |||
c256fa9b9f | |||
ab80c5080a | |||
acf04d33ef | |||
bd04107593 | |||
e208c7e3dd | |||
fc313b198a | |||
b6749f4b60 | |||
ad27b9eda5 | |||
fd8572d44a | |||
32d59861a0 | |||
5bd40ff416 | |||
d1c3d8cadc | |||
49f270b02b | |||
9148dfba34 | |||
ff89090a09 | |||
ca7b03852d | |||
9735e40e24 | |||
40d8758aae | |||
73bd499d97 | |||
59191bc4b7 | |||
3173401cc2 | |||
9c53ae61f4 | |||
20de1ca772 | |||
5259656ce1 | |||
00df20c618 | |||
7637afe7dc | |||
b864f68c19 | |||
0c12f1d2ee | |||
f3e687eac8 | |||
3653f7c6c1 | |||
ac92d0678b | |||
bd9e6a3bdf | |||
2ce93ce39e | |||
5b95ccae5f | |||
0310f07eab | |||
e3acaa05be | |||
91d8a32f25 | |||
457c7adf59 | |||
8ebdb7f493 | |||
6c55ca59b0 | |||
0e4a47c0aa | |||
33100cd204 | |||
39a8243f27 | |||
5f50922d32 | |||
1137ca0fce | |||
36f3429ad2 | |||
703092f2c9 | |||
fd75c7c7b4 | |||
8abbd5dacb | |||
ed0fa7e1b7 | |||
15f1245927 | |||
25434e42d0 | |||
d6749589e8 | |||
8fea88879a | |||
d9e778f501 | |||
885b6ffaef | |||
4c64920f45 | |||
7369ea6125 | |||
20b7d34577 | |||
6cc4c33b13 | |||
5abf9a0425 | |||
17a0b27109 | |||
2bcdae4d5d | |||
0244c6d5b8 | |||
bc6be40fdd | |||
8c22b58611 | |||
074fe3ea70 | |||
5666fdefce | |||
e9613b0340 | |||
8a030e8fc4 | |||
9c23bf02bd | |||
8468fe87eb | |||
9617e83d88 | |||
912a30c566 | |||
2cd84da835 | |||
64fad9a394 | |||
3b797b32bc | |||
f6ae48ec70 | |||
61869d7db1 | |||
e2e90a550e | |||
80ceead18d | |||
ed8884e192 | |||
3f6165799f | |||
d2a40d6885 | |||
eb8fc738af | |||
b9e1d917da | |||
f721e80685 | |||
361a115729 | |||
d107b84be4 | |||
4cb967f3ac | |||
6f070319a0 | |||
13edecde6c | |||
75d1230dd1 | |||
779d5462f2 | |||
03757c1fcb | |||
1faee65a68 | |||
f7ab90b93b | |||
a7fa7d748d | |||
2b6d4ff279 | |||
06cea89dae | |||
260ca64e94 | |||
6dc4adfc13 | |||
e727c184ef | |||
412c50b939 | |||
8dcd70edbe | |||
1b383c7285 | |||
e96a90b161 | |||
d64d491f63 | |||
a6da22fa70 | |||
56cf7a6628 | |||
5b71788a84 | |||
39f7aa8457 | |||
c6baee2622 | |||
20f49e8c89 | |||
d6a6e6220a | |||
3b333c37fd | |||
289d577bc1 | |||
00ba937171 | |||
df3560143d | |||
bdeb20f7c9 | |||
1dd35b7d08 | |||
450291b856 | |||
125adb4d32 | |||
13cc58937e | |||
ee6a852996 | |||
2cfe978e1f | |||
0625655456 | |||
39a1f5cfe0 | |||
961fdd861f | |||
28adc03cce | |||
68c482ec32 | |||
a739f89dd1 | |||
5c7042cff3 | |||
255d4634a9 | |||
cb4c2ab824 | |||
4c449124ee | |||
c74536c9b3 | |||
df10ef532f | |||
6e18d18a81 | |||
4bc078b5fd | |||
cf49882e96 | |||
b0c6cf3fc5 | |||
883f51be93 | |||
ad52d783bd | |||
05f9be046f | |||
26aaecc33d | |||
f91138d0a2 | |||
89d89ae1cf | |||
e67fbcdc79 | |||
f4572eedd0 | |||
988b515ad2 | |||
2205a395e7 | |||
108b582f0d | |||
6ebd808e8b | |||
343c1133f4 | |||
fea2044eb8 | |||
6b353ece82 | |||
5b3f40102f | |||
b2ab3de80e | |||
56f6c9c5f6 | |||
39b0c88c76 | |||
1fca8a8b95 | |||
d442494f3a | |||
1f1fe36b89 | |||
d21f04b8b4 | |||
e73e4375b8 | |||
2905b0318d | |||
ab1ecb5ba2 | |||
47b8d16067 | |||
dc020628b5 | |||
bed063eea1 | |||
ed76e52918 | |||
e0f58c615b | |||
a681a2ba02 | |||
856207c154 | |||
00d897f282 | |||
49a09657c5 | |||
38fb51a99e | |||
99a865fb0f | |||
b919dd7271 | |||
9ddf19a1a4 | |||
23353fb77a | |||
8bdfb8df68 | |||
a65a0f03d4 | |||
7322a4e4ef | |||
870be026d8 | |||
e4fc899aca | |||
5412ce276c | |||
6200daa5bb | |||
4207536377 | |||
90e042e30c | |||
4e45e28ea0 | |||
d7a19fdd1b | |||
428d2fdb76 | |||
d77409876c | |||
81714ce1a3 | |||
6bae9ed20d | |||
44f70646d3 | |||
ed83b5494c | |||
84bac4414c | |||
4c4a703e63 | |||
c08021d91f | |||
df5ec16842 | |||
055a34877e | |||
a538027fe7 | |||
77289737c5 | |||
512798f9c6 | |||
5322e9a643 | |||
34f8568547 | |||
66e0e5b370 | |||
960af783f7 | |||
1a884535ad | |||
b2bb0bf10f | |||
4de15b2b57 | |||
493844d005 | |||
046308c582 | |||
cedcbc5fcf | |||
a46baeed0b | |||
cff503922c | |||
ce2efc6a67 | |||
778aa6be09 | |||
0052657a22 | |||
f60b995236 | |||
ceedc7e32c | |||
62507c9759 | |||
7beb7e0f65 | |||
ad67225bf8 | |||
dd0f721694 | |||
24f1e87813 | |||
68a0d7897f | |||
d9d56b7492 | |||
96a0b9d416 | |||
d48f064636 | |||
4e0a1c1152 | |||
272cbc1485 | |||
0ba7188625 | |||
6a2f038515 | |||
9f04009f80 | |||
2763d8dcc1 | |||
f8eb881728 | |||
0406b91bd5 | |||
e6678dadd8 | |||
a2fb8a8dce | |||
5f212b1ae2 | |||
23432e616d | |||
7d44c666ff | |||
ca09595350 | |||
b1dc2d967e | |||
c546ad9720 | |||
53f53c5a91 | |||
d2f675e41c | |||
d66370aecb | |||
1694148bdb | |||
7d0eeef90a | |||
23f3af832c | |||
52a87a22ec | |||
ea63f7562f | |||
d3de4e3fbd | |||
f326595202 | |||
4dd4c9f99f | |||
69bed929af | |||
db457db060 | |||
12c113a7a7 | |||
be96cb9291 | |||
bfc7b98e1d | |||
054f498201 | |||
6024b87d27 | |||
59b1aa26bb | |||
f96bc5c052 | |||
67c05e07b1 | |||
dafaab66b1 | |||
4fd24da4e4 | |||
bdfc516715 | |||
94da2531e7 | |||
9e99a8c25a | |||
bc83890c39 | |||
2d55eab62e | |||
d66207bc68 | |||
9f39ce5d27 | |||
eef194c3aa | |||
22f4aabadf | |||
2a19d5f143 | |||
7279cc1db8 | |||
e41b0bc16d | |||
e3aab2a90f | |||
7eec8a899a | |||
3262b63325 | |||
be59876c60 | |||
26f395bb84 | |||
7653cf6ad0 | |||
3203dc549c | |||
4ae9c978ce | |||
cb5c18c783 | |||
cb2babb1a0 | |||
c648a5f117 | |||
e562c863f7 | |||
cdaf8f3d19 | |||
a33aab07e6 | |||
26a5197d33 | |||
8be2800486 | |||
a2528a7a98 | |||
2332e2b0c5 | |||
a70f1f449b | |||
e425a183f7 | |||
0bbeb733a2 | |||
ff77994204 | |||
563951b789 | |||
ea65f5e795 | |||
670048e555 | |||
35b5cb9860 | |||
978cf9d3ab | |||
96fb6d8f16 | |||
c5c66ceb98 | |||
1efb0213c5 | |||
1a466cfce4 | |||
46ba718ab2 | |||
b40cc2c294 | |||
c86da9afbd | |||
d00933f044 | |||
172f78daad | |||
d7cc82909a | |||
7ce65e421b | |||
0c8941715d | |||
5ba9bb7a67 | |||
7b73df78c6 | |||
43d479c786 | |||
b9eca84d34 | |||
aef005f451 | |||
24669e3ba7 | |||
e99eefbb00 | |||
08b84b1449 | |||
7c980f42c9 | |||
42366e4be4 | |||
991a0c642b | |||
6aac51a2a7 | |||
6adc12368c | |||
eb2ee3f259 | |||
8b5cd4ef0f | |||
4517f60630 | |||
b25bad9995 | |||
c9b178b193 | |||
6b0fe1b0b0 | |||
c908a060b8 | |||
063fc8e29c | |||
d870fef122 | |||
6b723ed72a | |||
56fb7e2c58 | |||
41a5282b7e | |||
6f9ede569e | |||
e2e11b1a29 | |||
b59daac6f3 | |||
5fef9188c9 | |||
2c5d825c87 | |||
d5d7d8a391 | |||
7811632e6f | |||
6c5c3bedbe | |||
0942f50781 | |||
1eb6dfe1b8 | |||
1a77acfda6 | |||
ffd7eaede5 | |||
688a315cbf | |||
48fff0e96b | |||
e6bb06a7cc | |||
26225f0bfb | |||
3e4f744e56 | |||
2d2ac5b3f6 | |||
e06b608b10 | |||
f24e567dc4 | |||
1ea488bb3d | |||
8d47a150df | |||
dc24252e82 | |||
2167be053d | |||
926ddc2bdf | |||
4f7a28863c | |||
6e902f5fec | |||
01e7d6f30e | |||
d6f1c10b1b | |||
65f0b483f8 | |||
6a52deec7d | |||
3d468c26b0 | |||
a5e61e27c7 | |||
59ba112959 | |||
4800f63c3a | |||
1fce237538 | |||
71685a3b48 | |||
c0b9ce16a7 | |||
d1407d0026 | |||
d5bfc503fe | |||
de50cf80a8 | |||
5086bfedac | |||
f67ad23033 | |||
b7c1400eb3 | |||
b956c6f093 | |||
bfc850a94d |
9
.gitignore
vendored
@ -18,13 +18,10 @@ config
|
||||
configure
|
||||
data/gnome-shell.desktop
|
||||
data/gnome-shell.desktop.in
|
||||
data/gnome-shell-clock-preferences.desktop
|
||||
data/gnome-shell-clock-preferences.desktop.in
|
||||
data/gschemas.compiled
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
data/org.gnome.accessibility.magnifier.gschema.xml
|
||||
data/org.gnome.accessibility.magnifier.gschema.valid
|
||||
js/misc/config.js
|
||||
intltool-extract.in
|
||||
intltool-merge.in
|
||||
intltool-update.in
|
||||
@ -45,14 +42,16 @@ src/Makefile
|
||||
src/Makefile.in
|
||||
src/gnomeshell-taskpanel
|
||||
src/gnome-shell
|
||||
src/gnome-shell-clock-preferences
|
||||
src/run-js-test
|
||||
src/test-recorder
|
||||
src/test-recorder.ogg
|
||||
src/test-theme
|
||||
src/st.h
|
||||
src/stamp-st.h
|
||||
src/stamp-st.h.tmp
|
||||
stamp-h1
|
||||
tests/run-test.sh
|
||||
xmldocs.make
|
||||
*~
|
||||
*.patch
|
||||
*.sw?
|
||||
|
70
configure.ac
@ -1,11 +1,14 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[2.91.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[2.91.90],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
|
||||
AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"])
|
||||
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
@ -57,16 +60,22 @@ fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.3.14
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.6.11
|
||||
GJS_MIN_VERSION=0.7
|
||||
MUTTER_MIN_VERSION=2.91.0
|
||||
GTK_MIN_VERSION=2.91.0
|
||||
CLUTTER_MIN_VERSION=1.5.15
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=0.7.11
|
||||
MUTTER_MIN_VERSION=2.91.90
|
||||
GTK_MIN_VERSION=3.0.0
|
||||
GIO_MIN_VERSION=2.25.9
|
||||
LIBECAL_REQUIRED=1.6.0
|
||||
LIBEDATASERVER_REQUIRED=1.2.0
|
||||
LIBEDATASERVERUI2_REQUIRED=1.2.0
|
||||
LIBEDATASERVERUI3_REQUIRED=2.91.6
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.13.12
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
||||
gio-unix-2.0 dbus-glib-1
|
||||
gio-unix-2.0 dbus-glib-1 libxml-2.0
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
mutter-plugins >= $MUTTER_MIN_VERSION
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
@ -76,7 +85,13 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra)
|
||||
libcanberra
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION)
|
||||
|
||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
|
||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
|
||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
@ -87,13 +102,35 @@ AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0)
|
||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
|
||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0`/gnome-bluetooth
|
||||
BLUETOOTH_LIBS="-L'$BLUETOOTH_DIR' -lgnome-bluetooth-applet"
|
||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
||||
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
|
||||
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
||||
AC_SUBST([HAVE_BLUETOOTH],[1])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_DEFINE([HAVE_BLUETOOTH],[0])
|
||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
||||
AC_MSG_RESULT([no])])
|
||||
|
||||
# Default to libedataserverui-3.0, but allow falling back to 1.2
|
||||
PKG_CHECK_EXISTS(libedataserverui-3.0,
|
||||
[EDS_API=3.0
|
||||
LIBEDATASERVERUI_REQUIRED=$LIBEDATASERVERUI3_REQUIRED],
|
||||
[EDS_API=1.2
|
||||
LIBEDATASERVERUI_REQUIRED=$LIBEDATASERVERUI2_REQUIRED])
|
||||
PKG_CHECK_MODULES(LIBECAL, libecal-1.2 >= $LIBECAL_REQUIRED libedataserver-1.2 >= $LIBEDATASERVER_REQUIRED libedataserverui-$EDS_API >= $LIBEDATASERVERUI_REQUIRED)
|
||||
AC_SUBST(LIBECAL_CFLAGS)
|
||||
AC_SUBST(LIBECAL_LIBS)
|
||||
|
||||
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
|
||||
# FIXME: metacity-plugins.pc should point directly to its .gir file
|
||||
@ -103,11 +140,7 @@ AC_SUBST(MUTTER_BIN_DIR)
|
||||
AC_SUBST(MUTTER_LIB_DIR)
|
||||
AC_SUBST(MUTTER_PLUGIN_DIR)
|
||||
|
||||
GJS_JS_DIR=`$PKG_CONFIG --variable=jsdir gjs-1.0`
|
||||
GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0`
|
||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||
AC_SUBST(GJS_JS_DIR)
|
||||
AC_SUBST(GJS_JS_NATIVE_DIR)
|
||||
AC_SUBST(GJS_CONSOLE)
|
||||
|
||||
AC_CHECK_FUNCS(fdwalk)
|
||||
@ -157,18 +190,11 @@ changequote([,])dnl
|
||||
AC_PATH_PROG(mutter, [mutter])
|
||||
AC_SUBST(mutter)
|
||||
|
||||
AC_MSG_CHECKING([if mutter was compiled with GTK+-3.0])
|
||||
if $PKG_CONFIG --libs libmutter-private | grep gtk-x11-3 >/dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_ERROR([GNOME Shell requires Mutter to be compiled against GTK+-3.0])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
data/Makefile
|
||||
js/Makefile
|
||||
js/misc/config.js
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
po/Makefile.in
|
||||
|
@ -1,5 +1,5 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-clock-preferences.desktop
|
||||
desktop_DATA = gnome-shell.desktop
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -12,26 +12,30 @@ desktop_DATA = gnome-shell.desktop gnome-shell-clock-preferences.desktop
|
||||
%.desktop:%.desktop.in
|
||||
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
|
||||
|
||||
dist_pkgdata_DATA = clock-preferences.ui
|
||||
|
||||
imagesdir = $(pkgdatadir)/images
|
||||
dist_images_DATA = \
|
||||
close-black.svg \
|
||||
magnifier.svg
|
||||
searchprovidersdir = $(pkgdatadir)/search_providers
|
||||
dist_searchproviders_DATA = \
|
||||
search_providers/google.xml \
|
||||
search_providers/wikipedia.xml
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
theme/add-workspace.svg \
|
||||
theme/calendar-arrow-left.svg \
|
||||
theme/calendar-arrow-right.svg \
|
||||
theme/calendar-today.svg \
|
||||
theme/close-window.svg \
|
||||
theme/close.svg \
|
||||
theme/corner-ripple.png \
|
||||
theme/dialog-error.svg \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected.svg \
|
||||
theme/gnome-shell.css \
|
||||
theme/mosaic-view-active.svg \
|
||||
theme/mosaic-view.svg \
|
||||
theme/move-window-on-new.svg \
|
||||
theme/panel-button-border.svg \
|
||||
theme/panel-button-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
theme/process-working.png \
|
||||
theme/remove-workspace.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/scroll-button-down-hover.png \
|
||||
theme/scroll-button-down.png \
|
||||
theme/scroll-button-up-hover.png \
|
||||
@ -47,12 +51,10 @@ dist_theme_DATA = \
|
||||
theme/toggle-off-intl.svg \
|
||||
theme/toggle-on-us.svg \
|
||||
theme/toggle-on-intl.svg \
|
||||
theme/ws-switch-arrow-left.svg \
|
||||
theme/ws-switch-arrow-right.svg
|
||||
theme/ws-switch-arrow-up.svg \
|
||||
theme/ws-switch-arrow-down.svg
|
||||
|
||||
gsettings_SCHEMAS = \
|
||||
org.gnome.accessibility.magnifier.gschema.xml \
|
||||
org.gnome.shell.gschema.xml
|
||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
@GSETTINGS_RULES@
|
||||
@ -85,16 +87,13 @@ install-data-local:
|
||||
|
||||
EXTRA_DIST = \
|
||||
gnome-shell.desktop.in.in \
|
||||
gnome-shell-clock-preferences.desktop.in.in \
|
||||
$(menu_DATA) \
|
||||
$(gconfschema_DATA) \
|
||||
$(shaders_DATA) \
|
||||
org.gnome.accessibility.magnifier.gschema.xml.in \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
|
||||
CLEANFILES = \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-clock-preferences.desktop.in \
|
||||
$(desktop_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
gschemas.compiled
|
||||
|
@ -1,188 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface domain="gnome-shell">
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkDialog" id="prefs-dialog">
|
||||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Clock Preferences</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="has_separator">False</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">18</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="top_padding">6</property>
|
||||
<property name="left_padding">12</property>
|
||||
<property name="right_padding">6</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="12hr_radio">
|
||||
<property name="label" translatable="yes">_12 hour format</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="24hr_radio">
|
||||
<property name="label" translatable="yes">_24 hour format</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">12hr_radio</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label_format">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Clock Format</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<property name="top_padding">6</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="date_check">
|
||||
<property name="label" translatable="yes">Show the _date</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="seconds_check">
|
||||
<property name="label" translatable="yes">Show seco_nds</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label_display">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Panel Display</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area1">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="prefs_close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="0">prefs_close_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
@ -1,66 +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: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+devel"
|
||||
sodipodi:docname="close-black.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></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="811"
|
||||
inkscape:window-width="1272"
|
||||
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="10.440056"
|
||||
inkscape:window-x="40"
|
||||
inkscape:window-y="40"
|
||||
inkscape:current-layer="Foreground" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.5,3.5l2,2L10,8l2.5,2.5l-2,2L8,10l-2.5,2.5l-2-2L6,8L3.5,5.5l2-2L8,6L10.5,3.5 z M0,8c0-4.418,3.582-8,8-8s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
|
||||
id="path2394"
|
||||
style="fill-opacity:1;fill:#545454" />
|
||||
</svg>
|
Before Width: | Height: | Size: 2.3 KiB |
@ -1,15 +0,0 @@
|
||||
[Desktop Entry]
|
||||
_Name=Clock
|
||||
_Comment=Customize the panel clock
|
||||
Exec=@bindir@/gnome-shell-clock-preferences
|
||||
Icon=gnome-panel-clock
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Settings;DesktopSettings;
|
||||
OnlyShowIn=GNOME;
|
||||
X-GNOME-ShellOnly=true
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
@ -7,7 +7,7 @@ X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Bugzilla-Version=@VERSION@
|
||||
Categories=GNOME;GTK;Utility;Core;
|
||||
Categories=GNOME;GTK;Core;
|
||||
OnlyShowIn=GNOME;
|
||||
NoDisplay=true
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
|
@ -22,7 +22,7 @@
|
||||
<applyto>/desktop/gnome/shell/windows/button_layout</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>string</type>
|
||||
<default>:minimize,maximize,close</default>
|
||||
<default>:close</default>
|
||||
<locale name="C">
|
||||
<short>Arrangement of buttons on the titlebar</short>
|
||||
<long>
|
||||
@ -44,19 +44,20 @@
|
||||
</schema>
|
||||
|
||||
<schema>
|
||||
<key>/schemas/desktop/gnome/shell/windows/side_by_side_tiling</key>
|
||||
<applyto>/desktop/gnome/shell/windows/side_by_side_tiling</applyto>
|
||||
<key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
|
||||
<applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
|
||||
<owner>gnome-shell</owner>
|
||||
<type>bool</type>
|
||||
<default>true</default>
|
||||
<locale name="C">
|
||||
<short>enable side-by-side tiling when dropping windows on screen edges</short>
|
||||
<short>enable edge tiling when dropping windows on screen edges</short>
|
||||
<long>
|
||||
If enabled, dropping windows on screen edges maximizes them
|
||||
If enabled, dropping windows on vertical screen edges maximizes them
|
||||
vertically and resizes them horizontally to cover half of the
|
||||
available area.
|
||||
available area. Dropping windows on the top screen edge maximizes them
|
||||
completely.
|
||||
|
||||
This key overrides /apps/metacity/general/side_by_side_tiling when
|
||||
This key overrides /apps/metacity/general/edge_tiling when
|
||||
running GNOME Shell.
|
||||
</long>
|
||||
</locale>
|
||||
|
@ -1,12 +1,30 @@
|
||||
<Menu>
|
||||
<DefaultLayout>
|
||||
<Menuname>Apps</Menuname>
|
||||
<Menuname>Games</Menuname>
|
||||
<Menuname>Tools</Menuname>
|
||||
</DefaultLayout>
|
||||
<DefaultLayout>
|
||||
<Menuname>Accessories</Menuname>
|
||||
<Menuname>Games</Menuname>
|
||||
<Menuname>Graphics</Menuname>
|
||||
<Menuname>Internet</Menuname>
|
||||
<Menuname>Multimedia</Menuname>
|
||||
<Menuname>Office</Menuname>
|
||||
<Menuname>Other</Menuname>
|
||||
</DefaultLayout>
|
||||
|
||||
<Name>Applications</Name>
|
||||
<AppDir>/usr/local/share/applications</AppDir>
|
||||
<DefaultAppDirs/>
|
||||
|
||||
<Menu>
|
||||
<Name>Accessories</Name>
|
||||
<Include>
|
||||
<And>
|
||||
<Category>Utility</Category>
|
||||
<Not>
|
||||
<Category>System</Category>
|
||||
</Not>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Games</Name>
|
||||
<Include>
|
||||
@ -15,28 +33,50 @@
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Tools</Name>
|
||||
<Name>Graphics</Name>
|
||||
<Include>
|
||||
<Category>Development</Category>
|
||||
<And>
|
||||
<Category>System</Category>
|
||||
<Not>
|
||||
<Category>Settings</Category>
|
||||
</Not>
|
||||
<Category>Graphics</Category>
|
||||
</And>
|
||||
<Category>Utility</Category>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Apps</Name>
|
||||
<Name>Internet</Name>
|
||||
<Include>
|
||||
<And>
|
||||
<Category>Network</Category>
|
||||
<Not><Category>Settings</Category></Not>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Multimedia</Name>
|
||||
<Include>
|
||||
<And>
|
||||
<Category>AudioVideo</Category>
|
||||
<Not><Category>Settings</Category></Not>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Office</Name>
|
||||
<Include>
|
||||
<And>
|
||||
<Category>Office</Category>
|
||||
</And>
|
||||
</Include>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<Name>Other</Name>
|
||||
<OnlyUnallocated/>
|
||||
<Include>
|
||||
<And>
|
||||
<Or>
|
||||
<Category>Documentation</Category>
|
||||
<Not><Category>Core</Category></Not>
|
||||
</Or>
|
||||
<Not><Category>Settings</Category></Not>
|
||||
<Not><Category>Screensaver</Category></Not>
|
||||
</And>
|
||||
|
@ -1,80 +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: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="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
enable-background="new 0 0 29 18"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46+devel"
|
||||
sodipodi:docname="magnifier.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
|
||||
id="metadata16"><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="defs14"><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 9 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="29 : 9 : 1"
|
||||
inkscape:persp3d-origin="14.5 : 6 : 1"
|
||||
id="perspective18" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="728"
|
||||
inkscape:window-width="1103"
|
||||
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="true"
|
||||
inkscape:zoom="27.260185"
|
||||
inkscape:cx="9.5844061"
|
||||
inkscape:cy="9.4435574"
|
||||
inkscape:window-x="142"
|
||||
inkscape:window-y="26"
|
||||
inkscape:current-layer="Foreground"
|
||||
inkscape:snap-global="true"
|
||||
showguides="false"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2391"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
|
||||
|
||||
<g
|
||||
id="g5"
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
transform="translate(-4,-0.023114)">
|
||||
<path
|
||||
d="m 6.246,13.98 c -0.319,-0.319 -0.319,-0.837 0,-1.157 L 9.963,9.106 c 0.319,-0.319 0.837,-0.319 1.157,0 l 0.786,0.787 c 0.32,0.319 0.32,0.837 0,1.157 l -3.717,3.717 c -0.32,0.319 -0.838,0.319 -1.157,0 l -0.786,-0.787 0,0 z"
|
||||
id="path7"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
d="M 9.076,11.937"
|
||||
id="path9"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="m 7.25,7.476886 c 0,-1.243 1.007,-2.25 2.2499998,-2.25 1.2430002,0 2.2500002,1.007 2.2500002,2.25 0,1.243 -1.007,2.25 -2.2500002,2.25 C 8.257,9.726886 7.25,8.719886 7.25,7.476886 z m -2.25,0 c 0,-2.485 2.015,-4.5 4.4999998,-4.5 2.4850002,0 4.5000002,2.015 4.5000002,4.5 0,2.4849998 -2.015,4.5 -4.5000002,4.5 C 7.015,11.976886 5,9.9618858 5,7.476886 z"
|
||||
id="path11"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
|
||||
</svg>
|
Before Width: | Height: | Size: 2.9 KiB |
@ -1,133 +0,0 @@
|
||||
<schemalist>
|
||||
|
||||
<enum id="MouseTrackingMode">
|
||||
<value nick="none" value="0"/>
|
||||
<value nick="centered" value="1"/>
|
||||
<value nick="proportional" value="2"/>
|
||||
<value nick="push" value="3"/>
|
||||
</enum>
|
||||
|
||||
<enum id="ScreenPosition">
|
||||
<value nick="none" value="0"/>
|
||||
<value nick="full-screen" value="1"/>
|
||||
<value nick="top-half" value="2"/>
|
||||
<value nick="bottom-half" value="3"/>
|
||||
<value nick="left-half" value="4"/>
|
||||
<value nick="right-half" value="5"/>
|
||||
</enum>
|
||||
|
||||
<schema id="org.gnome.accessibility.magnifier"
|
||||
path="/desktop/gnome/accessibility/magnifier/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="show-magnifier" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show or hide the magnifier</_summary>
|
||||
<_description>
|
||||
Show or hide the magnifier and all of its zoom regions.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="mouse-tracking" enum="MouseTrackingMode">
|
||||
<default>'proportional'</default>
|
||||
<_summary>Mouse Tracking Mode</_summary>
|
||||
<_description>
|
||||
Determines the position of the magnified mouse image within the
|
||||
magnified view and how it reacts to system mouse movement. The values
|
||||
are
|
||||
- none: no mouse tracking;
|
||||
- centered: the mouse image is
|
||||
displayed at the center of the zoom region (which also represents
|
||||
the point under the system mouse) and the magnified contents are
|
||||
scrolled as the system mouse moves;
|
||||
- proportional: the position of the magnified mouse in the zoom region
|
||||
is proportionally the same as the position of the system mouse on screen;
|
||||
- push: when the magnified mouse intersects a boundary of the zoom
|
||||
region, the contents are scrolled into view.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="screen-position" enum="ScreenPosition">
|
||||
<default>'full-screen'</default>
|
||||
<_summary>Screen position</_summary>
|
||||
<_description>
|
||||
The magnified view either fills the entire screen, or occupies the
|
||||
top-half, bottom-half, left-half, or right-half of the screen.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="mag-factor" type="d">
|
||||
<default>2.0</default>
|
||||
<_summary>Magnification factor</_summary>
|
||||
<_description>
|
||||
The power of the magnification. A value of 1.0 means no magnification.
|
||||
A value of 2.0 doubles the size.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="lens-mode" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Enable lens mode</_summary>
|
||||
<_description>
|
||||
Whether the magnified view should be centered over the location of
|
||||
the system mouse and move with it.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="scroll-at-edges" type="b">
|
||||
<default>false</default>
|
||||
<_summary>
|
||||
Scroll magnified contents beyond the edges of the desktop
|
||||
</_summary>
|
||||
<_description>
|
||||
For centered mouse tracking, when the system pointer is at or near the
|
||||
edge of the screen, the magnified contents continue to scroll such that
|
||||
the screen edge moves into the magnified view.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<!-- Cross-hairs -->
|
||||
<key name="show-cross-hairs" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show or hide crosshairs</_summary>
|
||||
<_description>
|
||||
Enables/disables display of crosshairs centered on the magnified
|
||||
mouse sprite.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-thickness" type="i">
|
||||
<default>8</default>
|
||||
<_summary>Thickness of the crosshairs</_summary>
|
||||
<_description>
|
||||
Width of the vertical and horizontal lines that make up the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-color" type="s">
|
||||
<default>'#ff0000'</default>
|
||||
<_summary>Color of the crosshairs</_summary>
|
||||
<_description>
|
||||
The color of the the vertical and horizontal lines that make up
|
||||
the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-opacity" type="i">
|
||||
<default>169</default>
|
||||
<_summary>Opacity of the crosshairs</_summary>
|
||||
<_description>
|
||||
Determines the transparency of the crosshairs, from fully opaque
|
||||
to fully transparent.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-length" type="i">
|
||||
<default>4096</default>
|
||||
<_summary>Length of the crosshairs</_summary>
|
||||
<_description>
|
||||
Determines the length of the vertical and horizontal lines that
|
||||
make up the crosshairs.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="cross-hairs-clip" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Clip the crosshairs at the center</_summary>
|
||||
<_description>
|
||||
Determines whether the crosshairs intersect the magnified mouse sprite,
|
||||
or are clipped such that the ends of the horizontal and vertical lines
|
||||
surround the mouse image.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
@ -30,28 +30,24 @@
|
||||
</_description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'openoffice.org-writer.desktop' ]</default>
|
||||
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
The applications corresponding to these identifiers
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="disabled-open-search-providers" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>disabled OpenSearch providers</_summary>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
</key>
|
||||
<key name="workspaces-view" type="s">
|
||||
<default>'single'</default>
|
||||
<_summary>Overview workspace view mode</_summary>
|
||||
<_description>
|
||||
The selected workspace view mode in the overview.
|
||||
Supported values are "single" and "grid".
|
||||
</_description>
|
||||
<choices>
|
||||
<choice value="single"/>
|
||||
<choice value="grid"/>
|
||||
</choices>
|
||||
<key name="looking-glass-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for the looking glass dialog</_summary>
|
||||
</key>
|
||||
<child name="clock" schema="org.gnome.shell.clock"/>
|
||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||
@ -71,51 +67,18 @@
|
||||
|
||||
<schema id="org.gnome.shell.clock" path="/apps/gnome-shell/clock/"
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key name="format" type="s">
|
||||
<default l10n="messages" context="hour_format">
|
||||
<!-- TRANSLATORS: This is the default hour format, choose ONLY '12-hour' or '24-hour'. -->
|
||||
"12-hour"
|
||||
</default>
|
||||
<_summary>Hour format</_summary>
|
||||
<_description>
|
||||
This key specifies the hour format used by the panel clock.
|
||||
Possible values are "12-hour", "24-hour", "unix" and "custom". If set
|
||||
to "unix", the clock will display time in seconds since Epoch,
|
||||
i.e. 1970-01-01. If set to "custom", the clock will display time
|
||||
according to the format specified in the custom_format key. Note that
|
||||
if set to either "unix" or "custom", the show_date and show_seconds
|
||||
keys are ignored.
|
||||
</_description>
|
||||
<choices>
|
||||
<choice value="12-hour"/>
|
||||
<choice value="24-hour"/>
|
||||
<choice value="unix"/>
|
||||
<choice value="custom"/>
|
||||
</choices>
|
||||
</key>
|
||||
<key name="custom-format" type="s">
|
||||
<default>''</default>
|
||||
<_summary>Custom format of the clock</_summary>
|
||||
<_description>
|
||||
This key specifies the format used by the panel clock when the format
|
||||
key is set to "custom". You can use conversion specifiers understood
|
||||
by strftime() to obtain a specific format. See the strftime() manual
|
||||
for more information.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-seconds" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show time with seconds</_summary>
|
||||
<_description>
|
||||
If true and format is either "12-hour" or "24-hour", display seconds in time.
|
||||
If true, display seconds in time.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-date" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Show date in clock</_summary>
|
||||
<_description>
|
||||
If true and format is either "12-hour" or "24-hour",
|
||||
display date in the clock, in addition to time.
|
||||
If true, display date in the clock, in addition to time.
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
@ -142,11 +105,13 @@
|
||||
take care of its own output - this might be used to send the output
|
||||
to an icecast server via shout2send or similar. When unset or set
|
||||
to an empty value, the default pipeline will be used. This is currently
|
||||
'videorate ! theoraenc ! oggmux' and records to Ogg Theora.
|
||||
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
|
||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
||||
for a guess at the optimal thread count on the system.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="file-extension" type="s">
|
||||
<default>'ogv'</default>
|
||||
<default>'webm'</default>
|
||||
<_summary>File extension used for storing the screencast</_summary>
|
||||
<_description>
|
||||
The filename for recorded screencasts will be a unique filename
|
||||
|
7
data/search_providers/google.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Google</ShortName>
|
||||
<Description>Google Search</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
|
||||
</OpenSearchDescription>
|
44
data/search_providers/wikipedia.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<ShortName>Wikipedia</ShortName>
|
||||
<Description>Wikipedia, the free encyclopedia</Description>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
|
||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
|
||||
<!-- The criterion for being below is being listed with more than 100,000
|
||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
|
||||
<Language>ar</Language>
|
||||
<Language>bg</Language>
|
||||
<Language>ca</Language>
|
||||
<Language>cs</Language>
|
||||
<Language>da</Language>
|
||||
<Language>de</Language>
|
||||
<Language>en</Language>
|
||||
<Language>eo</Language>
|
||||
<Language>es</Language>
|
||||
<Language>fa</Language>
|
||||
<Language>fi</Language>
|
||||
<Language>fr</Language>
|
||||
<Language>he</Language>
|
||||
<Language>hu</Language>
|
||||
<Language>id</Language>
|
||||
<Language>it</Language>
|
||||
<Language>ja</Language>
|
||||
<Language>ko</Language>
|
||||
<Language>lt</Language>
|
||||
<Language>nl</Language>
|
||||
<Language>no</Language>
|
||||
<Language>pl</Language>
|
||||
<Language>pt</Language>
|
||||
<Language>ro</Language>
|
||||
<Language>ru</Language>
|
||||
<Language>sk</Language>
|
||||
<Language>sl</Language>
|
||||
<Language>sr</Language>
|
||||
<Language>sv</Language>
|
||||
<Language>tr</Language>
|
||||
<Language>uk</Language>
|
||||
<Language>vi</Language>
|
||||
<Language>vo</Language>
|
||||
<Language>war</Language>
|
||||
<Language>zh</Language>
|
||||
</OpenSearchDescription>
|
@ -1,98 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="23"
|
||||
height="15"
|
||||
id="svg6375"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="New document 13">
|
||||
<defs
|
||||
id="defs6377">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 16 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="32 : 16 : 1"
|
||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
||||
id="perspective6383" />
|
||||
<inkscape:perspective
|
||||
id="perspective6366"
|
||||
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="11.197802"
|
||||
inkscape:cx="16"
|
||||
inkscape:cy="16"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata6380">
|
||||
<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
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-17)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g6243"
|
||||
transform="translate(-986.28859,-658.2796)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="rect5318"
|
||||
width="22"
|
||||
height="14"
|
||||
x="986.89801"
|
||||
y="675.86743"
|
||||
rx="0.49999979"
|
||||
ry="0.5" />
|
||||
<g
|
||||
id="g5320"
|
||||
transform="translate(402.77304,-12.882544)">
|
||||
<path
|
||||
id="path5322"
|
||||
d="m 595.125,692.53048 0,6.43903"
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
id="path5324"
|
||||
d="m 598.34451,695.75 -6.43902,0"
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.2 KiB |
82
data/theme/calendar-arrow-left.svg
Normal file
@ -0,0 +1,82 @@
|
||||
<?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="16"
|
||||
height="16"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r9942 custom"
|
||||
sodipodi:docname="New document 4">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="8.984481"
|
||||
inkscape:cy="5.6224906"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="930"
|
||||
inkscape:window-height="681"
|
||||
inkscape:window-x="1892"
|
||||
inkscape:window-y="272"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid17403"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<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(0,-1036.3622)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="path18028"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="84.5"
|
||||
sodipodi:cy="337.5"
|
||||
sodipodi:r1="5"
|
||||
sodipodi:r2="2.5"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
|
||||
transform="matrix(0,1.3621708,0.99186247,0,-325.48222,929.32667)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
82
data/theme/calendar-arrow-right.svg
Normal file
@ -0,0 +1,82 @@
|
||||
<?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="16"
|
||||
height="16"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48+devel r9942 custom"
|
||||
sodipodi:docname="arrow-left.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="7.7366092"
|
||||
inkscape:cy="6.4536271"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
borderlayer="true"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="930"
|
||||
inkscape:window-height="681"
|
||||
inkscape:window-x="1892"
|
||||
inkscape:window-y="272"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid17403"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<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(0,-1036.3622)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="path18028"
|
||||
sodipodi:sides="3"
|
||||
sodipodi:cx="84.5"
|
||||
sodipodi:cy="337.5"
|
||||
sodipodi:r1="5"
|
||||
sodipodi:r2="2.5"
|
||||
sodipodi:arg1="0.52359878"
|
||||
sodipodi:arg2="1.5707963"
|
||||
inkscape:flatsided="true"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 88.830127,340 80.169873,340 84.5,332.5 z"
|
||||
transform="matrix(0,1.3621708,-0.99186247,0,342.48324,929.32667)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
187
data/theme/calendar-today.svg
Normal file
@ -0,0 +1,187 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="28"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="calendar-today.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3770"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3007"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3067"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient3072"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient2997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="8.3750933"
|
||||
inkscape:cy="8.0837211"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-469.08263,-536.99307)">
|
||||
<g
|
||||
id="g3003">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
|
||||
sodipodi:end="6.2831853"
|
||||
sodipodi:start="3.1415927"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:ry="16"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:cx="51"
|
||||
id="path34506-3"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
sodipodi:type="arc" />
|
||||
<rect
|
||||
y="558.85046"
|
||||
x="468.96878"
|
||||
height="3.1425927"
|
||||
width="28.149134"
|
||||
id="rect2996"
|
||||
style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.7 KiB |
84
data/theme/dash-placeholder.svg
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="76"
|
||||
height="27"
|
||||
id="svg11252"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs11254">
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient39563-4-2"
|
||||
id="radialGradient68155-2-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.3486842,0,317.8421)"
|
||||
cx="49"
|
||||
cy="488"
|
||||
fx="49"
|
||||
fy="488"
|
||||
r="38" />
|
||||
<linearGradient
|
||||
id="linearGradient39563-4-2">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop39565-1-4" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop39567-7-9" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient39573-6-1"
|
||||
id="radialGradient68157-0-8"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="50.5"
|
||||
cy="487.5"
|
||||
fx="50.5"
|
||||
fy="487.5"
|
||||
r="10.5" />
|
||||
<linearGradient
|
||||
id="linearGradient39573-6-1">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop39575-5-6" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop39577-1-2" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(-337,-518.86218)">
|
||||
<g
|
||||
id="g99967"
|
||||
style="display:inline"
|
||||
transform="translate(326,44.862171)">
|
||||
<rect
|
||||
style="opacity:0.49375;color:#000000;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="rect99969"
|
||||
width="76"
|
||||
height="2"
|
||||
x="11"
|
||||
y="487"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<path
|
||||
style="opacity:0.43125;color:#000000;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path99971"
|
||||
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
|
||||
transform="matrix(1.2857143,0,0,1.2857143,-14.428572,-139.28571)" />
|
||||
<path
|
||||
transform="matrix(0.43589747,0,0,0.43589747,28.487179,275)"
|
||||
d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
|
||||
id="path99973"
|
||||
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -1,222 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg4908"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="dialog-error.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-filename="/home/andreas/project/gnome-icon-theme/scalable/actions/process-stop.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
version="1.0">
|
||||
<defs
|
||||
id="defs4910">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.349881,0,0,1.349881,-3.498814,-1.810859)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="9.7183542"
|
||||
fy="4.9892726"
|
||||
fx="9.6893959"
|
||||
cy="4.9892726"
|
||||
cx="9.6893959"
|
||||
id="radialGradient5177"
|
||||
xlink:href="#linearGradient5171"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.417917,0,0,2.417917,-14.17917,-4.903184)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="9.7785711"
|
||||
fy="3.458019"
|
||||
fx="10"
|
||||
cy="3.458019"
|
||||
cx="10"
|
||||
id="radialGradient5157"
|
||||
xlink:href="#linearGradient5151"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.928125,0,0,0.3143011,0.7718789,12.358015)"
|
||||
r="9.0598059"
|
||||
fy="18.022524"
|
||||
fx="10.739184"
|
||||
cy="18.022524"
|
||||
cx="10.739184"
|
||||
id="radialGradient5145"
|
||||
xlink:href="#linearGradient5139"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient5139"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5141"
|
||||
offset="0"
|
||||
style="stop-color:black;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5143"
|
||||
offset="1"
|
||||
style="stop-color:black;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5151"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5153"
|
||||
offset="0"
|
||||
style="stop-color:white;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5155"
|
||||
offset="1"
|
||||
style="stop-color:white;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5171">
|
||||
<stop
|
||||
id="stop5173"
|
||||
offset="0"
|
||||
style="stop-color:#fe3a00;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop5175"
|
||||
offset="1"
|
||||
style="stop-color:#c00;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="24.442987"
|
||||
inkscape:cy="10.142308"
|
||||
inkscape:current-layer="g7001"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1674"
|
||||
inkscape:window-height="970"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
width="48px"
|
||||
height="48px"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata4913">
|
||||
<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>Stop Process</dc:title>
|
||||
<dc:date>December 2006</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Steiner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>Andreas Nilsson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>stop</rdf:li>
|
||||
<rdf:li>halt</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/SourceCode" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-24)">
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
id="g7001"
|
||||
transform="matrix(1.4566048,0,0,1.4455352,0.4112881,1.2324709)">
|
||||
<path
|
||||
transform="matrix(0.91468137,0,0,0.70055266,-1.8812476,17.474032)"
|
||||
d="m 19.79899,18.022524 a 9.0598059,3.0935922 0 1 1 -18.1196115,0 9.0598059,3.0935922 0 1 1 18.1196115,0 z"
|
||||
sodipodi:ry="3.0935922"
|
||||
sodipodi:rx="9.0598059"
|
||||
sodipodi:cy="18.022524"
|
||||
sodipodi:cx="10.739184"
|
||||
id="path5137"
|
||||
style="color:#000000;fill:url(#radialGradient5145);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(0.87347736,0,0,0.83068052,-0.79308842,15.602788)"
|
||||
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
|
||||
sodipodi:ry="9.25"
|
||||
sodipodi:rx="9.25"
|
||||
sodipodi:cy="9.625"
|
||||
sodipodi:cx="10"
|
||||
id="path4262"
|
||||
style="color:#000000;fill:url(#radialGradient5177);fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:0.47435912;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.35393258;color:#000000;fill:none;stroke:url(#radialGradient5157);stroke-width:0.49999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
id="path5149"
|
||||
sodipodi:cx="10"
|
||||
sodipodi:cy="9.625"
|
||||
sodipodi:rx="9.25"
|
||||
sodipodi:ry="9.25"
|
||||
d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
|
||||
transform="matrix(0.82868359,0,0,0.78808147,-0.34515141,16.012803)" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
id="path5159"
|
||||
d="m 4.834121,20.642783 6.215127,5.91061"
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219134;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219146;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 11.04925,20.622826 4.8159529,26.553393"
|
||||
id="path5161"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 8.0 KiB |
@ -9,23 +9,23 @@
|
||||
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="23"
|
||||
height="15"
|
||||
id="svg5501"
|
||||
width="10"
|
||||
height="20"
|
||||
id="svg10003"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
sodipodi:docname="add-workspace.svg">
|
||||
inkscape:version="0.47 r22583"
|
||||
sodipodi:docname="filter-selected.svg">
|
||||
<defs
|
||||
id="defs5503">
|
||||
id="defs10005">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 16 : 1"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="32 : 16 : 1"
|
||||
inkscape:persp3d-origin="16 : 10.666667 : 1"
|
||||
id="perspective5509" />
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective10011" />
|
||||
<inkscape:perspective
|
||||
id="perspective5314"
|
||||
id="perspective9998"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
@ -39,29 +39,27 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.197802"
|
||||
inkscape:cx="-0.074583208"
|
||||
inkscape:cy="16"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="10.181818"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="997"
|
||||
inkscape:window-height="994"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-grids="true"
|
||||
inkscape:snap-bbox="true" />
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5506">
|
||||
id="metadata10008">
|
||||
<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>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@ -69,24 +67,15 @@
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0,-17)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g6239"
|
||||
transform="translate(-953.97989,-657.32287)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="rect5318-6"
|
||||
width="22"
|
||||
height="14"
|
||||
x="954.5"
|
||||
y="675"
|
||||
rx="0.49999979"
|
||||
ry="0.5" />
|
||||
<path
|
||||
style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 968.71951,682 -6.43902,0"
|
||||
id="path5324-5" />
|
||||
</g>
|
||||
transform="translate(0,-44)">
|
||||
<path
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect34320"
|
||||
d="m -0.18726572,54.181804 10.55634072,10.55636 10e-6,-21.11269 z"
|
||||
style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.6 KiB |
74
data/theme/panel-button-border.svg
Normal file
@ -0,0 +1,74 @@
|
||||
<?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="21"
|
||||
height="10"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="panel-button-border.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="44.8"
|
||||
inkscape:cx="8.6594891"
|
||||
inkscape:cy="5.7029946"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
guidetolerance="10000"
|
||||
objecttolerance="10000">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3792"
|
||||
empspacing="10"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect3796"
|
||||
width="3"
|
||||
height="2"
|
||||
x="9"
|
||||
y="8" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
111
data/theme/panel-button-highlight-narrow.svg
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="30"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="panel-button-highlight-narrow.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-171.36384"
|
||||
inkscape:cy="-53.255157"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-468.08632,-537.03477)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(0.35714286,0,0,1.5625,464.87203,515.15977)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
111
data/theme/panel-button-highlight-wide.svg
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="84"
|
||||
height="25"
|
||||
id="svg10621"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="panel-button-highlight-wide.svg">
|
||||
<defs
|
||||
id="defs10623">
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
id="radialGradient99561-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
cx="51"
|
||||
cy="30"
|
||||
fx="51"
|
||||
fy="30"
|
||||
r="42" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient34508-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop34510-1-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop34512-4-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="42"
|
||||
fy="30"
|
||||
fx="51"
|
||||
cy="30"
|
||||
cx="51"
|
||||
gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient10592"
|
||||
xlink:href="#linearGradient34508-1-3"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-118.50071"
|
||||
inkscape:cy="27.304508"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="843"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata10626">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-441.08632,-537.03477)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
id="path34506-3"
|
||||
sodipodi:cx="51"
|
||||
sodipodi:cy="30"
|
||||
sodipodi:rx="42"
|
||||
sodipodi:ry="16"
|
||||
d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
|
||||
sodipodi:start="3.1415927"
|
||||
sodipodi:end="6.2831853"
|
||||
transform="matrix(1,0,0,1.5625,432.08632,515.15977)"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
90
data/theme/running-indicator.svg
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="119.97824"
|
||||
height="119.97824"
|
||||
id="svg7355"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs7357">
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36429"
|
||||
id="radialGradient7461"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
|
||||
cx="47.878681"
|
||||
cy="171.25"
|
||||
fx="47.878681"
|
||||
fy="171.25"
|
||||
r="37" />
|
||||
<linearGradient
|
||||
id="linearGradient36429">
|
||||
<stop
|
||||
id="stop36431"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36433"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient36471"
|
||||
id="radialGradient7463"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
||||
cx="49.067139"
|
||||
cy="242.50381"
|
||||
fx="49.067139"
|
||||
fy="242.50381"
|
||||
r="37.00671" />
|
||||
<linearGradient
|
||||
id="linearGradient36471">
|
||||
<stop
|
||||
id="stop36473"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop36475"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="37.00671"
|
||||
fy="242.50381"
|
||||
fx="49.067139"
|
||||
cy="242.50381"
|
||||
cx="49.067139"
|
||||
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient7488"
|
||||
xlink:href="#linearGradient36471" />
|
||||
</defs>
|
||||
<g
|
||||
id="layer1"
|
||||
transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g30864"
|
||||
transform="translate(255.223,70.118091)">
|
||||
<rect
|
||||
ry="3.5996203"
|
||||
rx="3.5996203"
|
||||
y="98"
|
||||
x="11"
|
||||
height="74"
|
||||
width="74"
|
||||
id="rect14000"
|
||||
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
id="rect34520"
|
||||
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
|
||||
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
@ -2,13 +2,51 @@
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070">
|
||||
id="svg25070"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-down.svg">
|
||||
<metadata
|
||||
id="metadata3353">
|
||||
<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>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="718"
|
||||
inkscape:window-height="480"
|
||||
id="namedview3351"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6979167"
|
||||
inkscape:cx="48"
|
||||
inkscape:cy="48"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg25070" />
|
||||
<defs
|
||||
id="defs25072">
|
||||
<linearGradient
|
||||
@ -288,7 +326,7 @@
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
transform="translate(0,48)"
|
||||
transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
|
||||
id="layer1">
|
||||
<g
|
||||
transform="matrix(-2,0,0,2,-97.2497,-374.967)"
|
||||
@ -297,35 +335,42 @@
|
||||
<path
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
|
||||
transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
|
||||
id="path3165-7-3-1"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
|
||||
id="path3165-7-3-1-9"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="96" height="96" id="svg25070" version="1.1" inkscape:version="0.47 r22583" sodipodi:docname="dark-arrow-larger.svg">
|
||||
<defs id="defs25072">
|
||||
<inkscape:perspective sodipodi:type="inkscape:persp3d" inkscape:vp_x="0 : 24 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="48 : 24 : 1" inkscape:persp3d-origin="24 : 16 : 1" id="perspective25078"/>
|
||||
<inkscape:perspective id="perspective24985" 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"/>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4034-0-4" id="linearGradient24957" gradientUnits="userSpaceOnUse" gradientTransform="translate(6)" x1="-86.552246" y1="185.439" x2="-83.37072" y2="197.31261"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4034-0-4">
|
||||
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4036-5-7"/>
|
||||
<stop style="stop-color: rgb(186, 189, 182); stop-opacity: 1;" offset="1" id="stop4038-9-6"/>
|
||||
</linearGradient>
|
||||
<filter id="filter24765" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix24767" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix24769" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4632-1-3-9-3-2" id="linearGradient24955" gradientUnits="userSpaceOnUse" gradientTransform="translate(-5)" x1="-74.520325" y1="169.06032" x2="-74.520325" y2="205.94189"/>
|
||||
<linearGradient id="linearGradient4632-1-3-9-3-2">
|
||||
<stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4634-1-8-3-9-0"/>
|
||||
<stop id="stop4636-1-9-9-8-8" offset="0.0274937" style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"/>
|
||||
<stop id="stop4638-8-3-9-6-6" offset="0.274937" style="stop-color: rgb(242, 242, 242); stop-opacity: 1;"/>
|
||||
<stop id="stop4640-8-5-7-8-9" offset="0.38707438" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4642-5-41-9-6-9" offset="0.66528589" style="stop-color: rgb(217, 218, 216); stop-opacity: 1;"/>
|
||||
<stop id="stop4644-5-2-7-9-2" offset="0.76745707" style="stop-color: rgb(223, 224, 221); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(240, 240, 240); stop-opacity: 1;" offset="1" id="stop4646-3-2-3-7-3"/>
|
||||
</linearGradient>
|
||||
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-1" id="radialGradient24959" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
|
||||
<linearGradient id="linearGradient4869-4-1">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-2"/>
|
||||
<stop id="stop4879-7-4" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4877-6-1" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-0"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25011" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25013" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25015" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-0" id="radialGradient24961" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
|
||||
<linearGradient id="linearGradient4869-4-0">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-8"/>
|
||||
<stop id="stop4879-7-5" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
|
||||
<stop id="stop4877-6-5" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
|
||||
<stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-4"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25023" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25025" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25027" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941" id="linearGradient24963" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4941">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943"/>
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25033" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25035" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25037" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<linearGradient inkscape:collect="always" xlink:href="#linearGradient4941-7" id="linearGradient24965" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
|
||||
<linearGradient inkscape:collect="always" id="linearGradient4941-7">
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943-2"/>
|
||||
<stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945-5"/>
|
||||
</linearGradient>
|
||||
<filter id="filter25043" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25045" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25047" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="filter25049" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25051" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25053" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="filter25055" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix id="feColorMatrix25057" type="saturate" values="1" result="fbSourceGraphic"/>
|
||||
<feColorMatrix id="feColorMatrix25059" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.8284271" inkscape:cx="48.631638" inkscape:cy="57.536221" inkscape:current-layer="layer1" showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" inkscape:window-width="1200" inkscape:window-height="851" inkscape:window-x="0" inkscape:window-y="52" inkscape:window-maximized="0"/>
|
||||
<metadata id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title/>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(0, 48)">
|
||||
<g id="g4030-1-8" transform="matrix(2, 0, 0, 2, 193.25, -374.967)" style="stroke: rgb(0, 0, 0); display: inline; stroke-opacity: 1;">
|
||||
<path sodipodi:nodetypes="ccc" id="path3165-7-3" d="m -72.5,173.5 -14,14 14,14" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 7; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;"/>
|
||||
</g>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 185.28, -623.176)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4-8" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 207.28, -623.176)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 166.846, -534.143)"/>
|
||||
<path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0-9" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 188.846, -534.143)"/>
|
||||
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; opacity: 0.35; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z" id="path3165-7-3-1" sodipodi:nodetypes="ccccscccsc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
|
||||
<path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-linecap: round; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383" id="path3165-7-3-1-9" sodipodi:nodetypes="ccccccc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 13 KiB |
447
data/theme/ws-switch-arrow-up.svg
Normal file
@ -0,0 +1,447 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="96"
|
||||
height="96"
|
||||
id="svg25070"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.0 r9654"
|
||||
sodipodi:docname="ws-switch-arrow-up.svg">
|
||||
<defs
|
||||
id="defs25072">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 24 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="48 : 24 : 1"
|
||||
inkscape:persp3d-origin="24 : 16 : 1"
|
||||
id="perspective25078" />
|
||||
<inkscape:perspective
|
||||
id="perspective24985"
|
||||
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" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4034-0-4"
|
||||
id="linearGradient24957"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(6)"
|
||||
x1="-86.552246"
|
||||
y1="185.439"
|
||||
x2="-83.37072"
|
||||
y2="197.31261" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4034-0-4">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4036-5-7" />
|
||||
<stop
|
||||
style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4038-9-6" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter24765"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24767"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix24769"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4632-1-3-9-3-2"
|
||||
id="linearGradient24955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-5)"
|
||||
x1="-74.520325"
|
||||
y1="169.06032"
|
||||
x2="-74.520325"
|
||||
y2="205.94189" />
|
||||
<linearGradient
|
||||
id="linearGradient4632-1-3-9-3-2">
|
||||
<stop
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4634-1-8-3-9-0" />
|
||||
<stop
|
||||
id="stop4636-1-9-9-8-8"
|
||||
offset="0.0274937"
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4638-8-3-9-6-6"
|
||||
offset="0.274937"
|
||||
style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4640-8-5-7-8-9"
|
||||
offset="0.38707438"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4642-5-41-9-6-9"
|
||||
offset="0.66528589"
|
||||
style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4644-5-2-7-9-2"
|
||||
offset="0.76745707"
|
||||
style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4646-3-2-3-7-3" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-1"
|
||||
id="radialGradient24959"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-1">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-2" />
|
||||
<stop
|
||||
id="stop4879-7-4"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-1"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-0" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25011"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25013"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25015"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4869-4-0"
|
||||
id="radialGradient24961"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
|
||||
cx="-33.412369"
|
||||
cy="185.74171"
|
||||
fx="-33.412369"
|
||||
fy="185.74171"
|
||||
r="2.3554697" />
|
||||
<linearGradient
|
||||
id="linearGradient4869-4-0">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4871-6-8" />
|
||||
<stop
|
||||
id="stop4879-7-5"
|
||||
offset="0.31807542"
|
||||
style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
|
||||
<stop
|
||||
id="stop4877-6-5"
|
||||
offset="0.74691135"
|
||||
style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
|
||||
<stop
|
||||
style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
|
||||
offset="1"
|
||||
id="stop4873-1-4" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25023"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25025"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25027"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941"
|
||||
id="linearGradient24963"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25033"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25035"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25037"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4941-7"
|
||||
id="linearGradient24965"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-39.858727"
|
||||
y1="184.61784"
|
||||
x2="-38.244785"
|
||||
y2="188.84898" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4941-7">
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
|
||||
offset="0"
|
||||
id="stop4943-2" />
|
||||
<stop
|
||||
style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
|
||||
offset="1"
|
||||
id="stop4945-5" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
id="filter25043"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25045"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25047"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25049"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25051"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25053"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter25055"
|
||||
inkscape:label="Invert"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
inkscape:menu="Color"
|
||||
inkscape:menu-tooltip="Invert colors"
|
||||
color-interpolation-filters="sRGB">
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25057"
|
||||
type="saturate"
|
||||
values="1"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix25059"
|
||||
in="fbSourceGraphic"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="-12.356322"
|
||||
inkscape:cy="57.536221"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1200"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata25075">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(0, 48)">
|
||||
<g
|
||||
id="g3181"
|
||||
transform="matrix(0,1,-1,0,48.0003,-48)">
|
||||
<g
|
||||
style="stroke:#000000;stroke-opacity:1;display:inline"
|
||||
transform="matrix(2,0,0,2,193.25,-374.967)"
|
||||
id="g4030-1-8">
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
d="m -72.5,173.5 -14,14 14,14"
|
||||
id="path3165-7-3"
|
||||
sodipodi:nodetypes="ccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-8"
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
|
||||
d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
|
||||
sodipodi:ry="2.09375"
|
||||
sodipodi:rx="2.09375"
|
||||
sodipodi:cy="186.40625"
|
||||
sodipodi:cx="-38.59375"
|
||||
id="path4050-2-7-9-4-0-9"
|
||||
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccscccsc"
|
||||
id="path3165-7-3-1"
|
||||
d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(2,0,0,2,-586,-765.967)"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path3165-7-3-1-9"
|
||||
d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 16 KiB |
@ -33,7 +33,7 @@
|
||||
<foaf:Person>
|
||||
<foaf:name>Colin Walters</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:walters@verbum.org" />
|
||||
<gnome:userid>cwalters</gnome:userid>
|
||||
<gnome:userid>walters</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
|
@ -2,26 +2,29 @@
|
||||
jsdir = $(pkgdatadir)/js
|
||||
|
||||
nobase_dist_js_DATA = \
|
||||
misc/config.js \
|
||||
misc/docInfo.js \
|
||||
misc/fileUtils.js \
|
||||
misc/format.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/history.js \
|
||||
misc/params.js \
|
||||
misc/telepathy.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
prefs/clockPreferences.js \
|
||||
ui/altTab.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/chrome.js \
|
||||
ui/ctrlAltTab.js \
|
||||
ui/dash.js \
|
||||
ui/dateMenu.js \
|
||||
ui/dnd.js \
|
||||
ui/docDisplay.js \
|
||||
ui/endSessionDialog.js \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/genericDisplay.js \
|
||||
ui/iconGrid.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
@ -30,25 +33,33 @@ nobase_dist_js_DATA = \
|
||||
ui/magnifierDBus.js \
|
||||
ui/main.js \
|
||||
ui/messageTray.js \
|
||||
ui/modalDialog.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/overview.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/placeDisplay.js \
|
||||
ui/polkitAuthenticationAgent.js \
|
||||
ui/popupMenu.js \
|
||||
ui/runDialog.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
ui/searchDisplay.js \
|
||||
ui/shellDBus.js \
|
||||
ui/statusIconDispatcher.js \
|
||||
ui/statusMenu.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/power.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/telepathyClient.js \
|
||||
ui/tweener.js \
|
||||
ui/viewSelector.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
ui/windowManager.js \
|
||||
ui/workspace.js \
|
||||
ui/workspaceThumbnail.js \
|
||||
ui/workspacesView.js \
|
||||
ui/workspaceSwitcherPopup.js
|
||||
ui/workspaceSwitcherPopup.js \
|
||||
ui/xdndHandler.js
|
||||
|
10
js/misc/config.js.in
Normal file
@ -0,0 +1,10 @@
|
||||
/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
|
||||
/* The name of this package (not localized) */
|
||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
/* The version of this package */
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* The version of GJS we're linking to */
|
||||
const GJS_VERSION = '@GJS_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
|
@ -29,8 +29,8 @@ DocInfo.prototype = {
|
||||
return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
|
||||
},
|
||||
|
||||
launch : function() {
|
||||
Shell.DocSystem.get_default().open(this.recentInfo);
|
||||
launch : function(workspaceIndex) {
|
||||
Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
|
||||
},
|
||||
|
||||
matchTerms: function(terms) {
|
||||
@ -60,8 +60,7 @@ function getDocManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
|
||||
* which conform to the GenericDisplay item API.
|
||||
* DocManager wraps the DocSystem, primarily to expose DocInfo objects.
|
||||
*/
|
||||
function DocManager() {
|
||||
this._init();
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PresenceIface = {
|
||||
name: 'org.gnome.SessionManager.Presence',
|
||||
@ -43,3 +44,60 @@ Presence.prototype = {
|
||||
}
|
||||
};
|
||||
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
|
||||
|
||||
// Note inhibitors are immutable objects, so they don't
|
||||
// change at runtime (changes always come in the form
|
||||
// of new inhibitors)
|
||||
const InhibitorIface = {
|
||||
name: 'org.gnome.SessionManager.Inhibitor',
|
||||
properties: [{ name: 'app_id',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'client_id',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'reason',
|
||||
signature: 's',
|
||||
access: 'readonly' },
|
||||
{ name: 'flags',
|
||||
signature: 'u',
|
||||
access: 'readonly' },
|
||||
{ name: 'toplevel_xid',
|
||||
signature: 'u',
|
||||
access: 'readonly' },
|
||||
{ name: 'cookie',
|
||||
signature: 'u',
|
||||
access: 'readonly' }],
|
||||
};
|
||||
|
||||
function Inhibitor(objectPath) {
|
||||
this._init(objectPath);
|
||||
}
|
||||
|
||||
Inhibitor.prototype = {
|
||||
_init: function(objectPath) {
|
||||
DBus.session.proxifyObject(this,
|
||||
"org.gnome.SessionManager",
|
||||
objectPath);
|
||||
this.isLoaded = false;
|
||||
this._loadingPropertiesCount = InhibitorIface.properties.length;
|
||||
for (let i = 0; i < InhibitorIface.properties.length; i++) {
|
||||
let propertyName = InhibitorIface.properties[i].name;
|
||||
this.GetRemote(propertyName, Lang.bind(this,
|
||||
function(value, exception) {
|
||||
if (exception)
|
||||
return;
|
||||
|
||||
this[propertyName] = value;
|
||||
this._loadingPropertiesCount--;
|
||||
|
||||
if (this._loadingPropertiesCount == 0) {
|
||||
this.isLoaded = true;
|
||||
this.emit("is-loaded");
|
||||
}
|
||||
}));
|
||||
}
|
||||
},
|
||||
};
|
||||
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
|
||||
Signals.addSignalMethods(Inhibitor.prototype);
|
||||
|
72
js/misc/history.js
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const DEFAULT_LIMIT = 512;
|
||||
|
||||
function HistoryManager(settings_key) {
|
||||
this._init(settings_key);
|
||||
}
|
||||
|
||||
HistoryManager.prototype = {
|
||||
_init: function(settings_key, limit) {
|
||||
this._limit = limit || DEFAULT_LIMIT;
|
||||
this._key = settings_key;
|
||||
this._history = global.settings.get_strv(settings_key);
|
||||
this._historyIndex = -1;
|
||||
|
||||
global.settings.connect('changed::' + settings_key,
|
||||
Lang.bind(this, this._historyChanged));
|
||||
},
|
||||
|
||||
_historyChanged: function() {
|
||||
this._history = global.settings.get_strv(this._key);
|
||||
this._historyIndex = this._history.length;
|
||||
},
|
||||
|
||||
prevItem: function(text) {
|
||||
this._setHistory(this._historyIndex--, text);
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
nextItem: function(text) {
|
||||
this._setHistory(this._historyIndex++, text);
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
lastItem: function() {
|
||||
this._historyIndex = this._history.length;
|
||||
return this._indexChanged();
|
||||
},
|
||||
|
||||
addItem: function(input) {
|
||||
if (this._history.length == 0 ||
|
||||
this._history[this._history.length - 1] != input) {
|
||||
|
||||
this._history.push(input);
|
||||
this._save();
|
||||
}
|
||||
},
|
||||
|
||||
_indexChanged: function() {
|
||||
let current = this._history[this._historyIndex] || '';
|
||||
this.emit('changed', current);
|
||||
return current;
|
||||
},
|
||||
|
||||
_setHistory: function(index, text) {
|
||||
this._historyIndex = Math.max(this._historyIndex, 0);
|
||||
this._historyIndex = Math.min(this._historyIndex, this._history.length);
|
||||
|
||||
if (text)
|
||||
this._history[index] = text;
|
||||
},
|
||||
|
||||
_save: function() {
|
||||
if (this._history.length > this._limit)
|
||||
this._history.splice(0, this._history.length - this._key);
|
||||
global.settings.set_strv(this._key, this._history);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(HistoryManager.prototype);
|
@ -1,359 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const DBus = imports.dbus;
|
||||
|
||||
// D-Bus utils
|
||||
function nameToPath(name) {
|
||||
return '/' + name.replace(/\./g, '/');
|
||||
};
|
||||
|
||||
function pathToName(path) {
|
||||
if (path[0] != '/')
|
||||
throw new Error('not a D-Bus path: ' + path);
|
||||
return path.substr(1).replace(/\//g, '.');
|
||||
};
|
||||
|
||||
// This is tp_escape_as_identifier() from telepathy-glib
|
||||
function escapeAsIdentifier(name) {
|
||||
if (!name)
|
||||
return '_';
|
||||
|
||||
// first char is replaced with _XX if it's non-alpha,
|
||||
// later chars are replaced with _XX if they're non-alphanumeric
|
||||
if (name.length == 1) {
|
||||
return name.replace(/[^a-zA-Z]/, _hexEscape);
|
||||
} else {
|
||||
return (name[0].replace(/[^a-zA-Z]/, _hexEscape) +
|
||||
name.substring(1).replace(/[^a-zA-Z0-9]/g, _hexEscape));
|
||||
}
|
||||
}
|
||||
|
||||
function _hexEscape(ch) {
|
||||
return '_' + ch.charCodeAt(0).toString(16);
|
||||
}
|
||||
|
||||
// Telepathy D-Bus interface definitions. Note that most of these are
|
||||
// incomplete, and only cover the methods/properties/signals that
|
||||
// we're currently using.
|
||||
|
||||
const TELEPATHY = 'org.freedesktop.Telepathy';
|
||||
|
||||
const CLIENT_NAME = TELEPATHY + '.Client';
|
||||
const ClientIface = {
|
||||
name: CLIENT_NAME,
|
||||
properties: [
|
||||
{ name: 'Interfaces',
|
||||
signature: 'as',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_APPROVER_NAME = TELEPATHY + '.Client.Approver';
|
||||
const ClientApproverIface = {
|
||||
name: CLIENT_APPROVER_NAME,
|
||||
methods: [
|
||||
{ name: 'AddDispatchOperation',
|
||||
inSignature: 'a(oa{sv})oa{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'ApproverChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_HANDLER_NAME = TELEPATHY + '.Client.Handler';
|
||||
const ClientHandlerIface = {
|
||||
name: CLIENT_HANDLER_NAME,
|
||||
methods: [
|
||||
{ name: 'HandleChannels',
|
||||
inSignature: 'ooa(oa{sv})aota{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'HandlerChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CLIENT_OBSERVER_NAME = TELEPATHY + '.Client.Observer';
|
||||
const ClientObserverIface = {
|
||||
name: CLIENT_OBSERVER_NAME,
|
||||
methods: [
|
||||
{ name: 'ObserveChannels',
|
||||
inSignature: 'ooa(oa{sv})oaoa{sv}',
|
||||
outSignature: '' }
|
||||
],
|
||||
properties: [
|
||||
{ name: 'ObserverChannelFilter',
|
||||
signature: 'aa{sv}',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
|
||||
const CHANNEL_DISPATCH_OPERATION_NAME = TELEPATHY + '.ChannelDispatchOperation';
|
||||
const ChannelDispatchOperationIface = {
|
||||
name: CHANNEL_DISPATCH_OPERATION_NAME,
|
||||
methods: [
|
||||
{ name: 'HandleWith',
|
||||
inSignature: 's',
|
||||
outSignature: '' },
|
||||
{ name: 'Claim',
|
||||
inSignature: '',
|
||||
outSignature: '' }
|
||||
]
|
||||
};
|
||||
let ChannelDispatchOperation = DBus.makeProxyClass(ChannelDispatchOperationIface);
|
||||
|
||||
const CONNECTION_NAME = TELEPATHY + '.Connection';
|
||||
const ConnectionIface = {
|
||||
name: CONNECTION_NAME,
|
||||
signals: [
|
||||
{ name: 'StatusChanged',
|
||||
inSignature: 'uu' }
|
||||
]
|
||||
};
|
||||
let Connection = DBus.makeProxyClass(ConnectionIface);
|
||||
|
||||
const ConnectionStatus = {
|
||||
CONNECTED: 0,
|
||||
CONNECTING: 1,
|
||||
DISCONNECTED: 2
|
||||
};
|
||||
|
||||
const CONNECTION_ALIASING_NAME = CONNECTION_NAME + '.Interface.Aliasing';
|
||||
const ConnectionAliasingIface = {
|
||||
name: CONNECTION_ALIASING_NAME,
|
||||
methods: [
|
||||
{ name: 'RequestAliases',
|
||||
inSignature: 'au',
|
||||
outSignature: 'as'
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AliasesChanged',
|
||||
inSignature: 'a(us)' }
|
||||
]
|
||||
};
|
||||
let ConnectionAliasing = DBus.makeProxyClass(ConnectionAliasingIface);
|
||||
|
||||
const CONNECTION_AVATARS_NAME = CONNECTION_NAME + '.Interface.Avatars';
|
||||
const ConnectionAvatarsIface = {
|
||||
name: CONNECTION_AVATARS_NAME,
|
||||
methods: [
|
||||
{ name: 'GetKnownAvatarTokens',
|
||||
inSignature: 'au',
|
||||
outSignature: 'a{us}'
|
||||
},
|
||||
{ name: 'RequestAvatars',
|
||||
inSignature: 'au',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AvatarRetrieved',
|
||||
inSignature: 'usays'
|
||||
},
|
||||
{ name: 'AvatarUpdated',
|
||||
inSignature: 'us'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionAvatars = DBus.makeProxyClass(ConnectionAvatarsIface);
|
||||
|
||||
const CONNECTION_CONTACTS_NAME = CONNECTION_NAME + '.Interface.Contacts';
|
||||
const ConnectionContactsIface = {
|
||||
name: CONNECTION_CONTACTS_NAME,
|
||||
methods: [
|
||||
{ name: 'GetContactAttributes',
|
||||
inSignature: 'auasb',
|
||||
outSignature: 'a{ua{sv}}'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionContacts = DBus.makeProxyClass(ConnectionContactsIface);
|
||||
|
||||
const CONNECTION_REQUESTS_NAME = CONNECTION_NAME + '.Interface.Requests';
|
||||
const ConnectionRequestsIface = {
|
||||
name: CONNECTION_REQUESTS_NAME,
|
||||
methods: [
|
||||
{ name: 'CreateChannel',
|
||||
inSignature: 'a{sv}',
|
||||
outSignature: 'oa{sv}'
|
||||
},
|
||||
{ name: 'EnsureChannel',
|
||||
inSignature: 'a{sv}',
|
||||
outSignature: 'boa{sv}'
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{ name: 'Channels',
|
||||
signature: 'a(oa{sv})',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'NewChannels',
|
||||
inSignature: 'a(oa{sv})'
|
||||
},
|
||||
{ name: 'ChannelClosed',
|
||||
inSignature: 'o'
|
||||
}
|
||||
]
|
||||
};
|
||||
let ConnectionRequests = DBus.makeProxyClass(ConnectionRequestsIface);
|
||||
|
||||
const CONNECTION_SIMPLE_PRESENCE_NAME = CONNECTION_NAME + '.Interface.SimplePresence';
|
||||
const ConnectionSimplePresenceIface = {
|
||||
name: CONNECTION_SIMPLE_PRESENCE_NAME,
|
||||
methods: [
|
||||
{ name: 'SetPresence',
|
||||
inSignature: 'ss'
|
||||
},
|
||||
{ name: 'GetPresences',
|
||||
inSignature: 'au',
|
||||
outSignature: 'a{u(uss)}'
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'PresencesChanged',
|
||||
inSignature: 'a{u(uss)}' }
|
||||
]
|
||||
};
|
||||
let ConnectionSimplePresence = DBus.makeProxyClass(ConnectionSimplePresenceIface);
|
||||
|
||||
const ConnectionPresenceType = {
|
||||
UNSET: 0,
|
||||
OFFLINE: 1,
|
||||
AVAILABLE: 2,
|
||||
AWAY: 3,
|
||||
EXTENDED_AWAY: 4,
|
||||
HIDDEN: 5,
|
||||
BUSY: 6,
|
||||
UNKNOWN: 7,
|
||||
ERROR: 8
|
||||
};
|
||||
|
||||
const HandleType = {
|
||||
NONE: 0,
|
||||
CONTACT: 1,
|
||||
ROOM: 2,
|
||||
LIST: 3,
|
||||
GROUP: 4
|
||||
};
|
||||
|
||||
const CHANNEL_NAME = TELEPATHY + '.Channel';
|
||||
const ChannelIface = {
|
||||
name: CHANNEL_NAME,
|
||||
signals: [
|
||||
{ name: 'Closed',
|
||||
inSignature: '' }
|
||||
]
|
||||
};
|
||||
let Channel = DBus.makeProxyClass(ChannelIface);
|
||||
|
||||
const CHANNEL_TEXT_NAME = CHANNEL_NAME + '.Type.Text';
|
||||
const ChannelTextIface = {
|
||||
name: CHANNEL_TEXT_NAME,
|
||||
methods: [
|
||||
{ name: 'ListPendingMessages',
|
||||
inSignature: 'b',
|
||||
outSignature: 'a(uuuuus)'
|
||||
},
|
||||
{ name: 'AcknowledgePendingMessages',
|
||||
inSignature: 'au',
|
||||
outSignature: ''
|
||||
},
|
||||
{ name: 'Send',
|
||||
inSignature: 'us',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [
|
||||
{ name: 'Received',
|
||||
inSignature: 'uuuuus' }
|
||||
]
|
||||
};
|
||||
let ChannelText = DBus.makeProxyClass(ChannelTextIface);
|
||||
|
||||
const ChannelTextMessageType = {
|
||||
NORMAL: 0,
|
||||
ACTION: 1,
|
||||
NOTICE: 2,
|
||||
AUTO_REPLY: 3,
|
||||
DELIVERY_REPORT: 4
|
||||
};
|
||||
|
||||
const CHANNEL_CONTACT_LIST_NAME = CHANNEL_NAME + '.Type.ContactList';
|
||||
// There is no interface associated with ContactList; it's just a
|
||||
// special kind of Channel.Interface.Group
|
||||
|
||||
const CHANNEL_GROUP_NAME = CHANNEL_NAME + '.Interface.Group';
|
||||
const ChannelGroupIface = {
|
||||
name: CHANNEL_GROUP_NAME,
|
||||
properties: [
|
||||
{ name: 'Members',
|
||||
signature: 'au',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'MembersChanged',
|
||||
inSignature: 'sauauauauuu' }
|
||||
]
|
||||
};
|
||||
let ChannelGroup = DBus.makeProxyClass(ChannelGroupIface);
|
||||
|
||||
const ACCOUNT_MANAGER_NAME = TELEPATHY + '.AccountManager';
|
||||
const AccountManagerIface = {
|
||||
name: ACCOUNT_MANAGER_NAME,
|
||||
properties: [
|
||||
{ name: 'ValidAccounts',
|
||||
signature: 'ao',
|
||||
access: 'read' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'AccountValidityChanged',
|
||||
inSignature: 'ob' }
|
||||
]
|
||||
};
|
||||
let AccountManager = DBus.makeProxyClass(AccountManagerIface);
|
||||
|
||||
const ACCOUNT_NAME = TELEPATHY + '.Account';
|
||||
const AccountIface = {
|
||||
name: ACCOUNT_NAME,
|
||||
properties: [
|
||||
{ name: 'Connection',
|
||||
signature: 'o',
|
||||
access: 'read' }
|
||||
]
|
||||
};
|
||||
let Account = DBus.makeProxyClass(AccountIface);
|
||||
|
||||
const CHANNEL_DISPATCHER_NAME = TELEPATHY + '.ChannelDispatcher';
|
||||
const ChannelDispatcherIface = {
|
||||
name: CHANNEL_DISPATCHER_NAME,
|
||||
methods: [
|
||||
{ name: 'EnsureChannel',
|
||||
inSignature: 'oa{sv}xs',
|
||||
outSignature: 'o' }
|
||||
]
|
||||
};
|
||||
let ChannelDispatcher = DBus.makeProxyClass(ChannelDispatcherIface);
|
||||
|
||||
const CHANNEL_REQUEST_NAME = TELEPATHY + '.ChannelRequest';
|
||||
const ChannelRequestIface = {
|
||||
name: CHANNEL_REQUEST_NAME,
|
||||
methods: [
|
||||
{ name: 'Proceed',
|
||||
inSignature: '',
|
||||
outSignature: '' }
|
||||
],
|
||||
signals: [
|
||||
{ name: 'Failed',
|
||||
signature: 'ss' },
|
||||
{ name: 'Succeeded',
|
||||
signature: '' }
|
||||
]
|
||||
};
|
||||
let ChannelRequest = DBus.makeProxyClass(ChannelRequestIface);
|
180
js/misc/util.js
Normal file
@ -0,0 +1,180 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
|
||||
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
|
||||
|
||||
// findUrls:
|
||||
// @str: string to find URLs in
|
||||
//
|
||||
// Searches @str for URLs and returns an array of objects with %url
|
||||
// properties showing the matched URL string, and %pos properties indicating
|
||||
// the position within @str where the URL was found.
|
||||
//
|
||||
// Return value: the list of match objects, as described above
|
||||
function findUrls(str) {
|
||||
let res = [], match;
|
||||
while ((match = _urlRegexp.exec(str)))
|
||||
res.push({ url: match[0], pos: match.index });
|
||||
return res;
|
||||
}
|
||||
|
||||
// spawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background, handling any errors that occur
|
||||
// when trying to start the program.
|
||||
function spawn(argv) {
|
||||
try {
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
}
|
||||
}
|
||||
|
||||
// spawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background, handling any errors that
|
||||
// occur when trying to parse or start the program.
|
||||
function spawnCommandLine(command_line) {
|
||||
try {
|
||||
let [success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(command_line, err);
|
||||
}
|
||||
}
|
||||
|
||||
// spawnDesktop:
|
||||
// @id: a desktop file ID
|
||||
//
|
||||
// Spawns the desktop file identified by @id using startup notification,
|
||||
// etc, handling any errors that occur when trying to find or start
|
||||
// the program.
|
||||
function spawnDesktop(id) {
|
||||
try {
|
||||
trySpawnDesktop(id);
|
||||
} catch (err) {
|
||||
_handleSpawnError(id, err);
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background. If launching @argv fails,
|
||||
// this will throw an error.
|
||||
function trySpawn(argv)
|
||||
{
|
||||
try {
|
||||
GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH,
|
||||
null, null);
|
||||
} catch (err) {
|
||||
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
|
||||
err.message = _("Command not found");
|
||||
} else {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||
// execute child process "foo" (No such file or directory)
|
||||
// We are only interested in the part in the parentheses. (And
|
||||
// we can't pattern match the text, since it gets localized.)
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background. If launching @command_line
|
||||
// fails, this will throw an error.
|
||||
function trySpawnCommandLine(command_line) {
|
||||
let success, argc, argv;
|
||||
|
||||
try {
|
||||
[success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
} catch (err) {
|
||||
// Replace "Error invoking GLib.shell_parse_argv: " with
|
||||
// something nicer
|
||||
err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n");
|
||||
throw err;
|
||||
}
|
||||
|
||||
trySpawn(argv);
|
||||
}
|
||||
|
||||
// trySpawnDesktop:
|
||||
// @id: a desktop file ID
|
||||
//
|
||||
// Spawns the desktop file identified by @id using startup notification.
|
||||
// On error, throws an exception.
|
||||
function trySpawnDesktop(id) {
|
||||
let app;
|
||||
|
||||
// shell_app_system_load_from_desktop_file() will end up returning
|
||||
// a stupid error message if the desktop file doesn't exist, but
|
||||
// that's the only case it returns an error for, so we just
|
||||
// substitute our own error in instead
|
||||
try {
|
||||
app = Shell.AppSystem.get_default().load_from_desktop_file(id + '.desktop');
|
||||
} catch (err) {
|
||||
throw new Error(_("No such application"));
|
||||
}
|
||||
|
||||
try {
|
||||
app.launch();
|
||||
} catch(err) {
|
||||
// see trySpawn
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function _handleSpawnError(command, err) {
|
||||
let title = _("Execution of '%s' failed:").format(command);
|
||||
|
||||
let source = new MessageTray.SystemNotificationSource();
|
||||
Main.messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, title, err.message);
|
||||
notification.setTransient(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
||||
// killall:
|
||||
// @processName: a process name
|
||||
//
|
||||
// Kills @processName. If no process with the given name is found,
|
||||
// this will fail silently.
|
||||
function killall(processName) {
|
||||
try {
|
||||
// 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 can match
|
||||
// '/usr/bin/processName' but not 'gedit processName.c' or
|
||||
// whatever...
|
||||
|
||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
|
||||
// It might be useful to return success/failure, but we'd need
|
||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
||||
// the current callers care, we don't bother.
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to kill ' + processName);
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Gettext = imports.gettext;
|
||||
|
||||
const FORMAT_KEY = 'format';
|
||||
const SHOW_DATE_KEY = 'show-date';
|
||||
const SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
|
||||
function ClockPreferences(uiFile) {
|
||||
this._init(uiFile);
|
||||
};
|
||||
|
||||
ClockPreferences.prototype = {
|
||||
_init: function(uiFile) {
|
||||
let builder = new Gtk.Builder();
|
||||
builder.add_from_file(uiFile);
|
||||
|
||||
this._dialog = builder.get_object('prefs-dialog');
|
||||
this._dialog.connect('response', Lang.bind(this, this._onResponse));
|
||||
|
||||
this._12hrRadio = builder.get_object('12hr_radio');
|
||||
this._24hrRadio = builder.get_object('24hr_radio');
|
||||
this._dateCheck = builder.get_object('date_check');
|
||||
this._secondsCheck = builder.get_object('seconds_check');
|
||||
|
||||
delete builder;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._notifyId = this._settings.connect('changed',
|
||||
Lang.bind(this,
|
||||
this._updateDialog));
|
||||
|
||||
this._12hrRadio.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
let format = this._12hrRadio.active ? '12-hour' : '24-hour';
|
||||
this._settings.set_string(FORMAT_KEY, format);
|
||||
}));
|
||||
this._dateCheck.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
this._settings.set_boolean(SHOW_DATE_KEY,
|
||||
this._dateCheck.active);
|
||||
}));
|
||||
this._secondsCheck.connect('toggled', Lang.bind(this,
|
||||
function() {
|
||||
this._settings.set_boolean(SHOW_SECONDS_KEY,
|
||||
this._secondsCheck.active);
|
||||
}));
|
||||
|
||||
this._updateDialog();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._dialog.show_all();
|
||||
},
|
||||
|
||||
_updateDialog: function() {
|
||||
let format = this._settings.get_string(FORMAT_KEY);
|
||||
this._12hrRadio.active = (format == "12-hour");
|
||||
this._24hrRadio.active = (format == "24-hour");
|
||||
|
||||
this._dateCheck.active = this._settings.get_boolean(SHOW_DATE_KEY);
|
||||
this._secondsCheck.active = this._settings.get_boolean(SHOW_SECONDS_KEY);
|
||||
},
|
||||
|
||||
_onResponse: function() {
|
||||
this._dialog.destroy();
|
||||
this._settings.disconnect(this._notifyId);
|
||||
this.emit('destroy');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ClockPreferences.prototype);
|
||||
|
||||
function main(params) {
|
||||
if ('progName' in params)
|
||||
GLib.set_prgname(params['progName']);
|
||||
if ('localeDir' in params)
|
||||
Gettext.bindtextdomain('gnome-shell', params['localeDir']);
|
||||
|
||||
Gtk.init(null, null);
|
||||
|
||||
let clockPrefs = new ClockPreferences(params['uiFile']);
|
||||
clockPrefs.connect('destroy',
|
||||
function() {
|
||||
Gtk.main_quit();
|
||||
});
|
||||
clockPrefs.show();
|
||||
|
||||
Gtk.main();
|
||||
}
|
238
js/ui/altTab.js
@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
@ -15,6 +16,8 @@ const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_SCROLL_TIME = 0.10; // seconds
|
||||
const POPUP_FADE_TIME = 0.1; // seconds
|
||||
|
||||
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
|
||||
|
||||
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
|
||||
|
||||
const THUMBNAIL_DEFAULT_SIZE = 256;
|
||||
@ -34,7 +37,8 @@ function AltTabPopup() {
|
||||
AltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
|
||||
reactive: true });
|
||||
reactive: true,
|
||||
visible: false });
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
@ -49,6 +53,8 @@ AltTabPopup.prototype = {
|
||||
this._thumbnailTimeoutId = 0;
|
||||
this._motionTimeoutId = 0;
|
||||
|
||||
this.thumbnailsVisible = false;
|
||||
|
||||
// Initially disable hover so we ignore the enter-event if
|
||||
// the switcher appears underneath the current pointer location
|
||||
this._disableHover();
|
||||
@ -115,7 +121,7 @@ AltTabPopup.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
show : function(backward) {
|
||||
show : function(backward, switch_group) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let apps = tracker.get_running_apps ('');
|
||||
|
||||
@ -126,13 +132,13 @@ AltTabPopup.prototype = {
|
||||
return false;
|
||||
this._haveModal = true;
|
||||
|
||||
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.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
|
||||
|
||||
this._appSwitcher = new AppSwitcher(apps);
|
||||
this._appSwitcher = new AppSwitcher(apps, this);
|
||||
this.actor.add_actor(this._appSwitcher.actor);
|
||||
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
|
||||
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
|
||||
@ -140,7 +146,16 @@ AltTabPopup.prototype = {
|
||||
this._appIcons = this._appSwitcher.icons;
|
||||
|
||||
// Make the initial selection
|
||||
if (this._appIcons.length == 1) {
|
||||
if (switch_group) {
|
||||
if (backward) {
|
||||
this._select(0, this._appIcons[0].cachedWindows.length - 1);
|
||||
} else {
|
||||
if (this._appIcons[0].cachedWindows.length > 1)
|
||||
this._select(0, 1);
|
||||
else
|
||||
this._select(0, 0);
|
||||
}
|
||||
} else 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);
|
||||
@ -212,41 +227,38 @@ AltTabPopup.prototype = {
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
// X allows servers to represent Shift+Tab in two different ways
|
||||
if (shift && keysym == Clutter.Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
let event_state = Shell.get_event_state(event);
|
||||
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
|
||||
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
|
||||
|
||||
this._disableHover();
|
||||
|
||||
if (keysym == Clutter.grave)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.asciitilde)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
if (action == Meta.KeyBindingAction.SWITCH_GROUP)
|
||||
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
|
||||
else if (keysym == Clutter.Escape)
|
||||
this.destroy();
|
||||
else if (this._thumbnailsFocused) {
|
||||
if (keysym == Clutter.Tab) {
|
||||
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||
this._select(this._nextApp());
|
||||
else
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
} else if (keysym == Clutter.ISO_Left_Tab) {
|
||||
if (this._currentWindow == 0 || this._currentWindow == -1)
|
||||
this._select(this._previousApp());
|
||||
else
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
} else if (keysym == Clutter.Left)
|
||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
||||
if (backwards) {
|
||||
if (this._currentWindow == 0 || this._currentWindow == -1)
|
||||
this._select(this._previousApp());
|
||||
else
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
} else {
|
||||
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
|
||||
this._select(this._nextApp());
|
||||
else
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
}
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._currentApp, this._previousWindow());
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._currentApp, this._nextWindow());
|
||||
else if (keysym == Clutter.Up)
|
||||
this._select(this._currentApp, null, true);
|
||||
} else {
|
||||
if (keysym == Clutter.Tab)
|
||||
this._select(this._nextApp());
|
||||
else if (keysym == Clutter.ISO_Left_Tab)
|
||||
this._select(this._previousApp());
|
||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
|
||||
this._select(backwards ? this._previousApp() : this._nextApp());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previousApp());
|
||||
else if (keysym == Clutter.Right)
|
||||
@ -365,15 +377,18 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
if (this.actor.visible) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
} else
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
@ -383,11 +398,6 @@ AltTabPopup.prototype = {
|
||||
if (this._thumbnails)
|
||||
this._destroyThumbnails();
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
global.stage.disconnect(this._keyReleaseEventId);
|
||||
|
||||
if (this._motionTimeoutId != 0)
|
||||
Mainloop.source_remove(this._motionTimeoutId);
|
||||
if (this._thumbnailTimeoutId != 0)
|
||||
@ -458,11 +468,15 @@ AltTabPopup.prototype = {
|
||||
},
|
||||
|
||||
_destroyThumbnails : function() {
|
||||
Tweener.addTween(this._thumbnails.actor,
|
||||
let thumbnailsActor = this._thumbnails.actor;
|
||||
Tweener.addTween(thumbnailsActor,
|
||||
{ opacity: 0,
|
||||
time: THUMBNAIL_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() { this.destroy(); }
|
||||
onComplete: Lang.bind(this, function() {
|
||||
thumbnailsActor.destroy();
|
||||
this.thumbnailsVisible = false;
|
||||
})
|
||||
});
|
||||
this._thumbnails = null;
|
||||
},
|
||||
@ -478,7 +492,8 @@ AltTabPopup.prototype = {
|
||||
Tweener.addTween(this._thumbnails.actor,
|
||||
{ opacity: 255,
|
||||
time: THUMBNAIL_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -519,16 +534,11 @@ SwitcherList.prototype = {
|
||||
this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._leftArrow.connect('repaint', Lang.bind(this,
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
|
||||
}));
|
||||
|
||||
function() { _drawArrow(this._leftArrow, St.Side.LEFT); }));
|
||||
this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
|
||||
pseudo_class: 'highlighted' });
|
||||
this._rightArrow.connect('repaint', Lang.bind(this,
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.RIGHT);
|
||||
}));
|
||||
function() { _drawArrow(this._rightArrow, St.Side.RIGHT); }));
|
||||
|
||||
this.actor.add_actor(this._leftArrow);
|
||||
this.actor.add_actor(this._rightArrow);
|
||||
@ -585,23 +595,27 @@ SwitcherList.prototype = {
|
||||
},
|
||||
|
||||
addItem : function(item) {
|
||||
let bbox = new St.Clickable({ style_class: 'item-box',
|
||||
reactive: true });
|
||||
let bbox = new St.Button({ 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);
|
||||
}));
|
||||
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
|
||||
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
|
||||
|
||||
this._items.push(bbox);
|
||||
},
|
||||
|
||||
_onItemClicked: function (index) {
|
||||
this._itemActivated(index);
|
||||
},
|
||||
|
||||
_onItemEnter: function (index) {
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
addSeparator: function () {
|
||||
let box = new St.Bin({ style_class: 'separator' });
|
||||
this._separator = box;
|
||||
@ -609,16 +623,18 @@ SwitcherList.prototype = {
|
||||
},
|
||||
|
||||
highlight: function(index, justOutline) {
|
||||
if (this._highlighted != -1)
|
||||
this._items[this._highlighted].style_class = 'item-box';
|
||||
if (this._highlighted != -1) {
|
||||
this._items[this._highlighted].remove_style_pseudo_class('outlined');
|
||||
this._items[this._highlighted].remove_style_pseudo_class('selected');
|
||||
}
|
||||
|
||||
this._highlighted = index;
|
||||
|
||||
if (this._highlighted != -1) {
|
||||
if (justOutline)
|
||||
this._items[this._highlighted].style_class = 'outlined-item-box';
|
||||
this._items[this._highlighted].add_style_pseudo_class('outlined');
|
||||
else
|
||||
this._items[this._highlighted].style_class = 'selected-item-box';
|
||||
this._items[this._highlighted].add_style_pseudo_class('selected');
|
||||
}
|
||||
|
||||
let monitor = global.get_primary_monitor();
|
||||
@ -818,14 +834,14 @@ AppIcon.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppSwitcher(apps) {
|
||||
this._init(apps);
|
||||
function AppSwitcher(apps, altTabPopup) {
|
||||
this._init(apps, altTabPopup);
|
||||
}
|
||||
|
||||
AppSwitcher.prototype = {
|
||||
__proto__ : SwitcherList.prototype,
|
||||
|
||||
_init : function(apps) {
|
||||
_init : function(apps, altTabPopup) {
|
||||
SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
// Construct the AppIcons, sort by time, add to the popup
|
||||
@ -857,6 +873,8 @@ AppSwitcher.prototype = {
|
||||
|
||||
this._curApp = -1;
|
||||
this._iconSize = 0;
|
||||
this._altTabPopup = altTabPopup;
|
||||
this._mouseTimeOutId = 0;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
@ -864,17 +882,19 @@ AppSwitcher.prototype = {
|
||||
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
|
||||
j++;
|
||||
}
|
||||
let iconPadding = this._items[j].get_theme_node().get_horizontal_padding();
|
||||
let themeNode = this._items[j].get_theme_node();
|
||||
let iconPadding = themeNode.get_horizontal_padding();
|
||||
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
|
||||
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||
let iconSpacing = iconNaturalHeight + iconPadding;
|
||||
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
if (this._separator)
|
||||
totalSpacing += this._separator.width + this._list.spacing;
|
||||
|
||||
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||
let focus = global.get_focus_monitor();
|
||||
let primary = global.get_primary_monitor();
|
||||
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
||||
let availWidth = focus.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||
let height = 0;
|
||||
|
||||
for(let i = 0; i < iconSizes.length; i++) {
|
||||
@ -919,6 +939,29 @@ AppSwitcher.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// We override SwitcherList's _onItemEnter method to delay
|
||||
// activation when the thumbnail list is open
|
||||
_onItemEnter: function (index) {
|
||||
if (this._mouseTimeOutId != 0)
|
||||
Mainloop.source_remove(this._mouseTimeOutId);
|
||||
if (this._altTabPopup.thumbnailsVisible) {
|
||||
this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT,
|
||||
Lang.bind(this, function () {
|
||||
this._enterItem(index);
|
||||
this._mouseTimeOutId = 0;
|
||||
return false;
|
||||
}));
|
||||
} else
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
_enterItem: function(index) {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
if (this._items[index].contains(pickedActor))
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
// 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
|
||||
@ -951,10 +994,7 @@ AppSwitcher.prototype = {
|
||||
|
||||
let n = this._arrows.length;
|
||||
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
|
||||
arrow.connect('repaint', Lang.bind(this,
|
||||
function (area) {
|
||||
Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
|
||||
}));
|
||||
arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); });
|
||||
this._list.add_actor(arrow);
|
||||
this._arrows.push(arrow);
|
||||
|
||||
@ -1040,6 +1080,9 @@ ThumbnailList.prototype = {
|
||||
|
||||
for (let i = 0; i < this._thumbnailBins.length; i++) {
|
||||
let mutterWindow = this._windows[i].get_compositor_private();
|
||||
if (!mutterWindow)
|
||||
continue;
|
||||
|
||||
let windowTexture = mutterWindow.get_texture ();
|
||||
let [width, height] = windowTexture.get_size();
|
||||
let scale = Math.min(1.0, THUMBNAIL_DEFAULT_SIZE / width, availHeight / height);
|
||||
@ -1057,3 +1100,46 @@ ThumbnailList.prototype = {
|
||||
this._thumbnailBins = new Array();
|
||||
}
|
||||
};
|
||||
|
||||
function _drawArrow(area, side) {
|
||||
let themeNode = area.get_theme_node();
|
||||
let borderColor = themeNode.get_border_color(side);
|
||||
let bodyColor = themeNode.get_foreground_color();
|
||||
|
||||
let [width, height] = area.get_surface_size ();
|
||||
let cr = area.get_context();
|
||||
|
||||
cr.setLineWidth(1.0);
|
||||
Clutter.cairo_set_source_color(cr, borderColor);
|
||||
|
||||
switch (side) {
|
||||
case St.Side.TOP:
|
||||
cr.moveTo(0, height);
|
||||
cr.lineTo(Math.floor(width * 0.5), 0);
|
||||
cr.lineTo(width, height);
|
||||
break;
|
||||
|
||||
case St.Side.BOTTOM:
|
||||
cr.moveTo(width, 0);
|
||||
cr.lineTo(Math.floor(width * 0.5), height);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
cr.moveTo(width, height);
|
||||
cr.lineTo(0, Math.floor(height * 0.5));
|
||||
cr.lineTo(width, 0);
|
||||
break;
|
||||
|
||||
case St.Side.RIGHT:
|
||||
cr.moveTo(0, 0);
|
||||
cr.lineTo(width, Math.floor(height * 0.5));
|
||||
cr.lineTo(0, height);
|
||||
break;
|
||||
}
|
||||
|
||||
cr.strokePreserve();
|
||||
|
||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||
cr.fill();
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const WELL_MAX_COLUMNS = 16;
|
||||
const WELL_MAX_SEARCH_ROWS = 1;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
|
||||
function AlphabeticalView() {
|
||||
@ -31,10 +29,32 @@ function AlphabeticalView() {
|
||||
|
||||
AlphabeticalView.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this._grid = new IconGrid.IconGrid();
|
||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
|
||||
this._filterApp = null;
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.START,
|
||||
vfade: true });
|
||||
this.actor.add_actor(box);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
|
||||
// Reset scroll on mapping
|
||||
adjustment.value = 0;
|
||||
}));
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
@ -42,20 +62,24 @@ AlphabeticalView.prototype = {
|
||||
this._apps = [];
|
||||
},
|
||||
|
||||
_addApp: function(app) {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id()));
|
||||
appIcon.connect('launching', Lang.bind(this, function() {
|
||||
this.emit('launching');
|
||||
}));
|
||||
appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
|
||||
this.emit('drag-begin');
|
||||
}));
|
||||
_addApp: function(appInfo) {
|
||||
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
|
||||
|
||||
this._grid.addItem(appIcon.actor);
|
||||
|
||||
appIcon._appInfo = appInfo;
|
||||
if (this._filterApp && !this._filterApp(appInfo))
|
||||
appIcon.actor.hide();
|
||||
|
||||
this._apps.push(appIcon);
|
||||
},
|
||||
|
||||
setFilter: function(filter) {
|
||||
this._filterApp = filter;
|
||||
for (let i = 0; i < this._apps.length; i++)
|
||||
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
let ids = [];
|
||||
for (let i in apps)
|
||||
@ -72,8 +96,6 @@ AlphabeticalView.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AlphabeticalView.prototype);
|
||||
|
||||
function ViewByCategories() {
|
||||
this._init();
|
||||
}
|
||||
@ -81,59 +103,86 @@ function ViewByCategories() {
|
||||
ViewByCategories.prototype = {
|
||||
_init: function() {
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this.actor = new St.BoxLayout({ style_class: 'all-app' });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._view = new AlphabeticalView();
|
||||
|
||||
this._filters = new St.BoxLayout({ vertical: true });
|
||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
// Always select the "All" filter when switching to the app view
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (this.actor.mapped && this._allFilter)
|
||||
this._selectCategory(-1);
|
||||
}));
|
||||
|
||||
this._sections = [];
|
||||
},
|
||||
|
||||
_updateSections: function(apps) {
|
||||
this._removeAll();
|
||||
_selectCategory: function(num) {
|
||||
if (num != -1)
|
||||
this._allFilter.remove_style_pseudo_class('selected');
|
||||
else
|
||||
this._allFilter.add_style_pseudo_class('selected');
|
||||
|
||||
let sections = this._appSystem.get_sections();
|
||||
if (!sections)
|
||||
return;
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
if (i) {
|
||||
let actor = new St.Bin({ style_class: 'app-section-divider' });
|
||||
let divider = new St.Bin({ style_class: 'app-section-divider-container',
|
||||
child: actor,
|
||||
x_fill: true });
|
||||
this._view.setFilter(Lang.bind(this, function(app) {
|
||||
if (num == -1)
|
||||
return true;
|
||||
return this._sections[num].name == app.get_section();
|
||||
}));
|
||||
|
||||
this.actor.add(divider, { y_fill: false, expand: true });
|
||||
}
|
||||
let _apps = apps.filter(function(app) {
|
||||
return app.get_section() == sections[i];
|
||||
});
|
||||
this._sections[i] = { view: new AlphabeticalView(),
|
||||
apps: _apps,
|
||||
name: sections[i] };
|
||||
this._sections[i].view.connect('launching', Lang.bind(this, function() {
|
||||
this.emit('launching');
|
||||
}));
|
||||
this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
|
||||
this.emit('drag-begin');
|
||||
}));
|
||||
this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
|
||||
for (let i = 0; i < this._sections.length; i++) {
|
||||
if (i == num)
|
||||
this._sections[i].filterActor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._sections[i].filterActor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_addFilter: function(name, num) {
|
||||
let button = new St.Button({ label: name,
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START });
|
||||
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(num);
|
||||
}));
|
||||
|
||||
if (num != -1)
|
||||
this._sections[num] = { filterActor: button,
|
||||
name: name };
|
||||
else
|
||||
this._allFilter = button;
|
||||
},
|
||||
|
||||
_removeAll: function() {
|
||||
this.actor.destroy_children();
|
||||
this._sections.forEach(function (section) { section.view.disconnectAll(); });
|
||||
|
||||
this._sections = [];
|
||||
this._filters.destroy_children();
|
||||
},
|
||||
|
||||
refresh: function(apps) {
|
||||
this._updateSections(apps);
|
||||
for (let i = 0; i < this._sections.length; i++) {
|
||||
this._sections[i].view.refresh(this._sections[i].apps);
|
||||
}
|
||||
this._removeAll();
|
||||
|
||||
let sections = this._appSystem.get_sections();
|
||||
this._apps = apps;
|
||||
this._view.refresh(apps);
|
||||
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addFilter(_("All"), -1);
|
||||
|
||||
if (!sections)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < sections.length; i++)
|
||||
this._addFilter(sections[i], i);
|
||||
|
||||
this._selectCategory(-1);
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(ViewByCategories.prototype);
|
||||
|
||||
/* This class represents a display containing a collection of application items.
|
||||
* The applications are sorted based on their name.
|
||||
*/
|
||||
@ -148,25 +197,8 @@ AllAppDisplay.prototype = {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
}));
|
||||
|
||||
let bin = new St.BoxLayout({ style_class: 'all-app-controls-panel',
|
||||
reactive: true });
|
||||
this.actor = new St.BoxLayout({ style_class: 'all-app', vertical: true });
|
||||
this.actor.hide();
|
||||
|
||||
let view = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
style_class: 'all-app-scroll-view',
|
||||
vshadows: true });
|
||||
this._scrollView = view;
|
||||
this.actor.add(bin);
|
||||
this.actor.add(view, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
this._appView = new ViewByCategories();
|
||||
this._appView.connect('launching', Lang.bind(this, this.close));
|
||||
this._appView.connect('drag-begin', Lang.bind(this, this.close));
|
||||
this._scrollView.add_actor(this._appView.actor);
|
||||
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
},
|
||||
@ -177,99 +209,6 @@ AllAppDisplay.prototype = {
|
||||
});
|
||||
|
||||
this._appView.refresh(apps);
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.actor.visible) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: Overview.PANE_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.hide();
|
||||
this.emit('open-state-changed',
|
||||
this.actor.visible);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.show();
|
||||
this.emit('open-state-changed', this.actor.visible);
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: Overview.PANE_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (!this.actor.visible)
|
||||
return;
|
||||
this.toggle();
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AllAppDisplay.prototype);
|
||||
|
||||
function AppSearchResultDisplay(provider) {
|
||||
this._init(provider);
|
||||
}
|
||||
|
||||
AppSearchResultDisplay.prototype = {
|
||||
__proto__: Search.SearchResultDisplay.prototype,
|
||||
|
||||
_init: function (provider) {
|
||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: WELL_MAX_SEARCH_ROWS });
|
||||
this.actor = new St.Bin({ name: 'dashAppSearchResults',
|
||||
x_align: St.Align.START });
|
||||
this.actor.set_child(this._grid.actor);
|
||||
},
|
||||
|
||||
renderResults: function(results, terms) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let maxItems = WELL_MAX_SEARCH_ROWS * WELL_MAX_COLUMNS;
|
||||
for (let i = 0; i < results.length && i < maxItems; i++) {
|
||||
let result = results[i];
|
||||
let app = appSys.get_app(result);
|
||||
let display = new AppWellIcon(app);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._grid.removeAll();
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
},
|
||||
|
||||
selectIndex: function (index) {
|
||||
let nVisible = this.getVisibleResultCount();
|
||||
if (this.selectionIndex >= 0) {
|
||||
let prevActor = this._grid.getItemAtIndex(this.selectionIndex);
|
||||
prevActor._delegate.setSelected(false);
|
||||
}
|
||||
this.selectionIndex = -1;
|
||||
if (index >= nVisible)
|
||||
return false;
|
||||
else if (index < 0)
|
||||
return false;
|
||||
let targetActor = this._grid.getItemAtIndex(index);
|
||||
targetActor._delegate.setSelected(true);
|
||||
this.selectionIndex = index;
|
||||
return true;
|
||||
},
|
||||
|
||||
activateSelected: function() {
|
||||
if (this.selectionIndex < 0)
|
||||
return;
|
||||
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
|
||||
this.provider.activateResult(targetActor._delegate.app.get_id());
|
||||
}
|
||||
};
|
||||
|
||||
@ -294,14 +233,20 @@ BaseAppSearchProvider.prototype = {
|
||||
'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let app = this._appSys.get_app(id);
|
||||
app.activate();
|
||||
app.activate(params.workspace ? params.workspace.index() : -1);
|
||||
},
|
||||
|
||||
dragActivateResult: function(id) {
|
||||
dragActivateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let app = this._appSys.get_app(id);
|
||||
app.open_new_window();
|
||||
app.open_new_window(params.workspace ? params.workspace.get_index() : -1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -324,12 +269,10 @@ AppSearchProvider.prototype = {
|
||||
return this._appSys.subsearch(false, previousResults, terms);
|
||||
},
|
||||
|
||||
createResultContainerActor: function () {
|
||||
return new AppSearchResultDisplay(this);
|
||||
},
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
return new AppIcon(resultMeta.id);
|
||||
let app = this._appSys.get_app(resultMeta['id']);
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
},
|
||||
|
||||
expandSearch: function(terms) {
|
||||
@ -363,19 +306,21 @@ PrefsSearchProvider.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppIcon(app) {
|
||||
this._init(app);
|
||||
function AppIcon(app, params) {
|
||||
this._init(app, params);
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
__proto__: IconGrid.BaseIcon.prototype,
|
||||
|
||||
_init : function(app) {
|
||||
_init : function(app, params) {
|
||||
this.app = app;
|
||||
|
||||
let label = this.app.get_name();
|
||||
|
||||
IconGrid.BaseIcon.prototype._init.call(this, label);
|
||||
IconGrid.BaseIcon.prototype._init.call(this,
|
||||
label,
|
||||
params);
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
@ -383,22 +328,24 @@ AppIcon.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function AppWellIcon(app) {
|
||||
this._init(app);
|
||||
function AppWellIcon(app, iconParams) {
|
||||
this._init(app, iconParams);
|
||||
}
|
||||
|
||||
AppWellIcon.prototype = {
|
||||
_init : function(app) {
|
||||
_init : function(app, iconParams) {
|
||||
this.app = app;
|
||||
this.actor = new St.Clickable({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._icon = new AppIcon(app);
|
||||
this.actor.set_child(this._icon.actor);
|
||||
this.icon = new AppIcon(app, iconParams);
|
||||
this.actor.set_child(this.icon.actor);
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this._menu = null;
|
||||
@ -415,7 +362,6 @@ AppWellIcon.prototype = {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._menuTimeoutId = 0;
|
||||
@ -454,25 +400,25 @@ AppWellIcon.prototype = {
|
||||
Lang.bind(this, function() {
|
||||
this.popupMenu();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onClicked: function(actor, event) {
|
||||
this._removeMenuTimeout();
|
||||
|
||||
let button = event.get_button();
|
||||
if (button == 1) {
|
||||
this._onActivate(event);
|
||||
} else if (button == 2) {
|
||||
let newWorkspace = Main.overview.workspaces.addWorkspace();
|
||||
if (newWorkspace != null) {
|
||||
newWorkspace.activate(global.get_current_time());
|
||||
this.emit('launching');
|
||||
this.app.open_new_window();
|
||||
Main.overview.hide();
|
||||
}
|
||||
} else if (button == 3) {
|
||||
this.popupMenu();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onClicked: function(actor, button) {
|
||||
this._removeMenuTimeout();
|
||||
|
||||
if (button == 1) {
|
||||
this._onActivate(Clutter.get_current_event());
|
||||
} else if (button == 2) {
|
||||
// Last workspace is always empty
|
||||
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
|
||||
launchWorkspace.activate(global.get_current_time());
|
||||
this.emit('launching');
|
||||
this.app.open_new_window(-1);
|
||||
Main.overview.hide();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -487,18 +433,12 @@ AppWellIcon.prototype = {
|
||||
|
||||
if (!this._menu) {
|
||||
this._menu = new AppIconMenu(this);
|
||||
this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
|
||||
this.highlightWindow(window);
|
||||
}));
|
||||
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
|
||||
this.activateWindow(window);
|
||||
}));
|
||||
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
|
||||
if (isPoppedUp) {
|
||||
this._onMenuPoppedUp();
|
||||
} else {
|
||||
if (!isPoppedUp)
|
||||
this._onMenuPoppedDown();
|
||||
}
|
||||
}));
|
||||
|
||||
this._menuManager.addMenu(this._menu);
|
||||
@ -509,53 +449,16 @@ AppWellIcon.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
highlightWindow: function(metaWindow) {
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._getRunning())
|
||||
return;
|
||||
Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
|
||||
},
|
||||
|
||||
activateWindow: function(metaWindow) {
|
||||
if (metaWindow) {
|
||||
this._didActivateWindow = true;
|
||||
Main.activateWindow(metaWindow);
|
||||
} else {
|
||||
Main.overview.hide();
|
||||
}
|
||||
},
|
||||
|
||||
setSelected: function (isSelected) {
|
||||
this._selected = isSelected;
|
||||
if (this._selected)
|
||||
this.actor.add_style_class_name('selected');
|
||||
else
|
||||
this.actor.remove_style_class_name('selected');
|
||||
},
|
||||
|
||||
_onMenuPoppedUp: function() {
|
||||
if (this._getRunning()) {
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
|
||||
this._setWindowSelection = true;
|
||||
this._didActivateWindow = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onMenuPoppedDown: function() {
|
||||
this.actor.sync_hover();
|
||||
|
||||
if (this._didActivateWindow)
|
||||
return;
|
||||
if (!this._setWindowSelection)
|
||||
return;
|
||||
|
||||
Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
|
||||
this._setWindowSelection = false;
|
||||
},
|
||||
|
||||
_getRunning: function() {
|
||||
return this.app.state != Shell.AppState.STOPPED;
|
||||
},
|
||||
|
||||
_onActivate: function (event) {
|
||||
@ -564,30 +467,28 @@ AppWellIcon.prototype = {
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window();
|
||||
this.app.open_new_window(-1);
|
||||
} else {
|
||||
this.app.activate();
|
||||
this.app.activate(-1);
|
||||
}
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
// called by this._menuManager when it has the grab
|
||||
menuEventFilter: function(event) {
|
||||
return this._menu.menuEventFilter(event);
|
||||
},
|
||||
shellWorkspaceLaunch : function(params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
shellWorkspaceLaunch : function() {
|
||||
this.app.open_new_window();
|
||||
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.app.create_icon_texture(this._icon.iconSize);
|
||||
return this.app.create_icon_texture(Main.overview.dash.iconSize);
|
||||
},
|
||||
|
||||
// Returns the original actor that should align with the actor
|
||||
// we show as the item is being dragged.
|
||||
getDragActorSource: function() {
|
||||
return this._icon.icon;
|
||||
return this.icon.icon;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(AppWellIcon.prototype);
|
||||
@ -600,11 +501,14 @@ AppIconMenu.prototype = {
|
||||
__proto__: PopupMenu.PopupMenu.prototype,
|
||||
|
||||
_init: function(source) {
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
|
||||
let side = St.Side.LEFT;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
side = St.Side.RIGHT;
|
||||
|
||||
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
|
||||
|
||||
this._source = source;
|
||||
|
||||
this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
|
||||
this.connect('activate', Lang.bind(this, this._onActivate));
|
||||
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||
|
||||
@ -651,7 +555,6 @@ AppIconMenu.prototype = {
|
||||
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
|
||||
: _("Add to Favorites"));
|
||||
|
||||
this._highlightedItem = null;
|
||||
},
|
||||
|
||||
_appendSeparator: function () {
|
||||
@ -675,74 +578,16 @@ AppIconMenu.prototype = {
|
||||
if (open) {
|
||||
this.emit('popup', true);
|
||||
} else {
|
||||
this._updateHighlight(null);
|
||||
this.emit('popup', false);
|
||||
}
|
||||
},
|
||||
|
||||
// called by this._menuManager when it has the grab
|
||||
menuEventFilter: function(event) {
|
||||
let eventType = event.type();
|
||||
|
||||
// Check if the user is interacting with a window representation
|
||||
// rather than interacting with the menu
|
||||
|
||||
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this.emit('activate-window', metaWindow);
|
||||
} else if (eventType == Clutter.EventType.ENTER) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this._selectMenuItemForWindow(metaWindow, true);
|
||||
} else if (eventType == Clutter.EventType.LEAVE) {
|
||||
let metaWindow = this._findMetaWindowForActor(event.get_source());
|
||||
if (metaWindow)
|
||||
this._selectMenuItemForWindow(metaWindow, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_findMetaWindowForActor: function (actor) {
|
||||
if (actor._delegate instanceof Workspace.WindowClone)
|
||||
return actor._delegate.metaWindow;
|
||||
else if (actor.get_meta_window)
|
||||
return actor.get_meta_window();
|
||||
return null;
|
||||
},
|
||||
|
||||
_updateHighlight: function (item) {
|
||||
if (this._highlightedItem)
|
||||
this.emit('highlight-window', null);
|
||||
this._highlightedItem = item;
|
||||
if (this._highlightedItem) {
|
||||
let window = this._highlightedItem._window;
|
||||
if (window)
|
||||
this.emit('highlight-window', window);
|
||||
}
|
||||
},
|
||||
|
||||
_selectMenuItemForWindow: function (metaWindow, selected) {
|
||||
let items = this.getMenuItems();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let menuMetaWindow = item._window;
|
||||
if (menuMetaWindow == metaWindow)
|
||||
item.setActive(selected);
|
||||
}
|
||||
},
|
||||
|
||||
_onActiveChanged: function (menu, child) {
|
||||
this._updateHighlight(child);
|
||||
},
|
||||
|
||||
_onActivate: function (actor, child) {
|
||||
if (child._window) {
|
||||
let metaWindow = child._window;
|
||||
this.emit('activate-window', metaWindow);
|
||||
} else if (child == this._newWindowMenuItem) {
|
||||
this._source.app.open_new_window();
|
||||
this._source.app.open_new_window(-1);
|
||||
this.emit('activate-window', null);
|
||||
} else if (child == this._toggleFavoriteMenuItem) {
|
||||
let favs = AppFavorites.getAppFavorites();
|
||||
@ -756,135 +601,3 @@ AppIconMenu.prototype = {
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(AppIconMenu.prototype);
|
||||
|
||||
function AppWell() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AppWell.prototype = {
|
||||
_init : function() {
|
||||
this._placeholderText = null;
|
||||
this._menus = [];
|
||||
this._menuDisplays = [];
|
||||
|
||||
this._favorites = [];
|
||||
|
||||
this._grid = new IconGrid.IconGrid();
|
||||
this.actor = this._grid.actor;
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._tracker = Shell.WindowTracker.get_default();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
||||
},
|
||||
|
||||
_appIdListToHash: function(apps) {
|
||||
let ids = {};
|
||||
for (let i = 0; i < apps.length; i++)
|
||||
ids[apps[i].get_id()] = apps[i];
|
||||
return ids;
|
||||
},
|
||||
|
||||
_queueRedisplay: function () {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_redisplay: function () {
|
||||
this._grid.removeAll();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
/* hardcode here pending some design about how exactly desktop contexts behave */
|
||||
let contextId = '';
|
||||
|
||||
let running = this._tracker.get_running_apps(contextId);
|
||||
let runningIds = this._appIdListToHash(running);
|
||||
|
||||
let nFavorites = 0;
|
||||
for (let id in favorites) {
|
||||
let app = favorites[id];
|
||||
let display = new AppWellIcon(app);
|
||||
this._grid.addItem(display.actor);
|
||||
nFavorites++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < running.length; i++) {
|
||||
let app = running[i];
|
||||
if (app.get_id() in favorites)
|
||||
continue;
|
||||
let display = new AppWellIcon(app);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
if (this._placeholderText) {
|
||||
this._placeholderText.destroy();
|
||||
this._placeholderText = null;
|
||||
}
|
||||
|
||||
if (running.length == 0 && nFavorites == 0) {
|
||||
this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
|
||||
this.actor.add_actor(this._placeholderText);
|
||||
}
|
||||
},
|
||||
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppWellIcon)
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
else if (source instanceof Workspace.WindowClone)
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_transient())
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
let id = app.get_id();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
let srcIsFavorite = (id in favorites);
|
||||
|
||||
if (srcIsFavorite)
|
||||
return DND.DragMotionResult.NO_DROP;
|
||||
|
||||
return DND.DragMotionResult.COPY_DROP;
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
let app = null;
|
||||
if (source instanceof AppWellIcon) {
|
||||
app = this._appSystem.get_app(source.getId());
|
||||
} else if (source instanceof Workspace.WindowClone) {
|
||||
app = this._tracker.get_window_app(source.metaWindow);
|
||||
}
|
||||
|
||||
// Don't allow favoriting of transient apps
|
||||
if (app == null || app.is_transient()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let id = app.get_id();
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
||||
|
||||
let srcIsFavorite = (id in favorites);
|
||||
|
||||
if (srcIsFavorite) {
|
||||
return false;
|
||||
} else {
|
||||
Mainloop.idle_add(Lang.bind(this, function () {
|
||||
AppFavorites.getAppFavorites().addFavorite(id);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(AppWell.prototype);
|
||||
|
@ -63,7 +63,7 @@ AppFavorites.prototype = {
|
||||
return appId in this._favorites;
|
||||
},
|
||||
|
||||
_addFavorite: function(appId) {
|
||||
_addFavorite: function(appId, pos) {
|
||||
if (appId in this._favorites)
|
||||
return false;
|
||||
|
||||
@ -73,23 +73,35 @@ AppFavorites.prototype = {
|
||||
return false;
|
||||
|
||||
let ids = this._getIds();
|
||||
ids.push(appId);
|
||||
if (pos == -1)
|
||||
ids.push(appId);
|
||||
else
|
||||
ids.splice(pos, 0, appId);
|
||||
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
|
||||
this._favorites[appId] = app;
|
||||
return true;
|
||||
},
|
||||
|
||||
addFavorite: function(appId) {
|
||||
if (!this._addFavorite(appId))
|
||||
addFavoriteAtPos: function(appId, pos) {
|
||||
if (!this._addFavorite(appId, pos))
|
||||
return;
|
||||
|
||||
let app = Shell.AppSystem.get_default().get_app(appId);
|
||||
|
||||
Main.overview.infoBar.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
|
||||
this._removeFavorite(appId);
|
||||
}));
|
||||
},
|
||||
|
||||
addFavorite: function(appId) {
|
||||
this.addFavoriteAtPos(appId, -1);
|
||||
},
|
||||
|
||||
moveFavoriteToPos: function(appId, pos) {
|
||||
this._removeFavorite(appId);
|
||||
this._addFavorite(appId, pos);
|
||||
},
|
||||
|
||||
_removeFavorite: function(appId) {
|
||||
if (!appId in this._favorites)
|
||||
return false;
|
||||
@ -100,13 +112,16 @@ AppFavorites.prototype = {
|
||||
},
|
||||
|
||||
removeFavorite: function(appId) {
|
||||
let ids = this._getIds();
|
||||
let pos = ids.indexOf(appId);
|
||||
|
||||
let app = this._favorites[appId];
|
||||
if (!this._removeFavorite(appId))
|
||||
return;
|
||||
|
||||
Main.overview.infoBar.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
|
||||
Lang.bind(this, function () {
|
||||
this._addFavorite(appId);
|
||||
this._addFavorite(appId, pos);
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ const POPUP_ANIMATION_TIME = 0.15;
|
||||
|
||||
/**
|
||||
* BoxPointer:
|
||||
* @side: A St.Side type; currently only St.Side.TOP is implemented
|
||||
* @side: side to draw the arrow on
|
||||
* @binProperties: Properties to set on contained bin
|
||||
*
|
||||
* An actor which displays a triangle "arrow" pointing to a given
|
||||
@ -42,7 +42,7 @@ BoxPointer.prototype = {
|
||||
this.bin.raise(this._border);
|
||||
},
|
||||
|
||||
animateAppear: function(onComplete) {
|
||||
show: function(animate, onComplete) {
|
||||
let x = this.actor.x;
|
||||
let y = this.actor.y;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
@ -51,19 +51,21 @@ BoxPointer.prototype = {
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.actor.y -= rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this.actor.y += rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this.actor.x -= rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this.actor.x += rise;
|
||||
break;
|
||||
if (animate) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this.actor.y -= rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this.actor.y += rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this.actor.x -= rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this.actor.x += rise;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
@ -74,7 +76,7 @@ BoxPointer.prototype = {
|
||||
time: POPUP_ANIMATION_TIME });
|
||||
},
|
||||
|
||||
animateDisappear: function(onComplete) {
|
||||
hide: function(animate, onComplete) {
|
||||
let x = this.actor.x;
|
||||
let y = this.actor.y;
|
||||
let originalX = this.actor.x;
|
||||
@ -82,19 +84,21 @@ BoxPointer.prototype = {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
y += rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
y -= rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
x += rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
x -= rise;
|
||||
break;
|
||||
if (animate) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
y += rise;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
y -= rise;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
x += rise;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
x -= rise;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Tweener.addTween(this.actor, { opacity: 0,
|
||||
@ -185,10 +189,8 @@ BoxPointer.prototype = {
|
||||
let halfBorder = borderWidth / 2;
|
||||
let halfBase = Math.floor(base/2);
|
||||
|
||||
let borderColor = new Clutter.Color();
|
||||
themeNode.get_color('-arrow-border-color', borderColor);
|
||||
let backgroundColor = new Clutter.Color();
|
||||
themeNode.get_color('-arrow-background-color', backgroundColor);
|
||||
let borderColor = themeNode.get_color('-arrow-border-color');
|
||||
let backgroundColor = themeNode.get_color('-arrow-background-color');
|
||||
|
||||
let [width, height] = area.get_surface_size();
|
||||
let [boxWidth, boxHeight] = [width, height];
|
||||
@ -208,46 +210,88 @@ BoxPointer.prototype = {
|
||||
cr.translate(rise, 0);
|
||||
}
|
||||
|
||||
cr.moveTo(borderRadius, halfBorder);
|
||||
let [x1, y1] = [halfBorder, halfBorder];
|
||||
let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
|
||||
|
||||
cr.moveTo(x1 + borderRadius, y1);
|
||||
if (this._arrowSide == St.Side.TOP) {
|
||||
cr.lineTo(this._arrowOrigin - halfBase, halfBorder);
|
||||
cr.lineTo(this._arrowOrigin, halfBorder - rise);
|
||||
cr.lineTo(this._arrowOrigin + halfBase, halfBorder);
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y1);
|
||||
cr.lineTo(this._arrowOrigin, y1 - rise);
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y1);
|
||||
}
|
||||
}
|
||||
cr.lineTo(boxWidth - borderRadius, halfBorder);
|
||||
|
||||
cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius,
|
||||
cr.lineTo(x2 - borderRadius, y1);
|
||||
|
||||
// top-right corner
|
||||
cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
|
||||
if (this._arrowSide == St.Side.RIGHT) {
|
||||
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(boxWidth - halfBorder + rise, this._arrowOrigin);
|
||||
cr.lineTo(boxWidth - halfBorder, this._arrowOrigin + halfBase);
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
} else {
|
||||
cr.lineTo(x2, this._arrowOrigin - halfBase);
|
||||
cr.lineTo(x2 + rise, this._arrowOrigin);
|
||||
cr.lineTo(x2, this._arrowOrigin + halfBase);
|
||||
}
|
||||
}
|
||||
cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius);
|
||||
|
||||
cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
|
||||
cr.lineTo(x2, y2 - borderRadius);
|
||||
|
||||
// bottom-right corner
|
||||
cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
|
||||
0, Math.PI/2);
|
||||
|
||||
if (this._arrowSide == St.Side.BOTTOM) {
|
||||
cr.lineTo(this._arrowOrigin + halfBase, boxHeight - halfBorder);
|
||||
cr.lineTo(this._arrowOrigin, boxHeight - halfBorder + rise);
|
||||
cr.lineTo(this._arrowOrigin - halfBase, boxHeight - halfBorder);
|
||||
if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
} else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
|
||||
} else {
|
||||
cr.lineTo(this._arrowOrigin + halfBase, y2);
|
||||
cr.lineTo(this._arrowOrigin, y2 + rise);
|
||||
cr.lineTo(this._arrowOrigin - halfBase, y2);
|
||||
}
|
||||
}
|
||||
cr.lineTo(borderRadius, boxHeight - halfBorder);
|
||||
|
||||
cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
|
||||
cr.lineTo(x1 + borderRadius, y2);
|
||||
|
||||
// bottom-left corner
|
||||
cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
|
||||
Math.PI/2, Math.PI);
|
||||
|
||||
if (this._arrowSide == St.Side.LEFT) {
|
||||
cr.lineTo(halfBorder, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(halfBorder - rise, this._arrowOrigin);
|
||||
cr.lineTo(halfBorder, this._arrowOrigin - halfBase);
|
||||
if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
} else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
|
||||
} else {
|
||||
cr.lineTo(x1, this._arrowOrigin + halfBase);
|
||||
cr.lineTo(x1 - rise, this._arrowOrigin);
|
||||
cr.lineTo(x1, this._arrowOrigin - halfBase);
|
||||
}
|
||||
}
|
||||
cr.lineTo(halfBorder, borderRadius);
|
||||
|
||||
cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius,
|
||||
cr.lineTo(x1, y1 + borderRadius);
|
||||
|
||||
// top-left corner
|
||||
cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
|
||||
Math.PI, 3*Math.PI/2);
|
||||
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
@ -263,16 +307,24 @@ BoxPointer.prototype = {
|
||||
this.actor.show();
|
||||
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
|
||||
|
||||
let sourceCenterX = sourceX + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) / 2;
|
||||
let sourceCenterY = sourceY + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) / 2;
|
||||
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
|
||||
|
||||
// We also want to keep it onscreen, and separated from the
|
||||
// edge by the same distance as the main part of the box is
|
||||
// separated from its sourceActor
|
||||
let primary = global.get_primary_monitor();
|
||||
let arrowRise = this.actor.get_theme_node().get_length('-arrow-rise');
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||
let arrowBase = themeNode.get_length('-arrow-base');
|
||||
let borderRadius = themeNode.get_length('-arrow-border-radius');
|
||||
let margin = (4 * borderRadius + borderWidth + arrowBase);
|
||||
let halfMargin = margin / 2;
|
||||
|
||||
let resX, resY;
|
||||
|
||||
@ -296,42 +348,21 @@ BoxPointer.prototype = {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
case St.Side.BOTTOM:
|
||||
switch (alignment) {
|
||||
case St.Align.START:
|
||||
resX = sourceX;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
resX = sourceX - Math.floor((natWidth - sourceWidth) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
resX = sourceX - (natWidth - sourceWidth);
|
||||
break;
|
||||
}
|
||||
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
|
||||
|
||||
resX = Math.min(resX, primary.x + primary.width - natWidth - arrowRise - gap);
|
||||
resX = Math.max(resX, primary.x);
|
||||
|
||||
this.setArrowOrigin((sourceX - resX) + Math.floor(sourceWidth / 2));
|
||||
resX = Math.max(resX, primary.x + 10);
|
||||
resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
|
||||
this.setArrowOrigin(sourceCenterX - resX);
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
case St.Side.RIGHT:
|
||||
switch (alignment) {
|
||||
case St.Align.START:
|
||||
resY = sourceY;
|
||||
break;
|
||||
case St.Align.MIDDLE:
|
||||
resY = sourceY - Math.floor((natHeight - sourceHeight) / 2);
|
||||
break;
|
||||
case St.Align.END:
|
||||
resY = sourceY - (natHeight - sourceHeight);
|
||||
break;
|
||||
}
|
||||
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
|
||||
|
||||
resY = Math.min(resY, primary.y + primary.height - natHeight - arrowRise - gap);
|
||||
resY = Math.max(resY, primary.y);
|
||||
resY = Math.max(resY, primary.y + 10);
|
||||
resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
|
||||
|
||||
this.setArrowOrigin((sourceY - resY) + Math.floor(sourceHeight / 2));
|
||||
this.setArrowOrigin(sourceCenterY - resY);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -4,19 +4,83 @@ const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const C_ = Gettext.pgettext;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
function _sameDay(dateA, dateB) {
|
||||
return (dateA.getDate() == dateB.getDate() &&
|
||||
dateA.getMonth() == dateB.getMonth() &&
|
||||
dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
function _sameYear(dateA, dateB) {
|
||||
return (dateA.getYear() == dateB.getYear());
|
||||
}
|
||||
|
||||
/* TODO: maybe needs config - right now we assume that Saturday and
|
||||
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
|
||||
* Monday there)
|
||||
*/
|
||||
function _isWorkDay(date) {
|
||||
return date.getDay() != 0 && date.getDay() != 6;
|
||||
}
|
||||
|
||||
function _getBeginningOfDay(date) {
|
||||
let ret = new Date(date.getTime());
|
||||
ret.setHours(0);
|
||||
ret.setMinutes(0);
|
||||
ret.setSeconds(0);
|
||||
ret.setMilliseconds(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _getEndOfDay(date) {
|
||||
let ret = new Date(date.getTime());
|
||||
ret.setHours(23);
|
||||
ret.setMinutes(59);
|
||||
ret.setSeconds(59);
|
||||
ret.setMilliseconds(999);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _formatEventTime(event, clockFormat) {
|
||||
let ret;
|
||||
if (event.allDay) {
|
||||
/* Translators: Shown in calendar event list for all day events
|
||||
* Keep it short, best if you can use less then 10 characters
|
||||
*/
|
||||
ret = C_("event list time", "All Day");
|
||||
} else {
|
||||
switch (clockFormat) {
|
||||
case '24h':
|
||||
/* Translators: Shown in calendar event list, if 24h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* explicit fall-through */
|
||||
case '12h':
|
||||
/* Transators: Shown in calendar event list, if 12h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _getCalendarWeekForDate(date) {
|
||||
// Based on the algorithms found here:
|
||||
// http://en.wikipedia.org/wiki/Talk:ISO_week_date
|
||||
@ -43,12 +107,259 @@ function _getDigitWidth(actor){
|
||||
return width;
|
||||
}
|
||||
|
||||
function Calendar() {
|
||||
function _getCalendarDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Calendar grid abbreviation for Sunday.
|
||||
*
|
||||
* NOTE: These grid abbreviations are always shown together
|
||||
* and in order, e.g. "S M T W T F S".
|
||||
*/
|
||||
C_("grid sunday", "S"),
|
||||
/* Translators: Calendar grid abbreviation for Monday */
|
||||
C_("grid monday", "M"),
|
||||
/* Translators: Calendar grid abbreviation for Tuesday */
|
||||
C_("grid tuesday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Wednesday */
|
||||
C_("grid wednesday", "W"),
|
||||
/* Translators: Calendar grid abbreviation for Thursday */
|
||||
C_("grid thursday", "T"),
|
||||
/* Translators: Calendar grid abbreviation for Friday */
|
||||
C_("grid friday", "F"),
|
||||
/* Translators: Calendar grid abbreviation for Saturday */
|
||||
C_("grid saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
}
|
||||
|
||||
function _getEventDayAbbreviation(dayNumber) {
|
||||
let abbreviations = [
|
||||
/* Translators: Event list abbreviation for Sunday.
|
||||
*
|
||||
* NOTE: These list abbreviations are normally not shown together
|
||||
* so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
* both be 'T').
|
||||
*/
|
||||
C_("list sunday", "Su"),
|
||||
/* Translators: Event list abbreviation for Monday */
|
||||
C_("list monday", "M"),
|
||||
/* Translators: Event list abbreviation for Tuesday */
|
||||
C_("list tuesday", "T"),
|
||||
/* Translators: Event list abbreviation for Wednesday */
|
||||
C_("list wednesday", "W"),
|
||||
/* Translators: Event list abbreviation for Thursday */
|
||||
C_("list thursday", "Th"),
|
||||
/* Translators: Event list abbreviation for Friday */
|
||||
C_("list friday", "F"),
|
||||
/* Translators: Event list abbreviation for Saturday */
|
||||
C_("list saturday", "S")
|
||||
];
|
||||
return abbreviations[dayNumber];
|
||||
}
|
||||
|
||||
// Abstraction for an appointment/event in a calendar
|
||||
|
||||
function CalendarEvent(date, summary, allDay) {
|
||||
this._init(date, summary, allDay);
|
||||
}
|
||||
|
||||
CalendarEvent.prototype = {
|
||||
_init: function(date, summary, allDay) {
|
||||
this.date = date;
|
||||
this.summary = summary;
|
||||
this.allDay = allDay;
|
||||
}
|
||||
};
|
||||
|
||||
// Interface for appointments/events - e.g. the contents of a calendar
|
||||
//
|
||||
|
||||
// First, an implementation with no events
|
||||
function EmptyEventSource() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
Calendar.prototype = {
|
||||
EmptyEventSource.prototype = {
|
||||
_init: function() {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(EmptyEventSource.prototype);
|
||||
|
||||
// Second, wrap native Evolution event source
|
||||
function EvolutionEventSource() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
EvolutionEventSource.prototype = {
|
||||
_init: function() {
|
||||
this._native = new Shell.EvolutionEventSource();
|
||||
this._native.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
this._native.request_range(begin.getTime(), end.getTime());
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
let nativeEvents = this._native.get_events(begin.getTime(), end.getTime());
|
||||
for (let n = 0; n < nativeEvents.length; n++) {
|
||||
let nativeEvent = nativeEvents[n];
|
||||
result.push(new CalendarEvent(new Date(nativeEvent.msec_begin), nativeEvent.summary, nativeEvent.all_day));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let events = this.getEvents(dayBegin, dayEnd);
|
||||
|
||||
if (events.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(EvolutionEventSource.prototype);
|
||||
|
||||
// Finally, an implementation with fake events
|
||||
function FakeEventSource() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
FakeEventSource.prototype = {
|
||||
_init: function() {
|
||||
|
||||
this._fakeEvents = [];
|
||||
|
||||
// Generate fake events
|
||||
//
|
||||
let midnightToday = _getBeginningOfDay(new Date());
|
||||
let summary = '';
|
||||
|
||||
// '10-oclock pow-wow' is an event occuring IN THE PAST every four days at 10am
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() - n * 4 * 86400 * 1000);
|
||||
t.setHours(10);
|
||||
summary = '10-oclock pow-wow (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
|
||||
// '11-oclock thing' is an event occuring every three days at 11am
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() + n * 3 * 86400 * 1000);
|
||||
t.setHours(11);
|
||||
summary = '11-oclock thing (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
|
||||
// 'Weekly Meeting' is an event occuring every seven days at 1:45pm (two days displaced)
|
||||
for (let n = 0; n < 5; n++) {
|
||||
let t = new Date(midnightToday.getTime() + (n * 7 + 2) * 86400 * 1000);
|
||||
t.setHours(13);
|
||||
t.setMinutes(45);
|
||||
summary = 'Weekly Meeting (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, false));
|
||||
}
|
||||
|
||||
// 'Fun All Day' is an all-day event occuring every fortnight (three days displayed)
|
||||
for (let n = 0; n < 10; n++) {
|
||||
let t = new Date(midnightToday.getTime() + (n * 14 + 3) * 86400 * 1000);
|
||||
summary = 'Fun All Day (n=' + n + ')';
|
||||
this._fakeEvents.push(new CalendarEvent(t, summary, true));
|
||||
}
|
||||
|
||||
// 'Get Married' is an event that actually reflects reality (Dec 4, 2010) :-)
|
||||
this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 4, 16, 0), 'Get Married', false));
|
||||
|
||||
// ditto for 'NE Patriots vs NY Jets'
|
||||
this._fakeEvents.push(new CalendarEvent(new Date(2010, 11, 6, 20, 30), 'NE Patriots vs NY Jets', false));
|
||||
|
||||
// An event for tomorrow @6:30pm that is added/removed every five
|
||||
// seconds (to check that the ::changed signal works)
|
||||
let transientEventDate = new Date(midnightToday.getTime() + 86400 * 1000);
|
||||
transientEventDate.setHours(18);
|
||||
transientEventDate.setMinutes(30);
|
||||
transientEventDate.setSeconds(0);
|
||||
Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
|
||||
this._includeTransientEvent = false;
|
||||
this._transientEvent = new CalendarEvent(transientEventDate, 'A Transient Event', false);
|
||||
this._transientEventCounter = 1;
|
||||
},
|
||||
|
||||
_updateTransientEvent: function() {
|
||||
this._includeTransientEvent = !this._includeTransientEvent;
|
||||
this._transientEventCounter = this._transientEventCounter + 1;
|
||||
this._transientEvent.summary = 'A Transient Event (' + this._transientEventCounter + ')';
|
||||
this.emit('changed');
|
||||
Mainloop.timeout_add(5000, Lang.bind(this, this._updateTransientEvent));
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
},
|
||||
|
||||
getEvents: function(begin, end) {
|
||||
let result = [];
|
||||
//log('begin:' + begin);
|
||||
//log('end: ' + end);
|
||||
for(let n = 0; n < this._fakeEvents.length; n++) {
|
||||
let event = this._fakeEvents[n];
|
||||
if (event.date >= begin && event.date <= end) {
|
||||
result.push(event);
|
||||
}
|
||||
//log('when:' + event.date + ' summary:' + event.summary);
|
||||
}
|
||||
if (this._includeTransientEvent && this._transientEvent.date >= begin && this._transientEvent.date <= end)
|
||||
result.push(this._transientEvent);
|
||||
result.sort(function(event1, event2) {
|
||||
return event1.date.getTime() - event2.date.getTime();
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
hasEvents: function(day) {
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let events = this.getEvents(dayBegin, dayEnd);
|
||||
|
||||
if (events.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
Signals.addSignalMethods(FakeEventSource.prototype);
|
||||
|
||||
// Calendar:
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
function Calendar(eventSource) {
|
||||
this._init(eventSource);
|
||||
}
|
||||
|
||||
Calendar.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
|
||||
// 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.
|
||||
@ -60,7 +371,7 @@ Calendar.prototype = {
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
|
||||
let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0');
|
||||
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
|
||||
if (weekStartString.indexOf('calendar:week_start:') == 0) {
|
||||
this._weekStart = parseInt(weekStartString.substring(20));
|
||||
}
|
||||
@ -71,7 +382,8 @@ Calendar.prototype = {
|
||||
}
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
switch (Gettext_gtk20.gettext('calendar:MY')) {
|
||||
this._headerFormatWithoutYear = '%B';
|
||||
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
||||
case 'calendar:MY':
|
||||
this._headerFormat = '%B %Y';
|
||||
break;
|
||||
@ -85,7 +397,7 @@ Calendar.prototype = {
|
||||
}
|
||||
|
||||
// Start off with the current date
|
||||
this.date = new Date();
|
||||
this._selectedDate = new Date();
|
||||
|
||||
this.actor = new St.Table({ homogeneous: false,
|
||||
style_class: 'calendar',
|
||||
@ -100,9 +412,10 @@ Calendar.prototype = {
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this.date)) {
|
||||
this.date = date;
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
this._selectedDate = date;
|
||||
this._update();
|
||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||
}
|
||||
},
|
||||
|
||||
@ -116,45 +429,36 @@ Calendar.prototype = {
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
|
||||
let [backlabel, forwardlabel] = ['<', '>'];
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
|
||||
[backlabel, forwardlabel] = [forwardlabel, backlabel];
|
||||
}
|
||||
|
||||
let back = new St.Button({ label: backlabel, style_class: 'calendar-change-month' });
|
||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
||||
this._topBox.add(back);
|
||||
back.connect('clicked', Lang.bind(this, this._prevMonth));
|
||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||
|
||||
this._dateLabel = new St.Label();
|
||||
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
|
||||
let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' });
|
||||
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
|
||||
this._topBox.add(forward);
|
||||
forward.connect('clicked', Lang.bind(this, this._nextMonth));
|
||||
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||
|
||||
// Add weekday labels...
|
||||
//
|
||||
// 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);
|
||||
let iter = new Date(this._selectedDate);
|
||||
iter.setSeconds(0); // Leap second protection. Hah!
|
||||
iter.setHours(12);
|
||||
|
||||
if (this._useWeekdate) {
|
||||
this._weekdateHeader = new St.Label();
|
||||
this.actor.add(this._weekdateHeader,
|
||||
{ row: 1,
|
||||
col: 0,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
this._setWeekdateHeaderWidth();
|
||||
} else {
|
||||
this._weekdateHeader = null;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
|
||||
// Could use iter.toLocaleFormat('%a') but that normally gives three characters
|
||||
// and we want, ideally, a single character for e.g. S M T W T F S
|
||||
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
|
||||
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
|
||||
text: customDayAbbrev });
|
||||
this.actor.add(label,
|
||||
{ row: 1,
|
||||
col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: St.Align.END });
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
@ -178,33 +482,57 @@ Calendar.prototype = {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
case Clutter.ScrollDirection.LEFT:
|
||||
this._prevMonth();
|
||||
this._onPrevMonthButtonClicked();
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
case Clutter.ScrollDirection.RIGHT:
|
||||
this._nextMonth();
|
||||
this._onNextMonthButtonClicked();
|
||||
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);
|
||||
_onPrevMonthButtonClicked: function() {
|
||||
let newDate = new Date(this._selectedDate);
|
||||
let oldMonth = newDate.getMonth();
|
||||
if (oldMonth == 0) {
|
||||
newDate.setMonth(11);
|
||||
newDate.setFullYear(newDate.getFullYear() - 1);
|
||||
if (newDate.getMonth() != 11) {
|
||||
let day = 32 - new Date(newDate.getFullYear() - 1, 11, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear() - 1, 11, day);
|
||||
}
|
||||
}
|
||||
this._update();
|
||||
else {
|
||||
newDate.setMonth(oldMonth - 1);
|
||||
if (newDate.getMonth() != oldMonth - 1) {
|
||||
let day = 32 - new Date(newDate.getFullYear(), oldMonth - 1, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear(), oldMonth - 1, day);
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate);
|
||||
},
|
||||
|
||||
_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);
|
||||
_onNextMonthButtonClicked: function() {
|
||||
let newDate = new Date(this._selectedDate);
|
||||
let oldMonth = newDate.getMonth();
|
||||
if (oldMonth == 11) {
|
||||
newDate.setMonth(0);
|
||||
newDate.setFullYear(newDate.getFullYear() + 1);
|
||||
if (newDate.getMonth() != 0) {
|
||||
let day = 32 - new Date(newDate.getFullYear() + 1, 0, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear() + 1, 0, day);
|
||||
}
|
||||
}
|
||||
this._update();
|
||||
else {
|
||||
newDate.setMonth(oldMonth + 1);
|
||||
if (newDate.getMonth() != oldMonth + 1) {
|
||||
let day = 32 - new Date(newDate.getFullYear(), oldMonth + 1, 32).getDate();
|
||||
newDate = new Date(newDate.getFullYear(), oldMonth + 1, day);
|
||||
}
|
||||
}
|
||||
|
||||
this.setDate(newDate);
|
||||
},
|
||||
|
||||
_onSettingsChange: function() {
|
||||
@ -214,7 +542,12 @@ Calendar.prototype = {
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
|
||||
let now = new Date();
|
||||
|
||||
if (_sameYear(this._selectedDate, now))
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
|
||||
else
|
||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
|
||||
|
||||
// Remove everything but the topBox and the weekday labels
|
||||
let children = this.actor.get_children();
|
||||
@ -222,45 +555,217 @@ Calendar.prototype = {
|
||||
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);
|
||||
let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
|
||||
iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
|
||||
let now = new Date();
|
||||
let beginDate = new Date(this._selectedDate);
|
||||
beginDate.setDate(1);
|
||||
beginDate.setSeconds(0);
|
||||
beginDate.setHours(12);
|
||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
|
||||
let iter = new Date(beginDate);
|
||||
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';
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
let newlySelectedDate = new Date(iterStr);
|
||||
this.setDate(newlySelectedDate);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
else
|
||||
label.style_class = 'calendar-day';
|
||||
styleClass += ' calendar-nonwork-day'
|
||||
|
||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||
if (row == 2)
|
||||
styleClass = 'calendar-day-top ' + styleClass;
|
||||
if (iter.getDay() == this._weekStart)
|
||||
styleClass = 'calendar-day-left ' + styleClass;
|
||||
|
||||
if (_sameDay(now, iter))
|
||||
styleClass += ' calendar-today';
|
||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||
styleClass += ' calendar-other-month-day';
|
||||
|
||||
if (_sameDay(this._selectedDate, iter))
|
||||
button.add_style_pseudo_class('active');
|
||||
|
||||
if (hasEvents)
|
||||
styleClass += ' calendar-day-with-events'
|
||||
|
||||
button.style_class = styleClass;
|
||||
|
||||
let offsetCols = this._useWeekdate ? 1 : 0;
|
||||
this.actor.add(label,
|
||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: St.Align.END });
|
||||
this.actor.add(button,
|
||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||
|
||||
if (this._useWeekdate && iter.getDay() == 4) {
|
||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||
style_class: 'calendar-day calendar-calendarweek'});
|
||||
style_class: 'calendar-day-base calendar-week-number'});
|
||||
this.actor.add(label,
|
||||
{ row: row, col: 0,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
{ row: row, col: 0, y_align: St.Align.MIDDLE });
|
||||
}
|
||||
|
||||
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())
|
||||
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
|
||||
break;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter);
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(Calendar.prototype);
|
||||
|
||||
function EventsList(eventSource) {
|
||||
this._init(eventSource);
|
||||
}
|
||||
|
||||
EventsList.prototype = {
|
||||
_init: function(eventSource) {
|
||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
||||
this._date = new Date();
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
let weekStartString = Gettext_gtk30.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;
|
||||
}
|
||||
|
||||
this._update();
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
if (includeDayName) {
|
||||
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
|
||||
text: day } ),
|
||||
{ x_fill: true } );
|
||||
}
|
||||
timeBox.add(new St.Label( { style_class: 'events-day-time',
|
||||
text: time} ),
|
||||
{ x_fill: true } );
|
||||
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
|
||||
text: desc} ));
|
||||
},
|
||||
|
||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||
|
||||
if (events.length == 0 && !showNothingScheduled)
|
||||
return;
|
||||
|
||||
let vbox = new St.BoxLayout( {vertical: true} );
|
||||
this.actor.add(vbox);
|
||||
|
||||
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
|
||||
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
|
||||
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
|
||||
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
|
||||
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
|
||||
box.add(dayNameBox, {x_fill: false});
|
||||
box.add(timeBox, {x_fill: false});
|
||||
box.add(eventTitleBox, {expand: true});
|
||||
vbox.add(box);
|
||||
|
||||
for (let n = 0; n < events.length; n++) {
|
||||
let event = events[n];
|
||||
let dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||
let timeString = _formatEventTime(event, clockFormat);
|
||||
let summaryString = event.summary;
|
||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
|
||||
}
|
||||
|
||||
if (events.length == 0 && showNothingScheduled) {
|
||||
let now = new Date();
|
||||
/* Translators: Text to show if there are no events */
|
||||
let nothingEvent = new CalendarEvent(now, _("Nothing Scheduled"), true);
|
||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
||||
}
|
||||
},
|
||||
|
||||
_showOtherDay: function(day) {
|
||||
this.actor.destroy_children();
|
||||
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let dayString;
|
||||
let now = new Date();
|
||||
if (_sameYear(day, now))
|
||||
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d"));
|
||||
else
|
||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
|
||||
},
|
||||
|
||||
_showToday: function() {
|
||||
this.actor.destroy_children();
|
||||
|
||||
let now = new Date();
|
||||
let dayBegin = _getBeginningOfDay(now);
|
||||
let dayEnd = _getEndOfDay(now);
|
||||
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
|
||||
|
||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
||||
|
||||
if (dayEnd.getDay() <= 4 + this._weekStart) {
|
||||
/* If now is within the first 5 days we show "This week" and
|
||||
* include events up until and including Saturday/Sunday
|
||||
* (depending on whether a week starts on Sunday/Monday).
|
||||
*/
|
||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
||||
} else {
|
||||
/* otherwise it's one of the two last days of the week ... show
|
||||
* "Next week" and include events up until and including *next*
|
||||
* Saturday/Sunday
|
||||
*/
|
||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
|
||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the event list to show events from a specific date
|
||||
setDate: function(date) {
|
||||
if (!_sameDay(date, this._date)) {
|
||||
this._date = date;
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
let today = new Date();
|
||||
if (_sameDay (this._date, today)) {
|
||||
this._showToday();
|
||||
} else {
|
||||
this._showOtherDay(this._date);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -233,7 +233,7 @@ Chrome.prototype = {
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let windows = global.get_window_actors();
|
||||
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
// The chrome layer should be visible unless there is a window
|
||||
|
254
js/ui/ctrlAltTab.js
Normal file
@ -0,0 +1,254 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const AltTab = imports.ui.altTab;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const POPUP_APPICON_SIZE = 96;
|
||||
const POPUP_FADE_TIME = 0.1; // seconds
|
||||
|
||||
function CtrlAltTabManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CtrlAltTabManager.prototype = {
|
||||
_init: function() {
|
||||
this._items = [];
|
||||
this._focusManager = St.FocusManager.get_for_stage(global.stage);
|
||||
Main.wm.setKeybindingHandler('switch_panels', Lang.bind(this,
|
||||
function (shellwm, binding, window, backwards) {
|
||||
this.popup(backwards);
|
||||
}));
|
||||
},
|
||||
|
||||
addGroup: function(root, name, icon) {
|
||||
this._items.push({ root: root, name: name, iconName: icon });
|
||||
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
|
||||
this._focusManager.add_group(root);
|
||||
},
|
||||
|
||||
removeGroup: function(root) {
|
||||
this._focusManager.remove_group(root);
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
if (this._items[i].root == root) {
|
||||
this._items.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
focusGroup: function(root) {
|
||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
},
|
||||
|
||||
popup: function(backwards) {
|
||||
// Start with the set of focus groups that are currently mapped
|
||||
let items = this._items.filter(function (item) { return item.root.mapped; });
|
||||
|
||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let icon;
|
||||
let app = windowTracker.get_window_app(windows[i]);
|
||||
if (app)
|
||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ window: windows[i],
|
||||
name: windows[i].title,
|
||||
iconActor: icon });
|
||||
}
|
||||
|
||||
if (!items.length)
|
||||
return;
|
||||
|
||||
new CtrlAltTabPopup().show(items, backwards);
|
||||
}
|
||||
};
|
||||
|
||||
function mod(a, b) {
|
||||
return (a + b) % b;
|
||||
}
|
||||
|
||||
function CtrlAltTabPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CtrlAltTabPopup.prototype = {
|
||||
_init : function() {
|
||||
let primary = global.get_primary_monitor();
|
||||
this.actor = new St.BoxLayout({ name: 'ctrlAltTabPopup',
|
||||
reactive: true,
|
||||
x: primary.x + primary.width / 2,
|
||||
y: primary.y + primary.height / 2,
|
||||
anchor_gravity: Clutter.Gravity.CENTER });
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._haveModal = false;
|
||||
this._selection = 0;
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
},
|
||||
|
||||
show : function(items, startBackwards) {
|
||||
if (!Main.pushModal(this.actor))
|
||||
return false;
|
||||
this._haveModal = true;
|
||||
|
||||
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
|
||||
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
|
||||
|
||||
this._items = items;
|
||||
this._switcher = new CtrlAltTabSwitcher(items);
|
||||
this.actor.add_actor(this._switcher.actor);
|
||||
|
||||
if (startBackwards)
|
||||
this._selection = this._items.length - 1;
|
||||
this._select(this._selection);
|
||||
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
|
||||
this._finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.actor.opacity = 0;
|
||||
this.actor.show();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_next : function() {
|
||||
return mod(this._selection + 1, this._items.length);
|
||||
},
|
||||
|
||||
_previous : function() {
|
||||
return mod(this._selection - 1, this._items.length);
|
||||
},
|
||||
|
||||
_keyPressEvent : function(actor, event) {
|
||||
let keysym = event.get_key_symbol();
|
||||
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
|
||||
if (shift && keysym == Clutter.KEY_Tab)
|
||||
keysym = Clutter.ISO_Left_Tab;
|
||||
|
||||
if (keysym == Clutter.KEY_Escape)
|
||||
this.destroy();
|
||||
else if (keysym == Clutter.KEY_Tab)
|
||||
this._select(this._next());
|
||||
else if (keysym == Clutter.KEY_ISO_Left_Tab)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.KEY_Right)
|
||||
this._select(this._next());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_keyReleaseEvent : function(actor, event) {
|
||||
let [x, y, mods] = global.get_pointer();
|
||||
let state = mods & Clutter.ModifierType.MOD1_MASK;
|
||||
|
||||
if (state == 0)
|
||||
this._finish();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_finish : function() {
|
||||
this.destroy();
|
||||
|
||||
let item = this._items[this._selection];
|
||||
if (item.root)
|
||||
Main.ctrlAltTabManager.focusGroup(item.root);
|
||||
else
|
||||
Main.activateWindow(item.window);
|
||||
},
|
||||
|
||||
_popModal: function() {
|
||||
if (this._haveModal) {
|
||||
Main.popModal(this.actor);
|
||||
this._haveModal = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this._popModal();
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: POPUP_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
if (this._keyPressEventId)
|
||||
this.actor.disconnect(this._keyPressEventId);
|
||||
if (this._keyReleaseEventId)
|
||||
this.actor.disconnect(this._keyReleaseEventId);
|
||||
},
|
||||
|
||||
_select : function(num) {
|
||||
this._selection = num;
|
||||
this._switcher.highlight(num);
|
||||
}
|
||||
};
|
||||
|
||||
function CtrlAltTabSwitcher(items) {
|
||||
this._init(items);
|
||||
}
|
||||
|
||||
CtrlAltTabSwitcher.prototype = {
|
||||
__proto__ : AltTab.SwitcherList.prototype,
|
||||
|
||||
_init : function(items) {
|
||||
AltTab.SwitcherList.prototype._init.call(this, true);
|
||||
|
||||
for (let i = 0; i < items.length; i++)
|
||||
this._addIcon(items[i]);
|
||||
},
|
||||
|
||||
_addIcon : function(item) {
|
||||
let box = new St.BoxLayout({ style_class: 'alt-tab-app',
|
||||
vertical: true });
|
||||
|
||||
let icon = item.iconActor;
|
||||
if (!icon) {
|
||||
icon = new St.Icon({ icon_name: item.iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: POPUP_APPICON_SIZE });
|
||||
}
|
||||
box.add(icon, { x_fill: false, y_fill: false } );
|
||||
|
||||
let text = new St.Label({ text: item.name });
|
||||
box.add(text, { x_fill: false });
|
||||
|
||||
this.addItem(box);
|
||||
}
|
||||
};
|
1461
js/ui/dash.js
214
js/ui/dateMenu.js
Normal file
@ -0,0 +1,214 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Util = imports.misc.util;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
// in org.gnome.shell.clock
|
||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
function _onVertSepRepaint (area)
|
||||
{
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
let stippleColor = themeNode.get_color('-stipple-color');
|
||||
let stippleWidth = themeNode.get_length('-stipple-width');
|
||||
let x = Math.floor(width/2) + 0.5;
|
||||
cr.moveTo(x, 0);
|
||||
cr.lineTo(x, height);
|
||||
Clutter.cairo_set_source_color(cr, stippleColor);
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
};
|
||||
|
||||
function DateMenuButton() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DateMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
let item;
|
||||
let hbox;
|
||||
let vbox;
|
||||
|
||||
//this._eventSource = new Calendar.EmptyEventSource();
|
||||
//this._eventSource = new Calendar.FakeEventSource();
|
||||
this._eventSource = new Calendar.EvolutionEventSource();
|
||||
|
||||
let menuAlignment = 0.25;
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
|
||||
menuAlignment = 1.0 - menuAlignment;
|
||||
PanelMenu.Button.prototype._init.call(this, menuAlignment);
|
||||
|
||||
this._clock = new St.Label();
|
||||
this.actor.set_child(this._clock);
|
||||
|
||||
hbox = new St.BoxLayout({name: 'calendarArea'});
|
||||
this.menu.addActor(hbox);
|
||||
|
||||
// Fill up the first column
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox);
|
||||
|
||||
// Date
|
||||
this._date = new St.Label();
|
||||
this._date.style_class = 'datemenu-date-label';
|
||||
vbox.add(this._date);
|
||||
|
||||
this._eventList = new Calendar.EventsList(this._eventSource);
|
||||
|
||||
// Calendar
|
||||
this._calendar = new Calendar.Calendar(this._eventSource);
|
||||
this._calendar.connect('selected-date-changed',
|
||||
Lang.bind(this, function(calendar, date) {
|
||||
this._eventList.setDate(date);
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
item.setColumnWidths(1);
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
vbox.add(item.actor);
|
||||
|
||||
// Add vertical separator
|
||||
|
||||
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
|
||||
pseudo_class: 'highlighted' });
|
||||
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
|
||||
hbox.add(item);
|
||||
|
||||
// Fill up the second column
|
||||
|
||||
vbox = new St.BoxLayout({vertical: true});
|
||||
hbox.add(vbox);
|
||||
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
let now = new Date();
|
||||
this._calendar.setDate(now);
|
||||
// No need to update this._eventList as ::selected-date-changed
|
||||
// signal will fire
|
||||
}
|
||||
}));
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
// Track changes to clock settings
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
|
||||
|
||||
// Start the clock
|
||||
this._updateClockAndDate();
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
||||
|
||||
let clockFormat;
|
||||
let dateFormat;
|
||||
|
||||
switch (format) {
|
||||
case '24h':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
break;
|
||||
case '12h':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
break;
|
||||
}
|
||||
|
||||
let displayDate = new Date();
|
||||
let msecRemaining;
|
||||
if (showSeconds) {
|
||||
msecRemaining = 1000 - displayDate.getMilliseconds();
|
||||
if (msecRemaining < 50) {
|
||||
displayDate.setSeconds(displayDate.getSeconds() + 1);
|
||||
msecRemaining += 1000;
|
||||
}
|
||||
} else {
|
||||
msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
|
||||
displayDate.getMilliseconds());
|
||||
if (msecRemaining < 500) {
|
||||
displayDate.setMinutes(displayDate.getMinutes() + 1);
|
||||
msecRemaining += 60000;
|
||||
}
|
||||
}
|
||||
|
||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
||||
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
dateFormat = _("%A %B %e, %Y");
|
||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||
|
||||
Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClockAndDate));
|
||||
return false;
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
this.menu.close();
|
||||
Util.spawnDesktop('gnome-datetime-panel');
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
// TODO: pass '-c calendar' (to force the calendar at startup)
|
||||
// TODO: pass the selected day
|
||||
Util.spawnDesktop('evolution');
|
||||
},
|
||||
};
|
82
js/ui/dnd.js
@ -26,9 +26,9 @@ const DragMotionResult = {
|
||||
};
|
||||
|
||||
const DRAG_CURSOR_MAP = {
|
||||
0: Shell.Cursor.UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.COPY,
|
||||
2: Shell.Cursor.MOVE
|
||||
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
|
||||
1: Shell.Cursor.DND_COPY,
|
||||
2: Shell.Cursor.DND_MOVE
|
||||
};
|
||||
|
||||
const DragDropResult = {
|
||||
@ -100,6 +100,8 @@ _Draggable.prototype = {
|
||||
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
|
||||
this._eventsGrabbed = false;
|
||||
},
|
||||
|
||||
_onButtonPress : function (actor, event) {
|
||||
@ -110,11 +112,11 @@ _Draggable.prototype = {
|
||||
return false;
|
||||
|
||||
this._buttonDown = true;
|
||||
// special case St.Clickable: grabbing the pointer would mess up the
|
||||
// special case St.Button: grabbing the pointer would mess up the
|
||||
// internal state, so we start the drag manually on hover change
|
||||
if (this.actor instanceof St.Clickable)
|
||||
if (this.actor instanceof St.Button)
|
||||
this.actor.connect('notify::hover',
|
||||
Lang.bind(this, this._onClickableHoverChanged));
|
||||
Lang.bind(this, this._onButtonHoverChanged));
|
||||
else
|
||||
this._grabActor();
|
||||
|
||||
@ -125,8 +127,8 @@ _Draggable.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_onClickableHoverChanged: function(button) {
|
||||
if (button.hover || !button.held)
|
||||
_onButtonHoverChanged: function(button) {
|
||||
if (button.hover || !button.pressed)
|
||||
return;
|
||||
|
||||
button.fake_release();
|
||||
@ -147,13 +149,19 @@ _Draggable.prototype = {
|
||||
},
|
||||
|
||||
_grabEvents: function() {
|
||||
Clutter.grab_pointer(_getEventHandlerActor());
|
||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
||||
if (!this._eventsGrabbed) {
|
||||
Clutter.grab_pointer(_getEventHandlerActor());
|
||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
||||
this._eventsGrabbed = true;
|
||||
}
|
||||
},
|
||||
|
||||
_ungrabEvents: function() {
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
if (this._eventsGrabbed) {
|
||||
Clutter.ungrab_pointer();
|
||||
Clutter.ungrab_keyboard();
|
||||
this._eventsGrabbed = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onEvent: function(actor, event) {
|
||||
@ -213,7 +221,7 @@ _Draggable.prototype = {
|
||||
if (this._onEventId)
|
||||
this._ungrabActor();
|
||||
this._grabEvents();
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
|
||||
this._dragX = this._dragStartX = stageX;
|
||||
this._dragY = this._dragStartY = stageY;
|
||||
@ -374,7 +382,7 @@ _Draggable.prototype = {
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.set_cursor(Shell.Cursor.IN_DRAG);
|
||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -440,24 +448,44 @@ _Draggable.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
// Get position of the drag actor's source if the source is still around,
|
||||
// or return the original location if the actor itself was being dragged
|
||||
// or the source is no longer around.
|
||||
_getRestoreLocation: function() {
|
||||
let locX = this._snapBackX;
|
||||
let locY = this._snapBackY;
|
||||
let x, y, scale;
|
||||
|
||||
if (this._dragActorSource && this._dragActorSource.visible)
|
||||
[locX, locY] = this._dragActorSource.get_transformed_position();
|
||||
return [locX, locY];
|
||||
if (this._dragActorSource && this._dragActorSource.visible) {
|
||||
// Snap the clone back to its source
|
||||
[x, y] = this._dragActorSource.get_transformed_position();
|
||||
let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size();
|
||||
scale = this._dragActor.width / sourceScaledWidth;
|
||||
} else if (this._dragOrigParent) {
|
||||
// Snap the actor back to its original position within
|
||||
// its parent, adjusting for the fact that the parent
|
||||
// may have been moved or scaled
|
||||
let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
|
||||
let [parentWidth, parentHeight] = this._dragOrigParent.get_size();
|
||||
let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();
|
||||
let parentScale = parentScaledWidth / parentWidth;
|
||||
|
||||
x = parentX + parentScale * this._dragOrigX;
|
||||
y = parentY + parentScale * this._dragOrigY;
|
||||
scale = this._dragOrigScale * parentScale;
|
||||
} else {
|
||||
// Snap back actor to its original stage position
|
||||
x = this._snapBackX;
|
||||
y = this._snapBackY;
|
||||
scale = this._snapBackScale;
|
||||
}
|
||||
|
||||
return [x, y, scale];
|
||||
},
|
||||
|
||||
_cancelDrag: function(eventTime) {
|
||||
this._dragInProgress = false;
|
||||
let [snapBackX, snapBackY] = this._getRestoreLocation();
|
||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||
|
||||
if (this._actorDestroyed) {
|
||||
global.unset_cursor();
|
||||
if (!this._buttonDown)
|
||||
this._ungrabEvents();
|
||||
this.emit('drag-end', eventTime, false);
|
||||
return;
|
||||
}
|
||||
@ -467,8 +495,8 @@ _Draggable.prototype = {
|
||||
Tweener.addTween(this._dragActor,
|
||||
{ x: snapBackX,
|
||||
y: snapBackY,
|
||||
scale_x: this._snapBackScale,
|
||||
scale_y: this._snapBackScale,
|
||||
scale_x: snapBackScale,
|
||||
scale_y: snapBackScale,
|
||||
opacity: this._dragOrigOpacity,
|
||||
time: SNAP_BACK_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
@ -480,11 +508,11 @@ _Draggable.prototype = {
|
||||
|
||||
_restoreDragActor: function(eventTime) {
|
||||
this._dragInProgress = false;
|
||||
[restoreX, restoreY] = this._getRestoreLocation();
|
||||
[restoreX, restoreY, restoreScale] = this._getRestoreLocation();
|
||||
|
||||
// fade the actor back in at its original location
|
||||
this._dragActor.set_position(restoreX, restoreY);
|
||||
this._dragActor.set_scale(this._snapBackScale, this._snapBackScale);
|
||||
this._dragActor.set_scale(restoreScale, restoreScale);
|
||||
this._dragActor.opacity = 0;
|
||||
|
||||
this._animationInProgress = true;
|
||||
|
@ -1,487 +1,12 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const DocInfo = imports.misc.docInfo;
|
||||
const DND = imports.ui.dnd;
|
||||
const GenericDisplay = imports.ui.genericDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_DASH_DOCS = 50;
|
||||
const DASH_DOCS_ICON_SIZE = 16;
|
||||
|
||||
const DEFAULT_SPACING = 4;
|
||||
|
||||
/* This class represents a single display item containing information about a document.
|
||||
* We take the current number of seconds in the constructor to avoid looking up the current
|
||||
* time for every item when they are created in a batch.
|
||||
*
|
||||
* docInfo - DocInfo object containing information about the document
|
||||
* currentSeconds - current number of seconds since the epoch
|
||||
*/
|
||||
function DocDisplayItem(docInfo, currentSecs) {
|
||||
this._init(docInfo, currentSecs);
|
||||
}
|
||||
|
||||
DocDisplayItem.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplayItem.prototype,
|
||||
|
||||
_init : function(docInfo, currentSecs) {
|
||||
GenericDisplay.GenericDisplayItem.prototype._init.call(this);
|
||||
this._docInfo = docInfo;
|
||||
|
||||
this._setItemInfo(docInfo.name, '');
|
||||
|
||||
this._timeoutTime = -1;
|
||||
this._resetTimeDisplay(currentSecs);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
getUpdateTimeoutTime: function() {
|
||||
return this._timeoutTime;
|
||||
},
|
||||
|
||||
// Update any relative-time based displays for this item.
|
||||
redisplay: function(currentSecs) {
|
||||
this._resetTimeDisplay(currentSecs);
|
||||
},
|
||||
|
||||
//// Public method overrides ////
|
||||
|
||||
// Opens a document represented by this display item.
|
||||
launch : function() {
|
||||
this._docInfo.launch();
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Returns an icon for the item.
|
||||
_createIcon : function() {
|
||||
return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||
},
|
||||
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon : function() {
|
||||
return this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
|
||||
},
|
||||
|
||||
// Creates and returns a large preview icon, but only if this._docInfo is an image file
|
||||
// and we were able to generate a pixbuf from it successfully.
|
||||
_createLargePreviewIcon : function() {
|
||||
if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf('image/') != 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.NONE,
|
||||
this._docInfo.uri, -1, -1);
|
||||
} catch (e) {
|
||||
// An exception will be raised when the image format isn't know
|
||||
/* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
|
||||
* only ignore GDK_PIXBUF_ERROR_UNKNOWN_TYPE. */
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
//// Drag and Drop ////
|
||||
|
||||
shellWorkspaceLaunch: function() {
|
||||
this.launch();
|
||||
},
|
||||
|
||||
//// Private Methods ////
|
||||
|
||||
// Updates the last visited time displayed in the description text for the item.
|
||||
_resetTimeDisplay: function(currentSecs) {
|
||||
let lastSecs = this._docInfo.timestamp;
|
||||
let timeDelta = currentSecs - lastSecs;
|
||||
let [text, nextUpdate] = global.format_time_relative_pretty(timeDelta);
|
||||
this._timeoutTime = currentSecs + nextUpdate;
|
||||
this._setDescriptionText(text);
|
||||
}
|
||||
};
|
||||
|
||||
/* This class represents a display containing a collection of document items.
|
||||
* The documents are sorted by how recently they were last visited.
|
||||
*/
|
||||
function DocDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DocDisplay.prototype = {
|
||||
__proto__: GenericDisplay.GenericDisplay.prototype,
|
||||
|
||||
_init : function() {
|
||||
GenericDisplay.GenericDisplay.prototype._init.call(this);
|
||||
// We keep a single timeout callback for updating last visited times
|
||||
// for all the items in the display. This avoids creating individual
|
||||
// callbacks for each item in the display. So proper time updates
|
||||
// for individual items and item details depend on the item being
|
||||
// associated with one of the displays.
|
||||
this._updateTimeoutTargetTime = -1;
|
||||
this._updateTimeoutId = 0;
|
||||
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
this._docsStale = true;
|
||||
this._docManager.connect('changed', Lang.bind(this, function(mgr, userData) {
|
||||
this._docsStale = true;
|
||||
// Changes in local recent files should not happen when we are in the Overview mode,
|
||||
// but redisplaying right away is cool when we use Zephyr.
|
||||
// Also, we might be displaying remote documents, like Google Docs, in the future
|
||||
// which might be edited by someone else.
|
||||
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
|
||||
}));
|
||||
|
||||
this.connect('destroy', Lang.bind(this, function (o) {
|
||||
if (this._updateTimeoutId > 0)
|
||||
Mainloop.source_remove(this._updateTimeoutId);
|
||||
}));
|
||||
},
|
||||
|
||||
//// Protected method overrides ////
|
||||
|
||||
// Gets the list of recent items from the recent items manager.
|
||||
_refreshCache : function() {
|
||||
if (!this._docsStale)
|
||||
return true;
|
||||
this._allItems = {};
|
||||
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
||||
this._docsStale = false;
|
||||
return false;
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items based on how recently they were last visited.
|
||||
_setDefaultList : function() {
|
||||
// It seems to be an implementation detail of the Mozilla JavaScript that object
|
||||
// properties are returned during the iteration in the same order in which they were
|
||||
// defined, but it is not a guarantee according to this
|
||||
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in
|
||||
// While this._allItems associative array seems to always be ordered by last added,
|
||||
// as the results of this._recentManager.get_items() based on which it is constructed are,
|
||||
// we should do the sorting manually because we want the order to be based on last visited.
|
||||
//
|
||||
// This function is called each time the search string is set back to '' or we display
|
||||
// the Overview, so we are doing the sorting over the same items multiple times if the list
|
||||
// of recent items didn't change. We could store an additional array of doc ids and sort
|
||||
// them once when they are returned by this._recentManager.get_items() to avoid having to do
|
||||
// this sorting each time, but the sorting seems to be very fast anyway, so there is no need
|
||||
// to introduce an additional class variable.
|
||||
this._matchedItems = {};
|
||||
this._matchedItemKeys = [];
|
||||
let docIdsToRemove = [];
|
||||
for (docId in this._allItems) {
|
||||
this._matchedItems[docId] = 1;
|
||||
this._matchedItemKeys.push(docId);
|
||||
}
|
||||
|
||||
for (docId in docIdsToRemove) {
|
||||
delete this._allItems[docId];
|
||||
}
|
||||
|
||||
this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on how recently the items
|
||||
// were last visited.
|
||||
// Returns an integer value indicating the result of the comparison.
|
||||
_compareItems : function(itemIdA, itemIdB) {
|
||||
let docA = this._allItems[itemIdA];
|
||||
let docB = this._allItems[itemIdB];
|
||||
|
||||
return docB.timestamp - docA.timestamp;
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string by checking
|
||||
// the name of the document. Item info is expected to be GtkRecentInfo.
|
||||
// Returns a boolean flag indicating if itemInfo is a match.
|
||||
_isInfoMatching : function(itemInfo, search) {
|
||||
if (!itemInfo.exists())
|
||||
return false;
|
||||
|
||||
if (search == null || search == '')
|
||||
return true;
|
||||
|
||||
let name = itemInfo.name.toLowerCase();
|
||||
if (name.indexOf(search) >= 0)
|
||||
return true;
|
||||
// TODO: we can also check doc URIs, so that
|
||||
// if you search for a directory name, we display recent files from it
|
||||
return false;
|
||||
},
|
||||
|
||||
// Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
let currentSecs = new Date().getTime() / 1000;
|
||||
let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
|
||||
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
||||
return docDisplayItem;
|
||||
},
|
||||
|
||||
//// Private Methods ////
|
||||
|
||||
// A callback function that redisplays the items, updating their descriptions,
|
||||
// and sets up a new timeout callback.
|
||||
_docTimeout: function () {
|
||||
let currentSecs = new Date().getTime() / 1000;
|
||||
this._updateTimeoutId = 0;
|
||||
this._updateTimeoutTargetTime = -1;
|
||||
for (let docId in this._displayedItems) {
|
||||
let docDisplayItem = this._displayedItems[docId];
|
||||
docDisplayItem.redisplay(currentSecs);
|
||||
this._updateTimeoutCallback(docDisplayItem, currentSecs);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Updates the timeout callback if the timeout time for the docDisplayItem
|
||||
// is earlier than the target time for the current timeout callback.
|
||||
_updateTimeoutCallback: function (docDisplayItem, currentSecs) {
|
||||
let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
|
||||
if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
|
||||
if (this._updateTimeoutId > 0)
|
||||
Mainloop.source_remove(this._updateTimeoutId);
|
||||
this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
|
||||
this._updateTimeoutTargetTime = timeoutTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DocDisplay.prototype);
|
||||
|
||||
function DashDocDisplayItem(docInfo) {
|
||||
this._init(docInfo);
|
||||
}
|
||||
|
||||
DashDocDisplayItem.prototype = {
|
||||
_init: function(docInfo) {
|
||||
this._info = docInfo;
|
||||
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
||||
|
||||
this.actor = new St.Clickable({ style_class: 'recent-docs-item',
|
||||
reactive: true,
|
||||
x_align: St.Align.START });
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'recent-docs-item-box' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
box.add(this._icon);
|
||||
|
||||
let text = new St.Label({ text: docInfo.name });
|
||||
box.add(text);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, function () {
|
||||
docInfo.launch();
|
||||
Main.overview.hide();
|
||||
}));
|
||||
|
||||
this.actor._delegate = this;
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
getUri: function() {
|
||||
return this._info.uri;
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
|
||||
return this.dragActor;
|
||||
},
|
||||
|
||||
//// Drag and drop functions ////
|
||||
|
||||
shellWorkspaceLaunch: function () {
|
||||
this._info.launch();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class used to display two column recent documents in the dash
|
||||
*/
|
||||
function DashDocDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
DashDocDisplay.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer();
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
|
||||
this._actorsByUri = {};
|
||||
|
||||
this._docManager = DocInfo.getDocManager();
|
||||
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
||||
this._pendingDocsChange = true;
|
||||
this._checkDocExistence = false;
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let children = actor.get_children();
|
||||
|
||||
// We use two columns maximum. Just take the min and natural size of the
|
||||
// first two items, even though strictly speaking it's not correct; we'd
|
||||
// need to calculate how many items we could fit for the height, then
|
||||
// take the biggest preferred width for each column.
|
||||
// In practice the dash gets a fixed width anyways.
|
||||
|
||||
// If we have one child, add its minimum and natural size
|
||||
if (children.length > 0) {
|
||||
let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
|
||||
alloc.min_size += minSize;
|
||||
alloc.natural_size += naturalSize;
|
||||
}
|
||||
// If we have two, add its size, plus DEFAULT_SPACING
|
||||
if (children.length > 1) {
|
||||
let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
|
||||
alloc.min_size += DEFAULT_SPACING + minSize;
|
||||
alloc.natural_size += DEFAULT_SPACING + naturalSize;
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let children = actor.get_children();
|
||||
|
||||
// The width of an item is our allocated width, minus spacing, divided in half.
|
||||
this._itemWidth = Math.floor((forWidth - DEFAULT_SPACING) / 2);
|
||||
|
||||
let maxNatural = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
let [minSize, naturalSize] = child.get_preferred_height(this._itemWidth);
|
||||
maxNatural = Math.max(maxNatural, naturalSize);
|
||||
}
|
||||
|
||||
this._itemHeight = maxNatural;
|
||||
|
||||
let firstColumnChildren = Math.ceil(children.length / 2);
|
||||
alloc.natural_size = (firstColumnChildren * maxNatural +
|
||||
(firstColumnChildren - 1) * DEFAULT_SPACING);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let width = box.x2 - box.x1;
|
||||
let height = box.y2 - box.y1;
|
||||
|
||||
// Make sure this._itemWidth/Height have been computed, even
|
||||
// if the parent actor didn't check our size before allocating.
|
||||
// (Not clear if that is required or not as a Clutter
|
||||
// invariant; this is safe and cheap because of caching.)
|
||||
actor.get_preferred_height(width);
|
||||
|
||||
let children = actor.get_children();
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let columnIndex = 0;
|
||||
let i = 0;
|
||||
// Loop over the children, going vertically down first. When we run
|
||||
// out of vertical space (our y variable is bigger than box.y2), switch
|
||||
// to the second column.
|
||||
while (i < children.length) {
|
||||
let child = children[i];
|
||||
|
||||
if (y + this._itemHeight > box.y2) {
|
||||
// Is this the second column, or we're in
|
||||
// the first column and can't even fit one
|
||||
// item? In that case, break.
|
||||
if (columnIndex == 1 || i == 0) {
|
||||
break;
|
||||
}
|
||||
// Set x to the halfway point.
|
||||
columnIndex += 1;
|
||||
x = x + this._itemWidth + DEFAULT_SPACING;
|
||||
// And y is back to the top.
|
||||
y = 0;
|
||||
// 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();
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = y;
|
||||
childBox.x2 = childBox.x1 + this._itemWidth;
|
||||
childBox.y2 = y + this._itemHeight;
|
||||
|
||||
y = childBox.y2 + DEFAULT_SPACING;
|
||||
|
||||
child.allocate(childBox, flags);
|
||||
this.actor.set_skip_paint(child, false);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (this._checkDocExistence) {
|
||||
// Now we know how many docs we are displaying, queue a check to see if any of them
|
||||
// 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;
|
||||
}
|
||||
|
||||
for (; i < children.length; i++)
|
||||
this.actor.set_skip_paint(children[i], true);
|
||||
},
|
||||
|
||||
_onDocsChanged: function() {
|
||||
this._checkDocExistence = true;
|
||||
Main.queueDeferredWork(this._workId);
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
// Should be kept alive by the _actorsByUri
|
||||
this.actor.remove_all();
|
||||
let docs = this._docManager.getTimestampOrderedInfos();
|
||||
for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
|
||||
let doc = docs[i];
|
||||
let display = this._actorsByUri[doc.uri];
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Any unparented actors must have been deleted
|
||||
for (let uri in this._actorsByUri) {
|
||||
let display = this._actorsByUri[uri];
|
||||
if (display.actor.get_parent() == null) {
|
||||
display.actor.destroy();
|
||||
delete this._actorsByUri[uri];
|
||||
}
|
||||
}
|
||||
this.emit('changed');
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DashDocDisplay.prototype);
|
||||
|
||||
function DocSearchProvider() {
|
||||
this._init();
|
||||
@ -504,9 +29,12 @@ DocSearchProvider.prototype = {
|
||||
'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
activateResult: function(id, params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let docInfo = this._docManager.lookupByUri(id);
|
||||
docInfo.launch();
|
||||
docInfo.launch(params.workspace ? params.workspace.index() : -1);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
|
504
js/ui/endSessionDialog.js
Normal file
@ -0,0 +1,504 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
let _endSessionDialog = null;
|
||||
|
||||
const _ITEM_ICON_SIZE = 48;
|
||||
const _DIALOG_ICON_SIZE = 32;
|
||||
|
||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
||||
|
||||
const EndSessionDialogIface = {
|
||||
name: 'org.gnome.SessionManager.EndSessionDialog',
|
||||
methods: [{ name: 'Open',
|
||||
inSignature: 'uuuao',
|
||||
outSignature: ''
|
||||
}
|
||||
],
|
||||
signals: [{ name: 'Canceled',
|
||||
outSignature: '',
|
||||
}],
|
||||
properties: []
|
||||
};
|
||||
|
||||
const logoutDialogContent = {
|
||||
subjectWithUser: _("Log Out %s"),
|
||||
subject: _("Log Out"),
|
||||
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
|
||||
uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
|
||||
uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
|
||||
endDescription: _("Logging out of the system."),
|
||||
confirmButtonText: _("Log Out"),
|
||||
iconStyleClass: 'end-session-dialog-logout-icon'
|
||||
};
|
||||
|
||||
const shutdownDialogContent = {
|
||||
subject: _("Shut Down"),
|
||||
inhibitedDescription: _("Click Shut Down to quit these applications and shut down the system."),
|
||||
uninhibitedDescription: _("The system will shut down automatically in %d seconds."),
|
||||
endDescription: _("Shutting down the system."),
|
||||
confirmButtonText: _("Shut Down"),
|
||||
iconName: 'system-shutdown',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
const restartDialogContent = {
|
||||
subject: _("Restart"),
|
||||
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
|
||||
uninhibitedDescription: _("The system will restart automatically in %d seconds."),
|
||||
endDescription: _("Restarting the system."),
|
||||
confirmButtonText: _("Restart"),
|
||||
iconName: 'system-shutdown',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
||||
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
|
||||
};
|
||||
|
||||
function findAppFromInhibitor(inhibitor) {
|
||||
let desktopFile = inhibitor.app_id;
|
||||
|
||||
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
||||
desktopFile += '.desktop';
|
||||
|
||||
let candidateDesktopFiles = [];
|
||||
|
||||
candidateDesktopFiles.push(desktopFile);
|
||||
candidateDesktopFiles.push('gnome-' + desktopFile);
|
||||
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let app = null;
|
||||
for (let i = 0; i < candidateDesktopFiles.length; i++) {
|
||||
try {
|
||||
app = appSystem.get_app(candidateDesktopFiles[i]);
|
||||
|
||||
if (app)
|
||||
break;
|
||||
} catch(e) {
|
||||
// ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
function ListItem(app, reason) {
|
||||
this._init(app, reason);
|
||||
}
|
||||
|
||||
ListItem.prototype = {
|
||||
_init: function(app, reason) {
|
||||
this._app = app;
|
||||
this._reason = reason;
|
||||
|
||||
if (this._reason == null)
|
||||
this._reason = '';
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false});
|
||||
|
||||
this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
|
||||
can_focus: true,
|
||||
child: layout,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
|
||||
|
||||
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
|
||||
child: this._icon });
|
||||
layout.add(iconBin);
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
|
||||
vertical: true });
|
||||
layout.add(textLayout);
|
||||
|
||||
this._nameLabel = new St.Label({ text: this._app.get_name(),
|
||||
style_class: 'end-session-dialog-app-list-item-name' });
|
||||
textLayout.add(this._nameLabel,
|
||||
{ expand: false,
|
||||
x_fill: true });
|
||||
|
||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
||||
style_class: 'end-session-dialog-app-list-item-description' });
|
||||
textLayout.add(this._descriptionLabel,
|
||||
{ expand: true,
|
||||
x_fill: true });
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
this._app.activate(-1);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ListItem.prototype);
|
||||
|
||||
// The logout timer only shows updates every 10 seconds
|
||||
// until the last 10 seconds, then it shows updates every
|
||||
// second. This function takes a given time and returns
|
||||
// what we should show to the user for that time.
|
||||
function _roundSecondsToInterval(totalSeconds, secondsLeft, interval) {
|
||||
let time;
|
||||
|
||||
time = Math.ceil(secondsLeft);
|
||||
|
||||
// Final count down is in decrements of 1
|
||||
if (time <= interval)
|
||||
return time;
|
||||
|
||||
// Round up higher than last displayable time interval
|
||||
time += interval - 1;
|
||||
|
||||
// Then round down to that time interval
|
||||
if (time > totalSeconds)
|
||||
time = Math.ceil(totalSeconds);
|
||||
else
|
||||
time -= time % interval;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
function _setLabelText(label, text) {
|
||||
if (text) {
|
||||
label.set_text(text);
|
||||
label.show();
|
||||
} else {
|
||||
label.set_text('');
|
||||
label.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function EndSessionDialog() {
|
||||
if (_endSessionDialog == null) {
|
||||
this._init();
|
||||
DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
|
||||
this);
|
||||
_endSessionDialog = this;
|
||||
}
|
||||
|
||||
return _endSessionDialog;
|
||||
}
|
||||
|
||||
function init() {
|
||||
// This always returns the same singleton object
|
||||
// By instantiating it initially, we register the
|
||||
// bus object, etc.
|
||||
let dialog = new EndSessionDialog();
|
||||
}
|
||||
|
||||
EndSessionDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
_init: function() {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this);
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
|
||||
|
||||
this._secondsLeft = 0;
|
||||
this._totalSecondsToStayOpen = 0;
|
||||
this._inhibitors = [];
|
||||
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, this._onOpened));
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._updateContent));
|
||||
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateContent));
|
||||
|
||||
let mainContentLayout = new St.BoxLayout({ vertical: false });
|
||||
this.contentLayout.add(mainContentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: false });
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
mainContentLayout.add(this._iconBin,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageLayout = new St.BoxLayout({ vertical: true });
|
||||
mainContentLayout.add(messageLayout,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
|
||||
|
||||
messageLayout.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageLayout.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER,
|
||||
Gtk.PolicyType.AUTOMATIC);
|
||||
this.contentLayout.add(scrollView,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(this._applicationList,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.MIDDLE });
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_setIconFromFile: function(iconFile, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
this._iconBin.child = null;
|
||||
if (iconFile) {
|
||||
this._iconBin.show();
|
||||
this._iconBin.set_style('background-image: url("' + iconFile + '");');
|
||||
} else {
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setIconFromName: function(iconName, styleClass) {
|
||||
if (styleClass)
|
||||
this._iconBin.set_style_class_name(styleClass);
|
||||
this._iconBin.set_style(null);
|
||||
|
||||
if (iconName != null) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
|
||||
iconName,
|
||||
St.IconType.SYMBOLIC,
|
||||
_DIALOG_ICON_SIZE);
|
||||
|
||||
this._iconBin.child = icon;
|
||||
this._iconBin.show();
|
||||
} else {
|
||||
this._iconBin.child = null;
|
||||
this._iconBin.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateContent: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
let subject = dialogContent.subject;
|
||||
let description;
|
||||
|
||||
if (this._user.is_loaded && !dialogContent.iconName) {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
|
||||
this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
|
||||
} else if (dialogContent.iconName) {
|
||||
this._setIconFromName(dialogContent.iconName,
|
||||
dialogContent.iconStyleClass);
|
||||
}
|
||||
|
||||
if (this._inhibitors.length > 0) {
|
||||
this._stopTimer();
|
||||
description = dialogContent.inhibitedDescription;
|
||||
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
|
||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||
this._secondsLeft,
|
||||
10);
|
||||
|
||||
if (this._user.is_loaded) {
|
||||
let realName = this._user.get_real_name();
|
||||
|
||||
if (realName != null) {
|
||||
if (dialogContent.subjectWithUser)
|
||||
subject = dialogContent.subjectWithUser.format(realName);
|
||||
|
||||
if (dialogContent.uninhibitedDescriptionWithUser)
|
||||
description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
|
||||
else
|
||||
description = dialogContent.uninhibitedDescription.format(displayTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (!description)
|
||||
description = dialogContent.uninhibitedDescription.format(displayTime);
|
||||
} else {
|
||||
description = dialogContent.endDescription;
|
||||
}
|
||||
|
||||
_setLabelText(this._subjectLabel, subject);
|
||||
_setLabelText(this._descriptionLabel, description);
|
||||
},
|
||||
|
||||
_updateButtons: function() {
|
||||
if (this.state != ModalDialog.State.OPENING &&
|
||||
this.state != ModalDialog.State.OPENED)
|
||||
return;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let confirmButtonText = _("Confirm");
|
||||
|
||||
if (dialogContent.confirmButtonText)
|
||||
confirmButtonText = dialogContent.confirmButtonText;
|
||||
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: confirmButtonText,
|
||||
action: Lang.bind(this, this._confirm)
|
||||
}]);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._stopTimer();
|
||||
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
|
||||
'org.gnome.SessionManager.EndSessionDialog',
|
||||
'Canceled', '', []);
|
||||
this.close(global.get_current_time());
|
||||
},
|
||||
|
||||
_confirm: function() {
|
||||
this._fadeOutDialog();
|
||||
this._stopTimer();
|
||||
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
|
||||
'org.gnome.SessionManager.EndSessionDialog',
|
||||
'Confirmed', '', []);
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
if (this._inhibitors.length == 0)
|
||||
this._startTimer();
|
||||
},
|
||||
|
||||
_startTimer: function() {
|
||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||
Tweener.addTween(this,
|
||||
{ _secondsLeft: 0,
|
||||
time: this._secondsLeft,
|
||||
transition: 'linear',
|
||||
onUpdate: Lang.bind(this, this._updateContent),
|
||||
onComplete: Lang.bind(this, this._confirm),
|
||||
});
|
||||
},
|
||||
|
||||
_stopTimer: function() {
|
||||
Tweener.removeTweens(this);
|
||||
this._secondsLeft = 0;
|
||||
},
|
||||
|
||||
_onInhibitorLoaded: function(inhibitor) {
|
||||
if (this._inhibitors.indexOf(inhibitor) < 0) {
|
||||
// Stale inhibitor
|
||||
return;
|
||||
}
|
||||
|
||||
let app = findAppFromInhibitor(inhibitor);
|
||||
|
||||
if (app) {
|
||||
let item = new ListItem(app, inhibitor.reason);
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.close(global.get_current_time());
|
||||
}));
|
||||
this._applicationList.add(item.actor, { x_fill: true });
|
||||
this._stopTimer();
|
||||
} else {
|
||||
// inhibiting app is a service, not an application
|
||||
this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
|
||||
}
|
||||
|
||||
this._updateContent();
|
||||
},
|
||||
|
||||
OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._inhibitors = [];
|
||||
this._applicationList.destroy_children();
|
||||
this._type = type;
|
||||
|
||||
if (!(this._type in DialogContent))
|
||||
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
|
||||
"Unknown dialog type requested");
|
||||
|
||||
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
|
||||
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
|
||||
|
||||
inhibitor.connect('is-loaded',
|
||||
Lang.bind(this, function() {
|
||||
this._onInhibitorLoaded(inhibitor);
|
||||
}));
|
||||
this._inhibitors.push(inhibitor);
|
||||
}
|
||||
|
||||
if (!this.open(timestamp))
|
||||
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
|
||||
"Cannot grab pointer and keyboard");
|
||||
|
||||
this._updateButtons();
|
||||
this._updateContent();
|
||||
|
||||
let signalId = this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
callback();
|
||||
this.disconnect(signalId);
|
||||
}));
|
||||
}
|
||||
};
|
||||
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);
|
@ -4,7 +4,7 @@ const Clutter = imports.gi.Clutter;;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
|
||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
|
||||
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
@ -65,7 +65,7 @@ function 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') {
|
||||
if (Gettext_gtk30.gettext('default:LTR') == 'default:RTL') {
|
||||
St.Widget.set_default_direction(St.TextDirection.RTL);
|
||||
}
|
||||
|
||||
@ -88,8 +88,6 @@ function init() {
|
||||
|
||||
_blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
|
||||
'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
|
||||
_blockMethod('Gdk.Display.get_device_state', 'global.get_pointer',
|
||||
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
|
||||
_blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
|
||||
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
|
||||
|
||||
|
@ -4,6 +4,8 @@ const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
const ExtensionState = {
|
||||
ENABLED: 1,
|
||||
DISABLED: 2,
|
||||
@ -25,6 +27,36 @@ var disabledExtensions;
|
||||
// GFile for user extensions
|
||||
var userExtensionsDir = null;
|
||||
|
||||
/**
|
||||
* versionCheck:
|
||||
* @required: an array of versions we're compatible with
|
||||
* @current: the version we have
|
||||
*
|
||||
* Check if a component is compatible for an extension.
|
||||
* @required is an array, and at least one version must match.
|
||||
* @current must be in the format <major>.<minor>.<point>.<micro>
|
||||
* <micro> is always ignored
|
||||
* <point> is ignored if <minor> is even (so you can target the
|
||||
* whole stable release)
|
||||
* <minor> and <major> must match
|
||||
* Each target version must be at least <major> and <minor>
|
||||
*/
|
||||
function versionCheck(required, current) {
|
||||
let currentArray = current.split('.');
|
||||
let major = currentArray[0];
|
||||
let minor = currentArray[1];
|
||||
let point = currentArray[2];
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
let requiredArray = required[i].split('.');
|
||||
if (requiredArray[0] == major &&
|
||||
requiredArray[1] == minor &&
|
||||
(requiredArray[2] == point ||
|
||||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadExtension(dir, enabled, type) {
|
||||
let info;
|
||||
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
|
||||
@ -43,8 +75,8 @@ function loadExtension(dir, enabled, type) {
|
||||
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
|
||||
return;
|
||||
}
|
||||
let requiredProperties = ['uuid', 'name', 'description'];
|
||||
for (let i = 0; i < requiredProperties; i++) {
|
||||
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
|
||||
for (let i = 0; i < requiredProperties.length; i++) {
|
||||
let prop = requiredProperties[i];
|
||||
if (!meta[prop]) {
|
||||
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
|
||||
@ -68,6 +100,12 @@ function loadExtension(dir, enabled, type) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
|
||||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
|
||||
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
|
||||
return;
|
||||
}
|
||||
|
||||
extensionMeta[meta.uuid] = meta;
|
||||
extensionMeta[meta.uuid].type = type;
|
||||
extensionMeta[meta.uuid].path = dir.get_path();
|
||||
@ -112,7 +150,7 @@ function loadExtension(dir, enabled, type) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
extensionModule.main();
|
||||
extensionModule.main(meta);
|
||||
} catch (e) {
|
||||
if (stylesheetPath != null)
|
||||
theme.unload_stylesheet(stylesheetPath);
|
||||
|
@ -1,693 +0,0 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const RedisplayFlags = { NONE: 0,
|
||||
FULL: 1 << 1,
|
||||
SUBSEARCH: 1 << 2,
|
||||
IMMEDIATE: 1 << 3 };
|
||||
|
||||
// Used by subclasses
|
||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||
const PREVIEW_ICON_SIZE = 96;
|
||||
|
||||
/* 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
|
||||
* it by highlighting it with a different background color than the default.
|
||||
*/
|
||||
function GenericDisplayItem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplayItem.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ style_class: 'generic-display-item',
|
||||
reactive: true });
|
||||
|
||||
this.actor._delegate = this;
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// Activates the item by launching it
|
||||
this.emit('activate');
|
||||
return true;
|
||||
}));
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
|
||||
this._iconBin = new St.Bin();
|
||||
this.actor.add(this._iconBin);
|
||||
|
||||
this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
|
||||
vertical: true });
|
||||
this.actor.add(this._infoText, { expand: true, y_fill: false });
|
||||
|
||||
this._name = null;
|
||||
this._description = null;
|
||||
this._icon = null;
|
||||
|
||||
this._initialLoadComplete = false;
|
||||
|
||||
// An array of details description actors that we create over time for the item.
|
||||
// It is used for updating the description text inside the details actor when
|
||||
// the description text for the item is updated.
|
||||
this._detailsDescriptions = [];
|
||||
},
|
||||
|
||||
//// Draggable object interface ////
|
||||
|
||||
// Returns a cloned texture of the item's icon to represent the item as it
|
||||
// is being dragged.
|
||||
getDragActor: function(stageX, stageY) {
|
||||
return this._createIcon();
|
||||
},
|
||||
|
||||
// Returns the item icon, a separate copy of which is used to
|
||||
// represent the item as it is being dragged. This is used to
|
||||
// determine a snap-back location for the drag icon if it does
|
||||
// not get accepted by any drop target.
|
||||
getDragActorSource: function() {
|
||||
return this._icon;
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
// Highlights the item by setting a different background color than the default
|
||||
// if isSelected is true, removes the highlighting otherwise.
|
||||
markSelected: function(isSelected) {
|
||||
if (isSelected)
|
||||
this.actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
/*
|
||||
* Returns an actor containing item details. In the future details can have more information than what
|
||||
* the preview pop-up has and be item-type specific.
|
||||
*/
|
||||
createDetailsActor: function() {
|
||||
|
||||
let details = new St.BoxLayout({ style_class: 'generic-display-container',
|
||||
vertical: true });
|
||||
|
||||
let mainDetails = new St.BoxLayout({ style_class: 'generic-display-container' });
|
||||
|
||||
// Inner box with name and description
|
||||
let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
|
||||
vertical: true });
|
||||
let detailsName = new St.Label({ style_class: 'generic-display-details-name',
|
||||
text: this._name.text });
|
||||
textDetails.add(detailsName);
|
||||
|
||||
let detailsDescription = new St.Label({ text: this._description.text });
|
||||
textDetails.add(detailsDescription);
|
||||
|
||||
this._detailsDescriptions.push(detailsDescription);
|
||||
|
||||
mainDetails.add(textDetails, { expand: true });
|
||||
|
||||
let previewIcon = this._createPreviewIcon();
|
||||
let largePreviewIcon = this._createLargePreviewIcon();
|
||||
|
||||
if (previewIcon != null && largePreviewIcon == null) {
|
||||
mainDetails.insert_actor(previewIcon, 0);
|
||||
}
|
||||
|
||||
details.add(mainDetails);
|
||||
|
||||
if (largePreviewIcon != null) {
|
||||
details.add(largePreviewIcon);
|
||||
}
|
||||
|
||||
return details;
|
||||
},
|
||||
|
||||
// Destroys the item.
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
//// Pure virtual public methods ////
|
||||
|
||||
// Performes an action associated with launching this item, such as opening a file or an application.
|
||||
launch: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
/*
|
||||
* Creates the graphical elements for the item based on the item information.
|
||||
*
|
||||
* nameText - name of the item
|
||||
* descriptionText - short description of the item
|
||||
*/
|
||||
_setItemInfo: function(nameText, descriptionText) {
|
||||
if (this._name != null) {
|
||||
// this also removes this._name from the parent container,
|
||||
// so we don't need to call this.actor.remove_actor(this._name) directly
|
||||
this._name.destroy();
|
||||
this._name = null;
|
||||
}
|
||||
if (this._description != null) {
|
||||
this._description.destroy();
|
||||
this._description = null;
|
||||
}
|
||||
if (this._icon != null) {
|
||||
// though we get the icon from elsewhere, we assume its ownership here,
|
||||
// and therefore should be responsible for distroying it
|
||||
this._icon.destroy();
|
||||
this._icon = null;
|
||||
}
|
||||
|
||||
this._icon = this._createIcon();
|
||||
this._iconBin.set_child(this._icon);
|
||||
|
||||
this._name = new St.Label({ style_class: 'generic-display-item-name',
|
||||
text: nameText });
|
||||
this._infoText.add(this._name);
|
||||
|
||||
this._description = new St.Label({ style_class: 'generic-display-item-description',
|
||||
text: descriptionText ? descriptionText : '' });
|
||||
this._infoText.add(this._description);
|
||||
},
|
||||
|
||||
// Sets the description text for the item, including the description text
|
||||
// in the details actors that have been created for the item.
|
||||
_setDescriptionText: function(text) {
|
||||
this._description.text = text;
|
||||
for (let i = 0; i < this._detailsDescriptions.length; i++) {
|
||||
let detailsDescription = this._detailsDescriptions[i];
|
||||
if (detailsDescription != null) {
|
||||
detailsDescription.text = text;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//// Virtual protected methods ////
|
||||
|
||||
// Creates and returns a large preview icon, but only if we have a detailed image.
|
||||
_createLargePreviewIcon : function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
//// Pure virtual protected methods ////
|
||||
|
||||
// Returns an icon for the item.
|
||||
_createIcon: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Returns a preview icon for the item.
|
||||
_createPreviewIcon: function() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
//// Private methods ////
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(GenericDisplayItem.prototype);
|
||||
|
||||
/* This is a virtual class that represents a display containing a collection of items
|
||||
* that can be filtered with a search string.
|
||||
*/
|
||||
function GenericDisplay() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
GenericDisplay.prototype = {
|
||||
_init : function() {
|
||||
this._search = '';
|
||||
this._expanded = false;
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
vshadows: true });
|
||||
this._list = new St.BoxLayout({ style_class: 'generic-display-container',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._list);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
|
||||
this._pendingRedisplay = RedisplayFlags.NONE;
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
|
||||
|
||||
// map<itemId, Object> where Object represents the item info
|
||||
this._allItems = {};
|
||||
// set<itemId>
|
||||
this._matchedItems = {};
|
||||
// sorted array of items matched by search
|
||||
this._matchedItemKeys = [];
|
||||
// map<itemId, GenericDisplayItem>
|
||||
this._displayedItems = {};
|
||||
this._openDetailIndex = -1;
|
||||
this._selectedIndex = -1;
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
// Sets the search string and displays the matching items.
|
||||
setSearch: function(text) {
|
||||
let lowertext = text.toLowerCase();
|
||||
if (lowertext == this._search) {
|
||||
return;
|
||||
}
|
||||
let flags = RedisplayFlags.IMMEDIATE;
|
||||
if (this._search != '') {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
this._search = lowertext;
|
||||
this._redisplay(flags);
|
||||
},
|
||||
|
||||
// Launches the item that is currently selected, closing the Overview
|
||||
activateSelected: function() {
|
||||
if (this._selectedIndex != -1) {
|
||||
let selected = this._findDisplayedByIndex(this._selectedIndex);
|
||||
selected.launch();
|
||||
this.unsetSelected();
|
||||
Main.overview.hide();
|
||||
}
|
||||
},
|
||||
|
||||
// Moves the selection one up. If the selection was already on the top item, it's moved
|
||||
// to the bottom one. Returns true if the selection actually moved up, false if it wrapped
|
||||
// around to the bottom.
|
||||
selectUp: function() {
|
||||
let count = this._getVisibleCount();
|
||||
let selectedUp = true;
|
||||
let prev = this._selectedIndex - 1;
|
||||
if (this._selectedIndex <= 0) {
|
||||
prev = count - 1;
|
||||
selectedUp = false;
|
||||
}
|
||||
this._selectIndex(prev);
|
||||
return selectedUp;
|
||||
},
|
||||
|
||||
// Moves the selection one down. If the selection was already on the bottom item, it's moved
|
||||
// to the top one. Returns true if the selection actually moved down, false if it wrapped
|
||||
// around to the top.
|
||||
selectDown: function() {
|
||||
let count = this._getVisibleCount();
|
||||
let selectedDown = true;
|
||||
let next = this._selectedIndex + 1;
|
||||
if (this._selectedIndex == count - 1) {
|
||||
next = 0;
|
||||
selectedDown = false;
|
||||
}
|
||||
this._selectIndex(next);
|
||||
return selectedDown;
|
||||
},
|
||||
|
||||
// Selects the first item among the displayed items.
|
||||
selectFirstItem: function() {
|
||||
if (this.hasItems())
|
||||
this._selectIndex(0);
|
||||
},
|
||||
|
||||
// Selects the last item among the displayed items.
|
||||
selectLastItem: function() {
|
||||
let count = this._getVisibleCount();
|
||||
if (this.hasItems())
|
||||
this._selectIndex(count - 1);
|
||||
},
|
||||
|
||||
// Returns true if the display has some item selected.
|
||||
hasSelected: function() {
|
||||
return this._selectedIndex != -1;
|
||||
},
|
||||
|
||||
// Removes selection if some display item is selected.
|
||||
unsetSelected: function() {
|
||||
this._selectIndex(-1);
|
||||
},
|
||||
|
||||
// Returns true if the display has any displayed items.
|
||||
hasItems: function() {
|
||||
// TODO: figure out why this._list.displayedCount is returning a
|
||||
// positive number when this._mathedItems.length is 0
|
||||
// This can be triggered if a search string is entered for which there are no matches.
|
||||
// log('this._mathedItems.length: ' + this._matchedItems.length + ' this._list.displayedCount ' + this._list.displayedCount);
|
||||
return this._matchedItemKeys.length > 0;
|
||||
},
|
||||
|
||||
getMatchedItemsCount: function() {
|
||||
return this._matchedItemKeys.length;
|
||||
},
|
||||
|
||||
// Load the initial state
|
||||
load: function() {
|
||||
this._redisplay(RedisplayFlags.FULL);
|
||||
},
|
||||
|
||||
// Should be called when the display is closed
|
||||
resetState: function() {
|
||||
this._filterReset();
|
||||
this._openDetailIndex = -1;
|
||||
this.actor.get_vscroll_bar().get_adjustment().value = 0;
|
||||
},
|
||||
|
||||
// Returns an actor which acts as a sidebar; this is used for
|
||||
// the applications category view
|
||||
getNavigationArea: function () {
|
||||
return null;
|
||||
},
|
||||
|
||||
createDetailsForIndex: function(index) {
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
return item.createDetailsActor();
|
||||
},
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
_recreateDisplayItems: function() {
|
||||
this._removeAllDisplayItems();
|
||||
this._setDefaultList();
|
||||
for (let itemId in this._allItems) {
|
||||
this._addDisplayItem(itemId);
|
||||
}
|
||||
},
|
||||
|
||||
// Creates a display item based on the information associated with itemId
|
||||
// and adds it to the list of displayed items, but does not yet display it.
|
||||
_addDisplayItem : function(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.');
|
||||
return;
|
||||
}
|
||||
|
||||
let itemInfo = this._allItems[itemId];
|
||||
let displayItem = this._createDisplayItem(itemInfo);
|
||||
|
||||
displayItem.connect('activate',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
// update the selection
|
||||
this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
|
||||
this.activateSelected();
|
||||
}));
|
||||
|
||||
displayItem.connect('show-details',
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
let index = this._list.get_children().indexOf(displayItem.actor);
|
||||
/* Close the details pane if already open */
|
||||
if (index == this._openDetailIndex) {
|
||||
this._openDetailIndex = -1;
|
||||
this.emit('show-details', -1);
|
||||
} else {
|
||||
this._openDetailIndex = index;
|
||||
this.emit('show-details', index);
|
||||
}
|
||||
}));
|
||||
this._displayedItems[itemId] = displayItem;
|
||||
},
|
||||
|
||||
// Removes an item identifed by the itemId from the displayed items.
|
||||
_removeDisplayItem: function(itemId) {
|
||||
let children = this._list.get_children();
|
||||
let count = children.length;
|
||||
let displayItem = this._displayedItems[itemId];
|
||||
let displayItemIndex = children.indexOf(displayItem.actor);
|
||||
|
||||
if (this.hasSelected() && count == 1) {
|
||||
this.unsetSelected();
|
||||
} else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
|
||||
this.selectUp();
|
||||
}
|
||||
|
||||
displayItem.destroy();
|
||||
|
||||
delete this._displayedItems[itemId];
|
||||
},
|
||||
|
||||
// Removes all displayed items.
|
||||
_removeAllDisplayItems: function() {
|
||||
this.unsetSelected();
|
||||
for (itemId in this._displayedItems)
|
||||
this._removeDisplayItem(itemId);
|
||||
},
|
||||
|
||||
// Return true if there's an active search or other constraint
|
||||
// on the list
|
||||
_filterActive: function() {
|
||||
return this._search != '';
|
||||
},
|
||||
|
||||
// Called when we are resetting state
|
||||
_filterReset: function() {
|
||||
this.unsetSelected();
|
||||
},
|
||||
|
||||
_compareSearchMatch: function(a, b) {
|
||||
let countA = this._matchedItems[a];
|
||||
let countB = this._matchedItems[b];
|
||||
if (countA > countB)
|
||||
return -1;
|
||||
else if (countA < countB)
|
||||
return 1;
|
||||
else
|
||||
return this._compareItems(a, b);
|
||||
},
|
||||
|
||||
_setMatches: function(matches) {
|
||||
this._matchedItems = matches;
|
||||
this._matchedItemKeys = [];
|
||||
for (let itemId in this._matchedItems) {
|
||||
this._matchedItemKeys.push(itemId);
|
||||
}
|
||||
this._matchedItemKeys.sort(Lang.bind(this, this._compareSearchMatch));
|
||||
},
|
||||
|
||||
/**
|
||||
* _redisplaySubSearch:
|
||||
* A somewhat more optimized function called when we know
|
||||
* that we're going to be displaying a subset of the items
|
||||
* we already had, in the same order. In that case, we can
|
||||
* just hide the actors that shouldn't be shown.
|
||||
*/
|
||||
_redisplaySubSearch: function() {
|
||||
let matches = this._getSearchMatchedItems(true);
|
||||
|
||||
// Just hide all from the old set,
|
||||
// we'll show the ones we want below
|
||||
for (let itemId in this._displayedItems) {
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.hide();
|
||||
}
|
||||
|
||||
this._setMatches(matches);
|
||||
|
||||
for (let itemId in matches) {
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.show();
|
||||
}
|
||||
this._list.queue_relayout();
|
||||
},
|
||||
|
||||
_redisplayReordering: function() {
|
||||
if (!this._filterActive()) {
|
||||
this._setDefaultList();
|
||||
} else {
|
||||
this._setMatches(this._getSearchMatchedItems(false));
|
||||
}
|
||||
this._list.remove_all();
|
||||
for (let i = 0; i < this._matchedItemKeys.length; i++) {
|
||||
let itemId = this._matchedItemKeys[i];
|
||||
let item = this._displayedItems[itemId];
|
||||
item.actor.show();
|
||||
this._list.add_actor(item.actor);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Updates the displayed items, applying the search string if one exists.
|
||||
* @flags: Flags controlling redisplay behavior as follows:
|
||||
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
|
||||
* 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) {
|
||||
let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
|
||||
if (!immediate && !this.actor.mapped) {
|
||||
this._pendingRedisplay |= flags;
|
||||
return;
|
||||
}
|
||||
|
||||
let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
|
||||
let fullReload = (flags & RedisplayFlags.FULL) != 0;
|
||||
|
||||
let hadSelected = this.hasSelected();
|
||||
this.unsetSelected();
|
||||
|
||||
if (!this._initialLoadComplete)
|
||||
fullReload = true;
|
||||
|
||||
if (!this._refreshCache())
|
||||
fullReload = true;
|
||||
|
||||
if (fullReload) {
|
||||
this._recreateDisplayItems();
|
||||
this._initialLoadComplete = true;
|
||||
}
|
||||
|
||||
if (isSubSearch) {
|
||||
this._redisplaySubSearch();
|
||||
} else {
|
||||
this._redisplayReordering();
|
||||
}
|
||||
|
||||
if (hadSelected) {
|
||||
this._selectedIndex = -1;
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
this.emit('redisplayed');
|
||||
},
|
||||
|
||||
//// Pure virtual protected methods ////
|
||||
|
||||
// Performs the steps needed to have the latest information about the items.
|
||||
// Implementation should return %true if we are up to date, and %false
|
||||
// if a full reload occurred.
|
||||
_refreshCache: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Sets the list of the displayed items based on the default sorting order.
|
||||
// The default sorting order is specific to each implementing class.
|
||||
_setDefaultList: function() {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on the order in which the
|
||||
// items should be displayed.
|
||||
// Intended to be used as a compareFunction for array.sort().
|
||||
// Returns an integer value indicating the result of the comparison.
|
||||
_compareItems: function(itemIdA, itemIdB) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Checks if the item info can be a match for the search string.
|
||||
// Returns a boolean flag indicating if that's the case.
|
||||
_isInfoMatching: function(itemInfo, search) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
// Creates a display item based on itemInfo.
|
||||
_createDisplayItem: function(itemInfo) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_getItemSearchScore: function(itemId, terms) {
|
||||
let item = this._allItems[itemId];
|
||||
let score = 0;
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
let term = terms[i];
|
||||
if (this._isInfoMatching(item, term)) {
|
||||
score++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
},
|
||||
|
||||
_getSearchMatchedItems: function(isSubSearch) {
|
||||
// Break the search up into terms, and search for each
|
||||
// individual term. Keep track of the number of terms
|
||||
// each item matched.
|
||||
let terms = this._search.split(/\s+/);
|
||||
let matchScores = {};
|
||||
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._matchedItemKeys.length; i++) {
|
||||
let itemId = this._matchedItemKeys[i];
|
||||
let score = this._getItemSearchScore(itemId, terms);
|
||||
if (score > 0)
|
||||
matchScores[itemId] = score;
|
||||
}
|
||||
} else {
|
||||
for (let itemId in this._displayedItems) {
|
||||
let score = this._getItemSearchScore(itemId, terms);
|
||||
if (score > 0)
|
||||
matchScores[itemId] = score;
|
||||
}
|
||||
}
|
||||
return matchScores;
|
||||
},
|
||||
|
||||
// Returns a display item based on its index in the ordering of the
|
||||
// display children.
|
||||
_findDisplayedByIndex: function(index) {
|
||||
let actor = this._list.get_children()[index];
|
||||
return this._findDisplayedByActor(actor);
|
||||
},
|
||||
|
||||
// Returns a display item based on the actor that represents it in
|
||||
// the display.
|
||||
_findDisplayedByActor: function(actor) {
|
||||
for (itemId in this._displayedItems) {
|
||||
let item = this._displayedItems[itemId];
|
||||
if (item.actor == actor) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Selects (e.g. highlights) a display item at the provided index,
|
||||
// updates this.selectedItemDetails actor, and emits 'selected' signal.
|
||||
_selectIndex: function(index) {
|
||||
// Cleanup from the previous item
|
||||
if (this.hasSelected()) {
|
||||
this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
|
||||
}
|
||||
|
||||
this._selectedIndex = index;
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
// Mark the new item as selected and create its details pane
|
||||
let item = this._findDisplayedByIndex(index);
|
||||
item.markSelected(true);
|
||||
this.emit('selected');
|
||||
},
|
||||
|
||||
_getVisibleCount: function() {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(GenericDisplay.prototype);
|
@ -17,7 +17,8 @@ function BaseIcon(label, createIcon) {
|
||||
BaseIcon.prototype = {
|
||||
_init : function(label, params) {
|
||||
params = Params.parse(params, { createIcon: null,
|
||||
setSizeManually: false });
|
||||
setSizeManually: false,
|
||||
showLabel: true });
|
||||
this.actor = new St.Bin({ style_class: 'overview-icon',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -40,8 +41,12 @@ BaseIcon.prototype = {
|
||||
|
||||
box.add_actor(this._iconBin);
|
||||
|
||||
this._name = new St.Label({ text: label });
|
||||
box.add_actor(this._name);
|
||||
if (params.showLabel) {
|
||||
this._name = new St.Label({ text: label });
|
||||
box.add_actor(this._name);
|
||||
} else {
|
||||
this._name = null;
|
||||
}
|
||||
|
||||
if (params.createIcon)
|
||||
this.createIcon = params.createIcon;
|
||||
@ -55,27 +60,34 @@ BaseIcon.prototype = {
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
|
||||
let iconSize = availHeight;
|
||||
|
||||
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(-1);
|
||||
let preferredHeight = labelNatHeight + this._spacing + iconNatHeight;
|
||||
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
||||
: labelMinHeight;
|
||||
let iconSize = availHeight - this._spacing - labelHeight;
|
||||
let iconPadding = (availWidth - iconSize) / 2;
|
||||
let [iconMinWidth, iconNatWidth] = this._iconBin.get_preferred_width(-1);
|
||||
let preferredHeight = iconNatHeight;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
childBox.x1 = iconPadding;
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = availWidth - iconPadding;
|
||||
childBox.y2 = iconSize;
|
||||
this._iconBin.allocate(childBox, flags);
|
||||
if (this._name) {
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
|
||||
preferredHeight += this._spacing + labelNatHeight;
|
||||
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = availWidth;
|
||||
childBox.y1 = iconSize + this._spacing;
|
||||
childBox.y2 = childBox.y1 + labelHeight;
|
||||
this._name.allocate(childBox, flags);
|
||||
let labelHeight = availHeight >= preferredHeight ? labelNatHeight
|
||||
: labelMinHeight;
|
||||
iconSize -= this._spacing + labelHeight;
|
||||
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = availWidth;
|
||||
childBox.y1 = iconSize + this._spacing;
|
||||
childBox.y2 = childBox.y1 + labelHeight;
|
||||
this._name.allocate(childBox, flags);
|
||||
}
|
||||
|
||||
childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
|
||||
childBox.y1 = Math.floor((iconSize - iconNatHeight) / 2);
|
||||
childBox.x2 = childBox.x1 + iconNatWidth;
|
||||
childBox.y2 = childBox.y1 + iconNatHeight;
|
||||
this._iconBin.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
@ -84,9 +96,14 @@ BaseIcon.prototype = {
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(forWidth);
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
|
||||
alloc.min_size = iconMinHeight + this._spacing + labelMinHeight;
|
||||
alloc.natural_size = iconNatHeight + this._spacing + labelNatHeight;
|
||||
alloc.min_size = iconMinHeight;
|
||||
alloc.natural_size = iconNatHeight;
|
||||
|
||||
if (this._name) {
|
||||
let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
|
||||
alloc.min_size += this._spacing + labelMinHeight;
|
||||
alloc.natural_size += this._spacing + labelNatHeight;
|
||||
}
|
||||
},
|
||||
|
||||
// This can be overridden by a subclass, or by the createIcon
|
||||
@ -165,8 +182,16 @@ IconGrid.prototype = {
|
||||
alloc.natural_size = nColumns * this._item_size + totalSpacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
_getVisibleChildren: function() {
|
||||
let children = this._grid.get_children();
|
||||
children = children.filter(function(actor) {
|
||||
return actor.visible;
|
||||
});
|
||||
return children;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._getVisibleChildren();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
@ -182,7 +207,7 @@ IconGrid.prototype = {
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
let children = this._grid.get_children();
|
||||
let children = this._getVisibleChildren();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Cogl = imports.gi.Cogl;
|
||||
const GConf = imports.gi.GConf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
@ -13,6 +14,7 @@ const Mainloop = imports.mainloop;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const History = imports.misc.history;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const Link = imports.ui.link;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -36,6 +38,8 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
const HISTORY_KEY = 'looking-glass-history';
|
||||
|
||||
function Notebook() {
|
||||
this._init();
|
||||
}
|
||||
@ -359,6 +363,30 @@ ObjInspector.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function addBorderPaintHook(actor) {
|
||||
let signalId = actor.connect_after('paint',
|
||||
function () {
|
||||
let color = new Cogl.Color();
|
||||
color.init_from_4ub(0xff, 0, 0, 0xc4);
|
||||
Cogl.set_source_color(color);
|
||||
|
||||
let geom = actor.get_allocation_geometry();
|
||||
let width = 2;
|
||||
|
||||
// clockwise order
|
||||
Cogl.rectangle(0, 0, geom.width, width);
|
||||
Cogl.rectangle(geom.width - width, width,
|
||||
geom.width, geom.height);
|
||||
Cogl.rectangle(0, geom.height,
|
||||
geom.width - width, geom.height - width);
|
||||
Cogl.rectangle(0, geom.height - width,
|
||||
width, width);
|
||||
});
|
||||
|
||||
actor.queue_redraw();
|
||||
return signalId;
|
||||
}
|
||||
|
||||
function Inspector() {
|
||||
this._init();
|
||||
}
|
||||
@ -398,6 +426,9 @@ Inspector.prototype = {
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
if (!this._eventHandler)
|
||||
return;
|
||||
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let [minWidth, minHeight, natWidth, natHeight] =
|
||||
@ -415,6 +446,7 @@ Inspector.prototype = {
|
||||
Clutter.ungrab_pointer(this._eventHandler);
|
||||
Clutter.ungrab_keyboard(this._eventHandler);
|
||||
this._eventHandler.destroy();
|
||||
this._eventHandler = null;
|
||||
this.emit('closed');
|
||||
},
|
||||
|
||||
@ -490,10 +522,13 @@ Inspector.prototype = {
|
||||
let position = '[inspect x: ' + stageX + ' y: ' + stageY + ']';
|
||||
this._displayText.text = '';
|
||||
this._displayText.text = position + ' ' + this._target;
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = Shell.add_hook_paint_red_border(this._target);
|
||||
|
||||
if (this._borderPaintTarget != this._target) {
|
||||
if (this._borderPaintTarget != null)
|
||||
this._borderPaintTarget.disconnect(this._borderPaintId);
|
||||
this._borderPaintTarget = this._target;
|
||||
this._borderPaintId = addBorderPaintHook(this._target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -639,18 +674,10 @@ function LookingGlass() {
|
||||
|
||||
LookingGlass.prototype = {
|
||||
_init : function() {
|
||||
this._idleHistorySaveId = 0;
|
||||
let historyPath = global.userdatadir + '/lookingglass-history.txt';
|
||||
this._historyFile = Gio.file_new_for_path(historyPath);
|
||||
this._savedText = null;
|
||||
this._historyNavIndex = -1;
|
||||
this._history = [];
|
||||
this._borderPaintTarget = null;
|
||||
this._borderPaintId = 0;
|
||||
this._borderDestroyId = 0;
|
||||
|
||||
this._readHistory();
|
||||
|
||||
this._open = false;
|
||||
|
||||
this._offset = 0;
|
||||
@ -663,11 +690,11 @@ LookingGlass.prototype = {
|
||||
style_class: 'lg-dialog',
|
||||
vertical: true,
|
||||
visible: false });
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
let gconf = GConf.Client.get_default();
|
||||
gconf.add_dir('/desktop/gnome/interface', GConf.ClientPreloadType.PRELOAD_NONE);
|
||||
gconf.notify_add('/desktop/gnome/interface/monospace_font_name',
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._interfaceSettings.connect('changed::monospace-font-name',
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._updateFont();
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
@ -747,39 +774,30 @@ LookingGlass.prototype = {
|
||||
if (text == '')
|
||||
return true;
|
||||
this._evaluate(text);
|
||||
this._historyNavIndex = -1;
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._history = new History.HistoryManager(HISTORY_KEY);
|
||||
this._history.connect('changed', Lang.bind(this, function(history, text) {
|
||||
this._entry.text = text;
|
||||
}));
|
||||
|
||||
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Up) {
|
||||
if (this._historyNavIndex >= this._history.length - 1)
|
||||
return true;
|
||||
this._historyNavIndex++;
|
||||
if (this._historyNavIndex == 0)
|
||||
this._savedText = this._entry.text;
|
||||
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
|
||||
this._history.prevItem(o.get_text());
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (this._historyNavIndex <= 0)
|
||||
return true;
|
||||
this._historyNavIndex--;
|
||||
if (this._historyNavIndex < 0)
|
||||
this._entry.text = this._savedText;
|
||||
else
|
||||
this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
|
||||
this._history.nextItem(o.get_text());
|
||||
return true;
|
||||
} else {
|
||||
this._historyNavIndex = -1;
|
||||
this._savedText = null;
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_updateFont: function() {
|
||||
let gconf = GConf.Client.get_default();
|
||||
let fontName = gconf.get_string('/desktop/gnome/interface/monospace_font_name');
|
||||
let fontName = this._interfaceSettings.get_string('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);
|
||||
@ -790,29 +808,6 @@ LookingGlass.prototype = {
|
||||
+ 'font-family: "' + fontDesc.get_family() + '";';
|
||||
},
|
||||
|
||||
_readHistory: function () {
|
||||
if (!this._historyFile.query_exists(null))
|
||||
return;
|
||||
let [result, contents, length, etag] = this._historyFile.load_contents(null);
|
||||
this._history = contents.split('\n').filter(function (e) { return e != ''; });
|
||||
},
|
||||
|
||||
_queueHistorySave: function() {
|
||||
if (this._idleHistorySaveId > 0)
|
||||
return;
|
||||
this._idleHistorySaveId = Mainloop.timeout_add_seconds(5, Lang.bind(this, this._doSaveHistory));
|
||||
},
|
||||
|
||||
_doSaveHistory: function () {
|
||||
this._idleHistorySaveId = false;
|
||||
let output = this._historyFile.replace(null, true, Gio.FileCreateFlags.NONE, null);
|
||||
let dataOut = new Gio.DataOutputStream({ base_stream: output });
|
||||
dataOut.put_string(this._history.join('\n'), null);
|
||||
dataOut.put_string('\n', null);
|
||||
dataOut.close(null);
|
||||
return false;
|
||||
},
|
||||
|
||||
_pushResult: function(command, obj) {
|
||||
let index = this._results.length + this._offset;
|
||||
let result = new Result('>>> ' + command, obj, index);
|
||||
@ -824,7 +819,7 @@ LookingGlass.prototype = {
|
||||
}
|
||||
if (obj instanceof Clutter.Actor) {
|
||||
this._borderPaintTarget = obj;
|
||||
this._borderPaintId = Shell.add_hook_paint_red_border(obj);
|
||||
this._borderPaintId = addBorderPaintHook(obj);
|
||||
this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
|
||||
this._borderDestroyId = 0;
|
||||
this._borderPaintTarget = null;
|
||||
@ -843,8 +838,7 @@ LookingGlass.prototype = {
|
||||
},
|
||||
|
||||
_evaluate : function(command) {
|
||||
this._history.push(command);
|
||||
this._queueHistorySave();
|
||||
this._history.addItem(command);
|
||||
|
||||
let fullCmd = commandHeader + command;
|
||||
|
||||
@ -925,20 +919,16 @@ LookingGlass.prototype = {
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this.actor))
|
||||
if (!Main.pushModal(this._entry))
|
||||
return;
|
||||
|
||||
this._keyPressEventId = global.stage.connect('key-press-event',
|
||||
Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
this.actor.show();
|
||||
this.actor.lower(Main.chrome.actor);
|
||||
this._open = true;
|
||||
this._history.lastItem();
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
global.stage.set_key_focus(this._entry);
|
||||
|
||||
// We inverse compensate for the slow-down so you can change the factor
|
||||
// through LookingGlass without long waits.
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
@ -951,12 +941,8 @@ LookingGlass.prototype = {
|
||||
if (!this._open)
|
||||
return;
|
||||
|
||||
if (this._keyPressEventId)
|
||||
global.stage.disconnect(this._keyPressEventId);
|
||||
|
||||
this._objInspector.actor.hide();
|
||||
|
||||
this._historyNavIndex = -1;
|
||||
this._open = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
|
||||
@ -966,7 +952,7 @@ LookingGlass.prototype = {
|
||||
this._borderPaintTarget = null;
|
||||
}
|
||||
|
||||
Main.popModal(this.actor);
|
||||
Main.popModal(this._entry);
|
||||
|
||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
||||
transition: 'easeOutQuad',
|
||||
|
@ -112,6 +112,11 @@ ShellMagnifier.prototype = {
|
||||
* [left, top, right, bottom].
|
||||
* @viewPort Array of integers, [left, top, right, bottom] that defines
|
||||
* the position of the ZoomRegion on screen.
|
||||
*
|
||||
* FIXME: The arguments here are redundant, since the width and height of
|
||||
* the ROI are determined by the viewport and magnification factors.
|
||||
* We ignore the passed in width and height.
|
||||
*
|
||||
* @return The newly created ZoomRegion.
|
||||
*/
|
||||
createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) {
|
||||
|
318
js/ui/main.js
@ -17,8 +17,13 @@ const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Chrome = imports.ui.chrome;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
||||
const Environment = imports.ui.environment;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
@ -34,7 +39,9 @@ const ShellDBus = imports.ui.shellDBus;
|
||||
const TelepathyClient = imports.ui.telepathyClient;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
@ -50,15 +57,19 @@ let messageTray = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let telepathyClient = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let recorder = null;
|
||||
let shellDBusService = null;
|
||||
let modalCount = 0;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
let xdndHandler = null;
|
||||
let statusIconDispatcher = null;
|
||||
let _errorLogStack = [];
|
||||
let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
|
||||
let background = null;
|
||||
|
||||
@ -103,10 +114,8 @@ function start() {
|
||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
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);
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
loadTheme();
|
||||
|
||||
let shellwm = global.window_manager;
|
||||
shellwm.takeover_keybinding('panel_main_menu');
|
||||
@ -125,6 +134,7 @@ function start() {
|
||||
global.stage.add_actor(uiGroup);
|
||||
|
||||
placesManager = new PlaceDisplay.PlacesManager();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
overview = new Overview.Overview();
|
||||
chrome = new Chrome.Chrome();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
@ -136,6 +146,12 @@ function start() {
|
||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||
telepathyClient = new TelepathyClient.Client();
|
||||
|
||||
overview.init();
|
||||
statusIconDispatcher.start(messageTray.actor);
|
||||
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
ctrlAltTabManager.addGroup(panel.actor, _("Panel"), 'gnome-panel');
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
||||
@ -162,11 +178,21 @@ function start() {
|
||||
}
|
||||
});
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
||||
// Attempt to become a PolicyKit authentication agent
|
||||
PolkitAuthenticationAgent.init()
|
||||
|
||||
global.gdk_screen.connect('monitors-changed', _relayout);
|
||||
|
||||
ExtensionSystem.init();
|
||||
ExtensionSystem.loadExtensions();
|
||||
|
||||
panel.startStatusArea();
|
||||
panel.startupAnimation();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
@ -180,14 +206,144 @@ function start() {
|
||||
_log('info', 'loaded at ' + _startDate);
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
Mainloop.idle_add(_removeUnusedWorkspaces);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
if (perfModuleName) {
|
||||
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
|
||||
let module = eval('imports.perf.' + perfModuleName + ';');
|
||||
Scripting.runPerfScript(module, perfOutput);
|
||||
}
|
||||
|
||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
||||
Mainloop.idle_add(_nWorkspacesChanged);
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
let _checkWorkspacesId = 0;
|
||||
|
||||
function _checkWorkspaces() {
|
||||
let i;
|
||||
let emptyWorkspaces = [];
|
||||
|
||||
for (i = 0; i < _workspaces.length; i++)
|
||||
emptyWorkspaces[i] = true;
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
for (i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
|
||||
if (win.get_meta_window().is_on_all_workspaces())
|
||||
continue;
|
||||
|
||||
let workspaceIndex = win.get_workspace();
|
||||
emptyWorkspaces[workspaceIndex] = false;
|
||||
}
|
||||
|
||||
// If we don't have an empty workspace at the end, add one
|
||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
||||
global.screen.append_new_workspace(false, global.get_current_time());
|
||||
emptyWorkspaces.push(false);
|
||||
}
|
||||
|
||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
||||
if (emptyWorkspaces[i])
|
||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
||||
}
|
||||
|
||||
_checkWorkspacesId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
function _queueCheckWorkspaces() {
|
||||
if (_checkWorkspacesId == 0)
|
||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
||||
}
|
||||
|
||||
function _nWorkspacesChanged() {
|
||||
let oldNumWorkspaces = _workspaces.length;
|
||||
let newNumWorkspaces = global.screen.n_workspaces;
|
||||
|
||||
if (oldNumWorkspaces == newNumWorkspaces)
|
||||
return false;
|
||||
|
||||
let lostWorkspaces = [];
|
||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
||||
let w;
|
||||
|
||||
// Assume workspaces are only added at the end
|
||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
||||
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
||||
|
||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
||||
let workspace = _workspaces[w];
|
||||
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
||||
workspace._windowRemovedId = workspace.connect('window-removed', _queueCheckWorkspaces);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Assume workspaces are only removed sequentially
|
||||
// (e.g. 2,3,4 - not 2,4,7)
|
||||
let removedIndex;
|
||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
||||
let workspace = global.screen.get_workspace_by_index(w);
|
||||
if (_workspaces[w] != workspace) {
|
||||
removedIndex = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
||||
lostWorkspaces.forEach(function(workspace) {
|
||||
workspace.disconnect(workspace._windowAddedId);
|
||||
workspace.disconnect(workspace._windowRemovedId);
|
||||
});
|
||||
}
|
||||
|
||||
_queueCheckWorkspaces();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* getThemeStylesheet:
|
||||
*
|
||||
* Get the theme CSS file that the shell will load
|
||||
*
|
||||
* Returns: A file path that contains the theme CSS,
|
||||
* null if using the default
|
||||
*/
|
||||
function getThemeStylesheet()
|
||||
{
|
||||
return _cssStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* setThemeStylesheet:
|
||||
* @cssStylesheet: A file path that contains the theme CSS,
|
||||
* set it to null to use the default
|
||||
*
|
||||
* Set the theme CSS file that the shell will load
|
||||
*/
|
||||
function setThemeStylesheet(cssStylesheet)
|
||||
{
|
||||
_cssStylesheet = cssStylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* loadTheme:
|
||||
*
|
||||
* Reloads the theme CSS file
|
||||
*/
|
||||
function loadTheme() {
|
||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||
|
||||
let cssStylesheet = _defaultCssStylesheet;
|
||||
if (_cssStylesheet != null)
|
||||
cssStylesheet = _cssStylesheet;
|
||||
|
||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
||||
themeContext.set_theme (theme);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,38 +395,19 @@ function _relayout() {
|
||||
|
||||
// To avoid updating the position and size of the workspaces
|
||||
// in the overview, we just hide the overview. The positions
|
||||
// will be updated when it is next shown. We do the same for
|
||||
// the calendar popdown.
|
||||
// will be updated when it is next shown.
|
||||
overview.hide();
|
||||
panel.hideCalendar();
|
||||
}
|
||||
|
||||
// metacity-clutter currently uses the same prefs as plain metacity,
|
||||
// which probably means we'll be starting out with multiple workspaces;
|
||||
// remove any unused ones. (We do this from an idle handler, because
|
||||
// global.get_window_actors() still returns NULL at the point when start()
|
||||
// is called.)
|
||||
function _removeUnusedWorkspaces() {
|
||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||
return win.get_workspace() == workspaceIndex ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
}
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
let maxWorkspace = 0;
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let win = windows[i];
|
||||
|
||||
if (!win.get_meta_window().is_on_all_workspaces() &&
|
||||
win.get_workspace() > maxWorkspace) {
|
||||
maxWorkspace = win.get_workspace();
|
||||
}
|
||||
}
|
||||
let screen = global.screen;
|
||||
if (screen.n_workspaces > maxWorkspace) {
|
||||
for (let w = screen.n_workspaces - 1; w > maxWorkspace; w--) {
|
||||
let workspace = screen.get_workspace_by_index(w);
|
||||
screen.remove_workspace(workspace, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
function getWindowActorsForWorkspace(workspaceIndex) {
|
||||
return global.get_window_actors().filter(function (win) {
|
||||
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
||||
});
|
||||
}
|
||||
|
||||
// This function encapsulates hacks to make certain global keybindings
|
||||
@ -278,57 +415,67 @@ function _removeUnusedWorkspaces() {
|
||||
// are disabled with a global grab. (When there is a global grab, then
|
||||
// all key events will be delivered to the stage, so ::captured-event
|
||||
// on the stage can be used for global keybindings.)
|
||||
//
|
||||
// We expect to need to conditionally enable just a few keybindings
|
||||
// depending on circumstance; the main hackiness here is that we are
|
||||
// assuming that keybindings have their default values; really we
|
||||
// should be asking Mutter to resolve the key into an action and then
|
||||
// base our handling based on the action.
|
||||
function _globalKeyPressHandler(actor, event) {
|
||||
if (modalCount == 0)
|
||||
return false;
|
||||
if (event.type() != Clutter.EventType.KEY_RELEASE)
|
||||
if (event.type() != Clutter.EventType.KEY_PRESS)
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
let keyCode = event.get_key_code();
|
||||
let modifierState = Shell.get_event_state(event);
|
||||
// Check the overview key first, this isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
// The super key is the default for triggering the overview, and should
|
||||
// get us out of the overview when we are already in it.
|
||||
if (overview.visible)
|
||||
overview.hide();
|
||||
|
||||
let display = global.screen.get_display();
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = display.get_keybinding_action(keyCode, modifierState);
|
||||
|
||||
// The screenshot action should always be available (even if a
|
||||
// modal dialog is present)
|
||||
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
|
||||
let gconf = GConf.Client.get_default();
|
||||
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
||||
if (command != null && command != '')
|
||||
Util.spawnCommandLine(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whitelist some of the Metacity actions
|
||||
let display = global.screen.get_display();
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
// Other bindings are only available when the overview is up and
|
||||
// no modal dialog is present.
|
||||
if (!overview.visible || modalCount > 1)
|
||||
return false;
|
||||
|
||||
// This isn't a Meta.KeyBindingAction yet
|
||||
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
||||
overview.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
||||
let action = display.get_keybinding_action(keyCode, modifierState);
|
||||
switch (action) {
|
||||
case Meta.KeyBindingAction.COMMAND_SCREENSHOT:
|
||||
let gconf = GConf.Client.get_default();
|
||||
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
||||
if (command != null && command != '') {
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
}
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing; we also disable them in the main view.
|
||||
//
|
||||
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
// wm.actionMoveWorkspaceLeft();
|
||||
// return true;
|
||||
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
// wm.actionMoveWorkspaceRight();
|
||||
// return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_UP:
|
||||
wm.actionMoveWorkspaceUp();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
||||
wm.actionMoveWorkspaceLeft();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
||||
wm.actionMoveWorkspaceRight();
|
||||
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
||||
wm.actionMoveWorkspaceDown();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
||||
case Meta.KeyBindingAction.COMMAND_2:
|
||||
getRunDialog().open();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
||||
overview.hide();
|
||||
return true;
|
||||
case Meta.KeyBindingAction.SWITCH_PANELS:
|
||||
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -347,21 +494,30 @@ function _findModal(actor) {
|
||||
/**
|
||||
* pushModal:
|
||||
* @actor: #ClutterActor which will be given keyboard focus
|
||||
* @timestamp: optional timestamp
|
||||
*
|
||||
* Ensure we are in a mode where all keyboard and mouse input goes to
|
||||
* the stage. Multiple calls to this function act in a stacking fashion;
|
||||
* the effect will be undone when an equal number of popModal() invocations
|
||||
* have been made.
|
||||
* the stage, and focus @actor. Multiple calls to this function act in
|
||||
* a stacking fashion; the effect will be undone when an equal number
|
||||
* of popModal() invocations have been made.
|
||||
*
|
||||
* Next, record the current Clutter keyboard focus on a stack. If the modal stack
|
||||
* returns to this actor, reset the focus to the actor which was focused
|
||||
* at the time pushModal() was invoked.
|
||||
* Next, record the current Clutter keyboard focus on a stack. If the
|
||||
* modal stack returns to this actor, reset the focus to the actor
|
||||
* which was focused at the time pushModal() was invoked.
|
||||
*
|
||||
* @timestamp is optionally used to associate the call with a specific user
|
||||
* initiated event. If not provided then the value of
|
||||
* global.get_current_time() is assumed.
|
||||
*
|
||||
* Returns: true iff we successfully acquired a grab or already had one
|
||||
*/
|
||||
function pushModal(actor) {
|
||||
function pushModal(actor, timestamp) {
|
||||
|
||||
if (timestamp == undefined)
|
||||
timestamp = global.get_current_time();
|
||||
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(global.get_current_time())) {
|
||||
if (!global.begin_modal(timestamp)) {
|
||||
log('pushModal: invocation of begin_modal failed');
|
||||
return false;
|
||||
}
|
||||
@ -385,19 +541,28 @@ function pushModal(actor) {
|
||||
}
|
||||
modalActorFocusStack.push([actor, curFocus]);
|
||||
|
||||
global.stage.set_key_focus(null);
|
||||
global.stage.set_key_focus(actor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* popModal:
|
||||
* @actor: #ClutterActor passed to original invocation of pushModal().
|
||||
* @timestamp: optional timestamp
|
||||
*
|
||||
* Reverse the effect of pushModal(). If this invocation is undoing
|
||||
* the topmost invocation, then the focus will be restored to the
|
||||
* previous focus at the time when pushModal() was invoked.
|
||||
*
|
||||
* @timestamp is optionally used to associate the call with a specific user
|
||||
* initiated event. If not provided then the value of
|
||||
* global.get_current_time() is assumed.
|
||||
*/
|
||||
function popModal(actor) {
|
||||
function popModal(actor, timestamp) {
|
||||
|
||||
if (timestamp == undefined)
|
||||
timestamp = global.get_current_time();
|
||||
|
||||
modalCount -= 1;
|
||||
let focusIndex = _findModal(actor);
|
||||
if (focusIndex >= 0) {
|
||||
@ -415,7 +580,8 @@ function popModal(actor) {
|
||||
if (modalCount > 0)
|
||||
return;
|
||||
|
||||
global.end_modal(global.get_current_time());
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
}
|
||||
|
||||
|
235
js/ui/modalDialog.js
Normal file
@ -0,0 +1,235 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Pango = imports.gi.Pango;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||
|
||||
const State = {
|
||||
OPENED: 0,
|
||||
CLOSED: 1,
|
||||
OPENING: 2,
|
||||
CLOSING: 3,
|
||||
FADED_OUT: 4
|
||||
};
|
||||
|
||||
function ModalDialog() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ModalDialog.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { styleClass: null });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
|
||||
this._group = new St.Group({ visible: false,
|
||||
x: 0,
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
global.focus_manager.add_group(this._group);
|
||||
this._initialKeyFocus = this._group;
|
||||
|
||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||
|
||||
this._actionKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
|
||||
this._group.add_actor(this._backgroundBin);
|
||||
this._lightbox.highlight(this._backgroundBin);
|
||||
|
||||
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||
vertical: true });
|
||||
if (params.styleClass != null) {
|
||||
this._dialogLayout.add_style_class_name(params.styleClass);
|
||||
}
|
||||
this._backgroundBin.child = this._dialogLayout;
|
||||
|
||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||
this._dialogLayout.add(this.contentLayout,
|
||||
{ x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._buttonLayout = new St.BoxLayout({ opacity: 220,
|
||||
vertical: false });
|
||||
this._dialogLayout.add(this._buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.END });
|
||||
},
|
||||
|
||||
setButtons: function(buttons) {
|
||||
this._buttonLayout.destroy_children();
|
||||
this._actionKeys = {};
|
||||
|
||||
let i = 0;
|
||||
for (let index in buttons) {
|
||||
let buttonInfo = buttons[index];
|
||||
let label = buttonInfo['label'];
|
||||
let action = buttonInfo['action'];
|
||||
let key = buttonInfo['key'];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
|
||||
let x_alignment;
|
||||
if (buttons.length == 1)
|
||||
x_alignment = St.Align.END;
|
||||
else if (i == 0)
|
||||
x_alignment = St.Align.START;
|
||||
else if (i == buttons.length - 1)
|
||||
x_alignment = St.Align.END;
|
||||
else
|
||||
x_alignment = St.Align.MIDDLE;
|
||||
|
||||
this._initialKeyFocus = button;
|
||||
this._buttonLayout.add(button,
|
||||
{ expand: true,
|
||||
x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: x_alignment,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
button.connect('clicked', action);
|
||||
|
||||
if (key)
|
||||
this._actionKeys[key] = action;
|
||||
i++;
|
||||
}
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, keyPressEvent) {
|
||||
let symbol = keyPressEvent.get_key_symbol();
|
||||
let action = this._actionKeys[symbol];
|
||||
|
||||
if (action)
|
||||
action();
|
||||
},
|
||||
|
||||
_onGroupDestroy: function() {
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
_fadeOpen: function() {
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._backgroundBin.set_position(monitor.x, monitor.y);
|
||||
this._backgroundBin.set_size(monitor.width, monitor.height);
|
||||
|
||||
this.state = State.OPENING;
|
||||
|
||||
this._dialogLayout.opacity = 255;
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this._initialKeyFocus.grab_key_focus();
|
||||
this.state = State.OPENED;
|
||||
this.emit('opened');
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
open: function(timestamp) {
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
return true;
|
||||
|
||||
if (!Main.pushModal(this._group, timestamp))
|
||||
return false;
|
||||
|
||||
global.stage.set_key_focus(this._group);
|
||||
|
||||
this._fadeOpen();
|
||||
return true;
|
||||
},
|
||||
|
||||
close: function(timestamp) {
|
||||
if (this.state == State.CLOSED || this.state == State.CLOSING)
|
||||
return;
|
||||
|
||||
let needsPopModal;
|
||||
|
||||
if (this.state == State.OPENED || this.state == State.OPENING)
|
||||
needsPopModal = true;
|
||||
else
|
||||
needsPopModal = false;
|
||||
|
||||
this.state = State.CLOSING;
|
||||
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.CLOSED;
|
||||
this._group.hide();
|
||||
|
||||
if (needsPopModal)
|
||||
Main.popModal(this._group, timestamp);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
// This method is like close, but fades the dialog out much slower,
|
||||
// and leaves the lightbox in place. Once in the faded out state,
|
||||
// the dialog can be brought back by an open call, or the lightbox
|
||||
// can be dismissed by a close call.
|
||||
//
|
||||
// The main point of this method is to give some indication to the user
|
||||
// that the dialog reponse has been acknowledged but will take a few
|
||||
// moments before being processed.
|
||||
// e.g., if a user clicked "Log Out" then the dialog should go away
|
||||
// imediately, but the lightbox should remain until the logout is
|
||||
// complete.
|
||||
_fadeOutDialog: function(timestamp) {
|
||||
if (this.state == State.CLOSED || this.state == State.CLOSING)
|
||||
return;
|
||||
|
||||
if (this.state == State.FADED_OUT)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this._dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: FADE_OUT_DIALOG_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.FADED_OUT;
|
||||
Main.popModal(this._group, timestamp);
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ModalDialog.prototype);
|
@ -9,9 +9,11 @@ const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Params = imports.misc.params;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
let nextNotificationId = 1;
|
||||
|
||||
@ -75,11 +77,11 @@ const Urgency = {
|
||||
const rewriteRules = {
|
||||
'XChat': [
|
||||
{ pattern: /^XChat: Private message from: (\S*) \(.*\)$/,
|
||||
replacement: '<$1>' },
|
||||
replacement: '<$1>' },
|
||||
{ pattern: /^XChat: New public message from: (\S*) \((.*)\)$/,
|
||||
replacement: '$2 <$1>' },
|
||||
replacement: '$2 <$1>' },
|
||||
{ pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
|
||||
replacement: '$2 <$1>' }
|
||||
replacement: '$2 <$1>' }
|
||||
]
|
||||
};
|
||||
|
||||
@ -126,17 +128,8 @@ NotificationDaemon.prototype = {
|
||||
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();
|
||||
Util.killall('notification-daemon');
|
||||
Util.killall('notify-osd');
|
||||
}
|
||||
},
|
||||
|
||||
@ -153,9 +146,9 @@ NotificationDaemon.prototype = {
|
||||
return new St.Icon({ icon_name: icon,
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
} else if (hints.icon_data) {
|
||||
} else if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints.icon_data;
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return textureCache.load_from_raw(data, data.length, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
} else {
|
||||
@ -175,14 +168,43 @@ NotificationDaemon.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_newSource: function(title, pid) {
|
||||
let source = new Source(title, pid);
|
||||
this._sources[pid] = source;
|
||||
// Returns the source associated with ndata.notification if it is set.
|
||||
// Otherwise, returns the source associated with the pid if one is
|
||||
// stored in this._sources and the notification is not transient.
|
||||
// Otherwise, creates a new source as long as pid is provided.
|
||||
//
|
||||
// Either a pid or ndata.notification is needed to retrieve or
|
||||
// create a source.
|
||||
_getSource: function(title, pid, ndata) {
|
||||
if (!pid && !(ndata && ndata.notification))
|
||||
return null;
|
||||
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[pid];
|
||||
}));
|
||||
// We use notification's source for the notifications we still have
|
||||
// around that are getting replaced because we don't keep sources
|
||||
// for transient notifications in this._sources, but we still want
|
||||
// the notification associated with them to get replaced correctly.
|
||||
if (ndata && ndata.notification)
|
||||
return ndata.notification.source;
|
||||
|
||||
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
|
||||
|
||||
// We don't want to override a persistent notification
|
||||
// with a transient one from the same sender, so we
|
||||
// always create a new source object for new transient notifications
|
||||
// and never add it to this._sources .
|
||||
if (!isForTransientNotification && this._sources[pid])
|
||||
return this._sources[pid];
|
||||
|
||||
let source = new Source(title, pid);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
this._sources[pid] = source;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[pid];
|
||||
}));
|
||||
}
|
||||
|
||||
Main.messageTray.add(source);
|
||||
return source;
|
||||
@ -205,8 +227,6 @@ NotificationDaemon.prototype = {
|
||||
return id;
|
||||
}
|
||||
|
||||
summary = GLib.markup_escape_text(summary, -1);
|
||||
|
||||
let rewrites = rewriteRules[appName];
|
||||
if (rewrites) {
|
||||
for (let i = 0; i < rewrites.length; i++) {
|
||||
@ -218,6 +238,15 @@ NotificationDaemon.prototype = {
|
||||
|
||||
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
|
||||
|
||||
// Be compatible with the various hints for image data
|
||||
// 'image-data' is the latest name of this hint, introduced in 1.2
|
||||
if (!hints['image-data']) {
|
||||
if (hints['image_data'])
|
||||
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
||||
else if (hints['icon_data'])
|
||||
hints['image-data'] = hints['icon_data']; // previous versions of the spec
|
||||
}
|
||||
|
||||
let ndata = { appName: appName,
|
||||
icon: icon,
|
||||
summary: summary,
|
||||
@ -236,7 +265,8 @@ NotificationDaemon.prototype = {
|
||||
|
||||
let sender = DBus.getCurrentMessageContext().sender;
|
||||
let pid = this._senderToPid[sender];
|
||||
let source = pid ? this._sources[pid] : null;
|
||||
|
||||
let source = this._getSource(appName, pid, ndata);
|
||||
|
||||
if (source) {
|
||||
this._notifyForSource(source, ndata);
|
||||
@ -257,16 +287,23 @@ NotificationDaemon.prototype = {
|
||||
if (!ndata)
|
||||
return;
|
||||
|
||||
this._senderToPid[sender] = pid;
|
||||
source = this._sources[pid];
|
||||
|
||||
if (!source)
|
||||
source = this._newSource(appName, pid);
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
source = this._getSource(appName, pid, ndata);
|
||||
|
||||
// We only store sender-pid entries for persistent sources.
|
||||
// Removing the entries once the source is destroyed
|
||||
// would result in the entries associated with transient
|
||||
// sources removed once the notification is shown anyway.
|
||||
// However, keeping these pairs would mean that we would
|
||||
// possibly remove an entry associated with a persistent
|
||||
// source when a transient source for the same sender is
|
||||
// distroyed.
|
||||
if (!source.isTransient) {
|
||||
this._senderToPid[sender] = pid;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
}
|
||||
this._notifyForSource(source, ndata);
|
||||
}));
|
||||
|
||||
@ -281,19 +318,34 @@ NotificationDaemon.prototype = {
|
||||
let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ icon: iconActor,
|
||||
bannerMarkup: true });
|
||||
ndata.notification = notification;
|
||||
notification.connect('clicked', Lang.bind(this,
|
||||
function(n) {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
}));
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n) {
|
||||
function(n, reason) {
|
||||
delete this._notifications[id];
|
||||
let notificationClosedReason;
|
||||
switch (reason) {
|
||||
case MessageTray.NotificationDestroyedReason.EXPIRED:
|
||||
notificationClosedReason = NotificationClosedReason.EXPIRED;
|
||||
break;
|
||||
case MessageTray.NotificationDestroyedReason.DISMISSED:
|
||||
notificationClosedReason = NotificationClosedReason.DISMISSED;
|
||||
break;
|
||||
case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
|
||||
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
|
||||
break;
|
||||
}
|
||||
this._emitNotificationClosed(id, notificationClosedReason);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this,
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(id, actionId);
|
||||
}));
|
||||
notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
|
||||
} else {
|
||||
notification.update(summary, body, { icon: iconActor,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
|
||||
@ -302,8 +354,21 @@ NotificationDaemon.prototype = {
|
||||
for (let i = 0; i < actions.length - 1; i += 2)
|
||||
notification.addButton(actions[i], actions[i + 1]);
|
||||
}
|
||||
|
||||
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
notification.setUrgency(MessageTray.Urgency.LOW);
|
||||
break;
|
||||
case Urgency.NORMAL:
|
||||
notification.setUrgency(MessageTray.Urgency.NORMAL);
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
notification.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||
break;
|
||||
}
|
||||
notification.setResident(hints.resident == true);
|
||||
// 'transient' is a reserved keyword in JS, so we have to retrieve the value
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
|
||||
source.notify(notification, sourceIconActor);
|
||||
@ -313,10 +378,9 @@ NotificationDaemon.prototype = {
|
||||
let ndata = this._notifications[id];
|
||||
if (ndata) {
|
||||
if (ndata.notification)
|
||||
ndata.notification.destroy();
|
||||
ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
||||
delete this._notifications[id];
|
||||
}
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
|
||||
},
|
||||
|
||||
GetCapabilities: function() {
|
||||
@ -336,10 +400,10 @@ NotificationDaemon.prototype = {
|
||||
|
||||
GetServerInformation: function() {
|
||||
return [
|
||||
'GNOME Shell',
|
||||
Config.PACKAGE_NAME,
|
||||
'GNOME',
|
||||
'0.1', // FIXME, get this from somewhere
|
||||
'1.0'
|
||||
Config.PACKAGE_VERSION,
|
||||
'1.2'
|
||||
];
|
||||
},
|
||||
|
||||
@ -351,17 +415,13 @@ NotificationDaemon.prototype = {
|
||||
for (let id in this._sources) {
|
||||
let source = this._sources[id];
|
||||
if (source.app == tracker.focus_app) {
|
||||
source.activated();
|
||||
if (source.notification && !source.notification.resident)
|
||||
source.notification.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_actionInvoked: function(notification, action, source, id) {
|
||||
source.activated();
|
||||
this._emitActionInvoked(id, action);
|
||||
},
|
||||
|
||||
_emitNotificationClosed: function(id, reason) {
|
||||
DBus.session.emit_signal('/org/freedesktop/Notifications',
|
||||
'org.freedesktop.Notifications',
|
||||
@ -377,9 +437,7 @@ NotificationDaemon.prototype = {
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
let source = this._sources[icon.pid];
|
||||
if (!source)
|
||||
source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
|
||||
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
|
||||
source.setTrayIcon(icon);
|
||||
},
|
||||
|
||||
@ -420,13 +478,16 @@ Source.prototype = {
|
||||
},
|
||||
|
||||
_setApp: function() {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
// Only override the icon if we were previously using
|
||||
// notification-based icons (ie, not a trayicon)
|
||||
if (this.useNotificationIcon) {
|
||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||
if (!this._isTrayIcon) {
|
||||
this.useNotificationIcon = false;
|
||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||
}
|
||||
@ -439,12 +500,10 @@ Source.prototype = {
|
||||
},
|
||||
|
||||
_notificationClicked: function(notification) {
|
||||
notification.destroy();
|
||||
this.openApp();
|
||||
this.activated();
|
||||
},
|
||||
|
||||
activated: function() {
|
||||
_notificationRemoved: function() {
|
||||
if (!this._isTrayIcon)
|
||||
this.destroy();
|
||||
},
|
||||
|
520
js/ui/panel.js
@ -1,5 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
@ -11,11 +12,12 @@ const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Config = imports.misc.config;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const StatusMenu = imports.ui.statusMenu;
|
||||
const DateMenu = imports.ui.dateMenu;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
@ -25,6 +27,8 @@ const PANEL_ICON_SIZE = 24;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
|
||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||
|
||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const SPINNER_UPDATE_TIMEOUT = 130;
|
||||
const SPINNER_SPEED = 0.02;
|
||||
@ -33,14 +37,65 @@ const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'blue
|
||||
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'keyboard': imports.ui.status.keyboard.XKBIndicator
|
||||
};
|
||||
|
||||
const CLOCK_FORMAT_KEY = 'format';
|
||||
const CLOCK_CUSTOM_FORMAT_KEY = 'custom-format';
|
||||
if (Config.HAVE_BLUETOOTH)
|
||||
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
// in org.gnome.shell.clock
|
||||
const CLOCK_SHOW_DATE_KEY = 'show-date';
|
||||
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
|
||||
|
||||
|
||||
// To make sure the panel corners blend nicely with the panel,
|
||||
// we draw background and borders the same way, e.g. drawing
|
||||
// them as filled shapes from the outside inwards instead of
|
||||
// using cairo stroke(). So in order to give the border the
|
||||
// appearance of being drawn on top of the background, we need
|
||||
// to blend border and background color together.
|
||||
// For that purpose we use the following helper methods, taken
|
||||
// from st-theme-node-drawing.c
|
||||
function _norm(x) {
|
||||
return Math.round(x / 255);
|
||||
}
|
||||
|
||||
function _over(srcColor, dstColor) {
|
||||
let src = _premultiply(srcColor);
|
||||
let dst = _premultiply(dstColor);
|
||||
let result = new Clutter.Color();
|
||||
|
||||
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);
|
||||
|
||||
return _unpremultiply(result);
|
||||
}
|
||||
|
||||
function _premultiply(color) {
|
||||
return new Clutter.Color({ red: _norm(color.red * color.alpha),
|
||||
green: _norm(color.green * color.alpha),
|
||||
blue: _norm(color.blue * color.alpha),
|
||||
alpha: color.alpha });
|
||||
};
|
||||
|
||||
function _unpremultiply(color) {
|
||||
if (color.alpha == 0)
|
||||
return new Clutter.Color();
|
||||
|
||||
let red = Math.min((color.red * 255 + 127) / color.alpha, 255);
|
||||
let green = Math.min((color.green * 255 + 127) / color.alpha, 255);
|
||||
let blue = Math.min((color.blue * 255 + 127) / color.alpha, 255);
|
||||
return new Clutter.Color({ red: red, green: green,
|
||||
blue: blue, alpha: color.alpha });
|
||||
};
|
||||
|
||||
|
||||
function AnimatedIcon(name, size) {
|
||||
this._init(name, size);
|
||||
}
|
||||
@ -186,7 +241,7 @@ AppMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
this._metaDisplay = global.screen.get_display();
|
||||
this._startingApps = [];
|
||||
|
||||
@ -201,6 +256,8 @@ AppMenuButton.prototype = {
|
||||
this._container.connect('allocate', Lang.bind(this, this._contentAllocate));
|
||||
|
||||
this._iconBox = new Shell.Slicer({ name: 'appMenuIcon' });
|
||||
this._iconBox.connect('style-changed',
|
||||
Lang.bind(this, this._onIconBoxStyleChanged));
|
||||
this._container.add_actor(this._iconBox);
|
||||
this._label = new TextShadower();
|
||||
this._container.add_actor(this._label.actor);
|
||||
@ -211,7 +268,7 @@ AppMenuButton.prototype = {
|
||||
|
||||
this._visible = !Main.overview.visible;
|
||||
if (!this._visible)
|
||||
this.hide();
|
||||
this.actor.hide();
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
this.show();
|
||||
}));
|
||||
@ -246,32 +303,42 @@ AppMenuButton.prototype = {
|
||||
if (this._visible)
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
this.actor.show();
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._visible = true;
|
||||
},
|
||||
onCompleteScope: this });
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (!this._visible)
|
||||
return;
|
||||
|
||||
this._visible = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this.actor.hide();
|
||||
this._visible = false;
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
|
||||
_onIconBoxStyleChanged: function() {
|
||||
let node = this._iconBox.get_theme_node();
|
||||
let bottomClip = node.get_length('app-icon-bottom-clip');
|
||||
if (bottomClip > 0)
|
||||
this._iconBox.set_clip(0, 0,
|
||||
this._iconBox.width,
|
||||
this._iconBox.height - bottomClip);
|
||||
else
|
||||
this._iconBox.remove_clip();
|
||||
},
|
||||
|
||||
_stopAnimation: function(animate) {
|
||||
this._label.actor.remove_clip();
|
||||
if (this._updateId) {
|
||||
@ -483,147 +550,97 @@ AppMenuButton.prototype = {
|
||||
|
||||
Signals.addSignalMethods(AppMenuButton.prototype);
|
||||
|
||||
function ClockButton() {
|
||||
this._init();
|
||||
|
||||
function PanelCorner(side) {
|
||||
this._init(side);
|
||||
}
|
||||
|
||||
ClockButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
this.menu.addAction(_("Preferences"), Lang.bind(this, this._onPrefs));
|
||||
|
||||
this._clock = new St.Label();
|
||||
this.actor.set_child(this._clock);
|
||||
|
||||
this._calendarPopup = null;
|
||||
|
||||
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
|
||||
this._clockSettings.connect('changed', Lang.bind(this, this._updateClock));
|
||||
|
||||
// Start the clock
|
||||
this._updateClock();
|
||||
PanelCorner.prototype = {
|
||||
_init: function(side) {
|
||||
this._side = side;
|
||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._reposition));
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
let button = event.get_button();
|
||||
if (button == 3 &&
|
||||
(!this._calendarPopup || !this._calendarPopup.isOpen))
|
||||
this.menu.toggle();
|
||||
_repaint: function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
|
||||
let cornerRadius = node.get_length("-panel-corner-radius");
|
||||
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
|
||||
let outerBorderWidth = node.get_length('-panel-corner-outer-border-width');
|
||||
|
||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||
let innerBorderColor = node.get_color('-panel-corner-inner-border-color');
|
||||
let outerBorderColor = node.get_color('-panel-corner-outer-border-color');
|
||||
|
||||
let cr = this.actor.get_context();
|
||||
cr.setOperator(Cairo.Operator.SOURCE);
|
||||
|
||||
cr.moveTo(0, 0);
|
||||
if (this._side == St.Side.LEFT)
|
||||
cr.arc(cornerRadius,
|
||||
innerBorderWidth + outerBorderWidth + cornerRadius,
|
||||
cornerRadius, Math.PI, 3 * Math.PI / 2);
|
||||
else
|
||||
this._toggleCalendar();
|
||||
},
|
||||
cr.arc(0,
|
||||
innerBorderWidth + outerBorderWidth + cornerRadius,
|
||||
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
|
||||
cr.lineTo(cornerRadius, 0);
|
||||
cr.closePath();
|
||||
|
||||
closeCalendar: function() {
|
||||
if (!this._calendarPopup || !this._calendarPopup.isOpen)
|
||||
return;
|
||||
let savedPath = cr.copyPath();
|
||||
|
||||
this._calendarPopup.hide();
|
||||
let over = _over(outerBorderColor, backgroundColor);
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
cr.fill();
|
||||
|
||||
this.menu.isOpen = false;
|
||||
this.actor.remove_style_pseudo_class('pressed');
|
||||
},
|
||||
let xOffsetDirection = this._side == St.Side.LEFT ? -1 : 1;
|
||||
let offset = outerBorderWidth;
|
||||
over = _over(innerBorderColor, backgroundColor);
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
|
||||
openCalendar: function() {
|
||||
this._calendarPopup.show();
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
|
||||
// simulate an open menu, so it won't appear beneath the calendar
|
||||
this.menu.isOpen = true;
|
||||
this.actor.add_style_pseudo_class('pressed');
|
||||
},
|
||||
|
||||
_onPrefs: function() {
|
||||
let args = ['gnome-shell-clock-preferences'];
|
||||
let p = new Shell.Process({ args: args });
|
||||
|
||||
p.run();
|
||||
},
|
||||
|
||||
_toggleCalendar: function() {
|
||||
if (this._calendarPopup == null) {
|
||||
this._calendarPopup = new CalendarPopup();
|
||||
this._calendarPopup.actor.hide();
|
||||
}
|
||||
|
||||
if (this.menu.isOpen && !this._calendarPopup.isOpen) {
|
||||
this.menu.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._calendarPopup.isOpen)
|
||||
this.openCalendar();
|
||||
if (this._side == St.Side.LEFT)
|
||||
cr.rectangle(cornerRadius - offset, 0, offset, innerBorderWidth);
|
||||
else
|
||||
this.closeCalendar();
|
||||
cr.rectangle(0, 0, offset, innerBorderWidth);
|
||||
cr.fill();
|
||||
|
||||
offset = innerBorderWidth + outerBorderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
},
|
||||
|
||||
_updateClock: function() {
|
||||
let format = this._clockSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
|
||||
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
|
||||
_reposition: function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
|
||||
let clockFormat;
|
||||
switch (format) {
|
||||
case 'unix':
|
||||
// force updates every second
|
||||
showSeconds = true;
|
||||
clockFormat = '%s';
|
||||
break;
|
||||
case 'custom':
|
||||
// force updates every second
|
||||
showSeconds = true;
|
||||
clockFormat = this._clockSettings.get_string(CLOCK_CUSTOM_FORMAT_KEY);
|
||||
break;
|
||||
case '24-hour':
|
||||
if (showDate)
|
||||
/* Translators: This is the time format with date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
|
||||
: _("%a %b %e, %R");
|
||||
else
|
||||
/* Translators: This is the time format without date used
|
||||
in 24-hour mode. */
|
||||
clockFormat = showSeconds ? _("%a %R:%S")
|
||||
: _("%a %R");
|
||||
break;
|
||||
case '12-hour':
|
||||
default:
|
||||
if (showDate)
|
||||
/* Translators: This is a time format with date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
|
||||
: _("%a %b %e, %l:%M %p");
|
||||
else
|
||||
/* Translators: This is a time format without date used
|
||||
for AM/PM. */
|
||||
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
|
||||
: _("%a %l:%M %p");
|
||||
break;
|
||||
}
|
||||
let cornerRadius = node.get_length("-panel-corner-radius");
|
||||
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
|
||||
let outerBorderWidth = node.get_length('-panel-corner-outer-border-width');
|
||||
|
||||
let displayDate = new Date();
|
||||
let msecRemaining;
|
||||
if (showSeconds) {
|
||||
msecRemaining = 1000 - displayDate.getMilliseconds();
|
||||
if (msecRemaining < 50) {
|
||||
displayDate.setSeconds(displayDate.getSeconds() + 1);
|
||||
msecRemaining += 1000;
|
||||
}
|
||||
} else {
|
||||
msecRemaining = 60000 - (1000 * displayDate.getSeconds() +
|
||||
displayDate.getMilliseconds());
|
||||
if (msecRemaining < 500) {
|
||||
displayDate.setMinutes(displayDate.getMinutes() + 1);
|
||||
msecRemaining += 60000;
|
||||
}
|
||||
}
|
||||
|
||||
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
|
||||
Mainloop.timeout_add(msecRemaining, Lang.bind(this, this._updateClock));
|
||||
return false;
|
||||
this.actor.set_size(cornerRadius,
|
||||
innerBorderWidth + outerBorderWidth + cornerRadius);
|
||||
if (this._side == St.Side.LEFT)
|
||||
this.actor.set_position(Main.panel.actor.x,
|
||||
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth - outerBorderWidth);
|
||||
else
|
||||
this.actor.set_position(Main.panel.actor.x + Main.panel.actor.width - cornerRadius,
|
||||
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth - outerBorderWidth);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function Panel() {
|
||||
this._init();
|
||||
}
|
||||
@ -648,6 +665,16 @@ Panel.prototype = {
|
||||
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
|
||||
// This will eventually be automatic, see
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=584662
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
this._leftBox.add_style_pseudo_class('rtl');
|
||||
this._rightBox.add_style_pseudo_class('rtl');
|
||||
}
|
||||
|
||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||
|
||||
/* This box container ensures that the centerBox is positioned in the *absolute*
|
||||
* center, but can be pushed aside if necessary. */
|
||||
this._boxContainer = new Shell.GenericContainer();
|
||||
@ -743,14 +770,36 @@ Panel.prototype = {
|
||||
/* 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,
|
||||
can_focus: true });
|
||||
this.button = new St.Button({ name: 'panelActivities',
|
||||
style_class: 'panel-button',
|
||||
reactive: true,
|
||||
can_focus: true });
|
||||
this.button.set_child(label);
|
||||
|
||||
this.button._delegate = this.button;
|
||||
this.button._xdndTimeOut = 0;
|
||||
this.button.handleDragOver = Lang.bind(this,
|
||||
function(source, actor, x, y, time) {
|
||||
if (source == Main.xdndHandler) {
|
||||
if (this.button._xdndTimeOut != 0)
|
||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
||||
this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
this._xdndShowOverview(actor);
|
||||
}));
|
||||
}
|
||||
});
|
||||
this._leftBox.add(this.button);
|
||||
|
||||
// Synchronize the buttons pseudo classes with its corner
|
||||
this.button.connect('style-changed', Lang.bind(this,
|
||||
function(actor) {
|
||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
||||
let corner = rtl ? this._rightCorner : this._leftCorner;
|
||||
let pseudoClass = actor.get_style_pseudo_class();
|
||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
||||
}));
|
||||
|
||||
// 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
|
||||
// guard area (the "environs"). This avoids triggering the hot corner
|
||||
@ -786,6 +835,18 @@ Panel.prototype = {
|
||||
this._hotCorner.connect('leave-event',
|
||||
Lang.bind(this, this._onHotCornerLeft));
|
||||
|
||||
this._hotCorner._delegate = this._hotCorner;
|
||||
this._hotCorner.handleDragOver = Lang.bind(this,
|
||||
function(source, actor, x, y, time) {
|
||||
if (source == Main.xdndHandler) {
|
||||
if(!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
this.rippleAnimation();
|
||||
Main.overview.showTemporarily();
|
||||
Main.overview.beginItemDrag(actor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._boxContainer.add_actor(this._hotCornerEnvirons);
|
||||
this._boxContainer.add_actor(this._hotCorner);
|
||||
|
||||
@ -795,50 +856,43 @@ Panel.prototype = {
|
||||
this._menus.addMenu(appMenuButton.menu);
|
||||
|
||||
/* center */
|
||||
|
||||
this._clockButton = new ClockButton();
|
||||
this._centerBox.add(this._clockButton.actor, { y_fill: true });
|
||||
|
||||
this._menus.addMenu(this._clockButton.menu);
|
||||
this._dateMenu = new DateMenu.DateMenuButton();
|
||||
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
|
||||
this._menus.addMenu(this._dateMenu.menu);
|
||||
|
||||
/* right */
|
||||
|
||||
// System status applets live in statusBox, while legacy tray icons
|
||||
// live in trayBox
|
||||
// The trayBox is hidden when there are no tray icons.
|
||||
let statusBox = new St.BoxLayout({ name: 'statusTray' });
|
||||
let trayBox = new St.BoxLayout({ name: 'legacyTray' });
|
||||
this._trayBox = trayBox;
|
||||
this._statusBox = statusBox;
|
||||
this._trayBox = new St.BoxLayout({ name: 'legacyTray' });
|
||||
this._statusBox = new St.BoxLayout({ name: 'statusTray' });
|
||||
|
||||
trayBox.hide();
|
||||
this._rightBox.add(trayBox);
|
||||
this._rightBox.add(statusBox);
|
||||
this._trayBox.hide();
|
||||
this._rightBox.add(this._trayBox);
|
||||
this._rightBox.add(this._statusBox);
|
||||
|
||||
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
|
||||
let role = STANDARD_TRAY_ICON_ORDER[i];
|
||||
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
continue;
|
||||
}
|
||||
let indicator = new constructor();
|
||||
statusBox.add(indicator.actor);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
}
|
||||
this._statusmenu = new StatusMenu.StatusMenuButton();
|
||||
this._statusmenu.actor.name = 'panelStatus';
|
||||
this._rightBox.add(this._statusmenu.actor);
|
||||
|
||||
// Synchronize the buttons pseudo classes with its corner
|
||||
this._statusmenu.actor.connect('style-changed', Lang.bind(this,
|
||||
function(actor) {
|
||||
let rtl = actor.get_direction() == St.TextDirection.RTL;
|
||||
let corner = rtl ? this._leftCorner : this._rightCorner;
|
||||
let pseudoClass = actor.get_style_pseudo_class();
|
||||
corner.actor.set_style_pseudo_class(pseudoClass);
|
||||
}));
|
||||
|
||||
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
|
||||
this._statusmenu = new StatusMenu.StatusMenuButton();
|
||||
this._menus.addMenu(this._statusmenu.menu);
|
||||
this._rightBox.add(this._statusmenu.actor);
|
||||
|
||||
// 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
|
||||
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
|
||||
// to switch to.
|
||||
this.button.connect('clicked', Lang.bind(this, function(b, event) {
|
||||
this.button.connect('clicked', Lang.bind(this, function(b) {
|
||||
if (!Main.overview.animationInProgress) {
|
||||
this._maybeToggleOverviewOnClick();
|
||||
return true;
|
||||
@ -850,17 +904,56 @@ Panel.prototype = {
|
||||
// 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.
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
this.button.active = true;
|
||||
this.button.checked = true;
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this.button.active = false;
|
||||
this.button.checked = false;
|
||||
}));
|
||||
|
||||
Main.chrome.addActor(this.actor, { visibleInOverview: true });
|
||||
Main.chrome.addActor(this._leftCorner.actor, { visibleInOverview: true,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: false });
|
||||
Main.chrome.addActor(this._rightCorner.actor, { visibleInOverview: true,
|
||||
affectsStruts: false,
|
||||
affectsInputRegion: false });
|
||||
},
|
||||
|
||||
hideCalendar: function() {
|
||||
this._clockButton.closeCalendar();
|
||||
_xdndShowOverview: function (actor) {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
|
||||
if (pickedActor != this.button) {
|
||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
||||
this.button._xdndTimeOut = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
Main.overview.showTemporarily();
|
||||
Main.overview.beginItemDrag(actor);
|
||||
}
|
||||
|
||||
Mainloop.source_remove(this.button._xdndTimeOut);
|
||||
this.button._xdndTimeOut = 0;
|
||||
},
|
||||
|
||||
startStatusArea: function() {
|
||||
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
|
||||
let role = STANDARD_TRAY_ICON_ORDER[i];
|
||||
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
|
||||
if (!constructor) {
|
||||
// This icon is not implemented (this is a bug)
|
||||
continue;
|
||||
}
|
||||
let indicator = new constructor();
|
||||
this._statusBox.add(indicator.actor);
|
||||
this._menus.addMenu(indicator.menu);
|
||||
}
|
||||
|
||||
// PopupMenuManager depends on menus being added in order for
|
||||
// keyboard navigation
|
||||
this._menus.addMenu(this._statusmenu.menu);
|
||||
},
|
||||
|
||||
startupAnimation: function() {
|
||||
@ -935,6 +1028,17 @@ Panel.prototype = {
|
||||
Main.uiGroup.add_actor(ripple);
|
||||
},
|
||||
|
||||
rippleAnimation: function() {
|
||||
// Show three concentric ripples expanding outwards; the exact
|
||||
// parameters were found by trial and error, so don't look
|
||||
// for them to make perfect sense mathematically
|
||||
|
||||
// delay time scale opacity => scale opacity
|
||||
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
|
||||
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
|
||||
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
|
||||
},
|
||||
|
||||
_onHotCornerEntered : function() {
|
||||
if (this._menus.grabbed)
|
||||
return false;
|
||||
@ -943,14 +1047,7 @@ Panel.prototype = {
|
||||
if (!Main.overview.animationInProgress) {
|
||||
this._hotCornerActivationTime = Date.now() / 1000;
|
||||
|
||||
// Show three concentric ripples expanding outwards; the exact
|
||||
// parameters were found by trial and error, so don't look
|
||||
// for them to make perfect sense mathematically
|
||||
|
||||
// delay time scale opacity => scale opacity
|
||||
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
|
||||
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
|
||||
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
|
||||
this.rippleAnimation();
|
||||
Main.overview.toggle();
|
||||
}
|
||||
}
|
||||
@ -989,66 +1086,3 @@ Panel.prototype = {
|
||||
this._hotCornerActivationTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function CalendarPopup() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
CalendarPopup.prototype = {
|
||||
_init: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
this.actor = new St.Bin({ name: 'calendarPopup' });
|
||||
|
||||
this.calendar = new Calendar.Calendar();
|
||||
this.actor.set_child(this.calendar.actor);
|
||||
|
||||
this.isOpen = false;
|
||||
|
||||
Main.chrome.addActor(this.actor, { visibleInOverview: true,
|
||||
affectsStruts: false });
|
||||
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
|
||||
this.calendar.actor.connect('notify::width', Lang.bind(this, this._centerPopup));
|
||||
},
|
||||
|
||||
show: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
|
||||
if (this.isOpen)
|
||||
return;
|
||||
this.isOpen = true;
|
||||
|
||||
// Reset the calendar to today's date
|
||||
this.calendar.setDate(new Date());
|
||||
|
||||
this._centerPopup();
|
||||
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;
|
||||
|
||||
if (!this.isOpen)
|
||||
return;
|
||||
this.isOpen = false;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ y: panelActor.y + panelActor.height - this.actor.height,
|
||||
time: 0.2,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() { this.actor.hide(); },
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_centerPopup: function() {
|
||||
let panelActor = Main.panel.actor;
|
||||
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
|
||||
}
|
||||
};
|
||||
|
@ -47,13 +47,10 @@ Button.prototype = {
|
||||
},
|
||||
|
||||
_onOpenStateChanged: function(menu, open) {
|
||||
if (open) {
|
||||
this.actor.add_style_pseudo_class('pressed');
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (!focus || (focus != this.actor && !menu.contains(focus)))
|
||||
this.actor.grab_key_focus();
|
||||
} else
|
||||
this.actor.remove_style_pseudo_class('pressed');
|
||||
if (open)
|
||||
this.actor.add_style_pseudo_class('active');
|
||||
else
|
||||
this.actor.remove_style_pseudo_class('active');
|
||||
}
|
||||
};
|
||||
|
||||
@ -71,7 +68,7 @@ SystemStatusButton.prototype = {
|
||||
__proto__: Button.prototype,
|
||||
|
||||
_init: function(iconName,tooltipText) {
|
||||
Button.prototype._init.call(this, St.Align.START);
|
||||
Button.prototype._init.call(this, 0.0);
|
||||
this._iconActor = new St.Icon({ icon_name: iconName,
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'system-status-icon' });
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const GConf = imports.gi.GConf;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -13,12 +12,9 @@ const _ = Gettext.gettext;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
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;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
/**
|
||||
* Represents a place object, which is most normally a bookmark entry,
|
||||
@ -63,6 +59,21 @@ PlaceInfo.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to translate launch parameters into a GAppLaunchContext
|
||||
function _makeLaunchContext(params)
|
||||
{
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: null });
|
||||
|
||||
let launchContext = global.create_app_launch_context();
|
||||
if (params.workspace != null)
|
||||
launchContext.set_desktop(params.workspace.index());
|
||||
if (params.timestamp != null)
|
||||
launchContext.set_timestamp(params.timestamp);
|
||||
|
||||
return launchContext;
|
||||
}
|
||||
|
||||
function PlaceDeviceInfo(mount) {
|
||||
this._init(mount);
|
||||
}
|
||||
@ -82,9 +93,9 @@ PlaceDeviceInfo.prototype = {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
launch: function(params) {
|
||||
Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
|
||||
global.create_app_launch_context());
|
||||
_makeLaunchContext(params));
|
||||
},
|
||||
|
||||
isRemovable: function() {
|
||||
@ -109,27 +120,22 @@ PlaceDeviceInfo.prototype = {
|
||||
this._mount.unmount_finish(res);
|
||||
} catch (e) {
|
||||
let message = _("Failed to unmount '%s'").format(o.get_name());
|
||||
Main.overview.infoBar.setMessage(message,
|
||||
Main.overview.shellInfo.setMessage(message,
|
||||
Lang.bind(this, this.remove),
|
||||
_("Retry"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function PlacesManager() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
PlacesManager.prototype = {
|
||||
_init: function() {
|
||||
let gconf = GConf.Client.get_default();
|
||||
gconf.add_dir(NAUTILUS_PREFS_DIR, GConf.ClientPreloadType.PRELOAD_NONE);
|
||||
|
||||
this._defaultPlaces = [];
|
||||
this._mounts = [];
|
||||
this._bookmarks = [];
|
||||
this._isDesktopHome = gconf.get_bool(DESKTOP_IS_HOME_KEY);
|
||||
|
||||
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
||||
let homeUri = homeFile.get_uri();
|
||||
@ -139,8 +145,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
||||
@ -152,8 +158,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
|
||||
});
|
||||
|
||||
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
|
||||
@ -162,8 +168,12 @@ PlacesManager.prototype = {
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: size });
|
||||
},
|
||||
function () {
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
function (params) {
|
||||
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
|
||||
// launch it with the workspace from params. It's probably pretty rare
|
||||
// and odd to drag this place onto a workspace in any case
|
||||
|
||||
Util.spawn(['nautilus-connect-server']);
|
||||
});
|
||||
|
||||
let networkApp = null;
|
||||
@ -182,16 +192,17 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return networkApp.create_icon_texture(size);
|
||||
},
|
||||
function () {
|
||||
networkApp.launch();
|
||||
function (params) {
|
||||
params = Params.parse(params, { workspace: null,
|
||||
timestamp: 0 });
|
||||
|
||||
networkApp.launch_full(params.timestamp, [],
|
||||
params.workspace ? params.workspace.index() : -1);
|
||||
});
|
||||
}
|
||||
|
||||
this._defaultPlaces.push(this._home);
|
||||
|
||||
this._desktopMenuIndex = this._defaultPlaces.length;
|
||||
if (!this._isDesktopHome)
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
this._defaultPlaces.push(this._desktopMenu);
|
||||
|
||||
if (this._network)
|
||||
this._defaultPlaces.push(this._network);
|
||||
@ -229,9 +240,6 @@ PlacesManager.prototype = {
|
||||
}));
|
||||
|
||||
this._reloadBookmarks();
|
||||
|
||||
gconf.notify_add(DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
||||
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
@ -329,8 +337,8 @@ PlacesManager.prototype = {
|
||||
function(size) {
|
||||
return St.TextureCache.get_default().load_gicon(null, icon, size);
|
||||
},
|
||||
function() {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
|
||||
function(params) {
|
||||
Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
|
||||
});
|
||||
this._bookmarks.push(item);
|
||||
}
|
||||
@ -340,21 +348,6 @@ PlacesManager.prototype = {
|
||||
this.emit('places-updated');
|
||||
},
|
||||
|
||||
_updateDesktopMenuVisibility: function() {
|
||||
let gconf = GConf.Client.get_default();
|
||||
this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
||||
|
||||
if (this._isDesktopHome)
|
||||
this._removeById(this._defaultPlaces, 'special:desktop');
|
||||
else
|
||||
this._defaultPlaces.splice(this._desktopMenuIndex, 0,
|
||||
this._desktopMenu);
|
||||
|
||||
/* 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);
|
||||
@ -402,150 +395,8 @@ PlacesManager.prototype = {
|
||||
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
|
||||
}
|
||||
};
|
||||
|
||||
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 St.Clickable({ style_class: 'places-item',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'places-item-box' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
let bin = new St.Bin({ child: this._icon });
|
||||
box.add(bin);
|
||||
|
||||
let text = new St.Label({ text: info.name });
|
||||
box.add(text, { expand: true, x_fill: true });
|
||||
|
||||
if (info.isRemovable()) {
|
||||
let removeIcon = new St.Icon({ icon_name: 'media-eject',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: PLACES_ICON_SIZE });
|
||||
let removeIconBox = new St.Clickable({ child: removeIcon,
|
||||
reactive: true });
|
||||
box.add(removeIconBox);
|
||||
removeIconBox.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._info.remove();
|
||||
}));
|
||||
}
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
|
||||
this.actor._delegate = this;
|
||||
this._draggable = DND.makeDraggable(this.actor);
|
||||
this._draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
this._draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
_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 St.Table({ style_class: 'places-section',
|
||||
homogeneous: true });
|
||||
|
||||
this._defaultsList = [];
|
||||
this._bookmarksList = [];
|
||||
this._mountsList = [];
|
||||
|
||||
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() {
|
||||
for (let i = 0; i < this._defaultsList.length; i++)
|
||||
this._defaultsList[i].destroy();
|
||||
|
||||
this._defaultsList = [];
|
||||
let places = Main.placesManager.getDefaultPlaces();
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
this._defaultsList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._defaultsList[i], {row: i, col: 0});
|
||||
}
|
||||
this._updateMounts();
|
||||
},
|
||||
|
||||
_updateMounts: function() {
|
||||
for (let i = 0; i < this._mountsList.length; i++)
|
||||
this._mountsList[i].destroy();
|
||||
|
||||
this._mountsList = [];
|
||||
let places = Main.placesManager.getMounts();
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
this._mountsList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._mountsList[i], {row: this._defaultsList.length + i, col: 0});
|
||||
}
|
||||
},
|
||||
|
||||
_updateBookmarks: function() {
|
||||
for (let i = 0; i < this._bookmarksList.length; i++)
|
||||
this._bookmarksList[i].destroy();
|
||||
|
||||
this._bookmarksList = [];
|
||||
let places = Main.placesManager.getBookmarks();
|
||||
for (let i = 0; i < places.length; i ++) {
|
||||
this._bookmarksList[i] = new DashPlaceDisplayItem(places[i]).actor;
|
||||
this.actor.add(this._bookmarksList[i], {row: i, col: 1});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DashPlaceDisplay.prototype);
|
||||
|
||||
function PlaceSearchProvider() {
|
||||
this._init();
|
||||
@ -567,9 +418,9 @@ PlaceSearchProvider.prototype = {
|
||||
'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
activateResult: function(id, params) {
|
||||
let placeInfo = Main.placesManager.lookupPlaceById(id);
|
||||
placeInfo.launch();
|
||||
placeInfo.launch(params);
|
||||
},
|
||||
|
||||
_compareResultMeta: function (idA, idB) {
|
||||
|
350
js/ui/polkitAuthenticationAgent.js
Normal file
@ -0,0 +1,350 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||
*
|
||||
* Copyright 2010 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const PolkitAgent = imports.gi.PolkitAgent;
|
||||
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
|
||||
function AuthenticationDialog(message, cookie, userNames) {
|
||||
this._init(message, cookie, userNames);
|
||||
}
|
||||
|
||||
AuthenticationDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
|
||||
_init: function(message, cookie, userNames) {
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
|
||||
|
||||
this.message = message;
|
||||
this.userNames = userNames;
|
||||
|
||||
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
|
||||
vertical: false });
|
||||
this.contentLayout.add(mainContentBox,
|
||||
{ x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
|
||||
mainContentBox.add(icon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
|
||||
vertical: true });
|
||||
mainContentBox.add(messageBox,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
|
||||
text: _('Authentication Required') });
|
||||
|
||||
messageBox.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
|
||||
text: message });
|
||||
this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageBox.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
if (userNames.length > 1) {
|
||||
log('polkitAuthenticationAgent: Received ' + userNames.length +
|
||||
' identities that can be used for authentication. Only ' +
|
||||
'considering the first one.');
|
||||
}
|
||||
|
||||
let userName = userNames[0];
|
||||
|
||||
this._user = Gdm.UserManager.ref_default().get_user(userName);
|
||||
let userRealName = this._user.get_real_name()
|
||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
|
||||
// Special case 'root'
|
||||
if (userName == 'root')
|
||||
userRealName = _('Administrator');
|
||||
|
||||
// Work around Gdm.UserManager returning an empty string for the real name
|
||||
if (userRealName.length == 0)
|
||||
userRealName = userName;
|
||||
|
||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||
vertical: false });
|
||||
messageBox.add(userBox);
|
||||
|
||||
this._userIcon = new St.Icon();
|
||||
this._userIcon.hide();
|
||||
userBox.add(this._userIcon,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
|
||||
text: userRealName }));
|
||||
userBox.add(userLabel,
|
||||
{ x_fill: true,
|
||||
y_fill: false,
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._onUserChanged();
|
||||
|
||||
this._passwordBox = new St.BoxLayout({ vertical: false });
|
||||
messageBox.add(this._passwordBox);
|
||||
this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
|
||||
this._passwordBox.add(this._passwordLabel);
|
||||
this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
|
||||
text: _(''),
|
||||
can_focus: true});
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
|
||||
this._passwordBox.add(this._passwordEntry,
|
||||
{expand: true });
|
||||
this._passwordBox.hide();
|
||||
|
||||
this._errorBox = new St.BoxLayout({ style_class: 'polkit-dialog-error-box' });
|
||||
messageBox.add(this._errorBox);
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error',
|
||||
icon_size: 24,
|
||||
style_class: 'polkit-dialog-error-icon' });
|
||||
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||
this._errorMessage = new St.Label({ style_class: 'polkit-dialog-error-label' });
|
||||
this._errorMessage.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._errorMessage.clutter_text.line_wrap = true;
|
||||
this._errorBox.add(this._errorMessage, { expand: true,
|
||||
y_align: St.Align.MIDDLE,
|
||||
y_fill: true });
|
||||
this._errorBox.hide();
|
||||
|
||||
this._infoBox = new St.BoxLayout({ style_class: 'polkit-dialog-info-box' });
|
||||
messageBox.add(this._infoBox);
|
||||
let infoIcon = new St.Icon({ icon_name: 'dialog-information',
|
||||
icon_size: 24,
|
||||
style_class: 'polkit-dialog-info-icon' });
|
||||
this._infoBox.add(infoIcon, { y_align: St.Align.MIDDLE });
|
||||
this._infoMessage = new St.Label({ style_class: 'polkit-dialog-info-label'});
|
||||
this._infoMessage.clutter_text.line_wrap = true;
|
||||
this._infoBox.add(this._infoMessage, { expand: true,
|
||||
y_align: St.Align.MIDDLE,
|
||||
y_fill: true });
|
||||
this._infoBox.hide();
|
||||
|
||||
this.setButtons([{ label: _('Cancel'),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _('Authenticate'),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed)
|
||||
}]);
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||
this._cookie = cookie;
|
||||
|
||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||
cookie: this._cookie });
|
||||
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||
this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
this._session.initiate();
|
||||
}));
|
||||
},
|
||||
|
||||
_emitDone: function(keepVisible) {
|
||||
if (!this._doneEmitted) {
|
||||
this._doneEmitted = true;
|
||||
this.emit('done', keepVisible);
|
||||
}
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
let response = this._passwordEntry.get_text();
|
||||
this._session.response(response);
|
||||
// When the user responds, dismiss already shown info and
|
||||
// error texts (if any)
|
||||
this._errorBox.hide();
|
||||
this._infoBox.hide();
|
||||
},
|
||||
|
||||
_onAuthenticateButtonPressed: function() {
|
||||
this._onEntryActivate();
|
||||
},
|
||||
|
||||
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||
this._passwordBox.hide();
|
||||
this._emitDone(!gainedAuthorization);
|
||||
},
|
||||
|
||||
_onSessionRequest: function(session, request, echo_on) {
|
||||
// Cheap localization trick
|
||||
if (request == 'Password:')
|
||||
this._passwordLabel.set_text(_('Password:'));
|
||||
else
|
||||
this._passwordLabel.set_text(request);
|
||||
|
||||
if (echo_on)
|
||||
this._passwordEntry.clutter_text.set_password_char('');
|
||||
else
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
|
||||
this._passwordBox.show();
|
||||
this._passwordEntry.set_text('');
|
||||
this._passwordEntry.grab_key_focus();
|
||||
},
|
||||
|
||||
_onSessionShowError: function(session, text) {
|
||||
this._passwordEntry.set_text('');
|
||||
this._errorMessage.set_text(text);
|
||||
this._errorBox.show();
|
||||
},
|
||||
|
||||
_onSessionShowInfo: function(session, text) {
|
||||
this._passwordEntry.set_text('');
|
||||
this._infoMessage.set_text(text);
|
||||
this._infoBox.show();
|
||||
},
|
||||
|
||||
destroySession: function() {
|
||||
if (this._session) {
|
||||
this._session.cancel();
|
||||
this._session = null;
|
||||
}
|
||||
},
|
||||
|
||||
_onUserChanged: function() {
|
||||
if (this._user.is_loaded) {
|
||||
let iconFileName = this._user.get_icon_file();
|
||||
let iconFile = Gio.file_new_for_path(iconFileName);
|
||||
let icon;
|
||||
if (iconFile.query_exists(null)) {
|
||||
icon = new Gio.FileIcon({file: iconFile});
|
||||
} else {
|
||||
icon = new Gio.ThemedIcon({name: 'avatar-default'});
|
||||
}
|
||||
this._userIcon.set_gicon (icon);
|
||||
this._userIcon.show();
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.close(global.get_current_time());
|
||||
this._emitDone(false);
|
||||
},
|
||||
|
||||
};
|
||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||
|
||||
function AuthenticationAgent() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
AuthenticationAgent.prototype = {
|
||||
_init: function() {
|
||||
this._native = new Shell.PolkitAuthenticationAgent();
|
||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||
this._native.connect('cancel', Lang.bind(this, this._onCancel));
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
},
|
||||
|
||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||
this._currentDialog = new AuthenticationDialog(message, cookie, userNames);
|
||||
if (!this._currentDialog.open(global.get_current_time())) {
|
||||
// This can fail if e.g. unable to get input grab
|
||||
//
|
||||
// In an ideal world this wouldn't happen (because the
|
||||
// Shell is in complete control of the session) but that's
|
||||
// just not how things work right now.
|
||||
//
|
||||
// We could add retrying if this turns out to be a problem
|
||||
log('polkitAuthenticationAgent: Failed to show modal dialog');
|
||||
this._currentDialog.destroySession();
|
||||
this._currentDialog = null;
|
||||
this._native.complete()
|
||||
} else {
|
||||
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||
}
|
||||
},
|
||||
|
||||
_onCancel: function(nativeAgent) {
|
||||
this._completeRequest(false);
|
||||
},
|
||||
|
||||
_onDialogDone: function(dialog, keepVisible) {
|
||||
this._completeRequest(keepVisible);
|
||||
},
|
||||
|
||||
_reallyCompleteRequest: function() {
|
||||
this._currentDialog.close();
|
||||
this._currentDialog.destroySession();
|
||||
this._currentDialog = null;
|
||||
this._isCompleting = false;
|
||||
|
||||
this._native.complete()
|
||||
},
|
||||
|
||||
_completeRequest: function(keepVisible) {
|
||||
if (this._isCompleting)
|
||||
return;
|
||||
|
||||
this._isCompleting = true;
|
||||
|
||||
if (keepVisible) {
|
||||
// Give the user 2 seconds to read 'Authentication Failure' before
|
||||
// dismissing the dialog
|
||||
Mainloop.timeout_add(2000,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
this._reallyCompleteRequest();
|
||||
}));
|
||||
} else {
|
||||
this._reallyCompleteRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
let agent = new AuthenticationAgent();
|
||||
}
|
@ -11,16 +11,18 @@ const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
const History = imports.misc.history;
|
||||
|
||||
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
|
||||
|
||||
const HISTORY_KEY = 'command-history';
|
||||
const HISTORY_LIMIT = 512;
|
||||
|
||||
const DIALOG_FADE_TIME = 0.1;
|
||||
const DIALOG_GROW_TIME = 0.1;
|
||||
|
||||
function CommandCompleter() {
|
||||
this._init();
|
||||
@ -62,25 +64,6 @@ CommandCompleter.prototype = {
|
||||
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));
|
||||
},
|
||||
|
||||
_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));
|
||||
} else {
|
||||
this._enumerator.close(null);
|
||||
this._enumerator = null;
|
||||
this._update(this._i + 1);
|
||||
}
|
||||
},
|
||||
|
||||
update : function() {
|
||||
if (this._valid)
|
||||
return;
|
||||
@ -100,7 +83,12 @@ CommandCompleter.prototype = {
|
||||
}
|
||||
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));
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._childs[this._i].push(files[i].get_name());
|
||||
}
|
||||
this._update(this._i + 1);
|
||||
}));
|
||||
},
|
||||
|
||||
_onChanged : function(m, f, of, type) {
|
||||
@ -175,22 +163,15 @@ function RunDialog() {
|
||||
}
|
||||
|
||||
RunDialog.prototype = {
|
||||
__proto__: ModalDialog.ModalDialog.prototype,
|
||||
_init : function() {
|
||||
this._isOpen = false;
|
||||
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
|
||||
|
||||
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
|
||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||
}));
|
||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||
|
||||
this._history = global.settings.get_strv(HISTORY_KEY);
|
||||
this._historyIndex = -1;
|
||||
|
||||
global.settings.connect('changed::' + HISTORY_KEY, Lang.bind(this, function() {
|
||||
this._history = global.settings.get_strv(HISTORY_KEY);
|
||||
this._historyIndex = this._history.length;
|
||||
}));
|
||||
|
||||
this._internalCommands = { 'lg':
|
||||
Lang.bind(this, function() {
|
||||
Main.createLookingGlass().open();
|
||||
@ -207,66 +188,65 @@ RunDialog.prototype = {
|
||||
|
||||
'debugexit': Lang.bind(this, function() {
|
||||
Meta.exit(Meta.ExitCode.ERROR);
|
||||
}),
|
||||
|
||||
// rt is short for "reload theme"
|
||||
'rt': Lang.bind(this, function() {
|
||||
Main.loadTheme();
|
||||
})
|
||||
};
|
||||
|
||||
// All actors are inside _group. We create it initially
|
||||
// hidden then show it in show()
|
||||
this._group = new Clutter.Group({ visible: false,
|
||||
x: 0, y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
|
||||
this._box = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._group.add_actor(this._box);
|
||||
this._lightbox.highlight(this._box);
|
||||
|
||||
let dialogBox = new St.BoxLayout({ style_class: 'run-dialog', vertical: true });
|
||||
|
||||
this._box.set_child(dialogBox);
|
||||
|
||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||
text: _("Please enter a command:") });
|
||||
|
||||
dialogBox.add(label, { expand: true, y_fill: false });
|
||||
this.contentLayout.add(label, { y_align: St.Align.START });
|
||||
|
||||
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
|
||||
|
||||
this._entryText = entry.clutter_text;
|
||||
dialogBox.add(entry, { expand: true });
|
||||
this.contentLayout.add(entry, { y_align: St.Align.START });
|
||||
this.connect('opened',
|
||||
Lang.bind(this, function() {
|
||||
this._entryText.grab_key_focus();
|
||||
}));
|
||||
|
||||
this._errorBox = new St.BoxLayout();
|
||||
this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
|
||||
|
||||
dialogBox.add(this._errorBox, { expand: true });
|
||||
this.contentLayout.add(this._errorBox, { expand: true });
|
||||
|
||||
let errorIcon = new St.Button({ style_class: 'run-dialog-error-icon' });
|
||||
let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
|
||||
|
||||
this._errorBox.add(errorIcon);
|
||||
this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
|
||||
|
||||
this._commandError = false;
|
||||
|
||||
this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
|
||||
this._errorMessage.clutter_text.line_wrap = true;
|
||||
|
||||
this._errorBox.add(this._errorMessage, { expand: true });
|
||||
this._errorBox.add(this._errorMessage, { expand: true,
|
||||
y_align: St.Align.MIDDLE,
|
||||
y_fill: false });
|
||||
|
||||
this._errorBox.hide();
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
|
||||
this._history = new History.HistoryManager(HISTORY_KEY);
|
||||
this._history.connect('changed', Lang.bind(this, function(history, text) {
|
||||
this._entryText.set_text(text);
|
||||
}));
|
||||
|
||||
this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
|
||||
let symbol = e.get_key_symbol();
|
||||
if (symbol == Clutter.Down) {
|
||||
this._setCommandFromHistory(this._historyIndex++);
|
||||
this._history.nextItem(o.get_text());
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Up) {
|
||||
this._setCommandFromHistory(this._historyIndex--);
|
||||
this._history.prevItem(o.get_text());
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
@ -275,10 +255,10 @@ RunDialog.prototype = {
|
||||
else
|
||||
this._run(o.get_text(), false);
|
||||
if (!this._commandError)
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
if (symbol == Clutter.Escape) {
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.slash) {
|
||||
@ -321,22 +301,10 @@ RunDialog.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_saveHistory : function() {
|
||||
if (this._history.length > HISTORY_LIMIT) {
|
||||
this._history.splice(0, this._history.length - HISTORY_LIMIT);
|
||||
}
|
||||
global.settings.set_strv(HISTORY_KEY, this._history);
|
||||
},
|
||||
|
||||
_run : function(input, inTerminal) {
|
||||
let command = input;
|
||||
|
||||
if (this._history.length == 0 ||
|
||||
this._history[this._history.length - 1] != input) {
|
||||
this._history.push(input);
|
||||
this._saveHistory();
|
||||
}
|
||||
|
||||
this._history.addItem(input);
|
||||
this._commandError = false;
|
||||
let f;
|
||||
if (this._enableInternalCommands)
|
||||
@ -349,9 +317,7 @@ RunDialog.prototype = {
|
||||
try {
|
||||
if (inTerminal)
|
||||
command = 'gnome-terminal -x ' + input;
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({ 'args' : args });
|
||||
p.run();
|
||||
Util.trySpawnCommandLine(command);
|
||||
} catch (e) {
|
||||
// Mmmh, that failed - see if @input matches an existing file
|
||||
let path = null;
|
||||
@ -369,84 +335,37 @@ RunDialog.prototype = {
|
||||
global.create_app_launch_context());
|
||||
} else {
|
||||
this._commandError = true;
|
||||
// The exception contains an error string like:
|
||||
// Error invoking Shell.run: Failed to execute child
|
||||
// process "foo" (No such file or directory)
|
||||
// We are only interested in the actual error, so parse
|
||||
//that out.
|
||||
let m = /.+\((.+)\)/.exec(e);
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + m[1];
|
||||
this._errorMessage.set_text(errorStr);
|
||||
|
||||
this._errorBox.show();
|
||||
this._errorMessage.set_text(e.message);
|
||||
|
||||
if (!this._errorBox.visible) {
|
||||
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
|
||||
|
||||
let parentActor = this._errorBox.get_parent();
|
||||
Tweener.addTween(parentActor,
|
||||
{ height: parentActor.height + errorBoxNaturalHeight,
|
||||
time: DIALOG_GROW_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
parentActor.set_height(-1);
|
||||
this._errorBox.show();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_setCommandFromHistory: function(lastI) {
|
||||
if (this._historyIndex < 0)
|
||||
this._historyIndex = 0;
|
||||
if (this._historyIndex > this._history.length)
|
||||
this._historyIndex = this._history.length;
|
||||
|
||||
let text = this._entryText.get_text();
|
||||
if (text) {
|
||||
this._history[lastI] = text;
|
||||
}
|
||||
if (this._history[this._historyIndex]) {
|
||||
this._entryText.set_text(this._history[this._historyIndex]);
|
||||
} else
|
||||
this._entryText.set_text('');
|
||||
},
|
||||
|
||||
open : function() {
|
||||
if (this._isOpen) // Already shown
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._group))
|
||||
return;
|
||||
|
||||
// Position the dialog on the current monitor
|
||||
let monitor = global.get_focus_monitor();
|
||||
|
||||
this._historyIndex = this._history.length;
|
||||
|
||||
this._box.set_position(monitor.x, monitor.y);
|
||||
this._box.set_size(monitor.width, monitor.height);
|
||||
|
||||
this._isOpen = true;
|
||||
this._lightbox.show();
|
||||
this._group.opacity = 0;
|
||||
this._group.show();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 255,
|
||||
time: DIALOG_FADE_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
global.stage.set_key_focus(this._entryText);
|
||||
},
|
||||
|
||||
close : function() {
|
||||
if (!this._isOpen)
|
||||
return;
|
||||
|
||||
this._isOpen = false;
|
||||
open: function() {
|
||||
this._history.lastItem();
|
||||
this._errorBox.hide();
|
||||
this._entryText.set_text('');
|
||||
this._commandError = false;
|
||||
|
||||
Main.popModal(this._group);
|
||||
ModalDialog.ModalDialog.prototype.open.call(this);
|
||||
},
|
||||
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: DIALOG_FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._errorBox.hide();
|
||||
this._group.hide();
|
||||
this._entryText.set_text('');
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(RunDialog.prototype);
|
||||
|
120
js/ui/search.js
@ -1,8 +1,21 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const RESULT_ICON_SIZE = 24;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
|
||||
|
||||
const RESULT_ICON_SIZE = 48;
|
||||
|
||||
// Not currently referenced by the search API, but
|
||||
// this enumeration can be useful for provider
|
||||
@ -182,7 +195,7 @@ SearchProvider.prototype = {
|
||||
* 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'.
|
||||
* 'search-result-content'.
|
||||
*/
|
||||
createResultActor: function(resultMeta, terms) {
|
||||
return null;
|
||||
@ -211,6 +224,107 @@ SearchProvider.prototype = {
|
||||
};
|
||||
Signals.addSignalMethods(SearchProvider.prototype);
|
||||
|
||||
function OpenSearchSystem() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
OpenSearchSystem.prototype = {
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
let res = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
res.push({ id: i, name: this._providers[i].name });
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
setSearchTerms: function(terms) {
|
||||
this._terms = terms;
|
||||
},
|
||||
|
||||
_checkSupportedProviderLanguage: function(provider) {
|
||||
if (provider.url.search(/{language}/) == -1)
|
||||
return true;
|
||||
|
||||
let langs = GLib.get_language_names();
|
||||
|
||||
langs.push('en');
|
||||
let lang = null;
|
||||
for (let i = 0; i < langs.length; i++) {
|
||||
for (let k = 0; k < provider.langs.length; k++) {
|
||||
if (langs[i] == provider.langs[k])
|
||||
lang = langs[i];
|
||||
}
|
||||
if (lang)
|
||||
break;
|
||||
}
|
||||
provider.lang = lang;
|
||||
return lang != null;
|
||||
},
|
||||
|
||||
activateResult: function(id, params) {
|
||||
let searchTerms = this._terms.join(' ');
|
||||
|
||||
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
|
||||
if (url.match('{language}'))
|
||||
url = url.replace('{language}', this._providers[id].lang);
|
||||
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
} catch (e) {
|
||||
// TODO: remove this after glib will be removed from moduleset
|
||||
// In the default jhbuild, gio is in our prefix but gvfs is not
|
||||
Util.spawn(['gvfs-open', url])
|
||||
}
|
||||
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
_addProvider: function(fileName) {
|
||||
let file = Gio.file_new_for_path(global.datadir + '/search_providers/' + fileName);
|
||||
let source = '';
|
||||
|
||||
file.load_contents_async(null, Lang.bind(this, function (obj, res) {
|
||||
let [success, source] = file.load_contents_finish(res);
|
||||
if (source) {
|
||||
let [success, name, url, langs, icon_uri] = global.parse_search_provider(source);
|
||||
let provider ={ name: name,
|
||||
url: url,
|
||||
id: this._providers.length,
|
||||
icon_uri: icon_uri,
|
||||
langs: langs };
|
||||
if (this._checkSupportedProviderLanguage(provider)) {
|
||||
this._providers.push(provider);
|
||||
this.emit('changed');
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
this._providers = [];
|
||||
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
|
||||
let file = Gio.file_new_for_path(global.datadir + '/search_providers');
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let enabled = true;
|
||||
let name = files[i].get_name();
|
||||
for (let k = 0; k < names.length; k++)
|
||||
if (names[k] == name)
|
||||
enabled = false;
|
||||
if (enabled)
|
||||
this._addProvider(name);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(OpenSearchSystem.prototype);
|
||||
|
||||
function SearchSystem() {
|
||||
this._init();
|
||||
}
|
||||
@ -241,7 +355,7 @@ SearchSystem.prototype = {
|
||||
updateSearch: function(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (searchString == '')
|
||||
return null;
|
||||
return [];
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
let isSubSearch = terms.length == this._previousTerms.length;
|
||||
|
425
js/ui/searchDisplay.js
Normal file
@ -0,0 +1,425 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const MAX_SEARCH_RESULTS_ROWS = 2;
|
||||
|
||||
|
||||
function SearchResult(provider, metaInfo, terms) {
|
||||
this._init(provider, metaInfo, terms);
|
||||
}
|
||||
|
||||
SearchResult.prototype = {
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.actor = new St.Button({ style_class: 'search-result',
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
let content = provider.createResultActor(metaInfo, terms);
|
||||
if (content == null) {
|
||||
content = new St.Bin({ style_class: 'search-result-content',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: Lang.bind(this, function(size) {
|
||||
return this.metaInfo['icon'];
|
||||
})});
|
||||
content.set_child(icon.actor);
|
||||
}
|
||||
this._content = content;
|
||||
this.actor.set_child(content);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.beginItemDrag(this);
|
||||
}));
|
||||
draggable.connect('drag-end',
|
||||
Lang.bind(this, function() {
|
||||
Main.overview.endItemDrag(this);
|
||||
}));
|
||||
},
|
||||
|
||||
setSelected: function(selected) {
|
||||
if (selected)
|
||||
this._content.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._content.remove_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.provider.activateResult(this.metaInfo.id);
|
||||
Main.overview.toggle();
|
||||
},
|
||||
|
||||
_onResultClicked: function(actor) {
|
||||
this.activate();
|
||||
},
|
||||
|
||||
getDragActorSource: function() {
|
||||
return this.metaInfo['icon'];
|
||||
},
|
||||
|
||||
getDragActor: function(stageX, stageY) {
|
||||
return new Clutter.Clone({ source: this.metaInfo['icon'] });
|
||||
},
|
||||
|
||||
shellWorkspaceLaunch: function(params) {
|
||||
if (this.provider.dragActivateResult)
|
||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||
else
|
||||
this.provider.activateResult(this.metaInfo.id, params);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function GridSearchResults(provider) {
|
||||
this._init(provider);
|
||||
}
|
||||
|
||||
GridSearchResults.prototype = {
|
||||
__proto__: Search.SearchResultDisplay.prototype,
|
||||
|
||||
_init: function(provider) {
|
||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
this.actor = new St.Bin({ x_align: St.Align.START });
|
||||
this.actor.set_child(this._grid.actor);
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
|
||||
getVisibleResultCount: function() {
|
||||
return this._grid.visibleItemsCount();
|
||||
},
|
||||
|
||||
renderResults: function(results, terms) {
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let result = results[i];
|
||||
let meta = this.provider.getResultMeta(result);
|
||||
let display = new SearchResult(this.provider, meta, terms);
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this._grid.removeAll();
|
||||
this.selectionIndex = -1;
|
||||
},
|
||||
|
||||
selectIndex: function (index) {
|
||||
let nVisible = this.getVisibleResultCount();
|
||||
if (this.selectionIndex >= 0) {
|
||||
let prevActor = this._grid.getItemAtIndex(this.selectionIndex);
|
||||
prevActor._delegate.setSelected(false);
|
||||
}
|
||||
this.selectionIndex = -1;
|
||||
if (index >= nVisible)
|
||||
return false;
|
||||
else if (index < 0)
|
||||
return false;
|
||||
let targetActor = this._grid.getItemAtIndex(index);
|
||||
targetActor._delegate.setSelected(true);
|
||||
this.selectionIndex = index;
|
||||
return true;
|
||||
},
|
||||
|
||||
activateSelected: function() {
|
||||
if (this.selectionIndex < 0)
|
||||
return;
|
||||
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
|
||||
targetActor._delegate.activate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function SearchResults(searchSystem, openSearchSystem) {
|
||||
this._init(searchSystem, openSearchSystem);
|
||||
}
|
||||
|
||||
SearchResults.prototype = {
|
||||
_init: function(searchSystem, openSearchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._openSearchSystem = openSearchSystem;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
|
||||
let scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
vfade: true });
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
scrollView.add_actor(this._content);
|
||||
|
||||
this.actor.add(scrollView, { x_fill: true,
|
||||
y_fill: false,
|
||||
expand: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped)
|
||||
return;
|
||||
|
||||
let adjustment = scrollView.vscroll.adjustment;
|
||||
let direction = Overview.SwipeScrollDirection.VERTICAL;
|
||||
Main.overview.setScrollAdjustment(adjustment, direction);
|
||||
}));
|
||||
|
||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||
this._content.add(this._statusText);
|
||||
this._selectedProvider = -1;
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerMeta = [];
|
||||
for (let i = 0; i < this._providers.length; i++)
|
||||
this.createProviderMeta(this._providers[i]);
|
||||
|
||||
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
|
||||
this.actor.add(this._searchProvidersBox);
|
||||
|
||||
this._openSearchProviders = [];
|
||||
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
|
||||
this._updateOpenSearchProviderButtons();
|
||||
},
|
||||
|
||||
_updateOpenSearchProviderButtons: function() {
|
||||
this._selectedOpenSearchButton = -1;
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._openSearchProviders[i].actor.destroy();
|
||||
this._openSearchProviders = this._openSearchSystem.getProviders();
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++)
|
||||
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
|
||||
},
|
||||
|
||||
_updateOpenSearchButtonState: function() {
|
||||
for (let i = 0; i < this._openSearchProviders.length; i++) {
|
||||
if (i == this._selectedOpenSearchButton)
|
||||
this._openSearchProviders[i].actor.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._openSearchProviders[i].actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_createOpenSearchProviderButton: function(provider) {
|
||||
let button = new St.Button({ style_class: 'dash-search-button',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_align: St.Align.MIDDLE });
|
||||
let bin = new St.Bin({ x_fill: false,
|
||||
x_align:St.Align.MIDDLE });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
}));
|
||||
let title = new St.Label({ text: provider.name,
|
||||
style_class: 'dash-search-button-label' });
|
||||
|
||||
bin.set_child(title);
|
||||
button.set_child(bin);
|
||||
provider.actor = button;
|
||||
|
||||
this._searchProvidersBox.add(button);
|
||||
},
|
||||
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
let titleButton = new St.Button({ style_class: 'search-section-header',
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
|
||||
providerBox.add(titleButton);
|
||||
let titleBox = new St.BoxLayout();
|
||||
titleButton.set_child(titleBox);
|
||||
let title = new St.Label({ text: provider.title });
|
||||
let count = new St.Label();
|
||||
titleBox.add(title, { expand: true });
|
||||
titleBox.add(count);
|
||||
|
||||
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
providerBox.add(resultDisplayBin, { expand: true });
|
||||
let resultDisplay = provider.createResultContainerActor();
|
||||
if (resultDisplay == null) {
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
resultDisplayBin.set_child(resultDisplay.actor);
|
||||
|
||||
this._providerMeta.push({ actor: providerBox,
|
||||
resultDisplay: resultDisplay,
|
||||
count: count });
|
||||
this._content.add(providerBox);
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
this._selectedProvider = -1;
|
||||
this._visibleResultsCount = 0;
|
||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
meta.resultDisplay.clear();
|
||||
meta.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._searchSystem.reset();
|
||||
this._statusText.hide();
|
||||
this._clearDisplay();
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._statusText.set_text(_("Searching..."));
|
||||
this._statusText.show();
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
return this._providerMeta[this._providers.indexOf(provider)];
|
||||
},
|
||||
|
||||
updateSearch: function (searchString) {
|
||||
let results = this._searchSystem.updateSearch(searchString);
|
||||
|
||||
this._clearDisplay();
|
||||
|
||||
if (results.length == 0) {
|
||||
this._statusText.set_text(_("No matching results."));
|
||||
this._statusText.show();
|
||||
} else {
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
this._statusText.hide();
|
||||
}
|
||||
|
||||
let terms = this._searchSystem.getTerms();
|
||||
this._openSearchSystem.setSearchTerms(terms);
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let [provider, providerResults] = results[i];
|
||||
let meta = this._metaForProvider(provider);
|
||||
meta.actor.show();
|
||||
meta.resultDisplay.renderResults(providerResults, terms);
|
||||
meta.count.set_text('' + providerResults.length);
|
||||
}
|
||||
|
||||
if (this._selectedOpenSearchButton == -1)
|
||||
this.selectDown(false);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onHeaderClicked: function(provider) {
|
||||
provider.expandSearch(this._searchSystem.getTerms());
|
||||
},
|
||||
|
||||
_modifyActorSelection: function(resultDisplay, up) {
|
||||
let success;
|
||||
let index = resultDisplay.getSelectionIndex();
|
||||
if (up && index == -1)
|
||||
index = resultDisplay.getVisibleResultCount() - 1;
|
||||
else if (up)
|
||||
index = index - 1;
|
||||
else
|
||||
index = index + 1;
|
||||
return resultDisplay.selectIndex(index);
|
||||
},
|
||||
|
||||
selectUp: function(recursing) {
|
||||
if (this._selectedOpenSearchButton == -1) {
|
||||
for (let i = this._selectedProvider; i >= 0; i--) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, true);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._selectedOpenSearchButton == -1)
|
||||
this._selectedOpenSearchButton = this._openSearchProviders.length;
|
||||
this._selectedOpenSearchButton--;
|
||||
this._updateOpenSearchButtonState();
|
||||
if (this._selectedOpenSearchButton >= 0)
|
||||
return;
|
||||
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = this._providerMeta.length - 1;
|
||||
this.selectUp(true);
|
||||
}
|
||||
},
|
||||
|
||||
selectDown: function(recursing) {
|
||||
let current = this._selectedProvider;
|
||||
if (this._selectedOpenSearchButton == -1) {
|
||||
if (current == -1)
|
||||
current = 0;
|
||||
for (let i = current; i < this._providerMeta.length; i++) {
|
||||
let meta = this._providerMeta[i];
|
||||
if (!meta.actor.visible)
|
||||
continue;
|
||||
let success = this._modifyActorSelection(meta.resultDisplay, false);
|
||||
if (success) {
|
||||
this._selectedProvider = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._selectedOpenSearchButton++;
|
||||
|
||||
if (this._selectedOpenSearchButton < this._openSearchProviders.length) {
|
||||
this._updateOpenSearchButtonState();
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectedOpenSearchButton = -1;
|
||||
this._updateOpenSearchButtonState();
|
||||
|
||||
if (this._providerMeta.length > 0 && !recursing) {
|
||||
this._selectedProvider = 0;
|
||||
this.selectDown(true);
|
||||
}
|
||||
},
|
||||
|
||||
activateSelected: function() {
|
||||
if (this._selectedOpenSearchButton != -1) {
|
||||
let provider = this._openSearchProviders[this._selectedOpenSearchButton];
|
||||
this._openSearchSystem.activateResult(provider.id);
|
||||
Main.overview.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
let current = this._selectedProvider;
|
||||
if (current < 0)
|
||||
return;
|
||||
let meta = this._providerMeta[current];
|
||||
let resultDisplay = meta.resultDisplay;
|
||||
resultDisplay.activateSelected();
|
||||
Main.overview.hide();
|
||||
}
|
||||
};
|
@ -10,24 +10,23 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const A11Y_SCHEMA = "org.gnome.desktop.a11y.keyboard";
|
||||
const KEY_STICKY_KEYS_ENABLED = "stickykeys-enable";
|
||||
const KEY_BOUNCE_KEYS_ENABLED = "bouncekeys-enable";
|
||||
const KEY_SLOW_KEYS_ENABLED = "slowkeys-enable";
|
||||
const KEY_MOUSE_KEYS_ENABLED = "mousekeys-enable";
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
|
||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
|
||||
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
|
||||
const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
|
||||
|
||||
const AT_SCREEN_KEYBOARD_SCHEMA = "org.gnome.desktop.default-applications.at.mobility";
|
||||
const AT_SCREEN_READER_SCHEMA = "org.gnome.desktop.default-applications.at.visual";
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
|
||||
const XSETTINGS_SCHEMA = "org.gnome.settings-daemon.plugins.xsettings";
|
||||
const KEY_DPI = "dpi";
|
||||
const XSETTINGS_SCHEMA = 'org.gnome.settings-daemon.plugins.xsettings';
|
||||
const KEY_DPI = 'dpi';
|
||||
|
||||
const DPI_LOW_REASONABLE_VALUE = 50;
|
||||
const DPI_HIGH_REASONABLE_VALUE = 500;
|
||||
@ -37,14 +36,14 @@ const DPI_FACTOR_LARGER = 1.5;
|
||||
const DPI_FACTOR_LARGEST = 2.0;
|
||||
const DPI_DEFAULT = 96;
|
||||
|
||||
const KEY_META_DIR = "/apps/metacity/general";
|
||||
const KEY_VISUAL_BELL = KEY_META_DIR + "/visual_bell";
|
||||
const KEY_META_DIR = '/apps/metacity/general';
|
||||
const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell';
|
||||
|
||||
const DESKTOP_INTERFACE_SCHEMA = "org.gnome.desktop.interface";
|
||||
const KEY_GTK_THEME = "gtk-theme";
|
||||
const KEY_ICON_THEME = "icon-theme";
|
||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
|
||||
const KEY_GTK_THEME = 'gtk-theme';
|
||||
const KEY_ICON_THEME = 'icon-theme';
|
||||
|
||||
const HIGH_CONTRAST_THEME = "HighContrast";
|
||||
const HIGH_CONTRAST_THEME = 'HighContrast';
|
||||
|
||||
function getDPIFromX() {
|
||||
let screen = global.get_gdk_screen();
|
||||
@ -79,16 +78,19 @@ ATIndicator.prototype = {
|
||||
let highContrast = this._buildHCItem();
|
||||
this.menu.addMenuItem(highContrast);
|
||||
|
||||
let magnifier = this._buildMagItem();
|
||||
let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
|
||||
'screen-magnifier-enabled');
|
||||
this.menu.addMenuItem(magnifier);
|
||||
|
||||
let textZoom = this._buildFontItem();
|
||||
this.menu.addMenuItem(textZoom);
|
||||
|
||||
let screenReader = this._buildItem(_("Screen Reader"), AT_SCREEN_READER_SCHEMA, 'startup');
|
||||
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
|
||||
'screen-reader-enabled');
|
||||
this.menu.addMenuItem(screenReader);
|
||||
|
||||
let screenKeyboard = this._buildItem(_("Screen Keyboard"), AT_SCREEN_KEYBOARD_SCHEMA, 'startup');
|
||||
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
|
||||
'screen-keyboard-enabled');
|
||||
this.menu.addMenuItem(screenKeyboard);
|
||||
|
||||
let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
|
||||
@ -108,8 +110,7 @@ ATIndicator.prototype = {
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Universal Access Settings"), function() {
|
||||
let p = new Shell.Process({ args: ['gnome-control-center','universal-access'] });
|
||||
p.run();
|
||||
Util.spawnDesktop('gnome-universal-access-panel');
|
||||
});
|
||||
},
|
||||
|
||||
@ -167,9 +168,12 @@ ATIndicator.prototype = {
|
||||
if (enabled) {
|
||||
settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
|
||||
settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
|
||||
} else {
|
||||
} else if(!hasHC) {
|
||||
settings.set_string(KEY_GTK_THEME, gtkTheme);
|
||||
settings.set_string(KEY_ICON_THEME, iconTheme);
|
||||
} else {
|
||||
settings.reset(KEY_GTK_THEME);
|
||||
settings.reset(KEY_ICON_THEME);
|
||||
}
|
||||
});
|
||||
settings.connect('changed::' + KEY_GTK_THEME, function() {
|
||||
@ -219,18 +223,6 @@ ATIndicator.prototype = {
|
||||
return widget;
|
||||
},
|
||||
|
||||
_buildMagItem: function() {
|
||||
let mag = Main.magnifier;
|
||||
let widget = this._buildItemExtended(_("Zoom"),
|
||||
mag.isActive(),
|
||||
true,
|
||||
Lang.bind(mag, mag.setActive));
|
||||
mag.connect('active-changed', function(magnifier, active) {
|
||||
widget.setToggleState(active);
|
||||
});
|
||||
return widget;
|
||||
},
|
||||
|
||||
_keyChanged: function() {
|
||||
this.emit('gconf-changed');
|
||||
}
|
||||
|
492
js/ui/status/bluetooth.js
Normal file
@ -0,0 +1,492 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const ConnectionState = {
|
||||
DISCONNECTED: 0,
|
||||
CONNECTED: 1,
|
||||
DISCONNECTING: 2,
|
||||
CONNECTING: 3
|
||||
}
|
||||
|
||||
function Indicator() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Indicator.prototype = {
|
||||
__proto__: PanelMenu.SystemStatusButton.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
|
||||
|
||||
GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
|
||||
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
|
||||
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
|
||||
this._killswitch.connect('toggled', Lang.bind(this, function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
|
||||
this._applet.killswitch_state = this._killswitch.state ?
|
||||
GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
|
||||
GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
|
||||
} else
|
||||
this._killswitch.setToggleState(false);
|
||||
}));
|
||||
|
||||
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
|
||||
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
|
||||
this._discoverable.setToggleState(this._applet.discoverable);
|
||||
}));
|
||||
this._discoverable.connect('toggled', Lang.bind(this, function() {
|
||||
this._applet.discoverable = this._discoverable.state;
|
||||
}));
|
||||
|
||||
this._updateKillswitch();
|
||||
this.menu.addMenuItem(this._killswitch);
|
||||
this.menu.addMenuItem(this._discoverable);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
||||
new PopupMenu.PopupMenuItem(_("Setup a New Device...")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
|
||||
|
||||
this._fullMenuItems[1].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-sendto');
|
||||
});
|
||||
this._fullMenuItems[2].connect('activate', function() {
|
||||
GLib.spawn_command_line_async('bluetooth-wizard');
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._fullMenuItems.length; i++) {
|
||||
let item = this._fullMenuItems[i];
|
||||
this.menu.addMenuItem(item);
|
||||
}
|
||||
|
||||
this._deviceItemPosition = 3;
|
||||
this._deviceItems = [];
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
|
||||
this._updateDevices();
|
||||
|
||||
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
|
||||
this._updateFullMenu();
|
||||
|
||||
this.menu.addAction(_("Bluetooth Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center bluetooth');
|
||||
});
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
},
|
||||
|
||||
_updateKillswitch: function() {
|
||||
let current_state = this._applet.killswitch_state;
|
||||
let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
|
||||
let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
|
||||
let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
|
||||
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
|
||||
|
||||
this._killswitch.setToggleState(on);
|
||||
this._killswitch.actor.reactive = can_toggle;
|
||||
|
||||
if (has_adapter)
|
||||
this.actor.show();
|
||||
else
|
||||
this.actor.hide();
|
||||
|
||||
if (on) {
|
||||
this._discoverable.actor.show();
|
||||
this.setIcon('bluetooth-active');
|
||||
} else {
|
||||
this._discoverable.actor.hide();
|
||||
this.setIcon('bluetooth-disabled');
|
||||
}
|
||||
},
|
||||
|
||||
_deviceCompare: function(d1, d2) {
|
||||
return d1.device_path == d2.device_path &&
|
||||
d1.bdaddr == d2.bdaddr &&
|
||||
d1.can_connect == d2.can_connect &&
|
||||
d1.capabilities == d2.capabilities;
|
||||
},
|
||||
|
||||
_updateDevices: function() {
|
||||
let devices = this._applet.get_devices();
|
||||
|
||||
for (let i = 0; i < this._deviceItems.length; i++) {
|
||||
let item = this._deviceItems[i];
|
||||
let destroy = true;
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
// we need to deep compare because BluetoothSimpleDevice is a boxed type
|
||||
// (but we take advantage of that, because _skip will disappear the next
|
||||
// time get_devices() is called)
|
||||
if (this._deviceCompare(item._device, devices[i])) {
|
||||
item.label.text = devices[i].alias;
|
||||
devices[i]._skip = true;
|
||||
destroy = false;
|
||||
}
|
||||
}
|
||||
if (destroy) {
|
||||
item.destroy();
|
||||
item._destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
let newlist = [ ];
|
||||
for (let i = 0; i < this._deviceItems.length; i++) {
|
||||
let item = this._deviceItems[i];
|
||||
if (!item._destroyed)
|
||||
newlist.push(item);
|
||||
}
|
||||
this._deviceItems = newlist;
|
||||
|
||||
this._hasDevices = newlist.length > 0;
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
let d = devices[i];
|
||||
if (d._skip)
|
||||
continue;
|
||||
let item = this._createDeviceItem(d);
|
||||
if (item) {
|
||||
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
|
||||
this._deviceItems.push(item);
|
||||
this._hasDevices = true;
|
||||
}
|
||||
}
|
||||
if (this._hasDevices)
|
||||
this._deviceSep.actor.show();
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
},
|
||||
|
||||
_createDeviceItem: function(device) {
|
||||
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
|
||||
return null;
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
|
||||
item._device = device;
|
||||
|
||||
if (device.can_connect) {
|
||||
item._connected = device.connected;
|
||||
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
|
||||
|
||||
menuitem.connect('toggled', Lang.bind(this, function() {
|
||||
if (item._connected > ConnectionState.CONNECTED) {
|
||||
// operation already in progress, revert
|
||||
menuitem.setToggleState(menuitem.state);
|
||||
}
|
||||
if (item._connected) {
|
||||
item._connected = ConnectionState.DISCONNECTING;
|
||||
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
item._connected = ConnectionState.CONNECTING;
|
||||
this._applet.connect_device(item._device.device_path, function(applet, success) {
|
||||
if (success) { // apply
|
||||
item._connected = ConnectionState.CONNECTED;
|
||||
menuitem.setToggleState(true);
|
||||
} else { // revert
|
||||
item._connected = ConnectionState.DISCONNECTED;
|
||||
menuitem.setToggleState(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
item.menu.addMenuItem(menuitem);
|
||||
}
|
||||
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
||||
item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
|
||||
this._applet.send_to_address(device.bdaddr, device.alias);
|
||||
}));
|
||||
}
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
|
||||
item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
|
||||
this._applet.browse_address(device.bdaddr, event.get_time(),
|
||||
Lang.bind(this, function(applet, result) {
|
||||
try {
|
||||
applet.browse_address_finish(result);
|
||||
} catch (e) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new MessageTray.Notification(this._source,
|
||||
_("Bluetooth"),
|
||||
_("Error browsing device"),
|
||||
{ body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
switch (device.type) {
|
||||
case GnomeBluetoothApplet.Type.KEYBOARD:
|
||||
item.menu.addAction(_("Keyboard Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center keyboard');
|
||||
});
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.MOUSE:
|
||||
item.menu.addAction(_("Mouse Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center mouse');
|
||||
});
|
||||
break;
|
||||
case GnomeBluetoothApplet.Type.HEADSET:
|
||||
case GnomeBluetoothApplet.Type.HEADPHONES:
|
||||
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
|
||||
item.menu.addAction(_("Sound Settings"), function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center sound');
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
_updateFullMenu: function() {
|
||||
if (this._applet.show_full_menu) {
|
||||
this._showAll(this._fullMenuItems);
|
||||
if (this._hasDevices)
|
||||
this._showAll(this._deviceItems);
|
||||
else
|
||||
this._deviceSep.actor.hide();
|
||||
} else {
|
||||
this._hideAll(this._fullMenuItems);
|
||||
this._hideAll(this._deviceItems);
|
||||
}
|
||||
},
|
||||
|
||||
_showAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.show();
|
||||
},
|
||||
|
||||
_hideAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].actor.hide();
|
||||
},
|
||||
|
||||
_destroyAll: function(items) {
|
||||
for (let i = 0; i < items.length; i++)
|
||||
items[i].destroy();
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new Source();
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
|
||||
_authRequest: function(applet, device_path, name, long_name, uuid) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
|
||||
},
|
||||
|
||||
_confirmRequest: function(applet, device_path, name, long_name, pin) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
|
||||
},
|
||||
|
||||
_pinRequest: function(applet, device_path, name, long_name, numeric) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
|
||||
},
|
||||
|
||||
_cancelRequest: function() {
|
||||
this._source.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function Source() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
Source.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function() {
|
||||
MessageTray.Source.prototype._init.call(this, _("Bluetooth"));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
},
|
||||
|
||||
notify: function(notification) {
|
||||
this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
|
||||
if (this.notification == notification) {
|
||||
// the destroyed notification is the last for this source
|
||||
this.notification.disconnect(this._private_destroyId);
|
||||
this.destroy();
|
||||
}
|
||||
}));
|
||||
|
||||
MessageTray.Source.prototype.notify.call(this, notification);
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return new St.Icon({ icon_name: 'bluetooth-active',
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
icon_size: this.ICON_SIZE });
|
||||
}
|
||||
}
|
||||
|
||||
function AuthNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
AuthNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, uuid) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Authorization request from %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
|
||||
|
||||
this.addButton('always-grant', _("Always grant access"));
|
||||
this.addButton('grant', _("Grant this time only"));
|
||||
this.addButton('reject', _("Reject"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'always-grant':
|
||||
this._applet.agent_reply_auth(this._devicePath, true, true);
|
||||
break;
|
||||
case 'grant':
|
||||
this._applet.agent_reply_auth(this._devicePath, true, false);
|
||||
break;
|
||||
case 'reject':
|
||||
default:
|
||||
this._applet.agent_reply_auth(this._devicePath, false, false);
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function ConfirmNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
ConfirmNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, pin) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Pairing confirmation for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
|
||||
|
||||
this.addButton('matches', _("Matches"));
|
||||
this.addButton('does-not-match', _("Does not match"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'matches')
|
||||
this._applet.agent_reply_confirm(this._devicePath, true);
|
||||
else
|
||||
this._applet.agent_reply_confirm(this._devicePath, false);
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function PinNotification() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
PinNotification.prototype = {
|
||||
__proto__: MessageTray.Notification.prototype,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, numeric) {
|
||||
MessageTray.Notification.prototype._init.call(this,
|
||||
source,
|
||||
_("Bluetooth"),
|
||||
_("Pairing request for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this._numeric = numeric;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please enter the PIN mentioned on the device."));
|
||||
|
||||
this._entry = new St.Entry();
|
||||
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Return) {
|
||||
this.emit('action-invoked', 'ok');
|
||||
return true;
|
||||
} else if (key == Clutter.KEY_Escape) {
|
||||
this.emit('action-invoked', 'cancel');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.addActor(this._entry);
|
||||
|
||||
this.addButton('ok', _("OK"));
|
||||
this.addButton('cancel', _("Cancel"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'ok') {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, parseInt(this._entry.text));
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
|
||||
} else {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, -1);
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, null);
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}
|
||||
}
|
202
js/ui/status/keyboard.js
Normal file
@ -0,0 +1,202 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gkbd = imports.gi.Gkbd;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
function LayoutMenuItem() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
|
||||
LayoutMenuItem.prototype = {
|
||||
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
|
||||
|
||||
_init: function(config, id, indicator, long_name) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||
|
||||
this._config = config;
|
||||
this._id = id;
|
||||
this.label = new St.Label({ text: long_name });
|
||||
this.indicator = indicator;
|
||||
this.addActor(this.label);
|
||||
this.addActor(this.indicator);
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype.activate.call(this);
|
||||
this._config.lock_group(this._id);
|
||||
}
|
||||
};
|
||||
|
||||
function XKBIndicator() {
|
||||
this._init.call(this);
|
||||
}
|
||||
|
||||
XKBIndicator.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
|
||||
this._container = new Shell.GenericContainer();
|
||||
this._container.connect('get-preferred-width', Lang.bind(this, this._get_preferred_width));
|
||||
this._container.connect('get-preferred-height', Lang.bind(this, this._get_preferred_height));
|
||||
this._container.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.set_child(this._container);
|
||||
|
||||
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
|
||||
this._container.add_actor(this._iconActor);
|
||||
this._labelActors = [ ];
|
||||
this._layoutItems = [ ];
|
||||
|
||||
this._showFlags = false;
|
||||
this._config = Gkbd.Configuration.get();
|
||||
this._config.connect('changed', Lang.bind(this, this._sync_config));
|
||||
this._config.connect('group-changed', Lang.bind(this, this._sync_group));
|
||||
this._config.start_listen();
|
||||
|
||||
this._sync_config();
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
|
||||
Util.spawn(['gkbd-keyboard-display', '-g', this._config.get_current_group() + 1]);
|
||||
}));
|
||||
this.menu.addAction(_("Localization Settings"), function() {
|
||||
Util.spawn(['gnome-control-center', 'region']);
|
||||
});
|
||||
},
|
||||
|
||||
_sync_config: function() {
|
||||
this._showFlags = this._config.if_flags_shown();
|
||||
if (this._showFlags) {
|
||||
this._container.set_skip_paint(this._iconActor, false);
|
||||
} else {
|
||||
this._container.set_skip_paint(this._iconActor, true);
|
||||
}
|
||||
|
||||
let groups = this._config.get_group_names();
|
||||
if (groups.length > 1) {
|
||||
this.actor.show();
|
||||
} else {
|
||||
this.menu.close();
|
||||
this.actor.hide();
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._layoutItems.length; i++)
|
||||
this._layoutItems[i].destroy();
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].destroy();
|
||||
|
||||
let short_names = this._config.get_short_group_names();
|
||||
|
||||
this._selectedLayout = null;
|
||||
this._layoutItems = [ ];
|
||||
this._selectedLabel = null;
|
||||
this._labelActors = [ ];
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let icon_name = this._config.get_group_name(i);
|
||||
let actor;
|
||||
if (this._showFlags)
|
||||
actor = new St.Icon({ icon_name: icon_name, icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' });
|
||||
else
|
||||
actor = new St.Label({ text: short_names[i] });
|
||||
let item = new LayoutMenuItem(this._config, i, actor, groups[i]);
|
||||
item._short_group_name = short_names[i];
|
||||
item._icon_name = icon_name;
|
||||
this._layoutItems.push(item);
|
||||
this.menu.addMenuItem(item, i);
|
||||
|
||||
let shortLabel = new St.Label({ text: short_names[i] });
|
||||
this._labelActors.push(shortLabel);
|
||||
this._container.add_actor(shortLabel);
|
||||
this._container.set_skip_paint(shortLabel, true);
|
||||
}
|
||||
|
||||
this._sync_group();
|
||||
},
|
||||
|
||||
_sync_group: function() {
|
||||
let selected = this._config.get_current_group();
|
||||
|
||||
if (this._selectedLayout) {
|
||||
this._selectedLayout.setShowDot(false);
|
||||
this._selectedLayout = null;
|
||||
}
|
||||
|
||||
if (this._selectedLabel) {
|
||||
this._container.set_skip_paint(this._selectedLabel, true);
|
||||
this._selectedLabel = null;
|
||||
}
|
||||
|
||||
let item = this._layoutItems[selected];
|
||||
item.setShowDot(true);
|
||||
|
||||
this._iconActor.icon_name = item._icon_name;
|
||||
this._selectedLabel = this._labelActors[selected];
|
||||
this._container.set_skip_paint(this._selectedLabel, this._showFlags);
|
||||
|
||||
this._selectedLayout = item;
|
||||
},
|
||||
|
||||
_get_preferred_width: function(container, for_height, alloc) {
|
||||
/* Here, and in _get_preferred_height, we need to query for the
|
||||
height of all children, but we ignore the results for those
|
||||
we don't actually display. */
|
||||
let max_min_width = 0, max_natural_width = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
|
||||
if (!this._showFlags) {
|
||||
max_min_width = Math.max(max_min_width, min_width);
|
||||
max_natural_width = Math.max(max_natural_width, natural_width);
|
||||
}
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_width;
|
||||
alloc.natural_size = max_natural_width;
|
||||
},
|
||||
|
||||
_get_preferred_height: function(container, for_width, alloc) {
|
||||
let max_min_height = 0, max_natural_height = 0;
|
||||
if (this._showFlags)
|
||||
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
|
||||
|
||||
for (let i = 0; i < this._labelActors.length; i++) {
|
||||
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
|
||||
if (!this._showFlags) {
|
||||
max_min_height = Math.max(max_min_height, min_height);
|
||||
max_natural_height = Math.max(max_natural_height, natural_height);
|
||||
}
|
||||
}
|
||||
|
||||
alloc.min_size = max_min_height;
|
||||
alloc.natural_size = max_natural_height;
|
||||
},
|
||||
|
||||
_allocate: function(container, box, flags) {
|
||||
// translate box to (0, 0)
|
||||
box.x2 -= box.x1;
|
||||
box.x1 = 0;
|
||||
box.y2 -= box.y1;
|
||||
box.y1 = 0;
|
||||
|
||||
this._iconActor.allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
for (let i = 0; i < this._labelActors.length; i++)
|
||||
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
|
||||
}
|
||||
};
|
@ -1,7 +1,6 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const DBus = imports.dbus;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -10,6 +9,7 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -26,7 +26,10 @@ const UPDeviceType = {
|
||||
MOUSE: 5,
|
||||
KEYBOARD: 6,
|
||||
PDA: 7,
|
||||
PHONE: 8
|
||||
PHONE: 8,
|
||||
MEDIA_PLAYER: 9,
|
||||
TABLET: 10,
|
||||
COMPUTER: 11
|
||||
};
|
||||
|
||||
const UPDeviceState = {
|
||||
@ -68,17 +71,19 @@ Indicator.prototype = {
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('');
|
||||
this._primaryPercentage = new St.Label();
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(this._deviceSep);
|
||||
this._otherDevicePosition = 2;
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("What's using power..."),function() {
|
||||
GLib.spawn_command_line_async('gnome-power-statistics --device wakeups');
|
||||
});
|
||||
|
||||
this.menu.addAction(_("Power Settings"),function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center power');
|
||||
Util.spawnDesktop('gnome-power-panel');
|
||||
});
|
||||
|
||||
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
||||
@ -95,10 +100,26 @@ Indicator.prototype = {
|
||||
this._deviceSep.actor.hide();
|
||||
return;
|
||||
}
|
||||
let [device_id, device_type, summary, percentage, state, time] = device;
|
||||
let [device_id, device_type, icon, percentage, state, seconds] = device;
|
||||
if (device_type == UPDeviceType.BATTERY) {
|
||||
this._hasPrimary = true;
|
||||
this._batteryItem.label.text = summary;
|
||||
let time = Math.round(seconds / 60);
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
let timestring;
|
||||
if (time > 60) {
|
||||
if (minutes == 0) {
|
||||
timestring = Gettext.ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
|
||||
} else {
|
||||
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
|
||||
let template = _("%d %s %d %s remaining");
|
||||
|
||||
timestring = template.format (hours, Gettext.ngettext("hour", "hours", hours), minutes, Gettext.ngettext("minute", "minutes", minutes));
|
||||
}
|
||||
} else
|
||||
timestring = Gettext.ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
|
||||
this._batteryItem.label.text = timestring;
|
||||
this._primaryPercentage.text = Math.round(percentage) + '%';
|
||||
this._batteryItem.actor.show();
|
||||
if (this._deviceItems.length > 0)
|
||||
this._deviceSep.actor.show();
|
||||
@ -109,6 +130,17 @@ Indicator.prototype = {
|
||||
}
|
||||
|
||||
this._primaryDeviceId = device_id;
|
||||
if (this._primaryDeviceId) {
|
||||
this._batteryItem.actor.reactive = true;
|
||||
this._batteryItem.actor.can_focus = true;
|
||||
this._batteryItem.connect('activate', function(item) {
|
||||
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
||||
});
|
||||
} else {
|
||||
// virtual device
|
||||
this._batteryItem.actor.reactive = false;
|
||||
this._batteryItem.actor.can_focus = false;
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
@ -131,8 +163,7 @@ Indicator.prototype = {
|
||||
|
||||
let item = new DeviceItem (devices[i]);
|
||||
item.connect('activate', function() {
|
||||
let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
|
||||
p.run();
|
||||
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
||||
});
|
||||
this._deviceItems.push(item);
|
||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||
@ -164,8 +195,8 @@ Indicator.prototype = {
|
||||
|
||||
_checkError: function(error) {
|
||||
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
|
||||
GLib.spawn_command_line_sync('pkill -f "^gnome-power-manager$"');
|
||||
GLib.spawn_command_line_async('gnome-power-manager');
|
||||
Util.killall('gnome-power-manager');
|
||||
Util.spawn(['gnome-power-manager']);
|
||||
this._restarted = true;
|
||||
}
|
||||
}
|
||||
@ -181,24 +212,12 @@ DeviceItem.prototype = {
|
||||
_init: function(device) {
|
||||
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
|
||||
|
||||
let [device_id, device_type, summary, percentage, state, time] = device;
|
||||
let [device_id, device_type, icon, percentage, state, time] = device;
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
|
||||
this._label = new St.Label({ text: summary });
|
||||
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
|
||||
|
||||
let icon;
|
||||
switch (state) {
|
||||
case UPDeviceState.FULLY_CHARGED:
|
||||
icon = 'battery-full-charged';
|
||||
break;
|
||||
case UPDeviceState.UNKNOWN:
|
||||
icon = 'battery-missing';
|
||||
break;
|
||||
default:
|
||||
icon = this._percentageToIcon(percentage) + (state == UPDeviceState.CHARGING ? '-charging' : '');
|
||||
}
|
||||
|
||||
this._icon = new St.Icon({ icon_name: icon,
|
||||
this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon),
|
||||
icon_type: St.IconType.SYMBOLIC,
|
||||
style_class: 'popup-menu-icon' });
|
||||
|
||||
@ -206,21 +225,36 @@ DeviceItem.prototype = {
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentBin = new St.Bin({ x_align: St.Align.END });
|
||||
let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
|
||||
percentBin.child = percentLabel;
|
||||
this.addActor(percentBin);
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
},
|
||||
|
||||
_percentageToIcon: function(p) {
|
||||
if (p > 60)
|
||||
return 'battery-full';
|
||||
if (p > 30)
|
||||
return 'battery-good';
|
||||
if (p > 10)
|
||||
return 'battery-low';
|
||||
if (p > 0)
|
||||
return 'battery-caution';
|
||||
return 'battery-empty';
|
||||
_deviceTypeToString: function(type) {
|
||||
switch (type) {
|
||||
case UPDeviceType.AC_POWER:
|
||||
return _("AC adapter");
|
||||
case UPDeviceType.BATTERY:
|
||||
return _("Laptop battery");
|
||||
case UPDeviceType.UPS:
|
||||
return _("UPS");
|
||||
case UPDeviceType.MONITOR:
|
||||
return _("Monitor");
|
||||
case UPDeviceType.MOUSE:
|
||||
return _("Mouse");
|
||||
case UPDeviceType.KEYBOARD:
|
||||
return _("Keyboard");
|
||||
case UPDeviceType.PDA:
|
||||
return _("PDA");
|
||||
case UPDeviceType.PHONE:
|
||||
return _("Cell phone");
|
||||
case UPDeviceType.MEDIA_PLAYER:
|
||||
return _("Media player");
|
||||
case UPDeviceType.TABLET:
|
||||
return _("Tablet");
|
||||
case UPDeviceType.COMPUTER:
|
||||
return _("Computer");
|
||||
default:
|
||||
return _("Unknown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -18,6 +19,8 @@ const _ = Gettext.gettext;
|
||||
const VOLUME_MAX = 65536.0; /* PA_VOLUME_NORM */
|
||||
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
|
||||
|
||||
const VOLUME_NOTIFY_ID = 1;
|
||||
|
||||
function Indicator() {
|
||||
this._init.apply(this, arguments);
|
||||
}
|
||||
@ -38,12 +41,11 @@ Indicator.prototype = {
|
||||
this._output = null;
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
this._outputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Volume: Muted"), false);
|
||||
this._outputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_output'));
|
||||
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
|
||||
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
|
||||
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.menu.addMenuItem(this._outputSwitch);
|
||||
this.menu.addMenuItem(this._outputTitle);
|
||||
this.menu.addMenuItem(this._outputSlider);
|
||||
|
||||
this._separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
@ -52,36 +54,51 @@ Indicator.prototype = {
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
this._inputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Microphone: Muted"), false);
|
||||
this._inputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_input'));
|
||||
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
|
||||
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.menu.addMenuItem(this._inputSwitch);
|
||||
this.menu.addMenuItem(this._inputTitle);
|
||||
this.menu.addMenuItem(this._inputSlider);
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Sound Settings"), function() {
|
||||
let p = new Shell.Process({ args: ['gnome-control-center', 'sound'] });
|
||||
p.run();
|
||||
Util.spawnDesktop('gnome-sound-panel');
|
||||
});
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this._control.open();
|
||||
},
|
||||
|
||||
_getMaxVolume: function(property) {
|
||||
if (this[property].get_can_decibel())
|
||||
return (VOLUME_MAX * 1.5);
|
||||
else
|
||||
return VOLUME_MAX;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let currentVolume = this._output.volume;
|
||||
let maxVolume = this._getMaxVolume('_output');
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
this._output.volume = Math.max(0, currentVolume - VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
|
||||
let prev_muted = this._output.is_muted;
|
||||
this._output.volume = Math.max(0, currentVolume - maxVolume * VOLUME_ADJUSTMENT_STEP);
|
||||
if (this._output.volume < 1) {
|
||||
this._output.volume = 0;
|
||||
if (!prev_muted)
|
||||
this._output.change_is_muted(true);
|
||||
}
|
||||
this._output.push_volume();
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._output.volume = Math.min(VOLUME_MAX, currentVolume + VOLUME_MAX * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.volume = Math.min(maxVolume, currentVolume + maxVolume * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.change_is_muted(false);
|
||||
this._output.push_volume();
|
||||
}
|
||||
|
||||
this._notifyVolumeChange();
|
||||
},
|
||||
|
||||
_onControlReady: function() {
|
||||
@ -103,8 +120,7 @@ Indicator.prototype = {
|
||||
this._mutedChanged (null, null, '_output');
|
||||
this._volumeChanged (null, null, '_output');
|
||||
} else {
|
||||
this._outputSwitch.label.text = _("Volume: Muted");
|
||||
this._outputSwitch.setToggleState(false);
|
||||
this._outputSlider.setValue(0);
|
||||
this.setIcon('audio-volume-muted-symbolic');
|
||||
}
|
||||
},
|
||||
@ -124,7 +140,7 @@ Indicator.prototype = {
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputSwitch.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
},
|
||||
@ -147,23 +163,24 @@ Indicator.prototype = {
|
||||
}
|
||||
if (showInput) {
|
||||
this._separator.actor.show();
|
||||
this._inputSwitch.actor.show();
|
||||
this._inputTitle.actor.show();
|
||||
this._inputSlider.actor.show();
|
||||
} else {
|
||||
this._separator.actor.hide();
|
||||
this._inputSwitch.actor.hide();
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
let maxVolume = this._getMaxVolume('_output');
|
||||
if (volume <= 0) {
|
||||
return 'audio-volume-muted';
|
||||
} else {
|
||||
let v = volume / VOLUME_MAX;
|
||||
if (v < 0.33)
|
||||
let n = Math.floor(3 * volume / maxVolume) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low';
|
||||
if (v > 0.8)
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high';
|
||||
return 'audio-volume-medium';
|
||||
}
|
||||
@ -174,28 +191,30 @@ Indicator.prototype = {
|
||||
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
this[property].volume = value * VOLUME_MAX;
|
||||
let volume = value * this._getMaxVolume(property);
|
||||
let prev_muted = this[property].is_muted;
|
||||
if (volume < 1) {
|
||||
this[property].volume = 0;
|
||||
if (!prev_muted)
|
||||
this[property].change_is_muted(true);
|
||||
} else {
|
||||
this[property].volume = volume;
|
||||
if (prev_muted)
|
||||
this[property].change_is_muted(false);
|
||||
}
|
||||
this[property].push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.play_theme_sound('audio-volume-change');
|
||||
},
|
||||
|
||||
_switchToggled: function(switchItem, state, property) {
|
||||
if (this[property] == null) {
|
||||
log ('Volume mute switch toggled for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
this[property].change_is_muted(!state);
|
||||
this._notifyVolumeChange();
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
},
|
||||
|
||||
_mutedChanged: function(object, param_spec, property) {
|
||||
let muted = this[property].is_muted;
|
||||
let toggleSwitch = this[property+'Switch'];
|
||||
toggleSwitch.setToggleState(!muted);
|
||||
this._updateLabel(property);
|
||||
let slider = this[property+'Slider'];
|
||||
let maxVolume = this._getMaxVolume(property);
|
||||
slider.setValue(muted ? 0 : (this[property].volume / maxVolume));
|
||||
if (property == '_output') {
|
||||
if (muted)
|
||||
this.setIcon('audio-volume-muted');
|
||||
@ -205,18 +224,9 @@ Indicator.prototype = {
|
||||
},
|
||||
|
||||
_volumeChanged: function(object, param_spec, property) {
|
||||
this[property+'Slider'].setValue(this[property].volume / VOLUME_MAX);
|
||||
this._updateLabel(property);
|
||||
let maxVolume = this._getMaxVolume(property);
|
||||
this[property+'Slider'].setValue(this[property].volume / maxVolume);
|
||||
if (property == '_output' && !this._output.is_muted)
|
||||
this.setIcon(this._volumeToIcon(this._output.volume));
|
||||
},
|
||||
|
||||
_updateLabel: function(property) {
|
||||
let label;
|
||||
if (this[property].is_muted)
|
||||
label = (property == '_output' ? _("Volume: Muted") : _("Microphone: Muted"));
|
||||
else
|
||||
label = (property == '_output' ? _("Volume: %3.0f%%") : _("Microphone: %3.0f%%")).format(this[property].volume / VOLUME_MAX * 100);
|
||||
this[property+'Switch'].label.text = label;
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ const Signals = imports.signals;
|
||||
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'bluetooth-applet': 'bluetooth',
|
||||
@ -15,7 +16,10 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'nm-applet': 'network',
|
||||
'gnome-power-manager': 'battery',
|
||||
'keyboard': 'keyboard',
|
||||
'gnome-settings-daemon': 'display'
|
||||
'a11y-keyboard': 'a11y',
|
||||
'kbd-scrolllock': 'keyboard',
|
||||
'kbd-numlock': 'keyboard',
|
||||
'kbd-capslock': 'keyboard'
|
||||
};
|
||||
|
||||
function StatusIconDispatcher() {
|
||||
@ -27,14 +31,16 @@ StatusIconDispatcher.prototype = {
|
||||
this._traymanager = new Shell.TrayManager();
|
||||
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||
this._traymanager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||
this._traymanager.manage_stage(global.stage);
|
||||
|
||||
// Yet-another-Ubuntu-workaround - we have to kill their
|
||||
// app-indicators, so that applications fall back to normal
|
||||
// status icons
|
||||
// http://bugzilla.gnome.org/show_bug.cgi=id=621382
|
||||
let p = new Shell.Process({ args: ['pkill', '-f', '^([^ ]*/)?indicator-application-service$']});
|
||||
p.run();
|
||||
Util.killall('indicator-application-service');
|
||||
},
|
||||
|
||||
start: function(themeWidget) {
|
||||
this._traymanager.manage_stage(global.stage, themeWidget);
|
||||
},
|
||||
|
||||
_onTrayIconAdded: function(o, icon) {
|
||||
|
@ -5,6 +5,7 @@ const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
@ -12,6 +13,7 @@ const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
@ -26,17 +28,19 @@ StatusMenuButton.prototype = {
|
||||
__proto__: PanelMenu.Button.prototype,
|
||||
|
||||
_init: function() {
|
||||
PanelMenu.Button.prototype._init.call(this, St.Align.START);
|
||||
PanelMenu.Button.prototype._init.call(this, 0.0);
|
||||
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._gdm = Gdm.UserManager.ref_default();
|
||||
this._gdm.queue_load()
|
||||
this._gdm.queue_load();
|
||||
|
||||
this._user = this._gdm.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
this._presenceItems = {};
|
||||
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._iconBox = new St.Bin();
|
||||
@ -60,6 +64,8 @@ StatusMenuButton.prototype = {
|
||||
this._gdm.connect('notify::is-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));
|
||||
|
||||
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
@ -81,6 +87,21 @@ StatusMenuButton.prototype = {
|
||||
this._loginScreenItem.actor.hide();
|
||||
},
|
||||
|
||||
_updateSuspendOrPowerOff: function() {
|
||||
this._haveSuspend = this._upClient.get_can_suspend();
|
||||
|
||||
if (!this._suspendOrPowerOffItem)
|
||||
return;
|
||||
|
||||
// If we can't suspend show Power Off... instead
|
||||
// and disable the alt key
|
||||
if (!this._haveSuspend) {
|
||||
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
|
||||
} else {
|
||||
this._suspendOrPowerOffItem.updateText(_("Suspend"), ("Power Off..."));
|
||||
}
|
||||
},
|
||||
|
||||
_updatePresenceIcon: function(presence, status) {
|
||||
if (status == GnomeSession.PresenceStatus.AVAILABLE)
|
||||
this._iconBox.child = this._availableIcon;
|
||||
@ -98,12 +119,12 @@ StatusMenuButton.prototype = {
|
||||
_createSubMenu: function() {
|
||||
let item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available', true);
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
|
||||
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy', true);
|
||||
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
|
||||
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
|
||||
this.menu.addMenuItem(item);
|
||||
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
|
||||
@ -111,11 +132,11 @@ StatusMenuButton.prototype = {
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("My Account..."));
|
||||
item = new PopupMenu.PopupMenuItem(_("My Account"));
|
||||
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings..."));
|
||||
item = new PopupMenu.PopupMenuItem(_("System Settings"));
|
||||
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
@ -138,13 +159,12 @@ StatusMenuButton.prototype = {
|
||||
item = new PopupMenu.PopupSeparatorMenuItem();
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Suspend"));
|
||||
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
this.menu.addMenuItem(item);
|
||||
|
||||
item = new PopupMenu.PopupMenuItem(_("Shut Down..."));
|
||||
item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
|
||||
_("Power Off..."));
|
||||
this.menu.addMenuItem(item);
|
||||
this._suspendOrPowerOffItem = item;
|
||||
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
|
||||
this._updateSuspendOrPowerOff();
|
||||
},
|
||||
|
||||
_setPresenceStatus: function(item, event, status) {
|
||||
@ -153,17 +173,17 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-about-me']);
|
||||
Util.spawnDesktop('gnome-user-accounts-panel');
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-control-center']);
|
||||
Util.spawnDesktop('gnome-control-center');
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-screensaver-command', '--lock']);
|
||||
Util.spawn(['gnome-screensaver-command', '--lock']);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
@ -174,19 +194,17 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-session-save', '--logout-dialog']);
|
||||
Util.spawn(['gnome-session-save', '--logout-dialog']);
|
||||
},
|
||||
|
||||
_onShutDownActivate: function() {
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
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();
|
||||
if (this._haveSuspend &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._upClient.suspend_sync(null);
|
||||
} else {
|
||||
Util.spawn(['gnome-session-save', '--shutdown-dialog']);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -3,45 +3,32 @@
|
||||
const DBus = imports.dbus;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const Telepathy = imports.misc.telepathy;
|
||||
|
||||
let contactManager;
|
||||
let channelDispatcher;
|
||||
|
||||
// See Notification.appendMessage
|
||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||
const SCROLLBACK_RECENT_LENGTH = 20;
|
||||
const SCROLLBACK_IDLE_LENGTH = 5;
|
||||
|
||||
// A 'Qualified_Property_Value_Map' that represents a single-user
|
||||
// text-based chat.
|
||||
let singleUserTextChannel = {};
|
||||
singleUserTextChannel[Telepathy.CHANNEL_NAME + '.ChannelType'] = Telepathy.CHANNEL_TEXT_NAME;
|
||||
singleUserTextChannel[Telepathy.CHANNEL_NAME + '.TargetHandleType'] = Telepathy.HandleType.CONTACT;
|
||||
|
||||
// Some protocols only support 'multi-user' chats, and single-user
|
||||
// chats are just treated as multi-user chats with only one other
|
||||
// participant. Telepathy uses HandleType.NONE for all chats in these
|
||||
// protocols; there's no good way for us to tell if the channel is
|
||||
// single- or multi-user.
|
||||
let oneOrMoreUserTextChannel = {};
|
||||
oneOrMoreUserTextChannel[Telepathy.CHANNEL_NAME + '.ChannelType'] = Telepathy.CHANNEL_TEXT_NAME;
|
||||
oneOrMoreUserTextChannel[Telepathy.CHANNEL_NAME + '.TargetHandleType'] = Telepathy.HandleType.NONE;
|
||||
|
||||
// The (non-chat) channel indicating the users whose presence
|
||||
// information we subscribe to
|
||||
let subscribedContactsChannel = {};
|
||||
subscribedContactsChannel[Telepathy.CHANNEL_NAME + '.ChannelType'] = Telepathy.CHANNEL_CONTACT_LIST_NAME;
|
||||
subscribedContactsChannel[Telepathy.CHANNEL_NAME + '.TargetHandleType'] = Telepathy.HandleType.LIST;
|
||||
subscribedContactsChannel[Telepathy.CHANNEL_NAME + '.TargetID'] = 'subscribe';
|
||||
const NotificationDirection = {
|
||||
SENT: 'chat-sent',
|
||||
RECEIVED: 'chat-received'
|
||||
};
|
||||
|
||||
let contactFeatures = [Tp.ContactFeature.ALIAS,
|
||||
Tp.ContactFeature.AVATAR_DATA,
|
||||
Tp.ContactFeature.PRESENCE];
|
||||
|
||||
// This is GNOME Shell's implementation of the Telepathy 'Client'
|
||||
// interface. Specifically, the shell is a Telepathy 'Observer', which
|
||||
@ -54,508 +41,215 @@ function Client() {
|
||||
|
||||
Client.prototype = {
|
||||
_init : function() {
|
||||
let name = Telepathy.CLIENT_NAME + '.GnomeShell';
|
||||
DBus.session.exportObject(Telepathy.nameToPath(name), this);
|
||||
DBus.session.acquire_name(name, DBus.SINGLE_INSTANCE,
|
||||
function (name) { /* FIXME: acquired */ },
|
||||
function (name) { /* FIXME: lost */ });
|
||||
|
||||
this._accounts = {};
|
||||
// channel path -> Source
|
||||
this._sources = {};
|
||||
|
||||
contactManager = new ContactManager();
|
||||
contactManager.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
||||
// Set up a SimpleObserver, which will call _observeChannels whenever a
|
||||
// channel matching its filters is detected.
|
||||
// The second argument, recover, means _observeChannels will be run
|
||||
// for any existing channel as well.
|
||||
let dbus = Tp.DBusDaemon.dup();
|
||||
this._observer = Tp.SimpleObserver.new(dbus, true, 'GnomeShell', true,
|
||||
Lang.bind(this, this._observeChannels));
|
||||
|
||||
channelDispatcher = new Telepathy.ChannelDispatcher(DBus.session,
|
||||
Telepathy.CHANNEL_DISPATCHER_NAME,
|
||||
Telepathy.nameToPath(Telepathy.CHANNEL_DISPATCHER_NAME));
|
||||
// We only care about single-user text-based chats
|
||||
let props = {};
|
||||
props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
|
||||
props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.HandleType.CONTACT;
|
||||
this._observer.add_observer_filter(props);
|
||||
|
||||
// Acquire existing connections. (Needed to make things work
|
||||
// through a restart.)
|
||||
let accountManager = new Telepathy.AccountManager(DBus.session,
|
||||
Telepathy.ACCOUNT_MANAGER_NAME,
|
||||
Telepathy.nameToPath(Telepathy.ACCOUNT_MANAGER_NAME));
|
||||
accountManager.GetRemote('ValidAccounts', Lang.bind(this,
|
||||
function (accounts, err) {
|
||||
if (!accounts)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < accounts.length; i++)
|
||||
this._gotAccount(accounts[i]);
|
||||
}));
|
||||
accountManager.connect('AccountValidityChanged', Lang.bind(this, this._accountValidityChanged));
|
||||
try {
|
||||
this._observer.register();
|
||||
} catch (e) {
|
||||
throw new Error('Couldn\'t register SimpleObserver. Error: \n' + e);
|
||||
}
|
||||
},
|
||||
|
||||
_accountValidityChanged: function(accountManager, accountPath, valid) {
|
||||
if (!valid) {
|
||||
delete this._accounts[accountPath];
|
||||
// We don't need to clean up connections, sources, etc; they'll
|
||||
// get Closed and cleaned up independently.
|
||||
} else
|
||||
this._gotAccount(accountPath);
|
||||
},
|
||||
_observeChannels: function(observer, account, conn, channels,
|
||||
dispatchOp, requests, context) {
|
||||
let connPath = conn.get_object_path();
|
||||
let len = channels.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let channel = channels[i];
|
||||
let [targetHandle, targetHandleType] = channel.get_handle();
|
||||
|
||||
_gotAccount: function(accountPath) {
|
||||
let account = new Telepathy.Account(DBus.session,
|
||||
Telepathy.ACCOUNT_MANAGER_NAME,
|
||||
accountPath);
|
||||
this._accounts[accountPath] = account;
|
||||
account.GetRemote('Connection', Lang.bind(this,
|
||||
function (connPath, err) {
|
||||
if (!connPath || connPath == '/')
|
||||
return;
|
||||
/* Only observe contact text channels */
|
||||
if ((!(channel instanceof Tp.TextChannel)) ||
|
||||
targetHandleType != Tp.HandleType.CONTACT)
|
||||
continue;
|
||||
|
||||
let connReq = new Telepathy.ConnectionRequests(DBus.session,
|
||||
Telepathy.pathToName(connPath),
|
||||
connPath);
|
||||
connReq.GetRemote('Channels', Lang.bind(this,
|
||||
function(channels, err) {
|
||||
if (!channels)
|
||||
/* Request a TpContact */
|
||||
Shell.get_tp_contacts(conn, 1, [targetHandle],
|
||||
contactFeatures.length, contactFeatures,
|
||||
Lang.bind(this, function (connection, contacts, failed) {
|
||||
if (contacts.length < 1)
|
||||
return;
|
||||
|
||||
this._addChannels(accountPath, connPath, channels);
|
||||
}));
|
||||
|
||||
contactManager.addConnection(connPath);
|
||||
}));
|
||||
},
|
||||
|
||||
get Interfaces() {
|
||||
return [ Telepathy.CLIENT_OBSERVER_NAME ];
|
||||
},
|
||||
|
||||
get ObserverChannelFilter() {
|
||||
return [ singleUserTextChannel, oneOrMoreUserTextChannel ];
|
||||
},
|
||||
|
||||
ObserveChannels: function(accountPath, connPath, channels,
|
||||
dispatchOperation, requestsSatisfied,
|
||||
observerInfo) {
|
||||
this._addChannels(accountPath, connPath, channels);
|
||||
},
|
||||
|
||||
_addChannels: function(accountPath, connPath, channelDetailsList) {
|
||||
for (let i = 0; i < channelDetailsList.length; i++) {
|
||||
let [channelPath, props] = channelDetailsList[i];
|
||||
|
||||
// If this is being called from the startup code then it
|
||||
// won't have passed through our filters, so we need to
|
||||
// check the channel/targetHandle type ourselves.
|
||||
|
||||
let channelType = props[Telepathy.CHANNEL_NAME + '.ChannelType'];
|
||||
if (channelType != Telepathy.CHANNEL_TEXT_NAME)
|
||||
continue;
|
||||
|
||||
let targetHandleType = props[Telepathy.CHANNEL_NAME + '.TargetHandleType'];
|
||||
if (targetHandleType != Telepathy.HandleType.CONTACT &&
|
||||
targetHandleType != Telepathy.HandleType.NONE)
|
||||
continue;
|
||||
|
||||
let targetHandle = props[Telepathy.CHANNEL_NAME + '.TargetHandle'];
|
||||
let targetId = props[Telepathy.CHANNEL_NAME + '.TargetID'];
|
||||
|
||||
if (this._sources[connPath + ':' + targetHandle])
|
||||
continue;
|
||||
|
||||
let source = new Source(accountPath, connPath, channelPath,
|
||||
targetHandle, targetHandleType, targetId);
|
||||
this._sources[connPath + ':' + targetHandle] = source;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[connPath + ':' + targetHandle];
|
||||
}));
|
||||
/* We got the TpContact */
|
||||
this._createSource(account, conn, channel, contacts[0]);
|
||||
}), null);
|
||||
}
|
||||
|
||||
// Allow dbus method to return
|
||||
context.accept();
|
||||
},
|
||||
|
||||
_presenceChanged: function(contactManager, connPath, handle,
|
||||
type, message) {
|
||||
let source = this._sources[connPath + ':' + handle];
|
||||
if (!source)
|
||||
_createSource: function(account, conn, channel, contact) {
|
||||
if (this._sources[channel.get_object_path()])
|
||||
return;
|
||||
|
||||
source.setPresence(type, message);
|
||||
let source = new Source(account, conn, channel, contact);
|
||||
|
||||
this._sources[channel.get_object_path()] = source;
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
delete this._sources[channel.get_object_path()];
|
||||
}));
|
||||
}
|
||||
};
|
||||
DBus.conformExport(Client.prototype, Telepathy.ClientIface);
|
||||
DBus.conformExport(Client.prototype, Telepathy.ClientObserverIface);
|
||||
|
||||
|
||||
function ContactManager() {
|
||||
this._init();
|
||||
};
|
||||
|
||||
ContactManager.prototype = {
|
||||
_init: function() {
|
||||
this._connections = {};
|
||||
// Note that if we changed this to '/telepathy/avatars' then
|
||||
// we would share cache files with empathy. But since this is
|
||||
// not documented/guaranteed, it seems a little sketchy
|
||||
this._cacheDir = GLib.get_user_cache_dir() + '/gnome-shell/avatars';
|
||||
},
|
||||
|
||||
addConnection: function(connPath) {
|
||||
let info = this._connections[connPath];
|
||||
if (info)
|
||||
return info;
|
||||
|
||||
info = {};
|
||||
|
||||
// Figure out the cache subdirectory for this connection by
|
||||
// parsing the connection manager name (eg, 'gabble') and
|
||||
// protocol name (eg, 'jabber') from the Connection's path.
|
||||
// Telepathy requires the D-Bus path for a connection to have
|
||||
// a specific form, and explicitly says that clients are
|
||||
// allowed to parse it.
|
||||
let match = connPath.match(/\/org\/freedesktop\/Telepathy\/Connection\/([^\/]*\/[^\/]*)\/.*/);
|
||||
if (!match)
|
||||
throw new Error('Could not parse connection path ' + connPath);
|
||||
|
||||
info.cacheDir = this._cacheDir + '/' + match[1];
|
||||
GLib.mkdir_with_parents(info.cacheDir, 0x1c0); // 0x1c0 = octal 0700
|
||||
|
||||
// info.names[handle] is @handle's real name
|
||||
// info.tokens[handle] is the token for @handle's avatar
|
||||
info.names = {};
|
||||
info.tokens = {};
|
||||
|
||||
// info.icons[handle] is an array of the icon actors currently
|
||||
// being displayed for @handle. These will be updated
|
||||
// automatically if @handle's avatar changes.
|
||||
info.icons = {};
|
||||
|
||||
let connName = Telepathy.pathToName(connPath);
|
||||
|
||||
info.connectionAvatars = new Telepathy.ConnectionAvatars(DBus.session, connName, connPath);
|
||||
info.updatedId = info.connectionAvatars.connect(
|
||||
'AvatarUpdated', Lang.bind(this, this._avatarUpdated));
|
||||
info.retrievedId = info.connectionAvatars.connect(
|
||||
'AvatarRetrieved', Lang.bind(this, this._avatarRetrieved));
|
||||
|
||||
info.connectionContacts = new Telepathy.ConnectionContacts(DBus.session, connName, connPath);
|
||||
|
||||
info.connectionPresence = new Telepathy.ConnectionSimplePresence(DBus.session, connName, connPath);
|
||||
info.presenceChangedId = info.connectionPresence.connect(
|
||||
'PresencesChanged', Lang.bind(this, this._presencesChanged));
|
||||
|
||||
let conn = new Telepathy.Connection(DBus.session, connName, connPath);
|
||||
info.statusChangedId = conn.connect('StatusChanged', Lang.bind(this,
|
||||
function (status, reason) {
|
||||
if (status == Telepathy.ConnectionStatus.DISCONNECTED)
|
||||
this._removeConnection(conn);
|
||||
}));
|
||||
|
||||
let connReq = new Telepathy.ConnectionRequests(DBus.session,
|
||||
connName, connPath);
|
||||
connReq.EnsureChannelRemote(subscribedContactsChannel, Lang.bind(this,
|
||||
function (result, err) {
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
let [mine, channelPath, props] = result;
|
||||
this._gotContactsChannel(connPath, channelPath, props);
|
||||
}));
|
||||
|
||||
this._connections[connPath] = info;
|
||||
return info;
|
||||
},
|
||||
|
||||
_gotContactsChannel: function(connPath, channelPath, props) {
|
||||
let info = this._connections[connPath];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
info.contactsGroup = new Telepathy.ChannelGroup(DBus.session,
|
||||
Telepathy.pathToName(connPath),
|
||||
channelPath);
|
||||
info.contactsListChangedId =
|
||||
info.contactsGroup.connect('MembersChanged', Lang.bind(this, this._contactsListChanged, info));
|
||||
|
||||
info.contactsGroup.GetRemote('Members', Lang.bind(this,
|
||||
function(contacts, err) {
|
||||
if (!contacts)
|
||||
return;
|
||||
|
||||
info.connectionContacts.GetContactAttributesRemote(
|
||||
contacts, [Telepathy.CONNECTION_ALIASING_NAME], false,
|
||||
Lang.bind(this, this._gotContactAttributes, info));
|
||||
}));
|
||||
},
|
||||
|
||||
_contactsListChanged: function(group, message, added, removed,
|
||||
local_pending, remote_pending,
|
||||
actor, reason, info) {
|
||||
for (let i = 0; i < removed.length; i++)
|
||||
delete info.names[removed[i]];
|
||||
|
||||
info.connectionContacts.GetContactAttributesRemote(
|
||||
added, [Telepathy.CONNECTION_ALIASING_NAME], false,
|
||||
Lang.bind(this, this._gotContactAttributes, info));
|
||||
},
|
||||
|
||||
_gotContactAttributes: function(attrs, err, info) {
|
||||
if (!attrs)
|
||||
return;
|
||||
|
||||
for (let handle in attrs)
|
||||
info.names[handle] = attrs[handle][Telepathy.CONNECTION_ALIASING_NAME + '/alias'];
|
||||
},
|
||||
|
||||
_presencesChanged: function(conn, presences, err) {
|
||||
if (!presences)
|
||||
return;
|
||||
|
||||
let info = this._connections[conn.getPath()];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
for (let handle in presences) {
|
||||
let [type, status, message] = presences[handle];
|
||||
this.emit('presence-changed', conn.getPath(), handle, type, message);
|
||||
}
|
||||
},
|
||||
|
||||
_removeConnection: function(conn) {
|
||||
let info = this._connections[conn.getPath()];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
conn.disconnect(info.statusChangedId);
|
||||
info.connectionAvatars.disconnect(info.updatedId);
|
||||
info.connectionAvatars.disconnect(info.retrievedId);
|
||||
info.connectionPresence.disconnect(info.presenceChangedId);
|
||||
info.contactsGroup.disconnect(info.contactsListChangedId);
|
||||
|
||||
delete this._connections[conn.getPath()];
|
||||
},
|
||||
|
||||
_getFileForToken: function(info, token) {
|
||||
return info.cacheDir + '/' + Telepathy.escapeAsIdentifier(token);
|
||||
},
|
||||
|
||||
_setIcon: function(iconBox, info, handle) {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let token = info.tokens[handle];
|
||||
let file;
|
||||
|
||||
if (token) {
|
||||
file = this._getFileForToken(info, token);
|
||||
if (!GLib.file_test(file, GLib.FileTest.EXISTS))
|
||||
file = null;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
let uri = GLib.filename_to_uri(file, null);
|
||||
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
|
||||
} else {
|
||||
iconBox.child = new St.Icon({ icon_name: 'stock_person',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: iconBox._size });
|
||||
}
|
||||
},
|
||||
|
||||
_updateIcons: function(info, handle) {
|
||||
if (!info.icons[handle])
|
||||
return;
|
||||
|
||||
for (let i = 0; i < info.icons[handle].length; i++) {
|
||||
let iconBox = info.icons[handle][i];
|
||||
this._setIcon(iconBox, info, handle);
|
||||
}
|
||||
},
|
||||
|
||||
_avatarUpdated: function(conn, handle, token) {
|
||||
let info = this._connections[conn.getPath()];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if (info.tokens[handle] == token)
|
||||
return;
|
||||
|
||||
info.tokens[handle] = token;
|
||||
if (token != '') {
|
||||
let file = this._getFileForToken(info, token);
|
||||
if (!GLib.file_test(file, GLib.FileTest.EXISTS)) {
|
||||
info.connectionAvatars.RequestAvatarsRemote([handle]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._updateIcons(info, handle);
|
||||
},
|
||||
|
||||
_avatarRetrieved: function(conn, handle, token, avatarData, mimeType) {
|
||||
let info = this._connections[conn.getPath()];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
let file = this._getFileForToken(info, token);
|
||||
let success = false;
|
||||
try {
|
||||
success = GLib.file_set_contents(file, avatarData, avatarData.length);
|
||||
} catch (e) {
|
||||
logError(e, 'Error caching avatar data');
|
||||
}
|
||||
|
||||
if (success)
|
||||
this._updateIcons(info, handle);
|
||||
},
|
||||
|
||||
createAvatar: function(conn, handle, size) {
|
||||
let iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
iconBox._size = size;
|
||||
|
||||
let info = this._connections[conn.getPath()];
|
||||
if (!info)
|
||||
info = this.addConnection(conn.getPath());
|
||||
|
||||
if (!info.icons[handle])
|
||||
info.icons[handle] = [];
|
||||
info.icons[handle].push(iconBox);
|
||||
|
||||
iconBox.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
let i = info.icons[handle].indexOf(iconBox);
|
||||
if (i != -1)
|
||||
info.icons[handle].splice(i, 1);
|
||||
}));
|
||||
|
||||
// If we already have the icon cached and know its token, this
|
||||
// will fill it in. Otherwise it will fill in the default
|
||||
// icon.
|
||||
this._setIcon(iconBox, info, handle);
|
||||
|
||||
// Asynchronously load the real avatar if we don't have it yet.
|
||||
if (info.tokens[handle] == null) {
|
||||
info.connectionAvatars.GetKnownAvatarTokensRemote([handle], Lang.bind(this,
|
||||
function (tokens, err) {
|
||||
let token = tokens && tokens[handle] ? tokens[handle] : '';
|
||||
this._avatarUpdated(conn, handle, token);
|
||||
}));
|
||||
}
|
||||
|
||||
return iconBox;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ContactManager.prototype);
|
||||
|
||||
|
||||
function Source(accountPath, connPath, channelPath, targetHandle, targetHandleType, targetId) {
|
||||
this._init(accountPath, connPath, channelPath, targetHandle, targetHandleType, targetId);
|
||||
function Source(account, conn, channel, contact) {
|
||||
this._init(account, conn, channel, contact);
|
||||
}
|
||||
|
||||
Source.prototype = {
|
||||
__proto__: MessageTray.Source.prototype,
|
||||
|
||||
_init: function(accountPath, connPath, channelPath, targetHandle, targetHandleType, targetId) {
|
||||
MessageTray.Source.prototype._init.call(this, targetId);
|
||||
_init: function(account, conn, channel, contact) {
|
||||
MessageTray.Source.prototype._init.call(this, channel.get_identifier());
|
||||
|
||||
this._accountPath = accountPath;
|
||||
this.isChat = true;
|
||||
|
||||
let connName = Telepathy.pathToName(connPath);
|
||||
this._conn = new Telepathy.Connection(DBus.session, connName, connPath);
|
||||
this._channel = new Telepathy.Channel(DBus.session, connName, channelPath);
|
||||
this._closedId = this._channel.connect('Closed', Lang.bind(this, this._channelClosed));
|
||||
this._account = account;
|
||||
this._contact = contact;
|
||||
|
||||
this._targetHandle = targetHandle;
|
||||
this._targetHandleType = targetHandleType;
|
||||
this._targetId = targetId;
|
||||
this._conn = conn;
|
||||
this._channel = channel;
|
||||
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
|
||||
|
||||
if (targetHandleType == Telepathy.HandleType.CONTACT) {
|
||||
let aliasing = new Telepathy.ConnectionAliasing(DBus.session, connName, connPath);
|
||||
aliasing.RequestAliasesRemote([this._targetHandle], Lang.bind(this,
|
||||
function (aliases, err) {
|
||||
if (aliases && aliases.length)
|
||||
this.title = aliases[0];
|
||||
}));
|
||||
}
|
||||
this._updateAlias();
|
||||
|
||||
// Since we only create sources when receiving a message, this
|
||||
// is a plausible default
|
||||
this._presence = Telepathy.ConnectionPresenceType.AVAILABLE;
|
||||
this._notification = new Notification(this);
|
||||
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
||||
|
||||
this._channelText = new Telepathy.ChannelText(DBus.session, connName, channelPath);
|
||||
this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
|
||||
this._presence = contact.get_presence_type();
|
||||
|
||||
this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
|
||||
this._sentId = this._channel.connect('message-sent', Lang.bind(this, this._messageSent));
|
||||
this._receivedId = this._channel.connect('message-received', Lang.bind(this, this._messageReceived));
|
||||
|
||||
this._setSummaryIcon(this.createNotificationIcon());
|
||||
|
||||
this._notifyAliasId = this._contact.connect('notify::alias', Lang.bind(this, this._updateAlias));
|
||||
this._notifyAvatarId = this._contact.connect('notify::avatar-file', Lang.bind(this, this._updateAvatarIcon));
|
||||
this._presenceChangedId = this._contact.connect('presence-changed', Lang.bind(this, this._presenceChanged));
|
||||
|
||||
this._displayPendingMessages();
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
this.title = this._contact.get_alias();
|
||||
},
|
||||
|
||||
createNotificationIcon: function() {
|
||||
return contactManager.createAvatar(this._conn, this._targetHandle,
|
||||
this.ICON_SIZE);
|
||||
this._iconBox = new St.Bin({ style_class: 'avatar-box' });
|
||||
this._iconBox._size = this.ICON_SIZE;
|
||||
|
||||
this._updateAvatarIcon();
|
||||
|
||||
return this._iconBox;
|
||||
},
|
||||
|
||||
_updateAvatarIcon: function() {
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let file = this._contact.get_avatar_file();
|
||||
|
||||
if (file) {
|
||||
let uri = file.get_uri();
|
||||
this._iconBox.child = textureCache.load_uri_async(uri, this._iconBox._size, this._iconBox._size);
|
||||
} else {
|
||||
this._iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
icon_type: St.IconType.FULLCOLOR,
|
||||
icon_size: this._iconBox._size });
|
||||
}
|
||||
},
|
||||
|
||||
_notificationClicked: function(notification) {
|
||||
channelDispatcher.EnsureChannelRemote(this._accountPath,
|
||||
{ 'org.freedesktop.Telepathy.Channel.ChannelType': Telepathy.CHANNEL_TEXT_NAME,
|
||||
'org.freedesktop.Telepathy.Channel.TargetHandle': this._targetHandle,
|
||||
'org.freedesktop.Telepathy.Channel.TargetHandleType': this._targetHandleType },
|
||||
global.get_current_time(),
|
||||
'',
|
||||
Lang.bind(this, this._gotChannelRequest));
|
||||
let props = {};
|
||||
props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
|
||||
[props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();
|
||||
|
||||
let req = Tp.AccountChannelRequest.new(this._account, props, global.get_current_time());
|
||||
|
||||
req.ensure_channel_async('', null, null);
|
||||
},
|
||||
|
||||
_gotChannelRequest: function (chanReqPath, ex) {
|
||||
if (ex) {
|
||||
log ('EnsureChannelRemote failed? ' + ex);
|
||||
return;
|
||||
_displayPendingMessages: function() {
|
||||
let msgs = this._channel.get_pending_messages();
|
||||
|
||||
for (let i = 0; i < msgs.length; i++) {
|
||||
let msg = msgs[i];
|
||||
this._messageReceived(this._channel, msg);
|
||||
}
|
||||
|
||||
let chanReq = new Telepathy.ChannelRequest(DBus.session, Telepathy.CHANNEL_DISPATCHER_NAME, chanReqPath);
|
||||
chanReq.ProceedRemote();
|
||||
},
|
||||
|
||||
_gotPendingMessages: function(msgs, err) {
|
||||
if (!msgs)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < msgs.length; i++)
|
||||
this._messageReceived.apply(this, [this._channel].concat(msgs[i]));
|
||||
},
|
||||
|
||||
_channelClosed: function() {
|
||||
this._channel.disconnect(this._closedId);
|
||||
this._channelText.disconnect(this._receivedId);
|
||||
this._channel.disconnect(this._receivedId);
|
||||
this._channel.disconnect(this._sentId);
|
||||
|
||||
this._contact.disconnect(this._notifyAliasId);
|
||||
this._contact.disconnect(this._notifyAvatarId);
|
||||
this._contact.disconnect(this._presenceChangedId);
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_ensureNotification: function() {
|
||||
_messageReceived: function(channel, message) {
|
||||
this._notification.appendMessage(message, NotificationDirection.RECEIVED);
|
||||
this.notify();
|
||||
},
|
||||
|
||||
// This is called for both messages we send from
|
||||
// our client and other clients as well.
|
||||
_messageSent: function(channel, message, flags, token) {
|
||||
this._notification.appendMessage(message, NotificationDirection.SENT);
|
||||
},
|
||||
|
||||
notify: function() {
|
||||
if (!Main.messageTray.contains(this))
|
||||
Main.messageTray.add(this);
|
||||
|
||||
if (!this._notification)
|
||||
this._notification = new Notification(this);
|
||||
},
|
||||
|
||||
_messageReceived: function(channel, id, timestamp, sender,
|
||||
type, flags, text) {
|
||||
this._ensureNotification();
|
||||
this._notification.appendMessage(text);
|
||||
this.notify(this._notification);
|
||||
MessageTray.Source.prototype.notify.call(this, this._notification);
|
||||
},
|
||||
|
||||
respond: function(text) {
|
||||
this._channelText.SendRemote(Telepathy.ChannelTextMessageType.NORMAL, text);
|
||||
let msg = Tp.ClientMessage.new_text(Tp.ChannelTextMessageType.NORMAL, text);
|
||||
this._channel.send_message_async(msg, 0, null);
|
||||
},
|
||||
|
||||
setPresence: function(presence, message) {
|
||||
let msg, notify;
|
||||
_presenceChanged: function (contact, presence, type, status, message) {
|
||||
let msg, shouldNotify, title;
|
||||
|
||||
if (presence == Telepathy.ConnectionPresenceType.AVAILABLE) {
|
||||
msg = _("%s is online.").format(this.title);
|
||||
notify = (this._presence == Telepathy.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Telepathy.ConnectionPresenceType.OFFLINE ||
|
||||
presence == Telepathy.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||
presence = Telepathy.ConnectionPresenceType.OFFLINE;
|
||||
msg = _("%s is offline.").format(this.title);
|
||||
notify = (this._presence != Telepathy.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Telepathy.ConnectionPresenceType.AWAY) {
|
||||
msg = _("%s is away.").format(this.title);
|
||||
notify = false;
|
||||
} else if (presence == Telepathy.ConnectionPresenceType.BUSY) {
|
||||
msg = _("%s is busy.").format(this.title);
|
||||
notify = false;
|
||||
if (this._presence == presence)
|
||||
return;
|
||||
|
||||
title = GLib.markup_escape_text(this.title, -1);
|
||||
|
||||
if (presence == Tp.ConnectionPresenceType.AVAILABLE) {
|
||||
msg = _("%s is online.").format(title);
|
||||
shouldNotify = (this._presence == Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.OFFLINE ||
|
||||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
|
||||
presence = Tp.ConnectionPresenceType.OFFLINE;
|
||||
msg = _("%s is offline.").format(title);
|
||||
shouldNotify = (this._presence != Tp.ConnectionPresenceType.OFFLINE);
|
||||
} else if (presence == Tp.ConnectionPresenceType.AWAY) {
|
||||
msg = _("%s is away.").format(title);
|
||||
shouldNotify = false;
|
||||
} else if (presence == Tp.ConnectionPresenceType.BUSY) {
|
||||
msg = _("%s is busy.").format(title);
|
||||
shouldNotify = false;
|
||||
} else
|
||||
return;
|
||||
|
||||
@ -564,10 +258,9 @@ Source.prototype = {
|
||||
if (message)
|
||||
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
|
||||
|
||||
this._ensureNotification();
|
||||
this._notification.appendMessage(msg, true);
|
||||
if (notify)
|
||||
this.notify(this._notification);
|
||||
this._notification.appendPresence(msg, shouldNotify);
|
||||
if (shouldNotify)
|
||||
this.notify();
|
||||
}
|
||||
};
|
||||
|
||||
@ -580,29 +273,51 @@ Notification.prototype = {
|
||||
|
||||
_init: function(source) {
|
||||
MessageTray.Notification.prototype._init.call(this, source, source.title, null, { customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._responseEntry = new St.Entry({ style_class: 'chat-response' });
|
||||
this._responseEntry = new St.Entry({ style_class: 'chat-response',
|
||||
can_focus: true });
|
||||
this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated));
|
||||
this.setActionArea(this._responseEntry);
|
||||
|
||||
this._history = [];
|
||||
this._timestampTimeoutId = 0;
|
||||
},
|
||||
|
||||
appendMessage: function(text, asTitle) {
|
||||
if (asTitle)
|
||||
this.update(text, null, { customContent: true });
|
||||
else
|
||||
this.update(this.source.title, text, { customContent: true });
|
||||
this._append(text, 'chat-received');
|
||||
appendMessage: function(message, direction) {
|
||||
let [text, flags] = message.to_text();
|
||||
let timestamp = message.get_received_timestamp();
|
||||
|
||||
this.update(this.source.title, text, { customContent: true });
|
||||
this._append(text, direction, timestamp);
|
||||
},
|
||||
|
||||
_append: function(text, style) {
|
||||
_append: function(text, style, timestamp) {
|
||||
let currentTime = (Date.now() / 1000);
|
||||
if (!timestamp)
|
||||
timestamp = currentTime;
|
||||
let lastMessageTime = -1;
|
||||
if (this._history.length > 0)
|
||||
lastMessageTime = this._history[0].time;
|
||||
|
||||
// Reset the old message timeout
|
||||
if (this._timestampTimeoutId)
|
||||
Mainloop.source_remove(this._timestampTimeoutId);
|
||||
|
||||
let body = this.addBody(text);
|
||||
body.add_style_class_name(style);
|
||||
this.scrollTo(St.Side.BOTTOM);
|
||||
|
||||
let now = new Date().getTime() / 1000;
|
||||
this._history.unshift({ actor: body, time: now });
|
||||
this._history.unshift({ actor: body, time: timestamp, realMessage: true });
|
||||
|
||||
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
|
||||
this._appendTimestamp();
|
||||
else
|
||||
// Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME
|
||||
// from the timestamp of the message.
|
||||
this._timestampTimeoutId = Mainloop.timeout_add_seconds(
|
||||
SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp),
|
||||
Lang.bind(this, this._appendTimestamp));
|
||||
|
||||
if (this._history.length > 1) {
|
||||
// Keep the scrollback from growing too long. If the most
|
||||
@ -611,22 +326,42 @@ Notification.prototype = {
|
||||
// SCROLLBACK_RECENT_LENGTH previous messages. Otherwise
|
||||
// we'll keep SCROLLBACK_IDLE_LENGTH messages.
|
||||
|
||||
let lastMessageTime = this._history[1].time;
|
||||
let maxLength = (lastMessageTime < now - SCROLLBACK_RECENT_TIME) ?
|
||||
let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ?
|
||||
SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH;
|
||||
if (this._history.length > maxLength) {
|
||||
let expired = this._history.splice(maxLength);
|
||||
let filteredHistory = this._history.filter(function(item) { return item.realMessage });
|
||||
if (filteredHistory.length > maxLength) {
|
||||
let lastMessageToKeep = filteredHistory[maxLength];
|
||||
let expired = this._history.splice(this._history.indexOf(lastMessageToKeep));
|
||||
for (let i = 0; i < expired.length; i++)
|
||||
expired[i].actor.destroy();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
// Need to call the base class function first so that
|
||||
// it saves where the key focus was before.
|
||||
MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
|
||||
global.stage.set_key_focus(this._responseEntry.clutter_text);
|
||||
_appendTimestamp: function() {
|
||||
let lastMessageTime = this._history[0].time;
|
||||
let lastMessageDate = new Date(lastMessageTime * 1000);
|
||||
|
||||
/* Translators: this is a time format string followed by a date.
|
||||
If applicable, replace %X with a strftime format valid for your
|
||||
locale, without seconds. */
|
||||
// xgettext:no-c-format
|
||||
let timeLabel = this.addBody(lastMessageDate.toLocaleFormat(_("Sent at %X on %A")), false, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
timeLabel.add_style_class_name('chat-meta-message');
|
||||
this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false });
|
||||
|
||||
this._timestampTimeoutId = 0;
|
||||
return false;
|
||||
},
|
||||
|
||||
appendPresence: function(text, asTitle) {
|
||||
if (asTitle)
|
||||
this.update(text, null, { customContent: true, titleMarkup: true });
|
||||
else
|
||||
this.update(this.source.title, null, { customContent: true });
|
||||
let label = this.addBody(text, true);
|
||||
label.add_style_class_name('chat-meta-message');
|
||||
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false});
|
||||
},
|
||||
|
||||
_onEntryActivated: function() {
|
||||
@ -634,8 +369,9 @@ Notification.prototype = {
|
||||
if (text == '')
|
||||
return;
|
||||
|
||||
// Telepathy sends out the Sent signal for us.
|
||||
// see Source._messageSent
|
||||
this._responseEntry.set_text('');
|
||||
this._append(text, 'chat-sent');
|
||||
this.source.respond(text);
|
||||
}
|
||||
};
|
||||
|
@ -62,9 +62,16 @@ function addTween(target, tweeningParameters) {
|
||||
function _wrapTweening(target, tweeningParameters) {
|
||||
let state = _getTweenState(target);
|
||||
|
||||
if (target instanceof Clutter.Actor && !state.destroyedId)
|
||||
state.destroyedId = target.connect('destroy', _actorDestroyed);
|
||||
|
||||
if (!state.destroyedId) {
|
||||
if (target instanceof Clutter.Actor) {
|
||||
state.actor = target;
|
||||
state.destroyedId = target.connect('destroy', _actorDestroyed);
|
||||
} else if (target.actor && target.actor instanceof Clutter.Actor) {
|
||||
state.actor = target.actor;
|
||||
state.destroyedId = target.actor.connect('destroy', function() { _actorDestroyed(target); });
|
||||
}
|
||||
}
|
||||
|
||||
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
|
||||
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
|
||||
}
|
||||
@ -82,7 +89,7 @@ function _resetTweenState(target) {
|
||||
|
||||
if (state) {
|
||||
if (state.destroyedId)
|
||||
target.disconnect(state.destroyedId);
|
||||
state.actor.disconnect(state.destroyedId);
|
||||
if (state.idleCompletedId)
|
||||
Mainloop.source_remove(state.idleCompletedId);
|
||||
}
|
||||
|
601
js/ui/viewSelector.js
Normal file
@ -0,0 +1,601 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
|
||||
function SearchEntry(focusBase) {
|
||||
this._init(focusBase);
|
||||
}
|
||||
|
||||
SearchEntry.prototype = {
|
||||
_init : function(focusBase) {
|
||||
this.actor = new St.Entry({ name: 'searchEntry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
active; it should not exceed ~30
|
||||
characters */
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true });
|
||||
this.entry = this.actor.clutter_text;
|
||||
|
||||
this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-find',
|
||||
icon_type: St.IconType.SYMBOLIC });
|
||||
this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear',
|
||||
icon_type: St.IconType.SYMBOLIC });
|
||||
this.actor.set_secondary_icon(this._inactiveIcon);
|
||||
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this.actor.clutter_text.connect('text-changed',
|
||||
Lang.bind(this, this._onTextChanged));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
|
||||
global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
|
||||
|
||||
this.pane = null;
|
||||
|
||||
this._capturedEventId = 0;
|
||||
this._focusBase = focusBase;
|
||||
},
|
||||
|
||||
_updateCursorVisibility: function() {
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus == this._focusBase || focus == this.entry)
|
||||
this.entry.set_cursor_visible(true);
|
||||
else
|
||||
this.entry.set_cursor_visible(false);
|
||||
},
|
||||
|
||||
_onMapped: function() {
|
||||
if (this.actor.mapped) {
|
||||
// Enable 'find-as-you-type'
|
||||
this._capturedEventId = global.stage.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
this.entry.set_cursor_visible(true);
|
||||
this.entry.set_selection(0, 0);
|
||||
} else {
|
||||
// Disable 'find-as-you-type'
|
||||
if (this._capturedEventId > 0)
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
this.actor.sync_hover();
|
||||
|
||||
this.entry.text = '';
|
||||
|
||||
// Return focus to the viewSelector
|
||||
global.stage.set_key_focus(this._focusBase);
|
||||
|
||||
this.entry.set_cursor_visible(true);
|
||||
this.entry.set_selection(0, 0);
|
||||
},
|
||||
|
||||
getText: function () {
|
||||
return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
},
|
||||
|
||||
// some search term has been entered
|
||||
isActive: function() {
|
||||
return this.actor.get_text() != '';
|
||||
},
|
||||
|
||||
// the entry does not show the hint
|
||||
_isActivated: function() {
|
||||
return this.entry.text == this.actor.get_text();
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let source = event.get_source();
|
||||
let panelEvent = source && Main.panel.actor.contains(source);
|
||||
|
||||
switch (event.type()) {
|
||||
case Clutter.EventType.BUTTON_PRESS:
|
||||
// the user clicked outside after activating the entry, but
|
||||
// with no search term entered - cancel the search
|
||||
if (source != this.entry && this.entry.text == '') {
|
||||
this.reset();
|
||||
// allow only panel events to continue
|
||||
return !panelEvent;
|
||||
}
|
||||
return false;
|
||||
case Clutter.EventType.KEY_PRESS:
|
||||
// If some "special" actor grabbed the focus (run
|
||||
// dialog, looking glass); we don't want to interfere
|
||||
// with that
|
||||
let focus = global.stage.get_key_focus();
|
||||
if (focus != this._focusBase && focus != this.entry)
|
||||
return false;
|
||||
|
||||
let sym = event.get_key_symbol();
|
||||
|
||||
// If we have an active search, Escape cancels it - if we
|
||||
// haven't, the key is ignored
|
||||
if (sym == Clutter.Escape)
|
||||
if (this._isActivated()) {
|
||||
this.reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore non-printable keys
|
||||
if (!Clutter.keysym_to_unicode(sym))
|
||||
return false;
|
||||
|
||||
// Search started - move the key focus to the entry and
|
||||
// "repeat" the event
|
||||
if (!this._isActivated()) {
|
||||
global.stage.set_key_focus(this.entry);
|
||||
this.entry.event(event, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
default:
|
||||
// Suppress all other events outside the panel while the entry
|
||||
// is activated and no search has been entered - any click
|
||||
// outside the entry will cancel the search
|
||||
return (this.entry.text == '' && !panelEvent);
|
||||
}
|
||||
},
|
||||
|
||||
_onTextChanged: function() {
|
||||
if (this.isActive()) {
|
||||
this.actor.set_secondary_icon(this._activeIcon);
|
||||
|
||||
if (this._iconClickedId == 0)
|
||||
this._iconClickedId = this.actor.connect('secondary-icon-clicked',
|
||||
Lang.bind(this, function() {
|
||||
this.reset();
|
||||
}));
|
||||
} else {
|
||||
if (this._iconClickedId > 0)
|
||||
this.actor.disconnect(this._iconClickedId);
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this.actor.set_secondary_icon(this._inactiveIcon);
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
this._activeIcon = null;
|
||||
this._inactiveIcon = null;
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(SearchEntry.prototype);
|
||||
|
||||
|
||||
function BaseTab(titleActor, pageActor) {
|
||||
this._init(titleActor, pageActor);
|
||||
}
|
||||
|
||||
BaseTab.prototype = {
|
||||
_init: function(titleActor, pageActor) {
|
||||
this.title = titleActor;
|
||||
this.page = new St.Bin({ child: pageActor,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
style_class: 'view-tab-page' });
|
||||
|
||||
this.visible = false;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.visible = true;
|
||||
this.page.opacity = 0;
|
||||
this.page.show();
|
||||
|
||||
Tweener.addTween(this.page,
|
||||
{ opacity: 255,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.visible = false;
|
||||
Tweener.addTween(this.page,
|
||||
{ opacity: 0,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.page.hide();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_activate: function() {
|
||||
this.emit('activated');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(BaseTab.prototype);
|
||||
|
||||
|
||||
function ViewTab(label, pageActor) {
|
||||
this._init(label, pageActor);
|
||||
}
|
||||
|
||||
ViewTab.prototype = {
|
||||
__proto__: BaseTab.prototype,
|
||||
|
||||
_init: function(label, pageActor) {
|
||||
let titleActor = new St.Button({ label: label,
|
||||
style_class: 'view-tab-title' });
|
||||
titleActor.connect('clicked', Lang.bind(this, this._activate));
|
||||
|
||||
BaseTab.prototype._init.call(this, titleActor, pageActor);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function SearchTab(focusBase) {
|
||||
this._init(focusBase);
|
||||
}
|
||||
|
||||
SearchTab.prototype = {
|
||||
__proto__: BaseTab.prototype,
|
||||
|
||||
_init: function(focusBase) {
|
||||
this._searchActive = false;
|
||||
this._searchPending = false;
|
||||
this._keyPressId = 0;
|
||||
this._searchTimeoutId = 0;
|
||||
this._focusBase = focusBase;
|
||||
|
||||
this._searchSystem = new Search.SearchSystem();
|
||||
this._openSearchSystem = new Search.OpenSearchSystem();
|
||||
|
||||
this._searchEntry = new SearchEntry(focusBase);
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
|
||||
BaseTab.prototype._init.call(this,
|
||||
this._searchEntry.actor,
|
||||
this._searchResults.actor);
|
||||
this._searchEntry.entry.connect('text-changed',
|
||||
Lang.bind(this, this._onTextChanged));
|
||||
this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
this._searchResults.activateSelected();
|
||||
return true;
|
||||
}));
|
||||
},
|
||||
|
||||
show: function() {
|
||||
BaseTab.prototype.show.call(this);
|
||||
|
||||
if (this._keyPressId == 0)
|
||||
this._keyPressId = this._searchEntry.entry.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
BaseTab.prototype.hide.call(this);
|
||||
|
||||
if (this._keyPressId > 0) {
|
||||
this._searchEntry.entry.disconnect(this._keyPressId);
|
||||
this._keyPressId = 0;
|
||||
}
|
||||
this._searchEntry.reset();
|
||||
},
|
||||
|
||||
addSearchProvider: function(provider) {
|
||||
this._searchSystem.registerProvider(provider);
|
||||
this._searchResults.createProviderMeta(provider);
|
||||
},
|
||||
|
||||
_onTextChanged: function (se, prop) {
|
||||
let searchPreviouslyActive = this._searchActive;
|
||||
this._searchActive = this._searchEntry.isActive();
|
||||
this._searchPending = this._searchActive && !searchPreviouslyActive;
|
||||
if (this._searchPending) {
|
||||
this._searchResults.startingSearch();
|
||||
}
|
||||
if (this._searchActive) {
|
||||
this._activate();
|
||||
} else {
|
||||
this.emit('search-cancelled');
|
||||
}
|
||||
if (!this._searchActive) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this._searchTimeoutId > 0)
|
||||
return;
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
|
||||
},
|
||||
|
||||
_onKeyPress: function(entry, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Up) {
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
this._searchResults.selectUp(false);
|
||||
|
||||
return true;
|
||||
} else if (symbol == Clutter.Down) {
|
||||
if (!this._searchActive)
|
||||
return true;
|
||||
|
||||
this._searchResults.selectDown(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_doSearch: function () {
|
||||
this._searchTimeoutId = 0;
|
||||
let text = this._searchEntry.getText();
|
||||
this._searchResults.updateSearch(text);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function ViewSelector() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ViewSelector.prototype = {
|
||||
_init : function() {
|
||||
this.actor = new St.BoxLayout({ name: 'viewSelector',
|
||||
vertical: true });
|
||||
this.actor.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
|
||||
// The tab bar is located at the top of the view selector and
|
||||
// holds both "normal" tab labels and the search entry. The former
|
||||
// is left aligned, the latter right aligned - unless the text
|
||||
// direction is RTL, in which case the order is reversed.
|
||||
this._tabBar = new Shell.GenericContainer();
|
||||
this._tabBar.connect('get-preferred-width',
|
||||
Lang.bind(this, this._getPreferredTabBarWidth));
|
||||
this._tabBar.connect('get-preferred-height',
|
||||
Lang.bind(this, this._getPreferredTabBarHeight));
|
||||
this._tabBar.connect('allocate',
|
||||
Lang.bind(this, this._allocateTabBar));
|
||||
this.actor.add(this._tabBar);
|
||||
|
||||
// Box to hold "normal" tab labels
|
||||
this._tabBox = new St.BoxLayout({ name: 'viewSelectorTabBar' });
|
||||
this._tabBar.add_actor(this._tabBox);
|
||||
|
||||
// The searchArea just holds the entry
|
||||
this._searchArea = new St.Bin({ name: 'searchArea' });
|
||||
this._tabBar.add_actor(this._searchArea);
|
||||
|
||||
// The page area holds the tab pages. Every page is given the
|
||||
// area's full allocation, so that the pages would appear on top
|
||||
// of each other if the inactive ones weren't hidden.
|
||||
this._pageArea = new Shell.Stack();
|
||||
this.actor.add(this._pageArea, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
|
||||
this._tabs = [];
|
||||
this._activeTab = null;
|
||||
|
||||
this._searchTab = new SearchTab(this.actor);
|
||||
this._searchArea.set_child(this._searchTab.title);
|
||||
this._addTab(this._searchTab);
|
||||
|
||||
this._searchTab.connect('search-cancelled', Lang.bind(this,
|
||||
function() {
|
||||
this._switchTab(this._activeTab);
|
||||
}));
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
Main.overview.connect('hiding',
|
||||
Lang.bind(this, this._switchDefaultTab));
|
||||
|
||||
// Public constraints which may be used to tie actors' height or
|
||||
// vertical position to the current tab's content; as the content's
|
||||
// height and position depend on the view selector's style properties
|
||||
// (e.g. font size, padding, spacing, ...) it would be extremely hard
|
||||
// and ugly to get these from the outside. While it would be possible
|
||||
// to use position and height properties directly, outside code would
|
||||
// need to ensure that the content is properly allocated before
|
||||
// accessing the properties.
|
||||
this.constrainY = new Clutter.BindConstraint({ source: this._pageArea,
|
||||
coordinate: Clutter.BindCoordinate.Y });
|
||||
this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea,
|
||||
coordinate: Clutter.BindCoordinate.HEIGHT });
|
||||
},
|
||||
|
||||
_addTab: function(tab) {
|
||||
tab.page.hide();
|
||||
this._pageArea.add_actor(tab.page);
|
||||
tab.connect('activated', Lang.bind(this, function(tab) {
|
||||
this._switchTab(tab);
|
||||
}));
|
||||
},
|
||||
|
||||
addViewTab: function(title, pageActor) {
|
||||
let viewTab = new ViewTab(title, pageActor);
|
||||
this._tabs.push(viewTab);
|
||||
this._tabBox.add(viewTab.title);
|
||||
this._addTab(viewTab);
|
||||
},
|
||||
|
||||
_switchTab: function(tab) {
|
||||
if (this._activeTab && this._activeTab.visible) {
|
||||
if (this._activeTab == tab)
|
||||
return;
|
||||
this._activeTab.title.remove_style_pseudo_class('selected');
|
||||
this._activeTab.hide();
|
||||
}
|
||||
|
||||
if (tab != this._searchTab) {
|
||||
tab.title.add_style_pseudo_class('selected');
|
||||
this._activeTab = tab;
|
||||
if (this._searchTab.visible) {
|
||||
this._searchTab.hide();
|
||||
}
|
||||
}
|
||||
|
||||
if (!tab.visible)
|
||||
tab.show();
|
||||
|
||||
// Pull a Meg Ryan:
|
||||
if (Main.overview && Main.overview.workspaces) {
|
||||
if (tab != this._tabs[0]) {
|
||||
Tweener.addTween(Main.overview.workspaces.actor,
|
||||
{ opacity: 0,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
Main.overview.workspaces.actor.hide();
|
||||
Main.overview.workspaces.actor.opacity = 255;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
Main.overview.workspaces.actor.opacity = 0;
|
||||
Main.overview.workspaces.actor.show();
|
||||
Tweener.addTween(Main.overview.workspaces.actor,
|
||||
{ opacity: 255,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_switchDefaultTab: function() {
|
||||
if (this._tabs.length > 0)
|
||||
this._switchTab(this._tabs[0]);
|
||||
},
|
||||
|
||||
_nextTab: function() {
|
||||
if (this._tabs.length == 0 ||
|
||||
this._tabs[this._tabs.length - 1] == this._activeTab)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this._tabs.length; i++)
|
||||
if (this._tabs[i] == this._activeTab) {
|
||||
this._switchTab(this._tabs[i + 1]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_prevTab: function() {
|
||||
if (this._tabs.length == 0 || this._tabs[0] == this._activeTab)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this._tabs.length; i++)
|
||||
if (this._tabs[i] == this._activeTab) {
|
||||
this._switchTab(this._tabs[i - 1]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredTabBarWidth: function(box, forHeight, alloc) {
|
||||
let children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNat] = children[i].get_preferred_width(forHeight);
|
||||
alloc.min_size += childMin;
|
||||
alloc.natural_size += childNat;
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredTabBarHeight: function(box, forWidth, alloc) {
|
||||
let children = box.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
|
||||
if (childMin > alloc.min_size)
|
||||
alloc.min_size = childMin;
|
||||
if (childNatural > alloc.natural_size)
|
||||
alloc.natural_size = childNatural;
|
||||
}
|
||||
},
|
||||
|
||||
_allocateTabBar: function(container, box, flags) {
|
||||
let allocWidth = box.x2 - box.x1;
|
||||
let allocHeight = box.y2 - box.y1;
|
||||
|
||||
let [searchMinWidth, searchNatWidth] = this._searchArea.get_preferred_width(-1);
|
||||
let [barMinWidth, barNatWidth] = this._tabBox.get_preferred_width(-1);
|
||||
let childBox = new Clutter.ActorBox();
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = allocHeight;
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = allocWidth - barNatWidth;
|
||||
childBox.x2 = allocWidth;
|
||||
} else {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = barNatWidth;
|
||||
}
|
||||
this._tabBox.allocate(childBox, flags);
|
||||
|
||||
if (this.actor.get_direction() == St.TextDirection.RTL) {
|
||||
childBox.x1 = 0;
|
||||
childBox.x2 = searchNatWidth;
|
||||
} else {
|
||||
childBox.x1 = allocWidth - searchNatWidth;
|
||||
childBox.x2 = allocWidth;
|
||||
}
|
||||
this._searchArea.allocate(childBox, flags);
|
||||
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||
function() {
|
||||
this.constrainY.offset = this.actor.y;
|
||||
}));
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
let modifiers = Shell.get_event_state(event);
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
} else if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
|
||||
if (symbol == Clutter.Page_Up) {
|
||||
if (!this._searchActive)
|
||||
this._prevTab();
|
||||
return true;
|
||||
} else if (symbol == Clutter.Page_Down) {
|
||||
if (!this._searchActive)
|
||||
this._nextTab();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
addSearchProvider: function(provider) {
|
||||
this._searchTab.addSearchProvider(provider);
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(ViewSelector.prototype);
|
@ -116,6 +116,7 @@ WindowManager.prototype = {
|
||||
this.setKeybindingHandler('switch_to_workspace_up', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
|
||||
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._dimmedWindows.length; i++)
|
||||
@ -497,6 +498,8 @@ WindowManager.prototype = {
|
||||
|
||||
for (let i = 0; i < switchData.windows.length; i++) {
|
||||
let w = switchData.windows[i];
|
||||
if (w.window.is_destroyed()) // Window gone
|
||||
continue;
|
||||
if (w.window.get_parent() == switchData.outGroup) {
|
||||
w.window.reparent(w.parent);
|
||||
w.window.hide();
|
||||
@ -518,49 +521,84 @@ WindowManager.prototype = {
|
||||
|
||||
let tabPopup = new AltTab.AltTabPopup();
|
||||
|
||||
if (!tabPopup.show(backwards))
|
||||
if (!tabPopup.show(backwards, binding == 'switch_group'))
|
||||
tabPopup.destroy();
|
||||
},
|
||||
|
||||
_showWorkspaceSwitcher : function(shellwm, binding, window, backwards) {
|
||||
/* We don't support this kind of layout */
|
||||
if (binding == 'switch_to_workspace_up' || binding == 'switch_to_workspace_down')
|
||||
return;
|
||||
|
||||
if (global.screen.n_workspaces == 1)
|
||||
return;
|
||||
|
||||
if (this._workspaceSwitcherPopup == null)
|
||||
this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
|
||||
|
||||
if (binding == 'switch_to_workspace_left') {
|
||||
this.actionMoveWorkspaceLeft();
|
||||
}
|
||||
|
||||
if (binding == 'switch_to_workspace_right') {
|
||||
this.actionMoveWorkspaceRight();
|
||||
}
|
||||
if (binding == 'switch_to_workspace_up')
|
||||
this.actionMoveWorkspaceUp();
|
||||
else if (binding == 'switch_to_workspace_down')
|
||||
this.actionMoveWorkspaceDown();
|
||||
// left/right would effectively act as synonyms for up/down if we enabled them;
|
||||
// but that could be considered confusing.
|
||||
// else if (binding == 'switch_to_workspace_left')
|
||||
// this.actionMoveWorkspaceLeft();
|
||||
// else if (binding == 'switch_to_workspace_right')
|
||||
// this.actionMoveWorkspaceRight();
|
||||
},
|
||||
|
||||
actionMoveWorkspaceLeft: function() {
|
||||
let rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
if (activeWorkspaceIndex > 0) {
|
||||
global.screen.get_workspace_by_index(activeWorkspaceIndex - 1).activate(global.get_current_time());
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.LEFT, activeWorkspaceIndex - 1);
|
||||
} else if (!Main.overview.visible) {
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.LEFT, activeWorkspaceIndex);
|
||||
}
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
else if (!rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceRight: function() {
|
||||
let rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
if (activeWorkspaceIndex < global.screen.n_workspaces - 1) {
|
||||
global.screen.get_workspace_by_index(activeWorkspaceIndex + 1).activate(global.get_current_time());
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, activeWorkspaceIndex + 1);
|
||||
} else if (!Main.overview.visible) {
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.RIGHT, activeWorkspaceIndex);
|
||||
}
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (rtl && activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
else if (!rtl && activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceUp: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex > 0)
|
||||
indexToActivate--;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.UP, indexToActivate);
|
||||
},
|
||||
|
||||
actionMoveWorkspaceDown: function() {
|
||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
let indexToActivate = activeWorkspaceIndex;
|
||||
if (activeWorkspaceIndex < global.screen.n_workspaces - 1)
|
||||
indexToActivate++;
|
||||
|
||||
if (indexToActivate != activeWorkspaceIndex)
|
||||
global.screen.get_workspace_by_index(indexToActivate).activate(global.get_current_time());
|
||||
|
||||
if (!Main.overview.visible)
|
||||
this._workspaceSwitcherPopup.display(WorkspaceSwitcherPopup.DOWN, indexToActivate);
|
||||
}
|
||||
};
|
||||
|
@ -20,9 +20,6 @@ const FOCUS_ANIMATION_TIME = 0.15;
|
||||
|
||||
const WINDOW_DND_SIZE = 256;
|
||||
|
||||
const FRAME_COLOR = new Clutter.Color();
|
||||
FRAME_COLOR.from_pixel(0xffffffff);
|
||||
|
||||
const SCROLL_SCALE_AMOUNT = 100 / 5;
|
||||
|
||||
const LIGHTBOX_FADE_TIME = 0.1;
|
||||
@ -54,11 +51,6 @@ function _clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
// Spacing between workspaces. At the moment, the same spacing is used
|
||||
// in both zoomed-in and zoomed-out views; this is slightly
|
||||
// metaphor-breaking, but the alternatives are also weird.
|
||||
const GRID_SPACING = 15;
|
||||
const FRAME_SIZE = GRID_SPACING / 3;
|
||||
|
||||
function ScaledPoint(x, y, scaleX, scaleY) {
|
||||
[this.x, this.y, this.scaleX, this.scaleY] = arguments;
|
||||
@ -112,6 +104,12 @@ WindowClone.prototype = {
|
||||
|
||||
this._stackAbove = null;
|
||||
|
||||
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
|
||||
this.emit('size-changed');
|
||||
}));
|
||||
this._realWindowDestroyId = this.realWindow.connect('destroy',
|
||||
Lang.bind(this, this._disconnectRealWindowSignals));
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
|
||||
@ -140,7 +138,10 @@ WindowClone.prototype = {
|
||||
if (this.inDrag || this._zooming)
|
||||
// We'll fix up the stack after the drag/zooming
|
||||
return;
|
||||
this.actor.raise(this._stackAbove);
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
@ -161,7 +162,19 @@ WindowClone.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_disconnectRealWindowSignals: function() {
|
||||
if (this._sizeChangedId > 0)
|
||||
this.realWindow.disconnect(this._sizeChangedId);
|
||||
this._sizeChangedId = 0;
|
||||
|
||||
if (this._realWindowDestroyId > 0)
|
||||
this.realWindow.disconnect(this._realWindowDestroyId);
|
||||
this._realWindowDestroyId = 0;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._disconnectRealWindowSignals();
|
||||
|
||||
this.metaWindow._delegate = null;
|
||||
this.actor._delegate = null;
|
||||
if (this._zoomLightbox)
|
||||
@ -247,11 +260,13 @@ WindowClone.prototype = {
|
||||
this.emit('zoom-end');
|
||||
|
||||
this.actor.reparent(this._origParent);
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
// If the workspace has been destroyed while we were reparented to
|
||||
// the stage, _stackAbove will be unparented and we can't raise our
|
||||
// actor above it - as we are bound to be destroyed anyway in that
|
||||
// case, we can skip that step
|
||||
if (this._stackAbove && this._stackAbove.get_parent())
|
||||
else if (this._stackAbove.get_parent())
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
||||
@ -283,82 +298,20 @@ WindowClone.prototype = {
|
||||
// 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);
|
||||
if (this.actor.get_parent() != null) {
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
}
|
||||
|
||||
|
||||
this.emit('drag-end');
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(WindowClone.prototype);
|
||||
|
||||
|
||||
function DesktopClone(window) {
|
||||
this._init(window);
|
||||
}
|
||||
|
||||
DesktopClone.prototype = {
|
||||
_init : function(window) {
|
||||
this.actor = new Clutter.Group({ reactive: true });
|
||||
|
||||
let background = new Clutter.Clone({ source: global.background_actor });
|
||||
this.actor.add_actor(background);
|
||||
|
||||
if (window) {
|
||||
this._desktop = new Clutter.Clone({ source: window.get_texture() });
|
||||
this.actor.add_actor(this._desktop);
|
||||
this._desktop.hide();
|
||||
} else {
|
||||
this._desktop = null;
|
||||
}
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
},
|
||||
|
||||
zoomFromOverview: function(fadeInIcons) {
|
||||
if (this._desktop == null)
|
||||
return;
|
||||
|
||||
if (fadeInIcons) {
|
||||
this._desktop.opacity = 0;
|
||||
this._desktop.show();
|
||||
Tweener.addTween(this._desktop,
|
||||
{ opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
},
|
||||
|
||||
zoomToOverview: function(fadeOutIcons) {
|
||||
if (this._desktop == null)
|
||||
return;
|
||||
|
||||
if (fadeOutIcons) {
|
||||
this._desktop.opacity = 255;
|
||||
this._desktop.show();
|
||||
Tweener.addTween(this._desktop,
|
||||
{ opacity: 0,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this._desktop.hide();
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this._desktop.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_onButtonRelease : function (actor, event) {
|
||||
this.emit('selected', event.get_time());
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(DesktopClone.prototype);
|
||||
|
||||
|
||||
/**
|
||||
* @windowClone: Corresponding window clone
|
||||
* @parentActor: The actor which will be the parent of all overlay items
|
||||
@ -461,7 +414,7 @@ WindowOverlay.prototype = {
|
||||
// These parameters are not the values retrieved with
|
||||
// get_transformed_position() and get_transformed_size(),
|
||||
// as windowClone might be moving.
|
||||
// See Workspace._fadeInWindowOverlay
|
||||
// See Workspace._showWindowOverlay
|
||||
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight) {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
@ -534,12 +487,10 @@ WindowOverlay.prototype = {
|
||||
|
||||
_idleToggleCloseButton: function() {
|
||||
this._idleToggleCloseId = 0;
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
|
||||
x, y);
|
||||
if (actor != this._windowClone.actor && actor != this.closeButton) {
|
||||
if (!this._windowClone.actor.has_pointer &&
|
||||
!this.closeButton.has_pointer)
|
||||
this.closeButton.hide();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@ -561,7 +512,6 @@ WindowOverlay.prototype = {
|
||||
this._parentActor.queue_relayout();
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(WindowOverlay.prototype);
|
||||
|
||||
const WindowPositionFlags = {
|
||||
@ -602,46 +552,18 @@ Workspace.prototype = {
|
||||
this.actor.height = global.screen_height;
|
||||
this.scale = 1.0;
|
||||
|
||||
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
||||
let windows = Main.getWindowActorsForWorkspace(this.metaWorkspace.index());
|
||||
|
||||
// Find the desktop window
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (windows[i].meta_window.get_window_type() == Meta.WindowType.DESKTOP) {
|
||||
this._desktop = new DesktopClone(windows[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there wasn't one, fake it
|
||||
if (!this._desktop)
|
||||
this._desktop = new DesktopClone();
|
||||
|
||||
this._desktop.connect('selected',
|
||||
Lang.bind(this,
|
||||
function(clone, time) {
|
||||
// Only switch to the workspace when there's no application windows
|
||||
// open (we always have one window for the desktop). The problem
|
||||
// is that it's too easy to miss an app window and get the wrong
|
||||
// one focused.
|
||||
if (this._windows.length == 1) {
|
||||
this.metaWorkspace.activate(time);
|
||||
Main.overview.hide();
|
||||
}
|
||||
}));
|
||||
this.actor.add_actor(this._desktop.actor);
|
||||
|
||||
// Create clones for remaining windows that should be
|
||||
// Create clones for windows that should be
|
||||
// visible in the Overview
|
||||
this._windows = [this._desktop];
|
||||
this._windowOverlays = [ null ];
|
||||
this._windows = [];
|
||||
this._windowOverlays = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// A filter for what windows we display
|
||||
this._showOnlyWindows = null;
|
||||
|
||||
// Track window changes
|
||||
this._windowAddedId = this.metaWorkspace.connect('window-added',
|
||||
Lang.bind(this, this._windowAdded));
|
||||
@ -649,10 +571,6 @@ Workspace.prototype = {
|
||||
Lang.bind(this, this._windowRemoved));
|
||||
this._repositionWindowsId = 0;
|
||||
|
||||
this._visible = false;
|
||||
|
||||
this._frame = null;
|
||||
|
||||
this.leavingOverview = false;
|
||||
},
|
||||
|
||||
@ -676,91 +594,12 @@ Workspace.prototype = {
|
||||
new_parent.add_actor(this._windowOverlaysGroup);
|
||||
},
|
||||
|
||||
/**
|
||||
* lookupCloneForMetaWindow:
|
||||
* @metaWindow: A #MetaWindow
|
||||
*
|
||||
* Given a #MetaWindow instance, find the WindowClone object
|
||||
* which represents it in the workspaces display.
|
||||
*/
|
||||
lookupCloneForMetaWindow: function (metaWindow) {
|
||||
let index = this._lookupIndex (metaWindow);
|
||||
return index < 0 ? null : this._windows[index];
|
||||
},
|
||||
|
||||
containsMetaWindow: function (metaWindow) {
|
||||
return this._lookupIndex(metaWindow) >= 0;
|
||||
},
|
||||
|
||||
setShowOnlyWindows: function(showOnlyWindows, reposition) {
|
||||
this._showOnlyWindows = showOnlyWindows;
|
||||
this._resetCloneVisibility();
|
||||
if (reposition)
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
/**
|
||||
* setLightboxMode:
|
||||
* @showLightbox: If true, dim background and allow highlighting a specific window
|
||||
*
|
||||
* This function also resets the highlighted window state.
|
||||
*/
|
||||
setLightboxMode: function (showLightbox) {
|
||||
if (!this._lightbox)
|
||||
this._lightbox = new Lightbox.Lightbox(this.actor,
|
||||
{ fadeTime: LIGHTBOX_FADE_TIME });
|
||||
|
||||
if (showLightbox)
|
||||
this._lightbox.show();
|
||||
else
|
||||
this._lightbox.hide();
|
||||
|
||||
if (this._frame)
|
||||
this._frame.set_opacity(showLightbox ? 150 : 255);
|
||||
},
|
||||
|
||||
/**
|
||||
* setHighlightWindow:
|
||||
* @metaWindow: A #MetaWindow
|
||||
*
|
||||
* Draw the user's attention to the given window @metaWindow.
|
||||
*/
|
||||
setHighlightWindow: function (metaWindow) {
|
||||
if (!this._lightbox)
|
||||
return;
|
||||
|
||||
let actor;
|
||||
if (metaWindow != null) {
|
||||
let clone = this.lookupCloneForMetaWindow(metaWindow);
|
||||
actor = clone.actor;
|
||||
}
|
||||
this._lightbox.highlight(actor);
|
||||
},
|
||||
|
||||
// Mark the workspace selected/not-selected
|
||||
setSelected : function(selected) {
|
||||
// Don't draw a frame if we only have one workspace
|
||||
if (selected && global.screen.n_workspaces > 1) {
|
||||
if (this._frame)
|
||||
return;
|
||||
|
||||
// FIXME: do something cooler-looking using clutter-cairo
|
||||
this._frame = new Clutter.Rectangle({ color: FRAME_COLOR });
|
||||
this.actor.add_actor(this._frame);
|
||||
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
|
||||
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
|
||||
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
|
||||
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
|
||||
this._frame.lower_bottom();
|
||||
|
||||
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
|
||||
} else {
|
||||
if (!this._frame)
|
||||
return;
|
||||
this.actor.disconnect(this._framePosHandler);
|
||||
this._frame.destroy();
|
||||
this._frame = null;
|
||||
}
|
||||
isEmpty: function() {
|
||||
return this._windows.length == 0;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -770,55 +609,7 @@ Workspace.prototype = {
|
||||
* Set the workspace (desktop) reactive
|
||||
**/
|
||||
setReactive: function(reactive) {
|
||||
this._desktop.actor.reactive = reactive;
|
||||
},
|
||||
|
||||
_updateFramePosition : function() {
|
||||
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x,
|
||||
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y);
|
||||
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
|
||||
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;
|
||||
},
|
||||
|
||||
_resetCloneVisibility: function () {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let overlay = this._windowOverlays[i];
|
||||
|
||||
if (!this._isCloneVisible(clone)) {
|
||||
clone.actor.hide();
|
||||
overlay.hide();
|
||||
} else {
|
||||
clone.actor.show();
|
||||
}
|
||||
}
|
||||
this.actor.reactive = reactive;
|
||||
},
|
||||
|
||||
// Only use this for n <= 20 say
|
||||
@ -1002,14 +793,13 @@ Workspace.prototype = {
|
||||
|
||||
xCenter = xCenter * global.screen_width;
|
||||
|
||||
let rect = new Meta.Rectangle();
|
||||
metaWindow.get_outer_rect(rect);
|
||||
let rect = metaWindow.get_outer_rect();
|
||||
let buttonOuterHeight, captionHeight;
|
||||
let buttonOuterWidth = 0;
|
||||
|
||||
if (this._windowOverlays[1]) {
|
||||
[buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights();
|
||||
buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale;
|
||||
if (this._windowOverlays[0]) {
|
||||
[buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
|
||||
buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale;
|
||||
} else
|
||||
[buttonOuterHeight, captionHeight] = [0, 0];
|
||||
buttonOuterHeight /= this.scale;
|
||||
@ -1033,6 +823,9 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
setReservedSlot: function(clone) {
|
||||
if (this._reservedSlot == clone)
|
||||
return;
|
||||
|
||||
if (clone && this.containsMetaWindow(clone.metaWindow)) {
|
||||
this._reservedSlot = null;
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
@ -1057,22 +850,23 @@ Workspace.prototype = {
|
||||
this._repositionWindowsId = 0;
|
||||
}
|
||||
|
||||
let totalVisible = 0;
|
||||
|
||||
let visibleClones = this._getVisibleClones();
|
||||
let clones = this._windows.slice();
|
||||
if (this._reservedSlot)
|
||||
visibleClones.push(this._reservedSlot);
|
||||
clones.push(this._reservedSlot);
|
||||
|
||||
let workspaceZooming = flags & WindowPositionFlags.ZOOM;
|
||||
let animate = flags & WindowPositionFlags.ANIMATE;
|
||||
|
||||
// Start the animations
|
||||
let slots = this._computeAllWindowSlots(visibleClones.length);
|
||||
visibleClones = this._orderWindowsByMotionAndStartup(visibleClones, slots);
|
||||
let slots = this._computeAllWindowSlots(clones.length);
|
||||
clones = this._orderWindowsByMotionAndStartup(clones, slots);
|
||||
|
||||
for (let i = 0; i < visibleClones.length; i++) {
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
let isOnCurrentWorkspace = this.metaWorkspace == currentWorkspace;
|
||||
|
||||
for (let i = 0; i < clones.length; i++) {
|
||||
let slot = slots[i];
|
||||
let clone = visibleClones[i];
|
||||
let clone = clones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
let mainIndex = this._lookupIndex(metaWindow);
|
||||
let overlay = this._windowOverlays[mainIndex];
|
||||
@ -1086,7 +880,7 @@ Workspace.prototype = {
|
||||
|
||||
if (overlay)
|
||||
overlay.hide();
|
||||
if (animate) {
|
||||
if (animate && isOnCurrentWorkspace) {
|
||||
if (!metaWindow.showing_on_its_workspace()) {
|
||||
/* Hidden windows should fade in and grow
|
||||
* therefore we need to resize them now so they
|
||||
@ -1116,36 +910,34 @@ Workspace.prototype = {
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._fadeInWindowOverlay(clone, overlay);
|
||||
this._showWindowOverlay(clone, overlay, true);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
clone.actor.set_position(x, y);
|
||||
clone.actor.set_scale(scale, scale);
|
||||
this._fadeInWindowOverlay(clone, overlay);
|
||||
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
let desktopClone = this._windows[0];
|
||||
let clones = this._windows.slice();
|
||||
clones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||
|
||||
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];
|
||||
for (let i = 0; i < clones.length; i++) {
|
||||
let clone = clones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(desktopClone.actor);
|
||||
clone.setStackAbove(null);
|
||||
} else {
|
||||
let previousClone = visibleClones[i - 1];
|
||||
let previousClone = clones[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInWindowOverlay: function(clone, overlay) {
|
||||
_showWindowOverlay: function(clone, overlay, fade) {
|
||||
if (clone.inDrag)
|
||||
return;
|
||||
|
||||
@ -1157,34 +949,26 @@ Workspace.prototype = {
|
||||
// be after the workspace animation finishes.
|
||||
let [cloneX, cloneY] = clone.actor.get_position();
|
||||
let [cloneWidth, cloneHeight] = clone.actor.get_size();
|
||||
cloneX = this.gridX + this.scale * cloneX;
|
||||
cloneY = this.gridY + this.scale * cloneY;
|
||||
cloneX = this.x + this.scale * cloneX;
|
||||
cloneY = this.y + this.scale * cloneY;
|
||||
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
|
||||
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
|
||||
|
||||
if (!this._windowOverlaysGroup.visible)
|
||||
this._windowOverlaysGroup.show();
|
||||
|
||||
if (overlay) {
|
||||
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
|
||||
overlay.fadeIn();
|
||||
if (fade)
|
||||
overlay.fadeIn();
|
||||
else
|
||||
overlay.show();
|
||||
}
|
||||
},
|
||||
|
||||
_fadeInAllOverlays: function() {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
_showAllOverlays: function() {
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let overlay = this._windowOverlays[i];
|
||||
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
|
||||
continue;
|
||||
this._fadeInWindowOverlay(clone, overlay);
|
||||
}
|
||||
},
|
||||
|
||||
_hideAllOverlays: function() {
|
||||
for (let i = 1; i< this._windows.length; i++) {
|
||||
let overlay = this._windowOverlays[i];
|
||||
overlay.hide();
|
||||
this._showWindowOverlay(clone, overlay, this.metaWorkspace == currentWorkspace);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1197,8 +981,8 @@ Workspace.prototype = {
|
||||
let wsHeight = this.actor.height * this.scale;
|
||||
|
||||
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
|
||||
let inWorkspace = (this.gridX < x && x < this.gridX + wsWidth &&
|
||||
this.gridY < y && y < this.gridY + wsHeight);
|
||||
let inWorkspace = (this.x < x && x < this.x + wsWidth &&
|
||||
this.y < y && y < this.y + wsHeight);
|
||||
|
||||
if (pointerHasMoved && inWorkspace) {
|
||||
// store current cursor position
|
||||
@ -1212,8 +996,11 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
showWindowsOverlays: function() {
|
||||
if (this.leavingOverview)
|
||||
return;
|
||||
|
||||
this._windowOverlaysGroup.show();
|
||||
this._fadeInAllOverlays();
|
||||
this._showAllOverlays();
|
||||
},
|
||||
|
||||
hideWindowsOverlays: function() {
|
||||
@ -1309,8 +1096,8 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
// check for maximized windows on the workspace
|
||||
_haveMaximizedWindows: function() {
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
hasMaximizedWindows: function() {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let metaWindow = this._windows[i].metaWindow;
|
||||
if (metaWindow.showing_on_its_workspace() &&
|
||||
metaWindow.maximized_horizontally &&
|
||||
@ -1322,7 +1109,7 @@ Workspace.prototype = {
|
||||
|
||||
// Animate the full-screen to Overview transition.
|
||||
zoomToOverview : function() {
|
||||
this.actor.set_position(this.gridX, this.gridY);
|
||||
this.actor.set_position(this.x, this.y);
|
||||
this.actor.set_scale(this.scale, this.scale);
|
||||
|
||||
// Position and scale the windows.
|
||||
@ -1330,21 +1117,15 @@ Workspace.prototype = {
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.ZOOM);
|
||||
else
|
||||
this.positionWindows(WindowPositionFlags.ZOOM);
|
||||
|
||||
let active = global.screen.get_active_workspace();
|
||||
let fadeInIcons = (Main.overview.animationInProgress &&
|
||||
active == this.metaWorkspace &&
|
||||
!this._haveMaximizedWindows());
|
||||
this._desktop.zoomToOverview(fadeInIcons);
|
||||
|
||||
this._visible = true;
|
||||
},
|
||||
|
||||
// Animates the return from Overview mode
|
||||
zoomFromOverview : function() {
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
|
||||
this.leavingOverview = true;
|
||||
|
||||
this._hideAllOverlays();
|
||||
this.hideWindowsOverlays();
|
||||
|
||||
if (this._repositionWindowsId > 0) {
|
||||
Mainloop.source_remove(this._repositionWindowsId);
|
||||
@ -1353,8 +1134,11 @@ Workspace.prototype = {
|
||||
this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this,
|
||||
this._doneLeavingOverview));
|
||||
|
||||
if (this._metaWorkspace == currentWorkspace)
|
||||
return;
|
||||
|
||||
// Position and scale the windows.
|
||||
for (let i = 1; i < this._windows.length; i++) {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
|
||||
clone.zoomFromOverview();
|
||||
@ -1383,75 +1167,6 @@ Workspace.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
let active = global.screen.get_active_workspace();
|
||||
let fadeOutIcons = (active == this.metaWorkspace &&
|
||||
!this._haveMaximizedWindows());
|
||||
this._desktop.zoomFromOverview(fadeOutIcons);
|
||||
|
||||
this._visible = false;
|
||||
},
|
||||
|
||||
// Animates grid shrinking/expanding when a row or column
|
||||
// of workspaces is added or removed
|
||||
resizeToGrid : function (oldScale) {
|
||||
this._hideAllOverlays();
|
||||
Tweener.addTween(this.actor,
|
||||
{ x: this.gridX,
|
||||
y: this.gridY,
|
||||
scale_x: this.scale,
|
||||
scale_y: this.scale,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, this._fadeInAllOverlays)
|
||||
});
|
||||
},
|
||||
|
||||
// Animates the addition of a new (empty) workspace
|
||||
slideIn : function(oldScale) {
|
||||
if (this.gridCol > this.gridRow) {
|
||||
this.actor.set_position(global.screen_width, this.gridY);
|
||||
this.actor.set_scale(oldScale, oldScale);
|
||||
} else {
|
||||
this.actor.set_position(this.gridX, global.screen_height);
|
||||
this.actor.set_scale(this.scale, this.scale);
|
||||
}
|
||||
Tweener.addTween(this.actor,
|
||||
{ x: this.gridX,
|
||||
y: this.gridY,
|
||||
scale_x: this.scale,
|
||||
scale_y: this.scale,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this._visible = true;
|
||||
},
|
||||
|
||||
// Animates the removal of a workspace
|
||||
slideOut : function(onComplete) {
|
||||
let destX = this.actor.x, destY = this.actor.y;
|
||||
|
||||
this._hideAllOverlays();
|
||||
|
||||
if (this.gridCol > this.gridRow)
|
||||
destX = global.screen_width;
|
||||
else
|
||||
destY = global.screen_height;
|
||||
Tweener.addTween(this.actor,
|
||||
{ x: destX,
|
||||
y: destY,
|
||||
scale_x: this.scale,
|
||||
scale_y: this.scale,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: onComplete
|
||||
});
|
||||
|
||||
this._visible = false;
|
||||
|
||||
// Don't let the user try to select this workspace as it's
|
||||
// making its exit.
|
||||
this._desktop.reactive = false;
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
@ -1475,7 +1190,7 @@ Workspace.prototype = {
|
||||
// their parent (this.actor), but we might have a zoomed window
|
||||
// which has been reparented to the stage - _windows[0] holds
|
||||
// the desktop window, which is never reparented
|
||||
for (let w = 1; w < this._windows.length; w++)
|
||||
for (let w = 0; w < this._windows.length; w++)
|
||||
this._windows[w].destroy();
|
||||
this._windows = [];
|
||||
},
|
||||
@ -1487,8 +1202,7 @@ Workspace.prototype = {
|
||||
|
||||
// Tests if @win belongs to this workspaces
|
||||
_isMyWindow : function (win) {
|
||||
return win.get_workspace() == this.metaWorkspace.index() ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
|
||||
},
|
||||
|
||||
// Tests if @win should be shown in the Overview
|
||||
@ -1506,12 +1220,12 @@ Workspace.prototype = {
|
||||
Lang.bind(this, this._onCloneSelected));
|
||||
clone.connect('drag-begin',
|
||||
Lang.bind(this, function(clone) {
|
||||
this.emit('window-drag-begin', clone.actor);
|
||||
Main.overview.beginWindowDrag();
|
||||
overlay.hide();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function(clone) {
|
||||
this.emit('window-drag-end', clone.actor);
|
||||
Main.overview.endWindowDrag();
|
||||
overlay.show();
|
||||
}));
|
||||
clone.connect('zoom-start',
|
||||
@ -1522,6 +1236,10 @@ Workspace.prototype = {
|
||||
Lang.bind(this, function() {
|
||||
this._windowIsZooming = false;
|
||||
}));
|
||||
clone.connect('size-changed',
|
||||
Lang.bind(this, function() {
|
||||
this.positionWindows(0);
|
||||
}));
|
||||
|
||||
this.actor.add_actor(clone.actor);
|
||||
|
||||
@ -1534,7 +1252,7 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
_onShowOverlayClose: function (windowOverlay) {
|
||||
for (let i = 1; i < this._windowOverlays.length; i++) {
|
||||
for (let i = 0; i < this._windowOverlays.length; i++) {
|
||||
let overlay = this._windowOverlays[i];
|
||||
if (overlay == windowOverlay)
|
||||
continue;
|
||||
@ -1572,14 +1290,9 @@ Workspace.prototype = {
|
||||
this.metaWorkspace.index());
|
||||
},
|
||||
|
||||
_removeSelf : function(actor, event) {
|
||||
screen.remove_workspace(this.metaWorkspace, event.get_time());
|
||||
return true;
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
if (source instanceof WindowClone)
|
||||
if (source.realWindow && !this._isMyWindow(source.realWindow))
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
if (source.shellWorkspaceLaunch)
|
||||
return DND.DragMotionResult.COPY_DROP;
|
||||
@ -1588,7 +1301,7 @@ Workspace.prototype = {
|
||||
},
|
||||
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
if (source instanceof WindowClone) {
|
||||
if (source.realWindow) {
|
||||
let win = source.realWindow;
|
||||
if (this._isMyWindow(win))
|
||||
return false;
|
||||
@ -1607,8 +1320,8 @@ Workspace.prototype = {
|
||||
time);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
this.metaWorkspace.activate(time);
|
||||
source.shellWorkspaceLaunch();
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
||||
timestamp: time });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1643,11 +1356,11 @@ function _workspaceRelativeModifier(workspace) {
|
||||
}
|
||||
|
||||
return [ { name: 'x',
|
||||
parameters: { workspacePos: workspace.gridX,
|
||||
parameters: { workspacePos: workspace.x,
|
||||
overviewPos: overviewPosX,
|
||||
overviewScale: overviewScale } },
|
||||
{ name: 'y',
|
||||
parameters: { workspacePos: workspace.gridY,
|
||||
parameters: { workspacePos: workspace.y,
|
||||
overviewPos: overviewPosY,
|
||||
overviewScale: overviewScale } }
|
||||
];
|
||||
|
@ -12,8 +12,8 @@ const Tweener = imports.ui.tweener;
|
||||
const ANIMATION_TIME = 0.1;
|
||||
const DISPLAY_TIMEOUT = 600;
|
||||
|
||||
const LEFT = -1;
|
||||
const RIGHT = 1;
|
||||
const UP = -1;
|
||||
const DOWN = 1;
|
||||
|
||||
function WorkspaceSwitcherPopup() {
|
||||
this._init();
|
||||
@ -32,6 +32,8 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
this._container = new St.BoxLayout({ style_class: 'workspace-switcher-container' });
|
||||
this._list = new Shell.GenericContainer({ style_class: 'workspace-switcher' });
|
||||
this._itemSpacing = 0;
|
||||
this._childHeight = 0;
|
||||
this._childWidth = 0;
|
||||
this._list.connect('style-changed', Lang.bind(this, function() {
|
||||
this._itemSpacing = this._list.get_theme_node().get_length('spacing');
|
||||
}));
|
||||
@ -47,57 +49,59 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
|
||||
this._position();
|
||||
|
||||
this.actor.show();
|
||||
this.actor.hide();
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(DISPLAY_TIMEOUT, Lang.bind(this, this._onTimeout));
|
||||
},
|
||||
|
||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||
let children = this._list.get_children();
|
||||
let primary = global.get_primary_monitor();
|
||||
|
||||
let availwidth = primary.width;
|
||||
availwidth -= this.actor.get_theme_node().get_horizontal_padding();
|
||||
availwidth -= this._container.get_theme_node().get_horizontal_padding();
|
||||
availwidth -= this._list.get_theme_node().get_horizontal_padding();
|
||||
let availHeight = primary.height;
|
||||
availHeight -= Main.panel.actor.height;
|
||||
availHeight -= this.actor.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._container.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._list.get_theme_node().get_vertical_padding();
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(-1);
|
||||
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(childNaturalWidth);
|
||||
width += childNaturalHeight * primary.width / primary.height;
|
||||
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
|
||||
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight);
|
||||
height += childNaturalHeight * primary.width / primary.height;
|
||||
}
|
||||
|
||||
let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
|
||||
width += spacing;
|
||||
width = Math.min(width, availwidth);
|
||||
height += spacing;
|
||||
height = Math.min(height, availHeight);
|
||||
|
||||
this._childWidth = (width - spacing) / global.screen.n_workspaces;
|
||||
this._childHeight = (height - spacing) / global.screen.n_workspaces;
|
||||
|
||||
alloc.min_size = width;
|
||||
alloc.natural_size = width;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
|
||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||
let primary = global.get_primary_monitor();
|
||||
this._childHeight = Math.round(this._childWidth * primary.height / primary.width);
|
||||
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
|
||||
|
||||
alloc.min_size = this._childHeight;
|
||||
alloc.natural_size = this._childHeight;
|
||||
alloc.min_size = this._childWidth;
|
||||
alloc.natural_size = this._childWidth;
|
||||
},
|
||||
|
||||
_allocate : function (actor, box, flags) {
|
||||
let children = this._list.get_children();
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let x = box.x1;
|
||||
let prevChildBoxX2 = box.x1 - this._itemSpacing;
|
||||
let y = box.y1;
|
||||
let prevChildBoxY2 = box.y1 - this._itemSpacing;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
childBox.x1 = prevChildBoxX2 + this._itemSpacing;
|
||||
childBox.x2 = Math.round(x + this._childWidth);
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y1 + this._childHeight;
|
||||
x += this._childWidth + this._itemSpacing;
|
||||
prevChildBoxX2 = childBox.x2;
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x1 + this._childWidth;
|
||||
childBox.y1 = prevChildBoxY2 + this._itemSpacing;
|
||||
childBox.y2 = Math.round(y + this._childHeight);
|
||||
y += this._childHeight + this._itemSpacing;
|
||||
prevChildBoxY2 = childBox.y2;
|
||||
children[i].allocate(childBox, flags);
|
||||
}
|
||||
},
|
||||
@ -108,10 +112,10 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
for (let i = 0; i < global.screen.n_workspaces; i++) {
|
||||
let indicator = null;
|
||||
|
||||
if (i == activeWorkspaceIndex && direction == LEFT)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-left' });
|
||||
else if(i == activeWorkspaceIndex && direction == RIGHT)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-right' });
|
||||
if (i == activeWorkspaceIndex && direction == UP)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
|
||||
else if(i == activeWorkspaceIndex && direction == DOWN)
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
|
||||
else
|
||||
indicator = new St.Bin({ style_class: 'ws-switcher-box' });
|
||||
|
||||
@ -123,7 +127,8 @@ WorkspaceSwitcherPopup.prototype = {
|
||||
_position: function() {
|
||||
let primary = global.get_primary_monitor();
|
||||
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
|
||||
this._container.y = primary.y + Math.floor((primary.height - this._container.height) / 2);
|
||||
this._container.y = primary.y + Main.panel.actor.height +
|
||||
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);
|
||||
},
|
||||
|
||||
_show : function() {
|
||||
|
837
js/ui/workspaceThumbnail.js
Normal file
@ -0,0 +1,837 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
// The maximum size of a thumbnail is 1/8 the width and height of the screen
|
||||
let MAX_THUMBNAIL_SCALE = 1/8.;
|
||||
|
||||
const RESCALE_ANIMATION_TIME = 0.2;
|
||||
const SLIDE_ANIMATION_TIME = 0.2;
|
||||
|
||||
function WindowClone(realWindow) {
|
||||
this._init(realWindow);
|
||||
}
|
||||
|
||||
WindowClone.prototype = {
|
||||
_init : function(realWindow) {
|
||||
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
|
||||
reactive: true });
|
||||
this.actor._delegate = this;
|
||||
this.realWindow = realWindow;
|
||||
this.metaWindow = realWindow.meta_window;
|
||||
|
||||
this._positionChangedId = this.realWindow.connect('position-changed',
|
||||
Lang.bind(this, this._onPositionChanged));
|
||||
this._realWindowDestroyedId = this.realWindow.connect('destroy',
|
||||
Lang.bind(this, this._disconnectRealWindowSignals));
|
||||
this._onPositionChanged();
|
||||
|
||||
this.actor.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._draggable = DND.makeDraggable(this.actor,
|
||||
{ restoreOnSuccess: true,
|
||||
dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
|
||||
dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY });
|
||||
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this.inDrag = false;
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onPositionChanged: function() {
|
||||
let rect = this.metaWindow.get_outer_rect();
|
||||
this.actor.set_position(this.realWindow.x, this.realWindow.y);
|
||||
},
|
||||
|
||||
_disconnectRealWindowSignals: function() {
|
||||
if (this._positionChangedId != 0) {
|
||||
this.realWindow.disconnect(this._positionChangedId);
|
||||
this._positionChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._realWindowDestroyedId != 0) {
|
||||
this.realWindow.disconnect(this._realWindowDestroyedId);
|
||||
this._realWindowDestroyedId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._disconnectRealWindowSignals();
|
||||
|
||||
this.actor._delegate = null;
|
||||
|
||||
if (this.inDrag) {
|
||||
this.emit('drag-end');
|
||||
this.inDrag = false;
|
||||
}
|
||||
|
||||
this.disconnectAll();
|
||||
},
|
||||
|
||||
_onButtonRelease : function (actor, event) {
|
||||
this.emit('selected', event.get_time());
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_onDragBegin : function (draggable, time) {
|
||||
this.inDrag = true;
|
||||
this.emit('drag-begin');
|
||||
},
|
||||
|
||||
_onDragEnd : function (draggable, time, snapback) {
|
||||
this.inDrag = 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) {
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
}
|
||||
|
||||
|
||||
this.emit('drag-end');
|
||||
}
|
||||
};
|
||||
Signals.addSignalMethods(WindowClone.prototype);
|
||||
|
||||
|
||||
const ThumbnailState = {
|
||||
NEW : 0,
|
||||
ANIMATING_IN : 1,
|
||||
NORMAL: 2,
|
||||
REMOVING : 3,
|
||||
ANIMATING_OUT : 4,
|
||||
ANIMATED_OUT : 5,
|
||||
COLLAPSING : 6,
|
||||
DESTROYED : 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @metaWorkspace: a #Meta.Workspace
|
||||
*/
|
||||
function WorkspaceThumbnail(metaWorkspace) {
|
||||
this._init(metaWorkspace);
|
||||
}
|
||||
|
||||
WorkspaceThumbnail.prototype = {
|
||||
_init : function(metaWorkspace) {
|
||||
this.metaWorkspace = metaWorkspace;
|
||||
|
||||
this.actor = new St.Group({ reactive: true,
|
||||
clip_to_allocation: true,
|
||||
style_class: 'workspace-thumbnail' });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._contents = new Clutter.Group();
|
||||
this.actor.add_actor(this._contents);
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('button-press-event', Lang.bind(this,
|
||||
function(actor, event) {
|
||||
return true;
|
||||
}));
|
||||
this.actor.connect('button-release-event', Lang.bind(this,
|
||||
function(actor, event) {
|
||||
this._activate();
|
||||
return true;
|
||||
}));
|
||||
|
||||
this._background = new Clutter.Clone({ source: global.background_actor });
|
||||
this._contents.add_actor(this._background);
|
||||
|
||||
this.setPorthole(0, 0, global.screen_width, global.screen_height);
|
||||
|
||||
let windows = global.get_window_actors().filter(this._isMyWindow, this);
|
||||
|
||||
// Create clones for windows that should be visible in the Overview
|
||||
this._windows = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Track window changes
|
||||
this._windowAddedId = this.metaWorkspace.connect('window-added',
|
||||
Lang.bind(this, this._windowAdded));
|
||||
this._windowRemovedId = this.metaWorkspace.connect('window-removed',
|
||||
Lang.bind(this, this._windowRemoved));
|
||||
|
||||
this.state = ThumbnailState.NORMAL;
|
||||
this._slidePosition = 0; // Fully slid in
|
||||
this._collapseFraction = 0; // Not collapsed
|
||||
},
|
||||
|
||||
setPorthole: function(x, y, width, height) {
|
||||
this.actor.set_size(width, height);
|
||||
this._contents.set_position(-x, -y);
|
||||
},
|
||||
|
||||
_lookupIndex: function (metaWindow) {
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
if (this._windows[i].metaWindow == metaWindow) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
this._windows.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
|
||||
|
||||
for (let i = 0; i < this._windows.length; i++) {
|
||||
let clone = this._windows[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(this._background);
|
||||
} else {
|
||||
let previousClone = this._windows[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
set slidePosition(slidePosition) {
|
||||
this._slidePosition = slidePosition;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get slidePosition() {
|
||||
return this._slidePosition;
|
||||
},
|
||||
|
||||
set collapseFraction(collapseFraction) {
|
||||
this._collapseFraction = collapseFraction;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get collapseFraction() {
|
||||
return this._collapseFraction;
|
||||
},
|
||||
|
||||
_windowRemoved : function(metaWorkspace, metaWin) {
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
// find the position of the window in our list
|
||||
let index = this._lookupIndex (metaWin);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
let clone = this._windows[index];
|
||||
this._windows.splice(index, 1);
|
||||
clone.destroy();
|
||||
},
|
||||
|
||||
_windowAdded : function(metaWorkspace, metaWin) {
|
||||
if (this.leavingOverview)
|
||||
return;
|
||||
|
||||
let win = metaWin.get_compositor_private();
|
||||
|
||||
if (!win) {
|
||||
// Newly-created windows are added to a workspace before
|
||||
// the compositor finds out about them...
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
if (this.actor && metaWin.get_compositor_private())
|
||||
this._windowAdded(metaWorkspace, metaWin);
|
||||
return false;
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
let clone = this._addWindowClone(win);
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy: function(actor) {
|
||||
this.metaWorkspace.disconnect(this._windowAddedId);
|
||||
this.metaWorkspace.disconnect(this._windowRemovedId);
|
||||
|
||||
this._windows = [];
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
// Tests if @win belongs to this workspaces
|
||||
_isMyWindow : function (win) {
|
||||
return win.get_workspace() == this.metaWorkspace.index() ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
},
|
||||
|
||||
// Tests if @win should be shown in the Overview
|
||||
_isOverviewWindow : function (win) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
return tracker.is_window_interesting(win.get_meta_window());
|
||||
},
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win) {
|
||||
let clone = new WindowClone(win);
|
||||
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._activate));
|
||||
clone.connect('drag-begin',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.beginWindowDrag();
|
||||
}));
|
||||
clone.connect('drag-end',
|
||||
Lang.bind(this, function(clone) {
|
||||
Main.overview.endWindowDrag();
|
||||
}));
|
||||
this._contents.add_actor(clone.actor);
|
||||
|
||||
if (this._windows.length == 0)
|
||||
clone.setStackAbove(this._background);
|
||||
else
|
||||
clone.setStackAbove(this._windows[this._windows.length - 1].actor);
|
||||
|
||||
this._windows.push(clone);
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
_activate : function (clone, time) {
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return;
|
||||
|
||||
// a click on the already current workspace should go back to the main view
|
||||
if (this.metaWorkspace == global.screen.get_active_workspace())
|
||||
Main.overview.hide();
|
||||
else
|
||||
this.metaWorkspace.activate(time);
|
||||
},
|
||||
|
||||
// Draggable target interface
|
||||
handleDragOver : function(source, actor, x, y, time) {
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
if (source.realWindow && !this._isMyWindow(source.realWindow))
|
||||
return DND.DragMotionResult.MOVE_DROP;
|
||||
if (source.shellWorkspaceLaunch)
|
||||
return DND.DragMotionResult.COPY_DROP;
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
acceptDrop : function(source, actor, x, y, time) {
|
||||
if (this.state > ThumbnailState.NORMAL)
|
||||
return false;
|
||||
|
||||
if (source.realWindow) {
|
||||
let win = source.realWindow;
|
||||
if (this._isMyWindow(win))
|
||||
return false;
|
||||
|
||||
let metaWindow = win.get_meta_window();
|
||||
metaWindow.change_workspace_by_index(this.metaWorkspace.index(),
|
||||
false, // don't create workspace
|
||||
time);
|
||||
return true;
|
||||
} else if (source.shellWorkspaceLaunch) {
|
||||
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
|
||||
timestamp: time });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Signals.addSignalMethods(WorkspaceThumbnail.prototype);
|
||||
|
||||
|
||||
function ThumbnailsBox() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
ThumbnailsBox.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'workspace-thumbnails',
|
||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
|
||||
// When we animate the scale, we don't animate the requested size of the thumbnails, rather
|
||||
// we ask for our final size and then animate within that size. This slightly simplifies the
|
||||
// interaction with the main workspace windows (instead of constantly reallocating them
|
||||
// to a new size, they get a new size once, then use the standard window animation code
|
||||
// allocate the windows to their new positions), however it causes problems for drawing
|
||||
// the background and border wrapped around the thumbnail as we animate - we can't just pack
|
||||
// the container into a box and set style properties on the box since that box would wrap
|
||||
// around the final size not the animating size. So instead we fake the background with
|
||||
// an actor underneath the content and adjust the allocation of our children to leave space
|
||||
// for the border and padding of the background actor.
|
||||
this._background = new St.Bin({ style_class: 'workspace-thumbnails-background' });
|
||||
|
||||
// This will eventually be automatic, see https://bugzilla.gnome.org/show_bug.cgi?id=584662
|
||||
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
|
||||
this._background.add_style_pseudo_class('rtl');
|
||||
|
||||
this.actor.add_actor(this._background);
|
||||
|
||||
let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' });
|
||||
|
||||
// We don't want the indicator to affect drag-and-drop
|
||||
Shell.util_set_hidden_from_pick(indicator, true);
|
||||
|
||||
this._indicator = indicator;
|
||||
this.actor.add_actor(indicator);
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
this._pendingScaleUpdate = false;
|
||||
this._stateUpdateQueued = false;
|
||||
this._animatingIndicator = false;
|
||||
this._indicatorY = 0; // only used when _animatingIndicator is true
|
||||
|
||||
this._stateCounts = {};
|
||||
for (key in ThumbnailState)
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._switchWorkspaceNotifyId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._activeWorkspaceChanged));
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
this._pendingScaleUpdate = false;
|
||||
this._stateUpdateQueued = false;
|
||||
|
||||
this._stateCounts = {};
|
||||
for (key in ThumbnailState)
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
// The "porthole" is the portion of the screen that we show in the workspaces
|
||||
let panelHeight = Main.panel.actor.height;
|
||||
this._porthole = {
|
||||
x: 0,
|
||||
y: panelHeight,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height - panelHeight
|
||||
};
|
||||
|
||||
this.addThumbnails(0, global.screen.n_workspaces);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this._switchWorkspaceNotifyId > 0) {
|
||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
}
|
||||
|
||||
for (let w = 0; w < this._thumbnails.length; w++)
|
||||
this._thumbnails[w].destroy();
|
||||
this._thumbnails = [];
|
||||
},
|
||||
|
||||
addThumbnails: function(start, count) {
|
||||
for (let k = start; k < start + count; k++) {
|
||||
let metaWorkspace = global.screen.get_workspace_by_index(k);
|
||||
let thumbnail = new WorkspaceThumbnail(metaWorkspace);
|
||||
thumbnail.setPorthole(this._porthole.x, this._porthole.y,
|
||||
this._porthole.width, this._porthole.height);
|
||||
this._thumbnails.push(thumbnail);
|
||||
this.actor.add_actor(thumbnail.actor);
|
||||
|
||||
if (start > 0) { // not the initial fill
|
||||
thumbnail.state = ThumbnailState.NEW;
|
||||
thumbnail.slidePosition = 1; // start slid out
|
||||
this._haveNewThumbnails = true;
|
||||
} else {
|
||||
thumbnail.state = ThumbnailState.NORMAL;
|
||||
}
|
||||
|
||||
this._stateCounts[thumbnail.state]++;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
|
||||
// The thumbnails indicator actually needs to be on top of the thumbnails
|
||||
this._indicator.raise_top();
|
||||
},
|
||||
|
||||
removeThumbmails: function(start, count) {
|
||||
let currentPos = 0;
|
||||
for (let k = 0; k < this._thumbnails.length; k++) {
|
||||
let thumbnail = this._thumbnails[k];
|
||||
|
||||
if (thumbnail.state > ThumbnailState.NORMAL)
|
||||
continue;
|
||||
|
||||
if (currentPos >= start && currentPos < start + count)
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.REMOVING);
|
||||
|
||||
currentPos++;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
|
||||
syncStacking: function(stackIndices) {
|
||||
for (let i = 0; i < this._thumbnails.length; i++)
|
||||
this._thumbnails[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
set scale(scale) {
|
||||
this._scale = scale;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get scale() {
|
||||
return this._scale;
|
||||
},
|
||||
|
||||
set indicatorY(indicatorY) {
|
||||
this._indicatorY = indicatorY;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get indicatorY() {
|
||||
return this._indicatorY;
|
||||
},
|
||||
|
||||
_setThumbnailState: function(thumbnail, state) {
|
||||
this._stateCounts[thumbnail.state]--;
|
||||
thumbnail.state = state;
|
||||
this._stateCounts[thumbnail.state]++;
|
||||
},
|
||||
|
||||
_iterateStateThumbnails: function(state, callback) {
|
||||
if (this._stateCounts[state] == 0)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
if (this._thumbnails[i].state == state)
|
||||
callback.call(this, this._thumbnails[i]);
|
||||
}
|
||||
},
|
||||
|
||||
_tweenScale: function() {
|
||||
Tweener.addTween(this,
|
||||
{ scale: this._targetScale,
|
||||
time: RESCALE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._queueUpdateStates,
|
||||
onCompleteScope: this });
|
||||
},
|
||||
|
||||
_updateStates: function() {
|
||||
this._stateUpdateQueued = false;
|
||||
|
||||
// If we are animating the indicator, wait
|
||||
if (this._animatingIndicator)
|
||||
return;
|
||||
|
||||
// Then slide out any thumbnails that have been destroyed
|
||||
this._iterateStateThumbnails(ThumbnailState.REMOVING,
|
||||
function(thumbnail) {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_OUT);
|
||||
|
||||
Tweener.addTween(thumbnail,
|
||||
{ slidePosition: 1,
|
||||
time: SLIDE_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: function() {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATED_OUT);
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
|
||||
// As long as things are sliding out, don't proceed
|
||||
if (this._stateCounts[ThumbnailState.ANIMATING_OUT] > 0)
|
||||
return;
|
||||
|
||||
// Once that's complete, we can start scaling to the new size and collapse any removed thumbnails
|
||||
this._iterateStateThumbnails(ThumbnailState.ANIMATED_OUT,
|
||||
function(thumbnail) {
|
||||
this.actor.set_skip_paint(thumbnail.actor, true);
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.COLLAPSING);
|
||||
Tweener.addTween(thumbnail,
|
||||
{ collapseFraction: 1,
|
||||
time: RESCALE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._stateCounts[thumbnail.state]--;
|
||||
thumbnail.state = ThumbnailState.DESTROYED;
|
||||
|
||||
let index = this._thumbnails.indexOf(thumbnail);
|
||||
this._thumbnails.splice(index, 1);
|
||||
thumbnail.destroy();
|
||||
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
|
||||
if (this._pendingScaleUpdate) {
|
||||
this._tweenScale();
|
||||
this._pendingScaleUpdate = false;
|
||||
}
|
||||
|
||||
// Wait until that's done
|
||||
if (this._scale != this._targetScale || this._stateCounts[ThumbnailState.COLLAPSING] > 0)
|
||||
return;
|
||||
|
||||
// And then slide in any new thumbnails
|
||||
this._iterateStateThumbnails(ThumbnailState.NEW,
|
||||
function(thumbnail) {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.ANIMATING_IN);
|
||||
Tweener.addTween(thumbnail,
|
||||
{ slidePosition: 0,
|
||||
time: SLIDE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.NORMAL);
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_queueUpdateStates: function() {
|
||||
if (this._stateUpdateQueued)
|
||||
return;
|
||||
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateStates));
|
||||
|
||||
this._stateUpdateQueued = true;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
|
||||
forWidth = themeNode.adjust_for_width(forWidth);
|
||||
|
||||
// Note that for getPreferredWidth/Height we cheat a bit and skip propagating
|
||||
// the size request to our children because we know how big they are and know
|
||||
// that the actors aren't depending on the virtual functions being called.
|
||||
|
||||
if (this._thumbnails.length == 0)
|
||||
return;
|
||||
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
|
||||
[alloc.min_size, alloc.natural_size] =
|
||||
themeNode.adjust_preferred_height(totalSpacing,
|
||||
totalSpacing + nWorkspaces * this._porthole.height * MAX_THUMBNAIL_SCALE);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
|
||||
if (this._thumbnails.length == 0)
|
||||
return;
|
||||
|
||||
// We don't animate our preferred width, which is always reported according
|
||||
// to the actual number of current workspaces, we just animate within that
|
||||
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
|
||||
let avail = forHeight - totalSpacing;
|
||||
|
||||
let scale = (avail / nWorkspaces) / this._porthole.height;
|
||||
scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
|
||||
|
||||
let width = Math.round(this._porthole.width * scale);
|
||||
[alloc.min_size, alloc.natural_size] =
|
||||
themeNode.adjust_preferred_width(width, width);
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
|
||||
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
let contentBox = themeNode.get_content_box(box);
|
||||
|
||||
if (this._thumbnails.length == 0) // not visible
|
||||
return;
|
||||
|
||||
let portholeWidth = this._porthole.width;
|
||||
let portholeHeight = this._porthole.height;
|
||||
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||
|
||||
// Compute the scale we'll need once everything is updated
|
||||
let nWorkspaces = global.screen.n_workspaces;
|
||||
let totalSpacing = (nWorkspaces - 1) * spacing;
|
||||
let avail = (contentBox.y2 - contentBox.y1) - totalSpacing;
|
||||
|
||||
let newScale = (avail / nWorkspaces) / portholeHeight;
|
||||
newScale = Math.min(newScale, MAX_THUMBNAIL_SCALE);
|
||||
|
||||
if (newScale != this._targetScale) {
|
||||
if (this._targetScale > 0) {
|
||||
// We don't do the tween immediately because we need to observe the ordering
|
||||
// in queueUpdateStates - if workspaces have been removed we need to slide them
|
||||
// out as the first thing.
|
||||
this._targetScale = newScale;
|
||||
this._pendingScaleUpdate = true;
|
||||
} else {
|
||||
this._targetScale = this._scale = newScale;
|
||||
}
|
||||
|
||||
this._queueUpdateStates();
|
||||
}
|
||||
|
||||
let thumbnailHeight = portholeHeight * this._scale;
|
||||
let thumbnailWidth = Math.round(portholeWidth * this._scale);
|
||||
let roundedHScale = thumbnailWidth / portholeWidth;
|
||||
|
||||
let slideOffset; // X offset when thumbnail is fully slid offscreen
|
||||
if (rtl)
|
||||
slideOffset = - (thumbnailWidth + themeNode.get_padding(St.Side.LEFT));
|
||||
else
|
||||
slideOffset = thumbnailWidth + themeNode.get_padding(St.Side.RIGHT);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
// The background is horizontally restricted to correspond to the current thumbnail size
|
||||
// but otherwise covers the entire allocation
|
||||
if (rtl) {
|
||||
childBox.x1 = box.x1;
|
||||
childBox.x2 = box.x2 - ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
|
||||
} else {
|
||||
childBox.x1 = box.x1 + ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
|
||||
childBox.x2 = box.x2;
|
||||
}
|
||||
childBox.y1 = box.y1;
|
||||
childBox.y2 = box.y2;
|
||||
this._background.allocate(childBox, flags);
|
||||
|
||||
let indicatorY = this._indicatorY;
|
||||
// when not animating, the workspace position overrides this._indicatorY
|
||||
let indicatorWorkspace = !this._animatingIndicator ? global.screen.get_active_workspace() : null;
|
||||
|
||||
let y = contentBox.y1;
|
||||
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
let thumbnail = this._thumbnails[i];
|
||||
|
||||
if (i > 0)
|
||||
y += spacing - Math.round(thumbnail.collapseFraction * spacing);
|
||||
|
||||
// We might end up with thumbnailHeight being something like 99.33
|
||||
// pixels. To make this work and not end up with a gap at the bottom,
|
||||
// we need some thumbnails to be 99 pixels and some 100 pixels height;
|
||||
// we compute an actual scale separately for each thumbnail.
|
||||
let y1 = Math.round(y);
|
||||
let y2 = Math.round(y + thumbnailHeight);
|
||||
let roundedVScale = (y2 - y1) / portholeHeight;
|
||||
|
||||
let x1, x2;
|
||||
if (rtl) {
|
||||
x1 = contentBox.x1 + slideOffset * thumbnail.slidePosition;
|
||||
x2 = x1 + thumbnailWidth;
|
||||
} else {
|
||||
x1 = contentBox.x2 - thumbnailWidth + slideOffset * thumbnail.slidePosition;
|
||||
x2 = x1 + thumbnailWidth;
|
||||
}
|
||||
|
||||
if (thumbnail.metaWorkspace == indicatorWorkspace)
|
||||
indicatorY = y1;
|
||||
|
||||
// Allocating a scaled actor is funny - x1/y1 correspond to the origin
|
||||
// of the actor, but x2/y2 are increased by the *unscaled* size.
|
||||
childBox.x1 = x1;
|
||||
childBox.x2 = x1 + portholeWidth;
|
||||
childBox.y1 = y1;
|
||||
childBox.y2 = y1 + portholeHeight;
|
||||
|
||||
thumbnail.actor.set_scale(roundedHScale, roundedVScale);
|
||||
thumbnail.actor.allocate(childBox, flags);
|
||||
|
||||
// We round the collapsing portion so that we don't get thumbnails resizing
|
||||
// during an animation due to differences in rounded, but leave the uncollapsed
|
||||
// portion unrounded so that non-animating we end up with the right total
|
||||
y += thumbnailHeight - Math.round(thumbnailHeight * thumbnail.collapseFraction);
|
||||
}
|
||||
|
||||
if (rtl) {
|
||||
childBox.x1 = contentBox.x1;
|
||||
childBox.x2 = contentBox.x1 + thumbnailWidth;
|
||||
} else {
|
||||
childBox.x1 = contentBox.x2 - thumbnailWidth;
|
||||
childBox.x2 = contentBox.x2;
|
||||
}
|
||||
childBox.y1 = indicatorY;
|
||||
childBox.y2 = childBox.y1 + thumbnailHeight;
|
||||
this._indicator.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_activeWorkspaceChanged: function(wm, from, to, direction) {
|
||||
let thumbnail;
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
for (let i = 0; i < this._thumbnails.length; i++) {
|
||||
if (this._thumbnails[i].metaWorkspace == activeWorkspace) {
|
||||
thumbnail = this._thumbnails[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._animatingIndicator = true;
|
||||
this.indicatorY = this._indicator.allocation.y1;
|
||||
Tweener.addTween(this,
|
||||
{ indicatorY: thumbnail.actor.allocation.y1,
|
||||
time: WorkspacesView.WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
this._animatingIndicator = false;
|
||||
this._queueUpdateStates();
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
}
|
||||
};
|
127
js/ui/xdndHandler.js
Normal file
@ -0,0 +1,127 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const Mainloop = imports.mainloop;
|
||||
const DND = imports.ui.dnd;
|
||||
|
||||
function XdndHandler() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
XdndHandler.prototype = {
|
||||
_init: function() {
|
||||
// Used to display a clone of the cursor window when the
|
||||
// window group is hidden (like it happens in the overview)
|
||||
this._cursorWindowClone = null;
|
||||
|
||||
// Used as a drag actor in case we don't have a cursor window clone
|
||||
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
|
||||
global.stage.add_actor(this._dummy);
|
||||
this._dummy.hide();
|
||||
|
||||
// Mutter delays the creation of the output window as long
|
||||
// as possible to avoid flicker. In case a plugin wants to
|
||||
// access it directly it has to connect to the stage's show
|
||||
// signal. (see comment in compositor.c:meta_compositor_manage_screen)
|
||||
global.stage.connect('show', function () {
|
||||
global.init_xdnd();
|
||||
return false;
|
||||
});
|
||||
|
||||
global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
|
||||
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
|
||||
global.connect('xdnd-leave', Lang.bind(this, this._onLeave));
|
||||
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
},
|
||||
|
||||
// Called when the user cancels the drag (i.e release the button)
|
||||
_onLeave: function() {
|
||||
if (this._windowGroupVisibilityHandlerId != 0) {
|
||||
Mainloop.source_remove(this._windowGroupVisibilityHandlerId);
|
||||
this._windowGroupVisibilityHandlerId = 0;
|
||||
}
|
||||
this.emit('drag-end');
|
||||
},
|
||||
|
||||
_onEnter: function() {
|
||||
this._windowGroupVisibilityHandlerId =
|
||||
global.window_group.connect('notify::visible',
|
||||
Lang.bind(this, this._onWindowGroupVisibilityChanged));
|
||||
|
||||
this.emit('drag-begin', global.get_current_time());
|
||||
},
|
||||
|
||||
_onWindowGroupVisibilityChanged: function() {
|
||||
if (!global.window_group.visible) {
|
||||
if (this._cursorWindowClone)
|
||||
return;
|
||||
|
||||
let windows = global.get_window_actors();
|
||||
let cursorWindow = windows[windows.length - 1];
|
||||
|
||||
// FIXME: more reliable way?
|
||||
if (!cursorWindow.is_override_redirect())
|
||||
return;
|
||||
|
||||
let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION,
|
||||
source: cursorWindow});
|
||||
|
||||
this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
|
||||
global.overlay_group.add_actor(this._cursorWindowClone);
|
||||
Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
|
||||
|
||||
// Make sure that the clone has the same position as the source
|
||||
this._cursorWindowClone.add_constraint(constraint_position);
|
||||
} else {
|
||||
if (this._cursorWindowClone)
|
||||
{
|
||||
this._cursorWindowClone.destroy();
|
||||
this._cursorWindowClone = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onPositionChanged: function(obj, x, y) {
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
|
||||
// Make sure that the cursor window is on top
|
||||
if (this._cursorWindowClone)
|
||||
this._cursorWindowClone.raise_top();
|
||||
|
||||
let dragEvent = {
|
||||
x: x,
|
||||
y: y,
|
||||
dragActor: this._cursorWindowClone ? this._cursorWindowClone : this._dummy,
|
||||
source: this,
|
||||
targetActor: pickedActor
|
||||
};
|
||||
|
||||
for (let i = 0; i < DND.dragMonitors.length; i++) {
|
||||
let motionFunc = DND.dragMonitors[i].dragMotion;
|
||||
if (motionFunc) {
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DND.DragMotionResult.CONTINUE)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (pickedActor) {
|
||||
if (pickedActor._delegate && pickedActor._delegate.handleDragOver) {
|
||||
let result = pickedActor._delegate.handleDragOver(this,
|
||||
dragEvent.dragActor,
|
||||
x,
|
||||
y,
|
||||
global.get_current_time());
|
||||
if (result != DND.DragMotionResult.CONTINUE)
|
||||
return;
|
||||
}
|
||||
pickedActor = pickedActor.get_parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Signals.addSignalMethods(XdndHandler.prototype);
|
@ -8,16 +8,19 @@ el
|
||||
en_GB
|
||||
es
|
||||
et
|
||||
fa
|
||||
fi
|
||||
fr
|
||||
ga
|
||||
gl
|
||||
gu
|
||||
he
|
||||
hu
|
||||
id
|
||||
it
|
||||
ja
|
||||
ko
|
||||
kn
|
||||
lt
|
||||
nb
|
||||
nl
|
||||
|
@ -1,23 +1,34 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/gnome-shell-clock-preferences.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
data/org.gnome.accessibility.magnifier.gschema.xml.in
|
||||
[type: gettext/glade]data/clock-preferences.ui
|
||||
js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/calendar.js
|
||||
js/ui/dash.js
|
||||
js/ui/dateMenu.js
|
||||
js/ui/docDisplay.js
|
||||
js/ui/endSessionDialog.js
|
||||
js/ui/lookingGlass.js
|
||||
js/ui/messageTray.js
|
||||
js/ui/overview.js
|
||||
js/ui/panel.js
|
||||
js/ui/placeDisplay.js
|
||||
js/ui/popupMenu.js
|
||||
js/ui/runDialog.js
|
||||
js/ui/searchDisplay.js
|
||||
js/ui/statusMenu.js
|
||||
js/ui/status/accessibility.js
|
||||
js/ui/status/bluetooth.js
|
||||
js/ui/status/keyboard.js
|
||||
js/ui/status/power.js
|
||||
js/ui/status/volume.js
|
||||
js/ui/telepathyClient.js
|
||||
js/ui/viewSelector.js
|
||||
js/ui/windowAttentionHandler.js
|
||||
js/ui/workspacesView.js
|
||||
src/gvc/gvc-mixer-control.c
|
||||
src/gdmuser/gdm-user.c
|
||||
src/shell-app-system.c
|
||||
src/shell-global.c
|
||||
src/shell-uri-util.c
|
||||
src/shell-util.c
|
||||
|
||||
|
567
po/de.po
@ -6,19 +6,21 @@
|
||||
#
|
||||
# Hendrik Brandt <heb@gnome-de.org>, 2009.
|
||||
# Hendrik Richter <hendrikr@gnome.org>, 2009.
|
||||
# Mario Blättermann <mariobl@gnome.org>, 2009, 2010.
|
||||
# Mario Blättermann <mariobl@gnome.org>, 2009, 2010, 2011.
|
||||
# Mario Klug <mario@klug.me>, 2010.
|
||||
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009, 2010.
|
||||
# Jakob Kramer <jakob.kramer@gmx.de>, 2010.
|
||||
# Paul Seyfert <pseyfert@mathphys.fsk.uni-heidelberg.de>, 2010.
|
||||
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009, 2010, 2011.
|
||||
#
|
||||
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: 2010-10-10 03:14+0000\n"
|
||||
"PO-Revision-Date: 2010-10-10 20:23+0200\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2011-01-01 23:21+0100\n"
|
||||
"PO-Revision-Date: 2011-01-01 16:25+0100\n"
|
||||
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
|
||||
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@ -105,12 +107,8 @@ msgstr ""
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "Liste der Kennungen der Desktop-Dateien für bevorzugte Anwendungen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
msgid "Overview workspace view mode"
|
||||
msgstr "Modus des Arbeitsflächen-Überblicks"
|
||||
|
||||
# Hier blicke ich überhaupt nicht durch.
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
"used for gst-launch. The pipeline should have an unconnected sink pad where "
|
||||
@ -130,22 +128,22 @@ msgstr ""
|
||||
"Das kann zum Senden der Ausgabe über shout2send an einen Icecast-Server oder "
|
||||
"Ähnliches verwendet werden. Falls nicht (oder auf einen leeren Wert) "
|
||||
"gesetzt, so wird die vorgegebene Weiterleitung verwendet, welche derzeit "
|
||||
"»videorate ! theoraenc ! oggmux« lautet und die Aufnahme im Ogg-Theora-Format "
|
||||
"speichert."
|
||||
"»videorate ! theoraenc ! oggmux« lautet und die Aufnahme im Ogg-Theora-"
|
||||
"Format speichert."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr "Datum in der Uhr anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Wochentag im Kalender anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Zeit sekundengenau anzeigen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid ""
|
||||
"The applications corresponding to these identifiers will be displayed in the "
|
||||
"favorites area."
|
||||
@ -153,7 +151,7 @@ msgstr ""
|
||||
"Programme, welche auf diese Bezeichner zutreffen, werden im Favoriten-"
|
||||
"Bereich angezeigt."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid ""
|
||||
"The filename for recorded screencasts will be a unique filename based on the "
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
@ -164,7 +162,7 @@ msgstr ""
|
||||
"Dateiname sollte geändert werden, wenn Sie in einem anderen Containerformat "
|
||||
"aufnehmen."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
@ -173,19 +171,11 @@ msgstr ""
|
||||
"der GNOME-Shell aufgezeichnet werden soll, in Einzelbildern pro Sekunde."
|
||||
|
||||
# hmm Enkodieren oder Kodieren?
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "Die GStreamer-Weiterleitung zur Enkodierung des Screencasts"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid ""
|
||||
"The selected workspace view mode in the overview. Supported values are "
|
||||
"\"single\" and \"grid\"."
|
||||
msgstr ""
|
||||
"Die gewählte Ansicht des Arbeitsflächen-Überblicks. Mögliche Werte sind "
|
||||
"»single« (einfach) und »grid« (Raster)."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
@ -198,7 +188,7 @@ msgstr ""
|
||||
"deaktivieren, um Ihre Privatsphäre zu schützen. Bitte beachten Sie, dass "
|
||||
"bereits gespeicherte Daten hiervon nicht beeinflusst werden."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid ""
|
||||
"This key specifies the format used by the panel clock when the format key is "
|
||||
"set to \"custom\". You can use conversion specifiers understood by strftime"
|
||||
@ -211,7 +201,7 @@ msgstr ""
|
||||
"Format zu erhalten. Weitere Informationen finden Sie in der Handbuchseite zu "
|
||||
"strftime()."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid ""
|
||||
"This key specifies the hour format used by the panel clock. Possible values "
|
||||
"are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to \"unix\", "
|
||||
@ -221,18 +211,18 @@ msgid ""
|
||||
"the show_date and show_seconds keys are ignored."
|
||||
msgstr ""
|
||||
"Dieser Schlüssel legt das Stundenformat für die Anzeige der Panel-Uhr fest. "
|
||||
"Mögliche Werte sind »12-hour«, »24-hour«, »unix« und »custom«. Wenn Sie »unix« "
|
||||
"verwenden, zeigt die Uhr die Sekunden seit Epoch, dem Beginn der Unix-"
|
||||
"Mögliche Werte sind »12-hour«, »24-hour«, »unix« und »custom«. Wenn Sie "
|
||||
"»unix« verwenden, zeigt die Uhr die Sekunden seit Epoch, dem Beginn der Unix-"
|
||||
"Zeitrechnung am 1. Januar 1970 an. Wenn Sie »custom« (benutzerdefiniert) "
|
||||
"verwenden, zeigt die Uhr die Zeit gemäß dem im Schlüssel »custom_format« "
|
||||
"angegebenen Format an. Beachten Sie, dass bei »unix« oder »custom« die "
|
||||
"Schlüssel »show_date« und »show_seconds« ignoriert werden."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:26
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "UUIDs der zu deaktivierenden Erweiterungen"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:27
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr "Legt fest, ob der Status der Anwendungsnutzung erfasst werden soll"
|
||||
|
||||
@ -266,14 +256,14 @@ msgid ""
|
||||
msgstr ""
|
||||
"Legt die Position des vergrößerten Bildes der Maus innerhalb der "
|
||||
"vergrößerten Ansicht fest, und wie die Maus auf die Mausbewegungen des "
|
||||
"Systems reagiert. Folgende Werte sind möglich: »none« - keine Mausverfolgung; "
|
||||
"»centered« - das Mausbild wird in der Mitte des vergrößerten Bereiches "
|
||||
"dargestellt (welche auch den Zeiger der Systemmaus darstellt), wobei der "
|
||||
"vergrößerte Inhalt gerollt wird, sobald die Systemmaus bewegt wird; "
|
||||
"»proportional« - die Position der vergrößerten Maus im vergrößerten Bereich "
|
||||
"ist proportional zur Position der Systemmaus auf dem Bildschirm; »push« - "
|
||||
"wenn die vergrößerte Maus einen der Ränder des vergrößerten Bereichs "
|
||||
"berührt, wird der entsprechende Inhalt in die Ansicht geschoben."
|
||||
"Systems reagiert. Folgende Werte sind möglich: »none« - keine "
|
||||
"Mausverfolgung; »centered« - das Mausbild wird in der Mitte des vergrößerten "
|
||||
"Bereiches dargestellt (welche auch den Zeiger der Systemmaus darstellt), "
|
||||
"wobei der vergrößerte Inhalt gerollt wird, sobald die Systemmaus bewegt "
|
||||
"wird; »proportional« - die Position der vergrößerten Maus im vergrößerten "
|
||||
"Bereich ist proportional zur Position der Systemmaus auf dem Bildschirm; "
|
||||
"»push« - wenn die vergrößerte Maus einen der Ränder des vergrößerten "
|
||||
"Bereichs berührt, wird der entsprechende Inhalt in die Ansicht geschoben."
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:5
|
||||
msgid ""
|
||||
@ -422,62 +412,46 @@ msgstr "_12-Stunden-Format"
|
||||
msgid "_24 hour format"
|
||||
msgstr "_24-Stunden-Format"
|
||||
|
||||
#. **** Applications ****
|
||||
#: ../js/ui/appDisplay.js:316 ../js/ui/dash.js:778
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:155
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: ../js/ui/appDisplay.js:236
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "ANWENDUNGEN"
|
||||
|
||||
#: ../js/ui/appDisplay.js:348
|
||||
#: ../js/ui/appDisplay.js:266
|
||||
msgid "PREFERENCES"
|
||||
msgstr "EINSTELLUNGEN"
|
||||
|
||||
#: ../js/ui/appDisplay.js:648
|
||||
#: ../js/ui/appDisplay.js:563
|
||||
msgid "New Window"
|
||||
msgstr "Neues Fenster"
|
||||
|
||||
#: ../js/ui/appDisplay.js:652
|
||||
#: ../js/ui/appDisplay.js:567
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "Aus Favoriten entfernen"
|
||||
|
||||
#: ../js/ui/appDisplay.js:653
|
||||
#: ../js/ui/appDisplay.js:568
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Zu Favoriten hinzufügen"
|
||||
|
||||
#: ../js/ui/appDisplay.js:830
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Hier ablegen, um zu Favoriten hinzuzufügen"
|
||||
|
||||
#: ../js/ui/appFavorites.js:88
|
||||
#: ../js/ui/appFavorites.js:91
|
||||
#, c-format
|
||||
msgid "%s has been added to your favorites."
|
||||
msgstr "%s wurde zu Ihren Favoriten hinzugefügt"
|
||||
|
||||
#: ../js/ui/appFavorites.js:107
|
||||
#: ../js/ui/appFavorites.js:122
|
||||
#, c-format
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s wurde aus Ihren Favoriten entfernt"
|
||||
|
||||
#: ../js/ui/dash.js:142
|
||||
msgid "Find"
|
||||
msgstr "Suchen"
|
||||
#: ../js/ui/dash.js:27
|
||||
msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
|
||||
#: ../js/ui/dash.js:473
|
||||
msgid "Searching..."
|
||||
msgstr "Suche läuft …"
|
||||
|
||||
#: ../js/ui/dash.js:487
|
||||
msgid "No matching results."
|
||||
msgstr "Keine passenden Ergebnisse."
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:554
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "ORTE UND GERÄTE"
|
||||
|
||||
#. **** Documents ****
|
||||
#: ../js/ui/dash.js:804 ../js/ui/docDisplay.js:494
|
||||
#: ../js/ui/docDisplay.js:494
|
||||
msgid "RECENT ITEMS"
|
||||
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
|
||||
|
||||
@ -489,7 +463,9 @@ msgstr "Keine Erweiterungen installiert"
|
||||
msgid "Enabled"
|
||||
msgstr "Aktiviert"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:591
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:591 ../src/gvc/gvc-mixer-control.c:1087
|
||||
msgid "Disabled"
|
||||
msgstr "Deaktiviert"
|
||||
|
||||
@ -509,63 +485,71 @@ msgstr "Quelle zeigen"
|
||||
msgid "Web Page"
|
||||
msgstr "Webseite"
|
||||
|
||||
#: ../js/ui/overview.js:160
|
||||
#: ../js/ui/overview.js:96
|
||||
msgid "Undo"
|
||||
msgstr "Rückgängig"
|
||||
|
||||
#: ../js/ui/overview.js:158
|
||||
msgid "Windows"
|
||||
msgstr "Fenster"
|
||||
|
||||
#: ../js/ui/overview.js:161
|
||||
msgid "Applications"
|
||||
msgstr "Anwendungen"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:468
|
||||
#: ../js/ui/panel.js:474
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "%s beenden"
|
||||
|
||||
#: ../js/ui/panel.js:493
|
||||
#: ../js/ui/panel.js:499
|
||||
msgid "Preferences"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/panel.js:579
|
||||
#: ../js/ui/panel.js:585
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a, %e. %b, %R:%S"
|
||||
|
||||
#: ../js/ui/panel.js:580
|
||||
#: ../js/ui/panel.js:586
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a, %e. %b, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/panel.js:584
|
||||
#: ../js/ui/panel.js:590
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/panel.js:585
|
||||
#: ../js/ui/panel.js:591
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/panel.js:592
|
||||
#: ../js/ui/panel.js:598
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a, %e. %b, %H:%M:%S"
|
||||
|
||||
#: ../js/ui/panel.js:593
|
||||
#: ../js/ui/panel.js:599
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a, %e. %b, %H:%M"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/panel.js:597
|
||||
#: ../js/ui/panel.js:603
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %H:%M:%S"
|
||||
|
||||
#: ../js/ui/panel.js:598
|
||||
#: ../js/ui/panel.js:604
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %H:%M"
|
||||
|
||||
#. 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:743
|
||||
#: ../js/ui/panel.js:749
|
||||
msgid "Activities"
|
||||
msgstr "Aktivitäten"
|
||||
|
||||
@ -582,6 +566,10 @@ msgstr "Erneut versuchen"
|
||||
msgid "Connect to..."
|
||||
msgstr "Verbinden mit …"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:558
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "ORTE UND GERÄTE"
|
||||
|
||||
#. Translators: this MUST be either "toggle-switch-us"
|
||||
#. (for toggle switches containing the English words
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
@ -600,42 +588,332 @@ msgstr "Bitte geben Sie einen Befehl ein:"
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "Ausführung von »%s« ist gescheitert:"
|
||||
|
||||
#: ../js/ui/statusMenu.js:91
|
||||
#: ../js/ui/statusMenu.js:101
|
||||
msgid "Available"
|
||||
msgstr "Verfügbar"
|
||||
|
||||
#: ../js/ui/statusMenu.js:95
|
||||
#: ../js/ui/statusMenu.js:106
|
||||
msgid "Busy"
|
||||
msgstr "Beschäftigt"
|
||||
|
||||
#: ../js/ui/statusMenu.js:99
|
||||
msgid "Invisible"
|
||||
msgstr "Unsichtbar"
|
||||
#: ../js/ui/statusMenu.js:114
|
||||
msgid "My Account"
|
||||
msgstr "Eigenes Konto"
|
||||
|
||||
#: ../js/ui/statusMenu.js:106
|
||||
msgid "Account Information..."
|
||||
msgstr "Benutzerinformationen …"
|
||||
#: ../js/ui/statusMenu.js:118
|
||||
msgid "System Settings"
|
||||
msgstr "Systemeinstellungen"
|
||||
|
||||
#: ../js/ui/statusMenu.js:110
|
||||
msgid "System Settings..."
|
||||
msgstr "Systemeinstellungen …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:117
|
||||
#: ../js/ui/statusMenu.js:125
|
||||
msgid "Lock Screen"
|
||||
msgstr "Bildschirm sperren"
|
||||
|
||||
#: ../js/ui/statusMenu.js:121
|
||||
#: ../js/ui/statusMenu.js:129
|
||||
msgid "Switch User"
|
||||
msgstr "Benutzer wechseln"
|
||||
|
||||
#: ../js/ui/statusMenu.js:126
|
||||
#: ../js/ui/statusMenu.js:134
|
||||
msgid "Log Out..."
|
||||
msgstr "Abmelden …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:130
|
||||
#: ../js/ui/statusMenu.js:141
|
||||
msgid "Suspend..."
|
||||
msgstr "Bereitschaft …"
|
||||
|
||||
#: ../js/ui/statusMenu.js:145
|
||||
msgid "Shut Down..."
|
||||
msgstr "Ausschalten …"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:82
|
||||
msgid "Zoom"
|
||||
msgstr "Vergrößern"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:88
|
||||
msgid "Screen Reader"
|
||||
msgstr "Bildschirmleser"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:91
|
||||
msgid "Screen Keyboard"
|
||||
msgstr "Bildschirmtastatur"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:94
|
||||
msgid "Visual Alerts"
|
||||
msgstr "Visuelle Warnungen"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:97
|
||||
msgid "Sticky Keys"
|
||||
msgstr "Klebrige Tasten"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:100
|
||||
msgid "Slow Keys"
|
||||
msgstr "Tastenverzögerung"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:103
|
||||
msgid "Bounce Keys"
|
||||
msgstr "Springende Tasten"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:106
|
||||
msgid "Mouse Keys"
|
||||
msgstr "Maustasten"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:110
|
||||
msgid "Universal Access Settings"
|
||||
msgstr "Einstellungen zur Barrierefreiheit"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:163
|
||||
msgid "High Contrast"
|
||||
msgstr "Hoher Kontrast"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:205
|
||||
msgid "Large Text"
|
||||
msgstr "Große Schrift"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:42 ../js/ui/status/bluetooth.js:234
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:55
|
||||
msgid "Visibility"
|
||||
msgstr "Sichtbarkeit"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:69
|
||||
msgid "Send Files to Device..."
|
||||
msgstr "Dateien an Gerät senden …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:70
|
||||
msgid "Setup a New Device..."
|
||||
msgstr "Ein neues Gerät einrichten …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:94
|
||||
msgid "Bluetooth Settings"
|
||||
msgstr "Bluetooth-Einstellungen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:185
|
||||
msgid "Connection"
|
||||
msgstr "Verbindung"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:221
|
||||
msgid "Send Files..."
|
||||
msgstr "Dateien senden …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:226
|
||||
msgid "Browse Files..."
|
||||
msgstr "Dateien durchsuchen …"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:235
|
||||
msgid "Error browsing device"
|
||||
msgstr "Fehler beim Durchsuchen des Geräts"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:236
|
||||
#, c-format
|
||||
msgid "The requested device cannot be browsed, error is '%s'"
|
||||
msgstr ""
|
||||
"Das angesprochene Gerät kann nicht durchsucht werden. Der Fehler lautet »%s«"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:244
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Tastatureinstellungen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:249
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Maus-Einstellungen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:256 ../js/ui/status/volume.js:62
|
||||
msgid "Sound Settings"
|
||||
msgstr "Klangeinstellungen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:327 ../js/ui/status/bluetooth.js:361
|
||||
#: ../js/ui/status/bluetooth.js:401 ../js/ui/status/bluetooth.js:434
|
||||
msgid "Bluetooth Agent"
|
||||
msgstr "Bluetooth-Agent"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:362
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Legitimierungsanfrage von %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:368
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Gerät »%s« bittet um Zugriff auf den Dienst »%s«"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:370
|
||||
msgid "Always grant access"
|
||||
msgstr "Immer Zugriff gewähren"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:371
|
||||
msgid "Grant this time only"
|
||||
msgstr "Nur dieses Mal gewähren"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:372
|
||||
msgid "Reject"
|
||||
msgstr "Abweisen"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:402
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Koppelungsbestätigung für %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:408 ../js/ui/status/bluetooth.js:442
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr "Gerät »%s« möchte mit diesem Rechner gekoppelt werden"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:409
|
||||
#, c-format
|
||||
msgid "Please confirm whether the PIN '%s' matches the one on the device."
|
||||
msgstr ""
|
||||
"Bitte bestätigen Sie, ob die PIN »%s« mit der des Gerätes übereinstimmt."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:411
|
||||
msgid "Matches"
|
||||
msgstr "Stimmt überein"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:412
|
||||
msgid "Does not match"
|
||||
msgstr "Stimmt nicht überein"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:435
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr "Koppelungsanfrage für %s"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:443
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr "Bitte geben Sie die auf dem Gerät angezeigte PIN ein."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:459
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:460
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: ../js/ui/status/power.js:85
|
||||
msgid "Power Settings"
|
||||
msgstr "Energieeinstellungen"
|
||||
|
||||
#: ../js/ui/status/power.js:112
|
||||
#, c-format
|
||||
msgid "%d hour remaining"
|
||||
msgid_plural "%d hours remaining"
|
||||
msgstr[0] "%d Stunde verbleibend"
|
||||
msgstr[1] "%d Stunden verbleibend"
|
||||
|
||||
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
|
||||
#: ../js/ui/status/power.js:115
|
||||
#, c-format
|
||||
msgid "%d %s %d %s remaining"
|
||||
msgstr "%d %s %d %s verbleibend"
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "Stunde"
|
||||
msgstr[1] "Stunden"
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] "Minute"
|
||||
msgstr[1] "Minuten"
|
||||
|
||||
#: ../js/ui/status/power.js:120
|
||||
#, c-format
|
||||
msgid "%d minute remaining"
|
||||
msgid_plural "%d minutes remaining"
|
||||
msgstr[0] "%d Minute verbleibend"
|
||||
msgstr[1] "%d Minuten verbleibend"
|
||||
|
||||
#: ../js/ui/status/power.js:237
|
||||
msgid "AC adapter"
|
||||
msgstr "Netzteil"
|
||||
|
||||
#: ../js/ui/status/power.js:239
|
||||
msgid "Laptop battery"
|
||||
msgstr "Laptop-Akku"
|
||||
|
||||
#: ../js/ui/status/power.js:241
|
||||
msgid "UPS"
|
||||
msgstr "Notstromversorgung"
|
||||
|
||||
#: ../js/ui/status/power.js:243
|
||||
msgid "Monitor"
|
||||
msgstr "Bildschirm"
|
||||
|
||||
#: ../js/ui/status/power.js:245
|
||||
msgid "Mouse"
|
||||
msgstr "Maus"
|
||||
|
||||
#: ../js/ui/status/power.js:247
|
||||
msgid "Keyboard"
|
||||
msgstr "Tastatur"
|
||||
|
||||
#: ../js/ui/status/power.js:249
|
||||
msgid "PDA"
|
||||
msgstr "PDA"
|
||||
|
||||
#: ../js/ui/status/power.js:251
|
||||
msgid "Cell phone"
|
||||
msgstr "Mobiltelefon"
|
||||
|
||||
#: ../js/ui/status/power.js:253
|
||||
msgid "Media player"
|
||||
msgstr "Medienwiedergabegerät"
|
||||
|
||||
#: ../js/ui/status/power.js:255
|
||||
msgid "Tablet"
|
||||
msgstr "Tablet"
|
||||
|
||||
#: ../js/ui/status/power.js:257
|
||||
msgid "Computer"
|
||||
msgstr "Rechner"
|
||||
|
||||
#: ../js/ui/status/power.js:259 ../src/shell-app-system.c:1012
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: ../js/ui/status/volume.js:41
|
||||
msgid "Volume"
|
||||
msgstr "Lautstärke"
|
||||
|
||||
#: ../js/ui/status/volume.js:54
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#: ../js/ui/telepathyClient.js:560
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s ist angemeldet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:565
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s ist abgemeldet."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:568
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "»%s« ist abwesend."
|
||||
|
||||
#: ../js/ui/telepathyClient.js:571
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s ist beschäftigt."
|
||||
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:665
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "Gesendet am %A um %X "
|
||||
|
||||
#: ../js/ui/viewSelector.js:26
|
||||
msgid "Search your computer"
|
||||
msgstr "Ihren Rechner durchsuchen"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:43
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
@ -646,60 +924,82 @@ msgstr "Start von %s ist abgeschlossen"
|
||||
msgid "'%s' is ready"
|
||||
msgstr "»%s« ist bereit"
|
||||
|
||||
#: ../js/ui/workspacesView.js:230
|
||||
#: ../js/ui/workspacesView.js:244
|
||||
msgid ""
|
||||
"Can't add a new workspace because maximum workspaces limit has been reached."
|
||||
msgstr ""
|
||||
"Es kann keine weitere Arbeitsfläche hinzugefügt werden, weil das Maximum an "
|
||||
"Arbeitsflächen erreicht worden ist."
|
||||
|
||||
#: ../js/ui/workspacesView.js:247
|
||||
#: ../js/ui/workspacesView.js:260
|
||||
msgid "Can't remove the first workspace."
|
||||
msgstr "Die erste Arbeitsfläche kann nicht entfernt werden."
|
||||
|
||||
#: ../src/shell-global.c:1196
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1094
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "%u Ausgang"
|
||||
msgstr[1] "%u Ausgänge"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1104
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u Eingang"
|
||||
msgstr[1] "%u Eingänge"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:1402
|
||||
msgid "System Sounds"
|
||||
msgstr "Systemklänge"
|
||||
|
||||
#: ../src/shell-global.c:1156
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Vor weniger als einer Minute"
|
||||
|
||||
#: ../src/shell-global.c:1200
|
||||
#: ../src/shell-global.c:1160
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "Vor %d Minute"
|
||||
msgstr[1] "Vor %d Minuten"
|
||||
|
||||
#: ../src/shell-global.c:1205
|
||||
#: ../src/shell-global.c:1165
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "Vor %d Stunde"
|
||||
msgstr[1] "Vor %d Stunden"
|
||||
|
||||
#: ../src/shell-global.c:1210
|
||||
#: ../src/shell-global.c:1170
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "Vor %d Tag"
|
||||
msgstr[1] "Vor %d Tagen"
|
||||
|
||||
#: ../src/shell-global.c:1215
|
||||
#: ../src/shell-global.c:1175
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "Vor %d Woche"
|
||||
msgstr[1] "Vor %d Wochen"
|
||||
|
||||
#: ../src/shell-uri-util.c:89
|
||||
#: ../src/shell-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "Persönlicher Ordner"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-uri-util.c:104
|
||||
#: ../src/shell-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "Dateisystem"
|
||||
|
||||
#: ../src/shell-uri-util.c:250
|
||||
#: ../src/shell-util.c:250
|
||||
msgid "Search"
|
||||
msgstr "Suchen"
|
||||
|
||||
@ -708,11 +1008,42 @@ msgstr "Suchen"
|
||||
#. * example, "Trash: some-directory". It means that the
|
||||
#. * directory called "some-directory" is in the trash.
|
||||
#.
|
||||
#: ../src/shell-uri-util.c:300
|
||||
#: ../src/shell-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "What's using power..."
|
||||
#~ msgstr "Energieverbraucher …"
|
||||
|
||||
#~ msgid "Overview workspace view mode"
|
||||
#~ msgstr "Modus des Arbeitsflächen-Überblicks"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The selected workspace view mode in the overview. Supported values are "
|
||||
#~ "\"single\" and \"grid\"."
|
||||
#~ msgstr ""
|
||||
#~ "Die gewählte Ansicht des Arbeitsflächen-Überblicks. Mögliche Werte sind "
|
||||
#~ "»single« (einfach) und »grid« (Raster)."
|
||||
|
||||
#~ msgid "Drag here to add favorites"
|
||||
#~ msgstr "Hier ablegen, um zu Favoriten hinzuzufügen"
|
||||
|
||||
#~ msgid "Find"
|
||||
#~ msgstr "Suchen"
|
||||
|
||||
#~ msgid "Searching..."
|
||||
#~ msgstr "Suche läuft …"
|
||||
|
||||
#~ msgid "No matching results."
|
||||
#~ msgstr "Keine passenden Ergebnisse."
|
||||
|
||||
#~ msgid "Invisible"
|
||||
#~ msgstr "Unsichtbar"
|
||||
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "Benutzerinformationen …"
|
||||
|
||||
#~ msgid "ON"
|
||||
#~ msgstr "EIN"
|
||||
|
||||
@ -725,9 +1056,6 @@ msgstr "%1$s: %2$s"
|
||||
#~ msgid "%H:%M"
|
||||
#~ msgstr "%H:%M"
|
||||
|
||||
#~ msgid "Applications"
|
||||
#~ msgstr "Anwendungen"
|
||||
|
||||
#~ msgid "Recent Documents"
|
||||
#~ msgstr "Zuletzt geöffnete Dokumente"
|
||||
|
||||
@ -746,9 +1074,6 @@ msgstr "%1$s: %2$s"
|
||||
#~ msgid "SEARCH RESULTS"
|
||||
#~ msgstr "SUCHERGEBNISSE"
|
||||
|
||||
#~ msgid "Unknown"
|
||||
#~ msgstr "Unbekannt"
|
||||
|
||||
#~ msgid "Can't lock screen: %s"
|
||||
#~ msgstr "Bildschirm kann nicht gesperrt werden: %s"
|
||||
|
||||
|
702
po/et.po
@ -1,21 +1,26 @@
|
||||
# GNOME kesta eesti keele tõlge.
|
||||
# Estonian translation for gnome-shell.
|
||||
# Copyright (C) 2010 The Gnome Project
|
||||
#
|
||||
# Copyright (C) 2010, 2011 The Gnome Project
|
||||
#
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# Mattias Põldaru <mahfiaz gmail com>, 2010.
|
||||
#
|
||||
# Mattias Põldaru <mahfiaz gmail com>, 2010, 2011.
|
||||
# Ivar Smolin <okul linux ee>, 2011.
|
||||
#
|
||||
msgid ""
|
||||
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"
|
||||
"POT-Creation-Date: 2010-10-16 19:24+0000\n"
|
||||
"PO-Revision-Date: 2010-10-17 17:16+0300\n"
|
||||
"Last-Translator: Mattias Põldaru <mahfiaz gmail com>\n"
|
||||
"POT-Creation-Date: 2011-02-04 22:28+0000\n"
|
||||
"PO-Revision-Date: 2011-02-06 08:43+0200\n"
|
||||
"Last-Translator: Ivar Smolin <okul@linux.ee>\n"
|
||||
"Language-Team: Estonian <gnome-et@linux.ee>\n"
|
||||
"Language: et\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: et\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-Language: Estonian\n"
|
||||
"X-Poedit-Country: Estonia\n"
|
||||
@ -26,12 +31,6 @@ msgstr "GNOME kest"
|
||||
msgid "Window management and application launching"
|
||||
msgstr "Aknahaldur ja rakenduste käivitaja"
|
||||
|
||||
msgid "Clock"
|
||||
msgstr "Kell"
|
||||
|
||||
msgid "Customize the panel clock"
|
||||
msgstr "Paneelikella kohandamine"
|
||||
|
||||
msgid ""
|
||||
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
|
||||
"dialog."
|
||||
@ -39,15 +38,12 @@ msgstr ""
|
||||
"Lubab ligipääsu sisemistele silumise ja monitoorimise tööriistadele Alt-F2 "
|
||||
"dialoogi kaudu."
|
||||
|
||||
msgid "Custom format of the clock"
|
||||
msgstr "Kellaaaja kohandatud vorming"
|
||||
|
||||
msgid "Enable internal tools useful for developers and testers from Alt-F2"
|
||||
msgstr ""
|
||||
"Arendajate ja testijate jaoks sisemiste tööriistade lubamine Alt-F2 alt"
|
||||
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr "Faililaiend, mida kasutatakse ekraanivideo salvestamisel"
|
||||
msgstr "Ekraanivideo salvestamisel kasutatav faililaiend"
|
||||
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr "Ekraanivideo lindistamisel kasutatav kaadrikiirus."
|
||||
@ -56,36 +52,25 @@ msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should not be loaded."
|
||||
msgstr ""
|
||||
"GNOME Shelli laiendustel on omadus UUID. Selle võtmega määratakse "
|
||||
"laiendused, mida ei peaks laaditama."
|
||||
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr "Käsudialoogi (Alt-F2) ajalugu"
|
||||
|
||||
msgid "Hour format"
|
||||
msgstr "Tundide vorming"
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr "Kui määratud, siis kuvatakse kellaaja kõrval ka kuupäeva."
|
||||
|
||||
msgid ""
|
||||
"If true and format is either \"12-hour\" or \"24-hour\", display date in the "
|
||||
"clock, in addition to time."
|
||||
msgstr ""
|
||||
"Kui tõene ja vorming on kas \"12-tundi\" või \"24-tundi\", kuvatakse "
|
||||
"kellaaja kõrval ka kuupäeva."
|
||||
|
||||
msgid ""
|
||||
"If true and format is either \"12-hour\" or \"24-hour\", display seconds in "
|
||||
"time."
|
||||
msgstr ""
|
||||
"Kui tõene ja vorming on kas \"12-tundi\" või \"24-tundi\", kuvatakse "
|
||||
"kellaaega koos sekunditega."
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr "Kui määratud, siis kuvatakse aja juures ka sekundeid."
|
||||
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "Kui tõene, kuvatakse kalendris kuupäeva ISO nädalate järgi."
|
||||
msgstr "Kui määratud, kuvatakse kalendris kuupäeva ISO nädalavormingus."
|
||||
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "Lemmikrakenduste töölauafailide ID-de loend"
|
||||
|
||||
msgid "Overview workspace view mode"
|
||||
msgstr ""
|
||||
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
"used for gst-launch. The pipeline should have an unconnected sink pad where "
|
||||
@ -94,14 +79,16 @@ msgid ""
|
||||
"pipeline can also take care of its own output - this might be used to send "
|
||||
"the output to an icecast server via shout2send or similar. When unset or set "
|
||||
"to an empty value, the default pipeline will be used. This is currently "
|
||||
"'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
|
||||
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
|
||||
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
|
||||
"at the optimal thread count on the system."
|
||||
msgstr ""
|
||||
|
||||
msgid "Show date in clock"
|
||||
msgstr "Kell näitab kuupäeva"
|
||||
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "Kalendris näidatakse nädala kuupäeva"
|
||||
msgstr "Kalendris näidatakse kuupäeva nädalavormingus"
|
||||
|
||||
msgid "Show time with seconds"
|
||||
msgstr "Kellaaega näidatakse sekunditega"
|
||||
@ -116,6 +103,10 @@ msgid ""
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
"a different container format."
|
||||
msgstr ""
|
||||
"Ekraanivideo jaoks kasutatav failinimi on unikaalne, see sisaldab "
|
||||
"salvestamise kuupäeva ja ka käesoleva võtmega määratud laiendit. Mõnda teise "
|
||||
"konteinervormingusse salvestades tuleks ka sellele vormingule vastav laiend "
|
||||
"määrata."
|
||||
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
@ -125,11 +116,6 @@ msgstr ""
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The selected workspace view mode in the overview. Supported values are "
|
||||
"\"single\" and \"grid\"."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
@ -137,28 +123,15 @@ msgid ""
|
||||
"remove already saved data."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"This key specifies the format used by the panel clock when the format key is "
|
||||
"set to \"custom\". You can use conversion specifiers understood by strftime"
|
||||
"() to obtain a specific format. See the strftime() manual for more "
|
||||
"information."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"This key specifies the hour format used by the panel clock. Possible values "
|
||||
"are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to \"unix\", "
|
||||
"the clock will display time in seconds since Epoch, i.e. 1970-01-01. If set "
|
||||
"to \"custom\", the clock will display time according to the format specified "
|
||||
"in the custom_format key. Note that if set to either \"unix\" or \"custom\", "
|
||||
"the show_date and show_seconds keys are ignored."
|
||||
msgstr ""
|
||||
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "Keelatavate laienduste UUID-d"
|
||||
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr ""
|
||||
|
||||
msgid "disabled OpenSearch providers"
|
||||
msgstr ""
|
||||
|
||||
msgid "Clip the crosshairs at the center"
|
||||
msgstr "Niitristi keskel on auk"
|
||||
|
||||
@ -269,28 +242,25 @@ msgstr ""
|
||||
msgid "Width of the vertical and horizontal lines that make up the crosshairs."
|
||||
msgstr "Niitristi moodustavate püst- ja rõhtjoone laius"
|
||||
|
||||
msgid "Clock Format"
|
||||
msgstr "Kella formaat"
|
||||
msgid "Command not found"
|
||||
msgstr "Käsku ei leitud"
|
||||
|
||||
msgid "Clock Preferences"
|
||||
msgstr "Kella eelistused"
|
||||
#. Replace "Error invoking GLib.shell_parse_argv: " with
|
||||
#. something nicer
|
||||
msgid "Could not parse command:"
|
||||
msgstr "Käsku pole võimalik analüüsida:"
|
||||
|
||||
msgid "Panel Display"
|
||||
msgstr "Paneelikuva"
|
||||
msgid "No such application"
|
||||
msgstr "Sellist rakendust ei ole"
|
||||
|
||||
msgid "Show seco_nds"
|
||||
msgstr "_Sekundeid näidatakse"
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "'%s' käivitamine nurjus:"
|
||||
|
||||
msgid "Show the _date"
|
||||
msgstr "_Kuupäeva näidatakse"
|
||||
#. Translators: Filter to display all applications
|
||||
msgid "All"
|
||||
msgstr "Kõik"
|
||||
|
||||
msgid "_12 hour format"
|
||||
msgstr "_12 tunni vorming"
|
||||
|
||||
msgid "_24 hour format"
|
||||
msgstr "_24 tunni vorming"
|
||||
|
||||
#. **** Applications ****
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "Rakendused"
|
||||
|
||||
@ -306,9 +276,6 @@ msgstr "Eemalda lemmikutest"
|
||||
msgid "Add to Favorites"
|
||||
msgstr "Lisa lemmikutesse"
|
||||
|
||||
msgid "Drag here to add favorites"
|
||||
msgstr "Lemmikute lisamiseks lohista need siia"
|
||||
|
||||
#, c-format
|
||||
msgid "%s has been added to your favorites."
|
||||
msgstr "%s lisati lemmikutesse."
|
||||
@ -317,56 +284,136 @@ msgstr "%s lisati lemmikutesse."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s eemaldati lemmikutest."
|
||||
|
||||
msgid "Find"
|
||||
msgstr "Otsi"
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "Kogu päeva"
|
||||
|
||||
msgid "Searching..."
|
||||
msgstr "Otsimine..."
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
msgid "No matching results."
|
||||
msgstr "Tulemused puuduvad."
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
|
||||
#. **** Places ****
|
||||
#. Translators: This is in the sense of locations for documents,
|
||||
#. network locations, etc.
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "Asukohad ja seadmed"
|
||||
#. Translators: Calendar grid abbreviation for Sunday.
|
||||
#. *
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "P"
|
||||
|
||||
#. **** Documents ****
|
||||
msgid "RECENT ITEMS"
|
||||
msgstr "Hiljutised dokumendid"
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "E"
|
||||
|
||||
msgid "No extensions installed"
|
||||
msgstr "Ühtegi laiendust pole paigaldatud"
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "T"
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "Lubatud"
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "K"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Keelatud"
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "N"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "Viga"
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "R"
|
||||
|
||||
msgid "Out of date"
|
||||
msgstr "Pole värske"
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "L"
|
||||
|
||||
msgid "View Source"
|
||||
msgstr "Kuva lähtekoodi"
|
||||
#. Translators: Event list abbreviation for Sunday.
|
||||
#. *
|
||||
#. * NOTE: These list abbreviations are normally not shown together
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "P"
|
||||
|
||||
msgid "Web Page"
|
||||
msgstr "Veebileht"
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "E"
|
||||
|
||||
msgid "Undo"
|
||||
msgstr "Võta tagasi"
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "T"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "Lõpeta %s"
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "K"
|
||||
|
||||
msgid "Preferences"
|
||||
msgstr "Eelistused"
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "N"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "R"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "L"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %d. %B"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %d. %B %Y"
|
||||
|
||||
msgid "Today"
|
||||
msgstr "Täna"
|
||||
|
||||
msgid "Tomorrow"
|
||||
msgstr "Homme"
|
||||
|
||||
msgid "This week"
|
||||
msgstr "Käesolev nädal"
|
||||
|
||||
msgid "Next week"
|
||||
msgstr "Järgmine nädal"
|
||||
|
||||
msgid "Remove"
|
||||
msgstr "Eemalda"
|
||||
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Kuupäeva ja kellaaja sätted"
|
||||
|
||||
msgid "Open Calendar"
|
||||
msgstr "Ava kalender"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
@ -398,7 +445,111 @@ msgid "%a %l:%M:%S %p"
|
||||
msgstr "%A, %l:%M:%S %p"
|
||||
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%A, %l:%M %p"
|
||||
msgstr "%a, %l:%M %p"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A, %d. %B %Y"
|
||||
|
||||
msgid "RECENT ITEMS"
|
||||
msgstr "Hiljutised dokumendid"
|
||||
|
||||
#, c-format
|
||||
msgid "Log Out %s"
|
||||
msgstr "Logi %s välja"
|
||||
|
||||
msgid "Log Out"
|
||||
msgstr "Logi välja"
|
||||
|
||||
msgid "Click Log Out to quit these applications and log out of the system."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%s will be logged out automatically in %d seconds."
|
||||
msgstr "%s logitakse %d sekundi pärast automaatselt välja."
|
||||
|
||||
#, c-format
|
||||
msgid "You will be logged out automatically in %d seconds."
|
||||
msgstr "Sind logitakse %d sekundi pärast automaatselt välja."
|
||||
|
||||
msgid "Logging out of the system."
|
||||
msgstr "Süsteemist väljalogimine"
|
||||
|
||||
msgid "Shut Down"
|
||||
msgstr "Lülita välja"
|
||||
|
||||
msgid "Click Shut Down to quit these applications and shut down the system."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "The system will shut down automatically in %d seconds."
|
||||
msgstr "%d sekundi pärast lülitub süsteem automaatselt välja."
|
||||
|
||||
msgid "Shutting down the system."
|
||||
msgstr "Süsteemi väljalülitamine."
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "Taaskäivita"
|
||||
|
||||
msgid "Click Restart to quit these applications and restart the system."
|
||||
msgstr ""
|
||||
"Nende rakenduste sulgemiseks ja süsteemi taaskäivitamiseks klõpsa "
|
||||
"\"Taaskäivita\"."
|
||||
|
||||
#, c-format
|
||||
msgid "The system will restart automatically in %d seconds."
|
||||
msgstr "Süsteem taaskäivitatakse automaatselt %d sekundi pärast."
|
||||
|
||||
msgid "Restarting the system."
|
||||
msgstr "Süsteemi taaskäivitamine."
|
||||
|
||||
msgid "Confirm"
|
||||
msgstr "Kinnita"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Katkesta"
|
||||
|
||||
msgid "No extensions installed"
|
||||
msgstr "Ühtegi laiendust pole paigaldatud"
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "Lubatud"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
msgid "Disabled"
|
||||
msgstr "Keelatud"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "Viga"
|
||||
|
||||
msgid "Out of date"
|
||||
msgstr "Pole värske"
|
||||
|
||||
msgid "View Source"
|
||||
msgstr "Kuva lähtekoodi"
|
||||
|
||||
msgid "Web Page"
|
||||
msgstr "Veebileht"
|
||||
|
||||
msgid "System Information"
|
||||
msgstr "Süsteemi andmed"
|
||||
|
||||
msgid "Undo"
|
||||
msgstr "Võta tagasi"
|
||||
|
||||
msgid "Windows"
|
||||
msgstr "Aknad"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Rakendused"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "Lõpeta %s"
|
||||
|
||||
#. 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".
|
||||
@ -415,6 +566,9 @@ msgstr "Proovi uuesti"
|
||||
msgid "Connect to..."
|
||||
msgstr "Ühendumine..."
|
||||
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "Asukohad ja seadmed"
|
||||
|
||||
#. Translators: this MUST be either "toggle-switch-us"
|
||||
#. (for toggle switches containing the English words
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
@ -426,24 +580,17 @@ msgstr "toggle-switch-intl"
|
||||
msgid "Please enter a command:"
|
||||
msgstr "Palun sisesta käsk:"
|
||||
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "'%s' käivitamine nurjus:"
|
||||
|
||||
msgid "Available"
|
||||
msgstr "Saadaval"
|
||||
|
||||
msgid "Busy"
|
||||
msgstr "Hõivatud"
|
||||
|
||||
msgid "Invisible"
|
||||
msgstr "Nähtamatu"
|
||||
msgid "My Account"
|
||||
msgstr "Minu konto"
|
||||
|
||||
msgid "Account Information..."
|
||||
msgstr "Konto andmed..."
|
||||
|
||||
msgid "System Settings..."
|
||||
msgstr "Süsteemi sätted..."
|
||||
msgid "System Settings"
|
||||
msgstr "Süsteemi sätted"
|
||||
|
||||
msgid "Lock Screen"
|
||||
msgstr "Lukusta ekraan"
|
||||
@ -454,9 +601,237 @@ msgstr "Vaheta kasutajat"
|
||||
msgid "Log Out..."
|
||||
msgstr "Logi välja..."
|
||||
|
||||
#. This is temporarily removed, see
|
||||
#. http://bugzilla.gnome.org/show_bug.cgi?id=636680
|
||||
#. for details.
|
||||
#. item = new PopupMenu.PopupMenuItem(_("Suspend..."));
|
||||
#. item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
#. this.menu.addMenuItem(item);
|
||||
msgid "Shut Down..."
|
||||
msgstr "Lülita välja..."
|
||||
|
||||
msgid "Zoom"
|
||||
msgstr ""
|
||||
|
||||
msgid "Screen Reader"
|
||||
msgstr "Ekraanilugeja"
|
||||
|
||||
msgid "Screen Keyboard"
|
||||
msgstr "Ekraaniklaviatuur"
|
||||
|
||||
msgid "Visual Alerts"
|
||||
msgstr "Visuaalsed märguanded"
|
||||
|
||||
msgid "Sticky Keys"
|
||||
msgstr "Kleepuvad klahvid"
|
||||
|
||||
msgid "Slow Keys"
|
||||
msgstr "Aeglased klahvid"
|
||||
|
||||
msgid "Bounce Keys"
|
||||
msgstr "Põrkeklahvid"
|
||||
|
||||
msgid "Mouse Keys"
|
||||
msgstr "Hiireklahvid"
|
||||
|
||||
msgid "Universal Access Settings"
|
||||
msgstr "Universaalse ligipääsu sätted"
|
||||
|
||||
msgid "High Contrast"
|
||||
msgstr ""
|
||||
|
||||
msgid "Large Text"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bluetooth"
|
||||
msgstr "Bluetooth"
|
||||
|
||||
msgid "Visibility"
|
||||
msgstr "Nähtavus"
|
||||
|
||||
msgid "Send Files to Device..."
|
||||
msgstr "Failide saatmine seadmesse..."
|
||||
|
||||
msgid "Setup a New Device..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Bluetooth Settings"
|
||||
msgstr "Bluetoothi sätted"
|
||||
|
||||
msgid "Connection"
|
||||
msgstr "Ühendus"
|
||||
|
||||
msgid "Send Files..."
|
||||
msgstr "Failide saatmine..."
|
||||
|
||||
msgid "Browse Files..."
|
||||
msgstr "Failide sirvimine..."
|
||||
|
||||
msgid "Error browsing device"
|
||||
msgstr "Viga seadme sirvimisel"
|
||||
|
||||
#, c-format
|
||||
msgid "The requested device cannot be browsed, error is '%s'"
|
||||
msgstr ""
|
||||
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Klaviatuurisätted"
|
||||
|
||||
msgid "Mouse Settings"
|
||||
msgstr "Hiiresätted"
|
||||
|
||||
msgid "Sound Settings"
|
||||
msgstr "Helisätted"
|
||||
|
||||
msgid "Bluetooth Agent"
|
||||
msgstr "Bluetoothi agent"
|
||||
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr "Autoriseerimise päring seadmelt %s"
|
||||
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr "Seade %s soovib ligipääsu teenusele '%s'"
|
||||
|
||||
msgid "Always grant access"
|
||||
msgstr "Luba alati"
|
||||
|
||||
msgid "Grant this time only"
|
||||
msgstr "Luba ainult seekord"
|
||||
|
||||
msgid "Reject"
|
||||
msgstr "Lükka tagasi"
|
||||
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr "Paardumise kinnitus seadmele %s"
|
||||
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr "Seade '%s' tahab selle arvutiga paarduda"
|
||||
|
||||
#, c-format
|
||||
msgid "Please confirm whether the PIN '%s' matches the one on the device."
|
||||
msgstr "Palun kontrolli, et PIN-kood '%s' kattuks seadme parooliga."
|
||||
|
||||
msgid "Matches"
|
||||
msgstr "Kattub"
|
||||
|
||||
msgid "Does not match"
|
||||
msgstr "Ei kattu"
|
||||
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr "Seadmega %s paardumise päring"
|
||||
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr "Palun sisesta seadme poolt öeldav PIN-kood."
|
||||
|
||||
msgid "OK"
|
||||
msgstr "Olgu"
|
||||
|
||||
msgid "Localization Settings"
|
||||
msgstr "Lokaliseerimissätted"
|
||||
|
||||
msgid "Power Settings"
|
||||
msgstr "Toitesätted..."
|
||||
|
||||
#, c-format
|
||||
msgid "%d hour remaining"
|
||||
msgid_plural "%d hours remaining"
|
||||
msgstr[0] "jäänud %d tund"
|
||||
msgstr[1] "jäänud %d tundi"
|
||||
|
||||
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
|
||||
#, c-format
|
||||
msgid "%d %s %d %s remaining"
|
||||
msgstr "jäänud %d %s ja %d %s"
|
||||
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "tund"
|
||||
msgstr[1] "tundi"
|
||||
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] "minut"
|
||||
msgstr[1] "minutit"
|
||||
|
||||
#, c-format
|
||||
msgid "%d minute remaining"
|
||||
msgid_plural "%d minutes remaining"
|
||||
msgstr[0] "jäänud %d minut"
|
||||
msgstr[1] "jäänud %d minutit"
|
||||
|
||||
msgid "AC adapter"
|
||||
msgstr "Võrgutoite adapter"
|
||||
|
||||
msgid "Laptop battery"
|
||||
msgstr "Sülearvuti aku"
|
||||
|
||||
msgid "UPS"
|
||||
msgstr "UPS"
|
||||
|
||||
msgid "Monitor"
|
||||
msgstr "Monitor"
|
||||
|
||||
msgid "Mouse"
|
||||
msgstr "Hiir"
|
||||
|
||||
msgid "Keyboard"
|
||||
msgstr "Klaviatuur"
|
||||
|
||||
msgid "PDA"
|
||||
msgstr "Elektronmärkmik"
|
||||
|
||||
msgid "Cell phone"
|
||||
msgstr "Mobiiltelefon"
|
||||
|
||||
msgid "Media player"
|
||||
msgstr "Meediaesitaja"
|
||||
|
||||
msgid "Tablet"
|
||||
msgstr "Graafikalaud"
|
||||
|
||||
msgid "Computer"
|
||||
msgstr "Arvuti"
|
||||
|
||||
msgid "Unknown"
|
||||
msgstr "Tundmatu"
|
||||
|
||||
msgid "Volume"
|
||||
msgstr "Helivaljus"
|
||||
|
||||
msgid "Microphone"
|
||||
msgstr "Mikrofon"
|
||||
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr "%s on ühendatud."
|
||||
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr "%s on ühendamata."
|
||||
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr "%s on eemal."
|
||||
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr "%s on hõivatud."
|
||||
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr "Saadetud: %a, kell %X"
|
||||
|
||||
msgid "Search your computer"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "%s läks käima"
|
||||
@ -472,6 +847,25 @@ msgstr "Pole võimalik uut tööala lisada, kuna tööalade piir on saavutatud."
|
||||
msgid "Can't remove the first workspace."
|
||||
msgstr "Esimest tööala pole võimalik eemaldada."
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "System Sounds"
|
||||
msgstr "Süsteemi helid"
|
||||
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "Vähem kui minuti eest"
|
||||
|
||||
@ -518,3 +912,57 @@ msgstr "Otsing"
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Suspend..."
|
||||
#~ msgstr "Peata..."
|
||||
|
||||
#~ msgid "Clock"
|
||||
#~ msgstr "Kell"
|
||||
|
||||
#~ msgid "Customize the panel clock"
|
||||
#~ msgstr "Paneelikella kohandamine"
|
||||
|
||||
#~ msgid "Custom format of the clock"
|
||||
#~ msgstr "Kellaaaja kohandatud vorming"
|
||||
|
||||
#~ msgid "Hour format"
|
||||
#~ msgstr "Tundide vorming"
|
||||
|
||||
#~ msgid "Clock Format"
|
||||
#~ msgstr "Kellaaja vorming"
|
||||
|
||||
#~ msgid "Clock Preferences"
|
||||
#~ msgstr "Kella eelistused"
|
||||
|
||||
#~ msgid "Panel Display"
|
||||
#~ msgstr "Paneelikuva"
|
||||
|
||||
#~ msgid "Show seco_nds"
|
||||
#~ msgstr "_Sekundeid näidatakse"
|
||||
|
||||
#~ msgid "Show the _date"
|
||||
#~ msgstr "_Kuupäeva näidatakse"
|
||||
|
||||
#~ msgid "_12 hour format"
|
||||
#~ msgstr "_12 tunni vorming"
|
||||
|
||||
#~ msgid "_24 hour format"
|
||||
#~ msgstr "_24 tunni vorming"
|
||||
|
||||
#~ msgid "Preferences"
|
||||
#~ msgstr "Eelistused"
|
||||
|
||||
#~ msgid "Drag here to add favorites"
|
||||
#~ msgstr "Lemmikute lisamiseks lohista need siia"
|
||||
|
||||
#~ msgid "Find"
|
||||
#~ msgstr "Otsi"
|
||||
|
||||
#~ msgid "Searching..."
|
||||
#~ msgstr "Otsimine..."
|
||||
|
||||
#~ msgid "No matching results."
|
||||
#~ msgstr "Tulemused puuduvad."
|
||||
|
||||
#~ msgid "Invisible"
|
||||
#~ msgstr "Nähtamatu"
|
||||
|
730
po/fa.po
Normal file
@ -0,0 +1,730 @@
|
||||
# Persian translation for gnome-shell.
|
||||
# Copyright (C) 2010 Iranian Free Software Users Group (IFSUG.org)translation team.
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
# Arash Mousavi <mousavi.arash@gmail.com>, 2010.
|
||||
# Mahyar Moghimi <mahyar.moqimi@gmail.com>, 2010.
|
||||
#
|
||||
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: 2010-12-01 18:12+0000\n"
|
||||
"PO-Revision-Date: 2010-12-03 13:24+0330\n"
|
||||
"Last-Translator: Mahyar Moghimi <mahyar.moqimi@gmail.com>\n"
|
||||
"Language-Team: Persian <translate@ifsug.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Poedit-Language: Persian\n"
|
||||
"X-Poedit-Country: IRAN\n"
|
||||
"X-Poedit-SourceCharset: utf-8\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 "مدیریت پنجرهها و اجرا کنندهی برنامهها"
|
||||
|
||||
#: ../data/gnome-shell-clock-preferences.desktop.in.in.h:1
|
||||
msgid "Clock"
|
||||
msgstr "ساعت"
|
||||
|
||||
#: ../data/gnome-shell-clock-preferences.desktop.in.in.h:2
|
||||
msgid "Customize the panel clock"
|
||||
msgstr "سفارشیسازی ساعت تابلو"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:1
|
||||
msgid "Allows access to internal debugging and monitoring tools using the Alt-F2 dialog."
|
||||
msgstr "اجازه دسترسی به ابزارهای اشکالزدا و پایشگر داخلی با استفاده از محاورهی Alt-F2."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:2
|
||||
msgid "Custom format of the clock"
|
||||
msgstr "قالب سفارشی برای ساعت"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:3
|
||||
msgid "Enable internal tools useful for developers and testers from Alt-F2"
|
||||
msgstr "فعال کردن ابزارهای داخلی مفید برای توسعه دهندگان و آزمایش کنندگان از طریق Alt-F2"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:4
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr "پسوند پروندهی قابل استفاده برای ذخیره تصویربرداری از صفحهنمایش"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:5
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr "سرعت فریم استفاده شده در تصویربرداری از صفحهنمایش."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid "GNOME Shell extensions have a uuid property; this key lists extensions which should not be loaded."
|
||||
msgstr "افزونههای پوستهی گنوم مشخصهی uuid دارند؛ این کلید افزونههایی که نباید بار شوند را فهرست میکند."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr "تاریخچهی فرمان برای محاورهی (Alt-F2)"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
msgid "Hour format"
|
||||
msgstr "قالب ساعت"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
msgid "If true and format is either \"12-hour\" or \"24-hour\", display date in the clock, in addition to time."
|
||||
msgstr "اگر روی «درست» تنظیم شود و قالب ساعت هر یک از حالتهای «۱۲ ساعته» و یا «۲۴ ساعته» باشد، تاریخ را در کنار ساعت نشان میدهد."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
msgid "If true and format is either \"12-hour\" or \"24-hour\", display seconds in time."
|
||||
msgstr "اگر بر روی «درست» تنظیم شود و قالب ساعت هر یک از جالتهای «۱۲ ساعته» و یا «۲۴ ساعته» باشد، ثانیه را در کنار ساعت نشان میدهد."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr "در صورت تنظیم بر روی «درست»، تاریخ هفتگی ایزو را در تقویم نشان میدهد."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:12
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr "فهرست شناسههای پروندهی رومیزی برای برنامههای مورد پسند"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
msgid "Sets the GStreamer pipeline used to encode recordings. It follows the syntax used for gst-launch. The pipeline should have an unconnected sink pad where the recorded video is recorded. It will normally have a unconnected source pad; output from that pad will be written into the output file. However the pipeline can also take care of its own output - this might be used to send the output to an icecast server via shout2send or similar. When unset or set to an empty value, the default pipeline will be used. This is currently 'videorate ! theoraenc ! oggmux' and records to Ogg Theora."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr "نمایش تاریخ در ساعت"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr "نمایش هفته در تقویم"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr "نمایش ساعت همراه با ثانیه"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid "The applications corresponding to these identifiers will be displayed in the favorites area."
|
||||
msgstr "برنامههای مشابه این شناسهها در قسمت مورد پسندها نمایش داده میشود."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid "The filename for recorded screencasts will be a unique filename based on the current date, and use this extension. It should be changed when recording to a different container format."
|
||||
msgstr "نام پروندهی ضبط شده برای تصویربرداری از صفحهنمایش یکتا و براساس تاریخ جاری خواهد بود و از این افزونه استفاده خواهد کرد. اگر در زمان ضبط از قالب دیگری استفاده کنید باید تغییر کند."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid "The framerate of the resulting screencast recordered by GNOME Shell's screencast recorder in frames-per-second."
|
||||
msgstr "سرعت فریم حاصل از تصویربرداری از صفحه نمایش با استفاده از ضبط کننده نمایشگر پوستهی گنوم بر اساس فریم بر ثانیه"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr "مجرای ارتباطی gstreamer برای کدگذاری تصویربرداری از صفحه نمایش"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid "The shell normally monitors active applications in order to present the most used ones (e.g. in launchers). While this data will be kept private, you may want to disable this for privacy reasons. Please note that doing so won't remove already saved data."
|
||||
msgstr "پوسته گنوم در حالت عادی برنامههای فعال را جهت ارائه برنامههای بیشتر استفاده شده پایش می کند. (برای مثال در اجرا کنندهها). با اینکه که این اطلاعات به صورت خصوصی نگاهداری میشود، ممکن است شما بخواهید این امکان را به دلایل امنیتی غیرفعال کنید. لطفا توجه کنید این کار باعث پاک شدن اطلاعاتی که تاکنون ذخیره شدهاند نمیشود."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "This key specifies the format used by the panel clock when the format key is set to \"custom\". You can use conversion specifiers understood by strftime() to obtain a specific format. See the strftime() manual for more information."
|
||||
msgstr "این کلید قالب ساعتِ قاب را هنگامی که بر روی «سفارشی» تنظیم شده باشد تعیین میکند. شما میتوانید از مبدلهای قابل فهم در تابع ()strftime برای اختصاص قالب مشخص استفاده کنید. برای اطلاعات بیشتر مستندات ()strftime را مشاهده کنید."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "This key specifies the hour format used by the panel clock. Possible values are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to \"unix\", the clock will display time in seconds since Epoch, i.e. 1970-01-01. If set to \"custom\", the clock will display time according to the format specified in the custom_format key. Note that if set to either \"unix\" or \"custom\", the show_date and show_seconds keys are ignored."
|
||||
msgstr "این کلید قالب ساعت را در تابلو مشخص میکند. مقادیر ممکن عبارتند از: «۱۲ ساعته»، «۲۴ ساعته»، «یونیکس» و «سفارشی». چنانچه بر روی «یونیکس» تنظیم گردد، ساعت زمان را به ثانیه از زمان مبدا تاریخ نشان میدهد، برای مثال ۰۱-۰۱-۱۹۷۰. اگر بر روی «سفارشی» تنظیم شود ساعت زمان را با توجه به قالب معرفی شده در کلید custom_format نمایش خواهد داد. توجه کنید که چنانچه بر روی یکی از حالتهای «یونیکس» و یا «سفارشی» تنظیم شود، کلیدهای show_date و show_seconds نادیده گرفته خواهند شد."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr "شناسههای Uuid مربوط به افزونهها که غیرفعال شود"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:25
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr "اینکه اطلاعات برنامهها دربارهی میزان استفاده از منابع جمعآوری شود یا خیر"
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:1
|
||||
msgid "Clip the crosshairs at the center"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:2
|
||||
msgid "Color of the crosshairs"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:3
|
||||
msgid "Determines the length of the vertical and horizontal lines that make up the crosshairs."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:4
|
||||
msgid "Determines the position of the magnified mouse image within the magnified view and how it reacts to system mouse movement. The values are - none: no mouse tracking; - centered: the mouse image is displayed at the center of the zoom region (which also represents the point under the system mouse) and the magnified contents are scrolled as the system mouse moves; - proportional: the position of the magnified mouse in the zoom region is proportionally the same as the position of the system mouse on screen; - push: when the magnified mouse intersects a boundary of the zoom region, the contents are scrolled into view."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:5
|
||||
msgid "Determines the transparency of the crosshairs, from fully opaque to fully transparent."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:6
|
||||
msgid "Determines whether the crosshairs intersect the magnified mouse sprite, or are clipped such that the ends of the horizontal and vertical lines surround the mouse image."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:7
|
||||
#| msgid "Enabled"
|
||||
msgid "Enable lens mode"
|
||||
msgstr "به کار انداختن حالت لنز"
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:8
|
||||
msgid "Enables/disables display of crosshairs centered on the magnified mouse sprite."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:9
|
||||
msgid "For centered mouse tracking, when the system pointer is at or near the edge of the screen, the magnified contents continue to scroll such that the screen edge moves into the magnified view."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:10
|
||||
msgid "Length of the crosshairs"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:11
|
||||
msgid "Magnification factor"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:12
|
||||
msgid "Mouse Tracking Mode"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:13
|
||||
msgid "Opacity of the crosshairs"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:14
|
||||
msgid "Screen position"
|
||||
msgstr "مکان روی صفحه نمایش"
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:15
|
||||
msgid "Scroll magnified contents beyond the edges of the desktop"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:16
|
||||
msgid "Show or hide crosshairs"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:17
|
||||
msgid "Show or hide the magnifier"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:18
|
||||
msgid "Show or hide the magnifier and all of its zoom regions."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:19
|
||||
msgid "The color of the the vertical and horizontal lines that make up the crosshairs."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:20
|
||||
msgid "The magnified view either fills the entire screen, or occupies the top-half, bottom-half, left-half, or right-half of the screen."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:21
|
||||
msgid "The power of the magnification. A value of 1.0 means no magnification. A value of 2.0 doubles the size."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:22
|
||||
msgid "Thickness of the crosshairs"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:23
|
||||
msgid "Whether the magnified view should be centered over the location of the system mouse and move with it."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:24
|
||||
msgid "Width of the vertical and horizontal lines that make up the crosshairs."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/clock-preferences.ui.h:1
|
||||
msgid "Clock Format"
|
||||
msgstr "قالب ساعت"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:2
|
||||
msgid "Clock Preferences"
|
||||
msgstr "ترجیحات ساعت"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:3
|
||||
msgid "Panel Display"
|
||||
msgstr "نمایش تابلو"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:4
|
||||
msgid "Show seco_nds"
|
||||
msgstr "نمایش _ثانیهها"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:5
|
||||
msgid "Show the _date"
|
||||
msgstr "نمایش _تاریخ"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:6
|
||||
msgid "_12 hour format"
|
||||
msgstr "فالب _۱۲ ساعته"
|
||||
|
||||
#: ../data/clock-preferences.ui.h:7
|
||||
msgid "_24 hour format"
|
||||
msgstr "قالب _۲۴ ساعته"
|
||||
|
||||
#: ../js/ui/appDisplay.js:215
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "برنامهها"
|
||||
|
||||
#: ../js/ui/appDisplay.js:245
|
||||
msgid "PREFERENCES"
|
||||
msgstr "ترجیحات"
|
||||
|
||||
#: ../js/ui/appDisplay.js:542
|
||||
msgid "New Window"
|
||||
msgstr "پنجرهی جدید"
|
||||
|
||||
#: ../js/ui/appDisplay.js:546
|
||||
msgid "Remove from Favorites"
|
||||
msgstr "حذف از مورد پسندها"
|
||||
|
||||
#: ../js/ui/appDisplay.js:547
|
||||
msgid "Add to Favorites"
|
||||
msgstr "اضافه کردن به مورد پسندها"
|
||||
|
||||
#: ../js/ui/appFavorites.js:91
|
||||
#, c-format
|
||||
msgid "%s has been added to your favorites."
|
||||
msgstr "مورد %s به مورد پسندهای شما اضافه شد."
|
||||
|
||||
#: ../js/ui/appFavorites.js:122
|
||||
#, c-format
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "مورد %s ازمورد پسندهای شما حذف شد."
|
||||
|
||||
#: ../js/ui/dash.js:27
|
||||
msgid "Remove"
|
||||
msgstr "حذف"
|
||||
|
||||
#: ../js/ui/docDisplay.js:494
|
||||
msgid "RECENT ITEMS"
|
||||
msgstr "موارد اخیر"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:552
|
||||
msgid "No extensions installed"
|
||||
msgstr "هیچ افزونهای نصب نشده است"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:589
|
||||
msgid "Enabled"
|
||||
msgstr "به کار انداختن"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:591
|
||||
#: ../src/gvc/gvc-mixer-control.c:1087
|
||||
msgid "Disabled"
|
||||
msgstr "از کار انداختن"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:593
|
||||
msgid "Error"
|
||||
msgstr "خطا"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:595
|
||||
msgid "Out of date"
|
||||
msgstr "قدیمی"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:620
|
||||
msgid "View Source"
|
||||
msgstr "نمایش کدمنبع"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:626
|
||||
msgid "Web Page"
|
||||
msgstr "صفحهی وب"
|
||||
|
||||
#: ../js/ui/overview.js:112
|
||||
msgid "Undo"
|
||||
msgstr "برگردان"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:470
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
msgstr "خروج از %s"
|
||||
|
||||
#: ../js/ui/panel.js:495
|
||||
msgid "Preferences"
|
||||
msgstr "ترجیحات"
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/panel.js:581
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a %b %e, %R:%S"
|
||||
|
||||
#: ../js/ui/panel.js:582
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a %b %e, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/panel.js:586
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/panel.js:587
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/panel.js:594
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a %b %e, %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/panel.js:595
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a %b %e, %l:%M %p"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/panel.js:599
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/panel.js:600
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#. 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:745
|
||||
msgid "Activities"
|
||||
msgstr "فعالیتها"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:111
|
||||
#, c-format
|
||||
msgid "Failed to unmount '%s'"
|
||||
msgstr "عدم توانایی در پیاده کردن «%s»"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:114
|
||||
msgid "Retry"
|
||||
msgstr "سعی مجدد"
|
||||
|
||||
#: ../js/ui/placeDisplay.js:159
|
||||
msgid "Connect to..."
|
||||
msgstr "اتصال به..."
|
||||
|
||||
#: ../js/ui/placeDisplay.js:558
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "محلها و ابزارها"
|
||||
|
||||
#. Translators: this MUST be either "toggle-switch-us"
|
||||
#. (for toggle switches containing the English words
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:33
|
||||
msgid "toggle-switch-us"
|
||||
msgstr "toggle-switch-intl"
|
||||
|
||||
#: ../js/ui/runDialog.js:233
|
||||
msgid "Please enter a command:"
|
||||
msgstr "لطفا یک فرمان وارد کنید:"
|
||||
|
||||
#: ../js/ui/runDialog.js:378
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "اجرای «%s» شکست خورد:"
|
||||
|
||||
#: ../js/ui/statusMenu.js:101
|
||||
msgid "Available"
|
||||
msgstr "در دسترس"
|
||||
|
||||
#: ../js/ui/statusMenu.js:106
|
||||
msgid "Busy"
|
||||
msgstr "مشغول"
|
||||
|
||||
#: ../js/ui/statusMenu.js:114
|
||||
msgid "My Account"
|
||||
msgstr "حساب من"
|
||||
|
||||
#: ../js/ui/statusMenu.js:118
|
||||
#| msgid "System Settings..."
|
||||
msgid "System Settings"
|
||||
msgstr "تنظیمات سیستم"
|
||||
|
||||
#: ../js/ui/statusMenu.js:125
|
||||
msgid "Lock Screen"
|
||||
msgstr "قفل کردن صفحه"
|
||||
|
||||
#: ../js/ui/statusMenu.js:129
|
||||
msgid "Switch User"
|
||||
msgstr "تعویض کاربر"
|
||||
|
||||
#: ../js/ui/statusMenu.js:134
|
||||
msgid "Log Out..."
|
||||
msgstr "خروج از سیستم..."
|
||||
|
||||
#: ../js/ui/statusMenu.js:141
|
||||
msgid "Suspend..."
|
||||
msgstr "معلق کردن..."
|
||||
|
||||
#: ../js/ui/statusMenu.js:145
|
||||
msgid "Shut Down..."
|
||||
msgstr "خاموش کردن..."
|
||||
|
||||
#: ../js/ui/status/accessibility.js:82
|
||||
msgid "Zoom"
|
||||
msgstr "بزرگنمایی"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:88
|
||||
msgid "Screen Reader"
|
||||
msgstr "صفحه خوان"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:91
|
||||
msgid "Screen Keyboard"
|
||||
msgstr "صفحهکلید مجازی"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:94
|
||||
msgid "Visual Alerts"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:97
|
||||
msgid "Sticky Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:100
|
||||
msgid "Slow Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:103
|
||||
msgid "Bounce Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:106
|
||||
msgid "Mouse Keys"
|
||||
msgstr "کلیدهای موشی"
|
||||
|
||||
#: ../js/ui/status/accessibility.js:110
|
||||
msgid "Universal Access Settings"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:163
|
||||
msgid "High Contrast"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:205
|
||||
msgid "Large Text"
|
||||
msgstr "متن درشت"
|
||||
|
||||
#: ../js/ui/status/power.js:87
|
||||
msgid "What's using power..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:90
|
||||
#| msgid "System Settings..."
|
||||
msgid "Power Settings"
|
||||
msgstr "تنظیمات برق"
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
#, c-format
|
||||
#| msgid "%d hour ago"
|
||||
#| msgid_plural "%d hours ago"
|
||||
msgid "%d hour remaining"
|
||||
msgid_plural "%d hours remaining"
|
||||
msgstr[0] "%Id ساعت مانده"
|
||||
|
||||
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
|
||||
#: ../js/ui/status/power.js:120
|
||||
#, c-format
|
||||
msgid "%d %s %d %s remaining"
|
||||
msgstr "%d %s %d %s مانده"
|
||||
|
||||
#: ../js/ui/status/power.js:122
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "ساعت"
|
||||
|
||||
#: ../js/ui/status/power.js:122
|
||||
#| msgid "%d minute ago"
|
||||
#| msgid_plural "%d minutes ago"
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] "%Id دقیقه"
|
||||
|
||||
#: ../js/ui/status/power.js:125
|
||||
#, c-format
|
||||
#| msgid "%d minute ago"
|
||||
#| msgid_plural "%d minutes ago"
|
||||
msgid "%d minute remaining"
|
||||
msgid_plural "%d minutes remaining"
|
||||
msgstr[0] "%Id دقیقهی مانده"
|
||||
|
||||
#: ../js/ui/status/power.js:244
|
||||
msgid "AC adapter"
|
||||
msgstr "آداپتور برق مستقیم"
|
||||
|
||||
#: ../js/ui/status/power.js:246
|
||||
msgid "Laptop battery"
|
||||
msgstr "باتری لپتاپ"
|
||||
|
||||
#: ../js/ui/status/power.js:248
|
||||
msgid "UPS"
|
||||
msgstr "UPS"
|
||||
|
||||
#: ../js/ui/status/power.js:250
|
||||
msgid "Monitor"
|
||||
msgstr "صفحه نمایش"
|
||||
|
||||
#: ../js/ui/status/power.js:252
|
||||
msgid "Mouse"
|
||||
msgstr "موشی"
|
||||
|
||||
#: ../js/ui/status/power.js:254
|
||||
msgid "Keyboard"
|
||||
msgstr "صفحهکلید"
|
||||
|
||||
#: ../js/ui/status/power.js:256
|
||||
msgid "PDA"
|
||||
msgstr "دستیار دیجیتال شخصی"
|
||||
|
||||
#: ../js/ui/status/power.js:258
|
||||
msgid "Cell phone"
|
||||
msgstr "تلفن سلولی"
|
||||
|
||||
#: ../js/ui/status/power.js:260
|
||||
msgid "Media player"
|
||||
msgstr "پخش کنندهی رسانه"
|
||||
|
||||
#: ../js/ui/status/power.js:262
|
||||
#| msgid "Enabled"
|
||||
msgid "Tablet"
|
||||
msgstr "لوحرایانه"
|
||||
|
||||
#: ../js/ui/status/power.js:264
|
||||
msgid "Computer"
|
||||
msgstr "رایانه"
|
||||
|
||||
#: ../js/ui/status/power.js:266
|
||||
#: ../src/shell-app-system.c:1012
|
||||
msgid "Unknown"
|
||||
msgstr "ناشناخته"
|
||||
|
||||
#: ../js/ui/status/volume.js:41
|
||||
msgid "Volume"
|
||||
msgstr "بلندی صدا"
|
||||
|
||||
#: ../js/ui/status/volume.js:54
|
||||
msgid "Microphone"
|
||||
msgstr "میکروفون"
|
||||
|
||||
#: ../js/ui/status/volume.js:62
|
||||
#| msgid "System Settings..."
|
||||
msgid "Sound Settings"
|
||||
msgstr "تنظیمات صدا"
|
||||
|
||||
#: ../js/ui/viewSelector.js:26
|
||||
msgid "Search your computer"
|
||||
msgstr "جستجوی رایانهی شما"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:43
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr "راهاندازی %s پایان یافته است"
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:45
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "«%s» آماده است"
|
||||
|
||||
#: ../js/ui/workspacesView.js:244
|
||||
msgid "Can't add a new workspace because maximum workspaces limit has been reached."
|
||||
msgstr "نمیتوان میزکار جدیدی اضافه کرد زیرا به مرز بیشترین تعداد میزکار رسیدهایم."
|
||||
|
||||
#: ../js/ui/workspacesView.js:260
|
||||
msgid "Can't remove the first workspace."
|
||||
msgstr "نمیتوان اولین میزکار را حذف کرد."
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1094
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "%u خروجی"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1104
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u ورودی"
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:1402
|
||||
#| msgid "System Settings..."
|
||||
msgid "System Sounds"
|
||||
msgstr "صداهای سیستم"
|
||||
|
||||
#: ../src/shell-global.c:1163
|
||||
msgid "Less than a minute ago"
|
||||
msgstr "کمتر از یک دقیقه قبل"
|
||||
|
||||
#: ../src/shell-global.c:1167
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] "%Id دقیقهی پیش"
|
||||
|
||||
#: ../src/shell-global.c:1172
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] "%Id ساعت پیش"
|
||||
|
||||
#: ../src/shell-global.c:1177
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] "%Id روز پیش"
|
||||
|
||||
#: ../src/shell-global.c:1182
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] "%Id هفتهی پیش"
|
||||
|
||||
#: ../src/shell-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr "پوشهی آغازه"
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:104
|
||||
msgid "File System"
|
||||
msgstr "سیستم پروندهها"
|
||||
|
||||
#: ../src/shell-util.c:250
|
||||
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-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|
||||
#~ msgid "Overview workspace view mode"
|
||||
#~ msgstr "حالت بررسی اجمالی میزکار"
|
||||
#~ msgid ""
|
||||
#~ "The selected workspace view mode in the overview. Supported values are "
|
||||
#~ "\"single\" and \"grid\"."
|
||||
#~ msgstr ""
|
||||
#~ "میزکار انتخاب شده در حال مشاهده اجمالی. مقادیر قابل پشتیبانی عبارتند از "
|
||||
#~ "«تکی» و «شبکهای»."
|
||||
#~ msgid "Drag here to add favorites"
|
||||
#~ msgstr "به اینجا بکشید تا به مجبوبها اضافه شود"
|
||||
#~ msgid "Find"
|
||||
#~ msgstr "جستجو"
|
||||
#~ msgid "Searching..."
|
||||
#~ msgstr "درحال حستجو..."
|
||||
#~ msgid "No matching results."
|
||||
#~ msgstr "نتیجهی منطبقی پیدا نشد."
|
||||
#~ msgid "Invisible"
|
||||
#~ msgstr "نامرئی"
|
||||
#~ msgid "Account Information..."
|
||||
#~ msgstr "اطلاعات جساب"
|
||||
|
989
po/kn.po
Normal file
@ -0,0 +1,989 @@
|
||||
# Kannada translation for gnome-shell.
|
||||
# Copyright (C) 2011 gnome-shell's COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the gnome-shell package.
|
||||
#
|
||||
# Shankar Prasad <svenkate@redhat.com>, 2011.
|
||||
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: 2011-02-21 01:21+0000\n"
|
||||
"PO-Revision-Date: 2011-02-21 12:52+0530\n"
|
||||
"Last-Translator: Shankar Prasad <svenkate@redhat.com>\n"
|
||||
"Language-Team: Kannada <kn@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"
|
||||
"X-Generator: Lokalize 1.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 ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:1
|
||||
msgid ""
|
||||
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
|
||||
"dialog."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:2
|
||||
msgid "Enable internal tools useful for developers and testers from Alt-F2"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:3
|
||||
msgid "File extension used for storing the screencast"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:4
|
||||
msgid "Framerate used for recording screencasts."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:5
|
||||
msgid ""
|
||||
"GNOME Shell extensions have a uuid property; this key lists extensions which "
|
||||
"should not be loaded."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:6
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:7
|
||||
msgid "History for the looking glass dialog"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:8
|
||||
msgid "If true, display date in the clock, in addition to time."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:9
|
||||
msgid "If true, display seconds in time."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:10
|
||||
msgid "If true, display the ISO week date in the calendar."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:11
|
||||
msgid "List of desktop file IDs for favorite applications"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:13
|
||||
#, no-c-format
|
||||
msgid ""
|
||||
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
|
||||
"used for gst-launch. The pipeline should have an unconnected sink pad where "
|
||||
"the recorded video is recorded. It will normally have a unconnected source "
|
||||
"pad; output from that pad will be written into the output file. However the "
|
||||
"pipeline can also take care of its own output - this might be used to send "
|
||||
"the output to an icecast server via shout2send or similar. When unset or set "
|
||||
"to an empty value, the default pipeline will be used. This is currently "
|
||||
"'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux' and "
|
||||
"records to WEBM using the VP8 codec. %T is used as a placeholder for a guess "
|
||||
"at the optimal thread count on the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:14
|
||||
msgid "Show date in clock"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:15
|
||||
msgid "Show the week date in the calendar"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:16
|
||||
msgid "Show time with seconds"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:17
|
||||
msgid ""
|
||||
"The applications corresponding to these identifiers will be displayed in the "
|
||||
"favorites area."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:18
|
||||
msgid ""
|
||||
"The filename for recorded screencasts will be a unique filename based on the "
|
||||
"current date, and use this extension. It should be changed when recording to "
|
||||
"a different container format."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:19
|
||||
msgid ""
|
||||
"The framerate of the resulting screencast recordered by GNOME Shell's "
|
||||
"screencast recorder in frames-per-second."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:20
|
||||
msgid "The gstreamer pipeline used to encode the screencast"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:21
|
||||
msgid ""
|
||||
"The shell normally monitors active applications in order to present the most "
|
||||
"used ones (e.g. in launchers). While this data will be kept private, you may "
|
||||
"want to disable this for privacy reasons. Please note that doing so won't "
|
||||
"remove already saved data."
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:22
|
||||
msgid "Uuids of extensions to disable"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:23
|
||||
msgid "Whether to collect stats about applications usage"
|
||||
msgstr ""
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.h:24
|
||||
msgid "disabled OpenSearch providers"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/misc/util.js:86
|
||||
msgid "Command not found"
|
||||
msgstr ""
|
||||
|
||||
#. Replace "Error invoking GLib.shell_parse_argv: " with
|
||||
#. something nicer
|
||||
#: ../js/misc/util.js:113
|
||||
msgid "Could not parse command:"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/misc/util.js:135
|
||||
msgid "No such application"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/misc/util.js:148
|
||||
#, c-format
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
#: ../js/ui/appDisplay.js:174
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/appDisplay.js:261
|
||||
msgid "APPLICATIONS"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/appDisplay.js:291
|
||||
msgid "PREFERENCES"
|
||||
msgstr "PREFERENCES"
|
||||
|
||||
#: ../js/ui/appDisplay.js:551
|
||||
msgid "New Window"
|
||||
msgstr "ಹೊಸ ವಿಂಡೊ"
|
||||
|
||||
#: ../js/ui/appDisplay.js:555
|
||||
msgid "Remove from Favorites"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/appDisplay.js:556
|
||||
msgid "Add to Favorites"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/appFavorites.js:91
|
||||
#, c-format
|
||||
msgid "%s has been added to your favorites."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/appFavorites.js:122
|
||||
#, c-format
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr ""
|
||||
|
||||
#. Translators: Shown in calendar event list for all day events
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
#: ../js/ui/calendar.js:65
|
||||
msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
msgstr "ಎಲ್ಲಾ ದಿನ"
|
||||
|
||||
#. Translators: Shown in calendar event list, if 24h format
|
||||
#: ../js/ui/calendar.js:70
|
||||
msgctxt "event list time"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
|
||||
#. Transators: Shown in calendar event list, if 12h format
|
||||
#: ../js/ui/calendar.js:77
|
||||
msgctxt "event list time"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Sunday.
|
||||
#. *
|
||||
#. * NOTE: These grid abbreviations are always shown together
|
||||
#. * and in order, e.g. "S M T W T F S".
|
||||
#.
|
||||
#: ../js/ui/calendar.js:117
|
||||
msgctxt "grid sunday"
|
||||
msgid "S"
|
||||
msgstr "ಭಾ"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:119
|
||||
msgctxt "grid monday"
|
||||
msgid "M"
|
||||
msgstr "ಸೋ"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:121
|
||||
msgctxt "grid tuesday"
|
||||
msgid "T"
|
||||
msgstr "ಮಂ"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:123
|
||||
msgctxt "grid wednesday"
|
||||
msgid "W"
|
||||
msgstr "ಬು"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:125
|
||||
msgctxt "grid thursday"
|
||||
msgid "T"
|
||||
msgstr "ಗು"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:127
|
||||
msgctxt "grid friday"
|
||||
msgid "F"
|
||||
msgstr "ಶು"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:129
|
||||
msgctxt "grid saturday"
|
||||
msgid "S"
|
||||
msgstr "ಶ"
|
||||
|
||||
#. Translators: Event list abbreviation for Sunday.
|
||||
#. *
|
||||
#. * NOTE: These list abbreviations are normally not shown together
|
||||
#. * so they need to be unique (e.g. Tuesday and Thursday cannot
|
||||
#. * both be 'T').
|
||||
#.
|
||||
#: ../js/ui/calendar.js:142
|
||||
msgctxt "list sunday"
|
||||
msgid "Su"
|
||||
msgstr "ಭಾ"
|
||||
|
||||
#. Translators: Event list abbreviation for Monday
|
||||
#: ../js/ui/calendar.js:144
|
||||
msgctxt "list monday"
|
||||
msgid "M"
|
||||
msgstr "ಸೋ"
|
||||
|
||||
#. Translators: Event list abbreviation for Tuesday
|
||||
#: ../js/ui/calendar.js:146
|
||||
msgctxt "list tuesday"
|
||||
msgid "T"
|
||||
msgstr "ಮಂ"
|
||||
|
||||
#. Translators: Event list abbreviation for Wednesday
|
||||
#: ../js/ui/calendar.js:148
|
||||
msgctxt "list wednesday"
|
||||
msgid "W"
|
||||
msgstr "ಬು"
|
||||
|
||||
#. Translators: Event list abbreviation for Thursday
|
||||
#: ../js/ui/calendar.js:150
|
||||
msgctxt "list thursday"
|
||||
msgid "Th"
|
||||
msgstr "ಗು"
|
||||
|
||||
#. Translators: Event list abbreviation for Friday
|
||||
#: ../js/ui/calendar.js:152
|
||||
msgctxt "list friday"
|
||||
msgid "F"
|
||||
msgstr "ಶು"
|
||||
|
||||
#. Translators: Event list abbreviation for Saturday
|
||||
#: ../js/ui/calendar.js:154
|
||||
msgctxt "list saturday"
|
||||
msgid "S"
|
||||
msgstr "ಶ"
|
||||
|
||||
#. Translators: Text to show if there are no events
|
||||
#: ../js/ui/calendar.js:701
|
||||
msgid "Nothing Scheduled"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on current year
|
||||
#: ../js/ui/calendar.js:717
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d"
|
||||
msgstr "%A, %B %d"
|
||||
|
||||
#. Translators: Shown on calendar heading when selected day occurs on different year
|
||||
#: ../js/ui/calendar.js:720
|
||||
msgctxt "calendar heading"
|
||||
msgid "%A, %B %d, %Y"
|
||||
msgstr "%A, %B %d, %Y"
|
||||
|
||||
#: ../js/ui/calendar.js:730
|
||||
msgid "Today"
|
||||
msgstr "ಇಂದು"
|
||||
|
||||
#: ../js/ui/calendar.js:734
|
||||
msgid "Tomorrow"
|
||||
msgstr "ನಾಳೆ"
|
||||
|
||||
#: ../js/ui/calendar.js:743
|
||||
msgid "This week"
|
||||
msgstr "ಈ ವಾರ"
|
||||
|
||||
#: ../js/ui/calendar.js:751
|
||||
msgid "Next week"
|
||||
msgstr "ಮುಂದಿನ ವಾರ"
|
||||
|
||||
#: ../js/ui/dash.js:174
|
||||
msgid "Remove"
|
||||
msgstr "ತೆಗೆದು ಹಾಕು"
|
||||
|
||||
#: ../js/ui/dateMenu.js:93
|
||||
msgid "Date and Time Settings"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/dateMenu.js:112
|
||||
msgid "Open Calendar"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: This is the time format with date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:151
|
||||
msgid "%a %b %e, %R:%S"
|
||||
msgstr "%a %b %e, %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:152
|
||||
msgid "%a %b %e, %R"
|
||||
msgstr "%a %b %e, %R"
|
||||
|
||||
#. Translators: This is the time format without date used
|
||||
#. in 24-hour mode.
|
||||
#: ../js/ui/dateMenu.js:156
|
||||
msgid "%a %R:%S"
|
||||
msgstr "%a %R:%S"
|
||||
|
||||
#: ../js/ui/dateMenu.js:157
|
||||
msgid "%a %R"
|
||||
msgstr "%a %R"
|
||||
|
||||
#. Translators: This is a time format with date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:164
|
||||
msgid "%a %b %e, %l:%M:%S %p"
|
||||
msgstr "%a %b %e, %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:165
|
||||
msgid "%a %b %e, %l:%M %p"
|
||||
msgstr "%a %b %e, %l:%M %p"
|
||||
|
||||
#. Translators: This is a time format without date used
|
||||
#. for AM/PM.
|
||||
#: ../js/ui/dateMenu.js:169
|
||||
msgid "%a %l:%M:%S %p"
|
||||
msgstr "%a %l:%M:%S %p"
|
||||
|
||||
#: ../js/ui/dateMenu.js:170
|
||||
msgid "%a %l:%M %p"
|
||||
msgstr "%a %l:%M %p"
|
||||
|
||||
#. Translators: This is the date format to use when the calendar popup is
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
#: ../js/ui/dateMenu.js:196
|
||||
msgid "%A %B %e, %Y"
|
||||
msgstr "%A %B %e, %Y"
|
||||
|
||||
#: ../js/ui/docDisplay.js:19
|
||||
msgid "RECENT ITEMS"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:63
|
||||
#, c-format
|
||||
msgid "Log Out %s"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:64 ../js/ui/endSessionDialog.js:69
|
||||
msgid "Log Out"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:65
|
||||
msgid "Click Log Out to quit these applications and log out of the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:66
|
||||
#, c-format
|
||||
msgid "%s will be logged out automatically in %d seconds."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:67
|
||||
#, c-format
|
||||
msgid "You will be logged out automatically in %d seconds."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:68
|
||||
msgid "Logging out of the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:74 ../js/ui/endSessionDialog.js:78
|
||||
msgid "Shut Down"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:75
|
||||
msgid "Click Shut Down to quit these applications and shut down the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:76
|
||||
#, c-format
|
||||
msgid "The system will shut down automatically in %d seconds."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:77
|
||||
msgid "Shutting down the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:84 ../js/ui/endSessionDialog.js:88
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:85
|
||||
msgid "Click Restart to quit these applications and restart the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:86
|
||||
#, c-format
|
||||
msgid "The system will restart automatically in %d seconds."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:87
|
||||
msgid "Restarting the system."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:395
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/endSessionDialog.js:400 ../js/ui/status/bluetooth.js:470
|
||||
msgid "Cancel"
|
||||
msgstr "ರದ್ದು ಮಾಡು"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:587
|
||||
msgid "No extensions installed"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/lookingGlass.js:624
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
#: ../js/ui/lookingGlass.js:626 ../src/gvc/gvc-mixer-control.c:1087
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/lookingGlass.js:628
|
||||
msgid "Error"
|
||||
msgstr "ದೋಷ"
|
||||
|
||||
#: ../js/ui/lookingGlass.js:630
|
||||
msgid "Out of date"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/lookingGlass.js:655
|
||||
msgid "View Source"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/lookingGlass.js:661
|
||||
msgid "Web Page"
|
||||
msgstr "ಜಾಲ ಪುಟ"
|
||||
|
||||
#: ../js/ui/messageTray.js:1864
|
||||
msgid "System Information"
|
||||
msgstr "ವ್ಯವಸ್ಥೆಯ ಮಾಹಿತಿ"
|
||||
|
||||
#: ../js/ui/overview.js:88
|
||||
msgid "Undo"
|
||||
msgstr "ರದ್ದುಗೊಳಿಸು"
|
||||
|
||||
#: ../js/ui/overview.js:183
|
||||
msgid "Windows"
|
||||
msgstr "ವಿಂಡೋಗಳು"
|
||||
|
||||
#: ../js/ui/overview.js:186
|
||||
msgid "Applications"
|
||||
msgstr "ಅನ್ವಯಗಳು"
|
||||
|
||||
#. TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
#: ../js/ui/panel.js:478
|
||||
#, c-format
|
||||
msgid "Quit %s"
|
||||
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:612
|
||||
msgid "Activities"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/placeDisplay.js:122
|
||||
#, c-format
|
||||
msgid "Failed to unmount '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/placeDisplay.js:125
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/placeDisplay.js:165
|
||||
msgid "Connect to..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/placeDisplay.js:409
|
||||
msgid "PLACES & DEVICES"
|
||||
msgstr "PLACES & DEVICES"
|
||||
|
||||
#. Translators: this MUST be either "toggle-switch-us"
|
||||
#. (for toggle switches containing the English words
|
||||
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle
|
||||
#. switches containing "◯" and "|"). Other values will
|
||||
#. simply result in invisible toggle switches.
|
||||
#: ../js/ui/popupMenu.js:506
|
||||
msgid "toggle-switch-us"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/runDialog.js:201
|
||||
msgid "Please enter a command:"
|
||||
msgstr "ದಯವಿಟ್ಟು ಒಂದು ಆಜ್ಞೆಯನ್ನು ನಮೂದಿಸಿ:"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:295
|
||||
msgid "Searching..."
|
||||
msgstr "ಹುಡುಕಲಾಗುತ್ತಿದೆ..."
|
||||
|
||||
#: ../js/ui/searchDisplay.js:309
|
||||
msgid "No matching results."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/statusMenu.js:102
|
||||
msgid "Available"
|
||||
msgstr "ಲಭ್ಯ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:107
|
||||
msgid "Busy"
|
||||
msgstr "ಕಾರ್ಯನಿರತ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:115
|
||||
msgid "My Account"
|
||||
msgstr "ನನ್ನ ಖಾತೆ"
|
||||
|
||||
#: ../js/ui/statusMenu.js:119
|
||||
msgid "System Settings"
|
||||
msgstr "ವ್ಯವಸ್ಥೆಯ ಸಿದ್ಧತೆಗಳು"
|
||||
|
||||
#: ../js/ui/statusMenu.js:126
|
||||
msgid "Lock Screen"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/statusMenu.js:130
|
||||
msgid "Switch User"
|
||||
msgstr "ಬಳಕೆದಾರನನ್ನು ಬದಲಿಸು"
|
||||
|
||||
#: ../js/ui/statusMenu.js:135
|
||||
msgid "Log Out..."
|
||||
msgstr ""
|
||||
|
||||
#. This is temporarily removed, see
|
||||
#. http://bugzilla.gnome.org/show_bug.cgi?id=636680
|
||||
#. for details.
|
||||
#. item = new PopupMenu.PopupMenuItem(_("Suspend..."));
|
||||
#. item.connect('activate', Lang.bind(this, this._onShutDownActivate));
|
||||
#. this.menu.addMenuItem(item);
|
||||
#: ../js/ui/statusMenu.js:149
|
||||
msgid "Shut Down..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:81
|
||||
msgid "Zoom"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:88
|
||||
msgid "Screen Reader"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:92
|
||||
msgid "Screen Keyboard"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:96
|
||||
msgid "Visual Alerts"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:99
|
||||
msgid "Sticky Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:102
|
||||
msgid "Slow Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:105
|
||||
msgid "Bounce Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:108
|
||||
msgid "Mouse Keys"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:112
|
||||
msgid "Universal Access Settings"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:164
|
||||
msgid "High Contrast"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/accessibility.js:206
|
||||
msgid "Large Text"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:42 ../js/ui/status/bluetooth.js:241
|
||||
#: ../js/ui/status/bluetooth.js:337 ../js/ui/status/bluetooth.js:371
|
||||
#: ../js/ui/status/bluetooth.js:411 ../js/ui/status/bluetooth.js:444
|
||||
msgid "Bluetooth"
|
||||
msgstr "ಬ್ಲೂಟೂತ್"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:55
|
||||
msgid "Visibility"
|
||||
msgstr "ಗೋಚರಿಕೆ"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:69
|
||||
msgid "Send Files to Device..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:70
|
||||
msgid "Setup a New Device..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:95
|
||||
msgid "Bluetooth Settings"
|
||||
msgstr "ಬ್ಲೂಟೂತ್ ಸಿದ್ಧತೆಗಳು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:192
|
||||
msgid "Connection"
|
||||
msgstr "ಸಂಪರ್ಕ"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:228
|
||||
msgid "Send Files..."
|
||||
msgstr "ಕಡತಗಳನ್ನು ಕಳುಹಿಸು..."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:233
|
||||
msgid "Browse Files..."
|
||||
msgstr "ಕಡತಗಳಿಗಾಗಿ ವೀಕ್ಷಿಸು..."
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:242
|
||||
msgid "Error browsing device"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:243
|
||||
#, c-format
|
||||
msgid "The requested device cannot be browsed, error is '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:251
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "ಕೀಲಿಮಣೆ ಸಿದ್ಧತೆಗಳು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:256
|
||||
msgid "Mouse Settings"
|
||||
msgstr "ಮೌಸ್ನ ಸಿದ್ಧತೆಗಳು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:263 ../js/ui/status/volume.js:65
|
||||
msgid "Sound Settings"
|
||||
msgstr "ಧ್ವನಿಯ ಸಿದ್ಧತೆಗಳು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:372
|
||||
#, c-format
|
||||
msgid "Authorization request from %s"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:378
|
||||
#, c-format
|
||||
msgid "Device %s wants access to the service '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:380
|
||||
msgid "Always grant access"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:381
|
||||
msgid "Grant this time only"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:382
|
||||
msgid "Reject"
|
||||
msgstr "ತಿರಸ್ಕರಿಸು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:412
|
||||
#, c-format
|
||||
msgid "Pairing confirmation for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:418 ../js/ui/status/bluetooth.js:452
|
||||
#, c-format
|
||||
msgid "Device %s wants to pair with this computer"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:419
|
||||
#, c-format
|
||||
msgid "Please confirm whether the PIN '%s' matches the one on the device."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:421
|
||||
msgid "Matches"
|
||||
msgstr "ಹೊಂದಾಣಿಕೆಗಳು"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:422
|
||||
msgid "Does not match"
|
||||
msgstr "ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:445
|
||||
#, c-format
|
||||
msgid "Pairing request for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:453
|
||||
msgid "Please enter the PIN mentioned on the device."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:469
|
||||
msgid "OK"
|
||||
msgstr "ಸರಿ"
|
||||
|
||||
#: ../js/ui/status/keyboard.js:72
|
||||
msgid "Localization Settings"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:85
|
||||
msgid "Power Settings"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:112
|
||||
#, c-format
|
||||
msgid "%d hour remaining"
|
||||
msgid_plural "%d hours remaining"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining"
|
||||
#: ../js/ui/status/power.js:115
|
||||
#, c-format
|
||||
msgid "%d %s %d %s remaining"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../js/ui/status/power.js:117
|
||||
msgid "minute"
|
||||
msgid_plural "minutes"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../js/ui/status/power.js:120
|
||||
#, c-format
|
||||
msgid "%d minute remaining"
|
||||
msgid_plural "%d minutes remaining"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../js/ui/status/power.js:235
|
||||
msgid "AC adapter"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:237
|
||||
msgid "Laptop battery"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:239
|
||||
msgid "UPS"
|
||||
msgstr "UPS"
|
||||
|
||||
#: ../js/ui/status/power.js:241
|
||||
msgid "Monitor"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:243
|
||||
msgid "Mouse"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:245
|
||||
msgid "Keyboard"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:247
|
||||
msgid "PDA"
|
||||
msgstr "PDA"
|
||||
|
||||
#: ../js/ui/status/power.js:249
|
||||
msgid "Cell phone"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:251
|
||||
msgid "Media player"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:253
|
||||
msgid "Tablet"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:255
|
||||
msgid "Computer"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/power.js:257 ../src/shell-app-system.c:1013
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/volume.js:44
|
||||
msgid "Volume"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/status/volume.js:57
|
||||
msgid "Microphone"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/telepathyClient.js:240
|
||||
#, c-format
|
||||
msgid "%s is online."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/telepathyClient.js:245
|
||||
#, c-format
|
||||
msgid "%s is offline."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/telepathyClient.js:248
|
||||
#, c-format
|
||||
msgid "%s is away."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/telepathyClient.js:251
|
||||
#, c-format
|
||||
msgid "%s is busy."
|
||||
msgstr ""
|
||||
|
||||
#. Translators: this is a time format string followed by a date.
|
||||
#. If applicable, replace %X with a strftime format valid for your
|
||||
#. locale, without seconds.
|
||||
#: ../js/ui/telepathyClient.js:349
|
||||
#, no-c-format
|
||||
msgid "Sent at %X on %A"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: this is the text displayed
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters
|
||||
#: ../js/ui/viewSelector.js:30
|
||||
msgid "Type to search..."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:43
|
||||
#, c-format
|
||||
msgid "%s has finished starting"
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/windowAttentionHandler.js:45
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr ""
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1094
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#: ../src/gvc/gvc-mixer-control.c:1104
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../src/gvc/gvc-mixer-control.c:1402
|
||||
msgid "System Sounds"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/shell-global.c:1363
|
||||
msgid "Less than a minute ago"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/shell-global.c:1367
|
||||
#, c-format
|
||||
msgid "%d minute ago"
|
||||
msgid_plural "%d minutes ago"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../src/shell-global.c:1372
|
||||
#, c-format
|
||||
msgid "%d hour ago"
|
||||
msgid_plural "%d hours ago"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../src/shell-global.c:1377
|
||||
#, c-format
|
||||
msgid "%d day ago"
|
||||
msgid_plural "%d days ago"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../src/shell-global.c:1382
|
||||
#, c-format
|
||||
msgid "%d week ago"
|
||||
msgid_plural "%d weeks ago"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: ../src/shell-util.c:89
|
||||
msgid "Home Folder"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: this is the same string as the one found in
|
||||
#. * nautilus
|
||||
#: ../src/shell-util.c:104
|
||||
msgid "File System"
|
||||
msgstr ""
|
||||
|
||||
#: ../src/shell-util.c:250
|
||||
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-util.c:300
|
||||
#, c-format
|
||||
msgid "%1$s: %2$s"
|
||||
msgstr "%1$s: %2$s"
|
||||
|