Compare commits
	
		
			1058 Commits
		
	
	
		
			3.16.1
			...
			gnome-3-24
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6bd0872530 | ||
|   | 8dace05c26 | ||
|   | eb2699f2b1 | ||
|   | 34bab377b1 | ||
|   | 82c984dd33 | ||
|   | ecea0218f2 | ||
|   | 3478715e6a | ||
|   | cb6d9c9278 | ||
|   | 7f693ed2c4 | ||
|   | 44908cf470 | ||
|   | 7db7b7dcbd | ||
|   | a8b457b52e | ||
|   | cd678d86e8 | ||
|   | 384ef7e100 | ||
|   | 451c753e4a | ||
|   | ebc74957c8 | ||
|   | 70f1f2d7be | ||
|   | 1be2102d29 | ||
|   | 114f371753 | ||
|   | 363d35a3eb | ||
|   | 26433c2cb7 | ||
|   | db81ef3e95 | ||
|   | 091fb4ba2e | ||
|   | 54891a4cd0 | ||
|   | 31c9d2cf7a | ||
|   | 1b8bba5593 | ||
|   | 180a897588 | ||
|   | 95dba93046 | ||
|   | 35eddb3dfa | ||
|   | aa08bd75d0 | ||
|   | d104ebf612 | ||
|   | e7ad9d7217 | ||
|   | 35e6a31982 | ||
|   | 4c1467196e | ||
|   | 7329721216 | ||
|   | 946ff3b380 | ||
|   | 0c584182d3 | ||
|   | 58ed983b65 | ||
|   | 6f2fc9fe14 | ||
|   | 185f003363 | ||
|   | 6734746707 | ||
|   | 65b9369aaf | ||
|   | e1e4ce7049 | ||
|   | 812aa10119 | ||
|   | b0fa5e5696 | ||
|   | c7a8c372a1 | ||
|   | 73b7d9ace4 | ||
|   | 45c2627d4d | ||
|   | 635bdea284 | ||
|   | 22131d99ad | ||
|   | 4e22388089 | ||
|   | c526413119 | ||
|   | 1336b197d8 | ||
|   | 4b99415215 | ||
|   | 1ed107a2bf | ||
|   | cad5e06041 | ||
|   | 65d93eacd3 | ||
|   | 8369dc6b64 | ||
|   | a0c31478c0 | ||
|   | 647c8df12f | ||
|   | 6ab045b9ad | ||
|   | 2ebac8c186 | ||
|   | 06478f242a | ||
|   | 6557ae0a2b | ||
|   | adbec80596 | ||
|   | 2369ef1296 | ||
|   | ce0ea0434e | ||
|   | ede6ec2a28 | ||
|   | b4b19b551d | ||
|   | dc295927ed | ||
|   | 539cbf3593 | ||
|   | b477d215a8 | ||
|   | 5de85c708f | ||
|   | ad80cc8950 | ||
|   | bc3a506e68 | ||
|   | 92d740ba21 | ||
|   | d65199d2ac | ||
|   | 257b99ebd1 | ||
|   | c8be854365 | ||
|   | 436b764952 | ||
|   | 86063f15ed | ||
|   | 716f209537 | ||
|   | 7bba7fbf37 | ||
|   | a102c99c07 | ||
|   | 5761db5981 | ||
|   | 5d33820bd6 | ||
|   | 301acc920d | ||
|   | 495f9cba72 | ||
|   | 726fc1d4f7 | ||
|   | dd6452fe2a | ||
|   | 19aa57454b | ||
|   | c9555a3537 | ||
|   | 6f473a4f29 | ||
|   | f785f4ad02 | ||
|   | 35d0ba4454 | ||
|   | c3428f1efa | ||
|   | 9cc1e6b85c | ||
|   | 74e1058183 | ||
|   | 7c9f76944b | ||
|   | d393ca4f09 | ||
|   | a786f0bcd2 | ||
|   | 199bc85bce | ||
|   | d15b46f5cb | ||
|   | c0861b1227 | ||
|   | f97a3522e5 | ||
|   | 577e261d1a | ||
|   | 33fcff8a1a | ||
|   | cd30572b70 | ||
|   | bc711b6dec | ||
|   | 01f5065b87 | ||
|   | 9c2bf17ad7 | ||
|   | 5385b36a81 | ||
|   | 72ed9da1fc | ||
|   | d79924bdd1 | ||
|   | 3b7a4b08e2 | ||
|   | 09af4433b0 | ||
|   | 50df3084f7 | ||
|   | e90734913d | ||
|   | af18a0cf40 | ||
|   | 3d399796a6 | ||
|   | 60a2794c8b | ||
|   | e039871298 | ||
|   | e057333bf3 | ||
|   | e0c0d9223e | ||
|   | ada21c975f | ||
|   | b4df747464 | ||
|   | 9a38011e1e | ||
|   | e0f1fc2694 | ||
|   | da5390340e | ||
|   | 3a6b41495a | ||
|   | ce5875f365 | ||
|   | 4373d390dc | ||
|   | 9ed512e5c5 | ||
|   | c5856196a7 | ||
|   | afc3a8a7e7 | ||
|   | 4714c73f0f | ||
|   | 742155c384 | ||
|   | 2d814bfc5e | ||
|   | 8f8e512b37 | ||
|   | 3c828c8387 | ||
|   | 7c96b39bef | ||
|   | 64dbc8aa7f | ||
|   | 5649b9c64a | ||
|   | 3d0aab3b66 | ||
|   | ce419da6ec | ||
|   | 63b49c65ab | ||
|   | ff5bd0b925 | ||
|   | 0892220f63 | ||
|   | 1c95c9e3ff | ||
|   | 4b80cbe1cd | ||
|   | 46f3712421 | ||
|   | 18aa4ff30c | ||
|   | 8a75143a6e | ||
|   | 384a6e8684 | ||
|   | a0008f7471 | ||
|   | b53f95a9a0 | ||
|   | 1af88e52dd | ||
|   | 535028c9a7 | ||
|   | df816368a5 | ||
|   | f34202c3c8 | ||
|   | 28c61754a1 | ||
|   | 6e46166df4 | ||
|   | 15feaa1074 | ||
|   | d7c532daee | ||
|   | 427ec6834d | ||
|   | e594b30ead | ||
|   | f43a8a75e4 | ||
|   | 645aa01efd | ||
|   | 252dce1ec2 | ||
|   | da0066eb84 | ||
|   | adc811ff8f | ||
|   | 5117ccdeef | ||
|   | 19816523c7 | ||
|   | 846e3f8243 | ||
|   | d3c050b88d | ||
|   | f3d1c78c7d | ||
|   | c4f2bb5fe0 | ||
|   | fec511c786 | ||
|   | 8a6157c7c1 | ||
|   | d3bb7903e2 | ||
|   | 239b67eff6 | ||
|   | 0e0caee6ba | ||
|   | 62606c68b9 | ||
|   | da831e894c | ||
|   | 4b166dcc79 | ||
|   | 796fdca5c5 | ||
|   | c6f22826cf | ||
|   | 4e491b6f75 | ||
|   | 0569bb18f5 | ||
|   | 0353ebde5d | ||
|   | b5130c5943 | ||
|   | d54db8ffb3 | ||
|   | 0ff5fc8dbb | ||
|   | 32ec9959ef | ||
|   | 838721fc31 | ||
|   | 63e9c98248 | ||
|   | 6df30cbb64 | ||
|   | c102a89962 | ||
|   | ff5e39ab79 | ||
|   | 607b2efcce | ||
|   | 27d010110a | ||
|   | e13602b896 | ||
|   | 6777670f0d | ||
|   | b05739fa7e | ||
|   | 38854fb06a | ||
|   | b091cfea80 | ||
|   | a8955ffe05 | ||
|   | 475fd72ae1 | ||
|   | 31675d1812 | ||
|   | cbb0c1c091 | ||
|   | c1234f7793 | ||
|   | 1e6c44cb6b | ||
|   | 2202b9330f | ||
|   | 0b332fc019 | ||
|   | 4f4163eb05 | ||
|   | 649d360289 | ||
|   | 9bcdd9c274 | ||
|   | fbc60199bc | ||
|   | 785c813771 | ||
|   | 75f8279a19 | ||
|   | 5d07832e96 | ||
|   | 7395aaf9b4 | ||
|   | 2c070d38fb | ||
|   | 2c5bc4a1a9 | ||
|   | fa82af251f | ||
|   | e08f2a4a04 | ||
|   | 30e17036e8 | ||
|   | c75785efff | ||
|   | 3bf89055e3 | ||
|   | a76869216a | ||
|   | a9fd8bfa5e | ||
|   | 02a7b0dcfd | ||
|   | dd8c06f2c5 | ||
|   | c63b7f0c3f | ||
|   | a46ea3f8a0 | ||
|   | 93071d9167 | ||
|   | 2b2e9d4098 | ||
|   | 0429aad8bf | ||
|   | 2e74920a64 | ||
|   | b5bf82b5db | ||
|   | 98cdd44543 | ||
|   | 304b68eff9 | ||
|   | 18074951b9 | ||
|   | 0008ef70e1 | ||
|   | c3cdbd0dac | ||
|   | aefd61c3db | ||
|   | f7752ac699 | ||
|   | d017e6749c | ||
|   | ddfdfaed78 | ||
|   | a870a4d6de | ||
|   | e6e786a19c | ||
|   | 01975b61f5 | ||
|   | 3c61bef92d | ||
|   | 050f2090fb | ||
|   | 7746f1a5c4 | ||
|   | 97a1cdbe7a | ||
|   | b1dcea7cf1 | ||
|   | 9f6f48025d | ||
|   | 5e66ac2674 | ||
|   | 93c66b3537 | ||
|   | e0d7d28c20 | ||
|   | a82c564a73 | ||
|   | 6a40e72329 | ||
|   | 3737a9950c | ||
|   | 7784bc0905 | ||
|   | 49607e1313 | ||
|   | a81f18592a | ||
|   | 2ff988ef37 | ||
|   | 5136369c18 | ||
|   | 7557207b47 | ||
|   | f9d1e2fec0 | ||
|   | f9a03f212c | ||
|   | d200fb1d14 | ||
|   | eb844b095a | ||
|   | dc638c04a6 | ||
|   | b3cbce97ed | ||
|   | 14a32c128d | ||
|   | 2e332ffd12 | ||
|   | 71d9d483f2 | ||
|   | 1ef6262139 | ||
|   | da7db509e7 | ||
|   | 2a525bd8e8 | ||
|   | 51da2bf363 | ||
|   | d2c0ade880 | ||
|   | bcc3eccdab | ||
|   | e581e249ad | ||
|   | c131c44ef6 | ||
|   | cdd23d9cc7 | ||
|   | dc110db4e0 | ||
|   | 678670bf1b | ||
|   | 07fc15a1eb | ||
|   | 83005e27da | ||
|   | 56b20ef779 | ||
|   | 04adeec8f5 | ||
|   | 62d640cd9e | ||
|   | 9d7ad6748c | ||
|   | accd24e379 | ||
|   | 75d797a0ac | ||
|   | e1e321d3a7 | ||
|   | f14f238150 | ||
|   | ab3200019a | ||
|   | 28c028f7ac | ||
|   | 08a159d0d8 | ||
|   | 26aa32c6cc | ||
|   | 09e6bb5d56 | ||
|   | e661d904de | ||
|   | 407d51e871 | ||
|   | c4a07fad83 | ||
|   | 9ea8fdc1d1 | ||
|   | 3d6bf43649 | ||
|   | 7bc1d57ad7 | ||
|   | 68b671a4f4 | ||
|   | 1de1fd44c5 | ||
|   | e5101eb407 | ||
|   | 48e0d86340 | ||
|   | c64cf30160 | ||
|   | ff9d777c01 | ||
|   | d8c33deb33 | ||
|   | c03ff0bd4f | ||
|   | d597633d8f | ||
|   | 4f6eb49be3 | ||
|   | d23bd4c4a6 | ||
|   | 7f12265685 | ||
|   | 250023b823 | ||
|   | 3b52691ab4 | ||
|   | b6cd548186 | ||
|   | b3fabf11b1 | ||
|   | 15be562fdf | ||
|   | 599fa0b76c | ||
|   | c520eb4de0 | ||
|   | e33c68a415 | ||
|   | 97bd224261 | ||
|   | 503e086c56 | ||
|   | 7dd6b7f04f | ||
|   | 0b05b7a527 | ||
|   | 7e5274619a | ||
|   | 900957d658 | ||
|   | e006b9b400 | ||
|   | d042dd73aa | ||
|   | 347972e45f | ||
|   | 7e803fdf23 | ||
|   | ccacb5f6de | ||
|   | 5117139add | ||
|   | d5c0514e21 | ||
|   | d81a6bdf41 | ||
|   | 2812afed22 | ||
|   | f5bd86fa11 | ||
|   | b625f1692c | ||
|   | 36ac1f8902 | ||
|   | bec4b5c127 | ||
|   | 78cd8b9f9a | ||
|   | 205880e74e | ||
|   | 009d021e4f | ||
|   | 39a840e2c3 | ||
|   | 56d0d7253b | ||
|   | 7a29cc47d4 | ||
|   | d769b72c43 | ||
|   | a3439a5230 | ||
|   | 9f45161133 | ||
|   | ed99bef458 | ||
|   | c9ad54cd82 | ||
|   | d7e42d5d6e | ||
|   | 5b339c8371 | ||
|   | 9c6e68f3e7 | ||
|   | 70526a8025 | ||
|   | c405081d89 | ||
|   | 384e01b368 | ||
|   | f819654ec8 | ||
|   | 4670db6629 | ||
|   | 6ebabd50c6 | ||
|   | 0c22a21a24 | ||
|   | 70a0c4211c | ||
|   | a21af541c4 | ||
|   | e07ba91486 | ||
|   | a22e9ce9cd | ||
|   | d4ce51b1b7 | ||
|   | 830005069c | ||
|   | 8c49267658 | ||
|   | d0bab1f7ac | ||
|   | f00826f3fb | ||
|   | 717c0ea19f | ||
|   | 3cd8dd0f32 | ||
|   | aedd616346 | ||
|   | b3de3ff00b | ||
|   | b66dff8aed | ||
|   | ff814df03a | ||
|   | c9a528025c | ||
|   | 5f3ec8a6f1 | ||
|   | becd29c50a | ||
|   | a029a35050 | ||
|   | d6a78d61d1 | ||
|   | 9be46bd212 | ||
|   | 4df3afced0 | ||
|   | 90f8e1df80 | ||
|   | ea2ac89e61 | ||
|   | 695bfb9616 | ||
|   | 5dbdde8c15 | ||
|   | 854bfc17ed | ||
|   | c8b192bcae | ||
|   | f07306897f | ||
|   | 44d731fff5 | ||
|   | 52f939f64c | ||
|   | f1d12c18d8 | ||
|   | 373ebb50eb | ||
|   | b0b5be63b9 | ||
|   | 68f439425b | ||
|   | cae4d92191 | ||
|   | f336295475 | ||
|   | 06d0e7d74a | ||
|   | 5e0e3edc7b | ||
|   | 02a51bfa65 | ||
|   | dbd04df311 | ||
|   | e6adcd99c7 | ||
|   | 3171819c36 | ||
|   | e4976b535d | ||
|   | 06d1602f17 | ||
|   | 603d0dfbc3 | ||
|   | 0b6c5b4620 | ||
|   | d7894dbc44 | ||
|   | d25d8126ea | ||
|   | 2402d3e115 | ||
|   | ac4284d348 | ||
|   | f9ef80749a | ||
|   | 22f0d3076e | ||
|   | 127b6dca1c | ||
|   | 8cfff33291 | ||
|   | 21f534c7f0 | ||
|   | 9faa1db942 | ||
|   | 2fc9bffb23 | ||
|   | f16eae45a2 | ||
|   | 622e73c34c | ||
|   | eb1c3d7045 | ||
|   | fc26895f70 | ||
|   | ca224b379f | ||
|   | 42b609b852 | ||
|   | a24169e2aa | ||
|   | 144d1dca88 | ||
|   | 38ab6ba3ec | ||
|   | bc6a0626c1 | ||
|   | 497f4f4a3c | ||
|   | 8c8c89a5f1 | ||
|   | d658a5148a | ||
|   | 2c3e0dab0f | ||
|   | e2400e35bd | ||
|   | fc4562491b | ||
|   | 6e3bedb888 | ||
|   | 2b8332df5e | ||
|   | 795e7d4b59 | ||
|   | 522d46a525 | ||
|   | dcecf5e350 | ||
|   | a61c929450 | ||
|   | 5992e9f524 | ||
|   | ee4cd05ebd | ||
|   | af5e65fd79 | ||
|   | 284cbf3ea7 | ||
|   | c3e68955e3 | ||
|   | ffa28e7ee7 | ||
|   | 78d81f909c | ||
|   | 34551bf48b | ||
|   | e02cd53ac5 | ||
|   | b7f5154bd3 | ||
|   | d80247918c | ||
|   | bb8c2d3e13 | ||
|   | 6e0e37519d | ||
|   | 5c19ae5878 | ||
|   | 8713473c64 | ||
|   | a4b4f2f6fd | ||
|   | 03f22135b5 | ||
|   | 5b79f657eb | ||
|   | 4fc0c513af | ||
|   | f5c058a036 | ||
|   | 0dac0ad516 | ||
|   | 5d4d1e26fd | ||
|   | 2f0039e7dd | ||
|   | 9202309bed | ||
|   | 7e0e224e0e | ||
|   | 35825cf0c7 | ||
|   | b2d79b6362 | ||
|   | f5e1dc86cd | ||
|   | 8c51f0076f | ||
|   | 8347152730 | ||
|   | 3d77dd8f51 | ||
|   | 82e6935281 | ||
|   | 61070e6ec0 | ||
|   | 128697d6a7 | ||
|   | 424fa01eca | ||
|   | 775187b2e4 | ||
|   | 99b5e10acf | ||
|   | 208ed6e90d | ||
|   | de8d4c4cd8 | ||
|   | 3ae591434d | ||
|   | 61fb62f969 | ||
|   | c4584c20dc | ||
|   | c6eb95ff3a | ||
|   | ab68360d53 | ||
|   | 58a733dc93 | ||
|   | db8f6b4848 | ||
|   | e72d388a67 | ||
|   | b5dd4d1456 | ||
|   | a7562b4148 | ||
|   | 358f64d66b | ||
|   | 2a7f9f70b8 | ||
|   | c91085caf7 | ||
|   | 21ddbf0b8f | ||
|   | 2ad2853278 | ||
|   | e16f63a8e4 | ||
|   | c39ffa111f | ||
|   | 5182129196 | ||
|   | 02bad8e92b | ||
|   | 500ea13155 | ||
|   | ce8e7481e7 | ||
|   | 528f2e3cdc | ||
|   | 408211ba73 | ||
|   | e02467eada | ||
|   | 3e93a1bdd6 | ||
|   | a69a9ba51a | ||
|   | 1c0e0191e0 | ||
|   | f233122d4d | ||
|   | 72bfa91259 | ||
|   | ef195f0185 | ||
|   | 47da6b139e | ||
|   | 2705434955 | ||
|   | a7e030d0f9 | ||
|   | 8a44170f83 | ||
|   | 2ea6ae05e5 | ||
|   | 5c0eba7d3b | ||
|   | f81887772a | ||
|   | 9aa3d864dc | ||
|   | 2425b11df6 | ||
|   | 1cb644529f | ||
|   | 6cff0fd994 | ||
|   | 437256e0f1 | ||
|   | 841e0132e9 | ||
|   | 1545596c7e | ||
|   | 38406e070c | ||
|   | 9b07ce1d0d | ||
|   | bf8d30603e | ||
|   | bd6e7f14d1 | ||
|   | 5226d8b086 | ||
|   | bd92331780 | ||
|   | 387f5143f0 | ||
|   | b7867fe44c | ||
|   | ddea54a539 | ||
|   | 84da49c715 | ||
|   | 513dff45c1 | ||
|   | 3d697baa26 | ||
|   | 9eda10d091 | ||
|   | 0f82da8735 | ||
|   | 296b61b61c | ||
|   | 974a896dbc | ||
|   | 093fd54e2b | ||
|   | 5ae3e5aeb7 | ||
|   | af28a219be | ||
|   | 1883df2927 | ||
|   | 3274f270e3 | ||
|   | c97626e516 | ||
|   | 9c483dd9a6 | ||
|   | 8fd6e93fbe | ||
|   | 8416ba25de | ||
|   | 63f6ff9151 | ||
|   | 59c2ace98c | ||
|   | cf4465027a | ||
|   | a23b293fe2 | ||
|   | c039a3ddda | ||
|   | ebe071bd50 | ||
|   | 2edfd458b7 | ||
|   | 3b750aa815 | ||
|   | d96f97e55a | ||
|   | 55ac2f46ba | ||
|   | 262c04ab91 | ||
|   | c80e7784fb | ||
|   | 763567377f | ||
|   | ea9dcbf8b7 | ||
|   | 2b383bf73e | ||
|   | b8dcbfebb2 | ||
|   | a257aa35d5 | ||
|   | 941513b280 | ||
|   | a9bb0558bc | ||
|   | 58834b80d7 | ||
|   | b125a0e371 | ||
|   | 980ac0da8a | ||
|   | f15fb0bd73 | ||
|   | 7d25592165 | ||
|   | 9ee36f2b71 | ||
|   | f3e226103f | ||
|   | a03e5662e2 | ||
|   | 16bb3efeb9 | ||
|   | e01d867e69 | ||
|   | 1d4dfd7d5f | ||
|   | da05d1d864 | ||
|   | 6499424eba | ||
|   | 824a602963 | ||
|   | 0092d8fff3 | ||
|   | 5fbe454836 | ||
|   | 23a59d387b | ||
|   | 6b2a68e553 | ||
|   | 82950ecea0 | ||
|   | 2da4df219c | ||
|   | 8b5597fd8d | ||
|   | 82ae06ad55 | ||
|   | c594b66a2a | ||
|   | 13e3318e4d | ||
|   | 4aa4bb4a6a | ||
|   | 4a74df86e2 | ||
|   | 30ebc9c241 | ||
|   | e0f6cf538d | ||
|   | ba71382c72 | ||
|   | 87eb0d7e2f | ||
|   | da199242b2 | ||
|   | 5e3d378667 | ||
|   | a6d2735373 | ||
|   | 2ea435a928 | ||
|   | 75ba75900c | ||
|   | 7bacf4e344 | ||
|   | 258993d8ad | ||
|   | ff19e411a2 | ||
|   | e86c2826ef | ||
|   | 64741e41df | ||
|   | fceac27412 | ||
|   | 0a394799da | ||
|   | b83f2c0e33 | ||
|   | 225f825faf | ||
|   | ed054f4f40 | ||
|   | a1244a9b26 | ||
|   | 3a3714f4ff | ||
|   | be483c4137 | ||
|   | 3492121c3f | ||
|   | 364f1453c1 | ||
|   | 9392e50cf1 | ||
|   | f60a6ab465 | ||
|   | f3a92d558d | ||
|   | d387d4a60d | ||
|   | 4503ca4ca0 | ||
|   | eeb10c0ce3 | ||
|   | c69fcc2ef8 | ||
|   | 7d67d88ae8 | ||
|   | 310622b14e | ||
|   | 00065b84e1 | ||
|   | 4f7a8902f8 | ||
|   | d20a6e0988 | ||
|   | 96fa7b7e77 | ||
|   | 61e4d40852 | ||
|   | cc826f77fb | ||
|   | 1de288c5bb | ||
|   | fe986faa2d | ||
|   | f11b404622 | ||
|   | 508668107b | ||
|   | 674454621f | ||
|   | 2c907bd422 | ||
|   | 4eb6c0c4ee | ||
|   | dd2eff2b6f | ||
|   | d8960b396b | ||
|   | 4c29604e1e | ||
|   | ede0bf2d88 | ||
|   | 893bfdf85f | ||
|   | ac7a4c27fd | ||
|   | 4fe0233139 | ||
|   | f9f9c7fe5b | ||
|   | f1b90f174a | ||
|   | 3dd74c86c1 | ||
|   | 0edbdc529e | ||
|   | b08523b170 | ||
|   | cd2f8d8177 | ||
|   | a1e8c79d38 | ||
|   | 34fc454764 | ||
|   | e98a434ede | ||
|   | 02cdc065e7 | ||
|   | 77eb8f98c0 | ||
|   | 68679e1e7e | ||
|   | 05f2dbf205 | ||
|   | 45779bc7be | ||
|   | 1bf27b1f18 | ||
|   | 3ecdfaffd2 | ||
|   | ee8fd1e613 | ||
|   | ccfd5e35d6 | ||
|   | b2eab65a81 | ||
|   | 00814dd159 | ||
|   | 57f9ffcaa7 | ||
|   | ce83f378a5 | ||
|   | 30c7545ff3 | ||
|   | a13357c2a8 | ||
|   | 75dc5c16c8 | ||
|   | acd5d70209 | ||
|   | 669d20bcf0 | ||
|   | 793a8005a5 | ||
|   | 23b81fb241 | ||
|   | 73ba9d33eb | ||
|   | 75e2a7228c | ||
|   | 91d70f2487 | ||
|   | 779b1ae8e5 | ||
|   | 346ffd14d7 | ||
|   | 558b51f555 | ||
|   | ef18b7ea25 | ||
|   | 0044e225aa | ||
|   | c25c143b24 | ||
|   | b87da87252 | ||
|   | f9258bb5e3 | ||
|   | 3d747b00e6 | ||
|   | 450345b4d0 | ||
|   | 7563e1ebcd | ||
|   | f96cc4dd69 | ||
|   | c65a9c4d2e | ||
|   | c01bd37edc | ||
|   | 021cecbce2 | ||
|   | 2a0cb7ff05 | ||
|   | 0085a94706 | ||
|   | 5db38194dc | ||
|   | 2a26143149 | ||
|   | 62f22557aa | ||
|   | 516ea5a02e | ||
|   | 9154471aba | ||
|   | fbf6746acf | ||
|   | a36686a6aa | ||
|   | fd837d74d1 | ||
|   | fd57334395 | ||
|   | 85100cb65f | ||
|   | c70f6278d6 | ||
|   | e573441bac | ||
|   | 5ed9571b37 | ||
|   | 9d203ddc0f | ||
|   | 508a13ae72 | ||
|   | c0b50cbdf2 | ||
|   | 06f78549bd | ||
|   | 67afd7a6d8 | ||
|   | ffa8c2f2b1 | ||
|   | 3803a880e8 | ||
|   | 965aedb0bb | ||
|   | 80911535a7 | ||
|   | ca401d5036 | ||
|   | a1c091d98d | ||
|   | e1e08f0a68 | ||
|   | 1a4f629554 | ||
|   | 4113be770b | ||
|   | cf3f4850b8 | ||
|   | 14f374096a | ||
|   | 6fef5c37f7 | ||
|   | 6e7455aa1e | ||
|   | 03bf6fa399 | ||
|   | f777e761c0 | ||
|   | 31201d9618 | ||
|   | d95d78ac15 | ||
|   | bef4f17c49 | ||
|   | f4b7ab0cb6 | ||
|   | a180dde01c | ||
|   | 385c918e2e | ||
|   | d7401c8646 | ||
|   | a456d5eb19 | ||
|   | 6c08799c7b | ||
|   | 8d7bb6496c | ||
|   | f0496a2d3c | ||
|   | 6664553b7e | ||
|   | 2a950ca3b3 | ||
|   | 463cd6382c | ||
|   | 78db025b10 | ||
|   | f3265c28a9 | ||
|   | a6e5e459d3 | ||
|   | 9ba399bf18 | ||
|   | a52c91e9e5 | ||
|   | 9a7b47c23f | ||
|   | 18f7d20006 | ||
|   | 3f0ee88657 | ||
|   | c634718dfa | ||
|   | df6b31de05 | ||
|   | b8e29ae8c7 | ||
|   | cdba8e5cea | ||
|   | 4fccdaafb7 | ||
|   | df0b465e76 | ||
|   | aacdd4fd5e | ||
|   | 5f68c3a324 | ||
|   | 8ceae3b054 | ||
|   | 3fc5afaff1 | ||
|   | 8b7464c648 | ||
|   | 3e602b1765 | ||
|   | 5858028411 | ||
|   | 2c682ace81 | ||
|   | 45f3106814 | ||
|   | aa947f9948 | ||
|   | 0c72d1fcbd | ||
|   | 6d22670307 | ||
|   | 0b9e68e305 | ||
|   | da8155cbe5 | ||
|   | 5b7a052e18 | ||
|   | 6f26e39082 | ||
|   | 489b96a310 | ||
|   | e65d90d624 | ||
|   | 9c74e22313 | ||
|   | 48a1fce151 | ||
|   | 83e7f6f496 | ||
|   | 3c5c3a6597 | ||
|   | 1c3ea1649f | ||
|   | 48a54e8ac4 | ||
|   | 113a854048 | ||
|   | 882f5fa79e | ||
|   | 9acdb8012c | ||
|   | 731d64e0e4 | ||
|   | ccf1bd9f27 | ||
|   | c164a8fe03 | ||
|   | 1a39666f7c | ||
|   | f2731d4d6a | ||
|   | 3e63fb7abe | ||
|   | 8b4249ef26 | ||
|   | bf0be6ef12 | ||
|   | 294702d3f1 | ||
|   | 58f3b7c748 | ||
|   | e25502aeb2 | ||
|   | ffe4eaf00d | ||
|   | 2f88a7a1e1 | ||
|   | cd7d564125 | ||
|   | ede81017ec | ||
|   | a539e6236a | ||
|   | 90b7710834 | ||
|   | f8cc8f1dc1 | ||
|   | f8e5e3e435 | ||
|   | 508e751ffd | ||
|   | 207c847762 | ||
|   | 3c980566d3 | ||
|   | 50b59e0ca6 | ||
|   | 14c52bb00a | ||
|   | 9720b32987 | ||
|   | 674ae262c8 | ||
|   | db297e7fdb | ||
|   | d57c146514 | ||
|   | ec5a4328e3 | ||
|   | e4ee72c481 | ||
|   | 31f1e9ff0a | ||
|   | d6c049a8c9 | ||
|   | 44047ac881 | ||
|   | 464d5d8a13 | ||
|   | 29811a85dc | ||
|   | 36ee4e6c3b | ||
|   | 2036e4c85c | ||
|   | f24034de84 | ||
|   | 522ff86081 | ||
|   | 669e3c8ed9 | ||
|   | ce850f464c | ||
|   | 03eaa61cef | ||
|   | e10e953d24 | ||
|   | 2b47bb3d82 | ||
|   | 73d819116c | ||
|   | c8dd984663 | ||
|   | 6087eb6d0e | ||
|   | 97b43d1d36 | ||
|   | 57ebadbaf8 | ||
|   | cad7bb1151 | ||
|   | 9a376d47c5 | ||
|   | b79adc05f4 | ||
|   | f765c5e319 | ||
|   | edc445c0c9 | ||
|   | ab6b0f3f7f | ||
|   | 5e7902e733 | ||
|   | f9f821aa55 | ||
|   | 9ed4b2a5ae | ||
|   | 0130ced790 | ||
|   | 8dab07af82 | ||
|   | f6cd3fa5ed | ||
|   | faae1a028e | ||
|   | f5e32184fe | ||
|   | 18c7138237 | ||
|   | 3f0fbae7e2 | ||
|   | 7f1a258ff9 | ||
|   | 35b38d5cb2 | ||
|   | 09e8a437d4 | ||
|   | d2bedcc182 | ||
|   | 84eda6e459 | ||
|   | f5e7530fc7 | ||
|   | f983b34784 | ||
|   | 36bbe64898 | ||
|   | 627a393ed6 | ||
|   | a025b151ef | ||
|   | 18b6f13395 | ||
|   | 051413550f | ||
|   | 3e10574736 | ||
|   | 9a3041004b | ||
|   | 87f71b8ce1 | ||
|   | 825146f1e3 | ||
|   | 52995416fd | ||
|   | 6c43d0247a | ||
|   | 9aa98d9f0c | ||
|   | c3a29d6df1 | ||
|   | 82f84416a9 | ||
|   | 9dd3162dbe | ||
|   | 7ef519756a | ||
|   | 3bbe74d1c1 | ||
|   | 409f6718b8 | ||
|   | 9c0e179080 | ||
|   | b3b278d41f | ||
|   | 0f466dbafb | ||
|   | fbb4a9a3a6 | ||
|   | 8ddae5cd71 | ||
|   | b0915c7b60 | ||
|   | 831bb4e334 | ||
|   | 4e025506fa | ||
|   | abccf451bf | ||
|   | 14954117c0 | ||
|   | 629f408fe5 | ||
|   | 86c6ab3c01 | ||
|   | 4a6ff94701 | ||
|   | e480b08d58 | ||
|   | caf53861d1 | ||
|   | d0480648ba | ||
|   | eb8cfe799f | ||
|   | b9f2541880 | ||
|   | bde9b08bfe | ||
|   | 8a4c862633 | ||
|   | 785c90f4b8 | ||
|   | dd6a11e4c7 | ||
|   | 64e9503adb | ||
|   | 36c885bf34 | ||
|   | ad7cde805d | ||
|   | 2c2c67f4dc | ||
|   | cc4f8dfab0 | ||
|   | 0fb13608c5 | ||
|   | 09dbe17da0 | ||
|   | fdd347c9aa | ||
|   | 572095515b | ||
|   | f2d4aa0822 | ||
|   | 030a22d795 | ||
|   | c70afcdb44 | ||
|   | 526d6c03b8 | ||
|   | 261b55300d | ||
|   | e13bfd9a17 | ||
|   | e096d18bac | ||
|   | 9460f0e4f3 | ||
|   | e6591f52ac | ||
|   | 07e3d1fd5c | ||
|   | 1fbc6b24c8 | ||
|   | 982777be94 | ||
|   | 6610a34ad0 | ||
|   | bfa8a0441a | ||
|   | 7723622ec7 | ||
|   | 6bcc8c70ef | ||
|   | d114d5f95a | ||
|   | b5c734da42 | ||
|   | 2077bb94c1 | ||
|   | 65a4ee7fb4 | ||
|   | debf293298 | ||
|   | a0df3e7d1e | ||
|   | 0d67c2d164 | ||
|   | 682bd7b622 | ||
|   | fa0e54edbb | ||
|   | 5a0b209663 | ||
|   | 7e8859fd0e | ||
|   | 444fa2e0ab | ||
|   | a31455b921 | ||
|   | ac0213a516 | ||
|   | 45a6e2c305 | ||
|   | 11cbd396c0 | ||
|   | a343445cd2 | ||
|   | be3d62487c | ||
|   | 58905bd01a | ||
|   | 08506eac2d | ||
|   | 02c6b0374d | ||
|   | 0722c06275 | ||
|   | 17a4044d97 | ||
|   | 27a7194634 | ||
|   | d73f560bcc | ||
|   | e92f43b83e | ||
|   | fed79ce4e6 | ||
|   | fc45cf03bf | ||
|   | efde11a0f3 | ||
|   | fb951ff9b5 | ||
|   | f5865e895e | ||
|   | b8c2d4c6c7 | ||
|   | 778ad49ab4 | ||
|   | fe7dd1305f | ||
|   | 378a3df5ea | ||
|   | e63b81d69c | ||
|   | c2fa2cdd8a | ||
|   | 6f215427f8 | ||
|   | 67ed4e0570 | ||
|   | 8a15178557 | ||
|   | f3ecfab378 | ||
|   | 804563d5b2 | ||
|   | c3e5d983b9 | ||
|   | d21edcfed5 | ||
|   | 9b69a45eee | ||
|   | 7424ee755a | ||
|   | ff664fd1d8 | ||
|   | fd3f03580d | ||
|   | a09150846a | ||
|   | f2a9c55637 | ||
|   | eaa3f83e46 | ||
|   | e786cc1454 | ||
|   | cd0c632fcb | ||
|   | d5f248cb82 | ||
|   | 6a800abe06 | ||
|   | fe265554a7 | ||
|   | 7305466765 | ||
|   | 9ac55a98f1 | ||
|   | a1149fb6ad | ||
|   | dfc4cc4aaf | ||
|   | ef7541291b | ||
|   | 248a3e6b7e | ||
|   | 6b1e381750 | ||
|   | feaf6108f9 | ||
|   | 9ad104585d | ||
|   | 7c44af3616 | ||
|   | 0599bf41b0 | ||
|   | eac303f84c | ||
|   | 7bdd1c625c | ||
|   | 0003760fd9 | ||
|   | eafb8c8e38 | ||
|   | 60c8105559 | ||
|   | 54626c6f7e | ||
|   | cca528a630 | ||
|   | 530193a3a2 | ||
|   | 52e3149040 | ||
|   | ad297ea9dc | ||
|   | 2015fc97dc | ||
|   | 35889a0f7d | ||
|   | dcd84a4b53 | ||
|   | 01374989b1 | ||
|   | f300462003 | ||
|   | 1e4da1b99c | ||
|   | e1de6cb98d | ||
|   | 59a18c4ead | ||
|   | b881e4b62a | ||
|   | 7060ae077b | ||
|   | a7b0910566 | ||
|   | 60706f72d4 | ||
|   | 2e77f6b34b | ||
|   | 50d5030949 | ||
|   | 03dbb0f931 | ||
|   | 249619fabd | ||
|   | 60d1f7797c | ||
|   | dc4b8c876e | ||
|   | 1724723e63 | ||
|   | 02455b1e28 | ||
|   | 47a9b97f8b | ||
|   | 0aa29daa72 | ||
|   | 182b1c1941 | ||
|   | bbc8010de3 | ||
|   | 15baa56584 | ||
|   | a72683707f | ||
|   | f4baa4ddf8 | ||
|   | f9eb36434f | ||
|   | bc5e16bcea | ||
|   | 86e04048ff | ||
|   | d7c0ff5e89 | ||
|   | 51e1efa277 | ||
|   | 67f636cc68 | ||
|   | fcdfebd0e7 | ||
|   | 069ec3b910 | ||
|   | 1092f55b54 | ||
|   | 8a8abf12f9 | ||
|   | f044e29526 | ||
|   | 4f703019ca | ||
|   | 0e6baec350 | ||
|   | 08690d658f | ||
|   | 674325e96c | ||
|   | f8aa486ad1 | ||
|   | be78f0f36a | ||
|   | 9917f05be8 | ||
|   | d23228522c | ||
|   | 8c9896561e | ||
|   | 23cdb2125e | ||
|   | 43fc598bd5 | ||
|   | 2105d2f952 | 
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -16,11 +16,8 @@ config.log | ||||
| config.status | ||||
| config | ||||
| configure | ||||
| data/50-gnome-shell-*.xml | ||||
| data/gnome-shell.desktop | ||||
| data/gnome-shell.desktop.in | ||||
| data/gnome-shell-wayland.desktop | ||||
| data/gnome-shell-wayland.desktop.in | ||||
| data/org.gnome.Shell.desktop | ||||
| data/org.gnome.Shell.desktop.in | ||||
| data/gnome-shell-extension-prefs.desktop | ||||
| data/gnome-shell-extension-prefs.desktop.in | ||||
| data/gnome-shell-theme.gresource | ||||
| @@ -28,8 +25,6 @@ data/gschemas.compiled | ||||
| data/perf-background.xml | ||||
| data/org.gnome.shell.gschema.xml | ||||
| data/org.gnome.shell.gschema.valid | ||||
| data/org.gnome.shell.evolution.calendar.gschema.xml | ||||
| data/org.gnome.shell.evolution.calendar.gschema.valid | ||||
| data/org.gnome.Shell.PortalHelper.desktop | ||||
| data/org.gnome.Shell.PortalHelper.service | ||||
| data/theme/.sass-cache | ||||
| @@ -76,7 +71,6 @@ src/*-marshal.[ch] | ||||
| src/Makefile | ||||
| src/Makefile.in | ||||
| src/calendar-server/evolution-calendar.desktop | ||||
| src/calendar-server/evolution-calendar.desktop.in | ||||
| src/calendar-server/org.gnome.Shell.CalendarServer.service | ||||
| src/gnome-shell | ||||
| src/gnome-shell-calendar-server | ||||
|   | ||||
							
								
								
									
										629
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						| @@ -1,3 +1,632 @@ | ||||
| 3.24.3 | ||||
| ====== | ||||
| * Bypass proxies for captive portal [Bastien; #769692] | ||||
| * Fix missing icons in freedesktop notifications [Florian; #784245] | ||||
| * Fix blocked clicks in shutdown dialog [Florian; #781738] | ||||
| * Implement tablet rings/strips configuration [Carlos; #782033] | ||||
| * Misc. bug fixes [Matthias, Jeremy, Bastien, Florian; #780215, #782802, | ||||
|   #783286, #784130, #784353, #781471] | ||||
|  | ||||
| Contributors: | ||||
|   Jeremy Bicha, Carlos Garnacho, Matthias Liertzer, Florian Müllner, | ||||
|   Bastien Nocera | ||||
|  | ||||
| Translations: | ||||
|   Christian Stadelmann [de], Марко Костић [sr], Милош Поповић [sr@latin], | ||||
|   Furkan Ahmet Kara [tr], Jeremy Bicha [es, he] | ||||
|  | ||||
| 3.24.2 | ||||
| ====== | ||||
| * Only fetch weather information when there's a valid location [Rares; #780404] | ||||
| * Handle extension errors during reload due to settings change [Emilio; #781728] | ||||
| * Fix StEntry::primary-icon-clicked emission [Florian; #782190] | ||||
| * Allow search providers to provide clipboard text for results [Daiki; #775099] | ||||
| * Misc. bug fixes [Florian; #781545] | ||||
|  | ||||
| Contributors: | ||||
|   Florian Müllner, Emilio Pozuelo Monfort, Daiki Ueno, Rares Visalom | ||||
|  | ||||
| Translations: | ||||
|   Milo Casagrande [it], Милош Поповић [sr], Khaled Hosny [ar] | ||||
|  | ||||
| 3.24.1 | ||||
| ====== | ||||
| * Close Wifi selection dialog on lock [Florian; #780054] | ||||
| * Fix DND over window previews in overview [Florian; #737166] | ||||
| * Do not lock the screen when disabled by lockdown settings [Florian; #780212] | ||||
| * Follow GNOME Weather's location permissions [Florian; #780252] | ||||
| * Fix portals that require a new window to be loaded [Catalin; #759044] | ||||
| * Fix restricting menus to screen height on HiDPI displays [Cosimo; #753305] | ||||
| * Misc. bug fixes and cleanups [Florian, Cosimo, Bastien, Catalin, Carlos; | ||||
|   #780063, #780321, #780381, #780453, #758873, #780606, #642652] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Carlos Garnacho, Catalin Iacob, Florian Müllner, Bastien Nocera | ||||
|  | ||||
| Translations: | ||||
|   Marek Cernocky [cs], Piotr Drąg [pl], Anders Jonsson [sv], Stas Solovey [ru], | ||||
|   Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Daniel Korostil [uk], | ||||
|   Kukuh Syafaat [id], Milo Casagrande [it], Jiri Grönroos [fi], | ||||
|   Daniel Mustieles [es], Balázs Úr [hu], Guillaume Bernard [fr], | ||||
|   Changwoo Ryu [ko], Mario Blättermann [de], Fran Dieguez [gl], | ||||
|   Dušan Kazik [sk], Yuras Shumovich [be], Fabio Tomat [fur], | ||||
|   Kjartan Maraas [nb], Aurimas Černius [lt], Trần Ngọc Quân [vi], | ||||
|   Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Tom Tryfonidis [el], gogo [hr] | ||||
|  | ||||
| 3.24.0 | ||||
| ====== | ||||
|  | ||||
| Translations: | ||||
|   GNOME Translation Robot [tg], Мирослав Николић [sr, sr@latin], | ||||
|   Guillaume Bernard [fr], Rūdolfs Mazurs [lv], Emin Tufan Çetin [tr], | ||||
|   sujiniku [ja], Daniel Korostil [uk] | ||||
|  | ||||
| 3.23.92 | ||||
| ======= | ||||
| * Implement DND to overview on wayland [Hyungwon; #765003] | ||||
| * Make telepathy optional at runtime [Florian; #771721, #779878] | ||||
| * Don't show forecasts for NYC when geoclue gets stuck [Sebastian; #779898] | ||||
| * Add bottom edge drag gesture to bring up the OSK [Jan-Michael; #757712] | ||||
| * Allow switching between pads in the same group [Carlos; #779986] | ||||
| * Ignore showBanners policy for critical notifications [Florian; #779974] | ||||
| * Misc. bug fixes [Florian; #779435, #779819, #779820] | ||||
|  | ||||
| Contributors: | ||||
|   Jan-Michael Brummer, Allan Day, Carlos Garnacho, Hyungwon Hwang, | ||||
|   Sebastian Keller, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Enrico Nicoletto [pt_BR], Jiri Grönroos [fi], Chao-Hsiung Liao [zh_TW], | ||||
|   Piotr Drąg [pl], Piotr Drąg [he], Balázs Meskó [hu], Kris Thomsen [da], | ||||
|   Yuras Shumovich [be], Sveinn í Felli [is], Inaki Larranaga Murgoitio [eu], | ||||
|   Changwoo Ryu [ko], Jordi Mas [ca], Aurimas Černius [lt], | ||||
|   Мирослав Николић [sr, sr@latin], Christian Kirbach [de], Anders Jonsson [sv], | ||||
|   Fabio Tomat [fur], GNOME Translation Robot [gd], Dušan Kazik [sk], | ||||
|   Kukuh Syafaat [id], Marek Černocký [cs], Stas Solovey [ru], | ||||
|   Milo Casagrande [it], Fran Dieguez [gl], Daniel Boles [gl], A S Alam [pa], | ||||
|   Daniel Mustieles [es] | ||||
|  | ||||
| 3.23.91 | ||||
| ======= | ||||
| * Use the original timestamps for restored notifications [Florian; #766410] | ||||
| * Add weather information to date+time drop-down [Florian; #754031] | ||||
| * Refine message list layout in date+time drop-down [Florian; #775763] | ||||
| * Make next/prev media controls insensitive when unavailable [Florian; #773884] | ||||
| * Misc. bug fixes [Piotr, Bastien, Florian; #772210, #769546, #775799] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Carlos Garnacho, Florian Müllner, Bastien Nocera | ||||
|  | ||||
| Translations: | ||||
|   Baurzhan Muftakhidinov [kk], Jordi Mas [ca], Ask Hjorth Larsen [da], | ||||
|   Inaki Larranaga Murgoitio [eu], Daniel Mustieles [es], Dušan Kazik [sk], | ||||
|   Aurimas Černius [lt], Jiri Grönroos [fi], Kjartan Maraas [nb], | ||||
|   Piotr Drąg [pl], Daniel Korostil [uk], Kukuh Syafaat [id], | ||||
|   Milo Casagrande [it], Fabio Tomat [fur], Rafael Fontenelle [pt_BR], | ||||
|   Fran Dieguez [gl], Мирослав Николић [sr, sr@latin], Balázs Meskó [hu], | ||||
|   Chao-Hsiung Liao [zh_TW] | ||||
|  | ||||
| 3.23.90 | ||||
| ======= | ||||
| * Handle Ctrl+Q and Ctrl+W in portal window [Bastien; #764133] | ||||
| * Allow to scroll through ibus candidates with mouse [Peng; #776032] | ||||
| * Reload apps on .desktop file content changes [Adrian; #773636] | ||||
| * Use private data/cache directories in portal helper [Bastien; #775639] | ||||
| * Fix subsurfaces not showing up in previews [Rui; #756715] | ||||
| * Fix theme node transitions [Florian; #778145] | ||||
| * Update pad (o)leds on mode switches [Carlos; #776543] | ||||
| * Add security indicators to defend against malicious portals [Bastien; #749197] | ||||
| * Don't allow type ahead at the login screen [Ray; #766139] | ||||
| * Don't fail to load because of TLS errors [Bastien; #778253] | ||||
| * Ensure the network lists remains sorted on rename [Benjamin; #778686] | ||||
| * Toggle power-off/suspend button on long-press [Florian; #721173] | ||||
| * Add "kill-switch" for user extensions [Florian; #778664] | ||||
| * Add night light indicator to status area [Florian; #741224] | ||||
| * Misc. bug fixes [Michael, Bastien, Carlos, Rui, Florian, Alan, Philip, Jonas; | ||||
|   #759793, #735233, #762444, #777784, #777934, #778158, #776199, #778425, | ||||
|   #771098, #778552, #777317, #778660, #778661, #745626, #778672] | ||||
|  | ||||
| Contributors: | ||||
|   Jonas Ådahl, Benjamin Berg, Michael Catanzaro, Philip Chimento, | ||||
|   Alan Coopersmith, Piotr Drąg, Carlos Garnacho, Yuri Konotopov, | ||||
|   Lionel Landwerlin, Rui Matos, Florian Müllner, Bastien Nocera, | ||||
|   Adrian Perez de Castro, Robert Roth, Ray Strode, Peng Wu | ||||
|  | ||||
| Translations: | ||||
|   Jiri Grönroos [fi], Balázs Meskó [hu], Gábor Kelemen [hu], | ||||
|   Daniel Mustieles [es], Dušan Kazik [sk], | ||||
|   Piotr Drąg [ar, eu, fa, hr, pa, pt, sr, sr@latin], Rafael Fontenelle [pt_BR], | ||||
|   Jordi Mas [ca], Piotr Drąg [pl], Alexandre Franke [fr], | ||||
|   Baurzhan Muftakhidinov [kk], Yuras Shumovich [be], Mandy Wang [zh_CN], | ||||
|   Marek Černocký [cs], Kukuh Syafaat [id], Kjartan Maraas [nb], | ||||
|   Daniel Korostil [uk] | ||||
|  | ||||
| 3.23.3 | ||||
| ====== | ||||
| * Fix replacing of GNotifications [Florian; #775149] | ||||
| * Prepare for mozjs31 GJS [Philip; #775374] | ||||
| * Misc. bug fixes [Niels, Jonas; #775507, #776130] | ||||
|  | ||||
| Contributors: | ||||
|   Jonas Ådahl, Michael Catanzaro, Philip Chimento, Niels De Graef, | ||||
|   Carlos Garnacho, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Muhammet Kara [tr], Christian Kirbach [de], Baurzhan Muftakhidinov [kk], | ||||
|   Cheng-Chia Tseng [zh_TW], A S Alam [pa], Gianvito Cavasoli [it] | ||||
|  | ||||
| 3.23.2 | ||||
| ====== | ||||
| * Implement Pad configuration OSD [Carlos; #771067] | ||||
| * Show overview on three-finger touchpad pinch [Carlos; #765937] | ||||
| * Summarize network sections with too many devices [Florian; #773892] | ||||
| * Always show primary network icon when connected [Florian; #773890] | ||||
| * Fix fullscreen transitions on wayland [Rui; #770345] | ||||
| * Work around portal failures by using a URL without HTPPS redirect [Debarshi; #769940] | ||||
| * Fix app view hiding when no usage data is available [Florian, Xiaoguang; #774381] | ||||
| * Misc. bug fixes [Florian, Ray; #773875, #740043, #773893, #774643, #774805] | ||||
|  | ||||
| Contributors: | ||||
|   Carlos Garnacho, Rui Matos, Florian Müllner, Debarshi Ray, Ray Strode, | ||||
|   Xiaoguang Wang | ||||
|  | ||||
| Translations: | ||||
|   Balázs Meskó [hu], Fabio Tomat [fur], Marek Cernocky [cs], Stas Solovey [ru], | ||||
|   Daniel Mustieles [es], Marek Černocký [cs], Piotr Drąg [pl], | ||||
|   Rafael Fontenelle [pt_BR], Baurzhan Muftakhidinov [kk], Jiri Grönroos [fi], | ||||
|   Kjartan Maraas [nb] | ||||
|  | ||||
| 3.23.1 | ||||
| ====== | ||||
| * Request periodic scans while WiFi list is open [Dan; #767918] | ||||
| * Include extension UUID in structured log metadata [Jonh; #770717] | ||||
| * Line-wrap PAM messages on login screen [Tao; #764445] | ||||
| * Add a way to launch an app on the discrete GPU [Bastien; #773117] | ||||
| * Only allow graphs to lift screen shield when locked [Florian; #773328] | ||||
| * Add reload option to gnome-shell-extension-tool [Jonh; #772593] | ||||
| * Update background animations when resuming from suspend [Florian; #773265] | ||||
| * Misc. bug fixes [Cosimo, Bastien, Florian, Philip, Carlos; #772723, #772287, | ||||
|   #756432, #772386, #772386, #773085, #773634] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Philip Chimento, Carlos Garcia Campos, Florian Müllner, | ||||
|   Bastien Nocera, Jonh Wendell, Dan Williams, Tao Yang | ||||
|  | ||||
| Translations: | ||||
|   Fabio Tomat [fur], Philip Chimento [zh_CN], YunQiang Su [zh_CN], | ||||
|   Jordi Mas [ca], Piotr Drąg [pl], Muhammet Kara [tr], Marek Černocký [cs], | ||||
|   Daniel Korostil [uk], Dušan Kazik [sk] | ||||
|  | ||||
| 3.22.1 | ||||
| ====== | ||||
| * Fix hidden network indicator on startup [Florian; #772249] | ||||
| * Fix order of windows with modal dialogs in window switcher [Florian; #747153] | ||||
| * Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745] | ||||
| * Reliably match windows from Flatpak apps [Florian; #772615] | ||||
| * Misc. bug fixes [Philip; #742249] | ||||
|  | ||||
| Contributors: | ||||
|   Philip Chimento, Carlos Garnacho, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Inaki Larranaga Murgoitio [eu], Khaled Hosny [ar], BM [uz@cyrillic], | ||||
|   Milo Casagrande [it], Cheng-Chia Tseng [zh_TW], gogo [hr] | ||||
|  | ||||
| 3.22.0 | ||||
| ====== | ||||
| * Misc. bug fixes [Florian, Rui; #771391, #771536] #771656] | ||||
|  | ||||
| Contributors: | ||||
|   Rui Matos, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Ask Hjorth Larsen [da], GNOME Translation Robot [gd], Alexandre Franke [fr], | ||||
|   Daniel Korostil [uk], Jordi Mas [ca], Khaled Hosny [ar], David King [en_GB] | ||||
|  | ||||
| 3.21.92 | ||||
| ======= | ||||
| * Adjust screen capture to work with multiple stage views [Jonas; #770128] | ||||
| * Improve handling of cycle shortcuts [Florian; #771063] | ||||
| * Fix windows not getting undimmed in some cases [Rui; #770163, #752524] | ||||
| * Disable extension version check by default [Florian; #770887] | ||||
| * Misc. bug fixes [Rui, Florian, Michael; #770382, #770888, #770328] | ||||
|  | ||||
| Contributors: | ||||
|   Jonas Ådahl, Michael Catanzaro, Fran Dieguez, Olivier Fourdan, Rui Matos, | ||||
|   Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], | ||||
|   Muhammet Kara [tr], Trần Ngọc Quân [vi], A S Alam [pa], Yosef Or Boczko [he], | ||||
|   Anders Jonsson [sv], Tiago Santos [pt], Hannie Dumoleyn [nl], | ||||
|   Rūdolfs Mazurs [lv], Claude Paroz [fr], Arash Mousavi [fa], | ||||
|   Fran Dieguez [gl], Stas Solovey [ru], Tom Tryfonidis [el] | ||||
|  | ||||
| 3.21.91 | ||||
| ======= | ||||
| Translations: | ||||
|   Mario Blättermann [de], Jiri Grönroos [fi], Dušan Kazik [sk], | ||||
|   Andika Triwidada [id], Daniel Mustieles [es], Fabio Tomat [fur], | ||||
|   Enrico Nicoletto [pt_BR], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin] | ||||
|  | ||||
| 3.21.90.1 | ||||
| ========= | ||||
| Contributors: | ||||
|   Piotr Drąg | ||||
|  | ||||
| Translations: | ||||
|   Marek Černocký [cs], Balázs Úr [hu] | ||||
|  | ||||
| 3.21.90 | ||||
| ======= | ||||
| * Improve on-screen keyboard on wayland [Carlos; #765009] | ||||
| * Misc. bug fixes [Florian; #769156, #769216, #769074] | ||||
|  | ||||
| Contributors: | ||||
|   Carlos Garnacho, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Fabio Tomat [fur], Tiago Santos [pt], Daniel Mustieles [es], | ||||
|   Bernd Homuth [de], Aurimas Černius [lt], Balázs Úr [hu], | ||||
|   Yosef Or Boczko [he], Jiri Grönroos [fi], Marek Cernocky [cs], | ||||
|   Muhammet Kara [tr], Enrico Nicoletto [pt_BR], Andika Triwidada [id] | ||||
|  | ||||
| 3.21.4 | ||||
| ====== | ||||
| * overview: Fix switching workspaces when scrolling on non-primary monitors | ||||
|   [Florian; #766883, #768316] | ||||
| * Fix crash when using screen recorder under wayland [Rui; #767001] | ||||
| * Update theme on video memory purge errors [Rui; #739178] | ||||
| * Free old backgrounds immediately [Hyungwon; #766353] | ||||
| * Add support for system upgrades to end session dialog [Kalev; #763611] | ||||
| * Fix maximized windows flickering to the wrong size on restart [Owen; #761566] | ||||
| * Hide ignored events in calendar as well [Florian; #768538] | ||||
| * calendar: Only hide dismissed occurrence of recurring event [Florian; #748226] | ||||
| * Provide org.freedesktop.impl.portal.access implementation [Florian; #768669] | ||||
| * Misc. bug fixes and cleanups [Rui, Florian, Marinus, Jonas; #767954, #768317, | ||||
|   #746867, #762206, #768956, #768979] | ||||
|  | ||||
| Contributors: | ||||
|   Jonas Ådahl, Piotr Drąg, Hyungwon Hwang, Kalev Lember, Rui Matos, | ||||
|   Florian Müllner, Marinus Schraal, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Andika Triwidada [id], Daniel Mustieles [es], Bruce Cowan [en_GB], | ||||
|   Dušan Kazik [sk], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK] | ||||
|  | ||||
| 3.21.3 | ||||
| ====== | ||||
|  * Do not disable suspend action when locked [Florian; #725960] | ||||
|  * Remember input sources MRU list [Cosimo; #766826] | ||||
|  * networkAgent: Handle VPN service aliases [David; #658484] | ||||
|  * Plug a memory leak [Hans; #710230] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Florian Müllner, Hans Petter Jansson, David Woodhouse | ||||
|  | ||||
| Translations: | ||||
|   Tiago Santos [pt], Cédric Valmary [oc], Muhammet Kara [tr], | ||||
|   Daniel Mustieles [es], Rafael Fontenelle [pt_BR] | ||||
|  | ||||
| 3.21.2 | ||||
| ====== | ||||
| * Fix sorting of hidden apps in app switcher [Florian; #766238] | ||||
| * Set logind's LockedHint property when locked [Victor; #764773] | ||||
| * Allocate framebuffers early to fix a crash on NVIDIA [Martin; #764898] | ||||
| * Fix cycle-windows/cycle-group keybindings [Florian; #730739] | ||||
| * Switch to shared desktop schema for calendar settings [Iain; #766318] | ||||
| * Misc. bug fixes [Florian, Cosimo, Michele; #766325, #758471, #757556, | ||||
|   #757019, #766598] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Michele Gaio, Iain Lane, Florian Müllner, Martin Szulecki, | ||||
|   Victor Toso | ||||
|  | ||||
| Translations: | ||||
|   Tiago Santos [pt], Kjartan Maraas [nb], Jiro Matsuzawa [ja], | ||||
|   Cédric Valmary [oc], Sveinn í Felli [is] | ||||
|  | ||||
| 3.21.1 | ||||
| ====== | ||||
| * Save screencasts in HOME if XDG_VIDEO_DIR doesn't exist [Florian; #765015] | ||||
| * Don't show orientation lock when g-s-d won't rotate [Florian; #765267] | ||||
| * Misc. bug fixes [Heiher, Florian, Marek, Rui; #722752, #765061, #763068, | ||||
|   #765607, #757676, #760439] | ||||
|  | ||||
| Contributors: | ||||
|   Heiher, Marek Chalupa, Rui Matos, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Arash Mousavi [fa], Kristjan SCHMIDT [eo], GNOME Translation Robot [gd] | ||||
|  | ||||
| 3.20.1 | ||||
| ====== | ||||
| * Plug a memory leak [Aaron; #735705] | ||||
|  | ||||
| Contributors: | ||||
|   Aaron Plattner | ||||
|  | ||||
| Translations: | ||||
|   Daniel Korostil [uk], Matej Urbančič [sl], Inaki Larranaga Murgoitio [eu], | ||||
|   Cheng-Chia Tseng [zh_TW], Fabio Tomat [fur], Trần Ngọc Quân [vi], | ||||
|   YunQiang Su [zh_CN], Marek Černocký [cs], Arash Mousavi [fa], | ||||
|   Alexander Shopov [bg], Khaled Hosny [ar] | ||||
|  | ||||
| 3.20.0 | ||||
| ====== | ||||
|  | ||||
| Translations: | ||||
|   Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Milo Casagrande [it], | ||||
|   Anders Jonsson [sv], Muhammet Kara [tr], Alexandre Franke [fr], | ||||
|   Rūdolfs Mazurs [lv], Ask Hjorth Larsen [da], Jiro Matsuzawa [ja] | ||||
|  | ||||
| 3.19.92 | ||||
| ======= | ||||
| * Update location dialog according to latest mockups [Zeeshan; #762480] | ||||
| * Fix deleting chat notifications in calendar [Florian; #747991] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Rūdolfs Mazurs [lv], Changwoo Ryu [ko], Matej Urbančič [sl], | ||||
|   Justin van Steijn [nl], Fabio Tomat [fur], Kris Thomsen [da], | ||||
|   Marek Černocký [cs], Piotr Drąg [pl], Dušan Kazik [sk], | ||||
|   Мирослав Николић [sr, sr@latin], Balázs Úr [hu], Yosef Or Boczko [he], | ||||
|   Daniel Mustieles [es], Fran Dieguez [gl], Bernd Homuth [de], | ||||
|   Tom Tryfonidis [el], Jiri Grönroos [fi], Gil Forcada [ca], | ||||
|   Artur Morais [pt_BR], Aurimas Černius [lt], Stas Solovey [ru] | ||||
|  | ||||
| 3.19.91 | ||||
| ======= | ||||
| * location: Ask user only once [Zeeshan; #762559] | ||||
| * Fix jiggling when auto-hiding legacy tray [Florian; #747957] | ||||
| * Misc. bug fixes [Florian, Michael, Ting-Wei; #762475, #762507, #755659] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Michael Catanzaro, Ting-Wei Lan, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Мирослав Николић [sr, sr@latin], Piotr Drąg [pl], A S Alam [pa], | ||||
|   Artur de Aquino Morais [pt_BR], Daniel Mustieles [es], | ||||
|   Chao-Hsiung Liao [zh_TW], Daniel Korostil [uk], Fran Dieguez [gl], | ||||
|   Tom Tryfonidis [el], Bernd Homuth [de], Sebastian Rasmussen [sv], | ||||
|   Jordi Mas [ca], Piotr Drąg [ga], Cédric Valmary [oc], Gábor Kelemen [hu], | ||||
|   Baurzhan Muftakhidinov [kk], Friedel Wolff [af], Marek Černocký [cs], | ||||
|   Mingye Wang (Arthur2e5) [zh_CN], Aron Xu [zh_CN], Khaled Hosny [ar], | ||||
|   Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he] | ||||
|  | ||||
| 3.19.90 | ||||
| ======= | ||||
| * Correctly identify VPN secret requests [Lubomir; #760999] | ||||
| * Improve week number presentation [Jakub; #683245] | ||||
| * Add audio device selection dialog [Florian; #760284] | ||||
| * Add media controls to the time and date drop down [Florian; #756491] | ||||
| * Fix IBus candidate popup position under wayland [Rui; #753476] | ||||
| * Ask user to grant applications access to location [Zeeshan; #762119] | ||||
| * Misc. bug fixes [Mario, Jakub, Florian; #761208, #761772, #762270] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Michael Catanzaro, Rui Matos, Florian Müllner, | ||||
|   Lubomir Rintel, Mario Sanchez Prada, Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Alexander Shopov [bg], Balázs Meskó [hu], Fabio Tomat [fur], | ||||
|   Dušan Kazik [sk], Piotr Drąg [pl], Alexandre Franke [fr], | ||||
|   Mario Blättermann [de], Milo Casagrande [it], Jordi Mas [ca] | ||||
|  | ||||
| 3.19.4 | ||||
| ====== | ||||
| * gdm: Do not allow bypassing disabled Sign In button [Michael; #746180] | ||||
| * Style week numbers in calendar [Jakub; #683245] | ||||
| * Misc. bug fixes [Christophe, Jakub, Rui; #759708, #760577, #760945] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Catanzaro, Marek Černocký, Christophe Fergeau, Rui Matos, | ||||
|   Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Aurimas Černius [lt], Enrico Nicoletto [pt_BR], Andika Triwidada [id], | ||||
|   Mario Blättermann [de], Marek Černocký [cs], Kjartan Maraas [nb], | ||||
|   Muhammet Kara [tr], Stas Solovey [ru] | ||||
|  | ||||
| 3.19.3 | ||||
| ====== | ||||
| * Fix thumbnail scaling in window switcher on HiDPI [Florian; #758676] | ||||
| * Update animated backgrounds on timezone changes [Florian; #758939] | ||||
| * loginDialog: Update user list on user changes [Michael; #758568] | ||||
| * Fix touch interaction on wayland [Carlos; #756748] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Catanzaro, Carlos Garnacho, Kalev Lember, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Daniel Korostil [uk], Muhammet Kara [tr], Dušan Kazik [sk], | ||||
|   Baurzhan Muftakhidinov [kk], Marek Černocký [cs] | ||||
|  | ||||
| 3.19.2 | ||||
| ====== | ||||
| * Make gnome-shell DBus activatable [Ray; #741666] | ||||
| * Fix browser plugin crash in Firefox [Carlos; #737932, #757940] | ||||
| * Optionally show battery percentage in system status area [Bastien; #735771] | ||||
| * Misc. bug fixes [Kalev, Florian, Bastien; #757418, #757668, #757779, #757816, | ||||
|   #745626, #758220] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Michael Catanzaro, Piotr Drąg, Carlos Garcia Campos, | ||||
|   Kalev Lember, Florian Müllner, Bastien Nocera, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Pedro Albuquerque [pt], liushuyu [zh_CN], Yosef Or Boczko [he], | ||||
|   Jiri Grönroos [fi], Kjartan Maraas [nb], GNOME Translation Robot [gd], | ||||
|   Daniel Mustieles [es], Marek Černocký [cs], Kristjan SCHMIDT [eo], | ||||
|   Stas Solovey [ru] | ||||
|  | ||||
| 3.19.1 | ||||
| ====== | ||||
| * Respect text-scaling factor under wayland [Owen; #756447] | ||||
| * Show the Bluetooth submenu when there were setup devices [Bastien; #723848] | ||||
| * Misc. bug fixes [Florian, Cosimo, Rui, Ray, Owen, Jakub, Bastien; | ||||
|   #756697, #756714, #756605, #754814, #738942, #756983, #756925, | ||||
|   #757011, #673235, #757150] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Rui Matos, Florian Müllner, Bastien Nocera, Jakub Steiner, | ||||
|   Ray Strode, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Kjartan Maraas [nb], Khaled Hosny [ar], Balázs Meskó [hu], | ||||
|   Daniel Șerbănescu [ro], Marek Černocký [cs] | ||||
|  | ||||
| 3.18.1 | ||||
| ====== | ||||
| * Fix screen freezes when a notification is pushed [Carlos; #755425] | ||||
| * Fix overzealous ellipsization in system status menu [Adel, Florian; #708472] | ||||
| * Hide app menu when disabled by setting [Florian; #745919] | ||||
| * Fix lightbox effect when animations are disabled [Rui; #755827] | ||||
| * Do not mark hotplug notifications as critical [Florian; #657923] | ||||
| * Fix icons getting cut off in dash [Florian; #745649] | ||||
| * Animate fullscreen/unfullscreen operations [Cosimo; #707248] | ||||
| * Misc. bug fixes [Florian, Owen; #748919, #674799, #754581] | ||||
|  | ||||
| Contributors: | ||||
|   Emmanuele Bassi, Michael Catanzaro, Cosimo Cecchi, Matthias Clasen, | ||||
|   Adel Gadllah, Carlos Garnacho, Ekaterina Gerasimova, Rui Matos, | ||||
|   Florian Müllner, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Марко Костић [sr], Милош Поповић [sr@latin], Khaled Hosny [ar], | ||||
|   Trần Ngọc Quân [vi], Petr Kovar [cs], Alexandre Franke [fr], | ||||
|   Fran Dieguez [gl], Anders Jonsson [sv], Piotr Drąg [pl], Dušan Kazik [sk], | ||||
|   Milo Casagrande [it], Changwoo Ryu [ko], Stas Solovey [ru], | ||||
|   Rafael Fontenelle [pt_BR], Tom Tryfonidis [el], Aurimas Černius [lt], | ||||
|   Seán de Búrca [ga], Christian Kirbach [de], Jiri Grönroos [fi], | ||||
|   Pedro Albuquerque [pt], Baurzhan Muftakhidinov [kk], Daniel Mustieles [es], | ||||
|   Marek Černocký [cs], Ask Hjorth Larsen [da], Inaki Larranaga Murgoitio [eu] | ||||
|  | ||||
| 3.18.0 | ||||
| ====== | ||||
|  | ||||
| Translations: | ||||
|   Sendy Aditya Suryana [id], Kris Thomsen [da], Seán de Búrca [ga], | ||||
|   Andika Triwidada [id], Enrico Nicoletto [pt_BR], Anders Jonsson [sv], | ||||
|   Rūdolfs Mazurs [lv] | ||||
|  | ||||
| 3.17.92 | ||||
| ======= | ||||
| * Fix race when loading multiple background animations [Josselin; #741453] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Josselin Mouette, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Baurzhan Muftakhidinov [kk], Changwoo Ryu [ko], Christian Kirbach [de], | ||||
|   Kjartan Maraas [nb], Jiri Grönroos [fi], Arash Mousavi [fa], | ||||
|   Jiro Matsuzawa [ja], Marek Černocký [cs], Milo Casagrande [it] | ||||
|  | ||||
| 3.17.91 | ||||
| ======= | ||||
| * Fix login screen spinner causing wakeups while VT-switched away | ||||
|   [Ray, Rui; #753891] | ||||
| * Fix scrolling of user list on login screen [Florian; #754525] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Rui Matos, Florian Müllner, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Dušan Kazik [sk], Jordi Mas [ca], Aurimas Černius [lt], Stas Solovey [ru], | ||||
|   Piotr Drąg [pl], Pedro Albuquerque [pt], Daniel Mustieles [es], | ||||
|   Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr], Fran Dieguez [gl], | ||||
|   Hannie Dumoleyn [nl], Yosef Or Boczko [he], Tom Tryfonidis [el], | ||||
|   A S Alam [pa], Balázs Úr [hu], Alexandre Franke [fr], Frédéric Péters [fr] | ||||
|  | ||||
| 3.17.90 | ||||
| ======= | ||||
| * Avoid caret/focus viewport changes during pointer movement [Rui; #752138] | ||||
| * Match GTK+'s modal dialogs for system modal dialogs [Carlos; #746108] | ||||
| * Refine message list style [Florian; #749958] | ||||
| * Fix type-ahead behavior for backspace and compose key [Rui; #753319, #753320] | ||||
| * Refine the system status menu [Florian; #751377] | ||||
| * Misc. bug fixes and cleanups [Bastien, Ray, Florian, Jakub; #752779, #752739, | ||||
|   #741366, #651503, #753064, #753181, #752881] | ||||
|  | ||||
| Contributors: | ||||
|   Rui Matos, Florian Müllner, Bastien Nocera, Carlos Soriano, Jakub Steiner, | ||||
|   Ray Strode, Rico Tzschichholz | ||||
|  | ||||
| Translations: | ||||
|   Marek Černocký [cs], Kjartan Maraas [nb], Jordi Mas [ca], Muhammet Kara [tr], | ||||
|   Enrico Nicoletto [pt_BR] | ||||
|  | ||||
| 3.17.4 | ||||
| ====== | ||||
| * Fix fuzziness of app menu icon [Jakub; #747932] | ||||
| * Implement 4 finger swipe gesture for touchpads [Carlos; #752250] | ||||
| * Misc. bug fixes [Florian, Alexandre, Piotr, Ray, Mario; #751921, #659969, | ||||
|   #752438, #752675] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Alexandre Franke, Carlos Garnacho, Florian Müllner, | ||||
|   Mario Sanchez Prada, Jakub Steiner, Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Benjamin Steinwender [de], Pedro Albuquerque [pt], Fabio Tomat [fur], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es], Yosef Or Boczko [he], | ||||
|   Daniel Martinez [an] | ||||
|  | ||||
| 3.17.3 | ||||
| ====== | ||||
| * Handle touch events in OSK on wayland [Rui; #750287] | ||||
| * Reinstate left/right movement to window menu [Ron; #751344] | ||||
| * Allow extensions to disable "Window is ready" notification [Adel; #748846] | ||||
| * Misc. bug fixes [Watson, Michael, Ray, Rui, Florian, Cosimo; #750465, | ||||
|   #751016, #751517, #750714, #751541, #751599] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner, | ||||
|   Ray Strode, Wim Taymans, Ron Yorston, Watson Yuuma Sato | ||||
|  | ||||
| Translations: | ||||
|   Sebastian Rasmussen [sv], Dimitris Spingos [el], Muhammet Kara [tr], | ||||
|   Stas Solovey [ru], Benjamin Steinwender [de], Balázs Úr [hu], | ||||
|   Victor Ibragimov [tg], Dušan Kazik [sk], Pedro Albuquerque [pt] | ||||
|  | ||||
| 3.17.2 | ||||
| ====== | ||||
| * Remove StTable widget [Florian; #703833] | ||||
| * Increase visibility of expanders in alt-tab popup [Jakub; #745058] | ||||
| * Ensure suspend inhibitors are released when VT switched away [Rui; #749228] | ||||
| * Use iio-sensor-proxy directly for orientation lock [Bastien; #749671] | ||||
| * Misc. bug fixes [Florian, Lan, Carlos; #749279, #749383, #749529, #749490, | ||||
|   #749742] | ||||
|  | ||||
| Contributors: | ||||
|   Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera, | ||||
|   Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Yosef Or Boczko [he], sun [zh_CN], Felipe Braga [pt_BR], | ||||
|   Victor Ibragimov [tg], Gábor Kelemen [hu], Cédric Valmary [oc], | ||||
|   Dušan Kazik [sk], Kjartan Maraas [nb], Bruno Ramalhete [pt], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es] | ||||
|  | ||||
| 3.17.1 | ||||
| ====== | ||||
| * Add Display Settings entry to background menu [Meet; #697346] | ||||
| * Add window menu option to move to different monitor [Isaac; #633994] | ||||
| * Improve switch style in default/highContrast themes [Jakub; #746294, #747912] | ||||
| * Make event highlight in calendar more prominent [Jakub; #747715] | ||||
| * Fix keyboard focus when focusing a notification banner [Florian; #747205] | ||||
| * Move notification banners below the dateMenu [Meet, Florian; #745910] | ||||
| * Misc. bug fixes [Mario, Rui; #748338, #748541] | ||||
|  | ||||
| Contributors: | ||||
|   Isaac Ge, Rui Matos, Florian Müllner, Meet Parikh, Mario Sanchez Prada, | ||||
|   Jakub Steiner, Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Sveinn í Felli [is], Marek Černocký [cs], laurent Soleil [oc] | ||||
|  | ||||
| 3.16.1 | ||||
| ====== | ||||
| * gdm: Move long session chooser menus to the side [Florian; #734352] | ||||
|   | ||||
							
								
								
									
										24
									
								
								autogen.sh
									
									
									
									
									
								
							
							
						
						| @@ -4,8 +4,12 @@ | ||||
| srcdir=`dirname $0` | ||||
| test -z "$srcdir" && srcdir=. | ||||
|  | ||||
| (test -f $srcdir/configure.ac \ | ||||
|   && test -d $srcdir/src) || { | ||||
| olddir="$(pwd)" | ||||
|  | ||||
| cd "${srcdir}" | ||||
|  | ||||
| (test -f configure.ac \ | ||||
|   && test -d src) || { | ||||
|     echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" | ||||
|     echo " top-level gnome-shell directory" | ||||
|     exit 1 | ||||
| @@ -19,9 +23,13 @@ then | ||||
| fi | ||||
| git submodule update | ||||
|  | ||||
| which gnome-autogen.sh || { | ||||
|     echo "You need to install gnome-common from GNOME Git (or from" | ||||
|     echo "your OS vendor's package manager)." | ||||
|     exit 1 | ||||
| } | ||||
| . gnome-autogen.sh | ||||
| aclocal --install || exit 1 | ||||
| gtkdocize --copy || exit 1 | ||||
| intltoolize --force --copy --automake || exit 1 | ||||
| autoreconf --verbose --force --install || exit 1 | ||||
|  | ||||
| cd "${olddir}" | ||||
|  | ||||
| if [ "$NOCONFIGURE" = "" ]; then | ||||
|     "${srcdir}/configure" "$@" || exit 1 | ||||
| fi | ||||
|   | ||||
| @@ -3,7 +3,10 @@ mozillalibdir = $(BROWSER_PLUGIN_DIR) | ||||
|  | ||||
| mozillalib_LTLIBRARIES = libgnome-shell-browser-plugin.la | ||||
|  | ||||
| libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined | ||||
| # Browsers can unload and reload the module while browsing, which is not supported by GObject. | ||||
| # We pass -Wl,-z,nodelete to the linker to ensure the module is never unloaded. | ||||
| # https://bugzilla.gnome.org/show_bug.cgi?id=737932 | ||||
| libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined -Wl,-z,nodelete | ||||
|  | ||||
| libgnome_shell_browser_plugin_la_LIBADD = 	\ | ||||
| 	$(BROWSER_PLUGIN_LIBS) | ||||
|   | ||||
| @@ -33,20 +33,16 @@ | ||||
| #include <json-glib/json-glib.h> | ||||
|  | ||||
| #define ORIGIN "extensions.gnome.org" | ||||
| #define PLUGIN_NAME "Gnome Shell Integration" | ||||
| #define PLUGIN_DESCRIPTION "This plugin provides integration with Gnome Shell " \ | ||||
| #define PLUGIN_NAME "GNOME Shell Integration" | ||||
| #define PLUGIN_DESCRIPTION "This plugin provides integration with GNOME Shell " \ | ||||
|       "for live extension enabling and disabling. " \ | ||||
|       "It can be used only by extensions.gnome.org" | ||||
| #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type"; | ||||
| #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::GNOME Shell Integration Dummy Content-Type"; | ||||
|  | ||||
| #define PLUGIN_API_VERSION 5 | ||||
|  | ||||
| #define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation" | ||||
|  | ||||
| typedef struct { | ||||
|   GDBusProxy *proxy; | ||||
| } PluginData; | ||||
|  | ||||
| static NPNetscapeFuncs funcs; | ||||
|  | ||||
| static inline gchar * | ||||
| @@ -145,121 +141,6 @@ check_origin_and_protocol (NPP instance) | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /* =============== public entry points =================== */ | ||||
|  | ||||
| NPError | ||||
| NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
| { | ||||
|   /* global initialization routine, called once when plugin | ||||
|      is loaded */ | ||||
|  | ||||
|   g_debug ("plugin loaded"); | ||||
|  | ||||
|   memcpy (&funcs, pfuncs, sizeof (funcs)); | ||||
|  | ||||
|   plugin->size = sizeof(NPPluginFuncs); | ||||
|   plugin->newp = NPP_New; | ||||
|   plugin->destroy = NPP_Destroy; | ||||
|   plugin->getvalue = NPP_GetValue; | ||||
|   plugin->setwindow = NPP_SetWindow; | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_Shutdown(void) | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| const char* | ||||
| NP_GetMIMEDescription(void) | ||||
| { | ||||
|   return PLUGIN_MIME_STRING; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_GetValue(void         *instance, | ||||
|             NPPVariable   variable, | ||||
|             void         *value) | ||||
| { | ||||
|   switch (variable) { | ||||
|   case NPPVpluginNameString: | ||||
|     *(char**)value = PLUGIN_NAME; | ||||
|     break; | ||||
|   case NPPVpluginDescriptionString: | ||||
|     *(char**)value = PLUGIN_DESCRIPTION; | ||||
|     break; | ||||
|   default: | ||||
|     ; | ||||
|   } | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_New(NPMIMEType    mimetype, | ||||
|         NPP           instance, | ||||
|         uint16_t      mode, | ||||
|         int16_t       argc, | ||||
|         char        **argn, | ||||
|         char        **argv, | ||||
|         NPSavedData  *saved) | ||||
| { | ||||
|   /* instance initialization function */ | ||||
|   PluginData *data; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   g_debug ("plugin created"); | ||||
|  | ||||
|   if (!check_origin_and_protocol (instance)) | ||||
|     return NPERR_GENERIC_ERROR; | ||||
|  | ||||
|   data = g_slice_new (PluginData); | ||||
|   instance->pdata = data; | ||||
|  | ||||
|   data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, | ||||
|                                                G_DBUS_PROXY_FLAGS_NONE, | ||||
|                                                NULL, /* interface info */ | ||||
|                                                "org.gnome.Shell", | ||||
|                                                "/org/gnome/Shell", | ||||
|                                                "org.gnome.Shell.Extensions", | ||||
|                                                NULL, /* GCancellable */ | ||||
|                                                &error); | ||||
|   if (!data->proxy) | ||||
|     { | ||||
|       /* ignore error if the shell is not running, otherwise warn */ | ||||
|       if (error->domain != G_DBUS_ERROR || | ||||
|           error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER) | ||||
|         { | ||||
|           g_warning ("Failed to set up Shell proxy: %s", error->message); | ||||
|         } | ||||
|       g_clear_error (&error); | ||||
|       return NPERR_GENERIC_ERROR; | ||||
|     } | ||||
|  | ||||
|   g_debug ("plugin created successfully"); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_Destroy(NPP           instance, | ||||
| 	    NPSavedData **saved) | ||||
| { | ||||
|   /* instance finalization function */ | ||||
|  | ||||
|   PluginData *data = instance->pdata; | ||||
|  | ||||
|   g_debug ("plugin destroyed"); | ||||
|  | ||||
|   g_object_unref (data->proxy); | ||||
|  | ||||
|   g_slice_free (PluginData, data); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| /* =================== scripting interface =================== */ | ||||
|  | ||||
| typedef struct { | ||||
| @@ -330,45 +211,18 @@ static NPObject * | ||||
| plugin_object_allocate (NPP      instance, | ||||
|                         NPClass *klass) | ||||
| { | ||||
|   PluginData *data = instance->pdata; | ||||
|   PluginObject *obj = g_slice_new0 (PluginObject); | ||||
|   PluginObject *obj = (PluginObject *) funcs.memalloc (sizeof (PluginObject)); | ||||
|  | ||||
|   memset (obj, 0, sizeof (PluginObject)); | ||||
|   obj->instance = instance; | ||||
|   obj->proxy = g_object_ref (data->proxy); | ||||
|   obj->settings = g_settings_new (SHELL_SCHEMA); | ||||
|   obj->signal_id = g_signal_connect (obj->proxy, "g-signal", | ||||
|                                      G_CALLBACK (on_shell_signal), obj); | ||||
|  | ||||
|   obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION, | ||||
|                                          "org.gnome.Shell", | ||||
|                                          G_BUS_NAME_WATCHER_FLAGS_NONE, | ||||
|                                          on_shell_appeared, | ||||
|                                          NULL, | ||||
|                                          obj, | ||||
|                                          NULL); | ||||
|  | ||||
|   g_debug ("plugin object created"); | ||||
|  | ||||
|   return (NPObject*)obj; | ||||
|   return (NPObject*) obj; | ||||
| } | ||||
|  | ||||
| static void | ||||
| plugin_object_deallocate (NPObject *npobj) | ||||
| { | ||||
|   PluginObject *obj = (PluginObject*)npobj; | ||||
|  | ||||
|   g_signal_handler_disconnect (obj->proxy, obj->signal_id); | ||||
|   g_object_unref (obj->proxy); | ||||
|  | ||||
|   if (obj->listener) | ||||
|     funcs.releaseobject (obj->listener); | ||||
|  | ||||
|   if (obj->watch_name_id) | ||||
|     g_bus_unwatch_name (obj->watch_name_id); | ||||
|  | ||||
|   g_debug ("plugin object destroyed"); | ||||
|  | ||||
|   g_slice_free (PluginObject, obj); | ||||
|   funcs.memfree (npobj); | ||||
| } | ||||
|  | ||||
| static inline gboolean | ||||
| @@ -1019,6 +873,149 @@ init_methods_and_properties (void) | ||||
|   onextension_changed_id = funcs.getstringidentifier ("onchange"); | ||||
| } | ||||
|  | ||||
| /* =============== public entry points =================== */ | ||||
|  | ||||
| NPError | ||||
| NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
| { | ||||
|   /* global initialization routine, called once when plugin | ||||
|      is loaded */ | ||||
|  | ||||
|   g_debug ("plugin loaded"); | ||||
|  | ||||
|   memcpy (&funcs, pfuncs, sizeof (funcs)); | ||||
|  | ||||
|   plugin->size = sizeof(NPPluginFuncs); | ||||
|   plugin->newp = NPP_New; | ||||
|   plugin->destroy = NPP_Destroy; | ||||
|   plugin->getvalue = NPP_GetValue; | ||||
|   plugin->setwindow = NPP_SetWindow; | ||||
|   plugin->event = NPP_HandleEvent; | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_Shutdown(void) | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| const char* | ||||
| NP_GetMIMEDescription(void) | ||||
| { | ||||
|   return PLUGIN_MIME_STRING; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_GetValue(void         *instance, | ||||
|             NPPVariable   variable, | ||||
|             void         *value) | ||||
| { | ||||
|   switch (variable) { | ||||
|   case NPPVpluginNameString: | ||||
|     *(char**)value = PLUGIN_NAME; | ||||
|     break; | ||||
|   case NPPVpluginDescriptionString: | ||||
|     *(char**)value = PLUGIN_DESCRIPTION; | ||||
|     break; | ||||
|   default: | ||||
|     ; | ||||
|   } | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_New(NPMIMEType    mimetype, | ||||
|         NPP           instance, | ||||
|         uint16_t      mode, | ||||
|         int16_t       argc, | ||||
|         char        **argn, | ||||
|         char        **argv, | ||||
|         NPSavedData  *saved) | ||||
| { | ||||
|   /* instance initialization function */ | ||||
|   PluginObject *obj; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   g_debug ("plugin created"); | ||||
|  | ||||
|   if (!check_origin_and_protocol (instance)) | ||||
|     return NPERR_GENERIC_ERROR; | ||||
|  | ||||
|   /* set windowless mode */ | ||||
|   funcs.setvalue(instance, NPPVpluginWindowBool, NULL); | ||||
|  | ||||
|   g_debug ("creating scriptable object"); | ||||
|   init_methods_and_properties (); | ||||
|   obj = (PluginObject *) funcs.createobject (instance, &plugin_class); | ||||
|   instance->pdata = obj; | ||||
|  | ||||
|   obj->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, | ||||
|                                               G_DBUS_PROXY_FLAGS_NONE, | ||||
|                                               NULL, /* interface info */ | ||||
|                                               "org.gnome.Shell", | ||||
|                                               "/org/gnome/Shell", | ||||
|                                               "org.gnome.Shell.Extensions", | ||||
|                                               NULL, /* GCancellable */ | ||||
|                                               &error); | ||||
|   if (!obj->proxy) | ||||
|     { | ||||
|       /* ignore error if the shell is not running, otherwise warn */ | ||||
|       if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER)) | ||||
|         { | ||||
|           g_warning ("Failed to set up Shell proxy: %s", error->message); | ||||
|         } | ||||
|       g_clear_error (&error); | ||||
|       return NPERR_GENERIC_ERROR; | ||||
|     } | ||||
|  | ||||
|   obj->settings = g_settings_new (SHELL_SCHEMA); | ||||
|   obj->signal_id = g_signal_connect (obj->proxy, "g-signal", | ||||
|                                      G_CALLBACK (on_shell_signal), obj); | ||||
|   obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION, | ||||
|                                          "org.gnome.Shell", | ||||
|                                          G_BUS_NAME_WATCHER_FLAGS_NONE, | ||||
|                                          on_shell_appeared, | ||||
|                                          NULL, | ||||
|                                          obj, | ||||
|                                          NULL); | ||||
|  | ||||
|   g_debug ("plugin created successfully"); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_Destroy(NPP           instance, | ||||
| 	    NPSavedData **saved) | ||||
| { | ||||
|   /* instance finalization function */ | ||||
|   PluginObject *obj = (PluginObject *) instance->pdata; | ||||
|  | ||||
|   if (!obj) | ||||
|     return NPERR_INVALID_INSTANCE_ERROR; | ||||
|  | ||||
|   g_debug ("plugin destroyed"); | ||||
|  | ||||
|   g_signal_handler_disconnect (obj->proxy, obj->signal_id); | ||||
|   g_object_unref (obj->proxy); | ||||
|  | ||||
|   if (obj->listener) | ||||
|     funcs.releaseobject (obj->listener); | ||||
|  | ||||
|   if (obj->restart_listener) | ||||
|     funcs.releaseobject (obj->restart_listener); | ||||
|  | ||||
|   if (obj->watch_name_id) | ||||
|     g_bus_unwatch_name (obj->watch_name_id); | ||||
|  | ||||
|   funcs.releaseobject((NPObject *)obj); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_GetValue(NPP          instance, | ||||
| 	     NPPVariable  variable, | ||||
| @@ -1029,13 +1026,11 @@ NPP_GetValue(NPP          instance, | ||||
|   switch (variable) { | ||||
|   case NPPVpluginScriptableNPObject: | ||||
|     g_debug ("creating scriptable object"); | ||||
|     init_methods_and_properties (); | ||||
|     if (!instance->pdata) | ||||
|       return NPERR_INVALID_INSTANCE_ERROR; | ||||
|  | ||||
|     *(NPObject**)value = funcs.createobject (instance, &plugin_class); | ||||
|     break; | ||||
|  | ||||
|   case NPPVpluginNeedsXEmbed: | ||||
|     *(bool *)value = TRUE; | ||||
|     funcs.retainobject (instance->pdata); | ||||
|     *(NPObject**)value = instance->pdata; | ||||
|     break; | ||||
|  | ||||
|   default: | ||||
| @@ -1053,3 +1048,11 @@ NPP_SetWindow(NPP          instance, | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| int16_t | ||||
| NPP_HandleEvent(NPP   instance, | ||||
|                 void *event) | ||||
| { | ||||
|   /* Ignore the event */ | ||||
|   return FALSE; | ||||
| } | ||||
|   | ||||
							
								
								
									
										66
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,6 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.16.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.24.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AX_IS_RELEASE([git-directory]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -16,20 +17,20 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) | ||||
|  | ||||
| # Checks for programs. | ||||
| AC_PROG_CC | ||||
| AC_PROG_CXX | ||||
|  | ||||
| # Initialize libtool | ||||
| LT_PREREQ([2.2.6]) | ||||
| LT_INIT([disable-static]) | ||||
|  | ||||
| # i18n | ||||
| IT_PROG_INTLTOOL([0.40]) | ||||
|  | ||||
| GETTEXT_PACKAGE=gnome-shell | ||||
| AC_SUBST(GETTEXT_PACKAGE) | ||||
| AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", | ||||
|                    [The prefix for our gettext translation domains.]) | ||||
|  | ||||
| AM_GNU_GETTEXT_VERSION([0.19.6]) | ||||
| AM_GNU_GETTEXT([external]) | ||||
|  | ||||
| PKG_PROG_PKG_CONFIG([0.22]) | ||||
|  | ||||
| AC_PATH_PROG([XSLTPROC], [xsltproc]) | ||||
| @@ -40,6 +41,15 @@ GLIB_GSETTINGS | ||||
| AM_PATH_PYTHON([3]) | ||||
| AC_SUBST(PYTHON) | ||||
|  | ||||
| # We depend on a specific version of the libmutter API. The mutter variants of | ||||
| # the Cogl and Clutter libraries also use this API version. | ||||
| LIBMUTTER_API_VERSION=0 | ||||
|  | ||||
| LIBMUTTER=libmutter-$LIBMUTTER_API_VERSION | ||||
| LIBMUTTER_COGL=mutter-cogl-$LIBMUTTER_API_VERSION | ||||
| LIBMUTTER_COGL_PANGO=mutter-cogl-pango-$LIBMUTTER_API_VERSION | ||||
| LIBMUTTER_CLUTTER=mutter-clutter-$LIBMUTTER_API_VERSION | ||||
|  | ||||
| # We need at least this, since gst_plugin_register_static() was added | ||||
| # in 0.10.16, but nothing older than 0.10.21 has been tested. | ||||
| GSTREAMER_MIN_VERSION=0.11.92 | ||||
| @@ -51,7 +61,7 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then | ||||
|    AC_MSG_RESULT(yes) | ||||
|    build_recorder=true | ||||
|    recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0" | ||||
|    PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0) | ||||
|    PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules $LIBMUTTER_CLUTTER) | ||||
| else | ||||
|    AC_MSG_RESULT(no) | ||||
| fi | ||||
| @@ -63,8 +73,8 @@ AC_ARG_ENABLE([systemd], | ||||
|               [enable_systemd=$enableval], | ||||
|               [enable_systemd=auto]) | ||||
| AS_IF([test x$enable_systemd != xno], [ | ||||
|   AC_MSG_CHECKING([for libsystemd-journal]) | ||||
|   PKG_CHECK_EXISTS([libsystemd-journal], | ||||
|   AC_MSG_CHECKING([for libsystemd]) | ||||
|   PKG_CHECK_EXISTS([libsystemd], | ||||
|                    [have_systemd=yes | ||||
|                     AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])], | ||||
|                    [have_systemd=no]) | ||||
| @@ -73,15 +83,13 @@ AS_IF([test x$enable_systemd != xno], [ | ||||
|  | ||||
| AC_MSG_RESULT($enable_systemd) | ||||
|  | ||||
| CLUTTER_MIN_VERSION=1.21.5 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 | ||||
| GJS_MIN_VERSION=1.39.0 | ||||
| MUTTER_MIN_VERSION=3.16.1 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=1.49.1 | ||||
| GJS_MIN_VERSION=1.47.0 | ||||
| MUTTER_MIN_VERSION=3.24.0 | ||||
| GTK_MIN_VERSION=3.15.0 | ||||
| GIO_MIN_VERSION=2.37.0 | ||||
| GIO_MIN_VERSION=2.45.3 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVER_MIN_VERSION=3.13.90 | ||||
| TELEPATHY_GLIB_MIN_VERSION=0.17.5 | ||||
| LIBEDATASERVER_MIN_VERSION=3.17.2 | ||||
| POLKIT_MIN_VERSION=0.100 | ||||
| STARTUP_NOTIFICATION_MIN_VERSION=0.11 | ||||
| GCR_MIN_VERSION=3.7.5 | ||||
| @@ -94,31 +102,32 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
|             libxml-2.0 | ||||
|             gtk+-3.0 >= $GTK_MIN_VERSION | ||||
|             atk-bridge-2.0 | ||||
|             gjs-internals-1.0 >= $GJS_MIN_VERSION | ||||
|             gjs-1.0 >= $GJS_MIN_VERSION | ||||
|             $recorder_modules | ||||
|             gdk-x11-3.0 libsoup-2.4 | ||||
|             clutter-x11-1.0 >= $CLUTTER_MIN_VERSION | ||||
|             clutter-glx-1.0 >= $CLUTTER_MIN_VERSION | ||||
|             $LIBMUTTER_CLUTTER >= $MUTTER_MIN_VERSION | ||||
|             $LIBMUTTER_COGL_PANGO | ||||
|             libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION | ||||
|             gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION | ||||
|             libcanberra libcanberra-gtk3 | ||||
|             telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION | ||||
|             polkit-agent-1 >= $POLKIT_MIN_VERSION | ||||
|             gcr-base-3 >= $GCR_MIN_VERSION" | ||||
| if test x$have_systemd = xyes; then | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd-journal" | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd" | ||||
| fi | ||||
|  | ||||
| PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS) | ||||
| PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION) | ||||
| PKG_CHECK_MODULES(MUTTER, $LIBMUTTER >= $MUTTER_MIN_VERSION) | ||||
|  | ||||
| PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) | ||||
| PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) | ||||
| PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-1.0 >= $GJS_MIN_VERSION) | ||||
| PKG_CHECK_MODULES(ST, $LIBMUTTER_CLUTTER gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) | ||||
| PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0) | ||||
| PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0) | ||||
| PKG_CHECK_MODULES(TRAY, gtk+-3.0) | ||||
| PKG_CHECK_MODULES(TRAY, $LIBMUTTER_CLUTTER gtk+-3.0) | ||||
| PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.13.1) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.21.3) | ||||
|  | ||||
| AC_SUBST(LIBMUTTER_API_VERSION) | ||||
|  | ||||
| AC_ARG_ENABLE(browser-plugin, | ||||
|               [AS_HELP_STRING([--enable-browser-plugin], | ||||
| @@ -144,10 +153,10 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR]) | ||||
|  | ||||
| GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) | ||||
|  | ||||
| MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter` | ||||
| MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir $LIBMUTTER` | ||||
| AC_SUBST(MUTTER_GIR_DIR) | ||||
|  | ||||
| MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter` | ||||
| MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir $LIBMUTTER` | ||||
| AC_SUBST(MUTTER_TYPELIB_DIR) | ||||
|  | ||||
| GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0` | ||||
| @@ -220,7 +229,7 @@ if test "$enable_man" != no; then | ||||
| fi | ||||
| AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) | ||||
|  | ||||
| GNOME_COMPILE_WARNINGS([error]) | ||||
| AX_COMPILER_FLAGS() | ||||
| case "$WARN_CFLAGS" in | ||||
|     *-Werror*) | ||||
|         WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations" | ||||
| @@ -253,7 +262,6 @@ AC_CONFIG_FILES([ | ||||
|   docs/reference/st/Makefile | ||||
|   docs/reference/st/st-docs.sgml | ||||
|   js/Makefile | ||||
|   src/calendar-server/evolution-calendar.desktop.in | ||||
|   src/Makefile | ||||
|   src/gvc/Makefile | ||||
|   browser-plugin/Makefile | ||||
| @@ -269,7 +277,7 @@ Build configuration: | ||||
|        Prefix:                                 ${prefix} | ||||
|        Source code location:                   ${srcdir} | ||||
|        Compiler:                               ${CC} | ||||
|        Compiler Warnings:                      $enable_compile_warnings | ||||
|        Compiler Warnings:                      $ax_enable_compile_warnings | ||||
|  | ||||
|        Support for NetworkManager:             $have_networkmanager | ||||
|        Support for GStreamer recording:        $build_recorder | ||||
|   | ||||
| @@ -1,24 +1,24 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" ?> | ||||
| <KeyListEntries schema="org.gnome.shell.keybindings" | ||||
|                 group="system" | ||||
|                 _name="System" | ||||
|                 name="System" | ||||
|                 wm_name="GNOME Shell" | ||||
|                 package="gnome-shell"> | ||||
| 
 | ||||
| 	<KeyListEntry name="toggle-message-tray" | ||||
|                       _description="Show the notification list"/> | ||||
|                       description="Show the notification list"/> | ||||
| 
 | ||||
| 	<KeyListEntry name="focus-active-notification" | ||||
|                       _description="Focus the active notification"/> | ||||
|                       description="Focus the active notification"/> | ||||
| 
 | ||||
| 	<KeyListEntry name="toggle-overview" | ||||
|                       _description="Show the overview"/> | ||||
|                       description="Show the overview"/> | ||||
| 
 | ||||
| 	<KeyListEntry name="toggle-application-view" | ||||
|                       _description="Show all applications"/> | ||||
|                       description="Show all applications"/> | ||||
| 
 | ||||
| 	<KeyListEntry name="open-application-menu" | ||||
|                       _description="Open the application menu"/> | ||||
|                       description="Open the application menu"/> | ||||
| 
 | ||||
| </KeyListEntries> | ||||
| 
 | ||||
| @@ -2,17 +2,21 @@ CLEANFILES = | ||||
| NULL = | ||||
|  | ||||
| desktopdir=$(datadir)/applications | ||||
| desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop  gnome-shell-extension-prefs.desktop | ||||
| desktop_DATA = org.gnome.Shell.desktop gnome-shell-extension-prefs.desktop | ||||
|  | ||||
| if HAVE_NETWORKMANAGER | ||||
| desktop_DATA += org.gnome.Shell.PortalHelper.desktop | ||||
|  | ||||
| portaldir = $(datadir)/xdg-desktop-portal/portals | ||||
| portal_DATA = gnome-shell.portal | ||||
|  | ||||
| servicedir = $(datadir)/dbus-1/services | ||||
| service_DATA = org.gnome.Shell.PortalHelper.service | ||||
|  | ||||
| CLEANFILES += \ | ||||
| 	org.gnome.Shell.PortalHelper.service \ | ||||
| 	org.gnome.Shell.PortalHelper.desktop \ | ||||
| 	org.gnome.Shell.PortalHelper.service 	\ | ||||
| 	org.gnome.Shell.PortalHelper.desktop 	\ | ||||
| 	org.gnome.Shell.PortalHelper.desktop.in \ | ||||
| 	$(NULL) | ||||
|  | ||||
| endif | ||||
| @@ -28,10 +32,13 @@ endif | ||||
| 	    -e "s|@VERSION[@]|$(VERSION)|" \ | ||||
| 	    $< > $@ || rm $@ | ||||
|  | ||||
| @INTLTOOL_DESKTOP_RULE@ | ||||
| %.desktop:%.desktop.in | ||||
| 	$(AM_V_GEN) $(MSGFMT) --desktop --template $(builddir)/$< \ | ||||
| 	                      -d $(top_srcdir)/po -o $@ | ||||
|  | ||||
| introspectiondir = $(datadir)/dbus-1/interfaces | ||||
| introspection_DATA =				\ | ||||
| 	org.gnome.Shell.PadOsd.xml		\ | ||||
| 	org.gnome.Shell.Screencast.xml		\ | ||||
| 	org.gnome.Shell.Screenshot.xml		\ | ||||
| 	org.gnome.ShellSearchProvider.xml	\ | ||||
| @@ -57,6 +64,7 @@ dist_theme_files =						\ | ||||
| 	theme/gnome-shell-sass/NEWS				\ | ||||
| 	theme/gnome-shell-sass/README				\ | ||||
| 	theme/gnome-shell-sass/gnome-shell-sass.doap		\ | ||||
| 	theme/pad-osd.css						\ | ||||
| 	theme/parse-sass.sh					\ | ||||
| 	$(NULL) | ||||
|  | ||||
| @@ -80,14 +88,11 @@ perf-background.xml: perf-background.xml.in | ||||
| 	    $< > $@ || rm $@ | ||||
|  | ||||
| keysdir = @GNOME_KEYBINDINGS_KEYSDIR@ | ||||
| keys_in_files = 50-gnome-shell-system.xml.in | ||||
| keys_DATA = $(keys_in_files:.xml.in=.xml) | ||||
| keys_DATA = 50-gnome-shell-system.xml | ||||
|  | ||||
| gsettings_SCHEMAS = org.gnome.shell.gschema.xml | ||||
|  | ||||
| @INTLTOOL_XML_NOMERGE_RULE@ | ||||
|  | ||||
| %.gschema.xml.in: %.gschema.xml.in.in Makefile | ||||
| %.gschema.xml: %.gschema.xml.in Makefile | ||||
| 	$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \ | ||||
| 	$< > $@ || rm $@ | ||||
|  | ||||
| @@ -104,32 +109,29 @@ convertdir = $(datadir)/GConf/gsettings | ||||
| convert_DATA = gnome-shell-overrides.convert | ||||
|  | ||||
| EXTRA_DIST =						\ | ||||
| 	gnome-shell.desktop.in.in			\ | ||||
| 	gnome-shell-wayland.desktop.in.in		\ | ||||
| 	org.gnome.Shell.desktop.in.in			\ | ||||
| 	gnome-shell-extension-prefs.desktop.in.in	\ | ||||
| 	$(portal_DATA)					\ | ||||
| 	$(introspection_DATA)				\ | ||||
| 	$(menu_DATA)					\ | ||||
| 	$(convert_DATA)					\ | ||||
| 	$(keys_in_files)				\ | ||||
| 	$(keys_DATA)					\ | ||||
| 	$(dist_theme_files)				\ | ||||
| 	perf-background.xml.in				\ | ||||
| 	org.gnome.Shell.PortalHelper.desktop.in		\ | ||||
| 	org.gnome.Shell.PortalHelper.desktop.in.in	\ | ||||
| 	org.gnome.Shell.PortalHelper.service.in		\ | ||||
| 	org.gnome.shell.gschema.xml.in.in		\ | ||||
| 	org.gnome.shell.gschema.xml.in			\ | ||||
| 	gnome-shell-theme.gresource.xml 		\ | ||||
| 	$(resource_files)				\ | ||||
| 	$(NULL) | ||||
|  | ||||
| CLEANFILES +=						\ | ||||
| 	gnome-shell.desktop.in				\ | ||||
| 	gnome-shell-wayland.desktop.in			\ | ||||
| 	org.gnome.Shell.desktop.in			\ | ||||
| 	gnome-shell-extension-prefs.in			\ | ||||
| 	$(desktop_DATA)					\ | ||||
| 	$(keys_DATA)					\ | ||||
| 	$(gsettings_SCHEMAS)				\ | ||||
| 	perf-background.xml				\ | ||||
| 	gschemas.compiled				\ | ||||
| 	org.gnome.shell.gschema.valid			\ | ||||
| 	org.gnome.shell.gschema.xml.in			\ | ||||
| 	gnome-shell-theme.gresource			\ | ||||
| 	$(NULL) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| _Name=GNOME Shell Extension Preferences | ||||
| _Comment=Configure GNOME Shell Extensions | ||||
| Name=Shell Extensions | ||||
| Comment=Configure GNOME Shell Extensions | ||||
| Exec=@bindir@/gnome-shell-extension-prefs %u | ||||
| X-GNOME-Bugzilla-Bugzilla=GNOME | ||||
| X-GNOME-Bugzilla-Product=gnome-shell | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
|     <file>no-events.svg</file> | ||||
|     <file>no-notifications.svg</file> | ||||
|     <file>noise-texture.png</file> | ||||
|     <file>pad-osd.css</file> | ||||
|     <file>page-indicator-active.svg</file> | ||||
|     <file>page-indicator-inactive.svg</file> | ||||
|     <file>page-indicator-checked.svg</file> | ||||
| @@ -32,8 +33,10 @@ | ||||
|     <file>summary-counter.svg</file> | ||||
|     <file>toggle-off-us.svg</file> | ||||
|     <file>toggle-off-intl.svg</file> | ||||
|     <file>toggle-off-hc.svg</file> | ||||
|     <file>toggle-on-us.svg</file> | ||||
|     <file>toggle-on-intl.svg</file> | ||||
|     <file>toggle-on-hc.svg</file> | ||||
|     <file>ws-switch-arrow-up.png</file> | ||||
|     <file>ws-switch-arrow-down.png</file> | ||||
|   </gresource> | ||||
|   | ||||
| @@ -1,15 +0,0 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| _Name=GNOME Shell (wayland compositor) | ||||
| _Comment=Window management and application launching | ||||
| Exec=@bindir@/gnome-shell --wayland --display-server | ||||
| X-GNOME-Bugzilla-Bugzilla=GNOME | ||||
| X-GNOME-Bugzilla-Product=gnome-shell | ||||
| X-GNOME-Bugzilla-Component=general | ||||
| X-GNOME-Bugzilla-Version=@VERSION@ | ||||
| Categories=GNOME;GTK;Core; | ||||
| OnlyShowIn=GNOME; | ||||
| NoDisplay=true | ||||
| X-GNOME-Autostart-Phase=DisplayServer | ||||
| X-GNOME-Autostart-Notify=true | ||||
| X-GNOME-AutoRestart=false | ||||
							
								
								
									
										4
									
								
								data/gnome-shell.portal
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| [portal] | ||||
| DBusName=org.freedesktop.impl.portal.desktop.gnome | ||||
| Interfaces=org.freedesktop.impl.portal.Access | ||||
| UseIn=gnome | ||||
							
								
								
									
										28
									
								
								data/org.gnome.Shell.PadOsd.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| <!DOCTYPE node PUBLIC | ||||
| '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' | ||||
| 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'> | ||||
| <node> | ||||
|  | ||||
|   <!-- | ||||
|       org.gnome.Shell.PadOSD: | ||||
|       @short_description: Pad OSD interface | ||||
|  | ||||
|       The interface used to show button map OSD on pad devices. | ||||
|   --> | ||||
|   <interface name='org.gnome.Shell.Wacom.PadOsd'> | ||||
|  | ||||
|     <!-- | ||||
|         Show: | ||||
|         @device_node: device node file, usually in /dev/input/... | ||||
|         @edition_mode: whether toggling edition mode on when showing | ||||
|  | ||||
|         Shows the pad button map OSD for the requested device, the OSD | ||||
| 	will be shown according the current device settings (output | ||||
| 	mapping, left handed mode, ...) | ||||
|     --> | ||||
|     <method name='Show'> | ||||
|       <arg name='device_node' direction='in' type='o'/> | ||||
|       <arg name='edition_mode' direction='in' type='b'/> | ||||
|     </method> | ||||
|   </interface> | ||||
| </node> | ||||
| @@ -1,9 +1,10 @@ | ||||
| [Desktop Entry] | ||||
| _Name=Network Login | ||||
| Name=Network Login | ||||
| Type=Application | ||||
| Exec=gapplication launch org.gnome.Shell.PortalHelper | ||||
| DBusActivatable=true | ||||
| NoDisplay=true | ||||
| # Translators: Do NOT translate or transliterate this text (this is an icon file name)! | ||||
| Icon=network-workgroup | ||||
| StartupNotify=true | ||||
| OnlyShowIn=GNOME; | ||||
| @@ -1,7 +1,7 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| _Name=GNOME Shell | ||||
| _Comment=Window management and application launching | ||||
| Name=GNOME Shell | ||||
| Comment=Window management and application launching | ||||
| Exec=@bindir@/gnome-shell | ||||
| X-GNOME-Bugzilla-Bugzilla=GNOME | ||||
| X-GNOME-Bugzilla-Product=gnome-shell | ||||
| @@ -10,7 +10,7 @@ X-GNOME-Bugzilla-Version=@VERSION@ | ||||
| Categories=GNOME;GTK;Core; | ||||
| OnlyShowIn=GNOME; | ||||
| NoDisplay=true | ||||
| X-GNOME-Autostart-Phase=WindowManager | ||||
| X-GNOME-Autostart-Phase=DisplayServer | ||||
| X-GNOME-Provides=panel;windowmanager; | ||||
| X-GNOME-Autostart-Notify=true | ||||
| X-GNOME-AutoRestart=false | ||||
| @@ -3,132 +3,139 @@ | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="development-tools" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary> | ||||
|       <summary> | ||||
|         Enable internal tools useful for developers and testers from Alt-F2 | ||||
|       </_summary> | ||||
|       <_description> | ||||
|       </summary> | ||||
|       <description> | ||||
|         Allows access to internal debugging and monitoring tools | ||||
|         using the Alt-F2 dialog. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="enabled-extensions" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>UUIDs of extensions to enable</_summary> | ||||
|       <_description> | ||||
|       <summary>UUIDs of extensions to enable</summary> | ||||
|       <description> | ||||
|         GNOME Shell extensions have a UUID property; this key lists extensions | ||||
|         which should be loaded. Any extension that wants to be loaded needs | ||||
|         to be in this list. You can also manipulate this list with the | ||||
|         EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="disable-user-extensions" type="b"> | ||||
|       <default>false</default> | ||||
|       <summary>Disable user extensions</summary> | ||||
|       <description> | ||||
|         Disable all extensions the user has enabled without affecting | ||||
|         the “enabled-extension” setting. | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="disable-extension-version-validation" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Disables the validation of extension version compatibility</_summary> | ||||
|       <_description> | ||||
|       <default>true</default> | ||||
|       <summary>Disables the validation of extension version compatibility</summary> | ||||
|       <description> | ||||
|         GNOME Shell will only load extensions that claim to support the current | ||||
|         running version. Enabling this option will disable this check and try to | ||||
|         load all extensions regardless of the versions they claim to support. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="favorite-apps" type="as"> | ||||
|       <default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default> | ||||
|       <_summary>List of desktop file IDs for favorite applications</_summary> | ||||
|       <_description> | ||||
|       <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> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="app-picker-view" type="u"> | ||||
|       <default>0</default> | ||||
|       <_summary>App Picker View</_summary> | ||||
|       <_description> | ||||
|       <summary>App Picker View</summary> | ||||
|       <description> | ||||
|         Index of the currently selected view in the application picker. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="command-history" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>History for command (Alt-F2) dialog</_summary> | ||||
|       <summary>History for command (Alt-F2) dialog</summary> | ||||
|     </key> | ||||
|     <key name="looking-glass-history" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>History for the looking glass dialog</_summary> | ||||
|       <!-- Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass --> | ||||
|       <summary>History for the looking glass dialog</summary> | ||||
|     </key> | ||||
|     <key name="always-show-log-out" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Always show the 'Log out' menu item in the user menu.</_summary> | ||||
|       <_description> | ||||
|         This key overrides the automatic hiding of the 'Log out' | ||||
|       <summary>Always show the “Log out” menu item in the user menu.</summary> | ||||
|       <description> | ||||
|         This key overrides the automatic hiding of the “Log out” | ||||
|         menu item in single-user, single-session situations. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="remember-mount-password" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary> | ||||
|       <_description> | ||||
|       <summary>Whether to remember password for mounting encrypted or remote filesystems</summary> | ||||
|       <description> | ||||
|         The shell will request a password when an encrypted device or a | ||||
|         remote filesystem is mounted.  If the password can be saved for | ||||
|         future use a 'Remember Password' checkbox will be present. | ||||
|         future use a “Remember Password” checkbox will be present. | ||||
|         This key sets the default state of the checkbox. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="had-bluetooth-devices-setup" type="b"> | ||||
|       <default>false</default> | ||||
|       <summary>Whether the default Bluetooth adapter had set up devices associated to it</summary> | ||||
|       <description> | ||||
|         The shell will only show a Bluetooth menu item if a Bluetooth | ||||
|         adapter is powered, or if there were devices set up associated | ||||
|         with the default adapter. This will be reset if the default | ||||
|         adapter is ever seen not to have devices associated to it. | ||||
|       </description> | ||||
|     </key> | ||||
|     <child name="calendar" schema="org.gnome.shell.calendar"/> | ||||
|     <child name="keybindings" schema="org.gnome.shell.keybindings"/> | ||||
|     <child name="keyboard" schema="org.gnome.shell.keyboard"/> | ||||
|   </schema> | ||||
| 
 | ||||
|   <schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="show-weekdate" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Show the week date in the calendar</_summary> | ||||
|       <_description> | ||||
|         If true, display the ISO week date in the calendar. | ||||
|       </_description> | ||||
|       </key> | ||||
|   </schema> | ||||
| 
 | ||||
|   <schema id="org.gnome.shell.keybindings" path="/org/gnome/shell/keybindings/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="open-application-menu" type="as"> | ||||
|       <default>["<Super>F10"]</default> | ||||
|       <_summary>Keybinding to open the application menu</_summary> | ||||
|       <_description> | ||||
|       <summary>Keybinding to open the application menu</summary> | ||||
|       <description> | ||||
|         Keybinding to open the application menu. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="toggle-application-view" type="as"> | ||||
|       <default>["<Super>a"]</default> | ||||
|       <_summary>Keybinding to open the "Show Applications" view</_summary> | ||||
|       <_description> | ||||
|         Keybinding to open the "Show Applications" view of the Activities | ||||
|       <summary>Keybinding to open the “Show Applications” view</summary> | ||||
|       <description> | ||||
|         Keybinding to open the “Show Applications” view of the Activities | ||||
|         Overview. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="toggle-overview" type="as"> | ||||
|       <default>["<Super>s"]</default> | ||||
|       <_summary>Keybinding to open the overview</_summary> | ||||
|       <_description> | ||||
|       <summary>Keybinding to open the overview</summary> | ||||
|       <description> | ||||
|         Keybinding to open the Activities Overview. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="toggle-message-tray" type="as"> | ||||
|       <default>["<Super>v","<Super>m"]</default> | ||||
|       <_summary>Keybinding to toggle the visibility of the notification list</_summary> | ||||
|       <_description> | ||||
|       <summary>Keybinding to toggle the visibility of the notification list</summary> | ||||
|       <description> | ||||
|         Keybinding to toggle the visibility of the notification list. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="focus-active-notification" type="as"> | ||||
|       <default>["<Super>n"]</default> | ||||
|       <_summary>Keybinding to focus the active notification</_summary> | ||||
|       <_description> | ||||
|       <summary>Keybinding to focus the active notification</summary> | ||||
|       <description> | ||||
|         Keybinding to focus the active notification. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|     <key name="pause-resume-tweens" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</_summary> | ||||
|       <_description></_description> | ||||
|       <summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary> | ||||
|       <description></description> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
| @@ -136,10 +143,10 @@ | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="keyboard-type" type="s"> | ||||
|       <default>'touch'</default> | ||||
|       <_summary>Which keyboard to use</_summary> | ||||
|       <_description> | ||||
|       <summary>Which keyboard to use</summary> | ||||
|       <description> | ||||
|         The type of keyboard to use. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
| @@ -148,11 +155,11 @@ | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key type="b" name="current-workspace-only"> | ||||
|       <default>false</default> | ||||
|       <_summary>Limit switcher to current workspace.</_summary> | ||||
|       <_description> | ||||
|       <summary>Limit switcher to current workspace.</summary> | ||||
|       <description> | ||||
| 	If true, only applications that have windows on the current workspace are shown in the switcher. | ||||
| 	Otherwise, all applications are included. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
| @@ -166,20 +173,20 @@ | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode"> | ||||
|       <default>'both'</default> | ||||
|       <_summary>The application icon mode.</_summary> | ||||
|       <_description> | ||||
|       <summary>The application icon mode.</summary> | ||||
|       <description> | ||||
| 	Configures how the windows are shown in the switcher. Valid possibilities | ||||
| 	are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only' | ||||
| 	(shows only the application icon) or 'both'. | ||||
|       </_description> | ||||
| 	are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only” | ||||
| 	(shows only the application icon) or “both”. | ||||
|       </description> | ||||
|     </key> | ||||
|     <key type="b" name="current-workspace-only"> | ||||
|       <default>true</default> | ||||
|       <_summary>Limit switcher to current workspace.</_summary> | ||||
|       <_description> | ||||
|       <summary>Limit switcher to current workspace.</summary> | ||||
|       <description> | ||||
| 	If true, only windows from the current workspace are shown in the switcher. | ||||
| 	Otherwise, all windows are included. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|   </schema> | ||||
| 
 | ||||
| @@ -187,43 +194,43 @@ | ||||
| 	  gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="attach-modal-dialogs" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Attach modal dialog to the parent window</_summary> | ||||
|       <_description> | ||||
|       <summary>Attach modal dialog to the parent window</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running | ||||
|         GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
| 
 | ||||
|     <key name="edge-tiling" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Enable edge tiling when dropping windows on screen edges</_summary> | ||||
|       <_description> | ||||
|       <summary>Enable edge tiling when dropping windows on screen edges</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
| 
 | ||||
|     <key name="dynamic-workspaces" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Workspaces are managed dynamically</_summary> | ||||
|       <_description> | ||||
|       <summary>Workspaces are managed dynamically</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
| 
 | ||||
|     <key name="workspaces-only-on-primary" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Workspaces only on primary monitor</_summary> | ||||
|       <_description> | ||||
|       <summary>Workspaces only on primary monitor</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
| 
 | ||||
|     <key name="focus-change-on-pointer-rest" type="b"> | ||||
|       <default>true</default> | ||||
|       <_summary>Delay focus changes in mouse mode until the pointer stops moving</_summary> | ||||
|       <_description> | ||||
|       <summary>Delay focus changes in mouse mode until the pointer stops moving</summary> | ||||
|       <description> | ||||
|         This key overrides the key in org.gnome.mutter when running GNOME Shell. | ||||
|       </_description> | ||||
|       </description> | ||||
|     </key> | ||||
|   </schema> | ||||
| </schemalist> | ||||
| @@ -10,11 +10,11 @@ | ||||
|    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="29" | ||||
|    height="29" | ||||
|    width="24" | ||||
|    height="24" | ||||
|    id="svg10621" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="calendar-today.svg"> | ||||
|   <defs | ||||
|      id="defs10623"> | ||||
| @@ -118,17 +118,6 @@ | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient34508-1-3" | ||||
|        id="radialGradient3113" | ||||
|        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" | ||||
| @@ -137,22 +126,23 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="15.839192" | ||||
|      inkscape:cx="20.652108" | ||||
|      inkscape:cy="11.839084" | ||||
|      inkscape:zoom="8" | ||||
|      inkscape:cx="-23.537329" | ||||
|      inkscape:cy="-31.442864" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="741" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true"> | ||||
|      inkscape:window-width="2133" | ||||
|      inkscape:window-height="1241" | ||||
|      inkscape:window-x="238" | ||||
|      inkscape:window-y="88" | ||||
|      inkscape:window-maximized="0" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid3109" | ||||
| @@ -169,7 +159,7 @@ | ||||
|         <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> | ||||
| @@ -177,28 +167,12 @@ | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-469.08263,-532.99307)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);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 A 42,16 0 0 1 93,30 l -42,0 z" | ||||
|        sodipodi:start="3.1415927" | ||||
|        sodipodi:end="6.2831853" | ||||
|        transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)" | ||||
|        inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png" | ||||
|        inkscape:export-xdpi="90" | ||||
|        inkscape:export-ydpi="90" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|        id="rect2996" | ||||
|        width="31" | ||||
|        height="3" | ||||
|        x="468.08264" | ||||
|        y="558.99304" /> | ||||
|      transform="translate(-469.08263,-537.99307)"> | ||||
|     <circle | ||||
|        style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|        id="path7305" | ||||
|        cx="481.57138" | ||||
|        cy="559.4649" | ||||
|        r="1.5" /> | ||||
|   </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.6 KiB | 
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 68, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(166, 166, 166, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     border-color: #0d0d0d; | ||||
|     color: gray; | ||||
|     border-color: #0e0e0e; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -95,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #000; | ||||
|     background-color: #999999; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #1a1a1a; } | ||||
|       background-color: #cccccc; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -106,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #0d0d0d; | ||||
|   -slider-background-color: #0e0e0e; | ||||
|   -slider-border-color: black; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -160,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -179,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -191,7 +224,7 @@ StScrollBar { | ||||
| /* End Session Dialog */ | ||||
| .end-session-dialog { | ||||
|   spacing: 42px; | ||||
|   border: 3px solid rgba(238, 238, 236, 0.2); } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|  | ||||
| .end-session-dialog-list { | ||||
|   padding-top: 20px; } | ||||
| @@ -366,6 +399,77 @@ StScrollBar { | ||||
|   width: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| /* Audio selection dialog */ | ||||
| .audio-device-selection-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .audio-selection-content { | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .audio-selection-title { | ||||
|   font-weight: bold; | ||||
|   text-align: center; } | ||||
|  | ||||
| .audio-selection-box { | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .audio-selection-device { | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); | ||||
|   border-radius: 12px; } | ||||
|   .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus { | ||||
|     background-color: #215d9c; } | ||||
|  | ||||
| .audio-selection-device-box { | ||||
|   padding: 20px; | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .audio-selection-device-icon { | ||||
|   icon-size: 64px; } | ||||
|  | ||||
| /* Access Dialog */ | ||||
| .access-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .access-dialog-main-layout { | ||||
|   padding: 12px 20px 0; | ||||
|   spacing: 12px; } | ||||
|  | ||||
| .access-dialog-content { | ||||
|   max-width: 28em; | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .access-dialog-icon { | ||||
|   min-width: 48px; | ||||
|   icon-size: 48px; } | ||||
|  | ||||
| .access-dialog-title { | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .access-dialog-subtitle { | ||||
|   color: #999999; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| /* Geolocation Dialog */ | ||||
| .geolocation-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .geolocation-dialog-main-layout { | ||||
|   spacing: 12px; } | ||||
|  | ||||
| .geolocation-dialog-content { | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .geolocation-dialog-icon { | ||||
|   icon-size: 48px; } | ||||
|  | ||||
| .geolocation-dialog-title { | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .geolocation-dialog-reason { | ||||
|   color: #999999; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| /* Network Agent Dialog */ | ||||
| .network-dialog-secret-table { | ||||
|   spacing-rows: 15px; | ||||
| @@ -375,9 +479,9 @@ StScrollBar { | ||||
|   spacing-rows: 15px; | ||||
|   spacing-columns: 1em; } | ||||
|  | ||||
| /* Popvers/Menus */ | ||||
| /* Popovers/Menus */ | ||||
| .popup-menu { | ||||
|   min-width: 200px; } | ||||
|   min-width: 15em; } | ||||
|   .popup-menu .popup-sub-menu { | ||||
|     background-color: black; | ||||
|     box-shadow: inset 0 -1px 0px #0d0d0d; } | ||||
| @@ -411,7 +515,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -454,6 +558,21 @@ StScrollBar { | ||||
|     border-radius: 0.3em; | ||||
|     background-color: rgba(11, 12, 13, 0.5); | ||||
|     color: #eeeeec; } | ||||
|   .osd-window .level-bar { | ||||
|     background-color: #eeeeec; | ||||
|     border-radius: 0.3em; } | ||||
|  | ||||
| /* Pad OSD */ | ||||
| .pad-osd-window { | ||||
|   padding: 32px; | ||||
|   background-color: rgba(0, 0, 0, 0.8); } | ||||
|   .pad-osd-window .pad-osd-title-box { | ||||
|     spacing: 12px; } | ||||
|   .pad-osd-window .pad-osd-title-menu-box { | ||||
|     spacing: 6px; } | ||||
|  | ||||
| .combo-box-label { | ||||
|   width: 15em; } | ||||
|  | ||||
| /* App Switcher */ | ||||
| .switcher-popup { | ||||
| @@ -488,16 +607,19 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: black; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #fff; } | ||||
|   color: rgba(255, 255, 255, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #fff; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
|   width: 96px; | ||||
|   height: 96px; } | ||||
|  | ||||
| /* Window Cycler */ | ||||
| .cycler-highlight { | ||||
|   border: 5px solid #215d9c; } | ||||
|  | ||||
| /* Workspace Switcher */ | ||||
| .workspace-switcher-group { | ||||
|   padding: 12px; } | ||||
| @@ -593,6 +715,8 @@ StScrollBar { | ||||
|   #panel .panel-status-indicators-box, | ||||
|   #panel .panel-status-menu-box { | ||||
|     spacing: 2px; } | ||||
|   #panel .power-status.panel-status-indicators-box { | ||||
|     spacing: 0; } | ||||
|   #panel .screencast-indicator { | ||||
|     color: #f57900; } | ||||
|  | ||||
| @@ -614,9 +738,20 @@ StScrollBar { | ||||
| .datemenu-displays-section { | ||||
|   padding-bottom: 3em; } | ||||
|  | ||||
| .datemenu-displays-box { | ||||
|   spacing: 1em; } | ||||
|  | ||||
| .datemenu-calendar-column { | ||||
|   border: 0 solid #0d0d0d; } | ||||
|   .datemenu-calendar-column:ltr { | ||||
|     border-left-width: 1px; } | ||||
|   .datemenu-calendar-column:rtl { | ||||
|     border-right-width: 1px; } | ||||
|  | ||||
| .datemenu-today-button, | ||||
| .world-clocks-button, | ||||
| .message-list-section-title { | ||||
| .weather-button, | ||||
| .events-section-title { | ||||
|   border-radius: 4px; | ||||
|   padding: .4em; } | ||||
|  | ||||
| @@ -629,12 +764,15 @@ StScrollBar { | ||||
| .datemenu-today-button:hover, .datemenu-today-button:focus, | ||||
| .world-clocks-button:hover, | ||||
| .world-clocks-button:focus, | ||||
| .message-list-section-title:hover, | ||||
| .message-list-section-title:focus { | ||||
| .weather-button:hover, | ||||
| .weather-button:focus, | ||||
| .events-section-title:hover, | ||||
| .events-section-title:focus { | ||||
|   background-color: #0d0d0d; } | ||||
| .datemenu-today-button:active, | ||||
| .world-clocks-button:active, | ||||
| .message-list-section-title:active { | ||||
| .weather-button:active, | ||||
| .events-section-title:active { | ||||
|   color: white; | ||||
|   background-color: #215d9c; } | ||||
|  | ||||
| @@ -642,13 +780,17 @@ StScrollBar { | ||||
|   font-size: 1.5em; } | ||||
|  | ||||
| .world-clocks-header, | ||||
| .message-list-section-title { | ||||
| .weather-header, | ||||
| .events-section-title { | ||||
|   color: #999999; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .world-clocks-grid { | ||||
|   spacing-rows: 0.4em; } | ||||
|  | ||||
| .weather-box { | ||||
|   spacing: 0.4em; } | ||||
|  | ||||
| .calendar-month-label { | ||||
|   color: #f2f2f2; | ||||
|   font-weight: bold; | ||||
| @@ -684,7 +826,7 @@ StScrollBar { | ||||
|   border-radius: 1.4em; } | ||||
|   .calendar-day-base:hover, .calendar-day-base:focus { | ||||
|     background-color: #0d0d0d; } | ||||
|   .calendar-day-base:active { | ||||
|   .calendar-day-base:active, .calendar-day-base:selected { | ||||
|     color: white; | ||||
|     background-color: #215d9c; | ||||
|     border-color: transparent; } | ||||
| @@ -703,74 +845,113 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #7f7f7f; } | ||||
|   color: gray; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #f2f2f2; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(255, 255, 255, 0.15); | ||||
|   opacity: 0.5; } | ||||
|  | ||||
| .calendar-week-number { | ||||
|   font-size: 70%; | ||||
|   font-weight: bold; | ||||
|   width: 2.3em; | ||||
|   height: 1.8em; | ||||
|   border-radius: 2px; | ||||
|   padding: 0.5em 0 0; | ||||
|   margin: 6px; | ||||
|   background-color: rgba(255, 255, 255, 0.3); | ||||
|   color: #000; } | ||||
|  | ||||
| /* Message list */ | ||||
| .message-list { | ||||
|   width: 420px; } | ||||
|   width: 31.5em; } | ||||
|  | ||||
| .message-list-clear-button.button { | ||||
|   background-color: transparent; | ||||
|   margin: 1.5em 1.5em 0; } | ||||
|   .message-list-clear-button.button:hover, .message-list-clear-button.button:focus { | ||||
|     background-color: #0d0d0d; } | ||||
|  | ||||
| .message-list-sections { | ||||
|   spacing: 1.5em; } | ||||
|   spacing: 1em; } | ||||
|  | ||||
| .message-list-section, | ||||
| .message-list-section-list { | ||||
|   spacing: 0.7em; } | ||||
|  | ||||
| .message-list-section-title-box { | ||||
|   spacing: 0.4em; } | ||||
|  | ||||
| .message-list-section-close > StIcon { | ||||
|   icon-size: 16px; | ||||
|   border-radius: 8px; | ||||
|   color: #000; | ||||
|   background-color: #666666; } | ||||
|  | ||||
| /* FIXME: how do you do this in sass? */ | ||||
| .message-list-section-close:hover > StIcon, | ||||
| .message-list-section-close:focus > StIcon { | ||||
|   background-color: #999999; } | ||||
|  | ||||
| .message { | ||||
|   background-color: #0d0d0d; | ||||
|   border-radius: 3px; } | ||||
|   .message:hover, .message:focus { | ||||
|     background-color: #262626; } | ||||
|     background-color: #0d0d0d; } | ||||
|  | ||||
| .message-icon-bin { | ||||
|   padding: 8px 0px 8px 8px; } | ||||
|   padding: 10px 3px 10px 10px; } | ||||
|   .message-icon-bin:rtl { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|     padding: 10px 10px 10px 3px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   color: #cccccc; | ||||
|   icon-size: 16px; | ||||
|   -st-icon-style: symbolic; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #999999; } | ||||
|   padding: 0 12px; } | ||||
|  | ||||
| .message-secondary-bin > .event-time { | ||||
|   color: #999999; | ||||
|   font-size: 0.7em; | ||||
|   /* HACK: the label should be baseline-aligned with a 1em label, | ||||
|            fake this with some bottom padding */ | ||||
|   padding-bottom: 0.13em; } | ||||
|  | ||||
| .message-secondary-bin > StIcon { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   color: #f2f2f2; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   color: #cccccc; | ||||
|   padding: 10px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(255, 255, 255, 0.4); } | ||||
| .message-media-control { | ||||
|   padding: 12px; | ||||
|   color: #cccccc; } | ||||
|   .message-media-control:last-child:ltr { | ||||
|     padding-right: 18px; } | ||||
|   .message-media-control:last-child:rtl { | ||||
|     padding-left: 18px; } | ||||
|   .message-media-control:hover { | ||||
|     color: #fff; } | ||||
|   .message-media-control:insensitive { | ||||
|     color: #999999; } | ||||
|  | ||||
| .media-message-cover-icon { | ||||
|   icon-size: 48px !important; } | ||||
|   .media-message-cover-icon.fallback { | ||||
|     color: #1a1a1a; | ||||
|     background-color: #000; | ||||
|     border: 2px solid #000; | ||||
|     border-radius: 2px; | ||||
|     icon-size: 16px; | ||||
|     padding: 8px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -779,7 +960,7 @@ StScrollBar { | ||||
|     color: transparent; } | ||||
|  | ||||
| .aggregate-menu { | ||||
|   width: 360px; } | ||||
|   min-width: 21em; } | ||||
|   .aggregate-menu .popup-menu-icon { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| @@ -834,7 +1015,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -915,10 +1097,14 @@ StScrollBar { | ||||
| .search-entry { | ||||
|   width: 320px; | ||||
|   padding: 7px 9px; | ||||
|   border-radius: 6px; } | ||||
|   border-radius: 6px; | ||||
|   border-color: #747467; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; } | ||||
|   .search-entry:focus { | ||||
|     padding: 6px 8px; | ||||
|     border-width: 2px; } | ||||
|     border-width: 2px; | ||||
|     border-color: #215d9c; } | ||||
|   .search-entry .search-entry-icon { | ||||
|     icon-size: 1em; | ||||
|     padding: 0 4px; | ||||
| @@ -1016,7 +1202,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1335,10 +1521,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1350,7 +1535,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1443,13 +1628,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #7f7f7f; | ||||
|         color: gray; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(62, 67, 68, 0.7); | ||||
|         background-color: rgba(62, 67, 69, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
| @@ -1696,3 +1881,11 @@ StScrollBar { | ||||
|  | ||||
| stage { | ||||
|   -st-icon-style: symbolic; } | ||||
|  | ||||
| .toggle-switch { | ||||
|   width: 48px; } | ||||
|  | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); } | ||||
|   .toggle-switch-us:checked, .toggle-switch-intl:checked { | ||||
|     background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
|   | ||||
| @@ -6,3 +6,9 @@ | ||||
| stage { | ||||
|   -st-icon-style: symbolic; | ||||
| } | ||||
|  | ||||
| .toggle-switch { width: 48px; } | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); | ||||
|   &:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
| } | ||||
|   | ||||
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #939695; | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 71, 73, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(154, 154, 142, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #939695; | ||||
|     border-color: #323636; | ||||
|     color: #949796; | ||||
|     border-color: #333636; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -95,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #393f3f; | ||||
|     background-color: #a6a8a7; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #515a5a; } | ||||
|       background-color: #cacbc9; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -106,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #323636; | ||||
|   -slider-background-color: #333636; | ||||
|   -slider-border-color: #1c1f1f; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -160,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -179,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -191,7 +224,7 @@ StScrollBar { | ||||
| /* End Session Dialog */ | ||||
| .end-session-dialog { | ||||
|   spacing: 42px; | ||||
|   border: 3px solid rgba(238, 238, 236, 0.2); } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|  | ||||
| .end-session-dialog-list { | ||||
|   padding-top: 20px; } | ||||
| @@ -366,6 +399,77 @@ StScrollBar { | ||||
|   width: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| /* Audio selection dialog */ | ||||
| .audio-device-selection-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .audio-selection-content { | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .audio-selection-title { | ||||
|   font-weight: bold; | ||||
|   text-align: center; } | ||||
|  | ||||
| .audio-selection-box { | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .audio-selection-device { | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); | ||||
|   border-radius: 12px; } | ||||
|   .audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus { | ||||
|     background-color: #215d9c; } | ||||
|  | ||||
| .audio-selection-device-box { | ||||
|   padding: 20px; | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .audio-selection-device-icon { | ||||
|   icon-size: 64px; } | ||||
|  | ||||
| /* Access Dialog */ | ||||
| .access-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .access-dialog-main-layout { | ||||
|   padding: 12px 20px 0; | ||||
|   spacing: 12px; } | ||||
|  | ||||
| .access-dialog-content { | ||||
|   max-width: 28em; | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .access-dialog-icon { | ||||
|   min-width: 48px; | ||||
|   icon-size: 48px; } | ||||
|  | ||||
| .access-dialog-title { | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .access-dialog-subtitle { | ||||
|   color: #8e8e80; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| /* Geolocation Dialog */ | ||||
| .geolocation-dialog { | ||||
|   spacing: 30px; } | ||||
|  | ||||
| .geolocation-dialog-main-layout { | ||||
|   spacing: 12px; } | ||||
|  | ||||
| .geolocation-dialog-content { | ||||
|   spacing: 20px; } | ||||
|  | ||||
| .geolocation-dialog-icon { | ||||
|   icon-size: 48px; } | ||||
|  | ||||
| .geolocation-dialog-title { | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .geolocation-dialog-reason { | ||||
|   color: #8e8e80; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| /* Network Agent Dialog */ | ||||
| .network-dialog-secret-table { | ||||
|   spacing-rows: 15px; | ||||
| @@ -375,9 +479,9 @@ StScrollBar { | ||||
|   spacing-rows: 15px; | ||||
|   spacing-columns: 1em; } | ||||
|  | ||||
| /* Popvers/Menus */ | ||||
| /* Popovers/Menus */ | ||||
| .popup-menu { | ||||
|   min-width: 200px; } | ||||
|   min-width: 15em; } | ||||
|   .popup-menu .popup-sub-menu { | ||||
|     background-color: #343a3a; | ||||
|     box-shadow: inset 0 -1px 0px #282c2c; } | ||||
| @@ -411,7 +515,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -454,6 +558,21 @@ StScrollBar { | ||||
|     border-radius: 0.3em; | ||||
|     background-color: rgba(11, 12, 13, 0.5); | ||||
|     color: #eeeeec; } | ||||
|   .osd-window .level-bar { | ||||
|     background-color: #eeeeec; | ||||
|     border-radius: 0.3em; } | ||||
|  | ||||
| /* Pad OSD */ | ||||
| .pad-osd-window { | ||||
|   padding: 32px; | ||||
|   background-color: rgba(0, 0, 0, 0.8); } | ||||
|   .pad-osd-window .pad-osd-title-box { | ||||
|     spacing: 12px; } | ||||
|   .pad-osd-window .pad-osd-title-menu-box { | ||||
|     spacing: 6px; } | ||||
|  | ||||
| .combo-box-label { | ||||
|   width: 15em; } | ||||
|  | ||||
| /* App Switcher */ | ||||
| .switcher-popup { | ||||
| @@ -488,16 +607,19 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: #1c1f1f; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #eeeeec; } | ||||
|   color: rgba(238, 238, 236, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #eeeeec; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
|   width: 96px; | ||||
|   height: 96px; } | ||||
|  | ||||
| /* Window Cycler */ | ||||
| .cycler-highlight { | ||||
|   border: 5px solid #215d9c; } | ||||
|  | ||||
| /* Workspace Switcher */ | ||||
| .workspace-switcher-group { | ||||
|   padding: 12px; } | ||||
| @@ -593,6 +715,8 @@ StScrollBar { | ||||
|   #panel .panel-status-indicators-box, | ||||
|   #panel .panel-status-menu-box { | ||||
|     spacing: 2px; } | ||||
|   #panel .power-status.panel-status-indicators-box { | ||||
|     spacing: 0; } | ||||
|   #panel .screencast-indicator { | ||||
|     color: #f57900; } | ||||
|  | ||||
| @@ -614,9 +738,20 @@ StScrollBar { | ||||
| .datemenu-displays-section { | ||||
|   padding-bottom: 3em; } | ||||
|  | ||||
| .datemenu-displays-box { | ||||
|   spacing: 1em; } | ||||
|  | ||||
| .datemenu-calendar-column { | ||||
|   border: 0 solid #454c4c; } | ||||
|   .datemenu-calendar-column:ltr { | ||||
|     border-left-width: 1px; } | ||||
|   .datemenu-calendar-column:rtl { | ||||
|     border-right-width: 1px; } | ||||
|  | ||||
| .datemenu-today-button, | ||||
| .world-clocks-button, | ||||
| .message-list-section-title { | ||||
| .weather-button, | ||||
| .events-section-title { | ||||
|   border-radius: 4px; | ||||
|   padding: .4em; } | ||||
|  | ||||
| @@ -629,12 +764,15 @@ StScrollBar { | ||||
| .datemenu-today-button:hover, .datemenu-today-button:focus, | ||||
| .world-clocks-button:hover, | ||||
| .world-clocks-button:focus, | ||||
| .message-list-section-title:hover, | ||||
| .message-list-section-title:focus { | ||||
| .weather-button:hover, | ||||
| .weather-button:focus, | ||||
| .events-section-title:hover, | ||||
| .events-section-title:focus { | ||||
|   background-color: #454c4c; } | ||||
| .datemenu-today-button:active, | ||||
| .world-clocks-button:active, | ||||
| .message-list-section-title:active { | ||||
| .weather-button:active, | ||||
| .events-section-title:active { | ||||
|   color: white; | ||||
|   background-color: #215d9c; } | ||||
|  | ||||
| @@ -642,13 +780,17 @@ StScrollBar { | ||||
|   font-size: 1.5em; } | ||||
|  | ||||
| .world-clocks-header, | ||||
| .message-list-section-title { | ||||
| .weather-header, | ||||
| .events-section-title { | ||||
|   color: #8e8e80; | ||||
|   font-weight: bold; } | ||||
|  | ||||
| .world-clocks-grid { | ||||
|   spacing-rows: 0.4em; } | ||||
|  | ||||
| .weather-box { | ||||
|   spacing: 0.4em; } | ||||
|  | ||||
| .calendar-month-label { | ||||
|   color: #e2e2df; | ||||
|   font-weight: bold; | ||||
| @@ -684,7 +826,7 @@ StScrollBar { | ||||
|   border-radius: 1.4em; } | ||||
|   .calendar-day-base:hover, .calendar-day-base:focus { | ||||
|     background-color: #454c4c; } | ||||
|   .calendar-day-base:active { | ||||
|   .calendar-day-base:active, .calendar-day-base:selected { | ||||
|     color: white; | ||||
|     background-color: #215d9c; | ||||
|     border-color: transparent; } | ||||
| @@ -703,74 +845,113 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #939695; } | ||||
|   color: #949796; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(28, 31, 31, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #e2e2df; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(238, 238, 236, 0.15); | ||||
|   opacity: 0.5; } | ||||
|  | ||||
| .calendar-week-number { | ||||
|   font-size: 70%; | ||||
|   font-weight: bold; | ||||
|   width: 2.3em; | ||||
|   height: 1.8em; | ||||
|   border-radius: 2px; | ||||
|   padding: 0.5em 0 0; | ||||
|   margin: 6px; | ||||
|   background-color: rgba(238, 238, 236, 0.3); | ||||
|   color: #393f3f; } | ||||
|  | ||||
| /* Message list */ | ||||
| .message-list { | ||||
|   width: 420px; } | ||||
|   width: 31.5em; } | ||||
|  | ||||
| .message-list-clear-button.button { | ||||
|   background-color: transparent; | ||||
|   margin: 1.5em 1.5em 0; } | ||||
|   .message-list-clear-button.button:hover, .message-list-clear-button.button:focus { | ||||
|     background-color: #454c4c; } | ||||
|  | ||||
| .message-list-sections { | ||||
|   spacing: 1.5em; } | ||||
|   spacing: 1em; } | ||||
|  | ||||
| .message-list-section, | ||||
| .message-list-section-list { | ||||
|   spacing: 0.7em; } | ||||
|  | ||||
| .message-list-section-title-box { | ||||
|   spacing: 0.4em; } | ||||
|  | ||||
| .message-list-section-close > StIcon { | ||||
|   icon-size: 16px; | ||||
|   border-radius: 8px; | ||||
|   color: #393f3f; | ||||
|   background-color: #59594f; } | ||||
|  | ||||
| /* FIXME: how do you do this in sass? */ | ||||
| .message-list-section-close:hover > StIcon, | ||||
| .message-list-section-close:focus > StIcon { | ||||
|   background-color: #8e8e80; } | ||||
|  | ||||
| .message { | ||||
|   background-color: #454c4c; | ||||
|   border-radius: 3px; } | ||||
|   .message:hover, .message:focus { | ||||
|     background-color: #5d6767; } | ||||
|     background-color: #454c4c; } | ||||
|  | ||||
| .message-icon-bin { | ||||
|   padding: 8px 0px 8px 8px; } | ||||
|   padding: 10px 3px 10px 10px; } | ||||
|   .message-icon-bin:rtl { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|     padding: 10px 10px 10px 3px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   color: #bebeb6; | ||||
|   icon-size: 16px; | ||||
|   -st-icon-style: symbolic; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #8e8e80; } | ||||
|   padding: 0 12px; } | ||||
|  | ||||
| .message-secondary-bin > .event-time { | ||||
|   color: #8e8e80; | ||||
|   font-size: 0.7em; | ||||
|   /* HACK: the label should be baseline-aligned with a 1em label, | ||||
|            fake this with some bottom padding */ | ||||
|   padding-bottom: 0.13em; } | ||||
|  | ||||
| .message-secondary-bin > StIcon { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   color: #e2e2df; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   color: #bebeb6; | ||||
|   padding: 10px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(238, 238, 236, 0.4); } | ||||
| .message-media-control { | ||||
|   padding: 12px; | ||||
|   color: #bebeb6; } | ||||
|   .message-media-control:last-child:ltr { | ||||
|     padding-right: 18px; } | ||||
|   .message-media-control:last-child:rtl { | ||||
|     padding-left: 18px; } | ||||
|   .message-media-control:hover { | ||||
|     color: #eeeeec; } | ||||
|   .message-media-control:insensitive { | ||||
|     color: #8e8e80; } | ||||
|  | ||||
| .media-message-cover-icon { | ||||
|   icon-size: 48px !important; } | ||||
|   .media-message-cover-icon.fallback { | ||||
|     color: #515a5a; | ||||
|     background-color: #393f3f; | ||||
|     border: 2px solid #393f3f; | ||||
|     border-radius: 2px; | ||||
|     icon-size: 16px; | ||||
|     padding: 8px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -779,7 +960,7 @@ StScrollBar { | ||||
|     color: transparent; } | ||||
|  | ||||
| .aggregate-menu { | ||||
|   width: 360px; } | ||||
|   min-width: 21em; } | ||||
|   .aggregate-menu .popup-menu-icon { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| @@ -834,7 +1015,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -915,10 +1097,14 @@ StScrollBar { | ||||
| .search-entry { | ||||
|   width: 320px; | ||||
|   padding: 7px 9px; | ||||
|   border-radius: 6px; } | ||||
|   border-radius: 6px; | ||||
|   border-color: #747467; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; } | ||||
|   .search-entry:focus { | ||||
|     padding: 6px 8px; | ||||
|     border-width: 2px; } | ||||
|     border-width: 2px; | ||||
|     border-color: #215d9c; } | ||||
|   .search-entry .search-entry-icon { | ||||
|     icon-size: 1em; | ||||
|     padding: 0 4px; | ||||
| @@ -1016,7 +1202,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1335,10 +1521,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1350,7 +1535,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1443,13 +1628,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #939695; | ||||
|         color: #949796; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(66, 71, 73, 0.7); | ||||
|         background-color: rgba(66, 72, 73, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|   | ||||
							
								
								
									
										30
									
								
								data/theme/pad-osd.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| .Leader { | ||||
|     stroke-width: .5 !important; | ||||
|     stroke: #535353; | ||||
|     fill: none !important; | ||||
| } | ||||
|  | ||||
| .Button { | ||||
|     stroke-width: .25; | ||||
|     stroke: #ededed; | ||||
|     fill: #ededed; | ||||
| } | ||||
|  | ||||
| .Ring { | ||||
|     stroke-width: .5 !important; | ||||
|     stroke: #535353 !important; | ||||
|     fill: none !important; | ||||
| } | ||||
|  | ||||
| .Label { | ||||
|     stroke: none !important; | ||||
|     stroke-width: .1 !important; | ||||
|     font-size: .1 !important; | ||||
|     fill: transparent !important; | ||||
| } | ||||
|  | ||||
| .TouchStrip, .TouchRing { | ||||
|     stroke-width: .1 !important; | ||||
|     stroke: #ededed !important; | ||||
|     fill: #535353 !important; | ||||
| } | ||||
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 98 KiB | 
							
								
								
									
										133
									
								
								data/theme/toggle-off-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,133 @@ | ||||
| <?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="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-off-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        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" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-6.1820581" | ||||
|      inkscape:cy="-16.463788" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <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(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.76058,489.69039)" | ||||
|          id="toggle-off" | ||||
|          inkscape:label="#g8477"> | ||||
|         <circle | ||||
|            cy="1033.993" | ||||
|            cx="571.95966" | ||||
|            id="path8444" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            r="7" /> | ||||
|         <rect | ||||
|            ry="2.0108337" | ||||
|            rx="1.9562569" | ||||
|            y="1031.9885" | ||||
|            x="565.0083" | ||||
|            height="4.0216675" | ||||
|            width="34.850178" | ||||
|            id="rect8461" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,491.16456)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										113
									
								
								data/theme/toggle-on-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,113 @@ | ||||
| <?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="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        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" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-222.95215" | ||||
|      inkscape:cy="3.9378433" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <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(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,441.50868)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 4.4 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-intl.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -52,7 +52,7 @@ | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
| @@ -88,14 +88,14 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="32" | ||||
|      inkscape:cx="17.255148" | ||||
|      inkscape:cy="8.9252639" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="37.410841" | ||||
|      inkscape:cy="16.009314" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|   | ||||
| Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-us.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -32,28 +32,6 @@ | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461" | ||||
|        id="linearGradient77551" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient76469-7-7-4" | ||||
| @@ -80,6 +58,38 @@ | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461-1" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463-1" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465-4" | ||||
|          offset="1" | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient77551-6-5" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.8527367,0,0,0.80554422,-969.41608,-778.00299)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient11198" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-1066.3709,794.25325)" | ||||
|        x1="1322.5831" | ||||
|        y1="-312.51855" | ||||
|        x2="1322.5831" | ||||
|        y2="-306.53461" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -89,13 +99,13 @@ | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="33.380898" | ||||
|      inkscape:cy="6.9658271" | ||||
|      inkscape:cx="-26.798898" | ||||
|      inkscape:cy="5.3753009" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
| @@ -152,7 +162,7 @@ | ||||
|            height="25" | ||||
|            width="98" | ||||
|            id="rect38000" | ||||
|            style="color:#000000;fill:url(#linearGradient77551);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;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;enable-background:accumulate" /> | ||||
|            style="color:#000000;fill:url(#linearGradient11198);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;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;enable-background:accumulate;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="translate(2.0625,-2)" | ||||
|   | ||||
| Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.7 KiB | 
| @@ -113,7 +113,7 @@ expand_content_files= | ||||
| # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) | ||||
| # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) | ||||
| GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS) | ||||
| GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la | ||||
| GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la -rpath $(MUTTER_TYPELIB_DIR) | ||||
|  | ||||
| # This includes the standard gtk-doc make rules, copied by gtkdocize. | ||||
| include $(top_srcdir)/gtk-doc.make | ||||
| @@ -125,7 +125,7 @@ EXTRA_DIST += | ||||
| # Files not to distribute | ||||
| # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types | ||||
| # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt | ||||
| DISTCLEANFILES = $(DOC_MODULES).types | ||||
| DISTCLEANFILES = $(DOC_MODULE).types | ||||
|  | ||||
| # Comment this out if you want 'make check' to test you doc status | ||||
| # and run some sanity checks | ||||
|   | ||||
| @@ -50,7 +50,6 @@ | ||||
|     <xi:include href="xml/shell-util.xml"/> | ||||
|     <xi:include href="xml/shell-mount-operation.xml"/> | ||||
|     <xi:include href="xml/shell-polkit-authentication-agent.xml"/> | ||||
|     <xi:include href="xml/shell-tp-client.xml"/> | ||||
|   </chapter> | ||||
|   <chapter id="object-tree"> | ||||
|     <title>Object Hierarchy</title> | ||||
|   | ||||
| @@ -78,7 +78,7 @@ expand_content_files= | ||||
| # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) | ||||
| # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) | ||||
| GTKDOC_CFLAGS= | ||||
| GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la | ||||
| GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la -rpath $(MUTTER_TYPELIB_DIR) | ||||
|  | ||||
| # This includes the standard gtk-doc make rules, copied by gtkdocize. | ||||
| include $(top_srcdir)/gtk-doc.make | ||||
|   | ||||
| @@ -35,7 +35,6 @@ | ||||
|       <xi:include href="xml/st-bin.xml"/> | ||||
|       <xi:include href="xml/st-box-layout.xml"/> | ||||
|       <xi:include href="xml/st-scroll-view.xml"/> | ||||
|       <xi:include href="xml/st-table.xml"/> | ||||
|     </chapter> | ||||
|  | ||||
|     <chapter id="styling"> | ||||
|   | ||||
| @@ -11,6 +11,7 @@ misc/config.js: misc/config.js.in Makefile | ||||
| 	    -e "s|[@]datadir@|$(datadir)|g" \ | ||||
| 	    -e "s|[@]libexecdir@|$(libexecdir)|g" \ | ||||
| 	    -e "s|[@]sysconfdir@|$(sysconfdir)|g" \ | ||||
| 	    -e "s|[@]LIBMUTTER_API_VERSION@|$(LIBMUTTER_API_VERSION)|g" \ | ||||
|                $< > $@ | ||||
|  | ||||
| js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate-dependencies $(srcdir)/js-resources.gresource.xml) | ||||
|   | ||||
| @@ -5,6 +5,7 @@ const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Gdk = imports.gi.Gdk; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Format = imports.format; | ||||
|  | ||||
| @@ -23,11 +24,6 @@ const GnomeShellIface = '<node> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const customCss = '.prefs-button { \ | ||||
|                        padding: 8px; \ | ||||
|                        border-radius: 20px; \ | ||||
|                    }'; | ||||
|  | ||||
| const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface); | ||||
|  | ||||
| function stripPrefix(string, prefix) { | ||||
| @@ -97,9 +93,11 @@ const Application = new Lang.Class({ | ||||
|             widget = this._buildErrorUI(extension, e); | ||||
|         } | ||||
|  | ||||
|         let dialog = new Gtk.Dialog({ use_header_bar: true, | ||||
|                                       modal: true, | ||||
|                                       title: extension.metadata.name }); | ||||
|         let dialog = new Gtk.Window({ modal: !this._skipMainWindow, | ||||
|                                       type_hint: Gdk.WindowTypeHint.DIALOG }); | ||||
|         dialog.set_titlebar(new Gtk.HeaderBar({ show_close_button: true, | ||||
|                                                 title: extension.metadata.name, | ||||
|                                                 visible: true })); | ||||
|  | ||||
|         if (this._skipMainWindow) { | ||||
|             this.application.add_window(dialog); | ||||
| @@ -112,7 +110,7 @@ const Application = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         dialog.set_default_size(600, 400); | ||||
|         dialog.get_content_area().add(widget); | ||||
|         dialog.add(widget); | ||||
|         dialog.show(); | ||||
|     }, | ||||
|  | ||||
| @@ -148,16 +146,21 @@ const Application = new Lang.Class({ | ||||
|         this._window = new Gtk.ApplicationWindow({ application: app, | ||||
|                                                    window_position: Gtk.WindowPosition.CENTER }); | ||||
|  | ||||
|         this._window.set_size_request(800, 500); | ||||
|         this._window.set_default_size(800, 500); | ||||
|  | ||||
|         this._titlebar = new Gtk.HeaderBar({ show_close_button: true, | ||||
|                                              title: _("GNOME Shell Extensions") }); | ||||
|                                              title: _("Shell Extensions") }); | ||||
|         this._window.set_titlebar(this._titlebar); | ||||
|  | ||||
|         let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                               shadow_type: Gtk.ShadowType.IN, | ||||
|                                               halign: Gtk.Align.CENTER, | ||||
|                                               margin: 18 }); | ||||
|         let killSwitch = new Gtk.Switch({ valign: Gtk.Align.CENTER }); | ||||
|         this._titlebar.pack_end(killSwitch); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|         this._settings.bind('disable-user-extensions', killSwitch, 'active', | ||||
|                             Gio.SettingsBindFlags.DEFAULT | | ||||
|                             Gio.SettingsBindFlags.INVERT_BOOLEAN); | ||||
|  | ||||
|         let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER }); | ||||
|         this._window.add(scroll); | ||||
|  | ||||
|         this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE }); | ||||
| @@ -176,21 +179,6 @@ const Application = new Lang.Class({ | ||||
|         this._window.show_all(); | ||||
|     }, | ||||
|  | ||||
|     _addCustomStyle: function() { | ||||
|         let provider = new Gtk.CssProvider(); | ||||
|  | ||||
|         try { | ||||
|             provider.load_from_data(customCss, -1); | ||||
|         } catch(e) { | ||||
|             log('Failed to add application style'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let screen = this._window.window.get_screen(); | ||||
|         let priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION; | ||||
|         Gtk.StyleContext.add_provider_for_screen(screen, provider, priority); | ||||
|     }, | ||||
|  | ||||
|     _sortList: function(row1, row2) { | ||||
|         let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name; | ||||
|         let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name; | ||||
| @@ -239,7 +227,6 @@ const Application = new Lang.Class({ | ||||
|  | ||||
|     _onStartup: function(app) { | ||||
|         this._buildUI(app); | ||||
|         this._addCustomStyle(); | ||||
|         this._scanExtensions(); | ||||
|     }, | ||||
|  | ||||
| @@ -266,6 +253,18 @@ const Application = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const DescriptionLabel = new Lang.Class({ | ||||
|     Name: 'DescriptionLabel', | ||||
|     Extends: Gtk.Label, | ||||
|  | ||||
|     vfunc_get_preferred_height_for_width: function(width) { | ||||
|         // Hack: Request the maximum height allowed by the line limit | ||||
|         if (this.lines > 0) | ||||
|             return this.parent(0); | ||||
|         return this.parent(width); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ExtensionRow = new Lang.Class({ | ||||
|     Name: 'ExtensionRow', | ||||
|     Extends: Gtk.ListBoxRow, | ||||
| @@ -284,6 +283,10 @@ const ExtensionRow = new Lang.Class({ | ||||
|             Lang.bind(this, function() { | ||||
|                 this._switch.sensitive = this._canEnable(); | ||||
|             })); | ||||
|         this._settings.connect('changed::disable-user-extensions', | ||||
|             Lang.bind(this, function() { | ||||
|                 this._switch.sensitive = this._canEnable(); | ||||
|             })); | ||||
|  | ||||
|         this._buildUI(); | ||||
|     }, | ||||
| @@ -292,7 +295,8 @@ const ExtensionRow = new Lang.Class({ | ||||
|         let extension = ExtensionUtils.extensions[this.uuid]; | ||||
|  | ||||
|         let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, | ||||
|                                  hexpand: true, margin: 12, spacing: 6 }); | ||||
|                                  hexpand: true, margin_end: 24, spacing: 24, | ||||
|                                  margin: 12 }); | ||||
|         this.add(hbox); | ||||
|  | ||||
|         let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, | ||||
| @@ -306,9 +310,9 @@ const ExtensionRow = new Lang.Class({ | ||||
|         vbox.add(label); | ||||
|  | ||||
|         let desc = extension.metadata.description.split('\n')[0]; | ||||
|         label = new Gtk.Label({ label: desc, | ||||
|                                 ellipsize: Pango.EllipsizeMode.END, | ||||
|                                 halign: Gtk.Align.START }); | ||||
|         label = new DescriptionLabel({ label: desc, wrap: true, lines: 2, | ||||
|                                        ellipsize: Pango.EllipsizeMode.END, | ||||
|                                        xalign: 0, yalign: 0 }); | ||||
|         vbox.add(label); | ||||
|  | ||||
|         let button = new Gtk.Button({ valign: Gtk.Align.CENTER, | ||||
| @@ -316,7 +320,7 @@ const ExtensionRow = new Lang.Class({ | ||||
|         button.add(new Gtk.Image({ icon_name: 'emblem-system-symbolic', | ||||
|                                    icon_size: Gtk.IconSize.BUTTON, | ||||
|                                    visible: true })); | ||||
|         button.get_style_context().add_class('prefs-button'); | ||||
|         button.get_style_context().add_class('circular'); | ||||
|         hbox.add(button); | ||||
|  | ||||
|         this.prefsButton = button; | ||||
| @@ -339,7 +343,8 @@ const ExtensionRow = new Lang.Class({ | ||||
|         let extension = ExtensionUtils.extensions[this.uuid]; | ||||
|         let checkVersion = !this._settings.get_boolean('disable-extension-version-validation'); | ||||
|  | ||||
|         return !(checkVersion && ExtensionUtils.isOutOfDate(extension)); | ||||
|         return !this._settings.get_boolean('disable-user-extensions') && | ||||
|                !(checkVersion && ExtensionUtils.isOutOfDate(extension)); | ||||
|     }, | ||||
|  | ||||
|     _isEnabled: function() { | ||||
|   | ||||
| @@ -13,8 +13,9 @@ const Params = imports.misc.params; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
| const Pango = imports.gi.Pango; | ||||
|  | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 24; | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 16; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| @@ -113,6 +114,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this._message = new St.Label({ opacity: 0, | ||||
|                                        styleClass: 'login-dialog-message' }); | ||||
|         this._message.clutter_text.line_wrap = true; | ||||
|         this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START }); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', | ||||
| @@ -189,7 +191,8 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                              this._updateNextButtonSensitivity(this._entry.text.length > 0); | ||||
|                                          })); | ||||
|         this._entry.clutter_text.connect('activate', Lang.bind(this, function() { | ||||
|             this.emit('next'); | ||||
|             if (this.nextButton.reactive) | ||||
|                 this.emit('next'); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -258,6 +261,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onVerificationComplete: function() { | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED; | ||||
| 	this.cancelButton.reactive = false; | ||||
|     }, | ||||
| @@ -281,6 +285,12 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (oldActor) | ||||
|             Tweener.removeTweens(oldActor); | ||||
|  | ||||
|         let wasSpinner; | ||||
|         if (oldActor == this._spinner.actor) | ||||
|             wasSpinner = true; | ||||
|         else | ||||
|             wasSpinner = false; | ||||
|  | ||||
|         let isSpinner; | ||||
|         if (actor == this._spinner.actor) | ||||
|             isSpinner = true; | ||||
| @@ -290,6 +300,11 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (this._defaultButtonWellActor != actor && oldActor) { | ||||
|             if (!animate) { | ||||
|                 oldActor.opacity = 0; | ||||
|  | ||||
|                 if (wasSpinner) { | ||||
|                     if (this._spinner) | ||||
|                         this._spinner.stop(); | ||||
|                 } | ||||
|             } else { | ||||
|                 Tweener.addTween(oldActor, | ||||
|                                  { opacity: 0, | ||||
| @@ -298,7 +313,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                    transition: 'linear', | ||||
|                                    onCompleteScope: this, | ||||
|                                    onComplete: function() { | ||||
|                                       if (isSpinner) { | ||||
|                                       if (wasSpinner) { | ||||
|                                           if (this._spinner) | ||||
|                                               this._spinner.stop(); | ||||
|                                       } | ||||
| @@ -401,7 +416,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     updateSensitivity: function(sensitive) { | ||||
|         this._updateNextButtonSensitivity(sensitive); | ||||
|         this._updateNextButtonSensitivity(sensitive && this._entry.text.length > 0); | ||||
|         this._entry.reactive = sensitive; | ||||
|         this._entry.clutter_text.editable = sensitive; | ||||
|     }, | ||||
| @@ -432,8 +447,9 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let oldStatus = this.verificationStatus; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|         this.cancelButton.reactive = true; | ||||
|         this.nextButton.label = _("Next"); | ||||
|  | ||||
|         if (oldStatus == AuthPromptStatus.VERIFYING) | ||||
|         if (this._userVerifier) | ||||
|             this._userVerifier.cancel(); | ||||
|  | ||||
|         this._queryingService = null; | ||||
| @@ -488,6 +504,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|     finish: function(onComplete) { | ||||
|         if (!this._userVerifier.hasPendingMessages) { | ||||
|             this._userVerifier.clear(); | ||||
|             onComplete(); | ||||
|             return; | ||||
|         } | ||||
| @@ -495,12 +512,13 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let signalId = this._userVerifier.connect('no-more-messages', | ||||
|                                                   Lang.bind(this, function() { | ||||
|                                                       this._userVerifier.disconnect(signalId); | ||||
|                                                       this._userVerifier.clear(); | ||||
|                                                       onComplete(); | ||||
|                                                   })); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this.verificationStatus == AuthPromptStatus.NOT_VERIFYING || this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|         if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|             return; | ||||
|         } | ||||
|         this.reset(); | ||||
|   | ||||
| @@ -16,6 +16,34 @@ | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * In order for transformation animations to look good, they need to be | ||||
|  * incremental and have some order to them (e.g., fade out hidden items, | ||||
|  * then shrink to close the void left over). Chaining animations in this way can | ||||
|  * be error-prone and wordy using just Tweener callbacks. | ||||
|  * | ||||
|  * The classes in this file help with this: | ||||
|  * | ||||
|  * - Task.  encapsulates schedulable work to be run in a specific scope. | ||||
|  * | ||||
|  * - ConsecutiveBatch.  runs a series of tasks in order and completes | ||||
|  *                      when the last in the series finishes. | ||||
|  * | ||||
|  * - ConcurrentBatch.  runs a set of tasks at the same time and completes | ||||
|  *                     when the last to finish completes. | ||||
|  * | ||||
|  * - Hold.  prevents a batch from completing the pending task until | ||||
|  *          the hold is released. | ||||
|  * | ||||
|  * The tasks associated with a batch are specified in a list at batch | ||||
|  * construction time as either task objects or plain functions. | ||||
|  * Batches are task objects, themselves, so they can be nested. | ||||
|  * | ||||
|  * These classes aren't specific to GDM, but were found to be unintuitive and so | ||||
|  * are not used elsewhere. These APIs may ultimately get dropped entirely and | ||||
|  * replaced by something else. | ||||
|  */ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
|   | ||||
| @@ -23,6 +23,12 @@ function FprintManager() { | ||||
|                                    g_object_path: '/net/reactivated/Fprint/Manager', | ||||
|                                    g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) }); | ||||
|  | ||||
|     self.init(null); | ||||
|     try { | ||||
|         self.init(null); | ||||
|     } catch(e) { | ||||
|         log('Failed to connect to Fprint service: ' + e.message); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     return self; | ||||
| } | ||||
|   | ||||
| @@ -96,7 +96,7 @@ const UserListItem = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._user.disconnect(this._userChangedId); | ||||
|         this.user.disconnect(this._userChangedId); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
| @@ -212,6 +212,10 @@ const UserList = new Lang.Class({ | ||||
|         return item; | ||||
|     }, | ||||
|  | ||||
|     containsUser: function(user) { | ||||
|         return this._items[user.get_user_name()] != null; | ||||
|     }, | ||||
|  | ||||
|     addUser: function(user) { | ||||
|         if (!user.is_loaded) | ||||
|             return; | ||||
| @@ -535,6 +539,9 @@ const LoginDialog = new Lang.Class({ | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|         let centerY = dialogBox.y1 + (dialogBox.y2 - dialogBox.y1) / 2; | ||||
|  | ||||
|         natWidth = Math.min(natWidth, dialogBox.x2 - dialogBox.x1); | ||||
|         natHeight = Math.min(natHeight, dialogBox.y2 - dialogBox.y1); | ||||
|  | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = Math.floor(centerY - natHeight / 2); | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
| @@ -584,7 +591,14 @@ const LoginDialog = new Lang.Class({ | ||||
|         // try a different layout, or if we have what extra space we | ||||
|         // can hand out | ||||
|         if (bannerAllocation) { | ||||
|             let leftOverYSpace = dialogHeight - bannerHeight - authPromptHeight - logoHeight; | ||||
|             let bannerSpace; | ||||
|  | ||||
|             if (authPromptAllocation) | ||||
|                 bannerSpace = authPromptAllocation.y1 - bannerAllocation.y1; | ||||
|             else | ||||
|                 bannerSpace = 0; | ||||
|  | ||||
|             let leftOverYSpace = bannerSpace - bannerHeight; | ||||
|  | ||||
|             if (leftOverYSpace > 0) { | ||||
|                  // First figure out how much left over space is up top | ||||
| @@ -790,6 +804,11 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|         this._user = null; | ||||
|  | ||||
|         if (this._nextSignalId) { | ||||
|             this._authPrompt.disconnect(this._nextSignalId); | ||||
|             this._nextSignalId = 0; | ||||
|         } | ||||
|  | ||||
|         if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { | ||||
|             if (!this._disableUserList) | ||||
|                 this._showUserList(); | ||||
| @@ -867,7 +886,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _loginScreenSessionActivated: function() { | ||||
|         if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED) | ||||
|         if (this.actor.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|             return; | ||||
|  | ||||
|         Tweener.addTween(this.actor, | ||||
| @@ -884,7 +903,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                this._authPrompt.reset(); | ||||
|                                if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|                                    this._authPrompt.reset(); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
| @@ -913,11 +933,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                let id = Mainloop.idle_add(Lang.bind(this, function() { | ||||
|                                    this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                                GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync'); | ||||
|                                this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
| @@ -1119,6 +1135,10 @@ const LoginDialog = new Lang.Class({ | ||||
|             this._userManager.disconnect(this._userRemovedId); | ||||
|             this._userRemovedId = 0; | ||||
|         } | ||||
|         if (this._userChangedId) { | ||||
|             this._userManager.disconnect(this._userChangedId); | ||||
|             this._userChangedId = 0; | ||||
|         } | ||||
|         this._textureCache.disconnect(this._updateLogoTextureId); | ||||
|         Main.layoutManager.disconnect(this._startupCompleteId); | ||||
|         if (this._settings) { | ||||
| @@ -1165,6 +1185,14 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                             this._userList.removeUser(user); | ||||
|                                                         })); | ||||
|  | ||||
|         this._userChangedId = this._userManager.connect('user-changed', | ||||
|                                                         Lang.bind(this, function(userManager, user) { | ||||
|                                                             if (this._userList.containsUser(user) && user.locked) | ||||
|                                                                 this._userList.removeUser(user); | ||||
|                                                             else if (!this._userList.containsUser(user) && !user.locked) | ||||
|                                                                 this._userList.addUser(user); | ||||
|                                                         })); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
| @@ -1197,7 +1225,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addCharacter: function(unichar) { | ||||
|         this._authPrompt.addCharacter(unichar); | ||||
|         // Don't allow type ahead at the login screen | ||||
|     }, | ||||
|  | ||||
|     finish: function(onComplete) { | ||||
|   | ||||
| @@ -128,18 +128,22 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|  | ||||
|         this._client = client; | ||||
|  | ||||
|         this._defaultService = null; | ||||
|         this._preemptingService = null; | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); | ||||
|         this._settings.connect('changed', | ||||
|                                Lang.bind(this, this._updateDefaultService)); | ||||
|         this._updateDefaultService(); | ||||
|  | ||||
|         this._fprintManager = new Fprint.FprintManager(); | ||||
|         this._fprintManager = Fprint.FprintManager(); | ||||
|         this._smartcardManager = SmartcardManager.getSmartcardManager(); | ||||
|  | ||||
|         // We check for smartcards right away, since an inserted smartcard | ||||
|         // at startup should result in immediately initiating authentication. | ||||
|         // This is different than fingeprint readers, where we only check them | ||||
|         // after a user has been picked. | ||||
|         this.smartcardDetected = false; | ||||
|         this._checkForSmartcard(); | ||||
|  | ||||
|         this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted', | ||||
| @@ -293,7 +297,8 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     _checkForFingerprintReader: function() { | ||||
|         this._haveFingerprintReader = false; | ||||
|  | ||||
|         if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) { | ||||
|         if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) || | ||||
|             this._fprintManager == null) { | ||||
|             this._updateDefaultService(); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -23,18 +23,22 @@ | ||||
|     <file>misc/modemManager.js</file> | ||||
|     <file>misc/objectManager.js</file> | ||||
|     <file>misc/params.js</file> | ||||
|     <file>misc/permissionStore.js</file> | ||||
|     <file>misc/smartcardManager.js</file> | ||||
|     <file>misc/util.js</file> | ||||
|     <file>misc/weather.js</file> | ||||
|  | ||||
|     <file>perf/core.js</file> | ||||
|     <file>perf/hwtest.js</file> | ||||
|  | ||||
|     <file>portalHelper/main.js</file> | ||||
|  | ||||
|     <file>ui/accessDialog.js</file> | ||||
|     <file>ui/altTab.js</file> | ||||
|     <file>ui/animation.js</file> | ||||
|     <file>ui/appDisplay.js</file> | ||||
|     <file>ui/appFavorites.js</file> | ||||
|     <file>ui/audioDeviceSelection.js</file> | ||||
|     <file>ui/backgroundMenu.js</file> | ||||
|     <file>ui/background.js</file> | ||||
|     <file>ui/boxpointer.js</file> | ||||
| @@ -62,12 +66,15 @@ | ||||
|     <file>ui/magnifierDBus.js</file> | ||||
|     <file>ui/main.js</file> | ||||
|     <file>ui/messageTray.js</file> | ||||
|     <file>ui/messageList.js</file> | ||||
|     <file>ui/modalDialog.js</file> | ||||
|     <file>ui/mpris.js</file> | ||||
|     <file>ui/notificationDaemon.js</file> | ||||
|     <file>ui/osdWindow.js</file> | ||||
|     <file>ui/osdMonitorLabeler.js</file> | ||||
|     <file>ui/overview.js</file> | ||||
|     <file>ui/overviewControls.js</file> | ||||
|     <file>ui/padOsd.js</file> | ||||
|     <file>ui/panel.js</file> | ||||
|     <file>ui/panelMenu.js</file> | ||||
|     <file>ui/pointerWatcher.js</file> | ||||
| @@ -112,6 +119,7 @@ | ||||
|     <file>ui/status/brightness.js</file> | ||||
|     <file>ui/status/location.js</file> | ||||
|     <file>ui/status/keyboard.js</file> | ||||
|     <file>ui/status/nightLight.js</file> | ||||
|     <file>ui/status/network.js</file> | ||||
|     <file>ui/status/power.js</file> | ||||
|     <file>ui/status/rfkill.js</file> | ||||
|   | ||||
| @@ -15,3 +15,5 @@ const LOCALEDIR = '@datadir@/locale'; | ||||
| /* other standard directories */ | ||||
| const LIBEXECDIR = '@libexecdir@'; | ||||
| const SYSCONFDIR = '@sysconfdir@'; | ||||
| /* g-i package versions */ | ||||
| const LIBMUTTER_API_VERSION = '@LIBMUTTER_API_VERSION@' | ||||
|   | ||||
| @@ -6,9 +6,7 @@ | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const ShellJS = imports.gi.ShellJS; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const FileUtils = imports.misc.fileUtils; | ||||
| @@ -21,14 +19,25 @@ const ExtensionType = { | ||||
| // Maps uuid -> metadata object | ||||
| const extensions = {}; | ||||
|  | ||||
| /** | ||||
|  * getCurrentExtension: | ||||
|  * | ||||
|  * Returns the current extension, or null if not called from an extension. | ||||
|  */ | ||||
| function getCurrentExtension() { | ||||
|     let stack = (new Error()).stack; | ||||
|     let stack = (new Error()).stack.split('\n'); | ||||
|     let extensionStackLine; | ||||
|  | ||||
|     // Assuming we're importing this directly from an extension (and we shouldn't | ||||
|     // ever not be), its UUID should be directly in the path here. | ||||
|     let extensionStackLine = stack.split('\n')[1]; | ||||
|     // Search for an occurrence of an extension stack frame | ||||
|     // Start at 1 because 0 is the stack frame of this function | ||||
|     for (let i = 1; i < stack.length; i++) { | ||||
|         if (stack[i].indexOf('/gnome-shell/extensions/') > -1) { | ||||
|             extensionStackLine = stack[i]; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (!extensionStackLine) | ||||
|         throw new Error('Could not find current extension'); | ||||
|         return null; | ||||
|  | ||||
|     // The stack line is like: | ||||
|     //   init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8 | ||||
| @@ -38,7 +47,7 @@ function getCurrentExtension() { | ||||
|     //   @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8 | ||||
|     let match = new RegExp('@(.+):\\d+').exec(extensionStackLine); | ||||
|     if (!match) | ||||
|         throw new Error('Could not find current extension'); | ||||
|         return null; | ||||
|  | ||||
|     let path = match[1]; | ||||
|     let file = Gio.File.new_for_path(path); | ||||
| @@ -52,7 +61,7 @@ function getCurrentExtension() { | ||||
|         file = file.get_parent(); | ||||
|     } | ||||
|  | ||||
|     throw new Error('Could not find current extension'); | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -140,12 +149,13 @@ function createExtensionObject(uuid, dir, type) { | ||||
|     return extension; | ||||
| } | ||||
|  | ||||
| var _extension = null; | ||||
|  | ||||
| function installImporter(extension) { | ||||
|     _extension = extension; | ||||
|     ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path); | ||||
|     _extension = null; | ||||
|     let oldSearchPath = imports.searchPath.slice();  // make a copy | ||||
|     imports.searchPath = [extension.dir.get_parent().get_path()]; | ||||
|     // importing a "subdir" creates a new importer object that doesn't affect | ||||
|     // the global one | ||||
|     extension.imports = imports[extension.uuid]; | ||||
|     imports.searchPath = oldSearchPath; | ||||
| } | ||||
|  | ||||
| const ExtensionFinder = new Lang.Class({ | ||||
|   | ||||
| @@ -69,7 +69,7 @@ const HistoryManager = new Lang.Class({ | ||||
|             this._indexChanged(); | ||||
|         } | ||||
|  | ||||
|         return this._historyIndex[this._history.length]; | ||||
|         return this._historyIndex ? this._history[this._historyIndex -1] : null; | ||||
|     }, | ||||
|  | ||||
|     addItem: function(input) { | ||||
|   | ||||
| @@ -6,10 +6,11 @@ const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| let IBusCandidatePopup; | ||||
| try { | ||||
|     var IBus = imports.gi.IBus; | ||||
|     _checkIBusVersion(1, 5, 2); | ||||
|     const IBusCandidatePopup = imports.ui.ibusCandidatePopup; | ||||
|     IBusCandidatePopup = imports.ui.ibusCandidatePopup; | ||||
| } catch (e) { | ||||
|     var IBus = null; | ||||
|     log(e); | ||||
| @@ -189,7 +190,7 @@ const IBusManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     getEngineDesc: function(id) { | ||||
|         if (!IBus || !this._ready) | ||||
|         if (!IBus || !this._ready || !this._engines.hasOwnProperty(id)) | ||||
|             return null; | ||||
|  | ||||
|         return this._engines[id]; | ||||
|   | ||||
| @@ -128,7 +128,8 @@ const KeyboardManager = new Lang.Class({ | ||||
|         if (!found) | ||||
|             [, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE); | ||||
|  | ||||
|         let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id); | ||||
|         let _layout, _variant; | ||||
|         [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id); | ||||
|         if (found) | ||||
|             return { layout: _layout, variant: _variant }; | ||||
|         else | ||||
|   | ||||
| @@ -40,6 +40,9 @@ const SystemdLoginSessionIface = '<node> \ | ||||
| <signal name="Lock" /> \ | ||||
| <signal name="Unlock" /> \ | ||||
| <property name="Active" type="b" access="read" /> \ | ||||
| <method name="SetLockedHint"> \ | ||||
|     <arg type="b" direction="in"/> \ | ||||
| </method> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| @@ -131,10 +134,13 @@ const LoginManagerSystemd = new Lang.Class({ | ||||
|  | ||||
|     canSuspend: function(asyncCallback) { | ||||
|         this._proxy.CanSuspendRemote(function(result, error) { | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|             if (error) { | ||||
|                 asyncCallback(false, false); | ||||
|             } else { | ||||
|                 let needsAuth = result[0] == 'challenge'; | ||||
|                 let canSuspend = needsAuth || result[0] == 'yes'; | ||||
|                 asyncCallback(canSuspend, needsAuth); | ||||
|             } | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
| @@ -187,7 +193,7 @@ const LoginManagerDummy = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     canSuspend: function(asyncCallback) { | ||||
|         asyncCallback(false); | ||||
|         asyncCallback(false, false); | ||||
|     }, | ||||
|  | ||||
|     listSessions: function(asyncCallback) { | ||||
|   | ||||
							
								
								
									
										37
									
								
								js/misc/permissionStore.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
|  | ||||
| const PermissionStoreIface = '<node> \ | ||||
|   <interface name="org.freedesktop.impl.portal.PermissionStore"> \ | ||||
|     <method name="Lookup"> \ | ||||
|       <arg name="table" type="s" direction="in"/> \ | ||||
|       <arg name="id" type="s" direction="in"/> \ | ||||
|       <arg name="permissions" type="a{sas}" direction="out"/> \ | ||||
|       <arg name="data" type="v" direction="out"/> \ | ||||
|     </method> \ | ||||
|     <method name="Set"> \ | ||||
|       <arg name="table" type="s" direction="in"/> \ | ||||
|       <arg name="create" type="b" direction="in"/> \ | ||||
|       <arg name="id" type="s" direction="in"/> \ | ||||
|       <arg name="app_permissions" type="a{sas}" direction="in"/> \ | ||||
|       <arg name="data" type="v" direction="in"/> \ | ||||
|     </method> \ | ||||
|     <signal name="Changed"> \ | ||||
|       <arg name="table" type="s" direction="out"/> \ | ||||
|       <arg name="id" type="s" direction="out"/> \ | ||||
|       <arg name="deleted" type="b" direction="out"/> \ | ||||
|       <arg name="data" type="v" direction="out"/> \ | ||||
|       <arg name="permissions" type="a{sas}" direction="out"/> \ | ||||
|     </signal> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const PermissionStoreProxy = Gio.DBusProxy.makeProxyWrapper(PermissionStoreIface); | ||||
|  | ||||
| function PermissionStore(initCallback, cancellable) { | ||||
|     return new PermissionStoreProxy(Gio.DBus.session, | ||||
|                                     'org.freedesktop.impl.portal.PermissionStore', | ||||
|                                     '/org/freedesktop/impl/portal/PermissionStore', | ||||
|                                     initCallback, cancellable); | ||||
| }; | ||||
							
								
								
									
										136
									
								
								js/misc/util.js
									
									
									
									
									
								
							
							
						
						| @@ -1,9 +1,12 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gettext = imports.gettext; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Signals = imports.signals; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| @@ -94,7 +97,7 @@ function spawnApp(argv) { | ||||
|                                                       Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION); | ||||
|  | ||||
|         let context = global.create_app_launch_context(0, -1); | ||||
|         app.launch([], context); | ||||
|         app.launch([], context, false); | ||||
|     } catch(err) { | ||||
|         _handleSpawnError(argv[0], err); | ||||
|     } | ||||
| @@ -161,6 +164,41 @@ function _handleSpawnError(command, err) { | ||||
|     Main.notifyError(title, err.message); | ||||
| } | ||||
|  | ||||
| function formatTimeSpan(date) { | ||||
|     let now = GLib.DateTime.new_now_local(); | ||||
|  | ||||
|     let timespan = now.difference(date); | ||||
|  | ||||
|     let minutesAgo = timespan / GLib.TIME_SPAN_MINUTE; | ||||
|     let hoursAgo = timespan / GLib.TIME_SPAN_HOUR; | ||||
|     let daysAgo = timespan / GLib.TIME_SPAN_DAY; | ||||
|     let weeksAgo = daysAgo / 7; | ||||
|     let monthsAgo = daysAgo / 30; | ||||
|     let yearsAgo = weeksAgo / 52; | ||||
|  | ||||
|     if (minutesAgo < 5) | ||||
|         return _("Just now"); | ||||
|     if (hoursAgo < 1) | ||||
|         return Gettext.ngettext("%d minute ago", | ||||
|                                 "%d minutes ago", minutesAgo).format(minutesAgo); | ||||
|     if (daysAgo < 1) | ||||
|         return Gettext.ngettext("%d hour ago", | ||||
|                                 "%d hours ago", hoursAgo).format(hoursAgo); | ||||
|     if (daysAgo < 2) | ||||
|         return _("Yesterday"); | ||||
|     if (daysAgo < 15) | ||||
|         return Gettext.ngettext("%d day ago", | ||||
|                                 "%d days ago", daysAgo).format(daysAgo); | ||||
|     if (weeksAgo < 8) | ||||
|         return Gettext.ngettext("%d week ago", | ||||
|                                 "%d weeks ago", weeksAgo).format(weeksAgo); | ||||
|     if (yearsAgo < 1) | ||||
|         return Gettext.ngettext("%d month ago", | ||||
|                                 "%d months ago", monthsAgo).format(monthsAgo); | ||||
|     return Gettext.ngettext("%d year ago", | ||||
|                             "%d years ago", yearsAgo).format(yearsAgo); | ||||
| } | ||||
|  | ||||
| function formatTime(time, params) { | ||||
|     let date; | ||||
|     // HACK: The built-in Date type sucks at timezones, which we need for the | ||||
| @@ -243,7 +281,10 @@ function formatTime(time, params) { | ||||
|             // xgettext:no-c-format | ||||
|             format = N_("%B %d %Y, %l\u2236%M %p"); | ||||
|     } | ||||
|     return date.format(Shell.util_translate_time_string(format)); | ||||
|  | ||||
|     let formattedTime = date.format(Shell.util_translate_time_string(format)); | ||||
|     // prepend LTR-mark to colon/ratio to force a text direction on times | ||||
|     return formattedTime.replace(/([:\u2236])/g, '\u200e$1'); | ||||
| } | ||||
|  | ||||
| function createTimeLabel(date, params) { | ||||
| @@ -398,3 +439,94 @@ function ensureActorVisibleInScrollView(scrollView, actor) { | ||||
|                        time: SCROLL_TIME, | ||||
|                        transition: 'easeOutQuad' }); | ||||
| } | ||||
|  | ||||
| const AppSettingsMonitor = new Lang.Class({ | ||||
|     Name: 'AppSettingsMonitor', | ||||
|  | ||||
|     _init: function(appId, schemaId) { | ||||
|         this._appId = appId; | ||||
|         this._schemaId = schemaId; | ||||
|  | ||||
|         this._app = null; | ||||
|         this._settings = null; | ||||
|         this._handlers = []; | ||||
|  | ||||
|         this._schemaSource = Gio.SettingsSchemaSource.get_default(); | ||||
|  | ||||
|         this._appSystem = Shell.AppSystem.get_default(); | ||||
|         this._appSystem.connect('installed-changed', | ||||
|                                 Lang.bind(this, this._onInstalledChanged)); | ||||
|         this._onInstalledChanged(); | ||||
|     }, | ||||
|  | ||||
|     get available() { | ||||
|         return this._app != null && this._settings != null; | ||||
|     }, | ||||
|  | ||||
|     activateApp: function() { | ||||
|         if (this._app) | ||||
|             this._app.activate(); | ||||
|     }, | ||||
|  | ||||
|     watchSetting: function(key, callback) { | ||||
|         let handler = { id: 0, key: key, callback: callback }; | ||||
|         this._handlers.push(handler); | ||||
|  | ||||
|         this._connectHandler(handler); | ||||
|     }, | ||||
|  | ||||
|     _connectHandler: function(handler) { | ||||
|         if (!this._settings || handler.id > 0) | ||||
|             return; | ||||
|  | ||||
|         handler.id = this._settings.connect('changed::' + handler.key, | ||||
|                                             handler.callback); | ||||
|         handler.callback(this._settings, handler.key); | ||||
|     }, | ||||
|  | ||||
|     _disconnectHandler: function(handler) { | ||||
|         if (this._settings && handler.id > 0) | ||||
|             this._settings.disconnect(handler.id); | ||||
|         handler.id = 0; | ||||
|     }, | ||||
|  | ||||
|     _onInstalledChanged: function() { | ||||
|         let hadApp = (this._app != null); | ||||
|         this._app = this._appSystem.lookup_app(this._appId); | ||||
|         let haveApp = (this._app != null); | ||||
|  | ||||
|         if (hadApp == haveApp) | ||||
|             return; | ||||
|  | ||||
|         if (haveApp) | ||||
|             this._checkSettings(); | ||||
|         else | ||||
|             this._setSettings(null); | ||||
|     }, | ||||
|  | ||||
|     _setSettings: function(settings) { | ||||
|         this._handlers.forEach((handler) => { this._disconnectHandler(handler); }); | ||||
|  | ||||
|         let hadSettings = (this._settings != null); | ||||
|         this._settings = settings; | ||||
|         let haveSettings = (this._settings != null); | ||||
|  | ||||
|         this._handlers.forEach((handler) => { this._connectHandler(handler); }); | ||||
|  | ||||
|         if (hadSettings != haveSettings) | ||||
|             this.emit('available-changed'); | ||||
|     }, | ||||
|  | ||||
|     _checkSettings: function() { | ||||
|         let schema = this._schemaSource.lookup(this._schemaId, true); | ||||
|         if (schema) { | ||||
|             this._setSettings(new Gio.Settings({ settings_schema: schema })); | ||||
|         } else if (this._app) { | ||||
|             Mainloop.timeout_add_seconds(1, () => { | ||||
|                 this._checkSettings(); | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(AppSettingsMonitor.prototype); | ||||
|   | ||||
							
								
								
									
										247
									
								
								js/misc/weather.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,247 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Geoclue = imports.gi.Geoclue; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GWeather = imports.gi.GWeather; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const PermissionStore = imports.misc.permissionStore; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| // Minimum time between updates to show loading indication | ||||
| const UPDATE_THRESHOLD = 10 * GLib.TIME_SPAN_MINUTE; | ||||
|  | ||||
| const WeatherClient = new Lang.Class({ | ||||
|     Name: 'WeatherClient', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._loading = false; | ||||
|         this._locationValid = false; | ||||
|         this._lastUpdate = GLib.DateTime.new_from_unix_local(0); | ||||
|  | ||||
|         this._autoLocationRequested = false; | ||||
|         this._mostRecentLocation = null; | ||||
|  | ||||
|         this._gclueService = null; | ||||
|         this._gclueStarted = false; | ||||
|         this._gclueStarting = false; | ||||
|         this._gclueLocationChangedId = 0; | ||||
|  | ||||
|         this._weatherAuthorized = false; | ||||
|         this._permStore = new PermissionStore.PermissionStore((proxy, error) => { | ||||
|             if (error) { | ||||
|                 log('Failed to connect to permissionStore: ' + error.message); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this._permStore.LookupRemote('gnome', 'geolocation', (res, error) => { | ||||
|                 if (error) | ||||
|                     log('Error looking up permission: ' + error.message); | ||||
|  | ||||
|                 let [perms, data] = error ? [{}, null] : res; | ||||
|                 let  params = ['gnome', 'geolocation', false, data, perms]; | ||||
|                 this._onPermStoreChanged(this._permStore, '', params); | ||||
|             }); | ||||
|         }); | ||||
|         this._permStore.connectSignal('Changed', | ||||
|                                       Lang.bind(this, this._onPermStoreChanged)); | ||||
|  | ||||
|         this._locationSettings = new Gio.Settings({ schema_id: 'org.gnome.system.location' }); | ||||
|         this._locationSettings.connect('changed::enabled', | ||||
|                                        Lang.bind(this, this._updateAutoLocation)); | ||||
|  | ||||
|         this._world = GWeather.Location.get_world(); | ||||
|  | ||||
|         this._providers = GWeather.Provider.METAR | | ||||
|                           GWeather.Provider.YR_NO | | ||||
|                           GWeather.Provider.OWM; | ||||
|  | ||||
|         this._weatherInfo = new GWeather.Info({ enabled_providers: 0 }); | ||||
|         this._weatherInfo.connect_after('updated', () => { | ||||
|             this._lastUpdate = GLib.DateTime.new_now_local(); | ||||
|             this.emit('changed'); | ||||
|         }); | ||||
|  | ||||
|         this._weatherAppMon = new Util.AppSettingsMonitor('org.gnome.Weather.Application.desktop', | ||||
|                                                           'org.gnome.Weather.Application'); | ||||
|         this._weatherAppMon.connect('available-changed', () => { this.emit('changed'); }); | ||||
|         this._weatherAppMon.watchSetting('automatic-location', | ||||
|                                          Lang.bind(this, this._onAutomaticLocationChanged)); | ||||
|         this._weatherAppMon.watchSetting('locations', | ||||
|                                          Lang.bind(this, this._onLocationsChanged)); | ||||
|     }, | ||||
|  | ||||
|     get available() { | ||||
|         return this._weatherAppMon.available; | ||||
|     }, | ||||
|  | ||||
|     get loading() { | ||||
|         return this._loading; | ||||
|     }, | ||||
|  | ||||
|     get hasLocation() { | ||||
|         return this._locationValid; | ||||
|     }, | ||||
|  | ||||
|     get info() { | ||||
|         return this._weatherInfo; | ||||
|     }, | ||||
|  | ||||
|     activateApp: function() { | ||||
|         this._weatherAppMon.activateApp(); | ||||
|     }, | ||||
|  | ||||
|     update: function() { | ||||
|         if (!this._locationValid) | ||||
|             return; | ||||
|  | ||||
|         let now = GLib.DateTime.new_now_local(); | ||||
|         // Update without loading indication if the current info is recent enough | ||||
|         if (this._weatherInfo.is_valid() && | ||||
|             now.difference(this._lastUpdate) < UPDATE_THRESHOLD) | ||||
|             this._weatherInfo.update(); | ||||
|         else | ||||
|             this._loadInfo(); | ||||
|     }, | ||||
|  | ||||
|     get _useAutoLocation() { | ||||
|         return this._autoLocationRequested && | ||||
|                this._locationSettings.get_boolean('enabled') && | ||||
|                this._weatherAuthorized; | ||||
|     }, | ||||
|  | ||||
|     _loadInfo: function() { | ||||
|         let id = this._weatherInfo.connect('updated', () => { | ||||
|             this._weatherInfo.disconnect(id); | ||||
|             this._loading = false; | ||||
|         }); | ||||
|  | ||||
|         this._loading = true; | ||||
|         this.emit('changed'); | ||||
|  | ||||
|         this._weatherInfo.update(); | ||||
|     }, | ||||
|  | ||||
|     _locationsEqual: function(loc1, loc2) { | ||||
|         if (loc1 == loc2) | ||||
|             return true; | ||||
|  | ||||
|         if (loc1 == null || loc2 == null) | ||||
|             return false; | ||||
|  | ||||
|         return loc1.equal(loc2); | ||||
|     }, | ||||
|  | ||||
|     _setLocation: function(location) { | ||||
|         if (this._locationsEqual(this._weatherInfo.location, location)) | ||||
|             return; | ||||
|  | ||||
|         this._weatherInfo.abort(); | ||||
|         this._weatherInfo.set_location(location); | ||||
|         this._locationValid = (location != null); | ||||
|  | ||||
|         this._weatherInfo.set_enabled_providers(location ? this._providers : 0); | ||||
|  | ||||
|         if (location) | ||||
|             this._loadInfo(); | ||||
|         else | ||||
|             this.emit('changed'); | ||||
|     }, | ||||
|  | ||||
|     _updateLocationMonitoring: function() { | ||||
|         if (this._useAutoLocation) { | ||||
|             if (this._gclueLocationChangedId != 0 || this._gclueService == null) | ||||
|                 return; | ||||
|  | ||||
|             this._gclueLocationChangedId = | ||||
|                 this._gclueService.connect('notify::location', | ||||
|                                            Lang.bind(this, this._onGClueLocationChanged)); | ||||
|             this._onGClueLocationChanged(); | ||||
|         } else { | ||||
|             if (this._gclueLocationChangedId) | ||||
|                 this._gclueService.disconnect(this._gclueLocationChangedId); | ||||
|             this._gclueLocationChangedId = 0; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _startGClueService: function() { | ||||
|         if (this._gclueStarting) | ||||
|             return; | ||||
|  | ||||
|         this._gclueStarting = true; | ||||
|  | ||||
|         Geoclue.Simple.new('org.gnome.Shell', Geoclue.AccuracyLevel.CITY, null, | ||||
|             (o, res) => { | ||||
|                 try { | ||||
|                     this._gclueService = Geoclue.Simple.new_finish(res); | ||||
|                 } catch(e) { | ||||
|                     log('Failed to connect to Geoclue2 service: ' + e.message); | ||||
|                     this._setLocation(this._mostRecentLocation); | ||||
|                     return; | ||||
|                 } | ||||
|                 this._gclueStarted = true; | ||||
|                 this._gclueService.get_client().distance_threshold = 100; | ||||
|                 this._updateLocationMonitoring(); | ||||
|             }); | ||||
|     }, | ||||
|  | ||||
|     _onGClueLocationChanged: function() { | ||||
|         let geoLocation = this._gclueService.location; | ||||
|         let location = GWeather.Location.new_detached(geoLocation.description, | ||||
|                                                       null, | ||||
|                                                       geoLocation.latitude, | ||||
|                                                       geoLocation.longitude); | ||||
|         this._setLocation(location); | ||||
|     }, | ||||
|  | ||||
|     _onAutomaticLocationChanged: function(settings, key) { | ||||
|         let useAutoLocation = settings.get_boolean(key); | ||||
|         if (this._autoLocationRequested == useAutoLocation) | ||||
|             return; | ||||
|  | ||||
|         this._autoLocationRequested = useAutoLocation; | ||||
|  | ||||
|         this._updateAutoLocation(); | ||||
|     }, | ||||
|  | ||||
|     _updateAutoLocation: function() { | ||||
|         this._updateLocationMonitoring(); | ||||
|  | ||||
|         if (this._useAutoLocation) | ||||
|             this._startGClueService(); | ||||
|         else | ||||
|             this._setLocation(this._mostRecentLocation); | ||||
|     }, | ||||
|  | ||||
|     _onLocationsChanged: function(settings, key) { | ||||
|         let serialized = settings.get_value(key).deep_unpack().shift(); | ||||
|         let mostRecentLocation = null; | ||||
|  | ||||
|         if (serialized) | ||||
|             mostRecentLocation = this._world.deserialize(serialized); | ||||
|  | ||||
|         if (this._locationsEqual(this._mostRecentLocation, mostRecentLocation)) | ||||
|             return; | ||||
|  | ||||
|         this._mostRecentLocation = mostRecentLocation; | ||||
|  | ||||
|         if (!this._useAutoLocation || !this._gclueStarted) | ||||
|             this._setLocation(this._mostRecentLocation); | ||||
|     }, | ||||
|  | ||||
|     _onPermStoreChanged: function(proxy, sender, params) { | ||||
|         let [table, id, deleted, data, perms] = params; | ||||
|  | ||||
|         if (table != 'gnome' || id != 'geolocation') | ||||
|             return; | ||||
|  | ||||
|         let permission = perms['org.gnome.Weather.Application'] || ['NONE']; | ||||
|         let [accuracy] = permission; | ||||
|         this._weatherAuthorized = accuracy != 'NONE'; | ||||
|  | ||||
|         this._updateAutoLocation(); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WeatherClient.prototype); | ||||
| @@ -19,7 +19,15 @@ const PortalHelperResult = { | ||||
|     RECHECK: 2 | ||||
| }; | ||||
|  | ||||
| const PortalHelperSecurityLevel = { | ||||
|     NOT_YET_DETERMINED: 0, | ||||
|     SECURE: 1, | ||||
|     INSECURE: 2 | ||||
| }; | ||||
|  | ||||
| const INACTIVITY_TIMEOUT = 30000; //ms | ||||
| const CONNECTIVITY_CHECK_HOST = 'nmcheck.gnome.org'; | ||||
| const CONNECTIVITY_CHECK_URI = 'http://' + CONNECTIVITY_CHECK_HOST; | ||||
| const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC; | ||||
|  | ||||
| const HelperDBusInterface = '<node> \ | ||||
| @@ -42,6 +50,71 @@ const HelperDBusInterface = '<node> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const PortalHeaderBar = new Lang.Class({ | ||||
|     Name: 'PortalHeaderBar', | ||||
|     Extends: Gtk.HeaderBar, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent({ show_close_button: true }); | ||||
|  | ||||
|         // See ephy-title-box.c in epiphany for the layout | ||||
|         let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, | ||||
|                                  spacing: 0 }); | ||||
|         this.set_custom_title(vbox); | ||||
|  | ||||
|         /* TRANSLATORS: this is the title of the wifi captive portal login window */ | ||||
|         let titleLabel = new Gtk.Label({ label: _("Hotspot Login"), | ||||
|                                          wrap: false, | ||||
|                                          single_line_mode: true, | ||||
|                                          ellipsize: Pango.EllipsizeMode.END }); | ||||
|         titleLabel.get_style_context().add_class('title'); | ||||
|         vbox.add(titleLabel); | ||||
|  | ||||
|         let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, | ||||
|                                  spacing: 4, | ||||
|                                  halign: Gtk.Align.CENTER, | ||||
|                                  valign: Gtk.Align.BASELINE }); | ||||
|         hbox.get_style_context().add_class('subtitle'); | ||||
|         vbox.add(hbox); | ||||
|  | ||||
|         this._lockImage = new Gtk.Image({ icon_size: Gtk.IconSize.MENU, | ||||
|                                           valign: Gtk.Align.BASELINE }); | ||||
|         hbox.add(this._lockImage); | ||||
|  | ||||
|         this.subtitleLabel = new Gtk.Label({ wrap: false, | ||||
|                                              single_line_mode: true, | ||||
|                                              ellipsize: Pango.EllipsizeMode.END, | ||||
|                                              valign: Gtk.Align.BASELINE, | ||||
|                                              selectable: true}); | ||||
|         this.subtitleLabel.get_style_context().add_class('subtitle'); | ||||
|         hbox.add(this.subtitleLabel); | ||||
|  | ||||
|         vbox.show_all(); | ||||
|     }, | ||||
|  | ||||
|     setSubtitle: function(label) { | ||||
|         this.subtitleLabel.set_text(label); | ||||
|     }, | ||||
|  | ||||
|     setSecurityIcon: function(securityLevel) { | ||||
|         switch (securityLevel) { | ||||
|         case PortalHelperSecurityLevel.NOT_YET_DETERMINED: | ||||
|             this._lockImage.hide(); | ||||
|             break; | ||||
|         case PortalHelperSecurityLevel.SECURE: | ||||
|             this._lockImage.show(); | ||||
|             this._lockImage.set_from_icon_name("channel-secure-symbolic", Gtk.IconSize.MENU); | ||||
|             this._lockImage.set_tooltip_text(null); | ||||
|             break; | ||||
|         case PortalHelperSecurityLevel.INSECURE: | ||||
|             this._lockImage.show(); | ||||
|             this._lockImage.set_from_icon_name("channel-insecure-symbolic", Gtk.IconSize.MENU); | ||||
|             this._lockImage.set_tooltip_text(_('Your connection to this hotspot login is not secure. Passwords or other information you enter on this page can be viewed by people nearby.')); | ||||
|             break; | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const PortalWindow = new Lang.Class({ | ||||
|     Name: 'PortalWindow', | ||||
|     Extends: Gtk.ApplicationWindow, | ||||
| @@ -49,8 +122,14 @@ const PortalWindow = new Lang.Class({ | ||||
|     _init: function(application, url, timestamp, doneCallback) { | ||||
|         this.parent({ application: application }); | ||||
|  | ||||
|         this.connect('delete-event', Lang.bind(this, this.destroyWindow)); | ||||
|         this._headerBar = new PortalHeaderBar(); | ||||
|         this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED); | ||||
|         this.set_titlebar(this._headerBar); | ||||
|         this._headerBar.show(); | ||||
|  | ||||
|         if (!url) { | ||||
|             url = 'http://www.gnome.org'; | ||||
|             url = CONNECTIVITY_CHECK_URI; | ||||
|             this._originalUrlWasGnome = true; | ||||
|         } else { | ||||
|             this._originalUrlWasGnome = false; | ||||
| @@ -62,28 +141,38 @@ const PortalWindow = new Lang.Class({ | ||||
|         this._lastRecheck = 0; | ||||
|         this._recheckAtExit = false; | ||||
|  | ||||
|         this._webView = new WebKit.WebView(); | ||||
|         this._webContext = WebKit.WebContext.new_ephemeral(); | ||||
|         this._webContext.set_cache_model(WebKit.CacheModel.DOCUMENT_VIEWER); | ||||
|         this._webContext.set_network_proxy_settings(WebKit.NetworkProxyMode.NO_PROXY, null); | ||||
|  | ||||
|         this._webView = WebKit.WebView.new_with_context(this._webContext); | ||||
|         this._webView.connect('decide-policy', Lang.bind(this, this._onDecidePolicy)); | ||||
|         this._webView.connect('load-changed', Lang.bind(this, this._onLoadChanged)); | ||||
|         this._webView.connect('insecure-content-detected', Lang.bind(this, this._onInsecureContentDetected)); | ||||
|         this._webView.connect('load-failed-with-tls-errors', Lang.bind(this, this._onLoadFailedWithTlsErrors)); | ||||
|         this._webView.load_uri(url); | ||||
|         this._webView.connect('notify::title', Lang.bind(this, this._syncTitle)); | ||||
|         this._syncTitle(); | ||||
|         this._webView.connect('notify::uri', Lang.bind(this, this._syncUri)); | ||||
|         this._syncUri(); | ||||
|  | ||||
|         this.add(this._webView); | ||||
|         this._webView.show(); | ||||
|         this.set_size_request(600, 450); | ||||
|         this.maximize(); | ||||
|         this.present_with_time(timestamp); | ||||
|  | ||||
|         this.application.set_accels_for_action('app.quit', ['<Primary>q', '<Primary>w']); | ||||
|     }, | ||||
|  | ||||
|     _syncTitle: function() { | ||||
|         let title = this._webView.title; | ||||
|     destroyWindow: function() { | ||||
|         this.destroy(); | ||||
|     }, | ||||
|  | ||||
|         if (title) { | ||||
|             this.title = title; | ||||
|         } else { | ||||
|             /* TRANSLATORS: this is the title of the wifi captive portal login | ||||
|              * window, until we know the title of the actual login page */ | ||||
|             this.title = _("Web Authentication Redirect"); | ||||
|         } | ||||
|     _syncUri: function() { | ||||
|         let uri = this._webView.uri; | ||||
|         if (uri) | ||||
|             this._headerBar.setSubtitle(GLib.uri_unescape_string(uri, null)); | ||||
|         else | ||||
|             this._headerBar.setSubtitle(''); | ||||
|     }, | ||||
|  | ||||
|     refresh: function() { | ||||
| @@ -99,8 +188,46 @@ const PortalWindow = new Lang.Class({ | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onLoadChanged: function(view, loadEvent) { | ||||
|         if (loadEvent == WebKit.LoadEvent.STARTED) { | ||||
|             this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.NOT_YET_DETERMINED); | ||||
|         } else if (loadEvent == WebKit.LoadEvent.COMMITTED) { | ||||
|             let tlsInfo = this._webView.get_tls_info(); | ||||
|             let ret = tlsInfo[0]; | ||||
|             let flags = tlsInfo[2]; | ||||
|             if (ret && flags == 0) | ||||
|                 this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.SECURE); | ||||
|             else | ||||
|                 this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onInsecureContentDetected: function () { | ||||
|         this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE); | ||||
|     }, | ||||
|  | ||||
|     _onLoadFailedWithTlsErrors: function (view, failingURI, certificate, errors) { | ||||
|         this._headerBar.setSecurityIcon(PortalHelperSecurityLevel.INSECURE); | ||||
|         let uri = new Soup.URI(failingURI); | ||||
|         this._webContext.allow_tls_certificate_for_host(certificate, uri.get_host()); | ||||
|         this._webView.load_uri(failingURI); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onDecidePolicy: function(view, decision, type) { | ||||
|         if (type == WebKit.PolicyDecisionType.NEW_WINDOW_ACTION) { | ||||
|             let navigationAction = decision.get_navigation_action(); | ||||
|             if (navigationAction.is_user_gesture()) { | ||||
|                 // Even though the portal asks for a new window, | ||||
|                 // perform the navigation in the current one. Some | ||||
|                 // portals open a window as their last login step and | ||||
|                 // ignoring that window causes them to not let the | ||||
|                 // user go through. We don't risk popups taking over | ||||
|                 // the page because we check that the navigation is | ||||
|                 // user initiated. | ||||
|                 this._webView.load_request(navigationAction.get_request()); | ||||
|             } | ||||
|  | ||||
|             decision.ignore(); | ||||
|             return true; | ||||
|         } | ||||
| @@ -112,12 +239,12 @@ const PortalWindow = new Lang.Class({ | ||||
|         let uri = new Soup.URI(request.get_uri()); | ||||
|  | ||||
|         if (!uri.host_equal(this._uri) && this._originalUrlWasGnome) { | ||||
|             if (uri.get_host() == 'www.gnome.org' && this._everSeenRedirect) { | ||||
|             if (uri.get_host() == CONNECTIVITY_CHECK_HOST && this._everSeenRedirect) { | ||||
|                 // Yay, we got to gnome! | ||||
|                 decision.ignore(); | ||||
|                 this._doneCallback(PortalHelperResult.COMPLETED); | ||||
|                 return true; | ||||
|             } else if (uri.get_host() != 'www.gnome.org') { | ||||
|             } else if (uri.get_host() != CONNECTIVITY_CHECK_HOST) { | ||||
|                 this._everSeenRedirect = true; | ||||
|             } | ||||
|         } | ||||
| @@ -166,6 +293,10 @@ const WebPortalHelper = new Lang.Class({ | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(HelperDBusInterface, this); | ||||
|         this._queue = []; | ||||
|  | ||||
|         let action = new Gio.SimpleAction({ name: 'quit' }); | ||||
|         action.connect('activate', () => { this.active_window.destroyWindow(); }); | ||||
|         this.add_action(action); | ||||
|     }, | ||||
|  | ||||
|     vfunc_dbus_register: function(connection, path) { | ||||
| @@ -197,7 +328,7 @@ const WebPortalHelper = new Lang.Class({ | ||||
|  | ||||
|             if (obj.connection == connection) { | ||||
|                 if (obj.window) | ||||
|                     obj.window.destroy(); | ||||
|                     obj.window.destroyWindow(); | ||||
|                 this._queue.splice(i, 1); | ||||
|                 break; | ||||
|             } | ||||
| @@ -226,7 +357,7 @@ const WebPortalHelper = new Lang.Class({ | ||||
|         if (top.window != null) | ||||
|             return; | ||||
|  | ||||
|         top.window = new PortalWindow(this, top.uri, top.timestamp, Lang.bind(this, function(result) { | ||||
|         top.window = new PortalWindow(this, top.url, top.timestamp, Lang.bind(this, function(result) { | ||||
|             this._dbusImpl.emit_signal('Done', new GLib.Variant('(ou)', [top.connection, result])); | ||||
|         })); | ||||
|     }, | ||||
| @@ -239,6 +370,11 @@ function initEnvironment() { | ||||
| function main(argv) { | ||||
|     initEnvironment(); | ||||
|  | ||||
|     if (!WebKit.WebContext.new_ephemeral) { | ||||
|         log('WebKitGTK 2.16 is required for the portal-helper, see https://bugzilla.gnome.org/show_bug.cgi?id=780453'); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR); | ||||
|     Gettext.textdomain(Config.GETTEXT_PACKAGE); | ||||
|  | ||||
|   | ||||
							
								
								
									
										202
									
								
								js/ui/accessDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,202 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
|  | ||||
| const RequestIface = '<node> \ | ||||
| <interface name="org.freedesktop.impl.portal.Request"> \ | ||||
| <method name="Close"/> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const AccessIface = '<node> \ | ||||
| <interface name="org.freedesktop.impl.portal.Access"> \ | ||||
| <method name="AccessDialog"> \ | ||||
|   <arg type="o" name="handle" direction="in"/> \ | ||||
|   <arg type="s" name="app_id" direction="in"/> \ | ||||
|   <arg type="s" name="parent_window" direction="in"/> \ | ||||
|   <arg type="s" name="title" direction="in"/> \ | ||||
|   <arg type="s" name="subtitle" direction="in"/> \ | ||||
|   <arg type="s" name="body" direction="in"/> \ | ||||
|   <arg type="a{sv}" name="options" direction="in"/> \ | ||||
|   <arg type="u" name="response" direction="out"/> \ | ||||
|   <arg type="a{sv}" name="results" direction="out"/> \ | ||||
| </method> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const DialogResponse = { | ||||
|     OK: 0, | ||||
|     CANCEL: 1, | ||||
|     CLOSED: 2 | ||||
| }; | ||||
|  | ||||
| const AccessDialog = new Lang.Class({ | ||||
|     Name: 'AccessDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(invocation, handle, title, subtitle, body, options) { | ||||
|         this.parent({ styleClass: 'access-dialog' }); | ||||
|  | ||||
|         this._invocation = invocation; | ||||
|         this._handle = handle; | ||||
|  | ||||
|         this._requestExported = false; | ||||
|         this._request = Gio.DBusExportedObject.wrapJSObject(RequestIface, this); | ||||
|  | ||||
|         for (let option in options) | ||||
|             options[option] = options[option].deep_unpack(); | ||||
|  | ||||
|         this._buildLayout(title, subtitle, body, options); | ||||
|     }, | ||||
|  | ||||
|     _buildLayout: function(title, subtitle, body, options) { | ||||
|         // No support for non-modal system dialogs, so ignore the option | ||||
|         //let modal = options['modal'] || true; | ||||
|         let denyLabel = options['deny_label'] || _("Deny Access"); | ||||
|         let grantLabel = options['grant_label'] || _("Grant Access"); | ||||
|         let iconName = options['icon'] || null; | ||||
|         let choices = options['choices'] || []; | ||||
|  | ||||
|         let mainContentBox = new St.BoxLayout(); | ||||
|         mainContentBox.style_class = 'access-dialog-main-layout'; | ||||
|         this.contentLayout.add_actor(mainContentBox); | ||||
|  | ||||
|         let icon = new St.Icon({ style_class: 'access-dialog-icon', | ||||
|                                  icon_name: iconName, | ||||
|                                  y_align: Clutter.ActorAlign.START }); | ||||
|         mainContentBox.add_actor(icon); | ||||
|  | ||||
|         let messageBox = new St.BoxLayout({ vertical: true }); | ||||
|         messageBox.style_class = 'access-dialog-content', | ||||
|         mainContentBox.add_actor(messageBox); | ||||
|  | ||||
|         let label; | ||||
|         label = new St.Label({ style_class: 'access-dialog-title headline', | ||||
|                                text: title }); | ||||
|         messageBox.add_actor(label); | ||||
|  | ||||
|         label = new St.Label({ style_class: 'access-dialog-subtitle', | ||||
|                                text: subtitle }); | ||||
|         label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         label.clutter_text.line_wrap = true; | ||||
|         messageBox.add_actor(label); | ||||
|  | ||||
|         this._choices = new Map(); | ||||
|  | ||||
|         for (let i = 0; i < choices.length; i++) { | ||||
|             let [id, name, opts, selected] = choices[i]; | ||||
|             if (opts.length > 0) | ||||
|                 continue; // radio buttons, not implemented | ||||
|  | ||||
|             let check = new CheckBox.CheckBox(); | ||||
|             check.getLabelActor().text = name; | ||||
|             check.actor.checked = selected == "true"; | ||||
|             messageBox.add_actor(check.actor); | ||||
|  | ||||
|             this._choices.set(id, check); | ||||
|         } | ||||
|  | ||||
|         label = new St.Label({ text: body }); | ||||
|         label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         label.clutter_text.line_wrap = true; | ||||
|         messageBox.add_actor(label); | ||||
|  | ||||
|         this.addButton({ label: denyLabel, | ||||
|                          action: () => { | ||||
|                              this._sendResponse(DialogResponse.CANCEL); | ||||
|                          }, | ||||
|                          key: Clutter.KEY_Escape }); | ||||
|         this.addButton({ label: grantLabel, | ||||
|                          action: () => { | ||||
|                              this._sendResponse(DialogResponse.OK); | ||||
|                          }}); | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         let connection = this._invocation.get_connection(); | ||||
|         this._requestExported = this._request.export(connection, this._handle); | ||||
|     }, | ||||
|  | ||||
|     CloseAsync: function(invocation, params) { | ||||
|         if (this._invocation.get_sender() != invocation.get_sender()) { | ||||
|             invocation.return_error_literal(Gio.DBusError, | ||||
|                                             Gio.DBusError.ACCESS_DENIED, | ||||
|                                             ''); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._sendResponse(DialogResponse.CLOSED); | ||||
|     }, | ||||
|  | ||||
|     _sendResponse: function(response) { | ||||
|         if (this._requestExported) | ||||
|             this._request.unexport(); | ||||
|         this._requestExported = false; | ||||
|  | ||||
|         let results = {}; | ||||
|         if (response == DialogResponse.OK) { | ||||
|             for (let [id, check] of this._choices) { | ||||
|                 let checked = check.actor.checked ? 'true' : 'false'; | ||||
|                 results[id] = new GLib.Variant('s', checked); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Delay actual response until the end of the close animation (if any) | ||||
|         this.connect('closed', () => { | ||||
|             this._invocation.return_value(new GLib.Variant('(ua{sv})', | ||||
|                                                            [response, results])); | ||||
|         }); | ||||
|         this.close(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AccessDialogDBus = new Lang.Class({ | ||||
|     Name: 'AccessDialogDBus', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._accessDialog = null; | ||||
|  | ||||
|         this._windowTracker = Shell.WindowTracker.get_default(); | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AccessIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/portal/desktop'); | ||||
|  | ||||
|         Gio.DBus.session.own_name('org.freedesktop.impl.portal.desktop.gnome', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     AccessDialogAsync: function(params, invocation) { | ||||
|         if (this._accessDialog) { | ||||
|             invocation.return_error_literal(Gio.DBusError, | ||||
|                                             Gio.DBusError.LIMITS_EXCEEDED, | ||||
|                                             'Already showing a system access dialog'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let [handle, appId, parentWindow, title, subtitle, body, options] = params; | ||||
|         // We probably want to use parentWindow and global.display.focus_window | ||||
|         // for this check in the future | ||||
|         if (appId && appId + '.desktop' != this._windowTracker.focus_app.id) { | ||||
|             invocation.return_error_literal(Gio.DBusError, | ||||
|                                             Gio.DBusError.ACCESS_DENIED, | ||||
|                                             'Only the focused app is allowed to show a system access dialog'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dialog = new AccessDialog(invocation, handle, title, | ||||
|                                       subtitle, body, options); | ||||
|         dialog.open(); | ||||
|  | ||||
|         dialog.connect('closed', () => { this._accessDialog = null; }); | ||||
|  | ||||
|         this._accessDialog = dialog; | ||||
|     } | ||||
| }); | ||||
							
								
								
									
										199
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						| @@ -33,10 +33,9 @@ const AppIconMode = { | ||||
| }; | ||||
|  | ||||
| function _createWindowClone(window, size) { | ||||
|     let windowTexture = window.get_texture(); | ||||
|     let [width, height] = windowTexture.get_size(); | ||||
|     let [width, height] = window.get_size(); | ||||
|     let scale = Math.min(1.0, size / width, size / height); | ||||
|     return new Clutter.Clone({ source: windowTexture, | ||||
|     return new Clutter.Clone({ source: window, | ||||
|                                width: width * scale, | ||||
|                                height: height * scale, | ||||
|                                x_align: Clutter.ActorAlign.CENTER, | ||||
| @@ -46,6 +45,19 @@ function _createWindowClone(window, size) { | ||||
|                                y_expand: true }); | ||||
| }; | ||||
|  | ||||
| function getWindows(workspace) { | ||||
|     // We ignore skip-taskbar windows in switchers, but if they are attached | ||||
|     // to their parent, their position in the MRU list may be more appropriate | ||||
|     // than the parent; so start with the complete list ... | ||||
|     let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, | ||||
|                                               workspace); | ||||
|     // ... map windows to their parent where appropriate ... | ||||
|     return windows.map(w => { | ||||
|         return w.is_attached_dialog() ? w.get_transient_for() : w; | ||||
|     // ... and filter out skip-taskbar windows and duplicates | ||||
|     }).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) == i); | ||||
| } | ||||
|  | ||||
| const AppSwitcherPopup = new Lang.Class({ | ||||
|     Name: 'AppSwitcherPopup', | ||||
|     Extends: SwitcherPopup.SwitcherPopup, | ||||
| @@ -354,6 +366,149 @@ const AppSwitcherPopup = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const CyclerHighlight = new Lang.Class({ | ||||
|     Name: 'CyclerHighlight', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._window = null; | ||||
|  | ||||
|         this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|  | ||||
|         this._clone = new Clutter.Clone(); | ||||
|         this.actor.add_actor(this._clone); | ||||
|  | ||||
|         this._highlight = new St.Widget({ style_class: 'cycler-highlight' }); | ||||
|         this.actor.add_actor(this._highlight); | ||||
|  | ||||
|         let coordinate = Clutter.BindCoordinate.ALL; | ||||
|         let constraint = new Clutter.BindConstraint({ coordinate: coordinate }); | ||||
|         this._clone.bind_property('source', constraint, 'source', 0); | ||||
|  | ||||
|         this.actor.add_constraint(constraint); | ||||
|  | ||||
|         this.actor.connect('notify::allocation', | ||||
|                            Lang.bind(this, this._onAllocationChanged)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|     }, | ||||
|  | ||||
|     set window(w) { | ||||
|         if (this._window == w) | ||||
|             return; | ||||
|  | ||||
|         this._window = w; | ||||
|  | ||||
|         if (this._clone.source) | ||||
|             this._clone.source.sync_visibility(); | ||||
|  | ||||
|         let windowActor = this._window ? this._window.get_compositor_private() | ||||
|                                        : null; | ||||
|  | ||||
|         if (windowActor) | ||||
|             windowActor.hide(); | ||||
|  | ||||
|         this._clone.source = windowActor; | ||||
|     }, | ||||
|  | ||||
|     _onAllocationChanged: function() { | ||||
|         if (!this._window) { | ||||
|             this._highlight.set_size(0, 0); | ||||
|             this._highlight.hide(); | ||||
|         } else { | ||||
|             let [x, y] = this.actor.allocation.get_origin(); | ||||
|             let rect = this._window.get_frame_rect(); | ||||
|             this._highlight.set_size(rect.width, rect.height); | ||||
|             this._highlight.set_position(rect.x - x, rect.y - y); | ||||
|             this._highlight.show(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this.window = null; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const CyclerPopup = new Lang.Class({ | ||||
|     Name: 'CyclerPopup', | ||||
|     Extends: SwitcherPopup.SwitcherPopup, | ||||
|     Abstract: true, | ||||
|  | ||||
|     _init : function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._items = this._getWindows(); | ||||
|  | ||||
|         if (this._items.length == 0) | ||||
|             return; | ||||
|  | ||||
|         this._highlight = new CyclerHighlight(); | ||||
|         global.window_group.add_actor(this._highlight.actor); | ||||
|  | ||||
|         // We don't show an actual popup, so just provide what SwitcherPopup | ||||
|         // expects instead of inheriting from SwitcherList | ||||
|         this._switcherList = { actor: new St.Widget(), | ||||
|                                highlight: Lang.bind(this, this._highlightItem), | ||||
|                                connect: function() {} }; | ||||
|     }, | ||||
|  | ||||
|     _highlightItem: function(index, justOutline) { | ||||
|         this._highlight.window = this._items[index]; | ||||
|         global.window_group.set_child_above_sibling(this._highlight.actor, null); | ||||
|     }, | ||||
|  | ||||
|     _finish: function() { | ||||
|         let window = this._items[this._selectedIndex]; | ||||
|         let ws = window.get_workspace(); | ||||
|         let activeWs = global.screen.get_active_workspace(); | ||||
|  | ||||
|         if (window.minimized) { | ||||
|             Main.wm.skipNextEffect(window.get_compositor_private()); | ||||
|             window.unminimize(); | ||||
|         } | ||||
|  | ||||
|         if (activeWs == ws) { | ||||
|             Main.activateWindow(window); | ||||
|         } else { | ||||
|             // If the selected window is on a different workspace, we don't | ||||
|             // want it to disappear, then slide in with the workspace; instead, | ||||
|             // always activate it on the active workspace ... | ||||
|             activeWs.activate_with_focus(window, global.get_current_time()); | ||||
|  | ||||
|             // ... then slide it over to the original workspace if necessary | ||||
|             Main.wm.actionMoveWindow(window, ws); | ||||
|         } | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._highlight.actor.destroy(); | ||||
|  | ||||
|         this.parent(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|  | ||||
| const GroupCyclerPopup = new Lang.Class({ | ||||
|     Name: 'GroupCyclerPopup', | ||||
|     Extends: CyclerPopup, | ||||
|  | ||||
|     _getWindows: function() { | ||||
|         let app = Shell.WindowTracker.get_default().focus_app; | ||||
|         return app ? app.get_windows() : []; | ||||
|     }, | ||||
|  | ||||
|     _keyPressHandler: function(keysym, action) { | ||||
|         if (action == Meta.KeyBindingAction.CYCLE_GROUP) | ||||
|             this._select(this._next()); | ||||
|         else if (action == Meta.KeyBindingAction.CYCLE_GROUP_BACKWARD) | ||||
|             this._select(this._previous()); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const WindowSwitcherPopup = new Lang.Class({ | ||||
|     Name: 'WindowSwitcherPopup', | ||||
|     Extends: SwitcherPopup.SwitcherPopup, | ||||
| @@ -374,7 +529,7 @@ const WindowSwitcherPopup = new Lang.Class({ | ||||
|  | ||||
|     _getWindowList: function() { | ||||
|         let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null; | ||||
|         return global.display.get_tab_list(Meta.TabList.NORMAL, workspace); | ||||
|         return getWindows(workspace); | ||||
|     }, | ||||
|  | ||||
|     _keyPressHandler: function(keysym, action) { | ||||
| @@ -401,6 +556,32 @@ const WindowSwitcherPopup = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const WindowCyclerPopup = new Lang.Class({ | ||||
|     Name: 'WindowCyclerPopup', | ||||
|     Extends: CyclerPopup, | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' }); | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _getWindows: function() { | ||||
|         let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null; | ||||
|         return getWindows(workspace); | ||||
|     }, | ||||
|  | ||||
|     _keyPressHandler: function(keysym, action) { | ||||
|         if (action == Meta.KeyBindingAction.CYCLE_WINDOWS) | ||||
|             this._select(this._next()); | ||||
|         else if (action == Meta.KeyBindingAction.CYCLE_WINDOWS_BACKWARD) | ||||
|             this._select(this._previous()); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AppIcon = new Lang.Class({ | ||||
|     Name: 'AppIcon', | ||||
|  | ||||
| @@ -448,8 +629,6 @@ const AppSwitcher = new Lang.Class({ | ||||
|             }); | ||||
|             if (appIcon.cachedWindows.length > 0) | ||||
|                 this._addIcon(appIcon); | ||||
|             else if (workspace == null) | ||||
|                 throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name())); | ||||
|         } | ||||
|  | ||||
|         this._curApp = -1; | ||||
| @@ -687,15 +866,17 @@ const WindowIcon = new Lang.Class({ | ||||
|  | ||||
|         this._icon.destroy_all_children(); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|  | ||||
|         switch (mode) { | ||||
|             case AppIconMode.THUMBNAIL_ONLY: | ||||
|                 size = WINDOW_PREVIEW_SIZE; | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE)); | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor)); | ||||
|                 break; | ||||
|  | ||||
|             case AppIconMode.BOTH: | ||||
|                 size = WINDOW_PREVIEW_SIZE; | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE)); | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor)); | ||||
|  | ||||
|                 if (this.app) | ||||
|                     this._icon.add_actor(this._createAppIcon(this.app, | ||||
| @@ -707,7 +888,7 @@ const WindowIcon = new Lang.Class({ | ||||
|                 this._icon.add_actor(this._createAppIcon(this.app, size)); | ||||
|         } | ||||
|  | ||||
|         this._icon.set_size(size, size); | ||||
|         this._icon.set_size(size * scaleFactor, size * scaleFactor); | ||||
|     }, | ||||
|  | ||||
|     _createAppIcon: function(app, size) { | ||||
|   | ||||
| @@ -7,7 +7,7 @@ const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 100; | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 16; | ||||
|  | ||||
| const Animation = new Lang.Class({ | ||||
|     Name: 'Animation', | ||||
| @@ -33,7 +33,7 @@ const Animation = new Lang.Class({ | ||||
|             if (this._frame == 0) | ||||
|                 this._showFrame(0); | ||||
|  | ||||
|             this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update)); | ||||
|             this._timeoutId = GLib.timeout_add(GLib.PRIORITY_LOW, this._speed, Lang.bind(this, this._update)); | ||||
|             GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update'); | ||||
|         } | ||||
|  | ||||
| @@ -67,7 +67,7 @@ const Animation = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _animationsLoaded: function() { | ||||
|         this._isLoaded = true; | ||||
|         this._isLoaded = this._animations.get_n_children() > 0; | ||||
|  | ||||
|         if (this._isPlaying) | ||||
|             this.play(); | ||||
|   | ||||
| @@ -60,6 +60,18 @@ const PAGE_SWITCH_TIME = 0.3; | ||||
| const VIEWS_SWITCH_TIME = 0.4; | ||||
| const VIEWS_SWITCH_ANIMATION_DELAY = 0.1; | ||||
|  | ||||
| const SWITCHEROO_BUS_NAME = 'net.hadess.SwitcherooControl'; | ||||
| const SWITCHEROO_OBJECT_PATH = '/net/hadess/SwitcherooControl'; | ||||
|  | ||||
| const SwitcherooProxyInterface = '<node> \ | ||||
| <interface name="net.hadess.SwitcherooControl"> \ | ||||
|   <property name="HasDualGpu" type="b" access="read"/> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const SwitcherooProxy = Gio.DBusProxy.makeProxyWrapper(SwitcherooProxyInterface); | ||||
| let discreteGpuAvailable = false; | ||||
|  | ||||
| function _getCategories(info) { | ||||
|     let categoriesStr = info.get_categories(); | ||||
|     if (!categoriesStr) | ||||
| @@ -198,6 +210,14 @@ const BaseAppView = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     animate: function(animationDirection, onComplete) { | ||||
|         if (onComplete) { | ||||
|             let animationDoneId = this._grid.connect('animation-done', Lang.bind(this, | ||||
|                 function () { | ||||
|                     this._grid.disconnect(animationDoneId); | ||||
|                     onComplete(); | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         if (animationDirection == IconGrid.AnimationDirection.IN) { | ||||
|             let toAnimate = this._grid.actor.connect('notify::allocation', Lang.bind(this, | ||||
|                 function() { | ||||
| @@ -213,14 +233,6 @@ const BaseAppView = new Lang.Class({ | ||||
|         } else { | ||||
|             this._doSpringAnimation(animationDirection); | ||||
|         } | ||||
|  | ||||
|         if (onComplete) { | ||||
|             let animationDoneId = this._grid.connect('animation-done', Lang.bind(this, | ||||
|                 function () { | ||||
|                     this._grid.disconnect(animationDoneId); | ||||
|                     onComplete(); | ||||
|             })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     animateSwitch: function(animationDirection) { | ||||
| @@ -500,6 +512,11 @@ const AllView = new Lang.Class({ | ||||
|  | ||||
|     _loadApps: function() { | ||||
|         let apps = Gio.AppInfo.get_all().filter(function(appInfo) { | ||||
|             try { | ||||
|                 let id = appInfo.get_id(); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|                 return false; | ||||
|             } | ||||
|             return appInfo.should_show(); | ||||
|         }).map(function(app) { | ||||
|             return app.get_id(); | ||||
| @@ -753,7 +770,8 @@ const AllView = new Lang.Class({ | ||||
|         let fadeOffset = Math.min(this._grid.topPadding, | ||||
|                                   this._grid.bottomPadding); | ||||
|         this._scrollView.update_fade_effect(fadeOffset, 0); | ||||
|         this._scrollView.get_effect('fade').fade_edges = true; | ||||
|         if (fadeOffset > 0) | ||||
|             this._scrollView.get_effect('fade').fade_edges = true; | ||||
|  | ||||
|         if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) { | ||||
|             this._adjustment.value = 0; | ||||
| @@ -881,6 +899,8 @@ const ControlsBoxLayout = Lang.Class({ | ||||
| const ViewStackLayout = new Lang.Class({ | ||||
|     Name: 'ViewStackLayout', | ||||
|     Extends: Clutter.BinLayout, | ||||
|     Signals: { 'allocated-size-changed': { param_types: [GObject.TYPE_INT, | ||||
|                                                          GObject.TYPE_INT] } }, | ||||
|  | ||||
|     vfunc_allocate: function (actor, box, flags) { | ||||
|         let availWidth = box.x2 - box.x1; | ||||
| @@ -891,7 +911,6 @@ const ViewStackLayout = new Lang.Class({ | ||||
|         this.parent(actor, box, flags); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ViewStackLayout.prototype); | ||||
|  | ||||
| const AppDisplay = new Lang.Class({ | ||||
|     Name: 'AppDisplay', | ||||
| @@ -963,10 +982,36 @@ const AppDisplay = new Lang.Class({ | ||||
|             initialView = Views.ALL; | ||||
|         this._showView(initialView); | ||||
|         this._updateFrequentVisibility(); | ||||
|  | ||||
|         Gio.DBus.system.watch_name(SWITCHEROO_BUS_NAME, | ||||
|                                    Gio.BusNameWatcherFlags.NONE, | ||||
|                                    Lang.bind(this, this._switcherooProxyAppeared), | ||||
|                                    Lang.bind(this, function() { | ||||
|                                        this._switcherooProxy = null; | ||||
|                                        this._updateDiscreteGpuAvailable(); | ||||
|                                    })); | ||||
|     }, | ||||
|  | ||||
|     _updateDiscreteGpuAvailable: function() { | ||||
|         if (!this._switcherooProxy) | ||||
|             discreteGpuAvailable = false; | ||||
|         else | ||||
|             discreteGpuAvailable = this._switcherooProxy.HasDualGpu; | ||||
|     }, | ||||
|  | ||||
|     _switcherooProxyAppeared: function() { | ||||
|         this._switcherooProxy = new SwitcherooProxy(Gio.DBus.system, SWITCHEROO_BUS_NAME, SWITCHEROO_OBJECT_PATH, | ||||
|             Lang.bind(this, function(proxy, error) { | ||||
|                 if (error) { | ||||
|                     log(error.message); | ||||
|                     return; | ||||
|                 } | ||||
|                 this._updateDiscreteGpuAvailable(); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     animate: function(animationDirection, onComplete) { | ||||
|         let currentView = this._views[global.settings.get_uint('app-picker-view')].view; | ||||
|         let currentView = this._views.filter(v => v.control.has_style_pseudo_class('checked')).pop().view; | ||||
|  | ||||
|         // Animate controls opacity using iconGrid animation time, since | ||||
|         // it will be the time the AllView or FrequentView takes to show | ||||
| @@ -1038,6 +1083,8 @@ const AppSearchProvider = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this._appSys = Shell.AppSystem.get_default(); | ||||
|         this.id = 'applications'; | ||||
|         this.isRemoteProvider = false; | ||||
|         this.canLaunchSearch = false; | ||||
|     }, | ||||
|  | ||||
|     getResultMetas: function(apps, callback) { | ||||
| @@ -1060,7 +1107,7 @@ const AppSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     getInitialResultSet: function(terms, callback, cancellable) { | ||||
|         let query = terms.join(' '); | ||||
|         let groups = Gio.DesktopAppInfo.search(query); | ||||
|         let groups = Shell.AppSystem.search(query); | ||||
|         let usage = Shell.AppUsage.get_default(); | ||||
|         let results = []; | ||||
|         groups.forEach(function(group) { | ||||
| @@ -1206,6 +1253,7 @@ const FolderIcon = new Lang.Class({ | ||||
|  | ||||
|     _init: function(id, path, parentView) { | ||||
|         this.id = id; | ||||
|         this.name = ''; | ||||
|         this._parentView = parentView; | ||||
|  | ||||
|         this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder', | ||||
| @@ -1289,7 +1337,10 @@ const FolderIcon = new Lang.Class({ | ||||
|             if (!_listsIntersect(folderCategories, appCategories)) | ||||
|                 return; | ||||
|  | ||||
|             addAppId(appInfo.get_id()); | ||||
|             try { | ||||
|                 addAppId(appInfo.get_id()); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         this.actor.visible = this.view.getAllItems().length > 0; | ||||
| @@ -1805,7 +1856,7 @@ const AppIconMenu = new Lang.Class({ | ||||
|             if (!source.actor.mapped) | ||||
|                 this.close(); | ||||
|         })); | ||||
|         source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); })); | ||||
|         source.actor.connect('destroy', Lang.bind(this, this.destroy)); | ||||
|  | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|     }, | ||||
| @@ -1852,6 +1903,19 @@ const AppIconMenu = new Lang.Class({ | ||||
|                 this._appendSeparator(); | ||||
|             } | ||||
|  | ||||
|             if (discreteGpuAvailable && | ||||
|                 this._source.app.state == Shell.AppState.STOPPED && | ||||
|                 actions.indexOf('activate-discrete-gpu') == -1) { | ||||
|                 this._onDiscreteGpuMenuItem = this._appendMenuItem(_("Launch using Dedicated Graphics Card")); | ||||
|                 this._onDiscreteGpuMenuItem.connect('activate', Lang.bind(this, function() { | ||||
|                     if (this._source.app.state == Shell.AppState.STOPPED) | ||||
|                         this._source.animateLaunch(); | ||||
|  | ||||
|                     this._source.app.launch(0, -1, true); | ||||
|                     this.emit('activate-window', null); | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
|             for (let i = 0; i < actions.length; i++) { | ||||
|                 let action = actions[i]; | ||||
|                 let item = this._appendMenuItem(appInfo.get_action_name(action)); | ||||
|   | ||||
| @@ -10,22 +10,27 @@ const RENAMED_DESKTOP_IDS = { | ||||
|     'baobab.desktop': 'org.gnome.baobab.desktop', | ||||
|     'cheese.desktop': 'org.gnome.Cheese.desktop', | ||||
|     'dconf-editor.desktop': 'ca.desrt.dconf-editor.desktop', | ||||
|     'epiphany.desktop': 'org.gnome.Epiphany.desktop', | ||||
|     'file-roller.desktop': 'org.gnome.FileRoller.desktop', | ||||
|     'gcalctool.desktop': 'gnome-calculator.desktop', | ||||
|     'gcalctool.desktop': 'org.gnome.Calculator.desktop', | ||||
|     'geary.desktop': 'org.gnome.Geary.desktop', | ||||
|     'gedit.desktop': 'org.gnome.gedit.desktop', | ||||
|     'glchess.desktop': 'gnome-chess.desktop', | ||||
|     'glines.desktop': 'five-or-more.desktop', | ||||
|     'gnect.desktop': 'four-in-a-row.desktop', | ||||
|     'gnibbles.desktop': 'gnome-nibbles.desktop', | ||||
|     'gnibbles.desktop': 'org.gnome.Nibbles.desktop', | ||||
|     'gnobots2.desktop': 'gnome-robots.desktop', | ||||
|     'gnome-boxes.desktop': 'org.gnome.Boxes.desktop', | ||||
|     'gnome-calculator.desktop': 'org.gnome.Calculator.desktop', | ||||
|     'gnome-clocks.desktop': 'org.gnome.clocks.desktop', | ||||
|     'gnome-contacts.desktop': 'org.gnome.Contacts.desktop', | ||||
|     'gnome-documents.desktop': 'org.gnome.Documents.desktop', | ||||
|     'gnome-font-viewer.desktop': 'org.gnome.font-viewer.desktop', | ||||
|     'gnome-nibbles.desktop': 'org.gnome.Nibbles.desktop', | ||||
|     'gnome-photos.desktop': 'org.gnome.Photos.desktop', | ||||
|     'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop', | ||||
|     'gnome-software.desktop': 'org.gnome.Software.desktop', | ||||
|     'gnome-terminal.desktop': 'org.gnome.Terminal.desktop', | ||||
|     'gnome-weather.desktop': 'org.gnome.Weather.Application.desktop', | ||||
|     'gnomine.desktop': 'gnome-mines.desktop', | ||||
|     'gnotravex.desktop': 'gnome-tetravex.desktop', | ||||
| @@ -54,12 +59,14 @@ const AppFavorites = new Lang.Class({ | ||||
|  | ||||
|     reload: function() { | ||||
|         let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY); | ||||
|         let appSys = Shell.AppSystem.get_default(); | ||||
|  | ||||
|         // Map old desktop file names to the current ones | ||||
|         let updated = false; | ||||
|         ids = ids.map(function (id) { | ||||
|             let newId = RENAMED_DESKTOP_IDS[id]; | ||||
|             if (newId !== undefined) { | ||||
|             if (newId !== undefined && | ||||
|                 appSys.lookup_app(newId) != null) { | ||||
|                 updated = true; | ||||
|                 return newId; | ||||
|             } | ||||
| @@ -69,7 +76,6 @@ const AppFavorites = new Lang.Class({ | ||||
|         if (updated) | ||||
|             global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); | ||||
|  | ||||
|         let appSys = Shell.AppSystem.get_default(); | ||||
|         let apps = ids.map(function (id) { | ||||
|                 return appSys.lookup_app(id); | ||||
|             }).filter(function (app) { | ||||
|   | ||||
							
								
								
									
										216
									
								
								js/ui/audioDeviceSelection.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,216 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
|  | ||||
| const AudioDevice = { | ||||
|     HEADPHONES: 1 << 0, | ||||
|     HEADSET:    1 << 1, | ||||
|     MICROPHONE: 1 << 2 | ||||
| }; | ||||
|  | ||||
| const AudioDeviceSelectionIface = '<node> \ | ||||
| <interface name="org.gnome.Shell.AudioDeviceSelection"> \ | ||||
| <method name="Open"> \ | ||||
|     <arg name="devices" direction="in" type="as" /> \ | ||||
| </method> \ | ||||
| <method name="Close"> \ | ||||
| </method> \ | ||||
| <signal name="DeviceSelected"> \ | ||||
|     <arg name="device" type="s" /> \ | ||||
| </signal> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const AudioDeviceSelectionDialog = new Lang.Class({ | ||||
|     Name: 'AudioDeviceSelectionDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     _init: function(devices) { | ||||
|         this.parent({ styleClass: 'audio-device-selection-dialog' }); | ||||
|  | ||||
|         this._deviceItems = {}; | ||||
|  | ||||
|         this._buildLayout(); | ||||
|  | ||||
|         if (devices & AudioDevice.HEADPHONES) | ||||
|             this._addDevice(AudioDevice.HEADPHONES); | ||||
|         if (devices & AudioDevice.HEADSET) | ||||
|             this._addDevice(AudioDevice.HEADSET); | ||||
|         if (devices & AudioDevice.MICROPHONE) | ||||
|             this._addDevice(AudioDevice.MICROPHONE); | ||||
|  | ||||
|         if (this._selectionBox.get_n_children() < 2) | ||||
|             throw new Error('Too few devices for a selection'); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _buildLayout: function(devices) { | ||||
|         let title = new St.Label({ style_class: 'audio-selection-title', | ||||
|                                    text: _("Select Audio Device"), | ||||
|                                    x_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|         this.contentLayout.style_class = 'audio-selection-content'; | ||||
|         this.contentLayout.add(title); | ||||
|  | ||||
|         this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' }); | ||||
|         this.contentLayout.add(this._selectionBox, { expand: true }); | ||||
|  | ||||
|         this.addButton({ action: Lang.bind(this, this._openSettings), | ||||
|                          label: _("Sound Settings") }); | ||||
|         this.addButton({ action: Lang.bind(this, this.close), | ||||
|                          label: _("Cancel"), | ||||
|                          key: Clutter.Escape }); | ||||
|     }, | ||||
|  | ||||
|     _getDeviceLabel: function(device) { | ||||
|         switch(device) { | ||||
|             case AudioDevice.HEADPHONES: | ||||
|                 return _("Headphones"); | ||||
|             case AudioDevice.HEADSET: | ||||
|                 return _("Headset"); | ||||
|             case AudioDevice.MICROPHONE: | ||||
|                 return _("Microphone"); | ||||
|             default: | ||||
|                 return null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _getDeviceIcon: function(device) { | ||||
|         switch(device) { | ||||
|             case AudioDevice.HEADPHONES: | ||||
|                 return 'audio-headphones-symbolic'; | ||||
|             case AudioDevice.HEADSET: | ||||
|                 return 'audio-headset-symbolic'; | ||||
|             case AudioDevice.MICROPHONE: | ||||
|                 return 'audio-input-microphone-symbolic'; | ||||
|             default: | ||||
|                 return null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _addDevice: function(device) { | ||||
|         let box = new St.BoxLayout({ style_class: 'audio-selection-device-box', | ||||
|                                      vertical: true }); | ||||
|         box.connect('notify::height', | ||||
|             function() { | ||||
|                 Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                     function() { | ||||
|                         box.width = box.height; | ||||
|                     }); | ||||
|             }); | ||||
|  | ||||
|         let icon = new St.Icon({ style_class: 'audio-selection-device-icon', | ||||
|                                  icon_name: this._getDeviceIcon(device) }); | ||||
|         box.add(icon); | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'audio-selection-device-label', | ||||
|                                    text: this._getDeviceLabel(device), | ||||
|                                    x_align: Clutter.ActorAlign.CENTER }); | ||||
|         box.add(label); | ||||
|  | ||||
|         let button = new St.Button({ style_class: 'audio-selection-device', | ||||
|                                      can_focus: true, | ||||
|                                      child: box }); | ||||
|         this._selectionBox.add(button); | ||||
|  | ||||
|         button.connect('clicked', Lang.bind(this, | ||||
|             function() { | ||||
|                 this.emit('device-selected', device); | ||||
|                 this.close(); | ||||
|                 Main.overview.hide(); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _openSettings: function() { | ||||
|         let desktopFile = 'gnome-sound-panel.desktop' | ||||
|         let app = Shell.AppSystem.get_default().lookup_app(desktopFile); | ||||
|  | ||||
|         if (!app) { | ||||
|             log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!'); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this.close(); | ||||
|         Main.overview.hide(); | ||||
|         app.activate(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AudioDeviceSelectionDBus = new Lang.Class({ | ||||
|     Name: 'AudioDeviceSelectionDBus', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._audioSelectionDialog = null; | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection'); | ||||
|  | ||||
|         Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     _onDialogClosed: function() { | ||||
|         this._audioSelectionDialog = null; | ||||
|     }, | ||||
|  | ||||
|     _onDeviceSelected: function(dialog, device) { | ||||
|         let connection = this._dbusImpl.get_connection(); | ||||
|         let info = this._dbusImpl.get_info(); | ||||
|         let deviceName = Object.keys(AudioDevice).filter( | ||||
|             function(dev) { | ||||
|                 return AudioDevice[dev] == device; | ||||
|             })[0].toLowerCase(); | ||||
|         connection.emit_signal(this._audioSelectionDialog._sender, | ||||
|                                this._dbusImpl.get_object_path(), | ||||
|                                info ? info.name : null, | ||||
|                                'DeviceSelected', | ||||
|                                GLib.Variant.new('(s)', [deviceName])); | ||||
|     }, | ||||
|  | ||||
|     OpenAsync: function(params, invocation) { | ||||
|         if (this._audioSelectionDialog) { | ||||
|             invocation.return_value(null); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let [deviceNames] = params; | ||||
|         let devices = 0; | ||||
|         deviceNames.forEach(function(n) { | ||||
|             devices |= AudioDevice[n.toUpperCase()]; | ||||
|         }); | ||||
|  | ||||
|         let dialog; | ||||
|         try { | ||||
|             dialog = new AudioDeviceSelectionDialog(devices); | ||||
|         } catch(e) { | ||||
|             invocation.return_value(null); | ||||
|             return; | ||||
|         } | ||||
|         dialog._sender = invocation.get_sender(); | ||||
|  | ||||
|         dialog.connect('closed', Lang.bind(this, this._onDialogClosed)); | ||||
|         dialog.connect('device-selected', | ||||
|                        Lang.bind(this, this._onDeviceSelected)); | ||||
|         dialog.open(); | ||||
|  | ||||
|         this._audioSelectionDialog = dialog; | ||||
|         invocation.return_value(null); | ||||
|     }, | ||||
|  | ||||
|     CloseAsync: function(params, invocation) { | ||||
|         if (this._audioSelectionDialog && | ||||
|             this._audioSelectionDialog._sender == invocation.get_sender()) | ||||
|             this._audioSelectionDialog.close(); | ||||
|  | ||||
|         invocation.return_value(null); | ||||
|     } | ||||
| }); | ||||
| @@ -102,6 +102,7 @@ const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const Tweener = imports.ui.tweener; | ||||
| @@ -141,9 +142,9 @@ const BackgroundCache = new Lang.Class({ | ||||
|     Name: 'BackgroundCache', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._pendingFileLoads = []; | ||||
|         this._fileMonitors = {}; | ||||
|         this._backgroundSources = {}; | ||||
|         this._animations = {}; | ||||
|     }, | ||||
|  | ||||
|     monitorFile: function(file) { | ||||
| @@ -162,12 +163,14 @@ const BackgroundCache = new Lang.Class({ | ||||
|  | ||||
|     getAnimation: function(params) { | ||||
|         params = Params.parse(params, { file: null, | ||||
|                                         settingsSchema: null, | ||||
|                                         onLoaded: null }); | ||||
|  | ||||
|         if (_fileEqual0(this._animationFile, params.file)) { | ||||
|         let animation = this._animations[params.settingsSchema]; | ||||
|         if (animation && _fileEqual0(animation.file, params.file)) { | ||||
|             if (params.onLoaded) { | ||||
|                 let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                     params.onLoaded(this._animation); | ||||
|                     params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|                 GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -175,15 +178,14 @@ const BackgroundCache = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let animation = new Animation({ file: params.file }); | ||||
|         animation = new Animation({ file: params.file }); | ||||
|  | ||||
|         animation.load(Lang.bind(this, function() { | ||||
|                            this._animationFile = params.file; | ||||
|                            this._animation = animation; | ||||
|                            this._animations[params.settingsSchema] = animation; | ||||
|  | ||||
|                            if (params.onLoaded) { | ||||
|                                let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                                    params.onLoaded(this._animation); | ||||
|                                    params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                                GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -246,6 +248,21 @@ const Background = new Lang.Class({ | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|         this.isLoaded = false; | ||||
|  | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._timezoneChangedId = this._clock.connect('notify::timezone', | ||||
|             Lang.bind(this, function() { | ||||
|                 if (this._animation) | ||||
|                     this._loadAnimation(this._animation.file); | ||||
|             })); | ||||
|  | ||||
|         let loginManager = LoginManager.getLoginManager(); | ||||
|         this._prepareForSleepId = loginManager.connect('prepare-for-sleep', | ||||
|             (lm, aboutToSuspend) => { | ||||
|                 if (aboutToSuspend) | ||||
|                     return; | ||||
|                 this._refreshAnimation(); | ||||
|             }); | ||||
|  | ||||
|         this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() { | ||||
|                                             this.emit('changed'); | ||||
|                                         })); | ||||
| @@ -264,16 +281,30 @@ const Background = new Lang.Class({ | ||||
|         } | ||||
|         this._fileWatches = null; | ||||
|  | ||||
|         if (this._timezoneChangedId != 0) | ||||
|             this._clock.disconnect(this._timezoneChangedId); | ||||
|         this._timezoneChangedId = 0; | ||||
|  | ||||
|         if (this._prepareForSleepId != 0) | ||||
|             LoginManager.getLoginManager().disconnect(this._prepareForSleepId); | ||||
|         this._prepareForSleepId = 0; | ||||
|  | ||||
|         if (this._settingsChangedSignalId != 0) | ||||
|             this._settings.disconnect(this._settingsChangedSignalId); | ||||
|         this._settingsChangedSignalId = 0; | ||||
|     }, | ||||
|  | ||||
|     updateResolution: function() { | ||||
|         if (this._animation) { | ||||
|             this._removeAnimationTimeout(); | ||||
|             this._updateAnimation(); | ||||
|         } | ||||
|         if (this._animation) | ||||
|             this._refreshAnimation(); | ||||
|     }, | ||||
|  | ||||
|     _refreshAnimation: function() { | ||||
|         if (!this._animation) | ||||
|             return; | ||||
|  | ||||
|         this._removeAnimationTimeout(); | ||||
|         this._updateAnimation(); | ||||
|     }, | ||||
|  | ||||
|     _setLoaded: function() { | ||||
| @@ -351,11 +382,9 @@ const Background = new Lang.Class({ | ||||
|  | ||||
|         let cache = Meta.BackgroundImageCache.get_default(); | ||||
|         let numPendingImages = files.length; | ||||
|         let images = []; | ||||
|         for (let i = 0; i < files.length; i++) { | ||||
|             this._watchFile(files[i]); | ||||
|             let image = cache.load(files[i]); | ||||
|             images.push(image); | ||||
|             if (image.is_loaded()) { | ||||
|                 numPendingImages--; | ||||
|                 if (numPendingImages == 0) | ||||
| @@ -403,17 +432,18 @@ const Background = new Lang.Class({ | ||||
|  | ||||
|     _loadAnimation: function(file) { | ||||
|         this._cache.getAnimation({ file: file, | ||||
|                                          onLoaded: Lang.bind(this, function(animation) { | ||||
|                                              this._animation = animation; | ||||
|                                    settingsSchema: this._settings.schema_id, | ||||
|                                    onLoaded: Lang.bind(this, function(animation) { | ||||
|                                        this._animation = animation; | ||||
|  | ||||
|                                              if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                                  this._setLoaded(); | ||||
|                                                  return; | ||||
|                                              } | ||||
|                                        if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                            this._setLoaded(); | ||||
|                                            return; | ||||
|                                        } | ||||
|  | ||||
|                                              this._updateAnimation(); | ||||
|                                              this._watchFile(file); | ||||
|                                          }) | ||||
|                                        this._updateAnimation(); | ||||
|                                        this._watchFile(file); | ||||
|                                    }) | ||||
|                                  }); | ||||
|     }, | ||||
|  | ||||
| @@ -683,6 +713,7 @@ const BackgroundManager = new Lang.Class({ | ||||
|                            time: FADE_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: function() { | ||||
|                                oldBackgroundActor.background.run_dispose(); | ||||
|                                oldBackgroundActor.destroy(); | ||||
|                            } | ||||
|                          }); | ||||
|   | ||||
| @@ -16,9 +16,10 @@ const BackgroundMenu = new Lang.Class({ | ||||
|     _init: function(layoutManager) { | ||||
|         this.parent(layoutManager.dummyCursor, 0, St.Side.TOP); | ||||
|  | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop'); | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|  | ||||
|         this.actor.add_style_class_name('background-menu'); | ||||
|  | ||||
|   | ||||
| @@ -64,7 +64,8 @@ function startAppForMount(app, mount) { | ||||
|  | ||||
|     try { | ||||
|         retval = app.launch(files,  | ||||
|                             global.create_app_launch_context(0, -1)) | ||||
|                             global.create_app_launch_context(0, -1), | ||||
|                             false) | ||||
|     } catch (e) { | ||||
|         log('Unable to launch the application ' + app.get_name() | ||||
|             + ': ' + e.toString()); | ||||
| @@ -313,6 +314,10 @@ const AutorunSource = new Lang.Class({ | ||||
|  | ||||
|     getIcon: function() { | ||||
|         return this.mount.get_icon(); | ||||
|     }, | ||||
|  | ||||
|     _createPolicy: function() { | ||||
|         return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus'); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -325,9 +330,6 @@ const AutorunNotification = new Lang.Class({ | ||||
|  | ||||
|         this._manager = manager; | ||||
|         this._mount = source.mount; | ||||
|  | ||||
|         // set the notification to urgent, so that it expands out | ||||
|         this.setUrgency(MessageTray.Urgency.CRITICAL); | ||||
|     }, | ||||
|  | ||||
|     createBanner: function() { | ||||
|   | ||||
| @@ -9,9 +9,15 @@ const Gio = imports.gi.Gio; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gcr = imports.gi.Gcr; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const KeyringDialog = new Lang.Class({ | ||||
|     Name: 'KeyringDialog', | ||||
| @@ -58,27 +64,47 @@ const KeyringDialog = new Lang.Class({ | ||||
|                             { y_fill:  true, | ||||
|                               y_align: St.Align.START }); | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|         this._controlTable = null; | ||||
|  | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: '', | ||||
|                                               action: Lang.bind(this, this._onCancelButton), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._continueButton = this.addButton({ label: '', | ||||
|                                                 action: Lang.bind(this, this._onContinueButton), | ||||
|                                                 default: true }, | ||||
|                                               { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                                 default: true }); | ||||
|  | ||||
|         this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _buildControlTable: function() { | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         let table = new St.Widget({ style_class: 'keyring-dialog-control-table', | ||||
| @@ -101,15 +127,22 @@ const KeyringDialog = new Lang.Class({ | ||||
|             ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|             this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate)); | ||||
|  | ||||
|             let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|             this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|             this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|             if (rtl) { | ||||
|                 layout.attach(this._passwordEntry, 0, row, 1, 1); | ||||
|                 layout.attach(label, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(label, 2, row, 1, 1); | ||||
|             } else { | ||||
|                 layout.attach(label, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 2, row, 1, 1); | ||||
|             } | ||||
|             row++; | ||||
|         } else { | ||||
|             this._workSpinner = null; | ||||
|             this._passwordEntry = null; | ||||
|         } | ||||
|  | ||||
| @@ -178,7 +211,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|  | ||||
|         this._continueButton.can_focus = sensitive; | ||||
|         this._continueButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _ensureOpen: function() { | ||||
|   | ||||
| @@ -615,6 +615,14 @@ const NetworkAgent = new Lang.Class({ | ||||
|         this._vpnRequests = { }; | ||||
|         this._notifications = { }; | ||||
|  | ||||
|         this._pluginDir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN'])); | ||||
|         try { | ||||
|             let monitor = this._pluginDir.monitor(Gio.FileMonitorFlags.NONE, null); | ||||
|             monitor.connect('changed', () => { this._vpnCacheBuilt = false; }); | ||||
|         } catch(e) { | ||||
|             log('Failed to create monitor for VPN plugin dir: ' + e.message); | ||||
|         } | ||||
|  | ||||
|         this._native.connect('new-request', Lang.bind(this, this._newRequest)); | ||||
|         this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
|  | ||||
| @@ -765,9 +773,8 @@ const NetworkAgent = new Lang.Class({ | ||||
|         this._vpnCacheBuilt = true; | ||||
|         this._vpnBinaries = { }; | ||||
|  | ||||
|         let dir = Gio.file_new_for_path(GLib.build_filenamev([Config.SYSCONFDIR, 'NetworkManager/VPN'])); | ||||
|         try { | ||||
|             let fileEnum = dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null); | ||||
|             let fileEnum = this._pluginDir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null); | ||||
|             let info; | ||||
|  | ||||
|             while ((info = fileEnum.next_file(null))) { | ||||
| @@ -777,7 +784,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|  | ||||
|                 try { | ||||
|                     let keyfile = new GLib.KeyFile(); | ||||
|                     keyfile.load_from_file(dir.get_child(name).get_path(), GLib.KeyFileFlags.NONE); | ||||
|                     keyfile.load_from_file(this._pluginDir.get_child(name).get_path(), GLib.KeyFileFlags.NONE); | ||||
|                     let service = keyfile.get_string('VPN Connection', 'service'); | ||||
|                     let binary = keyfile.get_string('GNOME', 'auth-dialog'); | ||||
|                     let externalUIMode = false; | ||||
| @@ -796,13 +803,21 @@ const NetworkAgent = new Lang.Class({ | ||||
|                         path = GLib.build_filenamev([Config.LIBEXECDIR, path]); | ||||
|                     } | ||||
|  | ||||
|                     if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) | ||||
|                     if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) { | ||||
|                         this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints }; | ||||
|                     else | ||||
|                         try { | ||||
|                             let aliases = keyfile.get_string_list('VPN Connection', 'aliases'); | ||||
|  | ||||
|                             for (let alias of aliases) { | ||||
|                                 this._vpnBinaries[alias] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints }; | ||||
|                             } | ||||
|                         } catch(e) { } // ignore errors if key does not exist | ||||
|                     } else { | ||||
|                         throw new Error('VPN plugin at %s is not executable'.format(path)); | ||||
|                     } | ||||
|                 } catch(e) { | ||||
|                     log('Error \'%s\' while processing VPN keyfile \'%s\''. | ||||
|                         format(e.message, dir.get_child(name).get_path())); | ||||
|                         format(e.message, this._pluginDir.get_child(name).get_path())); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -13,13 +13,19 @@ const Mainloop = imports.mainloop; | ||||
| const Polkit = imports.gi.Polkit; | ||||
| const PolkitAgent = imports.gi.PolkitAgent; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Components = imports.ui.components; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const DIALOG_ICON_SIZE = 48; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const AuthenticationDialog = new Lang.Class({ | ||||
|     Name: 'AuthenticationDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
| @@ -136,6 +142,13 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); | ||||
|         this._passwordBox.add(this._passwordEntry, | ||||
|                               { expand: true }); | ||||
|  | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|         this._passwordBox.add(this._workSpinner.actor); | ||||
|  | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|         this._passwordBox.hide(); | ||||
|  | ||||
| @@ -165,17 +178,10 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: _("Cancel"), | ||||
|                                               action: Lang.bind(this, this.cancel), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._okButton = this.addButton({ label:  _("Authenticate"), | ||||
|                                           action: Lang.bind(this, this._onAuthenticateButtonPressed), | ||||
|                                           default: true }, | ||||
|                                         { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                           default: true }); | ||||
|  | ||||
|         this._doneEmitted = false; | ||||
|  | ||||
| @@ -183,6 +189,30 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._cookie = cookie; | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     performAuthentication: function() { | ||||
|         this.destroySession(); | ||||
|         this._session = new PolkitAgent.Session({ identity: this._identityToAuth, | ||||
| @@ -229,7 +259,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._okButton.can_focus = sensitive; | ||||
|         this._okButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivate: function() { | ||||
| @@ -268,7 +298,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|                  * requested authentication was not gained; this can happen | ||||
|                  * because of an authentication error (like invalid password), | ||||
|                  * for instance. */ | ||||
|                 this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again.")); | ||||
|                 this._errorMessageLabel.set_text(_("Sorry, that didn’t work. Please try again.")); | ||||
|                 this._errorMessageLabel.show(); | ||||
|                 this._infoMessageLabel.hide(); | ||||
|                 this._nullMessageLabel.hide(); | ||||
|   | ||||
| @@ -6,20 +6,28 @@ const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| const Tpl = imports.gi.TelepathyLogger; | ||||
| const Tp = imports.gi.TelepathyGLib; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| var Tpl = null; | ||||
| var Tp = null; | ||||
| try { | ||||
|     Tpl = imports.gi.TelepathyLogger; | ||||
|     Tp = imports.gi.TelepathyGLib; | ||||
| } catch(e) { | ||||
|     log('Telepathy is not available, chat integration will be disabled.'); | ||||
| } | ||||
|  | ||||
| const History = imports.misc.history; | ||||
| const Main = imports.ui.main; | ||||
| const MessageList = imports.ui.messageList; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Params = imports.misc.params; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const HAVE_TP = (Tp != null && Tpl != null); | ||||
|  | ||||
| // See Notification.appendMessage | ||||
| const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes | ||||
| const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes | ||||
| @@ -71,8 +79,43 @@ function makeMessageFromTplEvent(event) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| const TelepathyClient = new Lang.Class({ | ||||
| const TelepathyComponent = new Lang.Class({ | ||||
|     Name: 'TelepathyComponent', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._client = null; | ||||
|  | ||||
|         if (!HAVE_TP) | ||||
|             return; // Telepathy isn't available | ||||
|  | ||||
|         this._client = new TelepathyClient(); | ||||
|     }, | ||||
|  | ||||
|     enable: function() { | ||||
|         if (!this._client) | ||||
|             return; | ||||
|  | ||||
|         try { | ||||
|             this._client.register(); | ||||
|         } catch (e) { | ||||
|             throw new Error('Couldn\'t register Telepathy client. Error: \n' + e); | ||||
|         } | ||||
|  | ||||
|         if (!this._client.account_manager.is_prepared(Tp.AccountManager.get_feature_quark_core())) | ||||
|             this._client.account_manager.prepare_async(null, null); | ||||
|     }, | ||||
|  | ||||
|     disable: function() { | ||||
|         if (!this._client) | ||||
|             return; | ||||
|  | ||||
|         this._client.unregister(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const TelepathyClient = HAVE_TP ? new Lang.Class({ | ||||
|     Name: 'TelepathyClient', | ||||
|     Extends: Tp.BaseClient, | ||||
|  | ||||
|     _init: function() { | ||||
|         // channel path -> ChatSource | ||||
| @@ -97,39 +140,28 @@ const TelepathyClient = new Lang.Class({ | ||||
|         // channel matching its filters is detected. | ||||
|         // The second argument, recover, means _observeChannels will be run | ||||
|         // for any existing channel as well. | ||||
|         this._tpClient = new Shell.TpClient({ name: 'GnomeShell', | ||||
|                                               account_manager: this._accountManager, | ||||
|                                               uniquify_name: true }); | ||||
|         this._tpClient.set_observe_channels_func( | ||||
|             Lang.bind(this, this._observeChannels)); | ||||
|         this._tpClient.set_approve_channels_func( | ||||
|             Lang.bind(this, this._approveChannels)); | ||||
|         this._tpClient.set_handle_channels_func( | ||||
|             Lang.bind(this, this._handleChannels)); | ||||
|         this.parent({ name: 'GnomeShell', | ||||
|                       account_manager: this._accountManager, | ||||
|                       uniquify_name: true }); | ||||
|  | ||||
|         // We only care about single-user text-based chats | ||||
|         let filter = {}; | ||||
|         filter[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT; | ||||
|         filter[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE] = Tp.HandleType.CONTACT; | ||||
|  | ||||
|         this.set_observer_recover(true); | ||||
|         this.add_observer_filter(filter); | ||||
|         this.add_approver_filter(filter); | ||||
|         this.add_handler_filter(filter); | ||||
|  | ||||
|         // Allow other clients (such as Empathy) to pre-empt our channels if | ||||
|         // needed | ||||
|         this._tpClient.set_delegated_channels_callback( | ||||
|         this.set_delegated_channels_callback( | ||||
|             Lang.bind(this, this._delegatedChannelsCb)); | ||||
|     }, | ||||
|  | ||||
|     enable: function() { | ||||
|         try { | ||||
|             this._tpClient.register(); | ||||
|         } catch (e) { | ||||
|             throw new Error('Couldn\'t register Telepathy client. Error: \n' + e); | ||||
|         } | ||||
|  | ||||
|         if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core())) | ||||
|             this._accountManager.prepare_async(null, null); | ||||
|     }, | ||||
|  | ||||
|     disable: function() { | ||||
|         this._tpClient.unregister(); | ||||
|     }, | ||||
|  | ||||
|     _observeChannels: function(observer, account, conn, channels, | ||||
|                                dispatchOp, requests, context) { | ||||
|     vfunc_observe_channels: function(account, conn, channels, | ||||
|                                      dispatchOp, requests, context) { | ||||
|         let len = channels.length; | ||||
|         for (let i = 0; i < len; i++) { | ||||
|             let channel = channels[i]; | ||||
| @@ -153,7 +185,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|         if (this._chatSources[channel.get_object_path()]) | ||||
|             return; | ||||
|  | ||||
|         let source = new ChatSource(account, conn, channel, contact, this._tpClient); | ||||
|         let source = new ChatSource(account, conn, channel, contact, this); | ||||
|  | ||||
|         this._chatSources[channel.get_object_path()] = source; | ||||
|         source.connect('destroy', Lang.bind(this, | ||||
| @@ -162,8 +194,8 @@ const TelepathyClient = new Lang.Class({ | ||||
|                        })); | ||||
|     }, | ||||
|  | ||||
|     _handleChannels: function(handler, account, conn, channels, | ||||
|                               requests, user_action_time, context) { | ||||
|     vfunc_handle_channels: function(account, conn, channels, requests, | ||||
|                                     user_action_time, context) { | ||||
|         this._handlingChannels(account, conn, channels, true); | ||||
|         context.accept(); | ||||
|     }, | ||||
| @@ -193,7 +225,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|             // Telepathy spec states that handlers must foreground channels | ||||
|             // in HandleChannels calls which are already being handled. | ||||
|  | ||||
|             if (notify && this._tpClient.is_handling_channel(channel)) { | ||||
|             if (notify && this.is_handling_channel(channel)) { | ||||
|                 // We are already handling the channel, display the source | ||||
|                 let source = this._chatSources[channel.get_object_path()]; | ||||
|                 if (source) | ||||
| @@ -202,8 +234,8 @@ const TelepathyClient = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _approveChannels: function(approver, account, conn, channels, | ||||
|                                dispatchOp, context) { | ||||
|     vfunc_add_dispatch_operation: function(account, conn, channels, | ||||
|                                            dispatchOp, context) { | ||||
|         let channel = channels[0]; | ||||
|         let chanType = channel.get_channel_type(); | ||||
|  | ||||
| @@ -230,7 +262,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         // Approve private text channels right away as we are going to handle it | ||||
|         dispatchOp.claim_with_async(this._tpClient, Lang.bind(this, function(dispatchOp, result) { | ||||
|         dispatchOp.claim_with_async(this, Lang.bind(this, function(dispatchOp, result) { | ||||
|             try { | ||||
|                 dispatchOp.claim_with_finish(result); | ||||
|                 this._handlingChannels(account, conn, [channel], false); | ||||
| @@ -246,7 +278,7 @@ const TelepathyClient = new Lang.Class({ | ||||
|         // Nothing to do as we don't make a distinction between observed and | ||||
|         // handled channels. | ||||
|     }, | ||||
| }); | ||||
| }) : null; | ||||
|  | ||||
| const ChatSource = new Lang.Class({ | ||||
|     Name: 'ChatSource', | ||||
| @@ -303,6 +335,8 @@ const ChatSource = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _createPolicy: function() { | ||||
|         if (this._account.protocol_name == 'irc') | ||||
|             return new MessageTray.NotificationApplicationPolicy('org.gnome.Polari'); | ||||
|         return new MessageTray.NotificationApplicationPolicy('empathy'); | ||||
|     }, | ||||
|  | ||||
| @@ -467,11 +501,17 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._client.is_handling_channel(this._channel)) { | ||||
|             this._ackMessages(); | ||||
|             // The chat box has been destroyed so it can't | ||||
|             // handle the channel any more. | ||||
|             this._channel.close_async(function(channel, result) { | ||||
|                 channel.close_finish(result); | ||||
|             }); | ||||
|         } else { | ||||
|             // Don't indicate any unread messages when the notification | ||||
|             // that represents them has been destroyed. | ||||
|             this._pendingMessages = []; | ||||
|             this.countUpdated(); | ||||
|         } | ||||
|  | ||||
|         // Keep source alive while the channel is open | ||||
| @@ -652,7 +692,9 @@ const ChatNotification = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         if (message.direction == NotificationDirection.RECEIVED) | ||||
|             this.update(this.source.title, messageBody, { bannerMarkup: true }); | ||||
|             this.update(this.source.title, messageBody, | ||||
|                         { datetime: GLib.DateTime.new_from_unix_local (message.timestamp), | ||||
|                           bannerMarkup: true }); | ||||
|  | ||||
|         let group = (message.direction == NotificationDirection.RECEIVED ? | ||||
|                      'received' : 'sent'); | ||||
| @@ -864,7 +906,7 @@ const ChatNotificationBanner = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _addMessage: function(message) { | ||||
|         let highlighter = new Calendar.URLHighlighter(message.body, true, true); | ||||
|         let highlighter = new MessageList.URLHighlighter(message.body, true, true); | ||||
|         let body = highlighter.actor; | ||||
|  | ||||
|         let styles = message.styles; | ||||
| @@ -954,4 +996,4 @@ const ChatNotificationBanner = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Component = TelepathyClient; | ||||
| const Component = TelepathyComponent; | ||||
|   | ||||
| @@ -259,7 +259,7 @@ const ShowAppsIcon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _createIcon: function(size) { | ||||
|         this._iconActor = new St.Icon({ icon_name: 'view-grid-symbolic', | ||||
|         this._iconActor = new St.Icon({ icon_name: 'view-app-grid-symbolic', | ||||
|                                         icon_size: size, | ||||
|                                         style_class: 'show-apps-icon', | ||||
|                                         track_hover: true }); | ||||
| @@ -644,15 +644,14 @@ const Dash = new Lang.Class({ | ||||
|         let firstIcon = firstButton._delegate.icon; | ||||
|  | ||||
|         let minHeight, natHeight; | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|  | ||||
|         // Enforce the current icon size during the size request | ||||
|         firstIcon.setIconSize(this.iconSize); | ||||
|         firstIcon.icon.ensure_style(); | ||||
|         let [currentWidth, currentHeight] = firstIcon.icon.get_size(); | ||||
|         firstIcon.icon.set_size(this.iconSize * scaleFactor, this.iconSize * scaleFactor); | ||||
|         [minHeight, natHeight] = firstButton.get_preferred_height(-1); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|         firstIcon.icon.set_size(currentWidth, currentHeight); | ||||
|  | ||||
|         // Subtract icon padding and box spacing from the available height | ||||
|         availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + | ||||
| @@ -660,6 +659,10 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|         let availSize = availHeight / iconChildren.length; | ||||
|  | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|  | ||||
|         let newIconSize = baseIconSizes[0]; | ||||
|         for (let i = 0; i < iconSizes.length; i++) { | ||||
|             if (iconSizes[i] < availSize) | ||||
| @@ -753,42 +756,44 @@ const Dash = new Lang.Class({ | ||||
|         let newIndex = 0; | ||||
|         let oldIndex = 0; | ||||
|         while (newIndex < newApps.length || oldIndex < oldApps.length) { | ||||
|             let oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null; | ||||
|             let newApp = newApps.length > newIndex ? newApps[newIndex] : null; | ||||
|  | ||||
|             // No change at oldIndex/newIndex | ||||
|             if (oldApps[oldIndex] == newApps[newIndex]) { | ||||
|             if (oldApp == newApp) { | ||||
|                 oldIndex++; | ||||
|                 newIndex++; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // App removed at oldIndex | ||||
|             if (oldApps[oldIndex] && | ||||
|                 newApps.indexOf(oldApps[oldIndex]) == -1) { | ||||
|             if (oldApp && newApps.indexOf(oldApp) == -1) { | ||||
|                 removedActors.push(children[oldIndex]); | ||||
|                 oldIndex++; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // App added at newIndex | ||||
|             if (newApps[newIndex] && | ||||
|                 oldApps.indexOf(newApps[newIndex]) == -1) { | ||||
|                 addedItems.push({ app: newApps[newIndex], | ||||
|                                   item: this._createAppItem(newApps[newIndex]), | ||||
|             if (newApp && oldApps.indexOf(newApp) == -1) { | ||||
|                 addedItems.push({ app: newApp, | ||||
|                                   item: this._createAppItem(newApp), | ||||
|                                   pos: newIndex }); | ||||
|                 newIndex++; | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // App moved | ||||
|             let insertHere = newApps[newIndex + 1] && | ||||
|                              newApps[newIndex + 1] == oldApps[oldIndex]; | ||||
|             let nextApp = newApps.length > newIndex + 1 ? newApps[newIndex + 1] | ||||
|                                                         : null; | ||||
|             let insertHere = nextApp && nextApp == oldApp; | ||||
|             let alreadyRemoved = removedActors.reduce(function(result, actor) { | ||||
|                 let removedApp = actor.child._delegate.app; | ||||
|                 return result || removedApp == newApps[newIndex]; | ||||
|                 return result || removedApp == newApp; | ||||
|             }, false); | ||||
|  | ||||
|             if (insertHere || alreadyRemoved) { | ||||
|                 let newItem = this._createAppItem(newApps[newIndex]); | ||||
|                 addedItems.push({ app: newApps[newIndex], | ||||
|                 let newItem = this._createAppItem(newApp); | ||||
|                 addedItems.push({ app: newApp, | ||||
|                                   item: newItem, | ||||
|                                   pos: newIndex + removedActors.length }); | ||||
|                 newIndex++; | ||||
|   | ||||
| @@ -8,6 +8,7 @@ const Gtk = imports.gi.Gtk; | ||||
| const GWeather = imports.gi.GWeather; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Cairo = imports.cairo; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -20,6 +21,7 @@ const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Calendar = imports.ui.calendar; | ||||
| const Weather = imports.misc.weather; | ||||
|  | ||||
| function _isToday(date) { | ||||
|     let now = new Date(); | ||||
| @@ -36,7 +38,7 @@ const TodayButton = new Lang.Class({ | ||||
|         // on the current date can be confusing. So don't make the button reactive | ||||
|         // until the selected date changes. | ||||
|         this.actor = new St.Button({ style_class: 'datemenu-today-button', | ||||
|                                      x_align: St.Align.START, | ||||
|                                      x_expand: true, x_align: St.Align.START, | ||||
|                                      can_focus: true, | ||||
|                                      reactive: false | ||||
|                                    }); | ||||
| @@ -77,7 +79,7 @@ const TodayButton = new Lang.Class({ | ||||
|          * below the time in the shell; it should combine the weekday and the | ||||
|          * date, e.g. "Tuesday February 17 2015". | ||||
|          */ | ||||
|         let dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y")); | ||||
|         dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y")); | ||||
|         this.actor.accessible_name = date.toLocaleFormat(dateFormat); | ||||
|     } | ||||
| }); | ||||
| @@ -87,9 +89,7 @@ const WorldClocksSection = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._settings = null; | ||||
|         this._clockNotifyId = 0; | ||||
|         this._changedId = 0; | ||||
|  | ||||
|         this._locations = []; | ||||
|  | ||||
| @@ -98,8 +98,7 @@ const WorldClocksSection = new Lang.Class({ | ||||
|                                      can_focus: true }); | ||||
|         this.actor.connect('clicked', Lang.bind(this, | ||||
|             function() { | ||||
|                 let app = this._getClockApp(); | ||||
|                 app.activate(); | ||||
|                 this._clockAppMon.activateApp(); | ||||
|  | ||||
|                 Main.overview.hide(); | ||||
|                 Main.panel.closeCalendar(); | ||||
| @@ -112,40 +111,25 @@ const WorldClocksSection = new Lang.Class({ | ||||
|  | ||||
|         this.actor.child = this._grid; | ||||
|  | ||||
|         Shell.AppSystem.get_default().connect('installed-changed', | ||||
|                                               Lang.bind(this, this._sync)); | ||||
|         this._clockAppMon = new Util.AppSettingsMonitor('org.gnome.clocks.desktop', | ||||
|                                                         'org.gnome.clocks'); | ||||
|         this._clockAppMon.connect('available-changed', | ||||
|                                   Lang.bind(this, this._sync)); | ||||
|         this._clockAppMon.watchSetting('world-clocks', | ||||
|                                        Lang.bind(this, this._clocksChanged)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _getClockApp: function() { | ||||
|         return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop'); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         this.actor.visible = (this._getClockApp() != null); | ||||
|  | ||||
|         if (this.actor.visible) { | ||||
|             if (!this._settings) { | ||||
|                 this._settings = new Gio.Settings({ schema_id: 'org.gnome.clocks' }); | ||||
|                 this._changedId = | ||||
|                     this._settings.connect('changed::world-clocks', | ||||
|                                            Lang.bind(this, this._clocksChanged)); | ||||
|                 this._clocksChanged(); | ||||
|             } | ||||
|         } else { | ||||
|             if (this._settings) | ||||
|                 this._settings.disconnect(this._changedId); | ||||
|             this._settings = null; | ||||
|             this._changedId = 0; | ||||
|         } | ||||
|         this.actor.visible = this._clockAppMon.available; | ||||
|     }, | ||||
|  | ||||
|     _clocksChanged: function() { | ||||
|     _clocksChanged: function(settings) { | ||||
|         this._grid.destroy_all_children(); | ||||
|         this._locations = []; | ||||
|  | ||||
|         let world = GWeather.Location.get_world(); | ||||
|         let clocks = this._settings.get_value('world-clocks').deep_unpack(); | ||||
|         let clocks = settings.get_value('world-clocks').deep_unpack(); | ||||
|         for (let i = 0; i < clocks.length; i++) { | ||||
|             let l = world.deserialize(clocks[i].location); | ||||
|             this._locations.push({ location: l }); | ||||
| @@ -210,6 +194,136 @@ const WorldClocksSection = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const WeatherSection = new Lang.Class({ | ||||
|     Name: 'WeatherSection', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._weatherClient = new Weather.WeatherClient(); | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'weather-button', | ||||
|                                      x_fill: true, | ||||
|                                      can_focus: true }); | ||||
|         this.actor.connect('clicked', () => { | ||||
|             this._weatherClient.activateApp(); | ||||
|  | ||||
|             Main.overview.hide(); | ||||
|             Main.panel.closeCalendar(); | ||||
|         }); | ||||
|         this.actor.connect('notify::mapped', () => { | ||||
|             if (this.actor.mapped) | ||||
|                 this._weatherClient.update(); | ||||
|         }); | ||||
|  | ||||
|         let box = new St.BoxLayout({ style_class: 'weather-box', | ||||
|                                       vertical: true }); | ||||
|  | ||||
|         this.actor.child = box; | ||||
|  | ||||
|         box.add_child(new St.Label({ style_class: 'weather-header', | ||||
|                                      x_align: Clutter.ActorAlign.START, | ||||
|                                      text: _("Weather") })); | ||||
|  | ||||
|         this._conditionsLabel = new St.Label({ style_class: 'weather-conditions', | ||||
|                                                x_align: Clutter.ActorAlign.START }); | ||||
|         this._conditionsLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._conditionsLabel.clutter_text.line_wrap = true; | ||||
|         box.add_child(this._conditionsLabel); | ||||
|  | ||||
|         this._weatherClient.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _getSummary: function(info) { | ||||
|         let summary = info.get_conditions(); | ||||
|         if (summary == '-') | ||||
|             return info.get_sky(); | ||||
|         return summary; | ||||
|     }, | ||||
|  | ||||
|     _sameSummary: function(info1, info2) { | ||||
|         let [ok1, phenom1, qualifier1] = info1.get_value_conditions(); | ||||
|         let [ok2, phenom2, qualifier2] = info2.get_value_conditions(); | ||||
|         if (ok1 || ok2) | ||||
|             return ok1 == ok2 && phenom1 == phenom2 && qualifier1 == qualifier2; | ||||
|  | ||||
|         let [, sky1] = info1.get_value_sky(); | ||||
|         let [, sky2] = info2.get_value_sky(); | ||||
|         return sky1 == sky2; | ||||
|     }, | ||||
|  | ||||
|     _getSummaryText: function() { | ||||
|         let info = this._weatherClient.info; | ||||
|         let forecasts = info.get_forecast_list(); | ||||
|         if (forecasts.length == 0) // No forecasts, just current conditions | ||||
|             return '%s.'.format(this._getSummary(info)); | ||||
|  | ||||
|         let current = info; | ||||
|         let summaries = [this._getSummary(info)]; | ||||
|         for (let i = 0; i < forecasts.length; i++) { | ||||
|             let [ok, timestamp] = forecasts[i].get_value_update(); | ||||
|             if (!_isToday(new Date(timestamp * 1000))) | ||||
|                 continue; // Ignore forecasts from other days | ||||
|  | ||||
|             if (this._sameSummary(current, forecasts[i])) | ||||
|                 continue; // Ignore consecutive runs of equal summaries | ||||
|  | ||||
|             current = forecasts[i]; | ||||
|             if (summaries.push(this._getSummary(current)) == 3) | ||||
|                 break; // Use a maximum of three summaries | ||||
|         } | ||||
|  | ||||
|         let fmt; | ||||
|         switch(summaries.length) { | ||||
|             /* Translators: %s is a weather condition like "Clear sky"; see | ||||
|                libgweather for the possible condition strings. If at all | ||||
|                possible, the sentence should match the grammatical case etc. of | ||||
|                the inserted conditions. */ | ||||
|             case 1: fmt = _("%s all day."); break; | ||||
|  | ||||
|             /* Translators: %s is a weather condition like "Clear sky"; see | ||||
|                libgweather for the possible condition strings. If at all | ||||
|                possible, the sentence should match the grammatical case etc. of | ||||
|                the inserted conditions. */ | ||||
|             case 2: fmt = _("%s, then %s later."); break; | ||||
|  | ||||
|             /* Translators: %s is a weather condition like "Clear sky"; see | ||||
|                libgweather for the possible condition strings. If at all | ||||
|                possible, the sentence should match the grammatical case etc. of | ||||
|                the inserted conditions. */ | ||||
|             case 3: fmt = _("%s, then %s, followed by %s later."); break; | ||||
|         } | ||||
|         return String.prototype.format.apply(fmt, summaries); | ||||
|     }, | ||||
|  | ||||
|     _getLabelText: function() { | ||||
|         if (!this._weatherClient.hasLocation) | ||||
|             return _("Select a location…"); | ||||
|  | ||||
|         if (this._weatherClient.loading) | ||||
|             return _("Loading…"); | ||||
|  | ||||
|         let info = this._weatherClient.info; | ||||
|         if (info.is_valid()) | ||||
|             return this._getSummaryText() + ' ' + | ||||
|                    /* Translators: %s is a temperature with unit, e.g. "23℃" */ | ||||
|                    _("Feels like %s.").format(info.get_apparent()); | ||||
|  | ||||
|         if (info.network_error()) | ||||
|             return _("Go online for weather information"); | ||||
|  | ||||
|         return _("Weather information is currently unavailable"); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         this.actor.visible = this._weatherClient.available; | ||||
|  | ||||
|         if (!this.actor.visible) | ||||
|             return; | ||||
|  | ||||
|         this._conditionsLabel.text = this._getLabelText(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const MessagesIndicator = new Lang.Class({ | ||||
|     Name: 'MessagesIndicator', | ||||
|  | ||||
| @@ -256,8 +370,7 @@ const IndicatorPad = new Lang.Class({ | ||||
|  | ||||
|     _init: function(actor) { | ||||
|         this._source = actor; | ||||
|         this._source.connect('notify::visible', | ||||
|                              Lang.bind(this, this.queue_relayout)); | ||||
|         this._source.connect('notify::visible', () => { this.queue_relayout(); }); | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
| @@ -297,14 +410,38 @@ const FreezableBinLayout = new Lang.Class({ | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         if (!this._frozen || this._savedWidth.some(isNaN)) | ||||
|             this._savedWidth = this.parent(container, forHeight); | ||||
|             return this.parent(container, forHeight); | ||||
|         return this._savedWidth; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(container, forWidth) { | ||||
|         if (!this._frozen || this._savedHeight.some(isNaN)) | ||||
|             this._savedHeight = this.parent(container, forWidth); | ||||
|             return this.parent(container, forWidth); | ||||
|         return this._savedHeight; | ||||
|     }, | ||||
|  | ||||
|     vfunc_allocate: function(container, allocation, flags) { | ||||
|         this.parent(container, allocation, flags); | ||||
|  | ||||
|         let [width, height] = allocation.get_size(); | ||||
|         this._savedWidth = [width, width]; | ||||
|         this._savedHeight = [height, height]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const CalendarColumnLayout = new Lang.Class({ | ||||
|     Name: 'CalendarColumnLayout', | ||||
|     Extends: Clutter.BoxLayout, | ||||
|  | ||||
|     _init: function(actor) { | ||||
|         this.parent({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         this._calActor = actor; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         if (!this._calActor || this._calActor.get_parent() != container) | ||||
|             return this.parent(container, forHeight); | ||||
|         return this._calActor.get_preferred_width(forHeight); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -337,6 +474,8 @@ const DateMenuButton = new Lang.Class({ | ||||
|  | ||||
|         let layout = new FreezableBinLayout(); | ||||
|         let bin = new St.Widget({ layout_manager: layout }); | ||||
|         // For some minimal compatibility with PopupMenuItem | ||||
|         bin._delegate = this; | ||||
|         this.menu.box.add_child(bin); | ||||
|  | ||||
|         hbox = new St.BoxLayout({ name: 'calendarArea' }); | ||||
| @@ -357,23 +496,23 @@ const DateMenuButton = new Lang.Class({ | ||||
|                 this._date.setDate(now); | ||||
|                 this._messageList.setDate(now); | ||||
|             } | ||||
|             // Block notification banners while the menu is open | ||||
|             Main.messageTray.bannerBlocked = isOpen; | ||||
|         })); | ||||
|  | ||||
|         // Fill up the first column | ||||
|         this._messageList = new Calendar.MessageList(); | ||||
|         this._messageList = new Calendar.CalendarMessageList(); | ||||
|         hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START }); | ||||
|  | ||||
|         // Fill up the second column | ||||
|         vbox = new St.BoxLayout({ style_class: 'datemenu-calendar-column', | ||||
|                                   vertical: true }); | ||||
|         let boxLayout = new CalendarColumnLayout(this._calendar.actor); | ||||
|         vbox = new St.Widget({ style_class: 'datemenu-calendar-column', | ||||
|                                layout_manager: boxLayout }); | ||||
|         boxLayout.hookup_style(vbox); | ||||
|         hbox.add(vbox); | ||||
|  | ||||
|         this._date = new TodayButton(this._calendar); | ||||
|         vbox.add_actor(this._date.actor); | ||||
|  | ||||
|         vbox.add(this._calendar.actor); | ||||
|         vbox.add_actor(this._calendar.actor); | ||||
|  | ||||
|         this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade', | ||||
|                                                     x_expand: true, x_fill: true, | ||||
| @@ -388,6 +527,8 @@ const DateMenuButton = new Lang.Class({ | ||||
|         this._clocksItem = new WorldClocksSection(); | ||||
|         displaysBox.add(this._clocksItem.actor, { x_fill: true }); | ||||
|  | ||||
|         this._weatherItem = new WeatherSection(); | ||||
|         displaysBox.add(this._weatherItem.actor, { x_fill: true }); | ||||
|  | ||||
|         // Done with hbox for calendar and event list | ||||
|  | ||||
|   | ||||
							
								
								
									
										141
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						| @@ -79,9 +79,12 @@ const _Draggable = new Lang.Class({ | ||||
|                                         dragActorOpacity: undefined }); | ||||
|  | ||||
|         this.actor = actor; | ||||
|         if (!params.manualMode) | ||||
|         if (!params.manualMode) { | ||||
|             this.actor.connect('button-press-event', | ||||
|                                Lang.bind(this, this._onButtonPress)); | ||||
|             this.actor.connect('touch-event', | ||||
|                                Lang.bind(this, this._onTouchEvent)); | ||||
|         } | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, function() { | ||||
|             this._actorDestroyed = true; | ||||
| @@ -121,8 +124,50 @@ const _Draggable = new Lang.Class({ | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent: function (actor, event) { | ||||
|         if (event.type() != Clutter.EventType.TOUCH_BEGIN || | ||||
|             !global.display.is_pointer_emulating_sequence(event.get_event_sequence())) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (Tweener.getTweenCount(actor)) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._touchSequence = event.get_event_sequence(); | ||||
|  | ||||
|         this._buttonDown = true; | ||||
|         this._grabActor(); | ||||
|  | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|         this._dragStartX = stageX; | ||||
|         this._dragStartY = stageY; | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _grabDevice: function(actor) { | ||||
|         let manager = Clutter.DeviceManager.get_default(); | ||||
|         let pointer = manager.get_core_device(Clutter.InputDeviceType.POINTER_DEVICE); | ||||
|  | ||||
|         if (pointer && this._touchSequence) | ||||
|             pointer.sequence_grab(this._touchSequence, actor); | ||||
|         else if (pointer) | ||||
|             pointer.grab (actor); | ||||
|  | ||||
|         this._grabbedDevice = pointer; | ||||
|     }, | ||||
|  | ||||
|     _ungrabDevice: function() { | ||||
|         if (this._touchSequence) | ||||
|             this._grabbedDevice.sequence_ungrab (this._touchSequence); | ||||
|         else | ||||
|             this._grabbedDevice.ungrab(); | ||||
|  | ||||
|         this._touchSequence = null; | ||||
|         this._grabbedDevice = null; | ||||
|     }, | ||||
|  | ||||
|     _grabActor: function() { | ||||
|         Clutter.grab_pointer(this.actor); | ||||
|         this._grabDevice(this.actor); | ||||
|         this._onEventId = this.actor.connect('event', | ||||
|                                              Lang.bind(this, this._onEvent)); | ||||
|     }, | ||||
| @@ -131,7 +176,7 @@ const _Draggable = new Lang.Class({ | ||||
|         if (!this._onEventId) | ||||
|             return; | ||||
|  | ||||
|         Clutter.ungrab_pointer(); | ||||
|         this._ungrabDevice(); | ||||
|         this.actor.disconnect(this._onEventId); | ||||
|         this._onEventId = null; | ||||
|     }, | ||||
| @@ -140,13 +185,13 @@ const _Draggable = new Lang.Class({ | ||||
|         if (!this._eventsGrabbed) { | ||||
|             this._eventsGrabbed = Main.pushModal(_getEventHandlerActor()); | ||||
|             if (this._eventsGrabbed) | ||||
|                 Clutter.grab_pointer(_getEventHandlerActor()); | ||||
|                 this._grabDevice(_getEventHandlerActor()); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _ungrabEvents: function() { | ||||
|         if (this._eventsGrabbed) { | ||||
|             Clutter.ungrab_pointer(); | ||||
|             this._ungrabDevice(); | ||||
|             Main.popModal(_getEventHandlerActor()); | ||||
|             this._eventsGrabbed = false; | ||||
|         } | ||||
| @@ -157,7 +202,9 @@ const _Draggable = new Lang.Class({ | ||||
|         // didn't start the drag, to drop the draggable in case the drag was in progress, and | ||||
|         // to complete the drag and ensure that whatever happens to be under the pointer does | ||||
|         // not get triggered if the drag was cancelled with Esc. | ||||
|         if (event.type() == Clutter.EventType.BUTTON_RELEASE) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_RELEASE || | ||||
|             (event.type() == Clutter.EventType.TOUCH_END && | ||||
|              global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) { | ||||
|             this._buttonDown = false; | ||||
|             if (this._dragInProgress) { | ||||
|                 return this._dragActorDropped(event); | ||||
| @@ -172,7 +219,9 @@ const _Draggable = new Lang.Class({ | ||||
|             } | ||||
|         // We intercept MOTION event to figure out if the drag has started and to draw | ||||
|         // this._dragActor under the pointer when dragging is in progress | ||||
|         } else if (event.type() == Clutter.EventType.MOTION) { | ||||
|         } else if (event.type() == Clutter.EventType.MOTION || | ||||
|                    (event.type() == Clutter.EventType.TOUCH_UPDATE && | ||||
|                     global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) { | ||||
|             if (this._dragInProgress) { | ||||
|                 return this._updateDragPosition(event); | ||||
|             } else if (this._dragActor == null) { | ||||
| @@ -214,7 +263,7 @@ const _Draggable = new Lang.Class({ | ||||
|      * This function is useful to call if you've specified manualMode | ||||
|      * for the draggable. | ||||
|      */ | ||||
|     startDrag: function (stageX, stageY, time) { | ||||
|     startDrag: function (stageX, stageY, time, sequence) { | ||||
|         currentDraggable = this; | ||||
|         this._dragInProgress = true; | ||||
|  | ||||
| @@ -228,6 +277,8 @@ const _Draggable = new Lang.Class({ | ||||
|         this.emit('drag-begin', time); | ||||
|         if (this._onEventId) | ||||
|             this._ungrabActor(); | ||||
|  | ||||
|         this._touchSequence = sequence; | ||||
|         this._grabEvents(); | ||||
|         global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); | ||||
|  | ||||
| @@ -338,8 +389,8 @@ const _Draggable = new Lang.Class({ | ||||
|         let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold; | ||||
|         if ((Math.abs(stageX - this._dragStartX) > threshold || | ||||
|              Math.abs(stageY - this._dragStartY) > threshold)) { | ||||
|                 this.startDrag(stageX, stageY, event.get_time()); | ||||
|                 this._updateDragPosition(event); | ||||
|             this.startDrag(stageX, stageY, event.get_time(), this._touchSequence); | ||||
|             this._updateDragPosition(event); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
| @@ -520,20 +571,13 @@ const _Draggable = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._animationInProgress = true; | ||||
|         // No target, so snap back | ||||
|         Tweener.addTween(this._dragActor, | ||||
|                          { x: snapBackX, | ||||
|                            y: snapBackY, | ||||
|                            scale_x: snapBackScale, | ||||
|                            scale_y: snapBackScale, | ||||
|                            opacity: this._dragOrigOpacity, | ||||
|                            time: SNAP_BACK_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: this._onAnimationComplete, | ||||
|                            onCompleteScope: this, | ||||
|                            onCompleteParams: [this._dragActor, eventTime] | ||||
|                          }); | ||||
|         this._animateDragEnd(eventTime, | ||||
|                              { x: snapBackX, | ||||
|                                y: snapBackY, | ||||
|                                scale_x: snapBackScale, | ||||
|                                scale_y: snapBackScale, | ||||
|                                time: SNAP_BACK_ANIMATION_TIME, | ||||
|                              }); | ||||
|     }, | ||||
|  | ||||
|     _restoreDragActor: function(eventTime) { | ||||
| @@ -545,18 +589,44 @@ const _Draggable = new Lang.Class({ | ||||
|         this._dragActor.set_scale(restoreScale, restoreScale); | ||||
|         this._dragActor.opacity = 0; | ||||
|  | ||||
|         this._animateDragEnd(eventTime, | ||||
|                              { time: REVERT_ANIMATION_TIME }); | ||||
|     }, | ||||
|  | ||||
|     _animateDragEnd: function (eventTime, params) { | ||||
|         this._animationInProgress = true; | ||||
|         Tweener.addTween(this._dragActor, | ||||
|                          { opacity: this._dragOrigOpacity, | ||||
|                            time: REVERT_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: this._onAnimationComplete, | ||||
|                            onCompleteScope: this, | ||||
|                            onCompleteParams: [this._dragActor, eventTime] | ||||
|                          }); | ||||
|  | ||||
|         // finish animation if the actor gets destroyed | ||||
|         // during it | ||||
|         this._dragActorDestroyId = | ||||
|             this._dragActor.connect('destroy', | ||||
|                                     Lang.bind(this, this._finishAnimation)); | ||||
|  | ||||
|         params['opacity']          = this._dragOrigOpacity; | ||||
|         params['transition']       = 'easeOutQuad'; | ||||
|         params['onComplete']       = this._onAnimationComplete; | ||||
|         params['onCompleteScope']  = this; | ||||
|         params['onCompleteParams'] = [this._dragActor, eventTime]; | ||||
|  | ||||
|         // start the animation | ||||
|         Tweener.addTween(this._dragActor, params) | ||||
|     }, | ||||
|  | ||||
|     _finishAnimation : function () { | ||||
|         if (!this._animationInProgress) | ||||
|             return | ||||
|  | ||||
|         this._animationInProgress = false; | ||||
|         if (!this._buttonDown) | ||||
|             this._dragComplete(); | ||||
|  | ||||
|         global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|     }, | ||||
|  | ||||
|     _onAnimationComplete : function (dragActor, eventTime) { | ||||
|         dragActor.disconnect(this._dragActorDestroyId); | ||||
|         this._dragActorDestroyId = 0; | ||||
|  | ||||
|         if (this._dragOrigParent) { | ||||
|             Main.uiGroup.remove_child(this._dragActor); | ||||
|             this._dragOrigParent.add_actor(this._dragActor); | ||||
| @@ -565,12 +635,9 @@ const _Draggable = new Lang.Class({ | ||||
|         } else { | ||||
|             dragActor.destroy(); | ||||
|         } | ||||
|         global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|         this.emit('drag-end', eventTime, false); | ||||
|  | ||||
|         this._animationInProgress = false; | ||||
|         if (!this._buttonDown) | ||||
|             this._dragComplete(); | ||||
|         this.emit('drag-end', eventTime, false); | ||||
|         this._finishAnimation(); | ||||
|     }, | ||||
|  | ||||
|     _dragComplete: function() { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ const DRAG_DISTANCE = 80; | ||||
| const EdgeDragAction = new Lang.Class({ | ||||
|     Name: 'EdgeDragAction', | ||||
|     Extends: Clutter.GestureAction, | ||||
|     Signals: { 'activated': {} }, | ||||
|  | ||||
|     _init : function(side, allowedModes) { | ||||
|         this.parent(); | ||||
| @@ -81,4 +82,3 @@ const EdgeDragAction = new Lang.Class({ | ||||
|             this.emit('activated'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(EdgeDragAction.prototype); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
| /* | ||||
|  * Copyright 2010 Red Hat, Inc | ||||
|  * Copyright 2010-2016 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 | ||||
| @@ -114,7 +114,7 @@ const restartDialogContent = { | ||||
|     showOtherSessions: true, | ||||
| }; | ||||
|  | ||||
| const restartInstallDialogContent = { | ||||
| const restartUpdateDialogContent = { | ||||
|  | ||||
|     subject: C_("title", "Restart & Install Updates"), | ||||
|     description: function(seconds) { | ||||
| @@ -132,18 +132,38 @@ const restartInstallDialogContent = { | ||||
|     showOtherSessions: true, | ||||
| }; | ||||
|  | ||||
| const restartUpgradeDialogContent = { | ||||
|  | ||||
|     subject: C_("title", "Restart & Install Upgrade"), | ||||
|     upgradeDescription: function(distroName, distroVersion) { | ||||
|         /* Translators: This is the text displayed for system upgrades in the | ||||
|            shut down dialog. First %s gets replaced with the distro name and | ||||
|            second %s with the distro version to upgrade to */ | ||||
|         return _("%s %s will be installed after restart. Upgrade installation can take a long time: ensure that you have backed up and that the computer is plugged in.").format(distroName, distroVersion); | ||||
|     }, | ||||
|     disableTimer: true, | ||||
|     showBatteryWarning: false, | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart & Install") }], | ||||
|     iconName: 'view-refresh-symbolic', | ||||
|     iconStyleClass: 'end-session-dialog-shutdown-icon', | ||||
|     showOtherSessions: true, | ||||
| }; | ||||
|  | ||||
| const DialogType = { | ||||
|   LOGOUT: 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */, | ||||
|   SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */, | ||||
|   RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */, | ||||
|   UPDATE_RESTART: 3 | ||||
|   UPDATE_RESTART: 3, | ||||
|   UPGRADE_RESTART: 4 | ||||
| }; | ||||
|  | ||||
| const DialogContent = { | ||||
|     0 /* DialogType.LOGOUT */: logoutDialogContent, | ||||
|     1 /* DialogType.SHUTDOWN */: shutdownDialogContent, | ||||
|     2 /* DialogType.RESTART */: restartDialogContent, | ||||
|     3 /* DialogType.UPDATE_RESTART */: restartInstallDialogContent | ||||
|     3 /* DialogType.UPDATE_RESTART */: restartUpdateDialogContent, | ||||
|     4 /* DialogType.UPGRADE_RESTART */: restartUpgradeDialogContent | ||||
| }; | ||||
|  | ||||
| const MAX_USERS_IN_SESSION_DIALOG = 5; | ||||
| @@ -163,7 +183,10 @@ const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface); | ||||
| const PkOfflineIface = '<node> \ | ||||
| <interface name="org.freedesktop.PackageKit.Offline"> \ | ||||
|     <property name="UpdatePrepared" type="b" access="read"/> \ | ||||
|     <property name="TriggerAction" type="s" access="read"/> \ | ||||
|     <property name="UpdateTriggered" type="b" access="read"/> \ | ||||
|     <property name="UpgradePrepared" type="b" access="read"/> \ | ||||
|     <property name="UpgradeTriggered" type="b" access="read"/> \ | ||||
|     <property name="PreparedUpgrade" type="a{sv}" access="read"/> \ | ||||
|     <method name="Trigger"> \ | ||||
|         <arg type="s" name="action" direction="in"/> \ | ||||
|     </method> \ | ||||
| @@ -415,18 +438,25 @@ const EndSessionDialog = new Lang.Class({ | ||||
|  | ||||
|                 if (dialogContent.descriptionWithUser) | ||||
|                     description = dialogContent.descriptionWithUser(realName, displayTime); | ||||
|                 else | ||||
|                     description = dialogContent.description(displayTime); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Use a different description when we are installing a system upgrade | ||||
|         if (dialogContent.upgradeDescription) { | ||||
|             let name = this._pkOfflineProxy.PreparedUpgrade['name'].deep_unpack(); | ||||
|             let version = this._pkOfflineProxy.PreparedUpgrade['version'].deep_unpack(); | ||||
|  | ||||
|             if (name != null && version != null) | ||||
|                 description = dialogContent.upgradeDescription(name, version); | ||||
|         } | ||||
|  | ||||
|         // Fall back to regular description | ||||
|         if (!description) | ||||
|             description = dialogContent.description(displayTime); | ||||
|  | ||||
|         _setLabelText(this._descriptionLabel, description); | ||||
|         _setLabelText(this._subjectLabel, subject); | ||||
|  | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|         if (dialogContent.iconName) { | ||||
|             this._iconBin.child = new St.Icon({ icon_name: dialogContent.iconName, | ||||
|                                                 icon_size: _DIALOG_ICON_SIZE, | ||||
| @@ -698,9 +728,12 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._totalSecondsToStayOpen = totalSecondsToStayOpen; | ||||
|         this._type = type; | ||||
|  | ||||
|         if (this._type == DialogType.RESTART && | ||||
|             this._pkOfflineProxy.TriggerAction == 'reboot') | ||||
|             this._type = DialogType.UPDATE_RESTART; | ||||
|         if (this._type == DialogType.RESTART) { | ||||
|             if (this._pkOfflineProxy.UpdateTriggered) | ||||
|                 this._type = DialogType.UPDATE_RESTART; | ||||
|             else if (this._pkOfflineProxy.UpgradeTriggered) | ||||
|                 this._type = DialogType.UPGRADE_RESTART; | ||||
|         } | ||||
|  | ||||
|         this._applications = []; | ||||
|         this._applicationList.destroy_all_children(); | ||||
| @@ -727,19 +760,19 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         if (dialogContent.showOtherSessions) | ||||
|             this._loadSessions(); | ||||
|  | ||||
|         let updateAlreadyTriggered = this._pkOfflineProxy.TriggerAction == 'power-off' || this._pkOfflineProxy.TriggerAction == 'reboot'; | ||||
|         let updateTriggered = this._pkOfflineProxy.UpdateTriggered; | ||||
|         let updatePrepared = this._pkOfflineProxy.UpdatePrepared; | ||||
|         let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed; | ||||
|  | ||||
|         _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText); | ||||
|         this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed); | ||||
|         this._checkBox.actor.checked = (updatePrepared && updateAlreadyTriggered); | ||||
|         this._checkBox.actor.checked = (updatePrepared && updateTriggered); | ||||
|  | ||||
|         // We show the warning either together with the checkbox, or when | ||||
|         // updates have already been triggered, but the user doesn't have | ||||
|         // enough permissions to cancel them. | ||||
|         this._batteryWarning.visible = (dialogContent.showBatteryWarning && | ||||
|                                         (this._checkBox.actor.visible || updatePrepared && updateAlreadyTriggered && !updatesAllowed)); | ||||
|                                         (this._checkBox.actor.visible || updatePrepared && updateTriggered && !updatesAllowed)); | ||||
|  | ||||
|         this._updateButtons(); | ||||
|  | ||||
| @@ -749,7 +782,9 @@ const EndSessionDialog = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._startTimer(); | ||||
|         if (!dialogContent.disableTimer) | ||||
|             this._startTimer(); | ||||
|  | ||||
|         this._sync(); | ||||
|  | ||||
|         let signalId = this.connect('opened', | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| imports.gi.versions.Clutter = '1.0'; | ||||
| const Config = imports.misc.config; | ||||
|  | ||||
| imports.gi.versions.Clutter = Config.LIBMUTTER_API_VERSION; | ||||
| imports.gi.versions.Gio = '2.0'; | ||||
| imports.gi.versions.Gdk = '3.0'; | ||||
| imports.gi.versions.GdkPixbuf = '2.0'; | ||||
| @@ -61,10 +63,19 @@ function _patchLayoutClass(layoutClass, styleProps) { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| function _makeLoggingFunc(func) { | ||||
|     return function() { | ||||
|         return func([].join.call(arguments, ', ')); | ||||
|     }; | ||||
| function _loggingFunc() { | ||||
|     let fields = {'MESSAGE': [].join.call(arguments, ', ')}; | ||||
|     let domain = "GNOME Shell"; | ||||
|  | ||||
|     // If the caller is an extension, add it as metadata | ||||
|     let extension = imports.misc.extensionUtils.getCurrentExtension(); | ||||
|     if (extension != null) { | ||||
|         domain = extension.metadata.name; | ||||
|         fields['GNOME_SHELL_EXTENSION_UUID'] = extension.uuid; | ||||
|         fields['GNOME_SHELL_EXTENSION_NAME'] = extension.metadata.name; | ||||
|     } | ||||
|  | ||||
|     GLib.log_structured(domain, GLib.LogLevelFlags.LEVEL_MESSAGE, fields); | ||||
| } | ||||
|  | ||||
| function init() { | ||||
| @@ -72,7 +83,7 @@ function init() { | ||||
|     // browser convention of having that namespace be called 'window'.) | ||||
|     window.global = Shell.Global.get(); | ||||
|  | ||||
|     window.log = _makeLoggingFunc(window.log); | ||||
|     window.log = _loggingFunc; | ||||
|  | ||||
|     window._ = Gettext.gettext; | ||||
|     window.C_ = Gettext.pgettext; | ||||
| @@ -81,7 +92,6 @@ function init() { | ||||
|  | ||||
|     // Miscellaneous monkeypatching | ||||
|     _patchContainerClass(St.BoxLayout); | ||||
|     _patchContainerClass(St.Table); | ||||
|  | ||||
|     _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows', | ||||
|                                              column_spacing: 'spacing-columns' }); | ||||
|   | ||||
| @@ -252,7 +252,7 @@ const InstallExtensionDialog = new Lang.Class({ | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             invocation.return_value(GLib.Variant.new('(s)', 'successful')); | ||||
|             invocation.return_value(GLib.Variant.new('(s)', ['successful'])); | ||||
|         } | ||||
|  | ||||
|         _httpSession.queue_message(message, Lang.bind(this, function(session, message) { | ||||
|   | ||||
| @@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect); | ||||
| const disconnect = Lang.bind(_signals, _signals.disconnect); | ||||
|  | ||||
| const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; | ||||
| const DISABLE_USER_EXTENSIONS_KEY = 'disable-user-extensions'; | ||||
| const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation'; | ||||
|  | ||||
| var initted = false; | ||||
| @@ -238,11 +239,16 @@ function initExtension(uuid) { | ||||
| } | ||||
|  | ||||
| function getEnabledExtensions() { | ||||
|     let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); | ||||
|     if (!Array.isArray(Main.sessionMode.enabledExtensions)) | ||||
|     let extensions; | ||||
|     if (Array.isArray(Main.sessionMode.enabledExtensions)) | ||||
|         extensions = Main.sessionMode.enabledExtensions; | ||||
|     else | ||||
|         extensions = []; | ||||
|  | ||||
|     if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY)) | ||||
|         return extensions; | ||||
|  | ||||
|     return Main.sessionMode.enabledExtensions.concat(extensions); | ||||
|     return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY)); | ||||
| } | ||||
|  | ||||
| function onEnabledExtensionsChanged() { | ||||
| @@ -276,18 +282,27 @@ function _onVersionValidationChanged() { | ||||
|     // temporarily disable them all | ||||
|     enabledExtensions = []; | ||||
|     for (let uuid in ExtensionUtils.extensions) | ||||
|         reloadExtension(ExtensionUtils.extensions[uuid]); | ||||
|         try { | ||||
|             reloadExtension(ExtensionUtils.extensions[uuid]); | ||||
|         } catch(e) { | ||||
|             logExtensionError(uuid, e); | ||||
|         } | ||||
|     enabledExtensions = getEnabledExtensions(); | ||||
|  | ||||
|     if (Main.sessionMode.allowExtensions) { | ||||
|         enabledExtensions.forEach(function(uuid) { | ||||
|             enableExtension(uuid); | ||||
|             try { | ||||
|                 enableExtension(uuid); | ||||
|             } catch(e) { | ||||
|                 logExtensionError(uuid, e); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _loadExtensions() { | ||||
|     global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); | ||||
|     global.settings.connect('changed::' + DISABLE_USER_EXTENSIONS_KEY, onEnabledExtensionsChanged); | ||||
|     global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged); | ||||
|  | ||||
|     enabledExtensions = getEnabledExtensions(); | ||||
| @@ -334,7 +349,7 @@ function _sessionUpdated() { | ||||
|     // from allowExtensions in the future | ||||
|     if (Main.sessionMode.allowExtensions) { | ||||
|         if (initted) | ||||
|             onEnabledExtensionsChanged(); | ||||
|             enabledExtensions = getEnabledExtensions(); | ||||
|         enableAllExtensions(); | ||||
|     } else { | ||||
|         disableAllExtensions(); | ||||
|   | ||||
| @@ -19,6 +19,7 @@ const CandidateArea = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ vertical: true, | ||||
|                                         reactive: true, | ||||
|                                         visible: false }); | ||||
|         this._candidateBoxes = []; | ||||
|         for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) { | ||||
| @@ -39,6 +40,19 @@ const CandidateArea = new Lang.Class({ | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         this.actor.connect('scroll-event', Lang.bind(this, function(actor, event) { | ||||
|             let direction = event.get_scroll_direction(); | ||||
|             switch(direction) { | ||||
|             case Clutter.ScrollDirection.UP: | ||||
|                 this.emit('cursor-up'); | ||||
|                 break; | ||||
|             case Clutter.ScrollDirection.DOWN: | ||||
|                 this.emit('cursor-down'); | ||||
|                 break; | ||||
|             }; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' }); | ||||
|  | ||||
|         this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous button' }); | ||||
| @@ -144,6 +158,14 @@ const CandidatePopup = new Lang.Class({ | ||||
|         this._candidateArea.connect('next-page', Lang.bind(this, function() { | ||||
|             this._panelService.page_down(); | ||||
|         })); | ||||
|  | ||||
|         this._candidateArea.connect('cursor-up', Lang.bind(this, function() { | ||||
|             this._panelService.cursor_up(); | ||||
|         })); | ||||
|         this._candidateArea.connect('cursor-down', Lang.bind(this, function() { | ||||
|             this._panelService.cursor_down(); | ||||
|         })); | ||||
|  | ||||
|         this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) { | ||||
|             this._panelService.candidate_clicked(index, button, state); | ||||
|         })); | ||||
| @@ -158,10 +180,22 @@ const CandidatePopup = new Lang.Class({ | ||||
|  | ||||
|         panelService.connect('set-cursor-location', | ||||
|                              Lang.bind(this, function(ps, x, y, w, h) { | ||||
|                                  Main.layoutManager.setDummyCursorGeometry(x, y, w, h); | ||||
|                                  if (this._boxPointer.actor.visible) | ||||
|                                      this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0); | ||||
|                                  this._setDummyCursorGeometry(x, y, w, h); | ||||
|                              })); | ||||
|         try { | ||||
|             panelService.connect('set-cursor-location-relative', | ||||
|                                  Lang.bind(this, function(ps, x, y, w, h) { | ||||
|                                      if (!global.display.focus_window) | ||||
|                                          return; | ||||
|                                      let window = global.display.focus_window.get_compositor_private(); | ||||
|                                      this._setDummyCursorGeometry(window.x + x, window.y + y, w, h); | ||||
|                                  })); | ||||
|         } catch(e) { | ||||
|             // Only recent IBus versions have support for this signal | ||||
|             // which is used for wayland clients. In order to work | ||||
|             // with older IBus versions we can silently ignore the | ||||
|             // signal's absence. | ||||
|         } | ||||
|         panelService.connect('update-preedit-text', | ||||
|                              Lang.bind(this, function(ps, text, cursorPosition, visible) { | ||||
|                                  this._preeditText.visible = visible; | ||||
| @@ -246,6 +280,12 @@ const CandidatePopup = new Lang.Class({ | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     _setDummyCursorGeometry: function(x, y, w, h) { | ||||
|         Main.layoutManager.setDummyCursorGeometry(x, y, w, h); | ||||
|         if (this._boxPointer.actor.visible) | ||||
|             this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let isVisible = (this._preeditText.visible || | ||||
|                          this._auxText.visible || | ||||
|   | ||||
| @@ -10,6 +10,7 @@ const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| const InputSourceManager = imports.ui.status.keyboard; | ||||
|  | ||||
| const BoxPointer = imports.ui.boxpointer; | ||||
| const Layout = imports.ui.layout; | ||||
| @@ -24,9 +25,6 @@ const KEYBOARD_TYPE = 'keyboard-type'; | ||||
| const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEYBOARD = 'screen-keyboard-enabled'; | ||||
|  | ||||
| const CURSOR_BUS_NAME = 'org.gnome.SettingsDaemon.Cursor'; | ||||
| const CURSOR_OBJECT_PATH = '/org/gnome/SettingsDaemon/Cursor'; | ||||
|  | ||||
| const CARIBOU_BUS_NAME = 'org.gnome.Caribou.Daemon'; | ||||
| const CARIBOU_OBJECT_PATH = '/org/gnome/Caribou/Daemon'; | ||||
|  | ||||
| @@ -114,6 +112,35 @@ const Key = new Lang.Class({ | ||||
|                 key.release(); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|         button.connect('touch-event', Lang.bind(this, | ||||
|             function (actor, event) { | ||||
|                 let device = event.get_device(); | ||||
|                 let sequence = event.get_event_sequence(); | ||||
|  | ||||
|                 // We only handle touch events here on wayland. On X11 | ||||
|                 // we do get emulated pointer events, which already works | ||||
|                 // for single-touch cases. Besides, the X11 passive touch grab | ||||
|                 // set up by Mutter will make us see first the touch events | ||||
|                 // and later the pointer events, so it will look like two | ||||
|                 // unrelated series of events, we want to avoid double handling | ||||
|                 // in these cases. | ||||
|                 if (!Meta.is_wayland_compositor()) | ||||
|                     return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|                 if (!this._touchPressed && | ||||
|                     event.type() == Clutter.EventType.TOUCH_BEGIN) { | ||||
|                     device.sequence_grab(sequence, actor); | ||||
|                     this._touchPressed = true; | ||||
|                     key.press(); | ||||
|                 } else if (this._touchPressed && | ||||
|                            event.type() == Clutter.EventType.TOUCH_END && | ||||
|                            device.sequence_get_grabbed_actor(sequence) == actor) { | ||||
|                     device.sequence_ungrab(sequence); | ||||
|                     this._touchPressed = false; | ||||
|                     key.release(); | ||||
|                 } | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
| @@ -178,9 +205,6 @@ const Keyboard = new Lang.Class({ | ||||
|         this._keyboardSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA }); | ||||
|         this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._watchNameId = Gio.bus_watch_name(Gio.BusType.SESSION, CURSOR_BUS_NAME, 0, | ||||
|                                                Lang.bind(this, this._sync), | ||||
|                                                Lang.bind(this, this._sync)); | ||||
|         this._daemonProxy = null; | ||||
|         this._lastDeviceId = null; | ||||
|  | ||||
| @@ -546,7 +570,7 @@ const Keyboard = new Lang.Class({ | ||||
|     shouldTakeEvent: function(event) { | ||||
|         let actor = event.get_source(); | ||||
|         return Main.layoutManager.keyboardBox.contains(actor) || | ||||
|                actor._extended_keys || actor.extended_key; | ||||
|                !!actor._extended_keys || !!actor.extended_key; | ||||
|     }, | ||||
|  | ||||
|     _clearKeyboardRestTimer: function() { | ||||
| @@ -734,19 +758,48 @@ const ShellWaylandAdapter = new Lang.Class({ | ||||
|     Name: 'ShellWaylandAdapter', | ||||
|     Extends: Caribou.XAdapter, | ||||
|  | ||||
|     _init: function () { | ||||
|         this.parent(); | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
|         this._virtualDevice = deviceManager.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); | ||||
|  | ||||
|         this._inputSourceManager = InputSourceManager.getInputSourceManager(); | ||||
|         this._sourceChangedId = this._inputSourceManager.connect('current-source-changed', | ||||
|                                                                  Lang.bind(this, this._onSourceChanged)); | ||||
|         this._sourcesModifiedId = this._inputSourceManager.connect ('sources-changed', | ||||
|                                                                     Lang.bind(this, this._onSourcesModified)); | ||||
|     }, | ||||
|  | ||||
|     _onSourcesModified: function () { | ||||
|         this.emit('config-changed'); | ||||
|     }, | ||||
|  | ||||
|     _onSourceChanged: function (inputSourceManager, oldSource) { | ||||
|         let source = inputSourceManager.currentSource; | ||||
|         this.emit('group-changed', source.index, source.id, ''); | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_groups: function () { | ||||
|         let inputSources = this._inputSourceManager.inputSources; | ||||
|         let groups = [] | ||||
|         let variants = []; | ||||
|  | ||||
|         for (let i in inputSources) { | ||||
|             let is = inputSources[i]; | ||||
|             groups[is.index] = is.id; | ||||
|             variants[is.index] = ''; | ||||
|         } | ||||
|  | ||||
|         return [groups, groups.length, variants, variants.length]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_keyval_press: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             Shell.util_text_insert_keyval(focus, keyval); | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|         this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), | ||||
|                                           keyval, Clutter.KeyState.PRESSED); | ||||
|     }, | ||||
|  | ||||
|     vfunc_keyval_release: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             return;             // do nothing | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|         this._virtualDevice.notify_keyval(Clutter.get_current_event_time(), | ||||
|                                           keyval, Clutter.KeyState.RELEASED); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -220,7 +220,8 @@ const LayoutManager = new Lang.Class({ | ||||
|         global.stage.add_child(this.uiGroup); | ||||
|  | ||||
|         this.overviewGroup = new St.Widget({ name: 'overviewGroup', | ||||
|                                              visible: false }); | ||||
|                                              visible: false, | ||||
|                                              reactive: true }); | ||||
|         this.addChrome(this.overviewGroup); | ||||
|  | ||||
|         this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup', | ||||
| @@ -591,7 +592,10 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.addChrome(this._coverPane); | ||||
|  | ||||
|         if (Meta.is_restart()) { | ||||
|             // On restart, we don't do an animation | ||||
|             // On restart, we don't do an animation. Force an update of the | ||||
|             // regions immediately so that maximized windows restore to the | ||||
|             // right size taking struts into account. | ||||
|             this._updateRegions(); | ||||
|         } else if (Main.sessionMode.isGreeter) { | ||||
|             this.panelBox.translation_y = -this.panelBox.height; | ||||
|         } else { | ||||
| @@ -837,6 +841,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         // need to connect to 'destroy' too. | ||||
|  | ||||
|         this._trackedActors.push(actorData); | ||||
|         this._updateActorVisibility(actorData); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
| @@ -855,25 +860,23 @@ const LayoutManager = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _updateActorVisibility: function(actorData) { | ||||
|         if (!actorData.trackFullscreen) | ||||
|             return; | ||||
|  | ||||
|         let monitor = this.findMonitorForActor(actorData.actor); | ||||
|         actorData.actor.visible = !(global.window_group.visible && | ||||
|                                     monitor && | ||||
|                                     monitor.inFullscreen); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview; | ||||
|  | ||||
|         global.window_group.visible = windowsVisible; | ||||
|         global.top_window_group.visible = windowsVisible; | ||||
|  | ||||
|         for (let i = 0; i < this._trackedActors.length; i++) { | ||||
|             let actorData = this._trackedActors[i], visible; | ||||
|             if (!actorData.trackFullscreen) | ||||
|                 continue; | ||||
|  | ||||
|             if (!windowsVisible) | ||||
|                 visible = true; | ||||
|             else if (this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|                 visible = false; | ||||
|             else | ||||
|                 visible = true; | ||||
|             actorData.actor.visible = visible; | ||||
|         } | ||||
|         this._trackedActors.forEach(Lang.bind(this, this._updateActorVisibility)); | ||||
|     }, | ||||
|  | ||||
|     getWorkAreaForMonitor: function(monitorIndex) { | ||||
| @@ -893,7 +896,10 @@ const LayoutManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     findMonitorForActor: function(actor) { | ||||
|         return this.monitors[this.findIndexForActor(actor)]; | ||||
|         let index = this.findIndexForActor(actor); | ||||
|         if (index >= 0 && index < this.monitors.length) | ||||
|             return this.monitors[index]; | ||||
|         return null; | ||||
|     }, | ||||
|  | ||||
|     _queueUpdateRegions: function() { | ||||
| @@ -939,6 +945,11 @@ const LayoutManager = new Lang.Class({ | ||||
|         if (Main.modalCount > 0) | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|         // Bug workaround - get_transformed_position()/get_transformed_size() don't work after | ||||
|         // a change in stage size until the first pick or paint. | ||||
|         // https://bugzilla.gnome.org/show_bug.cgi?id=761565 | ||||
|         global.stage.get_actor_at_pos(Clutter.PickMode.ALL, 0, 0); | ||||
|  | ||||
|         let rects = [], struts = [], i; | ||||
|         let isPopupMenuVisible = global.top_window_group.get_children().some(isPopupMetaWindow); | ||||
|         let wantsInputRegion = !isPopupMenuVisible; | ||||
| @@ -958,7 +969,11 @@ const LayoutManager = new Lang.Class({ | ||||
|             if (actorData.affectsInputRegion && wantsInputRegion && actorData.actor.get_paint_visibility()) | ||||
|                 rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h })); | ||||
|  | ||||
|             if (actorData.affectsStruts) { | ||||
|             let monitor = null; | ||||
|             if (actorData.affectsStruts) | ||||
|                 monitor = this.findMonitorForActor(actorData.actor); | ||||
|  | ||||
|             if (monitor) { | ||||
|                 // Limit struts to the size of the screen | ||||
|                 let x1 = Math.max(x, 0); | ||||
|                 let x2 = Math.min(x + w, global.screen_width); | ||||
| @@ -975,7 +990,6 @@ const LayoutManager = new Lang.Class({ | ||||
|                 // spans the width/height across the middle of the | ||||
|                 // screen, then we don't create a strut for it at all. | ||||
|  | ||||
|                 let monitor = this.findMonitorForActor(actorData.actor); | ||||
|                 let side; | ||||
|                 if (x1 <= monitor.x && x2 >= monitor.x + monitor.width) { | ||||
|                     if (y1 <= monitor.y) | ||||
| @@ -1316,8 +1330,11 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierLeft: function(barrier, event) { | ||||
|         this._reset(); | ||||
|         this._isTriggered = false; | ||||
|         barrier._isHit = false; | ||||
|         if (this._barriers.every(function(b) { return !b._isHit; })) { | ||||
|             this._reset(); | ||||
|             this._isTriggered = false; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _trigger: function() { | ||||
| @@ -1327,6 +1344,8 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierHit: function(barrier, event) { | ||||
|         barrier._isHit = true; | ||||
|  | ||||
|         // If we've triggered the barrier, wait until the pointer has the | ||||
|         // left the barrier hitbox until we trigger it again. | ||||
|         if (this._isTriggered) | ||||
|   | ||||
| @@ -50,15 +50,14 @@ const LegacyTray = new Lang.Class({ | ||||
|         this._slideLayout.translationX = 0; | ||||
|         this._slideLayout.slideDirection = OverviewControls.SlideDirection.LEFT; | ||||
|  | ||||
|         this._slider = new St.Widget({ style_class: 'legacy-tray', | ||||
|                                        x_expand: true, y_expand: true, | ||||
|         this._slider = new St.Widget({ x_expand: true, y_expand: true, | ||||
|                                        x_align: Clutter.ActorAlign.START, | ||||
|                                        y_align: Clutter.ActorAlign.END, | ||||
|                                        layout_manager: this._slideLayout }); | ||||
|         this.actor.add_actor(this._slider); | ||||
|         this._slider.connect('notify::allocation', Lang.bind(this, this._syncBarrier)); | ||||
|  | ||||
|         this._box = new St.BoxLayout(); | ||||
|         this._box = new St.BoxLayout({ style_class: 'legacy-tray' }); | ||||
|         this._slider.add_actor(this._box); | ||||
|  | ||||
|         this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle', | ||||
|   | ||||
| @@ -33,7 +33,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' + | ||||
|                     'const Mainloop = imports.mainloop; ' + | ||||
|                     'const Meta = imports.gi.Meta; ' + | ||||
|                     'const Shell = imports.gi.Shell; ' + | ||||
|                     'const Tp = imports.gi.TelepathyGLib; ' + | ||||
|                     'const Main = imports.ui.main; ' + | ||||
|                     'const Lang = imports.lang; ' + | ||||
|                     'const Tweener = imports.ui.tweener; ' + | ||||
| @@ -784,6 +783,7 @@ const LookingGlass = new Lang.Class({ | ||||
|  | ||||
|         this._open = false; | ||||
|  | ||||
|         this._it = null; | ||||
|         this._offset = 0; | ||||
|         this._results = []; | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const Atspi = imports.gi.Atspi; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Lang = imports.lang; | ||||
| @@ -22,6 +23,8 @@ const MOUSE_POLL_FREQUENCY = 50; | ||||
| const CROSSHAIRS_CLIP_SIZE = [100, 100]; | ||||
| const NO_CHANGE = 0.0; | ||||
|  | ||||
| const POINTER_REST_TIME = 1000; // milliseconds | ||||
|  | ||||
| // Settings | ||||
| const APPLICATIONS_SCHEMA       = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEY                  = 'screen-magnifier-enabled'; | ||||
| @@ -709,6 +712,9 @@ const ZoomRegion = new Lang.Class({ | ||||
|         this._xCaret = 0; | ||||
|         this._yCaret = 0; | ||||
|  | ||||
|         this._pointerIdleMonitor = Meta.IdleMonitor.get_for_device(Meta.VIRTUAL_CORE_POINTER_ID); | ||||
|         this._scrollContentsTimerId = 0; | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', | ||||
|                                    Lang.bind(this, this._monitorsChanged)); | ||||
|         this._focusCaretTracker.connect('caret-moved', | ||||
| @@ -1068,6 +1074,26 @@ const ZoomRegion = new Lang.Class({ | ||||
|         return this._isMouseOverRegion(); | ||||
|     }, | ||||
|  | ||||
|     _clearScrollContentsTimer: function() { | ||||
|         if (this._scrollContentsTimerId != 0) { | ||||
|             Mainloop.source_remove(this._scrollContentsTimerId); | ||||
|             this._scrollContentsTimerId = 0; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _scrollContentsToDelayed: function(x, y) { | ||||
|         if (this._pointerIdleMonitor.get_idletime() >= POINTER_REST_TIME) { | ||||
|             this.scrollContentsTo(x, y); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._clearScrollContentsTimer(); | ||||
|         this._scrollContentsTimerId = Mainloop.timeout_add(POINTER_REST_TIME, Lang.bind(this, function() { | ||||
|             this._scrollContentsToDelayed(x, y); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * scrollContentsTo: | ||||
|      * Shift the contents of the magnified view such it is centered on the given | ||||
| @@ -1076,6 +1102,8 @@ const ZoomRegion = new Lang.Class({ | ||||
|      * @y:      The y-coord of the point to center on. | ||||
|      */ | ||||
|     scrollContentsTo: function(x, y) { | ||||
|         this._clearScrollContentsTimer(); | ||||
|  | ||||
|         this._followingCursor = false; | ||||
|         this._changeROI({ xCenter: x, | ||||
|                           yCenter: y }); | ||||
| @@ -1381,7 +1409,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED) | ||||
|             [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret); | ||||
|  | ||||
|         this.scrollContentsTo(xCaret, yCaret); | ||||
|         this._scrollContentsToDelayed(xCaret, yCaret); | ||||
|     }, | ||||
|  | ||||
|     _centerFromFocusPosition: function() { | ||||
| @@ -1395,7 +1423,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED) | ||||
|             [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus); | ||||
|  | ||||
|         this.scrollContentsTo(xFocus, yFocus); | ||||
|         this._scrollContentsToDelayed(xFocus, yFocus); | ||||
|     }, | ||||
|  | ||||
|     _centerFromPointPush: function(xPoint, yPoint) { | ||||
|   | ||||
| @@ -11,6 +11,8 @@ const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const AccessDialog = imports.ui.accessDialog; | ||||
| const AudioDeviceSelection = imports.ui.audioDeviceSelection; | ||||
| const Components = imports.ui.components; | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const EndSessionDialog = imports.ui.endSessionDialog; | ||||
| @@ -24,6 +26,7 @@ const ModalDialog = imports.ui.modalDialog; | ||||
| const OsdWindow = imports.ui.osdWindow; | ||||
| const OsdMonitorLabeler = imports.ui.osdMonitorLabeler; | ||||
| const Overview = imports.ui.overview; | ||||
| const PadOsd = imports.ui.padOsd; | ||||
| const Panel = imports.ui.panel; | ||||
| const Params = imports.misc.params; | ||||
| const RunDialog = imports.ui.runDialog; | ||||
| @@ -59,9 +62,12 @@ let screenShield = null; | ||||
| let notificationDaemon = null; | ||||
| let windowAttentionHandler = null; | ||||
| let ctrlAltTabManager = null; | ||||
| let padOsdService = null; | ||||
| let osdWindowManager = null; | ||||
| let osdMonitorLabeler = null; | ||||
| let sessionMode = null; | ||||
| let shellAccessDialogDBusService = null; | ||||
| let shellAudioSelectionDBusService = null; | ||||
| let shellDBusService = null; | ||||
| let shellMountOpDBusService = null; | ||||
| let screenSaverDBus = null; | ||||
| @@ -78,6 +84,7 @@ let _startDate; | ||||
| let _defaultCssStylesheet = null; | ||||
| let _cssStylesheet = null; | ||||
| let _a11ySettings = null; | ||||
| let _themeResource = null; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     if (sessionMode.isPrimary) | ||||
| @@ -119,6 +126,8 @@ function start() { | ||||
|                                        _loadDefaultStylesheet); | ||||
|     _initializeUI(); | ||||
|  | ||||
|     shellAccessDialogDBusService = new AccessDialog.AccessDialogDBus(); | ||||
|     shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus(); | ||||
|     shellDBusService = new ShellDBus.GnomeShell(); | ||||
|     shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); | ||||
|  | ||||
| @@ -137,9 +146,7 @@ function _initializeUI() { | ||||
|     Shell.WindowTracker.get_default(); | ||||
|     Shell.AppUsage.get_default(); | ||||
|  | ||||
|     let resource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource'); | ||||
|     resource._register(); | ||||
|  | ||||
|     reloadThemeResource(); | ||||
|     _loadDefaultStylesheet(); | ||||
|  | ||||
|     // Setup the stage hierarchy early | ||||
| @@ -150,6 +157,7 @@ function _initializeUI() { | ||||
|     // working until it's updated. | ||||
|     uiGroup = layoutManager.uiGroup; | ||||
|  | ||||
|     padOsdService = new PadOsd.PadOsdService(); | ||||
|     screencastService = new Screencast.ScreencastService(); | ||||
|     xdndHandler = new XdndHandler.XdndHandler(); | ||||
|     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); | ||||
| @@ -189,6 +197,8 @@ function _initializeUI() { | ||||
|         return true; | ||||
|     }); | ||||
|  | ||||
|     global.display.connect('gl-video-memory-purged', loadTheme); | ||||
|  | ||||
|     // Provide the bus object for gnome-session to | ||||
|     // initiate logouts. | ||||
|     EndSessionDialog.init(); | ||||
| @@ -290,6 +300,14 @@ function setThemeStylesheet(cssStylesheet) { | ||||
|     _cssStylesheet = cssStylesheet ? Gio.File.new_for_path(cssStylesheet) : null; | ||||
| } | ||||
|  | ||||
| function reloadThemeResource() { | ||||
|     if (_themeResource) | ||||
|         _themeResource._unregister(); | ||||
|  | ||||
|     _themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource'); | ||||
|     _themeResource._register(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * loadTheme: | ||||
|  * | ||||
|   | ||||
							
								
								
									
										713
									
								
								js/ui/messageList.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,713 @@ | ||||
| const Atk = imports.gi.Atk; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const MESSAGE_ANIMATION_TIME = 0.1; | ||||
|  | ||||
| const DEFAULT_EXPAND_LINES = 6; | ||||
|  | ||||
| function _fixMarkup(text, allowMarkup) { | ||||
|     if (allowMarkup) { | ||||
|         // Support &, ", ', < and >, escape all other | ||||
|         // occurrences of '&'. | ||||
|         let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); | ||||
|  | ||||
|         // Support <b>, <i>, and <u>, escape anything else | ||||
|         // so it displays as raw markup. | ||||
|         _text = _text.replace(/<(?!\/?[biu]>)/g, '<'); | ||||
|  | ||||
|         try { | ||||
|             Pango.parse_markup(_text, -1, ''); | ||||
|             return _text; | ||||
|         } catch (e) {} | ||||
|     } | ||||
|  | ||||
|     // !allowMarkup, or invalid markup | ||||
|     return GLib.markup_escape_text(text, -1); | ||||
| } | ||||
|  | ||||
| const URLHighlighter = new Lang.Class({ | ||||
|     Name: 'URLHighlighter', | ||||
|  | ||||
|     _init: function(text, lineWrap, allowMarkup) { | ||||
|         if (!text) | ||||
|             text = ''; | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter', | ||||
|                                     x_expand: true, x_align: Clutter.ActorAlign.START }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', Lang.bind(this, function() { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
|             if (hasColor) { | ||||
|                 let linkColor = color.to_string().substr(0, 7); | ||||
|                 if (linkColor != this._linkColor) { | ||||
|                     this._linkColor = linkColor; | ||||
|                     this._highlightUrls(); | ||||
|                 } | ||||
|             } | ||||
|         })); | ||||
|         this.actor.clutter_text.line_wrap = lineWrap; | ||||
|         this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|  | ||||
|         this.setMarkup(text, allowMarkup); | ||||
|         this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { | ||||
|             // Don't try to URL highlight when invisible. | ||||
|             // The MessageTray doesn't actually hide us, so | ||||
|             // we need to check for paint opacities as well. | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             // Keep Notification.actor from seeing this and taking | ||||
|             // a pointer grab, which would block our button-release-event | ||||
|             // handler, if an URL is clicked | ||||
|             return this._findUrlAtPos(event) != -1; | ||||
|         })); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1) { | ||||
|                 let url = this._urls[urlId].url; | ||||
|                 if (url.indexOf(':') == -1) | ||||
|                     url = 'http://' + url; | ||||
|  | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1)); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1 && !this._cursorChanged) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.POINTING_HAND); | ||||
|                 this._cursorChanged = true; | ||||
|             } else if (urlId == -1) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|                 this._cursorChanged = false; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('leave-event', Lang.bind(this, function() { | ||||
|             if (!this.actor.visible || this.actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             if (this._cursorChanged) { | ||||
|                 this._cursorChanged = false; | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     setMarkup: function(text, allowMarkup) { | ||||
|         text = text ? _fixMarkup(text, allowMarkup) : ''; | ||||
|         this._text = text; | ||||
|  | ||||
|         this.actor.clutter_text.set_markup(text); | ||||
|         /* clutter_text.text contain text without markup */ | ||||
|         this._urls = Util.findUrls(this.actor.clutter_text.text); | ||||
|         this._highlightUrls(); | ||||
|     }, | ||||
|  | ||||
|     _highlightUrls: function() { | ||||
|         // text here contain markup | ||||
|         let urls = Util.findUrls(this._text); | ||||
|         let markup = ''; | ||||
|         let pos = 0; | ||||
|         for (let i = 0; i < urls.length; i++) { | ||||
|             let url = urls[i]; | ||||
|             let str = this._text.substr(pos, url.pos - pos); | ||||
|             markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>'; | ||||
|             pos = url.pos + url.url.length; | ||||
|         } | ||||
|         markup += this._text.substr(pos); | ||||
|         this.actor.clutter_text.set_markup(markup); | ||||
|     }, | ||||
|  | ||||
|     _findUrlAtPos: function(event) { | ||||
|         let success; | ||||
|         let [x, y] = event.get_coords(); | ||||
|         [success, x, y] = this.actor.transform_stage_point(x, y); | ||||
|         let find_pos = -1; | ||||
|         for (let i = 0; i < this.actor.clutter_text.text.length; i++) { | ||||
|             let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i); | ||||
|             if (py > y || py + line_height < y || x < px) | ||||
|                 continue; | ||||
|             find_pos = i; | ||||
|         } | ||||
|         if (find_pos != -1) { | ||||
|             for (let i = 0; i < this._urls.length; i++) | ||||
|             if (find_pos >= this._urls[i].pos && | ||||
|                 this._urls[i].pos + this._urls[i].url.length > find_pos) | ||||
|                 return i; | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ScaleLayout = new Lang.Class({ | ||||
|     Name: 'ScaleLayout', | ||||
|     Extends: Clutter.BinLayout, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         this._container = null; | ||||
|         this.parent(params); | ||||
|     }, | ||||
|  | ||||
|     _connectContainer: function(container) { | ||||
|         if (this._container == container) | ||||
|             return; | ||||
|  | ||||
|         if (this._container) | ||||
|             for (let id of this._signals) | ||||
|                 this._container.disconnect(id); | ||||
|  | ||||
|         this._container = container; | ||||
|         this._signals = []; | ||||
|  | ||||
|         if (this._container) | ||||
|             for (let signal of ['notify::scale-x', 'notify::scale-y']) { | ||||
|                 let id = this._container.connect(signal, Lang.bind(this, | ||||
|                     function() { | ||||
|                         this.layout_changed(); | ||||
|                     })); | ||||
|                 this._signals.push(id); | ||||
|             } | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         this._connectContainer(container); | ||||
|  | ||||
|         let [min, nat] = this.parent(container, forHeight); | ||||
|         return [Math.floor(min * container.scale_x), | ||||
|                 Math.floor(nat * container.scale_x)]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(container, forWidth) { | ||||
|         this._connectContainer(container); | ||||
|  | ||||
|         let [min, nat] = this.parent(container, forWidth); | ||||
|         return [Math.floor(min * container.scale_y), | ||||
|                 Math.floor(nat * container.scale_y)]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const LabelExpanderLayout = new Lang.Class({ | ||||
|     Name: 'LabelExpanderLayout', | ||||
|     Extends: Clutter.LayoutManager, | ||||
|     Properties: { 'expansion': GObject.ParamSpec.double('expansion', | ||||
|                                                         'Expansion', | ||||
|                                                         'Expansion of the layout, between 0 (collapsed) ' + | ||||
|                                                         'and 1 (fully expanded', | ||||
|                                                          GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, | ||||
|                                                          0, 1, 0)}, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         this._expansion = 0; | ||||
|         this._expandLines = DEFAULT_EXPAND_LINES; | ||||
|  | ||||
|         this.parent(params); | ||||
|     }, | ||||
|  | ||||
|     get expansion() { | ||||
|         return this._expansion; | ||||
|     }, | ||||
|  | ||||
|     set expansion(v) { | ||||
|         if (v == this._expansion) | ||||
|             return; | ||||
|         this._expansion = v; | ||||
|         this.notify('expansion'); | ||||
|  | ||||
|         let visibleIndex = this._expansion > 0 ? 1 : 0; | ||||
|         for (let i = 0; this._container && i < this._container.get_n_children(); i++) | ||||
|             this._container.get_child_at_index(i).visible = (i == visibleIndex); | ||||
|  | ||||
|         this.layout_changed(); | ||||
|     }, | ||||
|  | ||||
|     set expandLines(v) { | ||||
|         if (v == this._expandLines) | ||||
|             return; | ||||
|         this._expandLines = v; | ||||
|         if (this._expansion > 0) | ||||
|             this.layout_changed(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_set_container: function(container) { | ||||
|         this._container = container; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         let [min, nat] = [0, 0]; | ||||
|  | ||||
|         for (let i = 0; i < container.get_n_children(); i++) { | ||||
|             if (i > 1) | ||||
|                 break; // we support one unexpanded + one expanded child | ||||
|  | ||||
|             let child = container.get_child_at_index(i); | ||||
|             let [childMin, childNat] = child.get_preferred_width(forHeight); | ||||
|             [min, nat] = [Math.max(min, childMin), Math.max(nat, childNat)]; | ||||
|         } | ||||
|  | ||||
|         return [min, nat]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(container, forWidth) { | ||||
|         let [min, nat] = [0, 0]; | ||||
|  | ||||
|         let children = container.get_children(); | ||||
|         if (children[0]) | ||||
|             [min, nat] = children[0].get_preferred_height(forWidth); | ||||
|  | ||||
|         if (children[1]) { | ||||
|             let [min2, nat2] = children[1].get_preferred_height(forWidth); | ||||
|             let [expMin, expNat] = [Math.min(min2, min * this._expandLines), | ||||
|                                     Math.min(nat2, nat * this._expandLines)]; | ||||
|             [min, nat] = [min + this._expansion * (expMin - min), | ||||
|                           nat + this._expansion * (expNat - nat)]; | ||||
|         } | ||||
|  | ||||
|         return [min, nat]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_allocate: function(container, box, flags) { | ||||
|         for (let i = 0; i < container.get_n_children(); i++) { | ||||
|             let child = container.get_child_at_index(i); | ||||
|  | ||||
|             if (child.visible) | ||||
|                 child.allocate(box, flags); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const Message = new Lang.Class({ | ||||
|     Name: 'Message', | ||||
|  | ||||
|     _init: function(title, body) { | ||||
|         this.expanded = false; | ||||
|  | ||||
|         this._useBodyMarkup = false; | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'message', | ||||
|                                      accessible_role: Atk.Role.NOTIFICATION, | ||||
|                                      can_focus: true, | ||||
|                                      x_expand: true, x_fill: true }); | ||||
|         this.actor.connect('key-press-event', | ||||
|                            Lang.bind(this, this._onKeyPressed)); | ||||
|  | ||||
|         let vbox = new St.BoxLayout({ vertical: true }); | ||||
|         this.actor.set_child(vbox); | ||||
|  | ||||
|         let hbox = new St.BoxLayout(); | ||||
|         vbox.add_actor(hbox); | ||||
|  | ||||
|         this._actionBin = new St.Widget({ layout_manager: new ScaleLayout(), | ||||
|                                           visible: false }); | ||||
|         vbox.add_actor(this._actionBin); | ||||
|  | ||||
|         this._iconBin = new St.Bin({ style_class: 'message-icon-bin', | ||||
|                                      y_expand: true, | ||||
|                                      y_align: St.Align.START, | ||||
|                                      visible: false }); | ||||
|         hbox.add_actor(this._iconBin); | ||||
|  | ||||
|         let contentBox = new St.BoxLayout({ style_class: 'message-content', | ||||
|                                             vertical: true, x_expand: true }); | ||||
|         hbox.add_actor(contentBox); | ||||
|  | ||||
|         this._mediaControls = new St.BoxLayout(); | ||||
|         hbox.add_actor(this._mediaControls); | ||||
|  | ||||
|         let titleBox = new St.BoxLayout(); | ||||
|         contentBox.add_actor(titleBox); | ||||
|  | ||||
|         this.titleLabel = new St.Label({ style_class: 'message-title' }); | ||||
|         this.setTitle(title); | ||||
|         titleBox.add_actor(this.titleLabel); | ||||
|  | ||||
|         this._secondaryBin = new St.Bin({ style_class: 'message-secondary-bin', | ||||
|                                           x_expand: true, y_expand: true, | ||||
|                                           x_fill: true, y_fill: true }); | ||||
|         titleBox.add_actor(this._secondaryBin); | ||||
|  | ||||
|         let closeIcon = new St.Icon({ icon_name: 'window-close-symbolic', | ||||
|                                       icon_size: 16 }); | ||||
|         this._closeButton = new St.Button({ child: closeIcon, opacity: 0 }); | ||||
|         titleBox.add_actor(this._closeButton); | ||||
|  | ||||
|         this._bodyStack = new St.Widget({ x_expand: true }); | ||||
|         this._bodyStack.layout_manager = new LabelExpanderLayout(); | ||||
|         contentBox.add_actor(this._bodyStack); | ||||
|  | ||||
|         this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup); | ||||
|         this.bodyLabel.actor.add_style_class_name('message-body'); | ||||
|         this._bodyStack.add_actor(this.bodyLabel.actor); | ||||
|         this.setBody(body); | ||||
|  | ||||
|         this._closeButton.connect('clicked', Lang.bind(this, this.close)); | ||||
|         this.actor.connect('notify::hover', Lang.bind(this, this._sync)); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this.emit('close'); | ||||
|     }, | ||||
|  | ||||
|     setIcon: function(actor) { | ||||
|         this._iconBin.child = actor; | ||||
|         this._iconBin.visible = (actor != null); | ||||
|     }, | ||||
|  | ||||
|     setSecondaryActor: function(actor) { | ||||
|         this._secondaryBin.child = actor; | ||||
|     }, | ||||
|  | ||||
|     setTitle: function(text) { | ||||
|         let title = text ? _fixMarkup(text.replace(/\n/g, ' '), false) : ''; | ||||
|         this.titleLabel.clutter_text.set_markup(title); | ||||
|     }, | ||||
|  | ||||
|     setBody: function(text) { | ||||
|         this._bodyText = text; | ||||
|         this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '', | ||||
|                                  this._useBodyMarkup); | ||||
|         if (this._expandedLabel) | ||||
|             this._expandedLabel.setMarkup(text, this._useBodyMarkup); | ||||
|     }, | ||||
|  | ||||
|     setUseBodyMarkup: function(enable) { | ||||
|         if (this._useBodyMarkup === enable) | ||||
|             return; | ||||
|         this._useBodyMarkup = enable; | ||||
|         if (this.bodyLabel) | ||||
|             this.setBody(this._bodyText); | ||||
|     }, | ||||
|  | ||||
|     setActionArea: function(actor) { | ||||
|         if (actor == null) { | ||||
|             if (this._actionBin.get_n_children() > 0) | ||||
|                 this._actionBin.get_child_at_index(0).destroy(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this._actionBin.get_n_children() > 0) | ||||
|             throw new Error('Message already has an action area'); | ||||
|  | ||||
|         this._actionBin.add_actor(actor); | ||||
|         this._actionBin.visible = this.expanded; | ||||
|     }, | ||||
|  | ||||
|     addMediaControl: function(iconName, callback) { | ||||
|         let icon = new St.Icon({ icon_name: iconName, icon_size: 16 }); | ||||
|         let button = new St.Button({ style_class: 'message-media-control', | ||||
|                                      child: icon }); | ||||
|         button.connect('clicked', callback); | ||||
|         this._mediaControls.add_actor(button); | ||||
|         return button; | ||||
|     }, | ||||
|  | ||||
|     setExpandedBody: function(actor) { | ||||
|         if (actor == null) { | ||||
|             if (this._bodyStack.get_n_children() > 1) | ||||
|                 this._bodyStack.get_child_at_index(1).destroy(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this._bodyStack.get_n_children() > 1) | ||||
|             throw new Error('Message already has an expanded body actor'); | ||||
|  | ||||
|         this._bodyStack.insert_child_at_index(actor, 1); | ||||
|     }, | ||||
|  | ||||
|     setExpandedLines: function(nLines) { | ||||
|         this._bodyStack.layout_manager.expandLines = nLines; | ||||
|     }, | ||||
|  | ||||
|     expand: function(animate) { | ||||
|         this.expanded = true; | ||||
|  | ||||
|         this._actionBin.visible = (this._actionBin.get_n_children() > 0); | ||||
|  | ||||
|         if (this._bodyStack.get_n_children() < 2) { | ||||
|             this._expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                      true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(this._expandedLabel.actor); | ||||
|         } | ||||
|  | ||||
|         if (animate) { | ||||
|             Tweener.addTween(this._bodyStack.layout_manager, | ||||
|                              { expansion: 1, | ||||
|                                time: MessageTray.ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|             this._actionBin.scale_y = 0; | ||||
|             Tweener.addTween(this._actionBin, | ||||
|                              { scale_y: 1, | ||||
|                                time: MessageTray.ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         } else { | ||||
|             this._bodyStack.layout_manager.expansion = 1; | ||||
|             this._actionBin.scale_y = 1; | ||||
|         } | ||||
|  | ||||
|         this.emit('expanded'); | ||||
|     }, | ||||
|  | ||||
|     unexpand: function(animate) { | ||||
|         if (animate) { | ||||
|             Tweener.addTween(this._bodyStack.layout_manager, | ||||
|                              { expansion: 0, | ||||
|                                time: MessageTray.ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|             Tweener.addTween(this._actionBin, | ||||
|                              { scale_y: 0, | ||||
|                                time: MessageTray.ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    this._actionBin.hide(); | ||||
|                                    this.expanded = false; | ||||
|                                }}); | ||||
|         } else { | ||||
|             this._bodyStack.layout_manager.expansion = 0; | ||||
|             this._actionBin.scale_y = 0; | ||||
|             this.expanded = false; | ||||
|         } | ||||
|  | ||||
|         this.emit('unexpanded'); | ||||
|     }, | ||||
|  | ||||
|     canClose: function() { | ||||
|         return this._mediaControls.get_n_children() == 0; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let visible = this.actor.hover && this.canClose(); | ||||
|         this._closeButton.opacity = visible ? 255 : 0; | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressed: function(a, event) { | ||||
|         let keysym = event.get_key_symbol(); | ||||
|  | ||||
|         if (keysym == Clutter.KEY_Delete || | ||||
|             keysym == Clutter.KEY_KP_Delete) { | ||||
|             this.close(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(Message.prototype); | ||||
|  | ||||
| const MessageListSection = new Lang.Class({ | ||||
|     Name: 'MessageListSection', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.BoxLayout({ style_class: 'message-list-section', | ||||
|                                         clip_to_allocation: true, | ||||
|                                         x_expand: true, vertical: true }); | ||||
|  | ||||
|         this._list = new St.BoxLayout({ style_class: 'message-list-section-list', | ||||
|                                         vertical: true }); | ||||
|         this.actor.add_actor(this._list); | ||||
|  | ||||
|         this._list.connect('actor-added', Lang.bind(this, this._sync)); | ||||
|         this._list.connect('actor-removed', Lang.bind(this, this._sync)); | ||||
|  | ||||
|         let id = Main.sessionMode.connect('updated', | ||||
|                                           Lang.bind(this, this._sync)); | ||||
|         this.actor.connect('destroy', function() { | ||||
|             Main.sessionMode.disconnect(id); | ||||
|         }); | ||||
|  | ||||
|         this._messages = new Map(); | ||||
|         this._date = new Date(); | ||||
|         this.empty = true; | ||||
|         this.canClear = false; | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _onKeyFocusIn: function(actor) { | ||||
|         this.emit('key-focus-in', actor); | ||||
|     }, | ||||
|  | ||||
|     get allowed() { | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     setDate: function(date) { | ||||
|         if (Calendar.sameDay(date, this._date)) | ||||
|             return; | ||||
|         this._date = date; | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     addMessage: function(message, animate) { | ||||
|         this.addMessageAtIndex(message, -1, animate); | ||||
|     }, | ||||
|  | ||||
|     addMessageAtIndex: function(message, index, animate) { | ||||
|         let obj = { | ||||
|             container: null, | ||||
|             destroyId: 0, | ||||
|             keyFocusId: 0, | ||||
|             closeId: 0 | ||||
|         }; | ||||
|         let pivot = new Clutter.Point({ x: .5, y: .5 }); | ||||
|         let scale = animate ? 0 : 1; | ||||
|         obj.container = new St.Widget({ layout_manager: new ScaleLayout(), | ||||
|                                         pivot_point: pivot, | ||||
|                                         scale_x: scale, scale_y: scale }); | ||||
|         obj.keyFocusId = message.actor.connect('key-focus-in', | ||||
|             Lang.bind(this, this._onKeyFocusIn)); | ||||
|         obj.destroyId = message.actor.connect('destroy', | ||||
|             Lang.bind(this, function() { | ||||
|                 this.removeMessage(message, false); | ||||
|             })); | ||||
|         obj.closeId = message.connect('close', | ||||
|             Lang.bind(this, function() { | ||||
|                 this.removeMessage(message, true); | ||||
|             })); | ||||
|  | ||||
|         this._messages.set(message, obj); | ||||
|         obj.container.add_actor(message.actor); | ||||
|  | ||||
|         this._list.insert_child_at_index(obj.container, index); | ||||
|  | ||||
|         if (animate) | ||||
|             Tweener.addTween(obj.container, { scale_x: 1, | ||||
|                                               scale_y: 1, | ||||
|                                               time: MESSAGE_ANIMATION_TIME, | ||||
|                                               transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     moveMessage: function(message, index, animate) { | ||||
|         let obj = this._messages.get(message); | ||||
|  | ||||
|         if (!animate) { | ||||
|             this._list.set_child_at_index(obj.container, index); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let onComplete = Lang.bind(this, function() { | ||||
|             this._list.set_child_at_index(obj.container, index); | ||||
|             Tweener.addTween(obj.container, { scale_x: 1, | ||||
|                                               scale_y: 1, | ||||
|                                               time: MESSAGE_ANIMATION_TIME, | ||||
|                                               transition: 'easeOutQuad' }); | ||||
|         }); | ||||
|         Tweener.addTween(obj.container, { scale_x: 0, | ||||
|                                           scale_y: 0, | ||||
|                                           time: MESSAGE_ANIMATION_TIME, | ||||
|                                           transition: 'easeOutQuad', | ||||
|                                           onComplete: onComplete }); | ||||
|     }, | ||||
|  | ||||
|     removeMessage: function(message, animate) { | ||||
|         let obj = this._messages.get(message); | ||||
|  | ||||
|         message.actor.disconnect(obj.destroyId); | ||||
|         message.actor.disconnect(obj.keyFocusId); | ||||
|         message.disconnect(obj.closeId); | ||||
|  | ||||
|         this._messages.delete(message); | ||||
|  | ||||
|         if (animate) { | ||||
|             Tweener.addTween(obj.container, { scale_x: 0, scale_y: 0, | ||||
|                                               time: MESSAGE_ANIMATION_TIME, | ||||
|                                               transition: 'easeOutQuad', | ||||
|                                               onComplete: function() { | ||||
|                                                   obj.container.destroy(); | ||||
|                                                   global.sync_pointer(); | ||||
|                                               }}); | ||||
|         } else { | ||||
|             obj.container.destroy(); | ||||
|             global.sync_pointer(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         let messages = [...this._messages.keys()].filter(function(message) { | ||||
|             return message.canClose(); | ||||
|         }); | ||||
|  | ||||
|         // If there are few messages, letting them all zoom out looks OK | ||||
|         if (messages.length < 2) { | ||||
|             messages.forEach(function(message) { | ||||
|                 message.close(); | ||||
|             }); | ||||
|         } else { | ||||
|             // Otherwise we slide them out one by one, and then zoom them | ||||
|             // out "off-screen" in the end to smoothly shrink the parent | ||||
|             let delay = MESSAGE_ANIMATION_TIME / Math.max(messages.length, 5); | ||||
|             for (let i = 0; i < messages.length; i++) { | ||||
|                 let message = messages[i]; | ||||
|                 let obj = this._messages.get(message); | ||||
|                 Tweener.addTween(obj.container, | ||||
|                                  { anchor_x: this._list.width, | ||||
|                                    opacity: 0, | ||||
|                                    time: MESSAGE_ANIMATION_TIME, | ||||
|                                    delay: i * delay, | ||||
|                                    transition: 'easeOutQuad', | ||||
|                                    onComplete: function() { | ||||
|                                        message.close(); | ||||
|                                    }}); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _canClear: function() { | ||||
|         for (let message of this._messages.keys()) | ||||
|             if (message.canClose()) | ||||
|                 return true; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let empty = this._list.get_n_children() == 0; | ||||
|         let changed = this.empty !== empty; | ||||
|         this.empty = empty; | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('empty-changed'); | ||||
|  | ||||
|         let canClear = this._canClear(); | ||||
|         changed = this.canClear !== canClear; | ||||
|         this.canClear = canClear; | ||||
|  | ||||
|         if (changed) | ||||
|             this.emit('can-clear-changed'); | ||||
|  | ||||
|         this.actor.visible = this.allowed && this._shouldShow(); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(MessageListSection.prototype); | ||||
| @@ -69,26 +69,6 @@ const Urgency = { | ||||
|     CRITICAL: 3 | ||||
| }; | ||||
|  | ||||
| function _fixMarkup(text, allowMarkup) { | ||||
|     if (allowMarkup) { | ||||
|         // Support &, ", ', < and >, escape all other | ||||
|         // occurrences of '&'. | ||||
|         let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); | ||||
|  | ||||
|         // Support <b>, <i>, and <u>, escape anything else | ||||
|         // so it displays as raw markup. | ||||
|         _text = _text.replace(/<(?!\/?[biu]>)/g, '<'); | ||||
|  | ||||
|         try { | ||||
|             Pango.parse_markup(_text, -1, ''); | ||||
|             return _text; | ||||
|         } catch (e) {} | ||||
|     } | ||||
|  | ||||
|     // !allowMarkup, or invalid markup | ||||
|     return GLib.markup_escape_text(text, -1); | ||||
| } | ||||
|  | ||||
| const FocusGrabber = new Lang.Class({ | ||||
|     Name: 'FocusGrabber', | ||||
|  | ||||
| @@ -258,6 +238,8 @@ const NotificationApplicationPolicy = new Lang.Class({ | ||||
|  | ||||
|     _changed: function(settings, key) { | ||||
|         this.emit('policy-changed', key); | ||||
|         if (key == 'enable') | ||||
|             this.emit('enable-changed'); | ||||
|     }, | ||||
|  | ||||
|     _canonicalizeId: function(id) { | ||||
| @@ -386,6 +368,7 @@ const Notification = new Lang.Class({ | ||||
|                                         secondaryGIcon: null, | ||||
|                                         bannerMarkup: false, | ||||
|                                         clear: false, | ||||
|                                         datetime: null, | ||||
|                                         soundName: null, | ||||
|                                         soundFile: null }); | ||||
|  | ||||
| @@ -393,6 +376,11 @@ const Notification = new Lang.Class({ | ||||
|         this.bannerBodyText = banner; | ||||
|         this.bannerBodyMarkup = params.bannerMarkup; | ||||
|  | ||||
|         if (params.datetime) | ||||
|             this.datetime = params.datetime; | ||||
|         else | ||||
|             this.datetime = GLib.DateTime.new_now_local(); | ||||
|  | ||||
|         if (params.gicon || params.clear) | ||||
|             this.gicon = params.gicon; | ||||
|  | ||||
| @@ -508,6 +496,7 @@ const NotificationBanner = new Lang.Class({ | ||||
|     _init: function(notification) { | ||||
|         this.parent(notification); | ||||
|  | ||||
|         this.actor.can_focus = false; | ||||
|         this.actor.add_style_class_name('notification-banner'); | ||||
|  | ||||
|         this._buttonBox = null; | ||||
| @@ -552,7 +541,8 @@ const NotificationBanner = new Lang.Class({ | ||||
|  | ||||
|     _addSecondaryIcon: function() { | ||||
|         if (this.notification.secondaryGIcon) { | ||||
|             let icon = new St.Icon({ gicon: this.notification.secondaryGIcon }); | ||||
|             let icon = new St.Icon({ gicon: this.notification.secondaryGIcon, | ||||
|                                      x_align: Clutter.ActorAlign.END }); | ||||
|             this.setSecondaryActor(icon); | ||||
|         } | ||||
|     }, | ||||
| @@ -813,7 +803,7 @@ const Source = new Lang.Class({ | ||||
|         notification.acknowledged = false; | ||||
|         this.pushNotification(notification); | ||||
|  | ||||
|         if (this.policy.showBanners) { | ||||
|         if (this.policy.showBanners || notification.urgency == Urgency.CRITICAL) { | ||||
|             this.emit('notify', notification); | ||||
|         } else { | ||||
|             notification.playSound(); | ||||
| @@ -975,6 +965,14 @@ const MessageTray = new Lang.Class({ | ||||
|         Shell.util_set_hidden_from_pick(this.actor, false); | ||||
|     }, | ||||
|  | ||||
|     get bannerAlignment() { | ||||
|         return this._bannerBin.get_x_align(); | ||||
|     }, | ||||
|  | ||||
|     set bannerAlignment(align) { | ||||
|         this._bannerBin.set_x_align(align); | ||||
|     }, | ||||
|  | ||||
|     _onNotificationKeyRelease: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) { | ||||
|             this._expireNotification(); | ||||
| @@ -1233,7 +1231,7 @@ const MessageTray = new Lang.Class({ | ||||
|         if (this._notificationState == State.HIDDEN) { | ||||
|             let nextNotification = this._notificationQueue[0] || null; | ||||
|             if (hasNotifications && nextNotification) { | ||||
|                 let limited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen; | ||||
|                 let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen; | ||||
|                 let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL); | ||||
|                 if (showNextNotification) | ||||
|                     this._showNotification(); | ||||
|   | ||||
| @@ -14,7 +14,6 @@ const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Layout = imports.ui.layout; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| @@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener; | ||||
| const OPEN_AND_CLOSE_TIME = 0.1; | ||||
| const FADE_OUT_DIALOG_TIME = 1.0; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 24; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const State = { | ||||
|     OPENED: 0, | ||||
|     CLOSED: 1, | ||||
| @@ -79,7 +74,9 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.add_actor(this._backgroundBin); | ||||
|  | ||||
|         this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', | ||||
|                                                vertical:    true }); | ||||
|                                                x_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                y_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                vertical:     true }); | ||||
|         // modal dialogs are fixed width and grow vertically; set the request | ||||
|         // mode accordingly so wrapped labels are handled correctly during | ||||
|         // size requests. | ||||
| @@ -100,7 +97,8 @@ const ModalDialog = new Lang.Class({ | ||||
|         this.backgroundStack.add_actor(this.dialogLayout); | ||||
|  | ||||
|  | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true }); | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                 style_class: "modal-dialog-content-box" }); | ||||
|         this.dialogLayout.add(this.contentLayout, | ||||
|                               { expand:  true, | ||||
|                                 x_fill:  true, | ||||
| @@ -108,8 +106,7 @@ const ModalDialog = new Lang.Class({ | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.START }); | ||||
|  | ||||
|         this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', | ||||
|                                                vertical: false }); | ||||
|         this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout ({ homogeneous:true }) }); | ||||
|         this.dialogLayout.add(this.buttonLayout, | ||||
|                               { x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.END }); | ||||
| @@ -118,8 +115,6 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._initialKeyFocus = this.dialogLayout; | ||||
|         this._initialKeyFocusDestroyId = 0; | ||||
|         this._savedKeyFocus = null; | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -147,16 +142,12 @@ const ModalDialog = new Lang.Class({ | ||||
|             else | ||||
|                 x_alignment = St.Align.MIDDLE; | ||||
|  | ||||
|             this.addButton(buttonInfo, { expand: true, | ||||
|                                          x_fill: false, | ||||
|                                          y_fill: false, | ||||
|                                          x_align: x_alignment, | ||||
|                                          y_align: St.Align.MIDDLE }); | ||||
|             this.addButton(buttonInfo); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     addButton: function(buttonInfo, layoutInfo) { | ||||
|         let label = buttonInfo['label']; | ||||
|     addButton: function(buttonInfo) { | ||||
|         let label = buttonInfo['label'] | ||||
|         let action = buttonInfo['action']; | ||||
|         let key = buttonInfo['key']; | ||||
|         let isDefault = buttonInfo['default']; | ||||
| @@ -170,10 +161,12 @@ const ModalDialog = new Lang.Class({ | ||||
|         else | ||||
|             keys = []; | ||||
|  | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-linked-button', | ||||
|                                      button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                      reactive:    true, | ||||
|                                      can_focus:   true, | ||||
|                                      x_expand:    true, | ||||
|                                      y_expand:    true, | ||||
|                                      label:       label }); | ||||
|         button.connect('clicked', action); | ||||
|  | ||||
| @@ -188,47 +181,11 @@ const ModalDialog = new Lang.Class({ | ||||
|         for (let i in keys) | ||||
|             this._buttonKeys[keys[i]] = buttonInfo; | ||||
|  | ||||
|         this.buttonLayout.add(button, layoutInfo); | ||||
|         this.buttonLayout.add_actor(button); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
|  | ||||
|     placeSpinner: function(layoutInfo) { | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|         this._workSpinner.actor.show(); | ||||
|  | ||||
|         this.buttonLayout.add(this._workSpinner.actor, layoutInfo); | ||||
|     }, | ||||
|  | ||||
|     setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function(object, event) { | ||||
|         this._pressedKey = event.get_key_symbol(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
| @@ -302,7 +259,7 @@ const ModalDialog = new Lang.Class({ | ||||
|         if (this.state == State.OPENED || this.state == State.OPENING) | ||||
|             return true; | ||||
|  | ||||
|         if (!this.pushModal({ timestamp: timestamp })) | ||||
|         if (!this.pushModal(timestamp)) | ||||
|             return false; | ||||
|  | ||||
|         this._fadeOpen(onPrimary); | ||||
| @@ -361,8 +318,11 @@ const ModalDialog = new Lang.Class({ | ||||
|     pushModal: function (timestamp) { | ||||
|         if (this._hasModal) | ||||
|             return true; | ||||
|         if (!Main.pushModal(this._group, { timestamp: timestamp, | ||||
|                                            actionMode: this._actionMode })) | ||||
|  | ||||
|         let params = { actionMode: this._actionMode }; | ||||
|         if (timestamp) | ||||
|             params['timestamp'] = timestamp; | ||||
|         if (!Main.pushModal(this._group, params)) | ||||
|             return false; | ||||
|  | ||||
|         this._hasModal = true; | ||||
|   | ||||
							
								
								
									
										287
									
								
								js/ui/mpris.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,287 @@ | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Calendar = imports.ui.calendar; | ||||
| const Main = imports.ui.main; | ||||
| const MessageList = imports.ui.messageList; | ||||
|  | ||||
| const DBusIface = '<node> \ | ||||
| <interface name="org.freedesktop.DBus"> \ | ||||
|   <method name="ListNames"> \ | ||||
|     <arg type="as" direction="out" name="names" /> \ | ||||
|   </method> \ | ||||
|   <signal name="NameOwnerChanged"> \ | ||||
|     <arg type="s" direction="out" name="name" /> \ | ||||
|     <arg type="s" direction="out" name="oldOwner" /> \ | ||||
|     <arg type="s" direction="out" name="newOwner" /> \ | ||||
|   </signal> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
| const DBusProxy = Gio.DBusProxy.makeProxyWrapper(DBusIface); | ||||
|  | ||||
| const MprisIface = '<node> \ | ||||
| <interface name="org.mpris.MediaPlayer2"> \ | ||||
|   <method name="Raise" /> \ | ||||
|   <property name="CanRaise" type="b" access="read" /> \ | ||||
|   <property name="DesktopEntry" type="s" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
| const MprisProxy = Gio.DBusProxy.makeProxyWrapper(MprisIface); | ||||
|  | ||||
| const MprisPlayerIface = '<node> \ | ||||
| <interface name="org.mpris.MediaPlayer2.Player"> \ | ||||
|   <method name="PlayPause" /> \ | ||||
|   <method name="Next" /> \ | ||||
|   <method name="Previous" /> \ | ||||
|   <property name="CanGoNext" type="b" access="read" /> \ | ||||
|   <property name="CanGoPrevious" type="b" access="read" /> \ | ||||
|   <property name="CanPlay" type="b" access="read" /> \ | ||||
|   <property name="Metadata" type="a{sv}" access="read" /> \ | ||||
|   <property name="PlaybackStatus" type="s" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
| const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface); | ||||
|  | ||||
| const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.'; | ||||
|  | ||||
| const MediaMessage = new Lang.Class({ | ||||
|     Name: 'MediaMessage', | ||||
|     Extends: MessageList.Message, | ||||
|  | ||||
|     _init: function(player) { | ||||
|         this._player = player; | ||||
|  | ||||
|         this.parent('', ''); | ||||
|  | ||||
|         this._icon = new St.Icon({ style_class: 'media-message-cover-icon' }); | ||||
|         this.setIcon(this._icon); | ||||
|  | ||||
|         this._prevButton = this.addMediaControl('media-skip-backward-symbolic', | ||||
|             Lang.bind(this, function() { | ||||
|                 this._player.previous(); | ||||
|             })); | ||||
|  | ||||
|         this._playPauseButton = this.addMediaControl(null, | ||||
|             Lang.bind(this, function() { | ||||
|                 this._player.playPause(); | ||||
|             })); | ||||
|  | ||||
|         this._nextButton = this.addMediaControl('media-skip-forward-symbolic', | ||||
|             Lang.bind(this, function() { | ||||
|                 this._player.next(); | ||||
|             })); | ||||
|  | ||||
|         this._player.connect('changed', Lang.bind(this, this._update)); | ||||
|         this._player.connect('closed', Lang.bind(this, this.close)); | ||||
|         this._update(); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
|         this._player.raise(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     }, | ||||
|  | ||||
|     _updateNavButton: function(button, sensitive) { | ||||
|         button.reactive = sensitive; | ||||
|     }, | ||||
|  | ||||
|     _update: function() { | ||||
|         this.setTitle(this._player.trackArtists.join(', ')); | ||||
|         this.setBody(this._player.trackTitle); | ||||
|  | ||||
|         if (this._player.trackCoverUrl) { | ||||
|             let file = Gio.File.new_for_uri(this._player.trackCoverUrl); | ||||
|             this._icon.gicon = new Gio.FileIcon({ file: file }); | ||||
|             this._icon.remove_style_class_name('fallback'); | ||||
|         } else { | ||||
|             this._icon.icon_name = 'audio-x-generic-symbolic'; | ||||
|             this._icon.add_style_class_name('fallback'); | ||||
|         } | ||||
|  | ||||
|         let isPlaying = this._player.status == 'Playing'; | ||||
|         let iconName = isPlaying ? 'media-playback-pause-symbolic' | ||||
|                                  : 'media-playback-start-symbolic'; | ||||
|         this._playPauseButton.child.icon_name = iconName; | ||||
|  | ||||
|         this._updateNavButton(this._prevButton, this._player.canGoPrevious); | ||||
|         this._updateNavButton(this._nextButton, this._player.canGoNext); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const MprisPlayer = new Lang.Class({ | ||||
|     Name: 'MprisPlayer', | ||||
|  | ||||
|     _init: function(busName) { | ||||
|         this._mprisProxy = new MprisProxy(Gio.DBus.session, busName, | ||||
|                                           '/org/mpris/MediaPlayer2', | ||||
|                                           Lang.bind(this, this._onMprisProxyReady)); | ||||
|         this._playerProxy = new MprisPlayerProxy(Gio.DBus.session, busName, | ||||
|                                                  '/org/mpris/MediaPlayer2', | ||||
|                                                  Lang.bind(this, this._onPlayerProxyReady)); | ||||
|  | ||||
|         this._visible = false; | ||||
|         this._trackArtists = []; | ||||
|         this._trackTitle = ''; | ||||
|         this._trackCoverUrl = ''; | ||||
|     }, | ||||
|  | ||||
|     get status() { | ||||
|         return this._playerProxy.PlaybackStatus; | ||||
|     }, | ||||
|  | ||||
|     get trackArtists() { | ||||
|         return this._trackArtists; | ||||
|     }, | ||||
|  | ||||
|     get trackTitle() { | ||||
|         return this._trackTitle; | ||||
|     }, | ||||
|  | ||||
|     get trackCoverUrl() { | ||||
|         return this._trackCoverUrl; | ||||
|     }, | ||||
|  | ||||
|     playPause: function() { | ||||
|         this._playerProxy.PlayPauseRemote(); | ||||
|     }, | ||||
|  | ||||
|     get canGoNext() { | ||||
|         return this._playerProxy.CanGoNext; | ||||
|     }, | ||||
|  | ||||
|     next: function() { | ||||
|         this._playerProxy.NextRemote(); | ||||
|     }, | ||||
|  | ||||
|     get canGoPrevious() { | ||||
|         return this._playerProxy.CanGoPrevious; | ||||
|     }, | ||||
|  | ||||
|     previous: function() { | ||||
|         this._playerProxy.PreviousRemote(); | ||||
|     }, | ||||
|  | ||||
|     raise: function() { | ||||
|         // The remote Raise() method may run into focus stealing prevention, | ||||
|         // so prefer activating the app via .desktop file if possible | ||||
|         let app = null; | ||||
|         if (this._mprisProxy.DesktopEntry) { | ||||
|             let desktopId = this._mprisProxy.DesktopEntry + '.desktop'; | ||||
|             app = Shell.AppSystem.get_default().lookup_app(desktopId); | ||||
|         } | ||||
|  | ||||
|         if (app) | ||||
|             app.activate(); | ||||
|         else if (this._mprisProxy.CanRaise) | ||||
|             this._mprisProxy.RaiseRemote(); | ||||
|     }, | ||||
|  | ||||
|     _close: function() { | ||||
|         this._mprisProxy.disconnect(this._ownerNotifyId); | ||||
|         this._mprisProxy = null; | ||||
|  | ||||
|         this._playerProxy.disconnect(this._propsChangedId); | ||||
|         this._playerProxy = null; | ||||
|  | ||||
|         this.emit('closed'); | ||||
|     }, | ||||
|  | ||||
|     _onMprisProxyReady: function() { | ||||
|         this._ownerNotifyId = this._mprisProxy.connect('notify::g-name-owner', | ||||
|             Lang.bind(this, function() { | ||||
|                 if (!this._mprisProxy.g_name_owner) | ||||
|                     this._close(); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _onPlayerProxyReady: function() { | ||||
|         this._propsChangedId = this._playerProxy.connect('g-properties-changed', | ||||
|                                                          Lang.bind(this, this._updateState)); | ||||
|         this._updateState(); | ||||
|     }, | ||||
|  | ||||
|     _updateState: function() { | ||||
|         let metadata = {}; | ||||
|         for (let prop in this._playerProxy.Metadata) | ||||
|             metadata[prop] = this._playerProxy.Metadata[prop].deep_unpack(); | ||||
|  | ||||
|         this._trackArtists = metadata['xesam:artist'] || [_("Unknown artist")]; | ||||
|         this._trackTitle = metadata['xesam:title'] || _("Unknown title"); | ||||
|         this._trackCoverUrl = metadata['mpris:artUrl'] || ''; | ||||
|         this.emit('changed'); | ||||
|  | ||||
|         let visible = this._playerProxy.CanPlay; | ||||
|  | ||||
|         if (this._visible != visible) { | ||||
|             this._visible = visible; | ||||
|             if (visible) | ||||
|                 this.emit('show'); | ||||
|             else | ||||
|                 this._close(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(MprisPlayer.prototype); | ||||
|  | ||||
| const MediaSection = new Lang.Class({ | ||||
|     Name: 'MediaSection', | ||||
|     Extends: MessageList.MessageListSection, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._players = new Map(); | ||||
|  | ||||
|         this._proxy = new DBusProxy(Gio.DBus.session, | ||||
|                                     'org.freedesktop.DBus', | ||||
|                                     '/org/freedesktop/DBus', | ||||
|                                     Lang.bind(this, this._onProxyReady)); | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty && Calendar.isToday(this._date); | ||||
|     }, | ||||
|  | ||||
|     _addPlayer: function(busName) { | ||||
|         if (this._players.get(busName)) | ||||
|             return; | ||||
|  | ||||
|         let player = new MprisPlayer(busName); | ||||
|         player.connect('closed', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._players.delete(busName); | ||||
|             })); | ||||
|         player.connect('show', Lang.bind(this, | ||||
|             function() { | ||||
|                 let message = new MediaMessage(player); | ||||
|                 this.addMessage(message, true); | ||||
|             })); | ||||
|         this._players.set(busName, player); | ||||
|     }, | ||||
|  | ||||
|     _onProxyReady: function() { | ||||
|         this._proxy.ListNamesRemote(Lang.bind(this, | ||||
|             function([names]) { | ||||
|                 names.forEach(Lang.bind(this, | ||||
|                     function(name) { | ||||
|                         if (!name.startsWith(MPRIS_PLAYER_PREFIX)) | ||||
|                             return; | ||||
|  | ||||
|                         this._addPlayer(name); | ||||
|                     })); | ||||
|             })); | ||||
|         this._proxy.connectSignal('NameOwnerChanged', | ||||
|                                   Lang.bind(this, this._onNameOwnerChanged)); | ||||
|     }, | ||||
|  | ||||
|     _onNameOwnerChanged: function(proxy, sender, [name, oldOwner, newOwner]) { | ||||
|         if (!name.startsWith(MPRIS_PLAYER_PREFIX)) | ||||
|             return; | ||||
|  | ||||
|         if (newOwner && !oldOwner) | ||||
|             this._addPlayer(name); | ||||
|     } | ||||
| }); | ||||
| @@ -128,10 +128,10 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|         switch (hints.urgency) { | ||||
|             case Urgency.LOW: | ||||
|             case Urgency.NORMAL: | ||||
|                 stockIcon = 'gtk-dialog-info'; | ||||
|                 stockIcon = 'dialog-information'; | ||||
|                 break; | ||||
|             case Urgency.CRITICAL: | ||||
|                 stockIcon = 'gtk-dialog-error'; | ||||
|                 stockIcon = 'dialog-error'; | ||||
|                 break; | ||||
|         } | ||||
|         return new Gio.ThemedIcon({ name: stockIcon }); | ||||
| @@ -186,7 +186,8 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|             return source; | ||||
|         } | ||||
|  | ||||
|         let source = new FdoNotificationDaemonSource(title, pid, sender, ndata ? ndata.hints['desktop-entry'] : null); | ||||
|         let appId = ndata ? ndata.hints['desktop-entry'] || null : null; | ||||
|         source = new FdoNotificationDaemonSource(title, pid, sender, appId); | ||||
|  | ||||
|         this._sources.push(source); | ||||
|         source.connect('destroy', Lang.bind(this, function() { | ||||
| @@ -391,10 +392,10 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|                 notification.setUrgency(MessageTray.Urgency.CRITICAL); | ||||
|                 break; | ||||
|         } | ||||
|         notification.setResident(hints.resident == true); | ||||
|         notification.setResident(!!hints.resident); | ||||
|         // '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); | ||||
|         notification.setTransient(!!hints['transient']); | ||||
|  | ||||
|         let sourceGIcon = source.useNotificationIcon ? gicon : null; | ||||
|         source.processNotification(notification, sourceGIcon); | ||||
| @@ -600,7 +601,8 @@ const GtkNotificationDaemonNotification = new Lang.Class({ | ||||
|               "priority": priority, | ||||
|               "buttons": buttons, | ||||
|               "default-action": defaultAction, | ||||
|               "default-action-target": defaultActionTarget } = notification; | ||||
|               "default-action-target": defaultActionTarget, | ||||
|               "timestamp": time } = notification; | ||||
|  | ||||
|         if (priority) { | ||||
|             let urgency = PRIORITY_URGENCY_MAP[priority.unpack()]; | ||||
| @@ -623,7 +625,8 @@ const GtkNotificationDaemonNotification = new Lang.Class({ | ||||
|         this._defaultActionTarget = defaultActionTarget; | ||||
|  | ||||
|         this.update(title.unpack(), body ? body.unpack() : null, | ||||
|                     { gicon: gicon ? Gio.icon_deserialize(gicon) : null }); | ||||
|                     { gicon: gicon ? Gio.icon_deserialize(gicon) : null, | ||||
|                       datetime : time ? GLib.DateTime.new_from_unix_local(time.unpack()) : null }); | ||||
|     }, | ||||
|  | ||||
|     _activateAction: function(namespacedActionId, target) { | ||||
| @@ -690,6 +693,7 @@ const GtkNotificationDaemonAppSource = new Lang.Class({ | ||||
|             throw new InvalidAppError(); | ||||
|  | ||||
|         this._notifications = {}; | ||||
|         this._notificationPending = false; | ||||
|  | ||||
|         this.parent(this._app.get_name()); | ||||
|     }, | ||||
| @@ -702,27 +706,35 @@ const GtkNotificationDaemonAppSource = new Lang.Class({ | ||||
|         return new MessageTray.NotificationApplicationPolicy(this._appId); | ||||
|     }, | ||||
|  | ||||
|     _createApp: function() { | ||||
|         return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath); | ||||
|     _createApp: function(callback) { | ||||
|         return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath, callback); | ||||
|     }, | ||||
|  | ||||
|     activateAction: function(actionId, target) { | ||||
|         let app = this._createApp(); | ||||
|         app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData()); | ||||
|  | ||||
|         this._createApp(function (app, error) { | ||||
|             if (error == null) | ||||
|                 app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData()); | ||||
|             else | ||||
|                 logError(error, 'Failed to activate application proxy'); | ||||
|         }); | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|         let app = this._createApp(); | ||||
|         app.ActivateRemote(getPlatformData()); | ||||
|  | ||||
|         this._createApp(function (app, error) { | ||||
|             if (error == null) | ||||
|                 app.ActivateRemote(getPlatformData()); | ||||
|             else | ||||
|                 logError(error, 'Failed to open application proxy'); | ||||
|         }); | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|     }, | ||||
|  | ||||
|     addNotification: function(notificationId, notificationParams, showBanner) { | ||||
|         this._notificationPending = true; | ||||
|  | ||||
|         if (this._notifications[notificationId]) | ||||
|             this._notifications[notificationId].destroy(); | ||||
|  | ||||
| @@ -736,6 +748,14 @@ const GtkNotificationDaemonAppSource = new Lang.Class({ | ||||
|             this.notify(notification); | ||||
|         else | ||||
|             this.pushNotification(notification); | ||||
|  | ||||
|         this._notificationPending = false; | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._notificationPending) | ||||
|             return; | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
|     removeNotification: function(notificationId) { | ||||
| @@ -847,6 +867,9 @@ const GtkNotificationDaemon = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let timestamp = GLib.DateTime.new_now_local().to_unix(); | ||||
|         notification['timestamp'] = new GLib.Variant('x', timestamp); | ||||
|  | ||||
|         source.addNotification(notificationId, notification, true); | ||||
|  | ||||
|         invocation.return_value(null); | ||||
|   | ||||
| @@ -15,8 +15,7 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     Name: 'OsdMonitorLabel', | ||||
|  | ||||
|     _init: function(monitor, label) { | ||||
|         this._actor = new St.Widget({ opacity: 0, | ||||
|                                       x_expand: true, | ||||
|         this._actor = new St.Widget({ x_expand: true, | ||||
|                                       y_expand: true }); | ||||
|  | ||||
|         this._monitor = monitor; | ||||
| @@ -34,10 +33,6 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|         this._position(); | ||||
|  | ||||
|         Meta.disable_unredirect_for_screen(global.screen); | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 255, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     _position: function() { | ||||
| @@ -52,15 +47,8 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 0, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function() { | ||||
|                                this._actor.destroy(); | ||||
|                                Meta.enable_unredirect_for_screen(global.screen); | ||||
|                            }) | ||||
|                          }); | ||||
|         this._actor.destroy(); | ||||
|         Meta.enable_unredirect_for_screen(global.screen); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -22,11 +22,13 @@ const LevelBar = new Lang.Class({ | ||||
|         this._level = 0; | ||||
|  | ||||
|         this.actor = new St.Bin({ style_class: 'level', | ||||
|                                   x_fill: true, y_fill: true }); | ||||
|         this._bar = new St.DrawingArea(); | ||||
|         this._bar.connect('repaint', Lang.bind(this, this._repaint)); | ||||
|                                   x_align: St.Align.START, | ||||
|                                   y_fill: true }); | ||||
|         this._bar = new St.Widget({ style_class: 'level-bar' }); | ||||
|  | ||||
|         this.actor.set_child(this._bar); | ||||
|  | ||||
|         this.actor.connect('notify::width', () => { this.level = this.level; }); | ||||
|     }, | ||||
|  | ||||
|     get level() { | ||||
| @@ -34,39 +36,44 @@ const LevelBar = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     set level(value) { | ||||
|         let newValue = Math.max(0, Math.min(value, 100)); | ||||
|         if (newValue == this._level) | ||||
|             return; | ||||
|         this._level = newValue; | ||||
|         this._bar.queue_repaint(); | ||||
|         this._level = Math.max(0, Math.min(value, 100)); | ||||
|  | ||||
|         let alloc = this.actor.get_allocation_box(); | ||||
|         let newWidth = Math.round((alloc.x2 - alloc.x1) * this._level / 100); | ||||
|         if (newWidth != this._bar.width) | ||||
|             this._bar.width = newWidth; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const OsdWindowConstraint = new Lang.Class({ | ||||
|     Name: 'OsdWindowConstraint', | ||||
|     Extends: Clutter.Constraint, | ||||
|  | ||||
|     _init: function(props) { | ||||
|         this._minSize = 0; | ||||
|         this.parent(props); | ||||
|     }, | ||||
|  | ||||
|     _repaint: function() { | ||||
|         let cr = this._bar.get_context(); | ||||
|     set minSize(v) { | ||||
|         this._minSize = v; | ||||
|         if (this.actor) | ||||
|             this.actor.queue_relayout(); | ||||
|     }, | ||||
|  | ||||
|         let node = this.actor.get_theme_node(); | ||||
|         let radius = node.get_border_radius(0); // assume same radius for all corners | ||||
|         Clutter.cairo_set_source_color(cr, node.get_foreground_color()); | ||||
|     vfunc_update_allocation: function(actor, actorBox) { | ||||
|         // Clutter will adjust the allocation for margins, | ||||
|         // so add it to our minimum size | ||||
|         let minSize = this._minSize + actor.margin_top + actor.margin_bottom; | ||||
|         let [width, height] = actorBox.get_size(); | ||||
|  | ||||
|         let [w, h] = this._bar.get_surface_size(); | ||||
|         w *= (this._level / 100.); | ||||
|         // Enforce a ratio of 1 | ||||
|         let size = Math.ceil(Math.max(minSize, height)); | ||||
|         actorBox.set_size(size, size); | ||||
|  | ||||
|         if (w == 0) | ||||
|             return; | ||||
|  | ||||
|         cr.moveTo(radius, 0); | ||||
|         if (w >= radius) | ||||
|             cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI); | ||||
|         else | ||||
|             cr.lineTo(w, 0); | ||||
|         if (w >= radius) | ||||
|             cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI); | ||||
|         else | ||||
|             cr.lineTo(w, h); | ||||
|         cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI); | ||||
|         cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI); | ||||
|         cr.fill(); | ||||
|         cr.$dispose(); | ||||
|         // Recenter | ||||
|         let [x, y] = actorBox.get_origin(); | ||||
|         actorBox.set_origin(Math.ceil(x + width / 2 - size / 2), | ||||
|                             Math.ceil(y + height / 2 - size / 2)); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -74,7 +81,6 @@ const OsdWindow = new Lang.Class({ | ||||
|     Name: 'OsdWindow', | ||||
|  | ||||
|     _init: function(monitorIndex) { | ||||
|         this._popupSize = 0; | ||||
|         this.actor = new St.Widget({ x_expand: true, | ||||
|                                      y_expand: true, | ||||
|                                      x_align: Clutter.ActorAlign.CENTER, | ||||
| @@ -84,19 +90,12 @@ const OsdWindow = new Lang.Class({ | ||||
|         let constraint = new Layout.MonitorConstraint({ index: monitorIndex }); | ||||
|         this.actor.add_constraint(constraint); | ||||
|  | ||||
|         this._boxConstraint = new OsdWindowConstraint(); | ||||
|         this._box = new St.BoxLayout({ style_class: 'osd-window', | ||||
|                                        vertical: true }); | ||||
|         this._box.add_constraint(this._boxConstraint); | ||||
|         this.actor.add_actor(this._box); | ||||
|  | ||||
|         this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged)); | ||||
|         this._box.connect('notify::height', Lang.bind(this, | ||||
|             function() { | ||||
|                 Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, | ||||
|                     function() { | ||||
|                         this._box.width = this._box.height; | ||||
|                     })); | ||||
|             })); | ||||
|  | ||||
|         this._icon = new St.Icon(); | ||||
|         this._box.add(this._icon, { expand: true }); | ||||
|  | ||||
| @@ -110,8 +109,11 @@ const OsdWindow = new Lang.Class({ | ||||
|         this._reset(); | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', | ||||
|                                    Lang.bind(this, this._monitorsChanged)); | ||||
|         this._monitorsChanged(); | ||||
|                                    Lang.bind(this, this._relayout)); | ||||
|         let themeContext = St.ThemeContext.get_for_stage(global.stage); | ||||
|         themeContext.connect('notify::scale-factor', | ||||
|                              Lang.bind(this, this._relayout)); | ||||
|         this._relayout(); | ||||
|         Main.uiGroup.add_child(this.actor); | ||||
|     }, | ||||
|  | ||||
| @@ -189,7 +191,7 @@ const OsdWindow = new Lang.Class({ | ||||
|         this.setLevel(null); | ||||
|     }, | ||||
|  | ||||
|     _monitorsChanged: function() { | ||||
|     _relayout: function() { | ||||
|         /* assume 110x110 on a 640x480 display and scale from there */ | ||||
|         let monitor = Main.layoutManager.monitors[this._monitorIndex]; | ||||
|         if (!monitor) | ||||
| @@ -198,30 +200,12 @@ const OsdWindow = new Lang.Class({ | ||||
|         let scalew = monitor.width / 640.0; | ||||
|         let scaleh = monitor.height / 480.0; | ||||
|         let scale = Math.min(scalew, scaleh); | ||||
|         this._popupSize = 110 * Math.max(1, scale); | ||||
|         let popupSize = 110 * Math.max(1, scale); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._icon.icon_size = this._popupSize / (2 * scaleFactor); | ||||
|         this._icon.icon_size = popupSize / (2 * scaleFactor); | ||||
|         this._box.translation_y = monitor.height / 4; | ||||
|         this._box.style_changed(); | ||||
|     }, | ||||
|  | ||||
|     _onStyleChanged: function() { | ||||
|         let themeNode = this._box.get_theme_node(); | ||||
|         let horizontalPadding = themeNode.get_horizontal_padding(); | ||||
|         let verticalPadding = themeNode.get_vertical_padding(); | ||||
|         let topBorder = themeNode.get_border_width(St.Side.TOP); | ||||
|         let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM); | ||||
|         let leftBorder = themeNode.get_border_width(St.Side.LEFT); | ||||
|         let rightBorder = themeNode.get_border_width(St.Side.RIGHT); | ||||
|  | ||||
|         let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder; | ||||
|         let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder; | ||||
|  | ||||
|         // minWidth/minHeight here are in real pixels, | ||||
|         // but the theme takes measures in unscaled dimensions | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor); | ||||
|         this._boxConstraint.minSize = popupSize; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -107,28 +107,20 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         this._overviewCreated = true; | ||||
|  | ||||
|         this._overview = new St.BoxLayout({ name: 'overview', | ||||
|                                             /* Translators: This is the main view to select | ||||
|                                                activities. See also note for "Activities" string. */ | ||||
|                                             accessible_name: _("Overview"), | ||||
|                                             vertical: true }); | ||||
|         this._overview.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
|         this._overview._delegate = this; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         let layout = new Clutter.BinLayout(); | ||||
|         this._stack = new Clutter.Actor({ layout_manager: layout }); | ||||
|         this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
|  | ||||
|         /* Translators: This is the main view to select | ||||
|            activities. See also note for "Activities" string. */ | ||||
|         this._overview = new St.BoxLayout({ name: 'overview', | ||||
|                                             accessible_name: _("Overview"), | ||||
|                                             reactive: true, | ||||
|                                             vertical: true, | ||||
|                                             x_expand: true, | ||||
|                                             y_expand: true }); | ||||
|         this._overview._delegate = this; | ||||
|  | ||||
|         this._backgroundGroup = new Meta.BackgroundGroup(); | ||||
|         this._backgroundGroup = new Meta.BackgroundGroup({ reactive: true }); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
| @@ -151,8 +143,7 @@ const Overview = new Lang.Class({ | ||||
|         Main.layoutManager.overviewGroup.add_child(this._coverPane); | ||||
|         this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; })); | ||||
|  | ||||
|         this._stack.add_actor(this._overview); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._stack); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._overview); | ||||
|  | ||||
|         this._coverPane.hide(); | ||||
|  | ||||
| @@ -161,6 +152,9 @@ const Overview = new Lang.Class({ | ||||
|             dragMotion: Lang.bind(this, this._onDragMotion) | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         Main.layoutManager.overviewGroup.connect('scroll-event', | ||||
|                                                  Lang.bind(this, this._onScrollEvent)); | ||||
|         Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin)); | ||||
|         Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd)); | ||||
|  | ||||
| @@ -257,7 +251,6 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         // Add our same-line elements after the search entry | ||||
|         this._overview.add(this._controls.actor, { y_fill: true, expand: true }); | ||||
|         this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
|  | ||||
|         // TODO - recalculate everything when desktop size changes | ||||
|         this.dashIconSize = this._dash.iconSize; | ||||
| @@ -374,7 +367,7 @@ const Overview = new Lang.Class({ | ||||
|         if (this.isDummy) | ||||
|             return; | ||||
|  | ||||
|         this._overview.add_action(action); | ||||
|         this._backgroundGroup.add_action(action); | ||||
|     }, | ||||
|  | ||||
|     _getDesktopClone: function() { | ||||
| @@ -385,7 +378,7 @@ const Overview = new Lang.Class({ | ||||
|             return null; | ||||
|  | ||||
|         let window = windows[0]; | ||||
|         let clone = new Clutter.Clone({ source: window.get_texture(), | ||||
|         let clone = new Clutter.Clone({ source: window, | ||||
|                                         x: window.x, y: window.y }); | ||||
|         clone.source.connect('destroy', Lang.bind(this, function() { | ||||
|             clone.destroy(); | ||||
| @@ -421,7 +414,7 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|     beginItemDrag: function(source) { | ||||
|         this.emit('item-drag-begin'); | ||||
|         this._inDrag = true; | ||||
|         this._inItemDrag = true; | ||||
|     }, | ||||
|  | ||||
|     cancelledItemDrag: function(source) { | ||||
| @@ -429,13 +422,15 @@ const Overview = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     endItemDrag: function(source) { | ||||
|         if (!this._inItemDrag) | ||||
|             return; | ||||
|         this.emit('item-drag-end'); | ||||
|         this._inDrag = false; | ||||
|         this._inItemDrag = false; | ||||
|     }, | ||||
|  | ||||
|     beginWindowDrag: function(window) { | ||||
|         this.emit('window-drag-begin', window); | ||||
|         this._inDrag = true; | ||||
|         this._inWindowDrag = true; | ||||
|     }, | ||||
|  | ||||
|     cancelledWindowDrag: function(window) { | ||||
| @@ -443,8 +438,10 @@ const Overview = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     endWindowDrag: function(window) { | ||||
|         if (!this._inWindowDrag) | ||||
|             return; | ||||
|         this.emit('window-drag-end', window); | ||||
|         this._inDrag = false; | ||||
|         this._inWindowDrag = false; | ||||
|     }, | ||||
|  | ||||
|     focusSearch: function() { | ||||
| @@ -488,7 +485,7 @@ const Overview = new Lang.Class({ | ||||
|     shouldToggleByCornerOrButton: function() { | ||||
|         if (this.animationInProgress) | ||||
|             return false; | ||||
|         if (this._inDrag) | ||||
|         if (this._inItemDrag || this._inWindowDrag) | ||||
|             return false; | ||||
|         if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT) | ||||
|             return true; | ||||
| @@ -554,8 +551,8 @@ const Overview = new Lang.Class({ | ||||
|         Meta.disable_unredirect_for_screen(global.screen); | ||||
|         this.viewSelector.show(); | ||||
|  | ||||
|         this._stack.opacity = 0; | ||||
|         Tweener.addTween(this._stack, | ||||
|         this._overview.opacity = 0; | ||||
|         Tweener.addTween(this._overview, | ||||
|                          { opacity: 255, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            time: ANIMATION_TIME, | ||||
| @@ -620,7 +617,7 @@ const Overview = new Lang.Class({ | ||||
|         this.viewSelector.animateFromOverview(); | ||||
|  | ||||
|         // Make other elements fade out. | ||||
|         Tweener.addTween(this._stack, | ||||
|         Tweener.addTween(this._overview, | ||||
|                          { opacity: 0, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            time: ANIMATION_TIME, | ||||
|   | ||||
| @@ -421,7 +421,6 @@ const ControlsManager = new Lang.Class({ | ||||
|  | ||||
|         let layout = new ControlsLayout(); | ||||
|         this.actor = new St.Widget({ layout_manager: layout, | ||||
|                                      reactive: true, | ||||
|                                      x_expand: true, y_expand: true, | ||||
|                                      clip_to_allocation: true }); | ||||
|         this._group = new St.BoxLayout({ name: 'overview-group', | ||||
|   | ||||
							
								
								
									
										996
									
								
								js/ui/padOsd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,996 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const St = imports.gi.St; | ||||
| const Rsvg = imports.gi.Rsvg; | ||||
| const GObject = imports.gi.GObject; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Atk = imports.gi.Atk; | ||||
| const Cairo = imports.cairo; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Layout = imports.ui.layout; | ||||
|  | ||||
| const ACTIVE_COLOR = "#729fcf"; | ||||
|  | ||||
| const LTR = 0; | ||||
| const RTL = 1; | ||||
|  | ||||
| const CW = 0; | ||||
| const CCW = 1; | ||||
|  | ||||
| const UP = 0; | ||||
| const DOWN = 1; | ||||
|  | ||||
| const PadChooser = new Lang.Class({ | ||||
|     Name: 'PadChooser', | ||||
|  | ||||
|     _init: function (device, groupDevices) { | ||||
|         this.actor = new St.Button({ style_class: 'pad-chooser-button', | ||||
|                                      toggle_mode: true, | ||||
|                                      x_fill: false, | ||||
|                                      y_fill: false, | ||||
|                                      x_align: St.Align.MIDDLE, | ||||
|                                      y_align: St.Align.MIDDLE }); | ||||
|         this.currentDevice = device; | ||||
|         this._padChooserMenu = null; | ||||
|  | ||||
|         let arrow = new St.Icon({ style_class: 'popup-menu-arrow', | ||||
|                                   icon_name: 'pan-down-symbolic', | ||||
|                                   accessible_role: Atk.Role.ARROW }); | ||||
|         this.actor.set_child(arrow); | ||||
|         this._ensureMenu(groupDevices); | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         this.actor.connect('clicked', Lang.bind(this, function (actor) { | ||||
|             if (actor.get_checked()) { | ||||
|                 if (this._padChooserMenu != null) | ||||
|                     this._padChooserMenu.open(true); | ||||
|                 else | ||||
|                     this.set_checked(false); | ||||
|             } else { | ||||
|                 this._padChooserMenu.close(true); | ||||
|             } | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _ensureMenu: function (devices) { | ||||
|         this._padChooserMenu =  new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP); | ||||
|         this._padChooserMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); })); | ||||
|         this._padChooserMenu.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._padChooserMenu.actor); | ||||
|  | ||||
|         for (let i = 0; i < devices.length; i++) { | ||||
|             let device = devices[i]; | ||||
|             if (device == this.currentDevice) | ||||
|                 continue; | ||||
|  | ||||
|             this._padChooserMenu.addAction(device.get_device_name(), () => { | ||||
|                 this.emit('pad-selected', device); | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function () { | ||||
|         this._padChooserMenu.destroy(); | ||||
|     }, | ||||
|  | ||||
|     update: function (devices) { | ||||
|         if (this._padChooserMenu) | ||||
|             this._padChooserMenu.actor.destroy(); | ||||
|         this.actor.set_checked(false); | ||||
|         this._ensureMenu(devices); | ||||
|     }, | ||||
|  | ||||
|     destroy: function () { | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(PadChooser.prototype); | ||||
|  | ||||
| const KeybindingEntry = new Lang.Class({ | ||||
|     Name: 'KeybindingEntry', | ||||
|  | ||||
|     _init: function () { | ||||
|         this.actor = new St.Entry({ hint_text: _("New shortcut…"), | ||||
|                                     style: 'width: 10em' }); | ||||
|         this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent: function (actor, event) { | ||||
|         if (event.type() != Clutter.EventType.KEY_PRESS) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let str = Gtk.accelerator_name_with_keycode(null, | ||||
|                                                     event.get_key_symbol(), | ||||
|                                                     event.get_key_code(), | ||||
|                                                     event.get_state()); | ||||
|         this.actor.set_text(str); | ||||
|         this.emit('keybinding-edited', str); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(KeybindingEntry.prototype); | ||||
|  | ||||
| const ActionComboBox = new Lang.Class({ | ||||
|     Name: 'ActionComboBox', | ||||
|  | ||||
|     _init: function () { | ||||
|         this.actor = new St.Button({ style_class: 'button' }); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onButtonClicked)); | ||||
|         this.actor.set_toggle_mode(true); | ||||
|  | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 6 }); | ||||
|         let box = new St.Widget({ layout_manager: boxLayout }); | ||||
|         this.actor.set_child(box); | ||||
|  | ||||
|         this._label = new St.Label({ style_class: 'combo-box-label' }); | ||||
|         box.add_child(this._label) | ||||
|  | ||||
|         let arrow = new St.Icon({ style_class: 'popup-menu-arrow', | ||||
|                                   icon_name: 'pan-down-symbolic', | ||||
|                                   accessible_role: Atk.Role.ARROW, | ||||
|                                   y_expand: true, | ||||
|                                   y_align: Clutter.ActorAlign.CENTER }); | ||||
|         box.add_child(arrow); | ||||
|  | ||||
|         this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP); | ||||
|         this._editMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); })); | ||||
|         this._editMenu.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._editMenu.actor); | ||||
|  | ||||
|         this._actionLabels = new Map(); | ||||
|         this._actionLabels.set(GDesktopEnums.PadButtonAction.NONE, _("Application defined")); | ||||
|         this._actionLabels.set(GDesktopEnums.PadButtonAction.HELP, _("Show on-screen help")); | ||||
|         this._actionLabels.set(GDesktopEnums.PadButtonAction.SWITCH_MONITOR, _("Switch monitor")); | ||||
|         this._actionLabels.set(GDesktopEnums.PadButtonAction.KEYBINDING, _("Assign keystroke")); | ||||
|  | ||||
|         this._buttonItems = []; | ||||
|  | ||||
|         for (let [action, label] of this._actionLabels.entries()) { | ||||
|             let selectedAction = action; | ||||
|             let item = this._editMenu.addAction(label, Lang.bind(this, function() { this._onActionSelected(selectedAction) })); | ||||
|  | ||||
|             /* These actions only apply to pad buttons */ | ||||
|             if (selectedAction == GDesktopEnums.PadButtonAction.HELP || | ||||
|                 selectedAction == GDesktopEnums.PadButtonAction.SWITCH_MONITOR) | ||||
|                 this._buttonItems.push(item); | ||||
|         } | ||||
|  | ||||
|         this.setAction(GDesktopEnums.PadButtonAction.NONE); | ||||
|     }, | ||||
|  | ||||
|     _onActionSelected: function (action) { | ||||
|         this.setAction(action); | ||||
|         this.popdown(); | ||||
|         this.emit('action-selected', action); | ||||
|     }, | ||||
|  | ||||
|     setAction: function (action) { | ||||
|         this._label.set_text(this._actionLabels.get(action)); | ||||
|     }, | ||||
|  | ||||
|     popup: function () { | ||||
|         this._editMenu.open(true); | ||||
|     }, | ||||
|  | ||||
|     popdown: function () { | ||||
|         this._editMenu.close(true); | ||||
|     }, | ||||
|  | ||||
|     _onButtonClicked: function () { | ||||
|         if (this.actor.get_checked()) | ||||
|             this.popup(); | ||||
|         else | ||||
|             this.popdown(); | ||||
|     }, | ||||
|  | ||||
|     setButtonActionsActive: function (active) { | ||||
|         this._buttonItems.forEach(item => { item.setSensitive(active); }); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ActionComboBox.prototype); | ||||
|  | ||||
| const ActionEditor = new Lang.Class({ | ||||
|     Name: 'ActionEditor', | ||||
|  | ||||
|     _init: function () { | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 12 }); | ||||
|  | ||||
|         this.actor = new St.Widget({ layout_manager: boxLayout }); | ||||
|  | ||||
|         this._actionComboBox = new ActionComboBox(); | ||||
|         this._actionComboBox.connect('action-selected', Lang.bind(this, this._onActionSelected)); | ||||
|         this.actor.add_actor(this._actionComboBox.actor); | ||||
|  | ||||
|         this._keybindingEdit = new KeybindingEntry(); | ||||
|         this._keybindingEdit.connect('keybinding-edited', Lang.bind(this, this._onKeybindingEdited)); | ||||
|         this.actor.add_actor(this._keybindingEdit.actor); | ||||
|  | ||||
|         this._doneButton = new St.Button({ label: _("Done"), | ||||
|                                            style_class: 'button', | ||||
|                                            x_expand: false}); | ||||
|         this._doneButton.connect('clicked', Lang.bind(this, this._onEditingDone)); | ||||
|         this.actor.add_actor(this._doneButton); | ||||
|     }, | ||||
|  | ||||
|     _updateKeybindingEntryState: function () { | ||||
|         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) { | ||||
|             this._keybindingEdit.actor.set_text(this._currentKeybinding); | ||||
|             this._keybindingEdit.actor.show(); | ||||
|             this._keybindingEdit.actor.grab_key_focus(); | ||||
|         } else { | ||||
|             this._keybindingEdit.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     setSettings: function (settings, action) { | ||||
|         this._buttonSettings = settings; | ||||
|  | ||||
|         this._currentAction = this._buttonSettings.get_enum('action'); | ||||
|         this._currentKeybinding = this._buttonSettings.get_string('keybinding'); | ||||
|         this._actionComboBox.setAction(this._currentAction); | ||||
|         this._updateKeybindingEntryState(); | ||||
|  | ||||
|         let isButton = (action == Meta.PadActionType.BUTTON); | ||||
|         this._actionComboBox.setButtonActionsActive(isButton); | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this._actionComboBox.popdown(); | ||||
|         this.actor.hide(); | ||||
|     }, | ||||
|  | ||||
|     _onKeybindingEdited: function (entry, keybinding) { | ||||
|         this._currentKeybinding = keybinding; | ||||
|     }, | ||||
|  | ||||
|     _onActionSelected: function (menu, action) { | ||||
|         this._currentAction = action; | ||||
|         this._updateKeybindingEntryState(); | ||||
|     }, | ||||
|  | ||||
|     _storeSettings: function () { | ||||
|         if (!this._buttonSettings) | ||||
|             return; | ||||
|  | ||||
|         let keybinding = null; | ||||
|  | ||||
|         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) | ||||
|             keybinding = this._currentKeybinding; | ||||
|  | ||||
|         this._buttonSettings.set_enum('action', this._currentAction); | ||||
|  | ||||
|         if (keybinding) | ||||
|             this._buttonSettings.set_string('keybinding', keybinding); | ||||
|         else | ||||
|             this._buttonSettings.reset('keybinding'); | ||||
|     }, | ||||
|  | ||||
|     _onEditingDone: function () { | ||||
|         this._storeSettings(); | ||||
|         this.close(); | ||||
|         this.emit('done'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ActionEditor.prototype); | ||||
|  | ||||
| const PadDiagram = new Lang.Class({ | ||||
|     Name: 'PadDiagram', | ||||
|     Extends: St.DrawingArea, | ||||
|     Properties: { 'left-handed': GObject.ParamSpec.boolean('left-handed', | ||||
|                                                            'left-handed', 'Left handed', | ||||
|                                                            GObject.ParamFlags.READWRITE | | ||||
|                                                            GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|                                                            false), | ||||
|                   'image': GObject.ParamSpec.string('image', 'image', 'Image', | ||||
|                                                     GObject.ParamFlags.READWRITE | | ||||
|                                                     GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|                                                     null), | ||||
|                   'editor-actor': GObject.ParamSpec.object('editor-actor', | ||||
|                                                            'editor-actor', | ||||
|                                                            'Editor actor', | ||||
|                                                            GObject.ParamFlags.READWRITE | | ||||
|                                                            GObject.ParamFlags.CONSTRUCT_ONLY, | ||||
|                                                            Clutter.Actor.$gtype) }, | ||||
|  | ||||
|     _init: function (params) { | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/pad-osd.css'); | ||||
|         let [success, css, etag] = file.load_contents(null); | ||||
|         this._curEdited = null; | ||||
|         this._prevEdited = null; | ||||
|         this._css = css; | ||||
|         this._labels = []; | ||||
|         this._activeButtons = []; | ||||
|         this.parent(params); | ||||
|     }, | ||||
|  | ||||
|     get left_handed() { | ||||
|         return this._leftHanded; | ||||
|     }, | ||||
|  | ||||
|     set left_handed(leftHanded) { | ||||
|         this._leftHanded = leftHanded; | ||||
|     }, | ||||
|  | ||||
|     get image() { | ||||
|         return this._imagePath; | ||||
|     }, | ||||
|  | ||||
|     set image(imagePath) { | ||||
|         let originalHandle = Rsvg.Handle.new_from_file(imagePath); | ||||
|         let dimensions = originalHandle.get_dimensions(); | ||||
|         this._imageWidth = dimensions.width; | ||||
|         this._imageHeight = dimensions.height; | ||||
|  | ||||
|         this._imagePath = imagePath; | ||||
|         this._handle = this._composeStyledDiagram(); | ||||
|     }, | ||||
|  | ||||
|     get editor_actor() { | ||||
|         return this._editorActor; | ||||
|     }, | ||||
|  | ||||
|     set editor_actor(actor) { | ||||
|         actor.hide(); | ||||
|         this._editorActor = actor; | ||||
|         this.add_actor(actor); | ||||
|     }, | ||||
|  | ||||
|     _wrappingSvgHeader: function () { | ||||
|         return ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + | ||||
|                 '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' + | ||||
|                 'xmlns:xi="http://www.w3.org/2001/XInclude" ' + | ||||
|                 'width="' + this._imageWidth + '" height="' + this._imageHeight + '"> ' + | ||||
|                 '<style type="text/css">'); | ||||
|     }, | ||||
|  | ||||
|     _wrappingSvgFooter: function () { | ||||
|         return ('</style>' + | ||||
|                 '<xi:include href="' + this._imagePath + '" />' + | ||||
|                 '</svg>'); | ||||
|     }, | ||||
|  | ||||
|     _cssString: function () { | ||||
|         let css = this._css; | ||||
|  | ||||
|         for (let i = 0; i < this._activeButtons.length; i++) { | ||||
|             let ch = String.fromCharCode('A'.charCodeAt() + this._activeButtons[i]); | ||||
|             css += ('.' + ch + ' { ' + | ||||
| 	            '  stroke: ' + ACTIVE_COLOR + ' !important; ' + | ||||
|                     '  fill: ' + ACTIVE_COLOR + ' !important; ' + | ||||
|                     '} '); | ||||
|         } | ||||
|  | ||||
|         return css; | ||||
|     }, | ||||
|  | ||||
|     _composeStyledDiagram: function () { | ||||
|         let svgData = ''; | ||||
|  | ||||
|         if (!GLib.file_test(this._imagePath, GLib.FileTest.EXISTS)) | ||||
|             return null; | ||||
|  | ||||
|         svgData += this._wrappingSvgHeader(); | ||||
|         svgData += this._cssString(); | ||||
|         svgData += this._wrappingSvgFooter(); | ||||
|  | ||||
|         let handle = new Rsvg.Handle(); | ||||
|         handle.set_base_uri(GLib.path_get_dirname(this._imagePath)); | ||||
|         handle.write(svgData); | ||||
|         handle.close(); | ||||
|  | ||||
|         return handle; | ||||
|     }, | ||||
|  | ||||
|     _updateDiagramScale: function () { | ||||
|         if (this._handle == null) | ||||
|             return; | ||||
|  | ||||
|         [this._actorWidth, this._actorHeight] = this.get_size(); | ||||
|         let dimensions = this._handle.get_dimensions(); | ||||
|         let scaleX = this._actorWidth / dimensions.width; | ||||
|         let scaleY = this._actorHeight / dimensions.height; | ||||
|         this._scale = Math.min(scaleX, scaleY); | ||||
|     }, | ||||
|  | ||||
|     _allocateChild: function (child, x, y, direction) { | ||||
|         let [prefHeight, natHeight] = child.get_preferred_height(-1); | ||||
|         let [prefWidth, natWidth] = child.get_preferred_width(natHeight); | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|  | ||||
|         if (direction == LTR) { | ||||
|             childBox.x1 = x; | ||||
|             childBox.x2 = x + natWidth; | ||||
|         } else { | ||||
|             childBox.x1 = x - natWidth; | ||||
|             childBox.x2 = x; | ||||
|         } | ||||
|  | ||||
|         childBox.y1 = y - natHeight / 2; | ||||
|         childBox.y2 = y + natHeight / 2; | ||||
|         child.allocate(childBox, 0); | ||||
|     }, | ||||
|  | ||||
|     vfunc_allocate: function (box, flags) { | ||||
|         this.parent(box, flags); | ||||
|         this._updateDiagramScale(); | ||||
|  | ||||
|         for (let i = 0; i < this._labels.length; i++) { | ||||
|             let [label, action, idx, dir] = this._labels[i]; | ||||
|             let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir); | ||||
|             this._allocateChild(label, x, y, arrangement); | ||||
|         } | ||||
|  | ||||
|         if (this._editorActor && this._curEdited) { | ||||
|             let [label, action, idx, dir] = this._curEdited; | ||||
|             let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir); | ||||
|             this._allocateChild(this._editorActor, x, y, arrangement); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     vfunc_repaint: function () { | ||||
|         if (this._handle == null) | ||||
|             return; | ||||
|  | ||||
|         if (this._scale == null) | ||||
|             this._updateDiagramScale(); | ||||
|  | ||||
|         let [width, height] = this.get_surface_size(); | ||||
|         let dimensions = this._handle.get_dimensions(); | ||||
|         let cr = this.get_context(); | ||||
|  | ||||
|         cr.save(); | ||||
|         cr.translate(width/2, height/2); | ||||
|         cr.scale(this._scale, this._scale); | ||||
|         if (this._leftHanded) | ||||
|             cr.rotate(Math.PI); | ||||
|         cr.translate(-dimensions.width/2, -dimensions.height/2); | ||||
|         this._handle.render_cairo(cr); | ||||
|         cr.restore(); | ||||
|         cr.$dispose(); | ||||
|     }, | ||||
|  | ||||
|     _transformPoint: function (x, y) { | ||||
|         if (this._handle == null || this._scale == null) | ||||
|             return [x, y]; | ||||
|  | ||||
|         // I miss Cairo.Matrix | ||||
|         let dimensions = this._handle.get_dimensions(); | ||||
|         x = x * this._scale + this._actorWidth / 2 - dimensions.width / 2 * this._scale; | ||||
|         y = y * this._scale + this._actorHeight / 2 - dimensions.height / 2 * this._scale;; | ||||
|         return [Math.round(x), Math.round(y)]; | ||||
|     }, | ||||
|  | ||||
|     _getItemLabelCoords: function (labelName, leaderName) { | ||||
|         if (this._handle == null) | ||||
|             return [false]; | ||||
|  | ||||
|         let leaderPos, leaderSize, pos; | ||||
|         let found, direction; | ||||
|  | ||||
|         [found, pos] = this._handle.get_position_sub('#' + labelName); | ||||
|         if (!found) | ||||
|             return [false]; | ||||
|  | ||||
|         [found, leaderPos] = this._handle.get_position_sub('#' + leaderName); | ||||
|         [found, leaderSize] = this._handle.get_dimensions_sub('#' + leaderName); | ||||
|         if (!found) | ||||
|             return [false]; | ||||
|  | ||||
|         if (pos.x > leaderPos.x + leaderSize.width) | ||||
|             direction = LTR; | ||||
|         else | ||||
|             direction = RTL; | ||||
|  | ||||
|         if (this._leftHanded) { | ||||
|             direction = 1 - direction; | ||||
|             pos.x = this._imageWidth - pos.x; | ||||
|             pos.y = this._imageHeight - pos.y; | ||||
|         } | ||||
|  | ||||
|         let [x, y] = this._transformPoint(pos.x, pos.y) | ||||
|  | ||||
|         return [true, x, y, direction]; | ||||
|     }, | ||||
|  | ||||
|     getButtonLabelCoords: function (button) { | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + button); | ||||
|         let labelName = 'Label' + ch; | ||||
|         let leaderName = 'Leader' + ch; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     getRingLabelCoords: function (number, dir) { | ||||
|         let numStr = number > 0 ? (number + 1).toString() : ''; | ||||
|         let dirStr = dir == CW ? 'CW' : 'CCW'; | ||||
|         let labelName = 'LabelRing' + numStr + dirStr; | ||||
|         let leaderName = 'LeaderRing' + numStr + dirStr; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     getStripLabelCoords: function (number, dir) { | ||||
|         let numStr = number > 0 ? (number + 1).toString() : ''; | ||||
|         let dirStr = dir == UP ? 'Up' : 'Down'; | ||||
|         let labelName = 'LabelStrip' + numStr + dirStr; | ||||
|         let leaderName = 'LeaderStrip' + numStr + dirStr; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     getLabelCoords: function (action, idx, dir) { | ||||
|         if (action == Meta.PadActionType.BUTTON) | ||||
|             return this.getButtonLabelCoords(idx); | ||||
|         else if (action == Meta.PadActionType.RING) | ||||
|             return this.getRingLabelCoords(idx, dir); | ||||
|         else if (action == Meta.PadActionType.STRIP) | ||||
|             return this.getStripLabelCoords(idx, dir); | ||||
|  | ||||
|         return [false]; | ||||
|     }, | ||||
|  | ||||
|     _invalidateSvg: function () { | ||||
|         if (this._handle == null) | ||||
|             return; | ||||
|         this._handle = this._composeStyledDiagram(); | ||||
|         this.queue_repaint(); | ||||
|     }, | ||||
|  | ||||
|     activateButton: function (button) { | ||||
|         this._activeButtons.push(button); | ||||
|         this._invalidateSvg(); | ||||
|     }, | ||||
|  | ||||
|     deactivateButton: function (button) { | ||||
|         for (let i = 0; i < this._activeButtons.length; i++) { | ||||
|             if (this._activeButtons[i] == button) | ||||
|                 this._activeButtons.splice(i, 1); | ||||
|         } | ||||
|         this._invalidateSvg(); | ||||
|     }, | ||||
|  | ||||
|     addLabel: function (label, type, idx, dir) { | ||||
|         this._labels.push([label, type, idx, dir]); | ||||
|         this.add_actor(label); | ||||
|     }, | ||||
|  | ||||
|     _applyLabel: function(label, action, idx, dir, str) { | ||||
|         if (str != null) { | ||||
|             label.set_text(str); | ||||
|  | ||||
|             let [found, x, y, arrangement] = this.getLabelCoords(action, idx, dir); | ||||
|             this._allocateChild(label, x, y, arrangement); | ||||
|         } | ||||
|         label.show(); | ||||
|     }, | ||||
|  | ||||
|     stopEdition: function (continues, str) { | ||||
|         this._editorActor.hide(); | ||||
|  | ||||
|         if (this._prevEdited) { | ||||
|             let [label, action, idx, dir] = this._prevEdited; | ||||
|             this._applyLabel(label, action, idx, dir, str); | ||||
|             this._prevEdited = null; | ||||
|         } | ||||
|  | ||||
|         if (this._curEdited) { | ||||
|             let [label, action, idx, dir] = this._curEdited; | ||||
|             this._applyLabel(label, action, idx, dir, str); | ||||
|             if (continues) | ||||
|                 this._prevEdited = this._curEdited; | ||||
|             this._curEdited = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     startEdition: function(action, idx, dir) { | ||||
|         let editedLabel; | ||||
|  | ||||
|         if (this._curEdited) | ||||
|             return; | ||||
|  | ||||
|         for (let i = 0; i < this._labels.length; i++) { | ||||
|             let [label, itemAction, itemIdx, itemDir] = this._labels[i]; | ||||
|             if (action == itemAction && idx == itemIdx && dir == itemDir) { | ||||
|                 this._curEdited = this._labels[i]; | ||||
|                 editedLabel = label; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (this._curEdited == null) | ||||
|             return; | ||||
|         let [found] = this.getLabelCoords(action, idx, dir); | ||||
|         if (!found) | ||||
|             return; | ||||
|         this._editorActor.show(); | ||||
|         editedLabel.hide(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PadOsd = new Lang.Class({ | ||||
|     Name: 'PadOsd', | ||||
|  | ||||
|     _init: function (padDevice, settings, imagePath, editionMode, monitorIndex) { | ||||
|         this.padDevice = padDevice; | ||||
|         this._groupPads = [ padDevice ]; | ||||
|         this._settings = settings; | ||||
|         this._imagePath = imagePath; | ||||
|         this._editionMode = editionMode; | ||||
|         this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); | ||||
|         this._padChooser = null; | ||||
|  | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
|         this._deviceAddedId = deviceManager.connect('device-added', Lang.bind(this, function (manager, device) { | ||||
|             if (device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE && | ||||
|                 this.padDevice.is_grouped(device)) { | ||||
|                 this._groupPads.push(device); | ||||
|                 this._updatePadChooser(); | ||||
|             } | ||||
|         })); | ||||
|         this._deviceRemovedId = deviceManager.connect('device-removed', Lang.bind(this, function (manager, device) { | ||||
|             // If the device is being removed, destroy the padOsd. | ||||
|             if (device == this.padDevice) { | ||||
|                 this.destroy(); | ||||
|             } else if (this._groupPads.indexOf(device) != -1) { | ||||
|                 // Or update the pad chooser if the device belongs to | ||||
|                 // the same group. | ||||
|                 this._groupPads.splice(this._groupPads.indexOf(device), 1); | ||||
|                 this._updatePadChooser(); | ||||
|  | ||||
|             } | ||||
|         })); | ||||
|  | ||||
|         deviceManager.list_devices().forEach(Lang.bind(this, function(device) { | ||||
|             if (device != this.padDevice && | ||||
|                 device.get_device_type() == Clutter.InputDeviceType.PAD_DEVICE && | ||||
|                 this.padDevice.is_grouped(device)) | ||||
|                 this._groupPads.push(device); | ||||
|         })); | ||||
|  | ||||
|         this.actor = new St.BoxLayout({ style_class: 'pad-osd-window', | ||||
|                                         x_expand: true, | ||||
|                                         y_expand: true, | ||||
|                                         vertical: true, | ||||
|                                         reactive: true }); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|  | ||||
|         this._monitorIndex = monitorIndex; | ||||
|         let constraint = new Layout.MonitorConstraint({ index: monitorIndex }); | ||||
|         this.actor.add_constraint(constraint); | ||||
|  | ||||
|         this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box', | ||||
|                                             vertical: false, | ||||
|                                             x_expand: false, | ||||
|                                             x_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_actor(this._titleBox); | ||||
|  | ||||
|         let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box', | ||||
|                                           vertical: true }); | ||||
|         this._titleBox.add_actor(labelBox); | ||||
|  | ||||
|         this._titleLabel = new St.Label({ style: 'font-side: larger; font-weight: bold;', | ||||
|                                           x_align: Clutter.ActorAlign.CENTER }); | ||||
|         this._titleLabel.clutter_text.set_text(padDevice.get_device_name()); | ||||
|         labelBox.add_actor(this._titleLabel); | ||||
|  | ||||
|         this._tipLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER }); | ||||
|         labelBox.add_actor(this._tipLabel); | ||||
|  | ||||
|         this._updatePadChooser(); | ||||
|  | ||||
|         this._actionEditor = new ActionEditor(); | ||||
|         this._actionEditor.connect('done', Lang.bind(this, this._endActionEdition)); | ||||
|  | ||||
|         this._padDiagram = new PadDiagram({ image: this._imagePath, | ||||
|                                             left_handed: settings.get_boolean('left-handed'), | ||||
|                                             editor_actor: this._actionEditor.actor, | ||||
|                                             x_expand: true, | ||||
|                                             y_expand: true }); | ||||
|         this.actor.add_actor(this._padDiagram); | ||||
|  | ||||
|         // FIXME: Fix num buttons. | ||||
|         let i = 0; | ||||
|         for (i = 0; i < 50; i++) { | ||||
|             let [found] = this._padDiagram.getButtonLabelCoords(i); | ||||
|             if (!found) | ||||
|                 break; | ||||
|             this._createLabel(Meta.PadActionType.BUTTON, i); | ||||
|         } | ||||
|  | ||||
|         for (i = 0; i < padDevice.get_n_rings(); i++) { | ||||
|             let [found] = this._padDiagram.getRingLabelCoords(i, CW); | ||||
|             if (!found) | ||||
|                 break; | ||||
|             this._createLabel(Meta.PadActionType.RING, i, CW); | ||||
|             this._createLabel(Meta.PadActionType.RING, i, CCW); | ||||
|         } | ||||
|  | ||||
|         for (i = 0; i < padDevice.get_n_strips(); i++) { | ||||
|             let [found] = this._padDiagram.getStripLabelCoords(i, UP); | ||||
|             if (!found) | ||||
|                 break; | ||||
|             this._createLabel(Meta.PadActionType.STRIP, i, UP); | ||||
|             this._createLabel(Meta.PadActionType.STRIP, i, DOWN); | ||||
|         } | ||||
|  | ||||
|         let buttonBox = new St.Widget({ layout_manager: new Clutter.BinLayout(), | ||||
|                                          x_expand: true, | ||||
|                                          x_align: Clutter.ActorAlign.CENTER, | ||||
|                                          y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_actor(buttonBox); | ||||
|         this._editButton = new St.Button({ label: _("Edit…"), | ||||
|                                            style_class: 'button', | ||||
|                                            x_align: Clutter.ActorAlign.CENTER, | ||||
|                                            can_focus: true }); | ||||
|         this._editButton.connect('clicked', Lang.bind(this, function () { this.setEditionMode(true) })); | ||||
|         buttonBox.add_actor(this._editButton); | ||||
|  | ||||
|         this._syncEditionMode(); | ||||
|         Main.pushModal(this.actor); | ||||
|     }, | ||||
|  | ||||
|     _updatePadChooser: function () { | ||||
|         if (this._groupPads.length > 1) { | ||||
|             if (this._padChooser == null) { | ||||
|                 this._padChooser = new PadChooser(this.padDevice, this._groupPads) | ||||
|                 this._padChooser.connect('pad-selected', Lang.bind(this, function (chooser, pad) { | ||||
|                     this._requestForOtherPad(pad); | ||||
|                 })); | ||||
|                 this._titleBox.add_child(this._padChooser.actor); | ||||
|             } else { | ||||
|                 this._padChooser.update(this._groupPads); | ||||
|             } | ||||
|         } else if (this._padChooser != null) { | ||||
|             this._padChooser.destroy(); | ||||
|             this._padChooser = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _requestForOtherPad: function (pad) { | ||||
|         if (pad == this.padDevice || | ||||
|             this._groupPads.indexOf(pad) == -1) | ||||
|             return; | ||||
|  | ||||
|         let editionMode = this._editionMode; | ||||
|         this.destroy(); | ||||
|         global.display.request_pad_osd(pad, editionMode); | ||||
|     }, | ||||
|  | ||||
|     _createLabel: function (type, number, dir) { | ||||
|         let str = global.display.get_pad_action_label(this.padDevice, type, number); | ||||
|         let label = new St.Label({ text: str ? str : _("None") }); | ||||
|         this._padDiagram.addLabel(label, type, number, dir); | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent : function (actor, event) { | ||||
|         if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS && | ||||
|             event.get_source_device() == this.padDevice) { | ||||
|             this._padDiagram.activateButton(event.get_button()); | ||||
|             let isModeSwitch = this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0; | ||||
|  | ||||
|             /* Buttons that switch between modes cannot be edited */ | ||||
|             if (this._editionMode && !isModeSwitch) | ||||
|                 this._startButtonActionEdition(event.get_button()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE && | ||||
|                    event.get_source_device() == this.padDevice) { | ||||
|             this._padDiagram.deactivateButton(event.get_button()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.KEY_PRESS && | ||||
|                    (!this._editionMode || event.get_key_symbol() == Clutter.Escape)) { | ||||
|             if (this._editedAction != null) | ||||
|                 this._endActionEdition(); | ||||
|             else | ||||
|                 this.destroy(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.get_source_device() == this.padDevice && | ||||
|                    event.type() == Clutter.EventType.PAD_STRIP) { | ||||
|             if (this._editionMode) { | ||||
|                 let [retval, number, mode] = event.get_pad_event_details(); | ||||
|                 this._startStripActionEdition(number, UP, mode); | ||||
|             } | ||||
|         } else if (event.get_source_device() == this.padDevice && | ||||
|                    event.type() == Clutter.EventType.PAD_RING) { | ||||
|             if (this._editionMode) { | ||||
|                 let [retval, number, mode] = event.get_pad_event_details(); | ||||
|                 this._startRingActionEdition(number, CCW, mode); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // If the event comes from another pad in the same group, | ||||
|         // show the OSD for it. | ||||
|         if (this._groupPads.indexOf(event.get_source_device()) != -1) { | ||||
|             this._requestForOtherPad(event.get_source_device()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _syncEditionMode: function () { | ||||
|         this._editButton.set_reactive(!this._editionMode); | ||||
|         this._editButton.save_easing_state(); | ||||
|         this._editButton.set_easing_duration(200); | ||||
|         this._editButton.set_opacity(this._editionMode ? 128 : 255); | ||||
|         this._editButton.restore_easing_state(); | ||||
|  | ||||
|         let title; | ||||
|  | ||||
|         if (this._editionMode) { | ||||
|             title = _("Press a button to configure"); | ||||
|             this._tipLabel.set_text(_("Press Esc to exit")); | ||||
|         } else { | ||||
|             title = this.padDevice.get_device_name(); | ||||
|             this._tipLabel.set_text(_("Press any key to exit")); | ||||
|         } | ||||
|  | ||||
|         this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>'); | ||||
|     }, | ||||
|  | ||||
|     _isEditedAction: function (type, number, dir) { | ||||
|         if (!this._editedAction) | ||||
|             return false; | ||||
|  | ||||
|         return (this._editedAction.type == type && | ||||
|                 this._editedAction.number == number && | ||||
|                 this._editedAction.dir == dir); | ||||
|     }, | ||||
|  | ||||
|     _followUpActionEdition: function (str) { | ||||
|         let { type, dir, number, mode } = this._editedAction; | ||||
|         let hasNextAction = (type == Meta.PadActionType.RING && dir == CCW || | ||||
|                              type == Meta.PadActionType.STRIP && dir == UP); | ||||
|         if (!hasNextAction) | ||||
|             return false; | ||||
|  | ||||
|         this._padDiagram.stopEdition(true, str); | ||||
|         this._editedAction = null; | ||||
|         if (type == Meta.PadActionType.RING) | ||||
|             this._startRingActionEdition(number, CW, mode); | ||||
|         else | ||||
|             this._startStripActionEdition(number, DOWN, mode); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _endActionEdition: function () { | ||||
|         this._actionEditor.close(); | ||||
|  | ||||
|         if (this._editedAction != null) { | ||||
|             let str = global.display.get_pad_action_label(this.padDevice, | ||||
|                                                           this._editedAction.type, | ||||
|                                                           this._editedAction.number); | ||||
|             if (this._followUpActionEdition(str)) | ||||
|                 return; | ||||
|  | ||||
|             this._padDiagram.stopEdition(false, str ? str : _("None")) | ||||
|             this._editedAction = null; | ||||
|         } | ||||
|  | ||||
|         this._editedActionSettings = null; | ||||
|     }, | ||||
|  | ||||
|     _startActionEdition: function (key, type, number, dir, mode) { | ||||
|         if (this._isEditedAction(type, number, dir)) | ||||
|             return; | ||||
|  | ||||
|         this._endActionEdition(); | ||||
|         this._editedAction = { type, number, dir, mode }; | ||||
|  | ||||
|         let settingsPath = this._settings.path + key + '/'; | ||||
|         this._editedActionSettings = Gio.Settings.new_with_path('org.gnome.desktop.peripherals.tablet.pad-button', | ||||
|                                                                 settingsPath); | ||||
|         this._actionEditor.setSettings(this._editedActionSettings, type); | ||||
|         this._padDiagram.startEdition(type, number, dir); | ||||
|     }, | ||||
|  | ||||
|     _startButtonActionEdition: function (button) { | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + button); | ||||
|         let key = 'button' + ch; | ||||
|         this._startActionEdition(key, Meta.PadActionType.BUTTON, button); | ||||
|     }, | ||||
|  | ||||
|     _startRingActionEdition: function (ring, dir, mode) { | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + ring); | ||||
|         let key = 'ring%s-%s-mode-%d'.format(ch, dir == CCW ? 'ccw' : 'cw', mode); | ||||
|         this._startActionEdition(key, Meta.PadActionType.RING, ring, dir, mode); | ||||
|     }, | ||||
|  | ||||
|     _startStripActionEdition: function (strip, dir, mode) { | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + strip); | ||||
|         let key = 'strip%s-%s-mode-%d'.format(ch, dir == UP ? 'up' : 'down', mode); | ||||
|         this._startActionEdition(key, Meta.PadActionType.STRIP, strip, dir, mode); | ||||
|     }, | ||||
|  | ||||
|     setEditionMode: function (editionMode) { | ||||
|         if (this._editionMode == editionMode) | ||||
|             return; | ||||
|  | ||||
|         this._editionMode = editionMode; | ||||
|         this._syncEditionMode(); | ||||
|     }, | ||||
|  | ||||
|     destroy: function () { | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function () { | ||||
|         Main.popModal(this.actor); | ||||
|         this._actionEditor.close(); | ||||
|  | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
|         if (this._deviceRemovedId != 0) { | ||||
|             deviceManager.disconnect(this._deviceRemovedId); | ||||
|             this._deviceRemovedId = 0; | ||||
|         } | ||||
|         if (this._deviceAddedId != 0) { | ||||
|             deviceManager.disconnect(this._deviceAddedId); | ||||
|             this._deviceAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._capturedEventId != 0) { | ||||
|             global.stage.disconnect(this._capturedEventId); | ||||
|             this._capturedEventId = 0; | ||||
|         } | ||||
|  | ||||
|         this.actor = null; | ||||
|         this.emit('closed'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PadOsd.prototype); | ||||
|  | ||||
| const PadOsdIface = '<node> \ | ||||
| <interface name="org.gnome.Shell.Wacom.PadOsd"> \ | ||||
| <method name="Show"> \ | ||||
|     <arg name="device_node" direction="in" type="o"/> \ | ||||
|     <arg name="edition_mode" direction="in" type="b"/> \ | ||||
| </method> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const PadOsdService = new Lang.Class({ | ||||
|     Name: 'PadOsdService', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom'); | ||||
|         Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     ShowAsync: function(params, invocation) { | ||||
|         let [deviceNode, editionMode] = params; | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
|         let devices = deviceManager.list_devices(); | ||||
|         let padDevice = null; | ||||
|  | ||||
|         devices.forEach(Lang.bind(this, function(device) { | ||||
|             if (deviceNode == device.get_device_node()) | ||||
|                 padDevice = device; | ||||
|         })); | ||||
|  | ||||
|         if (padDevice == null || | ||||
|             padDevice.get_device_type() != Clutter.InputDeviceType.PAD_DEVICE) { | ||||
|             invocation.return_error_literal(Gio.IOErrorEnum, | ||||
|                                             Gio.IOErrorEnum.CANCELLED, | ||||
|                                             "Invalid params"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         global.display.request_pad_osd(padDevice, editionMode); | ||||
|         invocation.return_value(null); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PadOsdService.prototype); | ||||
							
								
								
									
										103
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						| @@ -25,12 +25,12 @@ const RemoteMenu = imports.ui.remoteMenu; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const PANEL_ICON_SIZE = 24; | ||||
| const APP_MENU_ICON_MARGIN = 2; | ||||
| const PANEL_ICON_SIZE = 16; | ||||
| const APP_MENU_ICON_MARGIN = 0; | ||||
|  | ||||
| const BUTTON_DND_ACTIVATION_TIMEOUT = 250; | ||||
|  | ||||
| const SPINNER_ANIMATION_TIME = 0.2; | ||||
| const SPINNER_ANIMATION_TIME = 1.0; | ||||
|  | ||||
| // To make sure the panel corners blend nicely with the panel, | ||||
| // we draw background and borders the same way, e.g. drawing | ||||
| @@ -95,6 +95,7 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._startingApps = []; | ||||
|  | ||||
|         this._menuManager = panel.menuManager; | ||||
|         this._gtkSettings = Gtk.Settings.get_default(); | ||||
|         this._targetApp = null; | ||||
|         this._appMenuNotifyId = 0; | ||||
|         this._actionGroupNotifyId = 0; | ||||
| @@ -123,11 +124,14 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM); | ||||
|         this._container.add_actor(this._arrow); | ||||
|  | ||||
|         this._visible = !Main.overview.visible; | ||||
|         this._visible = this._gtkSettings.gtk_shell_shows_app_menu && | ||||
|                         !Main.overview.visible; | ||||
|         if (!this._visible) | ||||
|             this.actor.hide(); | ||||
|         this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync)); | ||||
|         this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync)); | ||||
|         this._showsAppMenuId = this._gtkSettings.connect('notify::gtk-shell-shows-app-menu', | ||||
|                                                          Lang.bind(this, this._sync)); | ||||
|  | ||||
|         this._stop = true; | ||||
|  | ||||
| @@ -305,7 +309,9 @@ const AppMenuButton = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let visible = (this._targetApp != null && !Main.overview.visibleTarget); | ||||
|         let visible = (this._targetApp != null && | ||||
|                        this._gtkSettings.gtk_shell_shows_app_menu && | ||||
|                        !Main.overview.visibleTarget); | ||||
|         if (visible) | ||||
|             this.show(); | ||||
|         else | ||||
| @@ -378,6 +384,10 @@ const AppMenuButton = new Lang.Class({ | ||||
|             Main.overview.disconnect(this._overviewShowingId); | ||||
|             this._overviewShowingId = 0; | ||||
|         } | ||||
|         if (this._showsAppMenuId > 0) { | ||||
|             this._gtkSettings.disconnect(this._showsAppMenuId); | ||||
|             this._showsAppMenuId = 0; | ||||
|         } | ||||
|         if (this._switchWorkspaceNotifyId > 0) { | ||||
|             global.window_manager.disconnect(this._switchWorkspaceNotifyId); | ||||
|             this._switchWorkspaceNotifyId = 0; | ||||
| @@ -449,7 +459,8 @@ const ActivitiesButton = new Lang.Class({ | ||||
|  | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END || | ||||
|             event.type() == Clutter.EventType.BUTTON_RELEASE) | ||||
|             Main.overview.toggle(); | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 Main.overview.toggle(); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
| @@ -457,7 +468,8 @@ const ActivitiesButton = new Lang.Class({ | ||||
|     _onKeyRelease: function(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { | ||||
|             Main.overview.toggle(); | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 Main.overview.toggle(); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
| @@ -642,14 +654,50 @@ const PanelCorner = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AggregateLayout = new Lang.Class({ | ||||
|     Name: 'AggregateLayout', | ||||
|     Extends: Clutter.BoxLayout, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         if (!params) | ||||
|             params = {}; | ||||
|         params['orientation'] = Clutter.Orientation.VERTICAL; | ||||
|         this.parent(params); | ||||
|  | ||||
|         this._sizeChildren = []; | ||||
|     }, | ||||
|  | ||||
|     addSizeChild: function(actor) { | ||||
|         this._sizeChildren.push(actor); | ||||
|         this.layout_changed(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         let themeNode = container.get_theme_node(); | ||||
|         let minWidth = themeNode.get_min_width(); | ||||
|         let natWidth = minWidth; | ||||
|  | ||||
|         for (let i = 0; i < this._sizeChildren.length; i++) { | ||||
|             let child = this._sizeChildren[i]; | ||||
|             let [childMin, childNat] = child.get_preferred_width(forHeight); | ||||
|             minWidth = Math.max(minWidth, childMin); | ||||
|             natWidth = Math.max(minWidth, childNat); | ||||
|         } | ||||
|         return [minWidth, natWidth]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AggregateMenu = new Lang.Class({ | ||||
|     Name: 'AggregateMenu', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(0.0, _("Settings"), false); | ||||
|         this.parent(0.0, C_("System menu in the top bar", "System"), false); | ||||
|         this.menu.actor.add_style_class_name('aggregate-menu'); | ||||
|  | ||||
|         let menuLayout = new AggregateLayout(); | ||||
|         this.menu.box.set_layout_manager(menuLayout); | ||||
|  | ||||
|         this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' }); | ||||
|         this.actor.add_child(this._indicators); | ||||
|  | ||||
| @@ -671,9 +719,11 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this._system = new imports.ui.status.system.Indicator(); | ||||
|         this._screencast = new imports.ui.status.screencast.Indicator(); | ||||
|         this._location = new imports.ui.status.location.Indicator(); | ||||
|         this._nightLight = new imports.ui.status.nightLight.Indicator(); | ||||
|  | ||||
|         this._indicators.add_child(this._screencast.indicators); | ||||
|         this._indicators.add_child(this._location.indicators); | ||||
|         this._indicators.add_child(this._nightLight.indicators); | ||||
|         if (this._network) { | ||||
|             this._indicators.add_child(this._network.indicators); | ||||
|         } | ||||
| @@ -697,8 +747,13 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this.menu.addMenuItem(this._location.menu); | ||||
|         this.menu.addMenuItem(this._rfkill.menu); | ||||
|         this.menu.addMenuItem(this._power.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(this._nightLight.menu); | ||||
|         this.menu.addMenuItem(this._system.menu); | ||||
|  | ||||
|         menuLayout.addSizeChild(this._location.menu.actor); | ||||
|         menuLayout.addSizeChild(this._rfkill.menu.actor); | ||||
|         menuLayout.addSizeChild(this._power.menu.actor); | ||||
|         menuLayout.addSizeChild(this._system.menu.actor); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| @@ -933,6 +988,14 @@ const Panel = new Lang.Class({ | ||||
|         this._updateBox(panel.center, this._centerBox); | ||||
|         this._updateBox(panel.right, this._rightBox); | ||||
|  | ||||
|         if (panel.left.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; | ||||
|         else if (panel.right.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; | ||||
|         // Default to center if there is no dateMenu | ||||
|         else | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; | ||||
|  | ||||
|         if (this._sessionStyle) | ||||
|             this._removeStyleClassName(this._sessionStyle); | ||||
|  | ||||
| @@ -995,6 +1058,7 @@ const Panel = new Lang.Class({ | ||||
|         if (parent) | ||||
|             parent.remove_actor(container); | ||||
|  | ||||
|  | ||||
|         box.insert_child_at_index(container, position); | ||||
|         if (indicator.menu) | ||||
|             this.menuManager.addMenu(indicator.menu); | ||||
| @@ -1004,6 +1068,8 @@ const Panel = new Lang.Class({ | ||||
|             emitter.disconnect(destroyId); | ||||
|             container.destroy(); | ||||
|         })); | ||||
|         indicator.connect('menu-set', Lang.bind(this, this._onMenuSet)); | ||||
|         this._onMenuSet(indicator); | ||||
|     }, | ||||
|  | ||||
|     addToStatusArea: function(role, indicator, position, box) { | ||||
| @@ -1035,5 +1101,24 @@ const Panel = new Lang.Class({ | ||||
|         this.actor.remove_style_class_name(className); | ||||
|         this._rightCorner.actor.remove_style_class_name(className); | ||||
|         this._leftCorner.actor.remove_style_class_name(className); | ||||
|     }, | ||||
|  | ||||
|     _onMenuSet: function(indicator) { | ||||
|         if (!indicator.menu || indicator.menu.hasOwnProperty('_openChangedId')) | ||||
|             return; | ||||
|  | ||||
|         indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', | ||||
|             Lang.bind(this, function(menu, isOpen) { | ||||
|                 let boxAlignment; | ||||
|                 if (this._leftBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.START; | ||||
|                 else if (this._centerBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.CENTER; | ||||
|                 else if (this._rightBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.END; | ||||
|  | ||||
|                 if (boxAlignment == Main.messageTray.bannerAlignment) | ||||
|                     Main.messageTray.bannerBlocked = isOpen; | ||||
|             })); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -128,6 +128,7 @@ const Button = new Lang.Class({ | ||||
|             Main.uiGroup.add_actor(this.menu.actor); | ||||
|             this.menu.actor.hide(); | ||||
|         } | ||||
|         this.emit('menu-set'); | ||||
|     }, | ||||
|  | ||||
|     _onEvent: function(actor, event) { | ||||
| @@ -173,8 +174,14 @@ const Button = new Lang.Class({ | ||||
|         // menu is higher then the screen; it's useful if part of the menu is | ||||
|         // scrollable so the minimum height is smaller than the natural height | ||||
|         let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let verticalMargins = this.menu.actor.margin_top + this.menu.actor.margin_bottom; | ||||
|         this.menu.actor.style = ('max-height: ' + Math.round(workArea.height - verticalMargins) + 'px;'); | ||||
|  | ||||
|         // The workarea and margin dimensions are in physical pixels, but CSS | ||||
|         // measures are in logical pixels, so make sure to consider the scale | ||||
|         // factor when computing max-height | ||||
|         let maxHeight = Math.round((workArea.height - verticalMargins) / scaleFactor); | ||||
|         this.menu.actor.style = ('max-height: %spx;').format(maxHeight); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|   | ||||
| @@ -396,6 +396,7 @@ const PopupImageMenuItem = new Lang.Class({ | ||||
|         this.actor.add_child(this.label); | ||||
|         this._icon = new St.Icon({ style_class: 'popup-menu-icon' }); | ||||
|         this.actor.add_child(this._icon, { align: St.Align.END }); | ||||
|         this.actor.label_actor = this.label; | ||||
|  | ||||
|         this.setIcon(iconName); | ||||
|     }, | ||||
| @@ -604,6 +605,24 @@ const PopupMenuBase = new Lang.Class({ | ||||
|         menuItem.actor.show(); | ||||
|     }, | ||||
|  | ||||
|     moveMenuItem: function(menuItem, position) { | ||||
|         let items = this._getMenuItems(); | ||||
|         let i = 0; | ||||
|  | ||||
|         while (i < items.length && position > 0) { | ||||
|                 if (items[i] != menuItem) | ||||
|                         position--; | ||||
|                 i++; | ||||
|         } | ||||
|  | ||||
|         if (i < items.length) { | ||||
|                 if (items[i] != menuItem) | ||||
|                         this.box.set_child_below_sibling(menuItem.actor, items[i].actor); | ||||
|         } else { | ||||
|                 this.box.set_child_above_sibling(menuItem.actor, null); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     addMenuItem: function(menuItem, position) { | ||||
|         let before_item = null; | ||||
|         if (position == undefined) { | ||||
| @@ -766,6 +785,11 @@ const PopupMenu = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onKeyPress: function(actor, event) { | ||||
|         // Disable toggling the menu by keyboard | ||||
|         // when it cannot be toggled by pointer | ||||
|         if (!actor.reactive) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let navKey; | ||||
|         switch (this._boxPointer.arrowSide) { | ||||
|             case St.Side.TOP: | ||||
| @@ -782,6 +806,16 @@ const PopupMenu = new Lang.Class({ | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         let state = event.get_state(); | ||||
|  | ||||
|         // if user has a modifier down (except capslock) | ||||
|         // then don't handle the key press here | ||||
|         state &= ~Clutter.ModifierType.LOCK_MASK; | ||||
|         state &= Clutter.ModifierType.MODIFIER_MASK; | ||||
|  | ||||
|         if (state) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { | ||||
|             this.toggle(); | ||||
| @@ -1059,11 +1093,6 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         let expander = new St.Bin({ style_class: 'popup-menu-item-expander' }); | ||||
|         this.actor.add(expander, { expand: true }); | ||||
|  | ||||
|         this.status = new St.Label({ style_class: 'popup-status-menu-item', | ||||
|                                      y_expand: true, | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_child(this.status); | ||||
|  | ||||
|         this._triangle = arrowIcon(St.Side.RIGHT); | ||||
|         this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); | ||||
|  | ||||
| @@ -1150,6 +1179,16 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         this.actor.remove_style_pseudo_class ('active'); | ||||
|         this._setOpenState(!this._getOpenState()); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent: function(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END) { | ||||
|             // Since we override the parent, we need to manage what the parent does | ||||
|             // with the active style class | ||||
|             this.actor.remove_style_pseudo_class ('active'); | ||||
|             this._setOpenState(!this._getOpenState()); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -192,6 +192,10 @@ const RemoteMenu = new Lang.Class({ | ||||
|                                               _removeItem.bind(null, this)); | ||||
|     }, | ||||
|  | ||||
|     get actionGroup() { | ||||
|         return this._actionGroup; | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this._tracker.destroy(); | ||||
|         this.parent(); | ||||
|   | ||||
| @@ -200,6 +200,7 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|         this.appInfo = appInfo; | ||||
|         this.id = appInfo.get_id(); | ||||
|         this.isRemoteProvider = true; | ||||
|         this.canLaunchSearch = false; | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size, meta) { | ||||
| @@ -278,7 +279,8 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|                                name: metas[i]['name'], | ||||
|                                description: metas[i]['description'], | ||||
|                                createIcon: Lang.bind(this, | ||||
|                                                      this.createIcon, metas[i]) }); | ||||
|                                                      this.createIcon, metas[i]), | ||||
|                                clipboardText: metas[i]['clipboardText'] }); | ||||
|         } | ||||
|         callback(resultMetas); | ||||
|     }, | ||||
| @@ -297,7 +299,7 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|         // the provider is not compatible with the new version of the interface, launch | ||||
|         // the app itself but warn so we can catch the error in logs | ||||
|         log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch'); | ||||
|         this.appInfo.launch([], global.create_app_launch_context(0, -1)); | ||||
|         this.appInfo.launch([], global.create_app_launch_context(0, -1), false); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -61,6 +61,7 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|                                    // rt is short for "reload theme" | ||||
|                                    'rt': Lang.bind(this, function() { | ||||
|                                        Main.reloadThemeResource(); | ||||
|                                        Main.loadTheme(); | ||||
|                                    }) | ||||
|                                  }; | ||||
| @@ -273,7 +274,7 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|     _restart: function() { | ||||
|         if (Meta.is_wayland_compositor()) { | ||||
|             this._showError('Restart is not available on Wayland'); | ||||
|             this._showError(_("Restart is not available on Wayland")); | ||||
|             return; | ||||
|         } | ||||
|         this._shouldFadeOut = false; | ||||
|   | ||||
| @@ -33,6 +33,9 @@ const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOCK_ENABLED_KEY = 'lock-enabled'; | ||||
| const LOCK_DELAY_KEY = 'lock-delay'; | ||||
|  | ||||
| const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; | ||||
| const DISABLE_LOCK_KEY = 'disable-lock-screen'; | ||||
|  | ||||
| const LOCKED_STATE_STR = 'screenShield.locked'; | ||||
| // fraction of screen height the arrow must reach before completing | ||||
| // the slide up automatically | ||||
| @@ -349,7 +352,6 @@ const Arrow = new Lang.Class({ | ||||
|     _init: function(params) { | ||||
|         this.parent(params); | ||||
|         this.x_fill = this.y_fill = true; | ||||
|         this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); | ||||
|  | ||||
|         this._drawingArea = new St.DrawingArea(); | ||||
|         this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow)); | ||||
| @@ -377,6 +379,22 @@ const Arrow = new Lang.Class({ | ||||
|         cr.$dispose(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_paint_volume: function(volume) { | ||||
|         if (!this.parent(volume)) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._shadow) | ||||
|             return true; | ||||
|  | ||||
|         let shadow_box = new Clutter.ActorBox(); | ||||
|         this._shadow.get_box(this._drawingArea.get_allocation_box(), shadow_box); | ||||
|  | ||||
|         volume.set_width(Math.max(shadow_box.x2 - shadow_box.x1, volume.get_width())); | ||||
|         volume.set_height(Math.max(shadow_box.y2 - shadow_box.y1, volume.get_height())); | ||||
|  | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     vfunc_style_changed: function() { | ||||
|         let node = this.get_theme_node(); | ||||
|         this._shadow = node.get_shadow('-arrow-shadow'); | ||||
| @@ -384,6 +402,8 @@ const Arrow = new Lang.Class({ | ||||
|             this._shadowHelper = St.ShadowHelper.new(this._shadow); | ||||
|         else | ||||
|             this._shadowHelper = null; | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_paint: function() { | ||||
| @@ -507,21 +527,25 @@ const ScreenShield = new Lang.Class({ | ||||
|                                                       this._liftShield(true, 0); | ||||
|                                               })); | ||||
|  | ||||
|         this._inhibitor = null; | ||||
|         this._aboutToSuspend = false; | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         this._loginManager.connect('prepare-for-sleep', | ||||
|                                    Lang.bind(this, this._prepareForSleep)); | ||||
|         this._inhibitSuspend(); | ||||
|  | ||||
|         this._loginSession = null; | ||||
|         this._loginManager.getCurrentSessionProxy(Lang.bind(this, | ||||
|             function(sessionProxy) { | ||||
|                 this._loginSession = sessionProxy; | ||||
|                 this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); })); | ||||
|                 this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); })); | ||||
|                 this._loginSession.connect('g-properties-changed', Lang.bind(this, this._syncInhibitor)); | ||||
|                 this._syncInhibitor(); | ||||
|             })); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA }); | ||||
|         this._settings.connect('changed::' + LOCK_ENABLED_KEY, Lang.bind(this, this._syncInhibitor)); | ||||
|  | ||||
|         this._lockSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA }); | ||||
|         this._lockSettings.connect('changed::' + DISABLE_LOCK_KEY, Lang.bind(this, this._syncInhibitor)); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._hasLockScreen = false; | ||||
| @@ -547,6 +571,21 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|         this.idleMonitor = Meta.IdleMonitor.get_core(); | ||||
|         this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _setActive: function(active) { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = active; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         if (this._loginSession) | ||||
|             this._loginSession.SetLockedHintRemote(active); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _createBackground: function(monitorIndex) { | ||||
| @@ -632,7 +671,10 @@ const ScreenShield = new Lang.Class({ | ||||
|         let isEnter = (symbol == Clutter.KEY_Return || | ||||
|                        symbol == Clutter.KEY_KP_Enter || | ||||
|                        symbol == Clutter.KEY_ISO_Enter); | ||||
|         if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape)) | ||||
|         let isEscape = (symbol == Clutter.KEY_Escape); | ||||
|         let isLiftChar = (GLib.unichar_isprint(unichar) && | ||||
|                           (this._isLocked || !GLib.unichar_isgraph(unichar))); | ||||
|         if (!isEnter && !isEscape && !isLiftChar) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._isLocked && | ||||
| @@ -664,31 +706,30 @@ const ScreenShield = new Lang.Class({ | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _inhibitSuspend: function() { | ||||
|         this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                    Lang.bind(this, function(inhibitor) { | ||||
|                                        this._inhibitor = inhibitor; | ||||
|                                    })); | ||||
|     }, | ||||
|  | ||||
|     _uninhibitSuspend: function() { | ||||
|         if (this._inhibitor) | ||||
|             this._inhibitor.close(null); | ||||
|         this._inhibitor = null; | ||||
|     _syncInhibitor: function() { | ||||
|         let lockEnabled = this._settings.get_boolean(LOCK_ENABLED_KEY); | ||||
|         let lockLocked = this._lockSettings.get_boolean(DISABLE_LOCK_KEY); | ||||
|         let inhibit = (this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && lockEnabled && !lockLocked); | ||||
|         if (inhibit) { | ||||
|             this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                        Lang.bind(this, function(inhibitor) { | ||||
|                                            if (this._inhibitor) | ||||
|                                                this._inhibitor.close(null); | ||||
|                                            this._inhibitor = inhibitor; | ||||
|                                        })); | ||||
|         } else { | ||||
|             if (this._inhibitor) | ||||
|                 this._inhibitor.close(null); | ||||
|             this._inhibitor = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _prepareForSleep: function(loginManager, aboutToSuspend) { | ||||
|         this._aboutToSuspend = aboutToSuspend; | ||||
|  | ||||
|         if (aboutToSuspend) { | ||||
|             if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) { | ||||
|                 this._uninhibitSuspend(); | ||||
|                 return; | ||||
|             } | ||||
|             this.lock(true); | ||||
|             if (this._settings.get_boolean(LOCK_ENABLED_KEY)) | ||||
|                 this.lock(true); | ||||
|         } else { | ||||
|             this._inhibitSuspend(); | ||||
|  | ||||
|             this._wakeUpScreen(); | ||||
|         } | ||||
|     }, | ||||
| @@ -698,7 +739,7 @@ const ScreenShield = new Lang.Class({ | ||||
|         let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1); | ||||
|         let maxOpacity = 255 * ARROW_ANIMATION_PEAK_OPACITY; | ||||
|         for (let i = 0; i < arrows.length; i++) { | ||||
|             arrows.opacity = 0; | ||||
|             arrows[i].opacity = 0; | ||||
|             Tweener.addTween(arrows[i], | ||||
|                              { opacity: 0, | ||||
|                                delay: unitaryDelay * (N_ARROWS - (i + 1)), | ||||
| @@ -817,6 +858,7 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _activateFade: function(lightbox, time) { | ||||
|         Main.uiGroup.set_child_above_sibling(lightbox.actor, null); | ||||
|         lightbox.show(time); | ||||
|  | ||||
|         if (this._becameActiveId == 0) | ||||
| @@ -1083,15 +1125,7 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _completeLockScreenShown: function() { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = true; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         if (this._aboutToSuspend) | ||||
|             this._uninhibitSuspend(); | ||||
|  | ||||
|         this._setActive(true); | ||||
|         this.emit('lock-screen-shown'); | ||||
|     }, | ||||
|  | ||||
| @@ -1185,8 +1219,7 @@ const ScreenShield = new Lang.Class({ | ||||
|             // gnome-settings-daemon will stop blanking the screen | ||||
|  | ||||
|             this._activationTime = 0; | ||||
|             this._isActive = false; | ||||
|             this.emit('active-changed'); | ||||
|             this._setActive(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -1229,9 +1262,8 @@ const ScreenShield = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         this._activationTime = 0; | ||||
|         this._isActive = false; | ||||
|         this._setActive(false); | ||||
|         this._isLocked = false; | ||||
|         this.emit('active-changed'); | ||||
|         this.emit('locked-changed'); | ||||
|         global.set_runtime_state(LOCKED_STATE_STR, null); | ||||
|     }, | ||||
| @@ -1266,6 +1298,11 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     lock: function(animate) { | ||||
|         if (this._lockSettings.get_boolean(DISABLE_LOCK_KEY)) { | ||||
|             log('Screen lock is locked down, not locking') // lock, lock - who's there? | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Warn the user if we can't become modal | ||||
|         if (!this._becomeModal()) { | ||||
|             Main.notifyError(_("Unable to lock"), | ||||
|   | ||||
| @@ -152,6 +152,8 @@ const SearchResultsBase = new Lang.Class({ | ||||
|  | ||||
|         this._resultDisplays = {}; | ||||
|  | ||||
|         this._clipboard = St.Clipboard.get_default(); | ||||
|  | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|     }, | ||||
|  | ||||
| @@ -181,6 +183,8 @@ const SearchResultsBase = new Lang.Class({ | ||||
|  | ||||
|     _activateResult: function(result, id) { | ||||
|         this.provider.activateResult(id, this._terms); | ||||
|         if (result.metaInfo.clipboardText) | ||||
|             this._clipboard.set_text(St.ClipboardType.CLIPBOARD, result.metaInfo.clipboardText); | ||||
|         Main.overview.toggle(); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -142,15 +142,15 @@ const GnomeShell = new Lang.Class({ | ||||
|         for (let param in params) | ||||
|             params[param] = params[param].deep_unpack(); | ||||
|  | ||||
|         let monitorIndex = -1; | ||||
|         if (params['monitor'] >= 0) | ||||
|             monitorIndex = params['monitor']; | ||||
|         let monitorIndex = params['monitor'] || -1; | ||||
|         let label = params['label'] || undefined; | ||||
|         let level = params['level'] || undefined; | ||||
|  | ||||
|         let icon = null; | ||||
|         if (params['icon']) | ||||
|             icon = Gio.Icon.new_for_string(params['icon']); | ||||
|  | ||||
|         Main.osdWindowManager.show(monitorIndex, icon, params['label'], params['level']); | ||||
|         Main.osdWindowManager.show(monitorIndex, icon, label, level); | ||||
|     }, | ||||
|  | ||||
|     FocusApp: function(id) { | ||||
|   | ||||
| @@ -380,7 +380,7 @@ const ShellMountPasswordDialog = new Lang.Class({ | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|  | ||||
|         this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label', | ||||
|                                                  text: _("Sorry, that didn\'t work. Please try again.") }); | ||||
|                                                  text: _("Sorry, that didn’t work. Please try again.") }); | ||||
|         this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._errorMessageLabel.clutter_text.line_wrap = true; | ||||
|         this._errorMessageLabel.hide(); | ||||
|   | ||||
| @@ -137,6 +137,10 @@ const Slider = new Lang.Class({ | ||||
|             this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent)); | ||||
|         } | ||||
|  | ||||
|         // We need to emit 'drag-begin' before moving the handle to make | ||||
|         // sure that no 'value-changed' signal is emitted before this one. | ||||
|         this.emit('drag-begin'); | ||||
|  | ||||
|         let absX, absY; | ||||
|         [absX, absY] = event.get_coords(); | ||||
|         this._moveHandle(absX, absY); | ||||
| @@ -224,6 +228,7 @@ const Slider = new Lang.Class({ | ||||
|             let delta = key == Clutter.KEY_Right ? 0.1 : -0.1; | ||||
|             this._value = Math.max(0, Math.min(this._value + delta, 1)); | ||||
|             this.actor.queue_repaint(); | ||||
|             this.emit('drag-begin'); | ||||
|             this.emit('value-changed', this._value); | ||||
|             this.emit('drag-end'); | ||||
|             return Clutter.EVENT_STOP; | ||||
|   | ||||
| @@ -102,8 +102,8 @@ const ATIndicator = new Lang.Class({ | ||||
|         if (this._syncMenuVisibilityIdle) | ||||
|             return; | ||||
|  | ||||
|         this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility)); | ||||
|         GLib.Source.set_name_by_id(this._syncMenuVisbilityIdle, '[gnome-shell] this._syncMenuVisibility'); | ||||
|         this._syncMenuVisibilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility)); | ||||
|         GLib.Source.set_name_by_id(this._syncMenuVisibilityIdle, '[gnome-shell] this._syncMenuVisibility'); | ||||
|     }, | ||||
|  | ||||
|     _buildItemExtended: function(string, initial_value, writable, on_set) { | ||||
|   | ||||