Compare commits
	
		
			1590 Commits
		
	
	
		
			wip/window
			...
			wip/notif-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					50395cded8 | ||
| 
						 | 
					e8ae8f75a0 | ||
| 
						 | 
					2b93bcf921 | ||
| 
						 | 
					a80b8f7791 | ||
| 
						 | 
					62c6563a3a | ||
| 
						 | 
					c22068d288 | ||
| 
						 | 
					d519a0a181 | ||
| 
						 | 
					ef1ab043a3 | ||
| 
						 | 
					fa350bf41e | ||
| 
						 | 
					ae74dbd1bb | ||
| 
						 | 
					afa5a871b3 | ||
| 
						 | 
					e13c0ca9e8 | ||
| 
						 | 
					f29d1beb3e | ||
| 
						 | 
					751a1b5bbe | ||
| 
						 | 
					00a32f53f5 | ||
| 
						 | 
					94688d354f | ||
| 
						 | 
					7f17acd0ea | ||
| 
						 | 
					8c74a4fee0 | ||
| 
						 | 
					234b90ac86 | ||
| 
						 | 
					ce46b06f36 | ||
| 
						 | 
					585930123d | ||
| 
						 | 
					5d00c1a5ee | ||
| 
						 | 
					f288c43e6e | ||
| 
						 | 
					b981a591c7 | ||
| 
						 | 
					3b7756b610 | ||
| 
						 | 
					292f87caf7 | ||
| 
						 | 
					ae2751a68b | ||
| 
						 | 
					589becbc79 | ||
| 
						 | 
					4c7fcf272c | ||
| 
						 | 
					e51aecee03 | ||
| 
						 | 
					d98e7dbd4a | ||
| 
						 | 
					1b97778925 | ||
| 
						 | 
					829e7623df | ||
| 
						 | 
					89675c9061 | ||
| 
						 | 
					f9df83802d | ||
| 
						 | 
					e51eb723fc | ||
| 
						 | 
					5b61f2d642 | ||
| 
						 | 
					094669baee | ||
| 
						 | 
					f6b5385495 | ||
| 
						 | 
					524e2df708 | ||
| 
						 | 
					9f887d9a28 | ||
| 
						 | 
					214a41793f | ||
| 
						 | 
					2e40ffc558 | ||
| 
						 | 
					3584887938 | ||
| 
						 | 
					0d6c002b8e | ||
| 
						 | 
					ec714864f2 | ||
| 
						 | 
					1b77149ec9 | ||
| 
						 | 
					93c9e031e3 | ||
| 
						 | 
					3ff4277f86 | ||
| 
						 | 
					2a63267be0 | ||
| 
						 | 
					2b365627ed | ||
| 
						 | 
					b8f0d0f0dc | ||
| 
						 | 
					df1bed941d | ||
| 
						 | 
					604085fdb9 | ||
| 
						 | 
					2d3c81390b | ||
| 
						 | 
					516b8f6bf8 | ||
| 
						 | 
					75fdca0b47 | ||
| 
						 | 
					9f366118f0 | ||
| 
						 | 
					c228a9a89a | ||
| 
						 | 
					e747fcb16f | ||
| 
						 | 
					a72a24ebff | ||
| 
						 | 
					8811ba2ec0 | ||
| 
						 | 
					38d8e465b3 | ||
| 
						 | 
					5b3fb024be | ||
| 
						 | 
					521f5f2b6b | ||
| 
						 | 
					e1b30b2924 | ||
| 
						 | 
					dd85670f8b | ||
| 
						 | 
					625f3a5113 | ||
| 
						 | 
					58c4a6c847 | ||
| 
						 | 
					752aca811c | ||
| 
						 | 
					a4cf0501ee | ||
| 
						 | 
					4c7b992c36 | ||
| 
						 | 
					264a51de3f | ||
| 
						 | 
					625280bcd6 | ||
| 
						 | 
					4f8265f82e | ||
| 
						 | 
					ff07d3a46a | ||
| 
						 | 
					5d11941638 | ||
| 
						 | 
					f1f659571b | ||
| 
						 | 
					c60d13b33e | ||
| 
						 | 
					33060d382b | ||
| 
						 | 
					4b1e412ac3 | ||
| 
						 | 
					659730ab09 | ||
| 
						 | 
					7c3a99b7ed | ||
| 
						 | 
					e7af257814 | ||
| 
						 | 
					c9190294bc | ||
| 
						 | 
					e070e3c44a | ||
| 
						 | 
					fe87de7cec | ||
| 
						 | 
					2519e4f08d | ||
| 
						 | 
					fe304d3c94 | ||
| 
						 | 
					1f4e6872ab | ||
| 
						 | 
					5ade2e7418 | ||
| 
						 | 
					3e7d325e77 | ||
| 
						 | 
					ef04a9d1ed | ||
| 
						 | 
					011fef4b2b | ||
| 
						 | 
					caf6389f79 | ||
| 
						 | 
					c7306449ae | ||
| 
						 | 
					8c45e6fa43 | ||
| 
						 | 
					9504d21297 | ||
| 
						 | 
					0832ca544a | ||
| 
						 | 
					a000a1f76e | ||
| 
						 | 
					ea8b02ff7f | ||
| 
						 | 
					3cc7112283 | ||
| 
						 | 
					50f0fc4e23 | ||
| 
						 | 
					0a4ad01d8a | ||
| 
						 | 
					19afabe2a1 | ||
| 
						 | 
					103027a446 | ||
| 
						 | 
					28c1f81f4a | ||
| 
						 | 
					82ec6c08b8 | ||
| 
						 | 
					551e57406d | ||
| 
						 | 
					1c8036b863 | ||
| 
						 | 
					f8bac5c197 | ||
| 
						 | 
					6ece67b654 | ||
| 
						 | 
					96411dfed5 | ||
| 
						 | 
					24897169a9 | ||
| 
						 | 
					05ddece9a0 | ||
| 
						 | 
					df08ae7996 | ||
| 
						 | 
					a244c1e987 | ||
| 
						 | 
					bc182f78b1 | ||
| 
						 | 
					2d18b06b3f | ||
| 
						 | 
					61852df9a1 | ||
| 
						 | 
					3969be38bd | ||
| 
						 | 
					92f9aff784 | ||
| 
						 | 
					815cfe6c20 | ||
| 
						 | 
					052b5176d2 | ||
| 
						 | 
					bccbcd8e4a | ||
| 
						 | 
					90589fabee | ||
| 
						 | 
					5947111f14 | ||
| 
						 | 
					c8c56a5443 | ||
| 
						 | 
					a83b9ed6d7 | ||
| 
						 | 
					f9c83bccb6 | ||
| 
						 | 
					2f720e22fc | ||
| 
						 | 
					329028d3b9 | ||
| 
						 | 
					5c3f9f6999 | ||
| 
						 | 
					4a6b89d44c | ||
| 
						 | 
					7fa1834ab6 | ||
| 
						 | 
					c7bc9f8925 | ||
| 
						 | 
					70d75ca311 | ||
| 
						 | 
					9830b3b8f7 | ||
| 
						 | 
					525c8780fd | ||
| 
						 | 
					76c4ec8ee4 | ||
| 
						 | 
					b4a48a7644 | ||
| 
						 | 
					5087f0930c | ||
| 
						 | 
					398cc5af85 | ||
| 
						 | 
					f073945d31 | ||
| 
						 | 
					c36abcb905 | ||
| 
						 | 
					e2ccbe5528 | ||
| 
						 | 
					68b2d5fcf0 | ||
| 
						 | 
					cd2bd7685a | ||
| 
						 | 
					85f811f147 | ||
| 
						 | 
					e8fd8b58d0 | ||
| 
						 | 
					2aa12e8f4b | ||
| 
						 | 
					ee0c76c2b9 | ||
| 
						 | 
					6ce6e77d2a | ||
| 
						 | 
					0117fcb0e7 | ||
| 
						 | 
					79bebe849d | ||
| 
						 | 
					25eadc5559 | ||
| 
						 | 
					a5784484e0 | ||
| 
						 | 
					a701b006c5 | ||
| 
						 | 
					2d68bbf94e | ||
| 
						 | 
					df305314c1 | ||
| 
						 | 
					d03239c009 | ||
| 
						 | 
					71ccad4399 | ||
| 
						 | 
					c916d43688 | ||
| 
						 | 
					3e6d0bc252 | ||
| 
						 | 
					79d0a848a4 | ||
| 
						 | 
					a6af33d450 | ||
| 
						 | 
					c6664adcce | ||
| 
						 | 
					4e8a9470d1 | ||
| 
						 | 
					1b88df9439 | ||
| 
						 | 
					ec42278654 | ||
| 
						 | 
					f435f249d0 | ||
| 
						 | 
					9c88fec4fc | ||
| 
						 | 
					9ecf466ce1 | ||
| 
						 | 
					a22fdea0e3 | ||
| 
						 | 
					90e52d7266 | ||
| 
						 | 
					74d9b6c2bf | ||
| 
						 | 
					b1e9873de5 | ||
| 
						 | 
					6fdc52a64a | ||
| 
						 | 
					b7d6792de9 | ||
| 
						 | 
					c78dc55e65 | ||
| 
						 | 
					af74bded14 | ||
| 
						 | 
					e3c9a9c3e4 | ||
| 
						 | 
					c68eecaf1c | ||
| 
						 | 
					b0bdf7f6c3 | ||
| 
						 | 
					d0f69a72dc | ||
| 
						 | 
					8d8c75d32d | ||
| 
						 | 
					e339e2658d | ||
| 
						 | 
					bc4a75a732 | ||
| 
						 | 
					e09e1bc3f5 | ||
| 
						 | 
					87c50eb495 | ||
| 
						 | 
					be291ee4f9 | ||
| 
						 | 
					e634b49859 | ||
| 
						 | 
					4dddaefa41 | ||
| 
						 | 
					cda60455f0 | ||
| 
						 | 
					a42b8870b0 | ||
| 
						 | 
					daa66a6de6 | ||
| 
						 | 
					6c6aed84bc | ||
| 
						 | 
					c59314acc1 | ||
| 
						 | 
					dd3cc78be5 | ||
| 
						 | 
					54b0b6eec5 | ||
| 
						 | 
					41654b22b3 | ||
| 
						 | 
					07cc60d65a | ||
| 
						 | 
					a7f9dc5114 | ||
| 
						 | 
					b5ae23d544 | ||
| 
						 | 
					75347cb4f7 | ||
| 
						 | 
					03a44b6ec2 | ||
| 
						 | 
					17d2349c49 | ||
| 
						 | 
					4e98c44052 | ||
| 
						 | 
					0bef281d66 | ||
| 
						 | 
					54af25ec24 | ||
| 
						 | 
					86ab02f400 | ||
| 
						 | 
					ae01cd143f | ||
| 
						 | 
					2974b29f15 | ||
| 
						 | 
					1b78dd662b | ||
| 
						 | 
					c22264a0ca | ||
| 
						 | 
					48c3e3f534 | ||
| 
						 | 
					6ccc134ba6 | ||
| 
						 | 
					9aa36d7851 | ||
| 
						 | 
					3278f77739 | ||
| 
						 | 
					5d24f48e3b | ||
| 
						 | 
					573c1c86cc | ||
| 
						 | 
					374c5967ba | ||
| 
						 | 
					66b71a36ce | ||
| 
						 | 
					e70fd5a57a | ||
| 
						 | 
					4589ce4d78 | ||
| 
						 | 
					d6197b0904 | ||
| 
						 | 
					30fb2b0d99 | ||
| 
						 | 
					5cdefc324d | ||
| 
						 | 
					b222d0fe44 | ||
| 
						 | 
					fe4fddf0d5 | ||
| 
						 | 
					c675c93733 | ||
| 
						 | 
					29485ff24b | ||
| 
						 | 
					f6ed3d9f88 | ||
| 
						 | 
					39a36cb510 | ||
| 
						 | 
					ff5550c82b | ||
| 
						 | 
					7d5ce1a159 | ||
| 
						 | 
					c492415386 | ||
| 
						 | 
					5616bbd45b | ||
| 
						 | 
					e117aa5297 | ||
| 
						 | 
					17ac1382df | ||
| 
						 | 
					057a026ea4 | ||
| 
						 | 
					6ce6e86318 | ||
| 
						 | 
					492558a2d2 | ||
| 
						 | 
					b78e00f372 | ||
| 
						 | 
					c2cc504837 | ||
| 
						 | 
					ac76940530 | ||
| 
						 | 
					55d1c7e2ab | ||
| 
						 | 
					fdf264ff64 | ||
| 
						 | 
					e917b7ce0f | ||
| 
						 | 
					ec6facb9e7 | ||
| 
						 | 
					60f3c09f90 | ||
| 
						 | 
					afdfd6cebc | ||
| 
						 | 
					edd66c40d9 | ||
| 
						 | 
					fc4bc5277a | ||
| 
						 | 
					821768a414 | ||
| 
						 | 
					522f3bf171 | ||
| 
						 | 
					fb7400ab85 | ||
| 
						 | 
					210128f22b | ||
| 
						 | 
					78ae233823 | ||
| 
						 | 
					8f25da7cea | ||
| 
						 | 
					7f52fdb435 | ||
| 
						 | 
					0ba05b29b9 | ||
| 
						 | 
					ef8123e3a2 | ||
| 
						 | 
					257e1f3096 | ||
| 
						 | 
					6441ae77d9 | ||
| 
						 | 
					3f0938072f | ||
| 
						 | 
					2fe06a28aa | ||
| 
						 | 
					38750ba798 | ||
| 
						 | 
					b4c01f8905 | ||
| 
						 | 
					104d70c88e | ||
| 
						 | 
					133a350f2f | ||
| 
						 | 
					496ab55357 | ||
| 
						 | 
					a391758e31 | ||
| 
						 | 
					eaf8ad4949 | ||
| 
						 | 
					2f583bdcf3 | ||
| 
						 | 
					db19012a41 | ||
| 
						 | 
					62be46884e | ||
| 
						 | 
					3f2e6a48a9 | ||
| 
						 | 
					ff124e5f74 | ||
| 
						 | 
					c07421c195 | ||
| 
						 | 
					de8348d3b9 | ||
| 
						 | 
					184df8a853 | ||
| 
						 | 
					12768a147c | ||
| 
						 | 
					94161cea37 | ||
| 
						 | 
					52a4ef7cf7 | ||
| 
						 | 
					3432f71500 | ||
| 
						 | 
					8282aa6c24 | ||
| 
						 | 
					59f9eaa1c9 | ||
| 
						 | 
					4433b735c4 | ||
| 
						 | 
					9cd30fa6b5 | ||
| 
						 | 
					2c7bbfb500 | ||
| 
						 | 
					51a1d23bf9 | ||
| 
						 | 
					c02e6e82bc | ||
| 
						 | 
					e37a3fa7e6 | ||
| 
						 | 
					e23c2ffecc | ||
| 
						 | 
					744f11d045 | ||
| 
						 | 
					b7eb1f3e8b | ||
| 
						 | 
					3f28091e52 | ||
| 
						 | 
					b4ee86955d | ||
| 
						 | 
					8b866efe92 | ||
| 
						 | 
					fb61ab8df7 | ||
| 
						 | 
					990956ece7 | ||
| 
						 | 
					414b592d53 | ||
| 
						 | 
					751154d9da | ||
| 
						 | 
					29addc499c | ||
| 
						 | 
					caa98de581 | ||
| 
						 | 
					1fd1ec4312 | ||
| 
						 | 
					f4607626e4 | ||
| 
						 | 
					b494c15e4b | ||
| 
						 | 
					3c0defa125 | ||
| 
						 | 
					f2df4d95de | ||
| 
						 | 
					fabcf20e06 | ||
| 
						 | 
					b9510b9ab7 | ||
| 
						 | 
					52a2ebad04 | ||
| 
						 | 
					89a2dc71fc | ||
| 
						 | 
					adb0de43d8 | ||
| 
						 | 
					e2a811a720 | ||
| 
						 | 
					5bcafc5c17 | ||
| 
						 | 
					2d24536caf | ||
| 
						 | 
					b088c4086b | ||
| 
						 | 
					3c58f4abd3 | ||
| 
						 | 
					e2a9b27b2b | ||
| 
						 | 
					fcd5f06c09 | ||
| 
						 | 
					6d93c8b3fd | ||
| 
						 | 
					2663e1be5d | ||
| 
						 | 
					0fa6be4614 | ||
| 
						 | 
					46163a6607 | ||
| 
						 | 
					645ef093f7 | ||
| 
						 | 
					7551e134da | ||
| 
						 | 
					5bec5fb6cb | ||
| 
						 | 
					c176af4da5 | ||
| 
						 | 
					2631f03108 | ||
| 
						 | 
					525c71658b | ||
| 
						 | 
					10e5778deb | ||
| 
						 | 
					6512a5fd6b | ||
| 
						 | 
					1af40b1345 | ||
| 
						 | 
					0418b68051 | ||
| 
						 | 
					a7283864e8 | ||
| 
						 | 
					4950bad2a7 | ||
| 
						 | 
					470ac0eae3 | ||
| 
						 | 
					87abbf9b20 | ||
| 
						 | 
					3e7e88cd5f | ||
| 
						 | 
					b7e1539699 | ||
| 
						 | 
					8492f2ba24 | ||
| 
						 | 
					737f4eb1c1 | ||
| 
						 | 
					58191ea66b | ||
| 
						 | 
					1f786df462 | ||
| 
						 | 
					fa4c481aed | ||
| 
						 | 
					d555fd7883 | ||
| 
						 | 
					fe7ece1f5a | ||
| 
						 | 
					2bb3aed729 | ||
| 
						 | 
					488a42696c | ||
| 
						 | 
					f43ff45683 | ||
| 
						 | 
					bde1451896 | ||
| 
						 | 
					fff2ca6f26 | ||
| 
						 | 
					bec57a6cee | ||
| 
						 | 
					a012ca4fac | ||
| 
						 | 
					3ba49b0a50 | ||
| 
						 | 
					314aa024b5 | ||
| 
						 | 
					598f750859 | ||
| 
						 | 
					8057848458 | ||
| 
						 | 
					e80c28a530 | ||
| 
						 | 
					5d05b66902 | ||
| 
						 | 
					59634b2cf5 | ||
| 
						 | 
					7c3892f5a2 | ||
| 
						 | 
					19406a238b | ||
| 
						 | 
					d6146197dd | ||
| 
						 | 
					38f241479c | ||
| 
						 | 
					aa45999824 | ||
| 
						 | 
					3b7593ed7f | ||
| 
						 | 
					f959cafb36 | ||
| 
						 | 
					e92d204d42 | ||
| 
						 | 
					f543161234 | ||
| 
						 | 
					9cc1017912 | ||
| 
						 | 
					fc719c19f9 | ||
| 
						 | 
					ad97fc6855 | ||
| 
						 | 
					407dc74502 | ||
| 
						 | 
					e5e764b402 | ||
| 
						 | 
					65ad65fe52 | ||
| 
						 | 
					8d09d20510 | ||
| 
						 | 
					5a5b04b2b0 | ||
| 
						 | 
					3113bac8e6 | ||
| 
						 | 
					9217f2c916 | ||
| 
						 | 
					32a49b7846 | ||
| 
						 | 
					12ef034b7b | ||
| 
						 | 
					e70e4a21f2 | ||
| 
						 | 
					7826fb4f04 | ||
| 
						 | 
					8f1b8909dc | ||
| 
						 | 
					3f7a989d38 | ||
| 
						 | 
					4d3fd7598d | ||
| 
						 | 
					620e3cef20 | ||
| 
						 | 
					812a61939e | ||
| 
						 | 
					793c6c2f7b | ||
| 
						 | 
					f8f4d0f646 | ||
| 
						 | 
					3a92aa751f | ||
| 
						 | 
					6882273aa0 | ||
| 
						 | 
					3b0197620f | ||
| 
						 | 
					f6240e114c | ||
| 
						 | 
					0f3c129b95 | ||
| 
						 | 
					6f87b01c47 | ||
| 
						 | 
					32110a9866 | ||
| 
						 | 
					ba459f4d20 | ||
| 
						 | 
					d868e6bfaf | ||
| 
						 | 
					9f3499a7c3 | ||
| 
						 | 
					ccec7732a7 | ||
| 
						 | 
					3b980a173f | ||
| 
						 | 
					246139f90b | ||
| 
						 | 
					4e85fb7d8d | ||
| 
						 | 
					ab32411b0c | ||
| 
						 | 
					477f28a6bd | ||
| 
						 | 
					96ef0a178d | ||
| 
						 | 
					ab603e7ef7 | ||
| 
						 | 
					d52104a62a | ||
| 
						 | 
					8d8d1cfdd6 | ||
| 
						 | 
					5451751513 | ||
| 
						 | 
					92ae26bb9f | ||
| 
						 | 
					3c8ee0c8cb | ||
| 
						 | 
					20f76b8118 | ||
| 
						 | 
					d8eeeead18 | ||
| 
						 | 
					5452162bc3 | ||
| 
						 | 
					a4adcba405 | ||
| 
						 | 
					66da594382 | ||
| 
						 | 
					aa426842f2 | ||
| 
						 | 
					ed53a45228 | ||
| 
						 | 
					2ddbcb2369 | ||
| 
						 | 
					61a58ff3c9 | ||
| 
						 | 
					638aee65c0 | ||
| 
						 | 
					f21c49f8da | ||
| 
						 | 
					583d2cb4e4 | ||
| 
						 | 
					aa70dcfc8f | ||
| 
						 | 
					ffb61c425b | ||
| 
						 | 
					858cf5e0c9 | ||
| 
						 | 
					881dd4666e | ||
| 
						 | 
					c4aeaf7fe8 | ||
| 
						 | 
					ea1f5a8fc6 | ||
| 
						 | 
					7f1e420a0a | ||
| 
						 | 
					1272eaf07f | ||
| 
						 | 
					2fe760cc4b | ||
| 
						 | 
					df3a50bae8 | ||
| 
						 | 
					0db3605f33 | ||
| 
						 | 
					173fa92116 | ||
| 
						 | 
					02ca58c1eb | ||
| 
						 | 
					4a22fe58bf | ||
| 
						 | 
					3fc478a14b | ||
| 
						 | 
					203bc674fe | ||
| 
						 | 
					a4a9f0a04c | ||
| 
						 | 
					918e7fffeb | ||
| 
						 | 
					e1648de661 | ||
| 
						 | 
					f7c94e6343 | ||
| 
						 | 
					2fba8e29e0 | ||
| 
						 | 
					de1bb4e203 | ||
| 
						 | 
					b4680a5c25 | ||
| 
						 | 
					d44f40d105 | ||
| 
						 | 
					8d5771e302 | ||
| 
						 | 
					fb31f99aed | ||
| 
						 | 
					b97f3a9ecf | ||
| 
						 | 
					ac22172a6e | ||
| 
						 | 
					57367380f5 | ||
| 
						 | 
					7101cc3170 | ||
| 
						 | 
					7051411be7 | ||
| 
						 | 
					bb8397b9b1 | ||
| 
						 | 
					bb8fa61cb4 | ||
| 
						 | 
					10147ee331 | ||
| 
						 | 
					3779ac2c8a | ||
| 
						 | 
					bdad4db9ec | ||
| 
						 | 
					36c69124f7 | ||
| 
						 | 
					7c8c811134 | ||
| 
						 | 
					ccfc9f3ab0 | ||
| 
						 | 
					d163b92e0b | ||
| 
						 | 
					887a21afb9 | ||
| 
						 | 
					634adc9f71 | ||
| 
						 | 
					974f01cef7 | ||
| 
						 | 
					ef9ade3548 | ||
| 
						 | 
					24fdb73b44 | ||
| 
						 | 
					ba06a87ba8 | ||
| 
						 | 
					e6be483755 | ||
| 
						 | 
					a36bfced47 | ||
| 
						 | 
					4faf421d5a | ||
| 
						 | 
					a739455414 | ||
| 
						 | 
					73f6e75d8d | ||
| 
						 | 
					17e7f8057a | ||
| 
						 | 
					b62c157680 | ||
| 
						 | 
					816f5162f9 | ||
| 
						 | 
					9d8f8277aa | ||
| 
						 | 
					cca14053a4 | ||
| 
						 | 
					427790f005 | ||
| 
						 | 
					17845bf71e | ||
| 
						 | 
					452f5ab3ba | ||
| 
						 | 
					335744e78a | ||
| 
						 | 
					c9e24439b2 | ||
| 
						 | 
					61c697b6db | ||
| 
						 | 
					3227d4f3ed | ||
| 
						 | 
					7e27afb645 | ||
| 
						 | 
					9ba4790b4d | ||
| 
						 | 
					3a26f7f4d5 | ||
| 
						 | 
					8b99617513 | ||
| 
						 | 
					587655f063 | ||
| 
						 | 
					7e9ecf4eb2 | ||
| 
						 | 
					5413010c60 | ||
| 
						 | 
					7d13cf1587 | ||
| 
						 | 
					24cd13935a | ||
| 
						 | 
					1ae7dbec67 | ||
| 
						 | 
					2b0a2ab3bc | ||
| 
						 | 
					4ed0f3e5f0 | ||
| 
						 | 
					9cacc703dd | ||
| 
						 | 
					9ae70c6519 | ||
| 
						 | 
					02b38fed49 | ||
| 
						 | 
					b2a65f809f | ||
| 
						 | 
					2931869522 | ||
| 
						 | 
					11d8640ba6 | ||
| 
						 | 
					65ff947b5e | ||
| 
						 | 
					2dd7db4808 | ||
| 
						 | 
					1d7354696e | ||
| 
						 | 
					cbceac4c8a | ||
| 
						 | 
					297877fbe2 | ||
| 
						 | 
					0d92451c49 | ||
| 
						 | 
					c8a58dcb69 | ||
| 
						 | 
					a4dea25d76 | ||
| 
						 | 
					bfb0235fc6 | ||
| 
						 | 
					6dcc3d637f | ||
| 
						 | 
					9bb4d17e31 | ||
| 
						 | 
					9df09db5fe | ||
| 
						 | 
					d8e28ec274 | ||
| 
						 | 
					d3905734c1 | ||
| 
						 | 
					8fe7f923ec | ||
| 
						 | 
					933f38390b | ||
| 
						 | 
					a4e019442f | ||
| 
						 | 
					d1c4e60636 | ||
| 
						 | 
					765d0228c0 | ||
| 
						 | 
					2d2020a20d | ||
| 
						 | 
					03ab282f67 | ||
| 
						 | 
					7f94cb1cad | ||
| 
						 | 
					a2a303bd72 | ||
| 
						 | 
					d34bf9a14d | ||
| 
						 | 
					68faba6bde | ||
| 
						 | 
					5c5b9cfd96 | ||
| 
						 | 
					9d683f4767 | ||
| 
						 | 
					f2912bad95 | ||
| 
						 | 
					c8adfe0131 | ||
| 
						 | 
					8b7e637e74 | ||
| 
						 | 
					43cffd7c4a | ||
| 
						 | 
					f3dad3765e | ||
| 
						 | 
					70c25141fc | ||
| 
						 | 
					b1b81a2672 | ||
| 
						 | 
					46197bf262 | ||
| 
						 | 
					58ec409e7f | ||
| 
						 | 
					c2d68599de | ||
| 
						 | 
					65f00f3af2 | ||
| 
						 | 
					6544326ffd | ||
| 
						 | 
					a23c206ccb | ||
| 
						 | 
					1b152e6bd0 | ||
| 
						 | 
					d9624d9882 | ||
| 
						 | 
					178b8471cc | ||
| 
						 | 
					719d2092a7 | ||
| 
						 | 
					2f3a4675da | ||
| 
						 | 
					9513be664b | ||
| 
						 | 
					64d8b7853a | ||
| 
						 | 
					4174e57c13 | ||
| 
						 | 
					88b395599a | ||
| 
						 | 
					b6d682c92c | ||
| 
						 | 
					3b02894341 | ||
| 
						 | 
					f3feb13dfe | ||
| 
						 | 
					114d8d0aba | ||
| 
						 | 
					503a843bb3 | ||
| 
						 | 
					c4d91aff40 | ||
| 
						 | 
					2ffe5faf6e | ||
| 
						 | 
					5d2a03aa82 | ||
| 
						 | 
					f4c83d1221 | ||
| 
						 | 
					100e91714b | ||
| 
						 | 
					dafb7b5259 | ||
| 
						 | 
					92906e217c | ||
| 
						 | 
					5c7b721879 | ||
| 
						 | 
					c27dcb0414 | ||
| 
						 | 
					7fcae1e974 | ||
| 
						 | 
					cc4659f5c6 | ||
| 
						 | 
					9fce12d6b4 | ||
| 
						 | 
					deb2f30b37 | ||
| 
						 | 
					751a3f0e94 | ||
| 
						 | 
					fee2a07e08 | ||
| 
						 | 
					17726abb0a | ||
| 
						 | 
					01f740ce69 | ||
| 
						 | 
					b168ccb605 | ||
| 
						 | 
					04a31a52ae | ||
| 
						 | 
					6a236fb91e | ||
| 
						 | 
					619fa1bff8 | ||
| 
						 | 
					53b37e8d0c | ||
| 
						 | 
					f8eb8adfbe | ||
| 
						 | 
					3c2aecb81f | ||
| 
						 | 
					efca9e11d6 | ||
| 
						 | 
					49189e0e43 | ||
| 
						 | 
					ea86c9bafb | ||
| 
						 | 
					c361b6a85c | ||
| 
						 | 
					c7ff45045c | ||
| 
						 | 
					090af35ea1 | ||
| 
						 | 
					8e668ca633 | ||
| 
						 | 
					4d9a16f33b | ||
| 
						 | 
					c9f2a0f694 | ||
| 
						 | 
					46bac3069e | ||
| 
						 | 
					d21aa0d85f | ||
| 
						 | 
					3e87d699eb | ||
| 
						 | 
					23aa216908 | ||
| 
						 | 
					e34e681157 | ||
| 
						 | 
					acd543fe4b | ||
| 
						 | 
					c2df5939d6 | ||
| 
						 | 
					b52e74b615 | ||
| 
						 | 
					ec2bb039ae | ||
| 
						 | 
					aeb9f5775f | ||
| 
						 | 
					47a20756b9 | ||
| 
						 | 
					a7aba1d585 | ||
| 
						 | 
					c89af0cea4 | ||
| 
						 | 
					9d3a109946 | ||
| 
						 | 
					079f1e6fff | ||
| 
						 | 
					7249b11899 | ||
| 
						 | 
					4cfb000812 | ||
| 
						 | 
					5262a41619 | ||
| 
						 | 
					887590730d | ||
| 
						 | 
					adb49bdf0b | ||
| 
						 | 
					7f3aadc157 | ||
| 
						 | 
					eb1c85f3f5 | ||
| 
						 | 
					dbdc884c96 | ||
| 
						 | 
					5166354e34 | ||
| 
						 | 
					04ea95049a | ||
| 
						 | 
					ec62e49001 | ||
| 
						 | 
					6fb21850d8 | ||
| 
						 | 
					98b50fd942 | ||
| 
						 | 
					729c962b7c | ||
| 
						 | 
					ebc15e60a8 | ||
| 
						 | 
					85f2d94253 | ||
| 
						 | 
					981a536cb5 | ||
| 
						 | 
					abf7c333b1 | ||
| 
						 | 
					b2f547e934 | ||
| 
						 | 
					0870a25e2f | ||
| 
						 | 
					1091e577a5 | ||
| 
						 | 
					151ad16fe6 | ||
| 
						 | 
					e325258091 | ||
| 
						 | 
					1139a02b40 | ||
| 
						 | 
					4b90953226 | ||
| 
						 | 
					d77fc01580 | ||
| 
						 | 
					216d84faeb | ||
| 
						 | 
					0c9d95f183 | ||
| 
						 | 
					913739d732 | ||
| 
						 | 
					7ecb5af587 | ||
| 
						 | 
					87f0e79749 | ||
| 
						 | 
					c85145d73c | ||
| 
						 | 
					eea689841b | ||
| 
						 | 
					e50a59361d | ||
| 
						 | 
					9862185bda | ||
| 
						 | 
					fe05d35bbb | ||
| 
						 | 
					ba602c17d4 | ||
| 
						 | 
					831bd07b0d | ||
| 
						 | 
					175c5d9fc3 | ||
| 
						 | 
					2639e30d9c | ||
| 
						 | 
					78a0218a91 | ||
| 
						 | 
					e12bf8daed | ||
| 
						 | 
					04e2072e2c | ||
| 
						 | 
					7bafe20a34 | ||
| 
						 | 
					554d5aeb7c | ||
| 
						 | 
					3991d2729d | ||
| 
						 | 
					5bc8a0860a | ||
| 
						 | 
					ad03fb0815 | ||
| 
						 | 
					e10d2a68f3 | ||
| 
						 | 
					213ee8d381 | ||
| 
						 | 
					52b1a1b835 | ||
| 
						 | 
					fce2930b85 | ||
| 
						 | 
					735f589b1c | ||
| 
						 | 
					69f17da5ca | ||
| 
						 | 
					5c0ee02251 | ||
| 
						 | 
					0cb4c7e437 | ||
| 
						 | 
					842c792868 | ||
| 
						 | 
					4ba8518462 | ||
| 
						 | 
					143dfb6246 | ||
| 
						 | 
					da4238ec68 | ||
| 
						 | 
					5f9e3edbe1 | ||
| 
						 | 
					1c68aee577 | ||
| 
						 | 
					e8d9a4bd49 | ||
| 
						 | 
					9520e87a38 | ||
| 
						 | 
					f9f5004909 | ||
| 
						 | 
					4f7014b2d5 | ||
| 
						 | 
					01dbfddb64 | ||
| 
						 | 
					6266a22d86 | ||
| 
						 | 
					09fe31179a | ||
| 
						 | 
					78343f4837 | ||
| 
						 | 
					a5619bc0a3 | ||
| 
						 | 
					dcb28aad2a | ||
| 
						 | 
					754cf172f5 | ||
| 
						 | 
					7890af1659 | ||
| 
						 | 
					d27d9fe694 | ||
| 
						 | 
					634a599db6 | ||
| 
						 | 
					5d0d859a1f | ||
| 
						 | 
					40c966fcd6 | ||
| 
						 | 
					d9245598a4 | ||
| 
						 | 
					03b0f4b16b | ||
| 
						 | 
					2daa0d057b | ||
| 
						 | 
					76eca409a3 | ||
| 
						 | 
					d84b018ba7 | ||
| 
						 | 
					027c3d1661 | ||
| 
						 | 
					4965b1ca7b | ||
| 
						 | 
					9cd7ea9371 | ||
| 
						 | 
					dc2468b27b | ||
| 
						 | 
					ea2451d882 | ||
| 
						 | 
					252617bd70 | ||
| 
						 | 
					8bd7003ea7 | ||
| 
						 | 
					280203158c | ||
| 
						 | 
					d456c3f62e | ||
| 
						 | 
					f64d17963b | ||
| 
						 | 
					f12378cf7b | ||
| 
						 | 
					15ff426be8 | ||
| 
						 | 
					c4a6837d56 | ||
| 
						 | 
					e6c28cf509 | ||
| 
						 | 
					a347a75617 | ||
| 
						 | 
					7fc2183826 | ||
| 
						 | 
					b6c3c9891c | ||
| 
						 | 
					d401b493a4 | ||
| 
						 | 
					da1a8308b6 | ||
| 
						 | 
					106d827a21 | ||
| 
						 | 
					1ebb162a00 | ||
| 
						 | 
					9d2791d9f8 | ||
| 
						 | 
					04a00f6564 | ||
| 
						 | 
					a5dd44c77f | ||
| 
						 | 
					8f86fd6bae | ||
| 
						 | 
					d5cd534320 | ||
| 
						 | 
					a5a6fd3bc2 | ||
| 
						 | 
					287ddda5df | ||
| 
						 | 
					7747f1c31d | ||
| 
						 | 
					8097cbbbe3 | ||
| 
						 | 
					e4c07875a3 | ||
| 
						 | 
					026fd4cf35 | ||
| 
						 | 
					87016f9620 | ||
| 
						 | 
					88393f0f65 | ||
| 
						 | 
					f5a9dbb348 | ||
| 
						 | 
					dbf3bb112c | ||
| 
						 | 
					f3186bd501 | ||
| 
						 | 
					3f1a252b91 | ||
| 
						 | 
					1240d6be76 | ||
| 
						 | 
					faf7b62f5c | ||
| 
						 | 
					445011b1e5 | ||
| 
						 | 
					e630fec63a | ||
| 
						 | 
					17421e8a63 | ||
| 
						 | 
					af06b78605 | ||
| 
						 | 
					3749b09366 | ||
| 
						 | 
					27cac10d0c | ||
| 
						 | 
					0590962d36 | ||
| 
						 | 
					c0c20d49a5 | ||
| 
						 | 
					cf7cf45003 | ||
| 
						 | 
					633dd0d9de | ||
| 
						 | 
					b929320d4c | ||
| 
						 | 
					7296bedd8e | ||
| 
						 | 
					e9fbbf4000 | ||
| 
						 | 
					dd44219aa5 | ||
| 
						 | 
					8cc1fe007d | ||
| 
						 | 
					c0b45c9fc4 | ||
| 
						 | 
					41315f45a9 | ||
| 
						 | 
					04d28a0eea | ||
| 
						 | 
					1e9cd3f785 | ||
| 
						 | 
					f462dd6a4c | ||
| 
						 | 
					34e75fc595 | ||
| 
						 | 
					dac513e046 | ||
| 
						 | 
					2c538d247b | ||
| 
						 | 
					f3b7f61e54 | ||
| 
						 | 
					d47ecf19f5 | ||
| 
						 | 
					da19b344b5 | ||
| 
						 | 
					eb66407926 | ||
| 
						 | 
					61c5b8e7d2 | ||
| 
						 | 
					4b09d57ec2 | ||
| 
						 | 
					a16f699dfc | ||
| 
						 | 
					b908a3d70a | ||
| 
						 | 
					d519c7263e | ||
| 
						 | 
					5dedc5d8ba | ||
| 
						 | 
					3ca1784ff4 | ||
| 
						 | 
					b54d512f3f | ||
| 
						 | 
					9d8fb19f55 | ||
| 
						 | 
					1ac4ab7edc | ||
| 
						 | 
					c9b73ac731 | ||
| 
						 | 
					e0b87f1e14 | ||
| 
						 | 
					394743efc8 | ||
| 
						 | 
					27a86a4756 | ||
| 
						 | 
					8ee0ef2cde | ||
| 
						 | 
					43f4682ec4 | ||
| 
						 | 
					5f081b8f8d | ||
| 
						 | 
					5023542882 | ||
| 
						 | 
					88d0731d80 | ||
| 
						 | 
					06cb8c52d7 | ||
| 
						 | 
					4d1358b7ed | ||
| 
						 | 
					0cfa7c1c56 | ||
| 
						 | 
					76928390a3 | ||
| 
						 | 
					af1f9cd76d | ||
| 
						 | 
					2f39f3d146 | ||
| 
						 | 
					f72f39bc26 | ||
| 
						 | 
					2659ba6bb4 | ||
| 
						 | 
					0b8c0c202e | ||
| 
						 | 
					37c8132632 | ||
| 
						 | 
					02f2f694e4 | ||
| 
						 | 
					d175a588f7 | ||
| 
						 | 
					4bb41f2f66 | ||
| 
						 | 
					088c46c7be | ||
| 
						 | 
					4228c40b3d | ||
| 
						 | 
					d581d29198 | ||
| 
						 | 
					5a7e854f9e | ||
| 
						 | 
					6e13823ccc | ||
| 
						 | 
					5c5f2fdf8f | ||
| 
						 | 
					96aa33f4ef | ||
| 
						 | 
					25fd23e703 | ||
| 
						 | 
					99cf4e5787 | ||
| 
						 | 
					66a4cb5875 | ||
| 
						 | 
					da14e2c349 | ||
| 
						 | 
					4cda61a16a | ||
| 
						 | 
					002afda503 | ||
| 
						 | 
					cb7a2e8c6a | ||
| 
						 | 
					d21ae1dad1 | ||
| 
						 | 
					4548859509 | ||
| 
						 | 
					09c6e6427a | ||
| 
						 | 
					8c34398a15 | ||
| 
						 | 
					a65b705080 | ||
| 
						 | 
					7e28c71074 | ||
| 
						 | 
					726f4a6715 | ||
| 
						 | 
					a444b43548 | ||
| 
						 | 
					473bb139d1 | ||
| 
						 | 
					d084770cea | ||
| 
						 | 
					619389ed20 | ||
| 
						 | 
					ad043e009e | ||
| 
						 | 
					89b9d079b1 | ||
| 
						 | 
					58a8845047 | ||
| 
						 | 
					3e6c8e68b4 | ||
| 
						 | 
					40cd92f701 | ||
| 
						 | 
					e216addf7c | ||
| 
						 | 
					9291594330 | ||
| 
						 | 
					f6010864ea | ||
| 
						 | 
					13e6a6def5 | ||
| 
						 | 
					575b373cd5 | ||
| 
						 | 
					0892065649 | ||
| 
						 | 
					766ef367fb | ||
| 
						 | 
					0c7d9958f5 | ||
| 
						 | 
					425b8f6073 | ||
| 
						 | 
					63593e45a6 | ||
| 
						 | 
					a6ee9806de | ||
| 
						 | 
					a6579f4ceb | ||
| 
						 | 
					639622a4fe | ||
| 
						 | 
					e28ec2f5ab | ||
| 
						 | 
					873753c735 | ||
| 
						 | 
					ff14951be4 | ||
| 
						 | 
					b47b445558 | ||
| 
						 | 
					6e9a2fea89 | ||
| 
						 | 
					64d2679b3c | ||
| 
						 | 
					3c3d4dfccb | ||
| 
						 | 
					0ccffed517 | ||
| 
						 | 
					20c18c1fc0 | ||
| 
						 | 
					a21c0097c2 | ||
| 
						 | 
					404d9ef2af | ||
| 
						 | 
					503fa1cbce | ||
| 
						 | 
					63f7991d0f | ||
| 
						 | 
					ae301c1f39 | ||
| 
						 | 
					8e911fb719 | ||
| 
						 | 
					a0d84e44c5 | ||
| 
						 | 
					81fb7ebb31 | ||
| 
						 | 
					3751211590 | ||
| 
						 | 
					6b4cba09be | ||
| 
						 | 
					6a7d184b7b | ||
| 
						 | 
					bde5cfc8bb | ||
| 
						 | 
					777c7a952b | ||
| 
						 | 
					c79bdd9029 | ||
| 
						 | 
					6a154efe65 | ||
| 
						 | 
					627f3ef36b | ||
| 
						 | 
					3d28836f2c | ||
| 
						 | 
					c28bd04958 | ||
| 
						 | 
					a6fb3acb42 | ||
| 
						 | 
					8a8b3bf96e | ||
| 
						 | 
					f7624e5f05 | ||
| 
						 | 
					55edfd2e4a | ||
| 
						 | 
					91878dd52c | ||
| 
						 | 
					c132358ec9 | ||
| 
						 | 
					e426f8ac47 | ||
| 
						 | 
					740dca8afc | ||
| 
						 | 
					6ae06d319c | ||
| 
						 | 
					30bc2b2f9c | ||
| 
						 | 
					7dc8e9d657 | ||
| 
						 | 
					1ab2fa5bf0 | ||
| 
						 | 
					779a1077bf | ||
| 
						 | 
					815bf7a53a | ||
| 
						 | 
					91a382dfb5 | ||
| 
						 | 
					2d8d4cd57d | ||
| 
						 | 
					0c5fe2b3bf | ||
| 
						 | 
					56c487347f | ||
| 
						 | 
					0a572fce1b | ||
| 
						 | 
					e898e29910 | ||
| 
						 | 
					843f076225 | ||
| 
						 | 
					fdb732c8c2 | ||
| 
						 | 
					33896a4e8f | ||
| 
						 | 
					51e016a0d6 | ||
| 
						 | 
					8737b06559 | ||
| 
						 | 
					15ab285174 | ||
| 
						 | 
					3a4782cc64 | ||
| 
						 | 
					0256a6d47b | ||
| 
						 | 
					8b0e846e0e | ||
| 
						 | 
					41acb5d3cc | ||
| 
						 | 
					a2f9b8ea9b | ||
| 
						 | 
					6237a1c505 | ||
| 
						 | 
					7c08db0b0f | ||
| 
						 | 
					df1270ac49 | ||
| 
						 | 
					46edc053d4 | ||
| 
						 | 
					908046c31a | ||
| 
						 | 
					8380c79875 | ||
| 
						 | 
					8a4879a96a | ||
| 
						 | 
					cdf1a77f08 | ||
| 
						 | 
					3f9857ccbd | ||
| 
						 | 
					1d65a31420 | ||
| 
						 | 
					dafdf0838a | ||
| 
						 | 
					f9cf135f68 | ||
| 
						 | 
					a1878e54c9 | ||
| 
						 | 
					95e5d899a9 | ||
| 
						 | 
					ee8321df67 | ||
| 
						 | 
					4918213e68 | ||
| 
						 | 
					ed7f349fc6 | ||
| 
						 | 
					2888f22a24 | ||
| 
						 | 
					fcb217f681 | ||
| 
						 | 
					9ffa9fe1a8 | ||
| 
						 | 
					905020c507 | ||
| 
						 | 
					02f5500641 | ||
| 
						 | 
					465af55d6e | ||
| 
						 | 
					80a3bb85aa | ||
| 
						 | 
					ea26bd3003 | ||
| 
						 | 
					508a511d2a | ||
| 
						 | 
					2d80cb71db | ||
| 
						 | 
					e31693bbee | ||
| 
						 | 
					fb561f10a7 | ||
| 
						 | 
					0c57d53e03 | ||
| 
						 | 
					3b1b9f589b | ||
| 
						 | 
					ac8d39acf4 | ||
| 
						 | 
					664e795217 | ||
| 
						 | 
					82bf323f63 | ||
| 
						 | 
					547ac85113 | ||
| 
						 | 
					46e0e4430d | ||
| 
						 | 
					609a31ea46 | ||
| 
						 | 
					3e99eb10d1 | ||
| 
						 | 
					b9d935af0c | ||
| 
						 | 
					31d3e82aa8 | ||
| 
						 | 
					dfdc17197b | ||
| 
						 | 
					62b965b4b7 | ||
| 
						 | 
					ba221abea5 | ||
| 
						 | 
					aa026c7134 | ||
| 
						 | 
					496cfff97a | ||
| 
						 | 
					ccaa7f5f3e | ||
| 
						 | 
					f492d21c70 | ||
| 
						 | 
					1983541f8c | ||
| 
						 | 
					e4cb3672b9 | ||
| 
						 | 
					a06a78a9c1 | ||
| 
						 | 
					2ba91ad837 | ||
| 
						 | 
					66eb3ea723 | ||
| 
						 | 
					d30e992b20 | ||
| 
						 | 
					c6a342563f | ||
| 
						 | 
					fd584eda05 | ||
| 
						 | 
					4301506590 | ||
| 
						 | 
					88f7c3a970 | ||
| 
						 | 
					f21a9f0cc2 | ||
| 
						 | 
					2233c2e618 | ||
| 
						 | 
					6264419bd4 | ||
| 
						 | 
					b62effb8fa | ||
| 
						 | 
					c8a07dd612 | ||
| 
						 | 
					02c99e4b25 | ||
| 
						 | 
					1242a16265 | ||
| 
						 | 
					a89fd17b8e | ||
| 
						 | 
					3d5e7bd6f1 | ||
| 
						 | 
					cefcb89487 | ||
| 
						 | 
					005272bde9 | ||
| 
						 | 
					491e60e4f2 | ||
| 
						 | 
					e5f72fd302 | ||
| 
						 | 
					5f21b100b8 | ||
| 
						 | 
					1a4c7629c5 | ||
| 
						 | 
					d21734ee47 | ||
| 
						 | 
					e140e2c367 | ||
| 
						 | 
					7ced1f5b54 | ||
| 
						 | 
					54b028ee3e | ||
| 
						 | 
					703336e1ea | ||
| 
						 | 
					9e936252ae | ||
| 
						 | 
					fc71a0f081 | ||
| 
						 | 
					86c72fa15d | ||
| 
						 | 
					6ba5af1e9e | ||
| 
						 | 
					33a4f59cfb | ||
| 
						 | 
					39134f0d9b | ||
| 
						 | 
					30e7440851 | ||
| 
						 | 
					be1a7bac7c | ||
| 
						 | 
					fb52a93a28 | ||
| 
						 | 
					efdf1ff755 | ||
| 
						 | 
					2c00dad211 | ||
| 
						 | 
					c23786c73e | ||
| 
						 | 
					7f1b07b76f | ||
| 
						 | 
					2f35ad6e65 | ||
| 
						 | 
					159c7d34c7 | ||
| 
						 | 
					fe8e990ed7 | ||
| 
						 | 
					1fb9b18cb6 | ||
| 
						 | 
					5c2586127b | ||
| 
						 | 
					661b266b45 | ||
| 
						 | 
					98af044196 | ||
| 
						 | 
					8006c336f5 | ||
| 
						 | 
					bdf07d2ce8 | ||
| 
						 | 
					efcf858e60 | ||
| 
						 | 
					93d9c16672 | ||
| 
						 | 
					7aaf261f5a | ||
| 
						 | 
					5eb4450012 | ||
| 
						 | 
					49c8cdd8f6 | ||
| 
						 | 
					c860b96a86 | ||
| 
						 | 
					69403bda80 | ||
| 
						 | 
					f5456b66ff | ||
| 
						 | 
					28b4c413cb | ||
| 
						 | 
					5b97250bb1 | ||
| 
						 | 
					5d26c29eaa | ||
| 
						 | 
					613944eccd | ||
| 
						 | 
					8d0e8fc021 | ||
| 
						 | 
					41ee70d414 | ||
| 
						 | 
					3691e8ddd7 | ||
| 
						 | 
					be54e94045 | ||
| 
						 | 
					366ca72342 | ||
| 
						 | 
					fc4e392ac1 | ||
| 
						 | 
					507be35d3a | ||
| 
						 | 
					5c0d62cd0e | ||
| 
						 | 
					7b7c4568b2 | ||
| 
						 | 
					f38091d96b | ||
| 
						 | 
					7c78e1fbf5 | ||
| 
						 | 
					72f0a48fac | ||
| 
						 | 
					193f872ebe | ||
| 
						 | 
					c3f96cf0e8 | ||
| 
						 | 
					df09109d81 | ||
| 
						 | 
					662cb9e2a3 | ||
| 
						 | 
					daa54a3798 | ||
| 
						 | 
					f035a1a0e0 | ||
| 
						 | 
					2688bf3333 | ||
| 
						 | 
					4095a58eb9 | ||
| 
						 | 
					c1b1ebe97e | ||
| 
						 | 
					a47b97d443 | ||
| 
						 | 
					df5d5583eb | ||
| 
						 | 
					79e764d5ec | ||
| 
						 | 
					2fcb04e5b2 | ||
| 
						 | 
					da1e264687 | ||
| 
						 | 
					03975287d2 | ||
| 
						 | 
					50a61b38f7 | ||
| 
						 | 
					1fe072471e | ||
| 
						 | 
					93e840295e | ||
| 
						 | 
					6ab7d640f0 | ||
| 
						 | 
					255cb8edb1 | ||
| 
						 | 
					367fb32493 | ||
| 
						 | 
					ef6d1fd6ce | ||
| 
						 | 
					3e8ab0645b | ||
| 
						 | 
					135727c9f7 | ||
| 
						 | 
					c58448817b | ||
| 
						 | 
					8ae0f1a9dc | ||
| 
						 | 
					ba9c1d98f6 | ||
| 
						 | 
					4db6e70f97 | ||
| 
						 | 
					9d1f789937 | ||
| 
						 | 
					11c2933e23 | ||
| 
						 | 
					fbd4951ea7 | ||
| 
						 | 
					744749f2f3 | ||
| 
						 | 
					db1c65970b | ||
| 
						 | 
					2d8ed4c77f | ||
| 
						 | 
					9ba970b83d | ||
| 
						 | 
					954d262d67 | ||
| 
						 | 
					1b6090fe13 | ||
| 
						 | 
					f8234b07f8 | ||
| 
						 | 
					25318f696d | ||
| 
						 | 
					1ab3d12bc7 | ||
| 
						 | 
					d66e0a0b45 | ||
| 
						 | 
					d46ceead04 | ||
| 
						 | 
					1cc9480e56 | ||
| 
						 | 
					c022b541f1 | ||
| 
						 | 
					96588466d4 | ||
| 
						 | 
					dbde12f8bf | ||
| 
						 | 
					660f0fec16 | ||
| 
						 | 
					fd9401cc62 | ||
| 
						 | 
					1edb9f7525 | ||
| 
						 | 
					15cfb9d1d9 | ||
| 
						 | 
					da6744da2d | ||
| 
						 | 
					bd5aa66a5f | ||
| 
						 | 
					7c30fe7738 | ||
| 
						 | 
					8ce599df38 | ||
| 
						 | 
					75fe13f1df | ||
| 
						 | 
					8ad6ded3ec | ||
| 
						 | 
					38d22c47f5 | ||
| 
						 | 
					956b6b89b6 | ||
| 
						 | 
					f27c2e6813 | ||
| 
						 | 
					d35c9f880a | ||
| 
						 | 
					d62aacf301 | ||
| 
						 | 
					716ea64212 | ||
| 
						 | 
					c9d6b13f6a | ||
| 
						 | 
					b437e68026 | ||
| 
						 | 
					1dfc38d078 | ||
| 
						 | 
					387184b052 | ||
| 
						 | 
					beec47d7ad | ||
| 
						 | 
					6b554337ff | ||
| 
						 | 
					08f95264d6 | ||
| 
						 | 
					2802920e93 | ||
| 
						 | 
					b04c47c15f | ||
| 
						 | 
					56d96383e2 | ||
| 
						 | 
					f2cbf846e7 | ||
| 
						 | 
					0088e94293 | ||
| 
						 | 
					a03a077e3d | ||
| 
						 | 
					85d2b9e32a | ||
| 
						 | 
					aa6471b3cc | ||
| 
						 | 
					b462a85c43 | ||
| 
						 | 
					9d8f30f955 | ||
| 
						 | 
					420db828e9 | ||
| 
						 | 
					fd8def705d | ||
| 
						 | 
					39c4fa1bf0 | ||
| 
						 | 
					32b964e9b7 | ||
| 
						 | 
					2980515c85 | ||
| 
						 | 
					36bee16781 | ||
| 
						 | 
					4f5d3e00db | ||
| 
						 | 
					6fb044f351 | ||
| 
						 | 
					b403845d03 | ||
| 
						 | 
					9d0e00acce | ||
| 
						 | 
					d5afe8f4f2 | ||
| 
						 | 
					3eb5ca3653 | ||
| 
						 | 
					db07aa42ea | ||
| 
						 | 
					081f51b9eb | ||
| 
						 | 
					38d9c16aba | ||
| 
						 | 
					392a426ddf | ||
| 
						 | 
					d77b2751a6 | ||
| 
						 | 
					3b28308291 | ||
| 
						 | 
					574ecb5ad4 | ||
| 
						 | 
					7a57a780d8 | ||
| 
						 | 
					cf9842433e | ||
| 
						 | 
					c6d089d701 | ||
| 
						 | 
					ec37e2d2b5 | ||
| 
						 | 
					e68b648a33 | ||
| 
						 | 
					56179d8a54 | ||
| 
						 | 
					47d232f694 | ||
| 
						 | 
					fc26fb2149 | ||
| 
						 | 
					92d828b04e | ||
| 
						 | 
					6f9dc913d4 | ||
| 
						 | 
					9ea0f7255f | ||
| 
						 | 
					938628a05f | ||
| 
						 | 
					a765dfc52e | ||
| 
						 | 
					d58f0646cf | ||
| 
						 | 
					792b963bda | ||
| 
						 | 
					9a8bf3b881 | ||
| 
						 | 
					1e02081cd2 | ||
| 
						 | 
					3f24a87034 | ||
| 
						 | 
					dd9f8021ff | ||
| 
						 | 
					74978e84f8 | ||
| 
						 | 
					6ef775390f | ||
| 
						 | 
					1313c1b157 | ||
| 
						 | 
					6d6c400b25 | ||
| 
						 | 
					46bd1b9b18 | ||
| 
						 | 
					dcea8bed6d | ||
| 
						 | 
					e8b35f4623 | ||
| 
						 | 
					ae263bb4db | ||
| 
						 | 
					754ca7c8f2 | ||
| 
						 | 
					804c02701a | ||
| 
						 | 
					fbb4077812 | ||
| 
						 | 
					961e1b89a2 | ||
| 
						 | 
					cc449228f3 | ||
| 
						 | 
					3984c47867 | ||
| 
						 | 
					cfb80266c2 | ||
| 
						 | 
					cc5198205d | ||
| 
						 | 
					a27b44a3c2 | ||
| 
						 | 
					937d064860 | ||
| 
						 | 
					9c814d1584 | ||
| 
						 | 
					415563dc6e | ||
| 
						 | 
					bed653737b | ||
| 
						 | 
					b53be942d4 | ||
| 
						 | 
					1d26161d23 | ||
| 
						 | 
					39afb58472 | ||
| 
						 | 
					22cd18571b | ||
| 
						 | 
					5753eb6682 | ||
| 
						 | 
					e26a6ea71b | ||
| 
						 | 
					6fbe765636 | ||
| 
						 | 
					22b2ccd83d | ||
| 
						 | 
					fc5aadd6dd | ||
| 
						 | 
					5a9f0c24b4 | ||
| 
						 | 
					0c12c072fa | ||
| 
						 | 
					f7284caefd | ||
| 
						 | 
					4e7f317679 | ||
| 
						 | 
					3534d6fddc | ||
| 
						 | 
					0b79e9cc9e | ||
| 
						 | 
					3ce97ccaa8 | ||
| 
						 | 
					407a340b2b | ||
| 
						 | 
					532346ecfb | ||
| 
						 | 
					cd25f5b6cb | ||
| 
						 | 
					5a0ac6c2ac | ||
| 
						 | 
					f0da08bbb1 | ||
| 
						 | 
					fb3f6e2238 | ||
| 
						 | 
					8b977252f3 | ||
| 
						 | 
					9582f9b6e5 | ||
| 
						 | 
					3f15a41006 | ||
| 
						 | 
					e1c4cfd7eb | ||
| 
						 | 
					a326f40bbf | ||
| 
						 | 
					09c2fff8fc | ||
| 
						 | 
					a4c1eb12b4 | ||
| 
						 | 
					4e80758970 | ||
| 
						 | 
					40ae408b3b | ||
| 
						 | 
					84d8d4f622 | ||
| 
						 | 
					5c04840312 | ||
| 
						 | 
					bd28d5c48a | ||
| 
						 | 
					f176d890c0 | ||
| 
						 | 
					3b158a96b7 | ||
| 
						 | 
					dcd0b2bf66 | ||
| 
						 | 
					edd1c89ea1 | ||
| 
						 | 
					32d858dce3 | ||
| 
						 | 
					1e4bb53a34 | ||
| 
						 | 
					f5f94097bf | ||
| 
						 | 
					77a3218db3 | ||
| 
						 | 
					c3ed40905a | ||
| 
						 | 
					268ac0bde8 | ||
| 
						 | 
					88e3f6af47 | ||
| 
						 | 
					21a85832b3 | ||
| 
						 | 
					254efdd122 | ||
| 
						 | 
					62ac6e74d9 | ||
| 
						 | 
					2c2268b39d | ||
| 
						 | 
					41aa14eaf0 | ||
| 
						 | 
					1f50f4658d | ||
| 
						 | 
					d31481fd8b | ||
| 
						 | 
					80ab28bc3a | ||
| 
						 | 
					48b7ebe1c0 | ||
| 
						 | 
					c59cf18337 | ||
| 
						 | 
					b7b1260540 | ||
| 
						 | 
					897c5634b0 | ||
| 
						 | 
					78e3a05e14 | ||
| 
						 | 
					1bb6367b79 | ||
| 
						 | 
					f5512ef21b | ||
| 
						 | 
					a0fa9937ba | ||
| 
						 | 
					ef2345ea85 | ||
| 
						 | 
					dd8fd09470 | ||
| 
						 | 
					a779e2aeca | ||
| 
						 | 
					aaaf25d578 | ||
| 
						 | 
					2e65c852c3 | ||
| 
						 | 
					1b206fe94c | ||
| 
						 | 
					8b93c97a09 | ||
| 
						 | 
					6247b55fc3 | ||
| 
						 | 
					12d9d49fa4 | ||
| 
						 | 
					aef3f097e4 | ||
| 
						 | 
					1a415d5fa7 | ||
| 
						 | 
					e4d46aee97 | ||
| 
						 | 
					d3a88e59b9 | ||
| 
						 | 
					44e3811520 | ||
| 
						 | 
					e0574d2861 | ||
| 
						 | 
					d4f66da793 | ||
| 
						 | 
					c7e3289396 | ||
| 
						 | 
					9cb7aeb32d | ||
| 
						 | 
					4537370a54 | ||
| 
						 | 
					9d2bc1142f | ||
| 
						 | 
					c44caa5c96 | ||
| 
						 | 
					77dc587686 | ||
| 
						 | 
					ce768241da | ||
| 
						 | 
					5f9e50175f | ||
| 
						 | 
					34ec457a47 | ||
| 
						 | 
					dd1651f2d1 | ||
| 
						 | 
					c3c529b001 | ||
| 
						 | 
					aa569304bc | ||
| 
						 | 
					3d57fd3227 | ||
| 
						 | 
					c18a6a6577 | ||
| 
						 | 
					9720301d01 | ||
| 
						 | 
					5ea75499fe | ||
| 
						 | 
					b45bbb77ef | ||
| 
						 | 
					d29b86baf0 | ||
| 
						 | 
					1730aff2b9 | ||
| 
						 | 
					1b03484b04 | ||
| 
						 | 
					f3749753ad | ||
| 
						 | 
					ae80e81b75 | ||
| 
						 | 
					8581e980e9 | ||
| 
						 | 
					7234aa601f | ||
| 
						 | 
					a8a2b7d7bb | ||
| 
						 | 
					9a901c7add | ||
| 
						 | 
					1a0b809ec5 | ||
| 
						 | 
					802c7254b4 | ||
| 
						 | 
					a99e3be44e | ||
| 
						 | 
					d7cf203547 | ||
| 
						 | 
					3364ce53e6 | ||
| 
						 | 
					2a2bcc8984 | ||
| 
						 | 
					6b8339e9b4 | ||
| 
						 | 
					32613ba544 | ||
| 
						 | 
					8e9d91fedc | ||
| 
						 | 
					41bb1c8535 | ||
| 
						 | 
					cb5e34f58a | ||
| 
						 | 
					f7c3cf5d78 | ||
| 
						 | 
					fdc0832506 | ||
| 
						 | 
					4413f816e6 | ||
| 
						 | 
					02224bb5fe | ||
| 
						 | 
					c37c4d8c6d | ||
| 
						 | 
					00ccbdaae9 | ||
| 
						 | 
					777189d7bd | ||
| 
						 | 
					c5dfc432d9 | ||
| 
						 | 
					144f7d6813 | ||
| 
						 | 
					6a19b7c1b0 | ||
| 
						 | 
					741b204fc7 | ||
| 
						 | 
					aa394754b6 | ||
| 
						 | 
					492f8f58bb | ||
| 
						 | 
					b9e1a5708d | ||
| 
						 | 
					576a9e4654 | ||
| 
						 | 
					a7bbbad185 | ||
| 
						 | 
					12ec05aed2 | ||
| 
						 | 
					2bde6612d1 | ||
| 
						 | 
					8460aa7d6a | ||
| 
						 | 
					64efbe703a | ||
| 
						 | 
					d272b0cbf3 | ||
| 
						 | 
					d52c95a15f | ||
| 
						 | 
					2b8414a453 | ||
| 
						 | 
					bc3d019ecf | ||
| 
						 | 
					bc9d44e5d7 | ||
| 
						 | 
					d1a8177778 | ||
| 
						 | 
					8d9aa6388d | ||
| 
						 | 
					6d317d300c | ||
| 
						 | 
					f647b3f0e0 | ||
| 
						 | 
					059b75cdbb | ||
| 
						 | 
					35fa42ca56 | ||
| 
						 | 
					4b450bab11 | ||
| 
						 | 
					4394a05243 | ||
| 
						 | 
					fd11ad95f6 | ||
| 
						 | 
					07b57de03e | ||
| 
						 | 
					a2a5f5df3f | ||
| 
						 | 
					148f2210ca | ||
| 
						 | 
					93f072d1fc | ||
| 
						 | 
					1104a385fa | ||
| 
						 | 
					f5b2febf13 | ||
| 
						 | 
					1b03e55cc3 | ||
| 
						 | 
					4a3f020cd9 | ||
| 
						 | 
					2acd23b14c | ||
| 
						 | 
					339a2f4a6f | ||
| 
						 | 
					0fef796757 | ||
| 
						 | 
					f0e5fb04fc | ||
| 
						 | 
					156be19c2d | ||
| 
						 | 
					956c486345 | ||
| 
						 | 
					062235f3a7 | ||
| 
						 | 
					945b357ed8 | ||
| 
						 | 
					c95ec8e99f | ||
| 
						 | 
					61eb631e59 | ||
| 
						 | 
					1ec349f7c9 | ||
| 
						 | 
					c4bc9616af | ||
| 
						 | 
					de050991d4 | ||
| 
						 | 
					6521951e82 | ||
| 
						 | 
					e818ddf152 | ||
| 
						 | 
					b6499e5248 | ||
| 
						 | 
					3cb809b444 | ||
| 
						 | 
					12b7e56261 | ||
| 
						 | 
					0007343175 | ||
| 
						 | 
					8e49c433e8 | ||
| 
						 | 
					16b2169965 | ||
| 
						 | 
					6a7fa52879 | ||
| 
						 | 
					4afc7438a6 | ||
| 
						 | 
					b80ce2a32f | ||
| 
						 | 
					386f88c9b2 | ||
| 
						 | 
					563f09d9de | ||
| 
						 | 
					423bdc6af1 | ||
| 
						 | 
					0d0413e027 | ||
| 
						 | 
					d4942858ba | ||
| 
						 | 
					81bb7009ea | ||
| 
						 | 
					310dc03e2a | ||
| 
						 | 
					362e0bf1ec | ||
| 
						 | 
					3bb330fcc4 | ||
| 
						 | 
					18f5d0241e | ||
| 
						 | 
					97f5b3f74d | ||
| 
						 | 
					f794acdb5b | ||
| 
						 | 
					f20909ba04 | ||
| 
						 | 
					f495ce13b1 | ||
| 
						 | 
					cac1d67b75 | ||
| 
						 | 
					548111e2ff | ||
| 
						 | 
					f5c0706c2e | ||
| 
						 | 
					e6ef11575b | ||
| 
						 | 
					7563e04743 | ||
| 
						 | 
					33e51cc38b | ||
| 
						 | 
					ef09596648 | ||
| 
						 | 
					93db5a091f | ||
| 
						 | 
					978ab2cdaa | ||
| 
						 | 
					cb09ae5cc0 | ||
| 
						 | 
					6bd275669d | ||
| 
						 | 
					51485396c7 | ||
| 
						 | 
					73e1f238cf | ||
| 
						 | 
					37487c243e | ||
| 
						 | 
					c05627a49e | ||
| 
						 | 
					4ba6791043 | ||
| 
						 | 
					54bec54765 | ||
| 
						 | 
					5c8c4e0aad | ||
| 
						 | 
					5cca26a565 | ||
| 
						 | 
					a347f02545 | ||
| 
						 | 
					a45cc0a048 | ||
| 
						 | 
					52ec5cf8e7 | ||
| 
						 | 
					3e4d0954b5 | ||
| 
						 | 
					e76bcce3bb | ||
| 
						 | 
					5a06b34b1d | ||
| 
						 | 
					79dcb0359f | ||
| 
						 | 
					5dc540b894 | ||
| 
						 | 
					0bbb2786f8 | ||
| 
						 | 
					c1fb1ba94c | ||
| 
						 | 
					974331b825 | ||
| 
						 | 
					f74567fbab | ||
| 
						 | 
					c77c01e437 | ||
| 
						 | 
					2b63680f55 | ||
| 
						 | 
					114d32a28f | ||
| 
						 | 
					cba5bca842 | ||
| 
						 | 
					e99351d4db | ||
| 
						 | 
					2524829023 | ||
| 
						 | 
					a7bcc4c00d | ||
| 
						 | 
					58ca6ec6aa | ||
| 
						 | 
					bb2599eb30 | ||
| 
						 | 
					2a95273b79 | ||
| 
						 | 
					d393bc45a4 | ||
| 
						 | 
					3a14eb9363 | ||
| 
						 | 
					3586e53fd9 | ||
| 
						 | 
					db75734774 | ||
| 
						 | 
					137cbbd141 | ||
| 
						 | 
					24f142df1d | ||
| 
						 | 
					b16ee1a3a6 | ||
| 
						 | 
					1b8580f12b | ||
| 
						 | 
					aefdf15a45 | ||
| 
						 | 
					99af697cd7 | ||
| 
						 | 
					30ff15ec0b | ||
| 
						 | 
					13ce39058d | ||
| 
						 | 
					4ed7f5eb67 | ||
| 
						 | 
					2df0c02dfd | ||
| 
						 | 
					158ca9746c | ||
| 
						 | 
					c58a2e8e46 | ||
| 
						 | 
					41117578c5 | ||
| 
						 | 
					a8ea6c2c66 | ||
| 
						 | 
					7652f4272c | ||
| 
						 | 
					04f1f5f94b | ||
| 
						 | 
					f78e17ce02 | ||
| 
						 | 
					c1518dc728 | ||
| 
						 | 
					899f7da032 | ||
| 
						 | 
					eec4334a78 | ||
| 
						 | 
					2ad9eafeaf | ||
| 
						 | 
					9dc9103d6c | ||
| 
						 | 
					ed0e880913 | ||
| 
						 | 
					a70e74e478 | ||
| 
						 | 
					4d34abeeef | ||
| 
						 | 
					621810c409 | ||
| 
						 | 
					62d2c7efc3 | ||
| 
						 | 
					c410dce07a | ||
| 
						 | 
					d2709c681f | ||
| 
						 | 
					0e6625f719 | ||
| 
						 | 
					cc6a408d5d | ||
| 
						 | 
					d10e3d8498 | ||
| 
						 | 
					c89bab0ff1 | ||
| 
						 | 
					6bc8963e8e | ||
| 
						 | 
					34db64234f | ||
| 
						 | 
					b43f0a4536 | ||
| 
						 | 
					45ba07c214 | ||
| 
						 | 
					4d72bfd495 | ||
| 
						 | 
					925eaa1db0 | ||
| 
						 | 
					69957dac3d | ||
| 
						 | 
					b1c2f4a8d6 | ||
| 
						 | 
					aba6c222cb | ||
| 
						 | 
					b8da674816 | ||
| 
						 | 
					f06dba5007 | ||
| 
						 | 
					8dfcee9039 | ||
| 
						 | 
					be355bf7b4 | ||
| 
						 | 
					a1eedd496c | ||
| 
						 | 
					9f7bad82e5 | ||
| 
						 | 
					4e17d11da6 | ||
| 
						 | 
					f38d5a9c06 | ||
| 
						 | 
					696efcdf20 | ||
| 
						 | 
					728c3a72ad | ||
| 
						 | 
					c8c839a569 | ||
| 
						 | 
					6827dacb4b | ||
| 
						 | 
					246271fd9d | ||
| 
						 | 
					4305ebc5b0 | ||
| 
						 | 
					39bff8bfbc | ||
| 
						 | 
					fc6a0c1006 | ||
| 
						 | 
					d6dc9af5ff | ||
| 
						 | 
					e62045eb7f | ||
| 
						 | 
					bda3e53511 | ||
| 
						 | 
					6295d0fc6c | ||
| 
						 | 
					3271e65d56 | ||
| 
						 | 
					f8225141dc | ||
| 
						 | 
					8e9e583b79 | ||
| 
						 | 
					bbadfce65a | ||
| 
						 | 
					9e191d681f | ||
| 
						 | 
					a3236997be | ||
| 
						 | 
					be961cd60e | ||
| 
						 | 
					4efd363134 | ||
| 
						 | 
					7bdee9ce05 | ||
| 
						 | 
					7dc9a9bf2f | ||
| 
						 | 
					87245c7b33 | ||
| 
						 | 
					db497a2ecf | ||
| 
						 | 
					9d250bec05 | ||
| 
						 | 
					1371f69810 | ||
| 
						 | 
					82ee6aed7f | ||
| 
						 | 
					d730fd0a5f | ||
| 
						 | 
					cb4e4bb2db | ||
| 
						 | 
					6fd3c0fbb4 | ||
| 
						 | 
					13170fbb3c | ||
| 
						 | 
					7e7295f259 | ||
| 
						 | 
					d30cb2d4d9 | ||
| 
						 | 
					f4100ac507 | ||
| 
						 | 
					1a0415a100 | ||
| 
						 | 
					1cc5bb5ec4 | ||
| 
						 | 
					43661fff76 | ||
| 
						 | 
					509bdceac2 | ||
| 
						 | 
					835cb8caa5 | ||
| 
						 | 
					ed32adc9bb | ||
| 
						 | 
					44f8fecf84 | ||
| 
						 | 
					0cee0e08e9 | ||
| 
						 | 
					73cd595b73 | ||
| 
						 | 
					c50b23e9ca | ||
| 
						 | 
					d1a763bc21 | ||
| 
						 | 
					de8ca5c1b5 | ||
| 
						 | 
					14372ba7f3 | ||
| 
						 | 
					ca861d8ff9 | ||
| 
						 | 
					37a316e66c | ||
| 
						 | 
					22c8be6645 | ||
| 
						 | 
					8cce1b4f6c | ||
| 
						 | 
					040bb9354a | ||
| 
						 | 
					fb0f9cd1a1 | ||
| 
						 | 
					3816db03f5 | ||
| 
						 | 
					d802416dae | ||
| 
						 | 
					2cd41773ba | ||
| 
						 | 
					8ce5b087bc | ||
| 
						 | 
					03fd74da4e | ||
| 
						 | 
					ed178b702f | ||
| 
						 | 
					b4567acc6b | ||
| 
						 | 
					953f44bcc5 | ||
| 
						 | 
					be4f259b71 | ||
| 
						 | 
					e9531487d9 | ||
| 
						 | 
					d715110961 | ||
| 
						 | 
					3c31908e08 | ||
| 
						 | 
					09d34a2129 | ||
| 
						 | 
					e5e3f3c299 | ||
| 
						 | 
					bcd1c793ea | ||
| 
						 | 
					185152a74a | ||
| 
						 | 
					ded99b9a09 | ||
| 
						 | 
					55a04bbf2b | ||
| 
						 | 
					7d5d7453c2 | ||
| 
						 | 
					952f58153f | ||
| 
						 | 
					d0d981435a | ||
| 
						 | 
					fd83990d8a | ||
| 
						 | 
					ad0c4caf1c | ||
| 
						 | 
					126f0ed95d | ||
| 
						 | 
					a2b499c460 | ||
| 
						 | 
					5a5b3bf291 | ||
| 
						 | 
					a4a6e7cf53 | ||
| 
						 | 
					658db43ad3 | ||
| 
						 | 
					cfecd063c9 | ||
| 
						 | 
					a8fe063726 | ||
| 
						 | 
					dcf637edd8 | ||
| 
						 | 
					c0345f1088 | ||
| 
						 | 
					0cbdfca141 | ||
| 
						 | 
					cb1f696e22 | ||
| 
						 | 
					cf7355e4d0 | ||
| 
						 | 
					dab8c5ea56 | ||
| 
						 | 
					4b889eac32 | ||
| 
						 | 
					86835db8f2 | ||
| 
						 | 
					bc317bf3f2 | ||
| 
						 | 
					5c036eadf9 | ||
| 
						 | 
					263474705b | ||
| 
						 | 
					1e781ec78f | ||
| 
						 | 
					ef1eabf033 | ||
| 
						 | 
					2fa40555e6 | ||
| 
						 | 
					2aae272d86 | ||
| 
						 | 
					7db0900cc8 | ||
| 
						 | 
					c1e2d66abd | ||
| 
						 | 
					78b1ba56ce | ||
| 
						 | 
					5385205b8e | ||
| 
						 | 
					5c8bbb511e | ||
| 
						 | 
					fde01f0b71 | ||
| 
						 | 
					1c04ae3216 | ||
| 
						 | 
					35b4907e52 | ||
| 
						 | 
					2431b8e021 | ||
| 
						 | 
					bd5c04b923 | ||
| 
						 | 
					6e00b6e214 | ||
| 
						 | 
					c9b079cbb5 | ||
| 
						 | 
					9163372786 | ||
| 
						 | 
					dabcd29fb6 | ||
| 
						 | 
					d36e435801 | ||
| 
						 | 
					a18fb27d0f | ||
| 
						 | 
					2dbe511519 | ||
| 
						 | 
					e031a5d28b | ||
| 
						 | 
					f9b32474b0 | ||
| 
						 | 
					53d268a7ef | ||
| 
						 | 
					70da558802 | ||
| 
						 | 
					11215374ff | ||
| 
						 | 
					cb45a38838 | ||
| 
						 | 
					bb4d430ebf | ||
| 
						 | 
					c17f84ca23 | ||
| 
						 | 
					0ae1f9ffc7 | ||
| 
						 | 
					d15bcd9845 | ||
| 
						 | 
					f79a11d993 | ||
| 
						 | 
					9391d9d11b | ||
| 
						 | 
					aee90a3116 | ||
| 
						 | 
					ffac5279a7 | ||
| 
						 | 
					318283fc70 | ||
| 
						 | 
					3582ba0c77 | ||
| 
						 | 
					985d0c786c | ||
| 
						 | 
					9c8c282e08 | ||
| 
						 | 
					93dc7a51c0 | ||
| 
						 | 
					393577ee78 | ||
| 
						 | 
					eef593a34e | ||
| 
						 | 
					3790e924e9 | ||
| 
						 | 
					2f165aade8 | ||
| 
						 | 
					2c502aec45 | ||
| 
						 | 
					6bbf246752 | ||
| 
						 | 
					0b3e8e29cf | ||
| 
						 | 
					d509ab7779 | ||
| 
						 | 
					502a9aefdc | ||
| 
						 | 
					ccba18aa8f | ||
| 
						 | 
					586ebcd5be | 
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -19,6 +19,8 @@ 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/gnome-shell-extension-prefs.desktop
 | 
			
		||||
data/gnome-shell-extension-prefs.desktop.in
 | 
			
		||||
data/gschemas.compiled
 | 
			
		||||
@@ -41,6 +43,8 @@ docs/reference/*/xml/
 | 
			
		||||
docs/reference/shell/doc-gen-*
 | 
			
		||||
gtk-doc.make
 | 
			
		||||
js/misc/config.js
 | 
			
		||||
js/js-resources.c
 | 
			
		||||
js/js-resources.h
 | 
			
		||||
intltool-extract.in
 | 
			
		||||
intltool-merge.in
 | 
			
		||||
intltool-update.in
 | 
			
		||||
@@ -71,13 +75,11 @@ src/calendar-server/evolution-calendar.desktop.in
 | 
			
		||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
 | 
			
		||||
src/gnome-shell
 | 
			
		||||
src/gnome-shell-calendar-server
 | 
			
		||||
src/gnome-shell-extension-tool
 | 
			
		||||
src/gnome-shell-extension-prefs
 | 
			
		||||
src/gnome-shell-extension-tool
 | 
			
		||||
src/gnome-shell-hotplug-sniffer
 | 
			
		||||
src/gnome-shell-jhbuild
 | 
			
		||||
src/gnome-shell-perf-helper
 | 
			
		||||
src/gnome-shell-perf-tool
 | 
			
		||||
src/gnome-shell-real
 | 
			
		||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
 | 
			
		||||
src/run-js-test
 | 
			
		||||
src/test-recorder
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						@@ -1,12 +1,12 @@
 | 
			
		||||
		    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
		       Version 2, June 1991
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 2, June 1991
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 | 
			
		||||
     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 | 
			
		||||
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
			    Preamble
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The licenses for most software are designed to take away your
 | 
			
		||||
freedom to share and change it.  By contrast, the GNU General Public
 | 
			
		||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users.  This
 | 
			
		||||
General Public License applies to most of the Free Software
 | 
			
		||||
Foundation's software and to any other program whose authors commit to
 | 
			
		||||
using it.  (Some other Free Software Foundation software is covered by
 | 
			
		||||
the GNU Library General Public License instead.)  You can apply it to
 | 
			
		||||
the GNU Lesser General Public License instead.)  You can apply it to
 | 
			
		||||
your programs, too.
 | 
			
		||||
 | 
			
		||||
  When we speak of free software, we are referring to freedom, not
 | 
			
		||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
 | 
			
		||||
		    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 | 
			
		||||
 | 
			
		||||
  0. This License applies to any program or other work which contains
 | 
			
		||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
 | 
			
		||||
    License.  (Exception: if the Program itself is interactive but
 | 
			
		||||
    does not normally print such an announcement, your work based on
 | 
			
		||||
    the Program is not required to print an announcement.)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
These requirements apply to the modified work as a whole.  If
 | 
			
		||||
identifiable sections of that work are not derived from the Program,
 | 
			
		||||
and can be reasonably considered independent and separate works in
 | 
			
		||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
 | 
			
		||||
access to copy the source code from the same place counts as
 | 
			
		||||
distribution of the source code, even though third parties are not
 | 
			
		||||
compelled to copy the source along with the object code.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  4. You may not copy, modify, sublicense, or distribute the Program
 | 
			
		||||
except as expressly provided under this License.  Any attempt
 | 
			
		||||
otherwise to copy, modify, sublicense or distribute the Program is
 | 
			
		||||
@@ -225,7 +225,7 @@ impose that choice.
 | 
			
		||||
 | 
			
		||||
This section is intended to make thoroughly clear what is believed to
 | 
			
		||||
be a consequence of the rest of this License.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  8. If the distribution and/or use of the Program is restricted in
 | 
			
		||||
certain countries either by patents or by copyrighted interfaces, the
 | 
			
		||||
original copyright holder who places the Program under this License
 | 
			
		||||
@@ -255,7 +255,7 @@ make exceptions for this.  Our decision will be guided by the two goals
 | 
			
		||||
of preserving the free status of all derivatives of our free software and
 | 
			
		||||
of promoting the sharing and reuse of software generally.
 | 
			
		||||
 | 
			
		||||
			    NO WARRANTY
 | 
			
		||||
                            NO WARRANTY
 | 
			
		||||
 | 
			
		||||
  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 | 
			
		||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 | 
			
		||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 | 
			
		||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 | 
			
		||||
POSSIBILITY OF SUCH DAMAGES.
 | 
			
		||||
 | 
			
		||||
		     END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
	    How to Apply These Terms to Your New Programs
 | 
			
		||||
                     END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
            How to Apply These Terms to Your New Programs
 | 
			
		||||
 | 
			
		||||
  If you develop a new program, and you want it to be of the greatest
 | 
			
		||||
possible use to the public, the best way to achieve this is to make it
 | 
			
		||||
@@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program; if not, write to the Free Software
 | 
			
		||||
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along
 | 
			
		||||
    with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
If the program is interactive, make it output a short notice like this
 | 
			
		||||
when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    Gnomovision version 69, Copyright (C) year  name of author
 | 
			
		||||
    Gnomovision version 69, Copyright (C) year name of author
 | 
			
		||||
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    under certain conditions; type `show c' for details.
 | 
			
		||||
@@ -336,5 +335,5 @@ necessary.  Here is a sample; alter the names:
 | 
			
		||||
This General Public License does not permit incorporating your program into
 | 
			
		||||
proprietary programs.  If your program is a subroutine library, you may
 | 
			
		||||
consider it more useful to permit linking proprietary applications with the
 | 
			
		||||
library.  If this is what you want to do, use the GNU Library General
 | 
			
		||||
library.  If this is what you want to do, use the GNU Lesser General
 | 
			
		||||
Public License instead of this License.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						@@ -138,8 +138,8 @@ GObjects, although this feature isn't used very often in the Shell itself.
 | 
			
		||||
 | 
			
		||||
        _init: function(icon, label) {
 | 
			
		||||
            this.parent({ reactive: false });
 | 
			
		||||
            this.addActor(icon);
 | 
			
		||||
            this.addActor(label);
 | 
			
		||||
            this.actor.add_child(icon);
 | 
			
		||||
            this.actor.add_child(label);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        open: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,11 @@
 | 
			
		||||
# Point to our macro directory and pick up user flags from the environment
 | 
			
		||||
ACLOCAL_AMFLAGS  = -I m4 ${ACLOCAL_FLAGS}
 | 
			
		||||
 | 
			
		||||
SUBDIRS = data js src browser-plugin tests po docs
 | 
			
		||||
SUBDIRS = data js src  tests po docs
 | 
			
		||||
 | 
			
		||||
if BUILD_BROWSER_PLUGIN
 | 
			
		||||
SUBDIRS += browser-plugin
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_MAN
 | 
			
		||||
SUBDIRS += man
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										570
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						@@ -1,3 +1,573 @@
 | 
			
		||||
3.13.2
 | 
			
		||||
======
 | 
			
		||||
* Make airplane mode menu insensitive in lock screen [Giovanni; #729224]
 | 
			
		||||
* Don't always extend struts to the screen edge [Florian; #683819]
 | 
			
		||||
* Fix keynav for alternatives in AltSwitcher [Florian; #727259]
 | 
			
		||||
* Implement window menus in the shell [Jasper; #726352]
 | 
			
		||||
* Support resource:/// URLs in GNOME_SHELL_JS envvar [Owen; #730409]
 | 
			
		||||
* Fix switcher popups with keybindings containing Escape [Rui; #730739]
 | 
			
		||||
* Update extension-prefs UI to follow GNOME 3 patterns [Florian; #730829]
 | 
			
		||||
* Add support for fallback app menu in window decorations [Florian; #730752]
 | 
			
		||||
* Fix keynav escaping open app folders [Florian; #726760]
 | 
			
		||||
* Misc. bug fixes [Kalev, Florian, Owen; #729429, #728449, #730408, #730753,
 | 
			
		||||
  #730653]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Piotr Drąg, Kalev Lember, Rui Matos, Florian Müllner,
 | 
			
		||||
  Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Ihar Hrachyshka [be], Giovanni Campagna [it], Carles Ferrando [ca@valencia],
 | 
			
		||||
  Daniel Mustieles [es], Aurimas Černius [lt], Enrico Nicoletto [pt_BR],
 | 
			
		||||
  Yosef Or Boczko [he], Marek Černocký [cs], Muhammet Kara [tr],
 | 
			
		||||
  Georges Neto [pt_BR], Andika Triwidada [id]
 | 
			
		||||
 | 
			
		||||
3.13.1
 | 
			
		||||
======
 | 
			
		||||
* Ensure the currently focused app icon is viewable [Rui; #726759]
 | 
			
		||||
* Improve language in location menu [Zeeshan; #726498]
 | 
			
		||||
* Improve HiDpi support [Cosimo; #726907]
 | 
			
		||||
* Set accessible role for window previews [Alejandro; #726670]
 | 
			
		||||
* Fix bad antialiasing on panel menu buttons [Carlos; #727336]
 | 
			
		||||
* Don't hide location menu [Zeeshan; #727398]
 | 
			
		||||
* Fix IM candidate window obscuring current text [Rui; #727579]
 | 
			
		||||
* Don't always extend struts to the screen edge [Florian; #663690]
 | 
			
		||||
* Add shortcuts for switching to the last workspace [Elad; #659288]
 | 
			
		||||
* Show OSD window on all monitors [Adel; #722684]
 | 
			
		||||
* Improve consistency of labels in network menu [Paul; #727163]
 | 
			
		||||
* Fix zombie search providers showing up [Jasper; #728597]
 | 
			
		||||
* Remove ConsoleKit support [Florian; #686626]
 | 
			
		||||
* Fix region screenshots with open shell menus [Florian; #709126]
 | 
			
		||||
* Support <shift>insert in text entries [Florian; #648318]
 | 
			
		||||
* Improve app picker scrolling on touch [Jasper; #729064]
 | 
			
		||||
* Don't make date button clickable when on current date [Carlos; #726724]
 | 
			
		||||
* Tweak heuristic for hiding workspace switcher [Florian; #662457]
 | 
			
		||||
* Add option to show in Software to app context menu [Matthias; #643043]
 | 
			
		||||
* Misc. bug fixes and cleanups [Bastien, Florian, Giovanni, Adel, Vadim,
 | 
			
		||||
  Carlos; #727983, #727948, #728512, #728681, #728897, #727384, #728820,
 | 
			
		||||
  #715042, #728449, #728343]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Elad Alfassa, Zeeshan Ali (Khattak), Giovanni Campagna, Cosimo Cecchi,
 | 
			
		||||
  Matthias Clasen, Piotr Drąg, Adel Gadllah, Paul Lange, Rui Matos,
 | 
			
		||||
  Simon McVittie, Florian Müllner, Bastien Nocera, Alejandro Piñeiro,
 | 
			
		||||
  Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he],
 | 
			
		||||
  Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el],
 | 
			
		||||
  Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko],
 | 
			
		||||
  Benjamin Steinwender [de], Sphinx Jiang [zh_CN],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt],
 | 
			
		||||
  Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl],
 | 
			
		||||
  Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it],
 | 
			
		||||
  Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja],
 | 
			
		||||
  Wouter Bolsterlee [nl], Daniel Korostil [uk], Daniel Șerbănescu [ro],
 | 
			
		||||
  Enrico Nicoletto [pt_BR]
 | 
			
		||||
 | 
			
		||||
3.12.0
 | 
			
		||||
======
 | 
			
		||||
* gdm: Reset greeter when coming back to login screen [Jasper; #726989]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu],
 | 
			
		||||
  Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da],
 | 
			
		||||
  Jiro Matsuzawa [ja], Dušan Kazik [sk]
 | 
			
		||||
 | 
			
		||||
3.11.92
 | 
			
		||||
=======
 | 
			
		||||
* calendar: Grab key focus after changing day [Volker; #725606]
 | 
			
		||||
* gdm: Don't load user list if disabled [Florian; #725905]
 | 
			
		||||
* Don't show network-offline in the top bar [Jasper; #725340]
 | 
			
		||||
* Improve radial shade effect of modal dialogs [Giovanni; #725830]
 | 
			
		||||
* Fix broken suspend-on-idle functionality [Giovanni; #712706]
 | 
			
		||||
* Close wifi selection dialog when device disappears [Giovanni; #723935]
 | 
			
		||||
* Don't close chats when pressing Escape [Giovanni; #724178]
 | 
			
		||||
* Improve smartcard support in login/lock screen [Ray; #726262, #726263]
 | 
			
		||||
* Wake up screen when resuming from suspend [Giovanni; #726378]
 | 
			
		||||
* Make bluetooth and location items insensitive when locked [Florian; #726319]
 | 
			
		||||
* Don't show bluetooth icon when there is no adapter [Giovanni; #725057]
 | 
			
		||||
* Make sure to keep the OSK on top of modal dialogs [Rui; #719451]
 | 
			
		||||
* Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian;
 | 
			
		||||
  #725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119,
 | 
			
		||||
  #726238, #585500, #704844, #726323, #726322, #726120, #726414]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner,
 | 
			
		||||
  Volker Sobek, Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
 | 
			
		||||
  Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id],
 | 
			
		||||
  A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN],
 | 
			
		||||
  Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
 | 
			
		||||
  Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr],
 | 
			
		||||
  Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi],
 | 
			
		||||
  maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb],
 | 
			
		||||
  Gábor Kelemen [hu], Ask H. Larsen [da]
 | 
			
		||||
 | 
			
		||||
3.11.91
 | 
			
		||||
=======
 | 
			
		||||
* Don't use network profile name in menu [Giovanni; #725586]
 | 
			
		||||
* calendar: Make date label clickable to return to current date [Vit; #641366]
 | 
			
		||||
* Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813,
 | 
			
		||||
  #724686, #725082, #724870, #724779, #725533]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah,
 | 
			
		||||
  Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el],
 | 
			
		||||
  Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be],
 | 
			
		||||
  eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl],
 | 
			
		||||
  Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl],
 | 
			
		||||
  Kjartan Maraas [nb], Changwoo Ryu [ko]
 | 
			
		||||
 | 
			
		||||
3.11.90
 | 
			
		||||
=======
 | 
			
		||||
* Stop showing two bluetooth entries [Giovanni; #709353]
 | 
			
		||||
* Improve styling of login/lock screen [Reda; #723833]
 | 
			
		||||
* Fix magnifier crosshairs [Magdalen; #723709]
 | 
			
		||||
* Make NetworkManager support optional [Michael; #669495]
 | 
			
		||||
* Make middle-click open a new instance [Florian; #695010]
 | 
			
		||||
* Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607]
 | 
			
		||||
* Remove notification counter on screen shield [Carlos; #709275]
 | 
			
		||||
* Improve app picker transition [Carlos; #722331]
 | 
			
		||||
* Add geolocation indicator to status menu [Zeeshan; #723684]
 | 
			
		||||
* Improve timestamps in chat notifications [Carlos; #708031, #715158]
 | 
			
		||||
* Improve network menus [Giovanni; #723570]
 | 
			
		||||
* Add "VPN Setting" item to VPN submenu [Giovanni; #709167]
 | 
			
		||||
* Improve appearance of disclosure arrows [Carlos; #720206]
 | 
			
		||||
* Add GSetting key to disable version validation of extensions [Adel; #724683]
 | 
			
		||||
* Delay auto-removing empty workspaces [Florian; #709064]
 | 
			
		||||
* Offer offline updates in the shutdown dialog [Kalev; #722898]
 | 
			
		||||
* Animate tile previews [Florian; #665758]
 | 
			
		||||
* Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128,
 | 
			
		||||
  #722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282,
 | 
			
		||||
  #724690, #722928]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna,
 | 
			
		||||
  Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie,
 | 
			
		||||
  Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Colin Walters
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar],
 | 
			
		||||
  Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl],
 | 
			
		||||
  Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an],
 | 
			
		||||
  Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as],
 | 
			
		||||
  Rafael Ferreira [pt_BR]
 | 
			
		||||
 | 
			
		||||
3.11.5
 | 
			
		||||
======
 | 
			
		||||
* Fix extension preference tool [Florian; #722334]
 | 
			
		||||
* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
 | 
			
		||||
* Add radial background shade for modal dialogs [Giovanni; #669798]
 | 
			
		||||
* Show attached modal windows in the overview [Giovanni; #650843]
 | 
			
		||||
* Add support for desktop actions [Giovanni; #669603]
 | 
			
		||||
* Indicate in system status when location service is used [Zeeshan; #709372]
 | 
			
		||||
* Add support for extended app folder schema [Jasper; #723179]
 | 
			
		||||
* Show status icon for wired network connections [Jasper; #708966]
 | 
			
		||||
* Indicate airplane mode in network selection dialog [Giovanni; #709128]
 | 
			
		||||
* Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper;
 | 
			
		||||
  #722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840,
 | 
			
		||||
  #722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685,
 | 
			
		||||
  #723570]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann,
 | 
			
		||||
  Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano,
 | 
			
		||||
  Jasper St. Pierre, Rico Tzschichholz, Matt Watson
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN],
 | 
			
		||||
  Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es],
 | 
			
		||||
  Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as],
 | 
			
		||||
  Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he],
 | 
			
		||||
  Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he],
 | 
			
		||||
  Andika Triwidada [id]
 | 
			
		||||
 | 
			
		||||
3.11.4
 | 
			
		||||
======
 | 
			
		||||
* Fix removal of workspacaes that are not at the end [Giovanni; #721417]
 | 
			
		||||
* Allow session mode to be specified in the environment [Ray; #720894]
 | 
			
		||||
* Special-case launching of terminals [Debarshi; #695010]
 | 
			
		||||
* Always show arrow if app switcher is scrollable [Jonh; #711467]
 | 
			
		||||
* Implement new app folders system [Jasper; #722117]
 | 
			
		||||
* Remove arrow from background menu [Tarun; #699608]
 | 
			
		||||
* Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039,
 | 
			
		||||
  #721439, #721507, #721629, #721868, #722210]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner,
 | 
			
		||||
  Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de],
 | 
			
		||||
  Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb],
 | 
			
		||||
  Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs],
 | 
			
		||||
  Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de],
 | 
			
		||||
  Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk],
 | 
			
		||||
  Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el],
 | 
			
		||||
  Fran Diéguez [gl]
 | 
			
		||||
 | 
			
		||||
3.11.3
 | 
			
		||||
======
 | 
			
		||||
* Fix fade effect of desktop icons [Florian; #707671]
 | 
			
		||||
* Fix issues with background management code [Jasper; #709313]
 | 
			
		||||
* Use new Glib facilities for application search [Jasper; #711631]
 | 
			
		||||
* Add focus indication to session menu button [Sebastien; #710539]
 | 
			
		||||
* Fix hover tracking for StEntries [Jasper; #706749]
 | 
			
		||||
* Fix reentrancy issue in message tray [Jasper; #711694]
 | 
			
		||||
* Tone down zoom animation on login/unlock [Jasper; #712362]
 | 
			
		||||
* Allow specifying monitor for OSD [Carlos; #712664]
 | 
			
		||||
* Fix resetting prompt on user switch [Ray; #710456]
 | 
			
		||||
* Stop using gnome-bluetooth-applet [Bastien; #719341]
 | 
			
		||||
* Add support for EAP-FAST password requests [Dan; #719813]
 | 
			
		||||
* Fix entry focus of chat notifications [Jasper; #709853]
 | 
			
		||||
* Make window previews keyboard navigatable [Jasper; #644306]
 | 
			
		||||
* Fix app switcher order with dialog windows [Florian; #719824]
 | 
			
		||||
* Allow remote search providers without icons [Debarshi; #719965]
 | 
			
		||||
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
 | 
			
		||||
  #712594, #712600, #712579]
 | 
			
		||||
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
 | 
			
		||||
  #712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
 | 
			
		||||
  #720298]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
 | 
			
		||||
  Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
 | 
			
		||||
  Jasper St. Pierre, Ray Strode, Dan Williams
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
 | 
			
		||||
  Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
 | 
			
		||||
  Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
 | 
			
		||||
  Daniel Mustieles [es]
 | 
			
		||||
 | 
			
		||||
3.11.2
 | 
			
		||||
======
 | 
			
		||||
* Cache search result display actors [Jasper; #704912]
 | 
			
		||||
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
 | 
			
		||||
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
 | 
			
		||||
* Support disabling browser plugin [Colin; #711218]
 | 
			
		||||
* Restore support for 'disable-restart-buttons' [Florian; #711244]
 | 
			
		||||
* Validate parameters of exposed DBus methods [Florian; #699752]
 | 
			
		||||
* Connect applications to systemd journal if available [Colin; #711626]
 | 
			
		||||
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
 | 
			
		||||
  #644306, #711555, #709806, #711631, #711732]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
 | 
			
		||||
  Rico Tzschichholz, Colin Walters
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
 | 
			
		||||
  Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
 | 
			
		||||
 | 
			
		||||
3.11.1
 | 
			
		||||
======
 | 
			
		||||
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
 | 
			
		||||
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
 | 
			
		||||
  #710137, #710596]
 | 
			
		||||
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
 | 
			
		||||
* Don't autostart remote search providers at login [Giovanni; #708830]
 | 
			
		||||
* Fix spacing in end-session dialog [Sebastien; #710543]
 | 
			
		||||
* Prepare for js24 [Tim; #711052]
 | 
			
		||||
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
 | 
			
		||||
  #710144, #710541, #691409, #710745, #688331, #704912]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
 | 
			
		||||
  Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
 | 
			
		||||
 | 
			
		||||
3.10.1
 | 
			
		||||
======
 | 
			
		||||
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
 | 
			
		||||
* Fix signal strength indicators in network selector [Jasper; #708442]
 | 
			
		||||
* Scroll search results when focusing provider icons [Jasper; #708868]
 | 
			
		||||
* Add separate hover/active states to page indicators [Carlos; #708852]
 | 
			
		||||
* Tweak appearance of user name and avatar [Yash; #702309]
 | 
			
		||||
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
 | 
			
		||||
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
 | 
			
		||||
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
 | 
			
		||||
* Make network settings items point to the right device [Giovanni; #709246]
 | 
			
		||||
* Remove animation of window preview titles [Sebastien; #709392]
 | 
			
		||||
* Add 'Notifications' switch to tray menu [Florian; #707073]
 | 
			
		||||
* Make dropdown arrows consistent [Carlos; #709564]
 | 
			
		||||
* power: Use icon from primary device for status [Jasper; #709925]
 | 
			
		||||
* Fix XDND drags to overview [Adel; #708887]
 | 
			
		||||
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
 | 
			
		||||
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
 | 
			
		||||
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
 | 
			
		||||
* Use ARROW role for labels representing arrows [Alejandro; #710120]
 | 
			
		||||
* Make selected view in app picker persistent [Florian; #710042]
 | 
			
		||||
* Make network selector navigable by keyboard [Alejandro; #710144]
 | 
			
		||||
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
 | 
			
		||||
  Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
 | 
			
		||||
  #709638, #709866, #709998, #710019, #710104, #710115]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
 | 
			
		||||
  Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
 | 
			
		||||
  Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Dieter Verfaillie, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
 | 
			
		||||
  Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
 | 
			
		||||
  Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
 | 
			
		||||
  Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
 | 
			
		||||
  A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
 | 
			
		||||
  Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
 | 
			
		||||
  Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
 | 
			
		||||
  Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
 | 
			
		||||
  Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
 | 
			
		||||
  Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
 | 
			
		||||
  Wouter Bolsterlee [nl], António Lima [pt]
 | 
			
		||||
 | 
			
		||||
3.10.0.1
 | 
			
		||||
=========
 | 
			
		||||
* Fix login screen [Ray; #708691]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Ray Strode, Giovanni Campagna, Jasper St. Pierree
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
 | 
			
		||||
  Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
 | 
			
		||||
  Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
 | 
			
		||||
 | 
			
		||||
3.10.0
 | 
			
		||||
======
 | 
			
		||||
* Fix fade effect in ScrollViews [Carlos; #708256]
 | 
			
		||||
* network: Resync when activating connection changes [Jasper; #708322]
 | 
			
		||||
* Close run dialog when the screen locks [Florian; #708218]
 | 
			
		||||
* Fix entry growing out of password dialogs [Florian; #708324, #703833]
 | 
			
		||||
* Vertically center labels in submenu items [Jasper; #708330]
 | 
			
		||||
* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
 | 
			
		||||
* Fix bluetooth icon not being added to status menu [Jasper; #708541]
 | 
			
		||||
* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
 | 
			
		||||
* Fix passwords being cleared twice when authentication fails [Florian; #708186]
 | 
			
		||||
* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
 | 
			
		||||
* Increase width of aggregate menu popup [Adel; #708472]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
 | 
			
		||||
  Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
 | 
			
		||||
  A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
 | 
			
		||||
  Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
 | 
			
		||||
  Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
 | 
			
		||||
  Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
 | 
			
		||||
  Kenneth Nielsen [da], Matej Urbančič [sl]
 | 
			
		||||
 | 
			
		||||
3.9.92
 | 
			
		||||
======
 | 
			
		||||
* Don't show page indicators if there's only one page [Florian; #707363]
 | 
			
		||||
* Make :active style of app and non-app results consistent [Jakub; #704714]
 | 
			
		||||
* Fade app pages when scrolled [Florian; #707409]
 | 
			
		||||
* Don't block scrolling on page indicators [Carlos; #707609]
 | 
			
		||||
* Tweak visual appearance of folder views [Florian; #707662]
 | 
			
		||||
* Don't put minimized apps at the end of the app switcher [Florian; #707663]
 | 
			
		||||
* Merge the wayland branch [Giovanni, Neil; #707467]
 | 
			
		||||
* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
 | 
			
		||||
* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
 | 
			
		||||
* Set approriate a11y states on expandable menu items [Alejandro; #708038]
 | 
			
		||||
* Improve page indicator animation [Carlos; #707565]
 | 
			
		||||
* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
 | 
			
		||||
  Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
 | 
			
		||||
  #707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
 | 
			
		||||
  #707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
 | 
			
		||||
  Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
 | 
			
		||||
  Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
 | 
			
		||||
  Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
 | 
			
		||||
  Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
 | 
			
		||||
  Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
 | 
			
		||||
  Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
 | 
			
		||||
  Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
 | 
			
		||||
  A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
 | 
			
		||||
  Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
 | 
			
		||||
  Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
 | 
			
		||||
  Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
 | 
			
		||||
  Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
 | 
			
		||||
  Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
 | 
			
		||||
 | 
			
		||||
3.9.91
 | 
			
		||||
======
 | 
			
		||||
* Improve submenu styling [Jakub; #706037]
 | 
			
		||||
* Fix changing slider values via keyboard [Alejandro; #706386]
 | 
			
		||||
* Fix accessibility of sliders [Alejandro; #706391]
 | 
			
		||||
* Tweak system actions style [Jakub; #706638]
 | 
			
		||||
* Add support for auth without username / fix Not Listed? [Ray; #706607]
 | 
			
		||||
* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
 | 
			
		||||
* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
 | 
			
		||||
* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
 | 
			
		||||
* authPrompt: Fix controls moving when showing messages [Ray; #706670]
 | 
			
		||||
* Tweak padding between system status icons [Allan; #706796]
 | 
			
		||||
* Add a generic accessible usable by JS code [Alejandro; #648623]
 | 
			
		||||
* Improve keynav and accessibility of the calendar [Alejandro; #706903]
 | 
			
		||||
* Update to new NetworkManager APIs [Jasper; #706098]
 | 
			
		||||
* Hide system actions section in the lock screen [Jasper; #706852]
 | 
			
		||||
* Don't show other logged in users at log out [Giovanni; #707124]
 | 
			
		||||
* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
 | 
			
		||||
* dash: Reload favorites when installed apps change [Giovanni; #706878]
 | 
			
		||||
* Don't open overview after closing last window on workspace [Florian; #662581]
 | 
			
		||||
* Add FocusApp DBus method [Giovanni; #654086]
 | 
			
		||||
* Add ShowApplications DBus method [Giovanni; #698743]
 | 
			
		||||
* Implement new app picker design [Carlos, Florian; #706081]
 | 
			
		||||
* Improve frequent apps being empty by default [Carlos, Florian; #694710]
 | 
			
		||||
* Extend clickable area of page indicators [Giovanni; #707314]
 | 
			
		||||
 | 
			
		||||
* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
 | 
			
		||||
  #706681, #706841, #706843, #707064, #706262, #707197, #707269]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
 | 
			
		||||
  Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Jakub Steiner, Ray Strode, Seán de Búrca
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
 | 
			
		||||
  Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
 | 
			
		||||
  Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
 | 
			
		||||
  Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
 | 
			
		||||
  Ihar Hrachyshka [be]
 | 
			
		||||
 | 
			
		||||
3.9.90
 | 
			
		||||
======
 | 
			
		||||
* workspaceThumbnails: Exclude transient windows when shifting workspaces
 | 
			
		||||
  [Bradley; #705174]
 | 
			
		||||
* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
 | 
			
		||||
* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
 | 
			
		||||
* Animate the lock screen notification transitions [Giovanni; #687660]
 | 
			
		||||
* Wake up the screen when new notifications appear [Giovanni; #703084]
 | 
			
		||||
* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
 | 
			
		||||
* dateMenu: Add style class for the clock label [Jonh; #705634]
 | 
			
		||||
* keyboard: Translate IBus IME name if possible [Daiki; #695673]
 | 
			
		||||
* power: Display single digit minutes correctly [Sebastian; #705803]
 | 
			
		||||
* Implement new aggregate status menu [Jasper; #705845]
 | 
			
		||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
 | 
			
		||||
* Fix alignment of search provider icons [Tarun; #695760]
 | 
			
		||||
* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
 | 
			
		||||
* Respect always-show-universal-access-status setting [Tanner; #705733]
 | 
			
		||||
* Handle .desktop files with capital letters [Giovanni; #706252]
 | 
			
		||||
* authPrompt: Add smartcard support [Ray; #683437]
 | 
			
		||||
* Fix call notifications in busy mode [Emilio; #666221]
 | 
			
		||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
 | 
			
		||||
* Move message tray menu to a tray button [Jasper; #699272]
 | 
			
		||||
* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
 | 
			
		||||
* Work towards running as wayland compositor [Giovanni]
 | 
			
		||||
 - Switch to Mutter abstraction layer for cursor tracking [#705911]
 | 
			
		||||
 - Add confirmation dialog for display changes [#706208]
 | 
			
		||||
* Use a different background in screen shield [Giovanni; #688210]
 | 
			
		||||
* Add fade animation before blanking the screen [Giovanni; #699112]
 | 
			
		||||
* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
 | 
			
		||||
  Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
 | 
			
		||||
  #701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
 | 
			
		||||
  #704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
 | 
			
		||||
  #706536]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
 | 
			
		||||
  Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
 | 
			
		||||
  Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
 | 
			
		||||
  Daiki Ueno, Colin Walters, Jonh Wendell
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
 | 
			
		||||
  Fran Diéguez [gl], Gabor Kelemen [hu],
 | 
			
		||||
  Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
 | 
			
		||||
  Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
 | 
			
		||||
  Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
 | 
			
		||||
  Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
 | 
			
		||||
  Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
 | 
			
		||||
 | 
			
		||||
3.9.5
 | 
			
		||||
=====
 | 
			
		||||
* Fix width changes of the calendar popup [Florian; #704200]
 | 
			
		||||
* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
 | 
			
		||||
  #704670]
 | 
			
		||||
* Update design of lock screen notifications [Allan; #702305]
 | 
			
		||||
* Don't show empty backgroundMenu [Michael; #703868]
 | 
			
		||||
* Add option to limit app switcher to current workspace [Adel; #703538]
 | 
			
		||||
* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
 | 
			
		||||
* Respect hasWorkspace property of session mode [Jasper; #698593]
 | 
			
		||||
* Fix fade of app menu icon in RTL locales [Jasper; #704583]
 | 
			
		||||
* Destroy notifications when the close button is clicked [Adel; #687016]
 | 
			
		||||
* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
 | 
			
		||||
* authPrompt: Fade out message if users start to type [Ray; #704817]
 | 
			
		||||
* Export timestamps of global shortcuts on DBus [Bastien; #704859]
 | 
			
		||||
* Fix duplicate search provider results [Jasper; #700283]
 | 
			
		||||
* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
 | 
			
		||||
  #703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
 | 
			
		||||
  #704347, #704453, #704471, #704542, #704707, #703905, #705037]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
 | 
			
		||||
  Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
 | 
			
		||||
  Michael Wood
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
 | 
			
		||||
  Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
 | 
			
		||||
  Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
 | 
			
		||||
 | 
			
		||||
3.9.4
 | 
			
		||||
=====
 | 
			
		||||
* Fix chat entries not being focused when expanded [Jasper; #698778]
 | 
			
		||||
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
 | 
			
		||||
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
 | 
			
		||||
* Round the ends of slider trough [Jasper; #702825]
 | 
			
		||||
* Add support for "box-shadow: none" [Cosimo; #702782]
 | 
			
		||||
* Keep chrome below popup windows [Florian; #702338]
 | 
			
		||||
* Move the session list to a popup menu [Ray; #702818]
 | 
			
		||||
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
 | 
			
		||||
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
 | 
			
		||||
* Fix management of asynchronous background loading [Lionel; #703001]
 | 
			
		||||
* Rework focus handling [Jasper; #700735]
 | 
			
		||||
* Optimize box-shadow rendering [Lionel; #689858]
 | 
			
		||||
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
 | 
			
		||||
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
 | 
			
		||||
  #702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
 | 
			
		||||
  #698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
 | 
			
		||||
  Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
 | 
			
		||||
  Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
 | 
			
		||||
  Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
 | 
			
		||||
  Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
 | 
			
		||||
 | 
			
		||||
3.9.3
 | 
			
		||||
=====
 | 
			
		||||
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								README
									
									
									
									
									
								
							
							
						
						@@ -8,7 +8,7 @@ For more information about GNOME Shell, including instructions on how
 | 
			
		||||
to build GNOME Shell from source and how to get involved with the project,
 | 
			
		||||
see:
 | 
			
		||||
 | 
			
		||||
 http://live.gnome.org/GnomeShell
 | 
			
		||||
 https://wiki.gnome.org/Projects/GnomeShell
 | 
			
		||||
 | 
			
		||||
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
 | 
			
		||||
product.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
# Run this to generate all the initial makefiles, etc.
 | 
			
		||||
 | 
			
		||||
srcdir=`dirname $0`
 | 
			
		||||
 
 | 
			
		||||
@@ -17,5 +17,4 @@ libgnome_shell_browser_plugin_la_SOURCES = 	\
 | 
			
		||||
 | 
			
		||||
libgnome_shell_browser_plugin_la_CFLAGS = 	\
 | 
			
		||||
	$(BROWSER_PLUGIN_CFLAGS)		\
 | 
			
		||||
	-DG_DISABLE_DEPRECATED			\
 | 
			
		||||
	-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,7 @@
 | 
			
		||||
 * General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *      Jasper St. Pierre <jstpierre@mecheye.net>
 | 
			
		||||
@@ -43,6 +41,8 @@
 | 
			
		||||
 | 
			
		||||
#define PLUGIN_API_VERSION 5
 | 
			
		||||
 | 
			
		||||
#define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  GDBusProxy *proxy;
 | 
			
		||||
} PluginData;
 | 
			
		||||
@@ -833,6 +833,16 @@ plugin_get_shell_version (PluginObject  *obj,
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
plugin_get_version_validation_enabled (PluginObject  *obj,
 | 
			
		||||
                                       NPVariant     *result)
 | 
			
		||||
{
 | 
			
		||||
  gboolean is_enabled = !g_settings_get_boolean (obj->settings, EXTENSION_DISABLE_VERSION_CHECK_KEY);
 | 
			
		||||
  BOOLEAN_TO_NPVARIANT(is_enabled, *result);
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define METHODS                                 \
 | 
			
		||||
  METHOD (list_extensions)                      \
 | 
			
		||||
  METHOD (get_info)                             \
 | 
			
		||||
@@ -852,6 +862,8 @@ static NPIdentifier api_version_id;
 | 
			
		||||
static NPIdentifier shell_version_id;
 | 
			
		||||
static NPIdentifier onextension_changed_id;
 | 
			
		||||
static NPIdentifier onrestart_id;
 | 
			
		||||
static NPIdentifier version_validation_enabled_id;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
plugin_object_has_method (NPObject     *npobj,
 | 
			
		||||
@@ -894,7 +906,8 @@ plugin_object_has_property (NPObject     *npobj,
 | 
			
		||||
  return (name == onextension_changed_id ||
 | 
			
		||||
          name == onrestart_id ||
 | 
			
		||||
          name == api_version_id ||
 | 
			
		||||
          name == shell_version_id);
 | 
			
		||||
          name == shell_version_id ||
 | 
			
		||||
          name == version_validation_enabled_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
@@ -912,6 +925,8 @@ plugin_object_get_property (NPObject     *npobj,
 | 
			
		||||
    return plugin_get_api_version (obj, result);
 | 
			
		||||
  else if (name == shell_version_id)
 | 
			
		||||
    return plugin_get_shell_version (obj, result);
 | 
			
		||||
  else if (name == version_validation_enabled_id)
 | 
			
		||||
    return plugin_get_version_validation_enabled (obj, result);
 | 
			
		||||
  else if (name == onextension_changed_id)
 | 
			
		||||
    {
 | 
			
		||||
      if (obj->listener)
 | 
			
		||||
@@ -990,6 +1005,7 @@ init_methods_and_properties (void)
 | 
			
		||||
  /* this is the JS public API; it is manipulated through NPIdentifiers for speed */
 | 
			
		||||
  api_version_id = funcs.getstringidentifier ("apiVersion");
 | 
			
		||||
  shell_version_id = funcs.getstringidentifier ("shellVersion");
 | 
			
		||||
  version_validation_enabled_id = funcs.getstringidentifier ("versionValidationEnabled");
 | 
			
		||||
 | 
			
		||||
  get_info_id = funcs.getstringidentifier ("getExtensionInfo");
 | 
			
		||||
  list_extensions_id = funcs.getstringidentifier ("listExtensions");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										157
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.9.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.13.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_HEADERS([config.h])
 | 
			
		||||
AC_CONFIG_SRCDIR([src/shell-global.c])
 | 
			
		||||
@@ -16,6 +16,7 @@ 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])
 | 
			
		||||
@@ -24,9 +25,6 @@ LT_INIT([disable-static])
 | 
			
		||||
# i18n
 | 
			
		||||
IT_PROG_INTLTOOL([0.40])
 | 
			
		||||
 | 
			
		||||
AM_GNU_GETTEXT([external])
 | 
			
		||||
AM_GNU_GETTEXT_VERSION([0.17])
 | 
			
		||||
 | 
			
		||||
GETTEXT_PACKAGE=gnome-shell
 | 
			
		||||
AC_SUBST(GETTEXT_PACKAGE)
 | 
			
		||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
 | 
			
		||||
@@ -53,17 +51,32 @@ 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 xfixes)
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
 | 
			
		||||
else
 | 
			
		||||
   AC_MSG_RESULT(no)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 | 
			
		||||
 | 
			
		||||
CLUTTER_MIN_VERSION=1.13.4
 | 
			
		||||
AC_ARG_ENABLE([systemd],
 | 
			
		||||
              AS_HELP_STRING([--enable-systemd], [Use 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],
 | 
			
		||||
                   [have_systemd=yes
 | 
			
		||||
                    AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
 | 
			
		||||
                   [have_systemd=no])
 | 
			
		||||
  AC_MSG_RESULT($have_systemd)
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_MSG_RESULT($enable_systemd)
 | 
			
		||||
 | 
			
		||||
CLUTTER_MIN_VERSION=1.15.90
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
 | 
			
		||||
GJS_MIN_VERSION=1.35.4
 | 
			
		||||
MUTTER_MIN_VERSION=3.9.3
 | 
			
		||||
GJS_MIN_VERSION=1.39.0
 | 
			
		||||
MUTTER_MIN_VERSION=3.13.2
 | 
			
		||||
GTK_MIN_VERSION=3.7.9
 | 
			
		||||
GIO_MIN_VERSION=2.37.0
 | 
			
		||||
LIBECAL_MIN_VERSION=3.5.3
 | 
			
		||||
@@ -73,54 +86,56 @@ POLKIT_MIN_VERSION=0.100
 | 
			
		||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
 | 
			
		||||
GCR_MIN_VERSION=3.7.5
 | 
			
		||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
 | 
			
		||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
 | 
			
		||||
NETWORKMANAGER_MIN_VERSION=0.9.8
 | 
			
		||||
PULSE_MIN_VERS=2.0
 | 
			
		||||
 | 
			
		||||
# Collect more than 20 libraries for a prize!
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
			       libxml-2.0
 | 
			
		||||
                               gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
                               atk-bridge-2.0
 | 
			
		||||
                               libmutter >= $MUTTER_MIN_VERSION
 | 
			
		||||
                               gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
			       libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
 | 
			
		||||
                               $recorder_modules
 | 
			
		||||
                               gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
			       clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
			       clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
                               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 xfixes
 | 
			
		||||
                               libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                               libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                               libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
 | 
			
		||||
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
 | 
			
		||||
            $recorder_modules
 | 
			
		||||
            gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
            xtst
 | 
			
		||||
            clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            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"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
 | 
			
		||||
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(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(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
 | 
			
		||||
PKG_CHECK_MODULES(TRAY, 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.7.4)
 | 
			
		||||
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([for bluetooth support])
 | 
			
		||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
 | 
			
		||||
        [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
 | 
			
		||||
	 BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
 | 
			
		||||
	 AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
 | 
			
		||||
	 AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[1])
 | 
			
		||||
	 AC_MSG_RESULT([yes])],
 | 
			
		||||
AC_ARG_ENABLE(browser-plugin,
 | 
			
		||||
              [AS_HELP_STRING([--enable-browser-plugin],
 | 
			
		||||
                              [Enable browser plugin [default=yes]])],,
 | 
			
		||||
              enable_browser_plugin=yes)
 | 
			
		||||
AS_IF([test x$enable_browser_plugin = xyes], [
 | 
			
		||||
  PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
 | 
			
		||||
])
 | 
			
		||||
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0,
 | 
			
		||||
        [AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[1])],
 | 
			
		||||
	[AC_DEFINE([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_MSG_RESULT([no])])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[0])])
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
 | 
			
		||||
@@ -132,13 +147,17 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
 | 
			
		||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 | 
			
		||||
 | 
			
		||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_GIR_DIR)
 | 
			
		||||
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_TYPELIB_DIR)
 | 
			
		||||
 | 
			
		||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
 | 
			
		||||
AC_SUBST(GJS_CONSOLE)
 | 
			
		||||
 | 
			
		||||
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
 | 
			
		||||
AC_SUBST(GLIB_COMPILE_RESOURCES)
 | 
			
		||||
 | 
			
		||||
AC_CHECK_FUNCS(fdwalk)
 | 
			
		||||
AC_CHECK_FUNCS(mallinfo)
 | 
			
		||||
AC_CHECK_HEADERS([sys/resource.h])
 | 
			
		||||
@@ -154,6 +173,38 @@ if test "$langinfo_ok" = "yes"; then
 | 
			
		||||
            [Define if _NL_TIME_FIRST_WEEKDAY is available])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(networkmanager,
 | 
			
		||||
             AS_HELP_STRING([--disable-networkmanager],
 | 
			
		||||
                            [disable NetworkManager support  @<:@default=auto@:>@]),,
 | 
			
		||||
              [enable_networkmanager=auto])
 | 
			
		||||
 | 
			
		||||
if test "x$enable_networkmanager" != "xno"; then
 | 
			
		||||
   PKG_CHECK_MODULES(NETWORKMANAGER,
 | 
			
		||||
                     [libnm-glib
 | 
			
		||||
                     libnm-util >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                     libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                     libsecret-unstable],
 | 
			
		||||
                     [have_networkmanager=yes],
 | 
			
		||||
                     [have_networkmanager=no])
 | 
			
		||||
 | 
			
		||||
   GNOME_SHELL_CFLAGS="$GNOME_SHELL_CFLAGS $NETWORKMANAGER_CFLAGS"
 | 
			
		||||
   GNOME_SHELL_LIBS="$GNOME_SHELL_LIBS $NETWORKMANAGER_LIBS"
 | 
			
		||||
else
 | 
			
		||||
   have_networkmanager="no  (disabled)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "x$have_networkmanager" = "xyes"; then
 | 
			
		||||
   AC_DEFINE(HAVE_NETWORKMANAGER, [1], [Define if we have NetworkManager])
 | 
			
		||||
   AC_SUBST([HAVE_NETWORKMANAGER], [1])
 | 
			
		||||
else
 | 
			
		||||
   if test "x$enable_networkmanager" = "xyes"; then
 | 
			
		||||
      AC_MSG_ERROR([Couldn't find NetworkManager.])
 | 
			
		||||
   fi
 | 
			
		||||
   AC_SUBST([HAVE_NETWORKMANAGER], [0])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(HAVE_NETWORKMANAGER, test "$have_networkmanager" = "yes")
 | 
			
		||||
 | 
			
		||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
 | 
			
		||||
AM_PATH_GLIB_2_0()
 | 
			
		||||
 | 
			
		||||
@@ -172,10 +223,14 @@ fi
 | 
			
		||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
 | 
			
		||||
 | 
			
		||||
GNOME_COMPILE_WARNINGS([error])
 | 
			
		||||
case "$WARN_CFLAGS" in
 | 
			
		||||
    *-Werror*)
 | 
			
		||||
        WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations"
 | 
			
		||||
        ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
 | 
			
		||||
  AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
 | 
			
		||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
 | 
			
		||||
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS"
 | 
			
		||||
AC_SUBST(AM_CFLAGS)
 | 
			
		||||
 | 
			
		||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
 | 
			
		||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
 | 
			
		||||
@@ -199,3 +254,15 @@ AC_CONFIG_FILES([
 | 
			
		||||
  man/Makefile
 | 
			
		||||
])
 | 
			
		||||
AC_OUTPUT
 | 
			
		||||
 | 
			
		||||
echo "
 | 
			
		||||
Build configuration:
 | 
			
		||||
 | 
			
		||||
       Prefix:                                 ${prefix}
 | 
			
		||||
       Source code location:                   ${srcdir}
 | 
			
		||||
       Compiler:                               ${CC}
 | 
			
		||||
       Compiler Warnings:                      $enable_compile_warnings
 | 
			
		||||
 | 
			
		||||
       Support for NetworkManager:             $have_networkmanager
 | 
			
		||||
       Support for GStreamer recording:        $build_recorder
 | 
			
		||||
"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<KeyListEntries schema="org.gnome.shell.keybindings"
 | 
			
		||||
                group="system"
 | 
			
		||||
                _name="Screenshots"
 | 
			
		||||
                wm_name="GNOME Shell"
 | 
			
		||||
                package="gnome-shell">
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-recording"
 | 
			
		||||
                      _description="Record a screencast"/>
 | 
			
		||||
 | 
			
		||||
</KeyListEntries>
 | 
			
		||||
 | 
			
		||||
@@ -1,8 +1,5 @@
 | 
			
		||||
wandadir = $(pkgdatadir)
 | 
			
		||||
dist_wanda_DATA = wanda.png
 | 
			
		||||
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop  gnome-shell-extension-prefs.desktop
 | 
			
		||||
 | 
			
		||||
# We substitute in bindir so it works as an autostart
 | 
			
		||||
# file when built in a non-system prefix
 | 
			
		||||
@@ -38,9 +35,14 @@ dist_theme_DATA =				\
 | 
			
		||||
	theme/filter-selected-rtl.svg		\
 | 
			
		||||
	theme/gnome-shell.css			\
 | 
			
		||||
	theme/logged-in-indicator.svg		\
 | 
			
		||||
	theme/menu-arrow-symbolic.svg		    \
 | 
			
		||||
	theme/message-tray-background.png	\
 | 
			
		||||
	theme/more-results.svg			\
 | 
			
		||||
	theme/noise-texture.png			\
 | 
			
		||||
	theme/page-indicator-active.svg		\
 | 
			
		||||
	theme/page-indicator-inactive.svg	\
 | 
			
		||||
	theme/page-indicator-checked.svg	\
 | 
			
		||||
	theme/page-indicator-hover.svg		\
 | 
			
		||||
	theme/panel-button-border.svg		\
 | 
			
		||||
	theme/panel-button-highlight-narrow.svg	\
 | 
			
		||||
	theme/panel-button-highlight-wide.svg	\
 | 
			
		||||
@@ -56,10 +58,7 @@ dist_theme_DATA =				\
 | 
			
		||||
	theme/ws-switch-arrow-down.png
 | 
			
		||||
 | 
			
		||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
 | 
			
		||||
keys_in_files =					\
 | 
			
		||||
	50-gnome-shell-screenshot.xml.in	\
 | 
			
		||||
	50-gnome-shell-system.xml.in		\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
keys_in_files = 50-gnome-shell-system.xml.in
 | 
			
		||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
 | 
			
		||||
 | 
			
		||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
 | 
			
		||||
@@ -84,6 +83,7 @@ convert_DATA = gnome-shell-overrides.convert
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST =						\
 | 
			
		||||
	gnome-shell.desktop.in.in			\
 | 
			
		||||
	gnome-shell-wayland.desktop.in.in		\
 | 
			
		||||
	gnome-shell-extension-prefs.desktop.in.in	\
 | 
			
		||||
	$(introspection_DATA)				\
 | 
			
		||||
	$(menu_DATA)					\
 | 
			
		||||
@@ -93,6 +93,7 @@ EXTRA_DIST =						\
 | 
			
		||||
 | 
			
		||||
CLEANFILES =						\
 | 
			
		||||
	gnome-shell.desktop.in				\
 | 
			
		||||
	gnome-shell-wayland.desktop.in			\
 | 
			
		||||
	gnome-shell-extension-prefs.in			\
 | 
			
		||||
	$(desktop_DATA)					\
 | 
			
		||||
	$(keys_DATA)					\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
[org.gnome.shell.overrides]
 | 
			
		||||
attach-modal-dialogs = /desktop/gnome/shell/windows/attach_modal_dialogs
 | 
			
		||||
button-layout = /desktop/gnome/shell/windows/button_layout
 | 
			
		||||
edge-tiling = /desktop/gnome/shell/windows/edge_tiling
 | 
			
		||||
workspaces-only-on-primary = /desktop/gnome/shell/windows/workspaces_only_on_primary
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								data/gnome-shell-wayland.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
[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
 | 
			
		||||
@@ -13,29 +13,37 @@
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="enabled-extensions" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>Uuids of extensions to enable</_summary>
 | 
			
		||||
      <_summary>UUIDs of extensions to enable</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        GNOME Shell extensions have a uuid property; this key lists extensions
 | 
			
		||||
        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 DBus methods on org.gnome.Shell.
 | 
			
		||||
        EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="disable-extension-version-validation" type="b">
 | 
			
		||||
      <default>false</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>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="favorite-apps" type="as">
 | 
			
		||||
      <default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
 | 
			
		||||
      <default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Documents.desktop' ]</default>
 | 
			
		||||
      <_summary>List of desktop file IDs for favorite applications</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The applications corresponding to these identifiers
 | 
			
		||||
        will be displayed in the favorites area.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="app-folder-categories" type="as">
 | 
			
		||||
      <default>[ 'Utilities', 'Sundry' ]</default>
 | 
			
		||||
      <_summary>List of categories that should be displayed as folders</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Each category name in this list will be represented as folder in the
 | 
			
		||||
        application view, rather than being displayed inline in the main view.
 | 
			
		||||
      </_description>
 | 
			
		||||
    <key name="app-picker-view" type="u">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <summary>App Picker View</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
        Index of the currently selected view in the application picker.
 | 
			
		||||
      </description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="command-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
@@ -45,22 +53,12 @@
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for the looking glass dialog</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-im-presence" type="i">
 | 
			
		||||
      <default>1</default>
 | 
			
		||||
      <_summary>Internally used to store the last IM presence explicitly set by the user. The
 | 
			
		||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-session-presence" type="i">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <_summary>Internally used to store the last session presence status for the user. The
 | 
			
		||||
value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="always-show-log-out" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
 | 
			
		||||
      <_summary>Always show the 'Log out' menu item in the user menu.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the automatic hiding of the 'Log out'
 | 
			
		||||
        menuitem in single-user, single-session situations.
 | 
			
		||||
        menu item in single-user, single-session situations.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="remember-mount-password" type="b">
 | 
			
		||||
@@ -74,9 +72,9 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <child name="calendar" schema="org.gnome.shell.calendar"/>
 | 
			
		||||
    <child name="recorder" schema="org.gnome.shell.recorder"/>
 | 
			
		||||
    <child name="keybindings" schema="org.gnome.shell.keybindings"/>
 | 
			
		||||
    <child name="keyboard" schema="org.gnome.shell.keyboard"/>
 | 
			
		||||
    <child name="location" schema="org.gnome.shell.location"/>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
 | 
			
		||||
@@ -128,12 +126,10 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
        Keybinding to focus the active notification.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-recording" type="as">
 | 
			
		||||
      <default><![CDATA[['<Control><Shift><Alt>r']]]></default>
 | 
			
		||||
      <_summary>Keybinding to toggle the screen recorder</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to start/stop the builtin screen recorder.
 | 
			
		||||
      </_description>
 | 
			
		||||
    <key name="pause-resume-tweens" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
 | 
			
		||||
      <description></description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -148,41 +144,42 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
 | 
			
		||||
  <enum id="org.gnome.shell.geoclue.AccuracyLevel">
 | 
			
		||||
    <value value="0" nick="off"/>
 | 
			
		||||
    <value value="1" nick="country"/>
 | 
			
		||||
    <value value="4" nick="city"/>
 | 
			
		||||
    <value value="5" nick="neighborhood"/>
 | 
			
		||||
    <value value="6" nick="street"/>
 | 
			
		||||
    <value value="8" nick="exact"/>
 | 
			
		||||
  </enum>
 | 
			
		||||
  <schema id="org.gnome.shell.location"
 | 
			
		||||
          path="/org/gnome/shell/location/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="framerate" type="i">
 | 
			
		||||
      <default>30</default>
 | 
			
		||||
      <_summary>Framerate used for recording screencasts.</_summary>
 | 
			
		||||
    <key name="max-accuracy-level" enum="org.gnome.shell.geoclue.AccuracyLevel">
 | 
			
		||||
      <default>'exact'</default>
 | 
			
		||||
      <_summary>The maximum accuracy level of location.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The framerate of the resulting screencast recordered
 | 
			
		||||
        by GNOME Shell's screencast recorder in frames-per-second.
 | 
			
		||||
        Configures the maximum level of location accuracy applications are
 | 
			
		||||
        allowed to see. Valid options are 'off' (disable location tracking),
 | 
			
		||||
        'country', 'city', 'neighborhood', 'street', and 'exact' (typically
 | 
			
		||||
        requires GPS receiver). Please keep in mind that this only controls
 | 
			
		||||
        what GeoClue will allow applications to see and they can find user's
 | 
			
		||||
        location on their own using network resources (albeit with street-level
 | 
			
		||||
        accuracy at best).
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="pipeline" type="s">
 | 
			
		||||
      <default>''</default>
 | 
			
		||||
      <_summary>The gstreamer pipeline used to encode the screencast</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Sets the GStreamer pipeline used to encode recordings.
 | 
			
		||||
        It follows the syntax used for gst-launch. The pipeline should have
 | 
			
		||||
        an unconnected sink pad where the recorded video is recorded. It will
 | 
			
		||||
        normally have a unconnected source pad; output from that pad
 | 
			
		||||
        will be written into the output file. However the pipeline can also
 | 
			
		||||
        take care of its own output - this might be used to send the output
 | 
			
		||||
        to an icecast server via shout2send or similar. When unset or set
 | 
			
		||||
        to an empty value, the default pipeline will be used. This is currently
 | 
			
		||||
        'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
 | 
			
		||||
        and records to WEBM using the VP8 codec. %T is used as a placeholder
 | 
			
		||||
        for a guess at the optimal thread count on the system.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="file-extension" type="s">
 | 
			
		||||
      <default>'webm'</default>
 | 
			
		||||
      <_summary>File extension used for storing the screencast</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The filename for recorded screencasts will be a unique filename
 | 
			
		||||
        based on the current date, and use this extension. It should be
 | 
			
		||||
        changed when recording to a different container format.
 | 
			
		||||
      </_description>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.app-switcher"
 | 
			
		||||
          path="/org/gnome/shell/app-switcher/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key type="b" name="current-workspace-only">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <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>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -224,15 +221,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="button-layout" type="s">
 | 
			
		||||
      <default>":close"</default>
 | 
			
		||||
      <_summary>Arrangement of buttons on the titlebar</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key overrides the key in org.gnome.desktop.wm.preferences when
 | 
			
		||||
        running GNOME Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <key name="edge-tiling" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Enable edge tiling when dropping windows on screen edges</_summary>
 | 
			
		||||
@@ -259,10 +247,10 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								data/theme/menu-arrow-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,90 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg3863"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="menu-arrow.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3865" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="15.836083"
 | 
			
		||||
     inkscape:cx="-3.1641676"
 | 
			
		||||
     inkscape:cy="11.823817"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     showguides="true"
 | 
			
		||||
     inkscape:guide-bbox="true"
 | 
			
		||||
     inkscape:window-width="1366"
 | 
			
		||||
     inkscape:window-height="702"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:snap-bbox="true">
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       orientation="1,0"
 | 
			
		||||
       position="15.996443,16.922964"
 | 
			
		||||
       id="guide3873" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       orientation="0,1"
 | 
			
		||||
       position="28.041217,3.1256134"
 | 
			
		||||
       id="guide3875" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       orientation="0,1"
 | 
			
		||||
       position="-0.80372916,24.469088"
 | 
			
		||||
       id="guide3877" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       orientation="1,0"
 | 
			
		||||
       position="3.0363102,34.649657"
 | 
			
		||||
       id="guide3879" />
 | 
			
		||||
    <sodipodi:guide
 | 
			
		||||
       orientation="1,0"
 | 
			
		||||
       position="29.023553,28.577037"
 | 
			
		||||
       id="guide3881" />
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid2988" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3868">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-16)">
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:none"
 | 
			
		||||
       d="m 4,23 8,0 -4,5 z"
 | 
			
		||||
       id="path3883"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       sodipodi:nodetypes="cccc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.5 KiB  | 
@@ -14,7 +14,7 @@
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg12430"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.3.1 r9886"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="more-results.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs12432" />
 | 
			
		||||
@@ -25,18 +25,18 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="8.3155237"
 | 
			
		||||
     inkscape:cy="0.89548874"
 | 
			
		||||
     inkscape:zoom="90.509668"
 | 
			
		||||
     inkscape:cx="6.5009792"
 | 
			
		||||
     inkscape:cy="8.3589595"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="g14642-3-0"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1376"
 | 
			
		||||
     inkscape:window-x="1200"
 | 
			
		||||
     inkscape:window-y="187"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="840"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
@@ -90,6 +90,11 @@
 | 
			
		||||
         transform="translate(-2,0)"
 | 
			
		||||
         width="16"
 | 
			
		||||
         height="16" />
 | 
			
		||||
      <path
 | 
			
		||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
 | 
			
		||||
         d="M 7 5 L 7 7 L 5 7 L 5 9 L 7 9 L 7 11 L 9 11 L 9 9 L 11 9 L 11 7 L 9 7 L 9 5 L 7 5 z "
 | 
			
		||||
         transform="translate(141.99984,397.99107)"
 | 
			
		||||
         id="rect3757" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										71
									
								
								data/theme/page-indicator-active.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,71 @@
 | 
			
		||||
<?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="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg4703"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-pushed.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4705" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="31.392433"
 | 
			
		||||
     inkscape:cx="1.0245308"
 | 
			
		||||
     inkscape:cy="13.3715"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid6140" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4708">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
 | 
			
		||||
       d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       id="path4711"
 | 
			
		||||
       style="fill:#fdffff;fill-opacity:1;stroke:none"
 | 
			
		||||
       sodipodi:type="arc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										67
									
								
								data/theme/page-indicator-checked.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
<?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="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg4703"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-active.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4705" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="22.197802"
 | 
			
		||||
     inkscape:cx="2.1522887"
 | 
			
		||||
     inkscape:cy="16.782904"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1021"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4708">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       id="path4711"
 | 
			
		||||
       style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
 | 
			
		||||
       sodipodi:type="arc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										67
									
								
								data/theme/page-indicator-hover.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
<?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="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg5266"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-inactive.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5268" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.313709"
 | 
			
		||||
     inkscape:cx="-2.307566"
 | 
			
		||||
     inkscape:cy="17.859535"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5271">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
       id="path5274"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										67
									
								
								data/theme/page-indicator-inactive.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
<?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="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg5266"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-inactive.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5268" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.313709"
 | 
			
		||||
     inkscape:cx="-2.307566"
 | 
			
		||||
     inkscape:cy="17.859535"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5271">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
 | 
			
		||||
       id="path5274"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 13 KiB  | 
@@ -66,6 +66,7 @@ IGNORE_HFILES=					\
 | 
			
		||||
	gactionmuxer.h				\
 | 
			
		||||
	gactionobservable.h			\
 | 
			
		||||
	gactionobserver.h			\
 | 
			
		||||
	shell-network-agent.h			\
 | 
			
		||||
	shell-recorder-src.h
 | 
			
		||||
 | 
			
		||||
if !BUILD_RECORDER
 | 
			
		||||
@@ -112,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) $(BLUETOOTH_LIBS) $(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
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 
 | 
			
		||||
@@ -48,10 +48,8 @@
 | 
			
		||||
    <xi:include href="xml/shell-global.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-keybinding-modes.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-wm.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-xfixes-cursor.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-util.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mount-operation.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-network-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tp-client.xml"/>
 | 
			
		||||
  </chapter>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,17 +17,15 @@ packages. If you are interested in building GNOME Shell from source,
 | 
			
		||||
we would recommend building from version control using the build
 | 
			
		||||
script described at:
 | 
			
		||||
 | 
			
		||||
 http://live.gnome.org/GnomeShell
 | 
			
		||||
 https://wiki.gnome.org/Projects/GnomeShell
 | 
			
		||||
 | 
			
		||||
Not only will that give you the very latest version of this rapidly
 | 
			
		||||
changing project, it will be much easier than get GNOME Shell and
 | 
			
		||||
its dependencies to build from tarballs.</description>
 | 
			
		||||
  <!--
 | 
			
		||||
  <homepage rdf:resource="http://live.gnome.org/GnomeShell" />
 | 
			
		||||
  -->
 | 
			
		||||
  <homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" />
 | 
			
		||||
  <mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" />
 | 
			
		||||
  <download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
 | 
			
		||||
  <bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
 | 
			
		||||
  <bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
 | 
			
		||||
 | 
			
		||||
  <category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								js/Makefile.am
									
									
									
									
									
								
							
							
						
						@@ -1,120 +1,38 @@
 | 
			
		||||
NULL =
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = misc/config.js.in
 | 
			
		||||
CLEANFILES = misc/config.js
 | 
			
		||||
BUILT_SOURCES =
 | 
			
		||||
 | 
			
		||||
misc/config.js: misc/config.js.in Makefile
 | 
			
		||||
	[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
 | 
			
		||||
	sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
 | 
			
		||||
	    -e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
 | 
			
		||||
	    -e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
 | 
			
		||||
	    -e "s|[@]HAVE_NETWORKMANAGER@|$(HAVE_NETWORKMANAGER)|g" \
 | 
			
		||||
	    -e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
 | 
			
		||||
	    -e "s|[@]datadir@|$(datadir)|g" \
 | 
			
		||||
	    -e "s|[@]libexecdir@|$(libexecdir)|g" \
 | 
			
		||||
	    -e "s|[@]sysconfdir@|$(sysconfdir)|g" \
 | 
			
		||||
               $< > $@
 | 
			
		||||
 | 
			
		||||
jsdir = $(pkgdatadir)/js
 | 
			
		||||
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/js-resources.gresource.xml)
 | 
			
		||||
js-resources.h: js-resources.gresource.xml $(js_resource_files) misc/config.js
 | 
			
		||||
	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
 | 
			
		||||
js-resources.c: js-resources.gresource.xml $(js_resource_files) misc/config.js
 | 
			
		||||
	$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
 | 
			
		||||
 | 
			
		||||
nobase_dist_js_DATA = 	\
 | 
			
		||||
	gdm/batch.js		\
 | 
			
		||||
	gdm/fingerprint.js	\
 | 
			
		||||
	gdm/loginDialog.js	\
 | 
			
		||||
	gdm/powerMenu.js	\
 | 
			
		||||
	gdm/realmd.js		\
 | 
			
		||||
	gdm/util.js		\
 | 
			
		||||
	extensionPrefs/main.js	\
 | 
			
		||||
	misc/config.js		\
 | 
			
		||||
	misc/extensionUtils.js	\
 | 
			
		||||
	misc/fileUtils.js	\
 | 
			
		||||
	misc/gnomeSession.js	\
 | 
			
		||||
	misc/hash.js		\
 | 
			
		||||
	misc/history.js		\
 | 
			
		||||
	misc/jsParse.js		\
 | 
			
		||||
	misc/loginManager.js	\
 | 
			
		||||
	misc/modemManager.js	\
 | 
			
		||||
	misc/params.js		\
 | 
			
		||||
	misc/util.js		\
 | 
			
		||||
	perf/core.js		\
 | 
			
		||||
	ui/altTab.js		\
 | 
			
		||||
	ui/animation.js		\
 | 
			
		||||
	ui/appDisplay.js	\
 | 
			
		||||
	ui/appFavorites.js	\
 | 
			
		||||
	ui/backgroundMenu.js	\
 | 
			
		||||
	ui/background.js	\
 | 
			
		||||
	ui/boxpointer.js	\
 | 
			
		||||
	ui/calendar.js		\
 | 
			
		||||
	ui/checkBox.js		\
 | 
			
		||||
	ui/ctrlAltTab.js	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
	ui/dnd.js		\
 | 
			
		||||
	ui/endSessionDialog.js	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/extensionDownloader.js \
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/ibusCandidatePopup.js\
 | 
			
		||||
	ui/grabHelper.js	\
 | 
			
		||||
	ui/iconGrid.js		\
 | 
			
		||||
	ui/keyboard.js		\
 | 
			
		||||
	ui/layout.js		\
 | 
			
		||||
	ui/lightbox.js		\
 | 
			
		||||
	ui/lookingGlass.js	\
 | 
			
		||||
	ui/magnifier.js		\
 | 
			
		||||
	ui/magnifierDBus.js	\
 | 
			
		||||
	ui/main.js		\
 | 
			
		||||
	ui/messageTray.js	\
 | 
			
		||||
	ui/modalDialog.js	\
 | 
			
		||||
	ui/separator.js		\
 | 
			
		||||
	ui/sessionMode.js	\
 | 
			
		||||
	ui/shellEntry.js	\
 | 
			
		||||
	ui/shellMountOperation.js \
 | 
			
		||||
	ui/slider.js		\
 | 
			
		||||
	ui/notificationDaemon.js \
 | 
			
		||||
	ui/osdWindow.js		\
 | 
			
		||||
	ui/overview.js		\
 | 
			
		||||
	ui/overviewControls.js	\
 | 
			
		||||
	ui/panel.js		\
 | 
			
		||||
	ui/panelMenu.js		\
 | 
			
		||||
	ui/pointerWatcher.js    \
 | 
			
		||||
	ui/popupMenu.js		\
 | 
			
		||||
	ui/remoteSearch.js	\
 | 
			
		||||
	ui/remoteMenu.js	\
 | 
			
		||||
	ui/runDialog.js		\
 | 
			
		||||
	ui/screencast.js	\
 | 
			
		||||
	ui/screenshot.js	\
 | 
			
		||||
        ui/screenShield.js	\
 | 
			
		||||
	ui/scripting.js		\
 | 
			
		||||
	ui/search.js		\
 | 
			
		||||
	ui/searchDisplay.js	\
 | 
			
		||||
	ui/shellDBus.js		\
 | 
			
		||||
	ui/status/accessibility.js	\
 | 
			
		||||
	ui/status/keyboard.js	\
 | 
			
		||||
	ui/status/lockScreenMenu.js	\
 | 
			
		||||
	ui/status/network.js	\
 | 
			
		||||
	ui/status/power.js	\
 | 
			
		||||
	ui/status/volume.js	\
 | 
			
		||||
	ui/status/bluetooth.js	\
 | 
			
		||||
	ui/switcherPopup.js	\
 | 
			
		||||
	ui/tweener.js		\
 | 
			
		||||
	ui/unlockDialog.js	\
 | 
			
		||||
	ui/userMenu.js		\
 | 
			
		||||
	ui/userWidget.js	\
 | 
			
		||||
	ui/viewSelector.js	\
 | 
			
		||||
	ui/wanda.js		\
 | 
			
		||||
	ui/windowAttentionHandler.js	\
 | 
			
		||||
	ui/windowManager.js	\
 | 
			
		||||
	ui/workspace.js		\
 | 
			
		||||
	ui/workspaceThumbnail.js	\
 | 
			
		||||
	ui/workspacesView.js	\
 | 
			
		||||
	ui/workspaceSwitcherPopup.js    \
 | 
			
		||||
	ui/xdndHandler.js	\
 | 
			
		||||
	ui/components/__init__.js		\
 | 
			
		||||
	ui/components/autorunManager.js		\
 | 
			
		||||
	ui/components/automountManager.js	\
 | 
			
		||||
	ui/components/networkAgent.js		\
 | 
			
		||||
	ui/components/polkitAgent.js		\
 | 
			
		||||
	ui/components/recorder.js		\
 | 
			
		||||
	ui/components/telepathyClient.js	\
 | 
			
		||||
	ui/components/keyring.js		\
 | 
			
		||||
js_built_sources = js-resources.c js-resources.h
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES += $(js_built_sources)
 | 
			
		||||
 | 
			
		||||
all-local: $(js_built_sources)
 | 
			
		||||
 | 
			
		||||
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	$(js_resource_dist_files) \
 | 
			
		||||
	js-resources.gresource.xml \
 | 
			
		||||
	misc/config.js.in \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
CLEANFILES = \
 | 
			
		||||
	$(js_built_sources) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,13 +13,20 @@ const _ = Gettext.gettext;
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
const ExtensionUtils = imports.misc.extensionUtils;
 | 
			
		||||
 | 
			
		||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
 | 
			
		||||
<signal name="ExtensionStatusChanged">
 | 
			
		||||
    <arg type="s" name="uuid"/>
 | 
			
		||||
    <arg type="i" name="state"/>
 | 
			
		||||
    <arg type="s" name="error"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const GnomeShellIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.Extensions"> \
 | 
			
		||||
<signal name="ExtensionStatusChanged"> \
 | 
			
		||||
    <arg type="s" name="uuid"/> \
 | 
			
		||||
    <arg type="i" name="state"/> \
 | 
			
		||||
    <arg type="s" name="error"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const customCss = '.prefs-button { \
 | 
			
		||||
                       padding: 8px; \
 | 
			
		||||
                       border-radius: 20px; \
 | 
			
		||||
                   }';
 | 
			
		||||
 | 
			
		||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
 | 
			
		||||
 | 
			
		||||
@@ -44,13 +51,9 @@ const Application = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsModules = {};
 | 
			
		||||
 | 
			
		||||
        this._extensionIters = {};
 | 
			
		||||
        this._startupUuid = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildModel: function() {
 | 
			
		||||
        this._model = new Gtk.ListStore();
 | 
			
		||||
        this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
 | 
			
		||||
        this._loaded = false;
 | 
			
		||||
        this._skipMainWindow = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionAvailable: function(uuid) {
 | 
			
		||||
@@ -68,11 +71,6 @@ const Application = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setExtensionInsensitive: function(layout, cell, model, iter, data) {
 | 
			
		||||
        let uuid = model.get_value(iter, 0);
 | 
			
		||||
        cell.set_sensitive(this._extensionAvailable(uuid));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getExtensionPrefsModule: function(extension) {
 | 
			
		||||
        let uuid = extension.metadata.uuid;
 | 
			
		||||
 | 
			
		||||
@@ -102,21 +100,23 @@ const Application = new Lang.Class({
 | 
			
		||||
            widget = this._buildErrorUI(extension, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Destroy the current prefs widget, if it exists
 | 
			
		||||
        if (this._extensionPrefsBin.get_child())
 | 
			
		||||
            this._extensionPrefsBin.get_child().destroy();
 | 
			
		||||
        let dialog = new Gtk.Dialog({ use_header_bar: true,
 | 
			
		||||
                                      modal: true,
 | 
			
		||||
                                      title: extension.metadata.name });
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin.add(widget);
 | 
			
		||||
        this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
 | 
			
		||||
    },
 | 
			
		||||
        if (this._skipMainWindow) {
 | 
			
		||||
            this.application.add_window(dialog);
 | 
			
		||||
            if (this._window)
 | 
			
		||||
                this._window.destroy();
 | 
			
		||||
            this._window = dialog;
 | 
			
		||||
            this._window.window_position = Gtk.WindowPosition.CENTER;
 | 
			
		||||
        } else {
 | 
			
		||||
            dialog.transient_for = this._window;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    _extensionSelected: function() {
 | 
			
		||||
        let [success, iter] = this._extensionSelector.get_active_iter();
 | 
			
		||||
        if (!success)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let uuid = this._model.get_value(iter, 0);
 | 
			
		||||
        this._selectExtension(uuid);
 | 
			
		||||
        dialog.set_default_size(600, 400);
 | 
			
		||||
        dialog.get_content_area().add(widget);
 | 
			
		||||
        dialog.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildErrorUI: function(extension, exc) {
 | 
			
		||||
@@ -149,48 +149,26 @@ const Application = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _buildUI: function(app) {
 | 
			
		||||
        this._window = new Gtk.ApplicationWindow({ application: app,
 | 
			
		||||
                                                   window_position: Gtk.WindowPosition.CENTER,
 | 
			
		||||
                                                   title: _("GNOME Shell Extension Preferences") });
 | 
			
		||||
                                                   window_position: Gtk.WindowPosition.CENTER });
 | 
			
		||||
 | 
			
		||||
        this._window.set_size_request(600, 400);
 | 
			
		||||
        this._window.set_size_request(800, 500);
 | 
			
		||||
 | 
			
		||||
        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
 | 
			
		||||
        this._window.add(vbox);
 | 
			
		||||
        this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
 | 
			
		||||
                                             title: _("GNOME Shell Extensions") });
 | 
			
		||||
        this._window.set_titlebar(this._titlebar);
 | 
			
		||||
 | 
			
		||||
        let toolbar = new Gtk.Toolbar();
 | 
			
		||||
        toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
 | 
			
		||||
        vbox.add(toolbar);
 | 
			
		||||
        let toolitem;
 | 
			
		||||
        let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
 | 
			
		||||
                                              shadow_type: Gtk.ShadowType.IN,
 | 
			
		||||
                                              halign: Gtk.Align.CENTER,
 | 
			
		||||
                                              margin: 18 });
 | 
			
		||||
        this._window.add(scroll);
 | 
			
		||||
 | 
			
		||||
        let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
 | 
			
		||||
                                    use_markup: true });
 | 
			
		||||
        toolitem = new Gtk.ToolItem({ child: label });
 | 
			
		||||
        toolbar.add(toolitem);
 | 
			
		||||
        this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
 | 
			
		||||
        this._extensionSelector.set_sort_func(Lang.bind(this, this._sortList));
 | 
			
		||||
        this._extensionSelector.set_header_func(Lang.bind(this, this._updateHeader));
 | 
			
		||||
 | 
			
		||||
        this._extensionSelector = new Gtk.ComboBox({ model: this._model,
 | 
			
		||||
                                                     margin_left: 8,
 | 
			
		||||
                                                     hexpand: true });
 | 
			
		||||
        this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
 | 
			
		||||
        scroll.add(this._extensionSelector);
 | 
			
		||||
 | 
			
		||||
        let renderer = new Gtk.CellRendererText();
 | 
			
		||||
        this._extensionSelector.pack_start(renderer, true);
 | 
			
		||||
        this._extensionSelector.add_attribute(renderer, 'text', 1);
 | 
			
		||||
        this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
 | 
			
		||||
        this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
 | 
			
		||||
 | 
			
		||||
        toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
 | 
			
		||||
        toolitem.set_expand(true);
 | 
			
		||||
        toolbar.add(toolitem);
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin = new Gtk.Frame();
 | 
			
		||||
        vbox.add(this._extensionPrefsBin);
 | 
			
		||||
 | 
			
		||||
        let label = new Gtk.Label({
 | 
			
		||||
            label: _("Select an extension to configure using the combobox above."),
 | 
			
		||||
            vexpand: true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._extensionPrefsBin.add(label);
 | 
			
		||||
 | 
			
		||||
        this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
 | 
			
		||||
        this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
 | 
			
		||||
@@ -201,23 +179,61 @@ 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;
 | 
			
		||||
        return name1.localeCompare(name2);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHeader: function(row, before) {
 | 
			
		||||
        if (!before || row.get_header())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let sep = new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL });
 | 
			
		||||
        row.set_header(sep);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scanExtensions: function() {
 | 
			
		||||
        let finder = new ExtensionUtils.ExtensionFinder();
 | 
			
		||||
        finder.connect('extension-found', Lang.bind(this, this._extensionFound));
 | 
			
		||||
        finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
 | 
			
		||||
        finder.scanExtensions();
 | 
			
		||||
        this._extensionsLoaded();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionFound: function(signals, extension) {
 | 
			
		||||
        let iter = this._model.append();
 | 
			
		||||
        this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
 | 
			
		||||
        this._extensionIters[extension.uuid] = iter;
 | 
			
		||||
    _extensionFound: function(finder, extension) {
 | 
			
		||||
        let row = new ExtensionRow(extension.uuid);
 | 
			
		||||
 | 
			
		||||
        row.prefsButton.visible = this._extensionAvailable(row.uuid);
 | 
			
		||||
        row.prefsButton.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._selectExtension(row.uuid);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        row.show_all();
 | 
			
		||||
        this._extensionSelector.add(row);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionsLoaded: function() {
 | 
			
		||||
        if (this._startupUuid && this._extensionAvailable(this._startupUuid))
 | 
			
		||||
            this._selectExtension(this._startupUuid);
 | 
			
		||||
        this._startupUuid = null;
 | 
			
		||||
        this._skipMainWindow = false;
 | 
			
		||||
        this._loaded = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function() {
 | 
			
		||||
@@ -225,29 +241,125 @@ const Application = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStartup: function(app) {
 | 
			
		||||
        this._buildModel();
 | 
			
		||||
        this._buildUI(app);
 | 
			
		||||
        this._addCustomStyle();
 | 
			
		||||
        this._scanExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCommandLine: function(app, commandLine) {
 | 
			
		||||
        app.activate();
 | 
			
		||||
        let args = commandLine.get_arguments();
 | 
			
		||||
 | 
			
		||||
        if (args.length) {
 | 
			
		||||
            let uuid = args[0];
 | 
			
		||||
 | 
			
		||||
            this._skipMainWindow = true;
 | 
			
		||||
 | 
			
		||||
            // Strip off "extension:///" prefix which fakes a URI, if it exists
 | 
			
		||||
            uuid = stripPrefix(uuid, "extension:///");
 | 
			
		||||
 | 
			
		||||
            if (this._extensionAvailable(uuid))
 | 
			
		||||
                this._selectExtension(uuid);
 | 
			
		||||
            else
 | 
			
		||||
            else if (!this._loaded)
 | 
			
		||||
                this._startupUuid = uuid;
 | 
			
		||||
            else
 | 
			
		||||
                this._skipMainWindow = false;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ExtensionRow = new Lang.Class({
 | 
			
		||||
    Name: 'ExtensionRow',
 | 
			
		||||
    Extends: Gtk.ListBoxRow,
 | 
			
		||||
 | 
			
		||||
    _init: function(uuid) {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this.uuid = uuid;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.shell' });
 | 
			
		||||
        this._settings.connect('changed::enabled-extensions', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._switch.state = this._isEnabled();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._buildUI();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildUI: function() {
 | 
			
		||||
        let extension = ExtensionUtils.extensions[this.uuid];
 | 
			
		||||
 | 
			
		||||
        let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
 | 
			
		||||
                                 hexpand: true, margin: 12, spacing: 6 });
 | 
			
		||||
        this.add(hbox);
 | 
			
		||||
 | 
			
		||||
        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
 | 
			
		||||
                                 spacing: 6, hexpand: true });
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        let name = GLib.markup_escape_text(extension.metadata.name, -1);
 | 
			
		||||
        let label = new Gtk.Label({ label: '<b>' + name + '</b>',
 | 
			
		||||
                                    use_markup: true,
 | 
			
		||||
                                    halign: Gtk.Align.START });
 | 
			
		||||
        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 });
 | 
			
		||||
        vbox.add(label);
 | 
			
		||||
 | 
			
		||||
        let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
 | 
			
		||||
                                      no_show_all: true });
 | 
			
		||||
        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');
 | 
			
		||||
        hbox.add(button);
 | 
			
		||||
 | 
			
		||||
        this.prefsButton = button;
 | 
			
		||||
 | 
			
		||||
        this._switch = new Gtk.Switch({ valign: Gtk.Align.CENTER,
 | 
			
		||||
                                        state: this._isEnabled() });
 | 
			
		||||
        this._switch.connect('notify::active', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this._switch.active)
 | 
			
		||||
                    this._enable();
 | 
			
		||||
                else
 | 
			
		||||
                    this._disable();
 | 
			
		||||
            }));
 | 
			
		||||
        this._switch.connect('state-set', function() { return true; });
 | 
			
		||||
        hbox.add(this._switch);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isEnabled: function() {
 | 
			
		||||
        let extensions = this._settings.get_strv('enabled-extensions');
 | 
			
		||||
        return extensions.indexOf(this.uuid) != -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _enable: function() {
 | 
			
		||||
        let extensions = this._settings.get_strv('enabled-extensions');
 | 
			
		||||
        if (extensions.indexOf(this.uuid) != -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        extensions.push(this.uuid);
 | 
			
		||||
        this._settings.set_strv('enabled-extensions', extensions);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _disable: function() {
 | 
			
		||||
        let extensions = this._settings.get_strv('enabled-extensions');
 | 
			
		||||
        let pos = extensions.indexOf(this.uuid);
 | 
			
		||||
        if (pos == -1)
 | 
			
		||||
            return;
 | 
			
		||||
        do {
 | 
			
		||||
            extensions.splice(pos, 1);
 | 
			
		||||
            pos = extensions.indexOf(this.uuid);
 | 
			
		||||
        } while (pos != -1);
 | 
			
		||||
        this._settings.set_strv('enabled-extensions', extensions);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function initEnvironment() {
 | 
			
		||||
    // Monkey-patch in a "global" object that fakes some Shell utilities
 | 
			
		||||
    // that ExtensionUtils depends on.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										505
									
								
								js/gdm/authPrompt.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,505 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
 | 
			
		||||
 | 
			
		||||
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
 | 
			
		||||
 | 
			
		||||
const AuthPromptMode = {
 | 
			
		||||
    UNLOCK_ONLY: 0,
 | 
			
		||||
    UNLOCK_OR_LOG_IN: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AuthPromptStatus = {
 | 
			
		||||
    NOT_VERIFYING: 0,
 | 
			
		||||
    VERIFYING: 1,
 | 
			
		||||
    VERIFICATION_FAILED: 2,
 | 
			
		||||
    VERIFICATION_SUCCEEDED: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const BeginRequestType = {
 | 
			
		||||
    PROVIDE_USERNAME: 0,
 | 
			
		||||
    DONT_PROVIDE_USERNAME: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AuthPrompt = new Lang.Class({
 | 
			
		||||
    Name: 'AuthPrompt',
 | 
			
		||||
 | 
			
		||||
    _init: function(gdmClient, mode) {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
 | 
			
		||||
        this._gdmClient = gdmClient;
 | 
			
		||||
        this._mode = mode;
 | 
			
		||||
 | 
			
		||||
        let reauthenticationOnly;
 | 
			
		||||
        if (this._mode == AuthPromptMode.UNLOCK_ONLY)
 | 
			
		||||
            reauthenticationOnly = true;
 | 
			
		||||
        else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
 | 
			
		||||
            reauthenticationOnly = false;
 | 
			
		||||
 | 
			
		||||
        this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
 | 
			
		||||
        this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
 | 
			
		||||
        this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
 | 
			
		||||
        this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
 | 
			
		||||
        this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
 | 
			
		||||
        this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
 | 
			
		||||
        this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
 | 
			
		||||
        this.smartcardDetected = this._userVerifier.smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        this.connect('next', Lang.bind(this, function() {
 | 
			
		||||
                         this.updateSensitivity(false);
 | 
			
		||||
                         this.startSpinning();
 | 
			
		||||
                         if (this._queryingService) {
 | 
			
		||||
                             this._userVerifier.answerQuery(this._queryingService, this._entry.text);
 | 
			
		||||
                         } else {
 | 
			
		||||
                             this._preemptiveAnswer = this._entry.text;
 | 
			
		||||
                         }
 | 
			
		||||
                     }));
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.actor.connect('key-press-event',
 | 
			
		||||
                           Lang.bind(this, function(actor, event) {
 | 
			
		||||
                               if (event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
                                   this.cancel();
 | 
			
		||||
                               }
 | 
			
		||||
                               return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                           }));
 | 
			
		||||
 | 
			
		||||
        this._userWell = new St.Bin({ x_fill: true,
 | 
			
		||||
                                      x_align: St.Align.START });
 | 
			
		||||
        this.actor.add(this._userWell,
 | 
			
		||||
                       { x_align: St.Align.START,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: true,
 | 
			
		||||
                         expand: true });
 | 
			
		||||
        this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._label,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: false,
 | 
			
		||||
                         y_fill: true,
 | 
			
		||||
                         x_align: St.Align.START });
 | 
			
		||||
        this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        ShellEntry.addContextMenu(this._entry, { isPassword: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._entry,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this._message = new St.Label({ opacity: 0,
 | 
			
		||||
                                       styleClass: 'login-dialog-message' });
 | 
			
		||||
        this._message.clutter_text.line_wrap = true;
 | 
			
		||||
        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',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
        this.actor.add(this._buttonBox,
 | 
			
		||||
                       { expand:  true,
 | 
			
		||||
                         x_align: St.Align.MIDDLE,
 | 
			
		||||
                         y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this._defaultButtonWellActor = null;
 | 
			
		||||
 | 
			
		||||
        this._initButtons();
 | 
			
		||||
 | 
			
		||||
        let spinnerIcon = global.datadir + '/theme/process-working.svg';
 | 
			
		||||
        this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
 | 
			
		||||
        this._spinner.actor.opacity = 0;
 | 
			
		||||
        this._spinner.actor.show();
 | 
			
		||||
        this._defaultButtonWell.add_child(this._spinner.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this._userVerifier.clear();
 | 
			
		||||
        this._userVerifier.disconnectAll();
 | 
			
		||||
        this._userVerifier = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initButtons: function() {
 | 
			
		||||
        this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                            button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            can_focus: true,
 | 
			
		||||
                                            label: _("Cancel") });
 | 
			
		||||
        this.cancelButton.connect('clicked',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       this.cancel();
 | 
			
		||||
                                   }));
 | 
			
		||||
        this._buttonBox.add(this.cancelButton,
 | 
			
		||||
                            { expand: false,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.START,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._buttonBox.add(this._defaultButtonWell,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                          button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                          reactive: true,
 | 
			
		||||
                                          can_focus: true,
 | 
			
		||||
                                          label: _("Next") });
 | 
			
		||||
        this.nextButton.connect('clicked',
 | 
			
		||||
                                 Lang.bind(this, function() {
 | 
			
		||||
                                     this.emit('next');
 | 
			
		||||
                                 }));
 | 
			
		||||
        this.nextButton.add_style_pseudo_class('default');
 | 
			
		||||
        this._buttonBox.add(this.nextButton,
 | 
			
		||||
                            { expand: false,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._updateNextButtonSensitivity(this._entry.text.length > 0);
 | 
			
		||||
 | 
			
		||||
        this._entry.clutter_text.connect('text-changed',
 | 
			
		||||
                                         Lang.bind(this, function() {
 | 
			
		||||
                                             if (!this._userVerifier.hasPendingMessages)
 | 
			
		||||
                                                 this._fadeOutMessage();
 | 
			
		||||
 | 
			
		||||
                                             this._updateNextButtonSensitivity(this._entry.text.length > 0);
 | 
			
		||||
                                         }));
 | 
			
		||||
        this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('next');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAskQuestion: function(verifier, serviceName, question, passwordChar) {
 | 
			
		||||
        if (this._preemptiveAnswer) {
 | 
			
		||||
            if (this._queryingService)
 | 
			
		||||
                this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
 | 
			
		||||
            this._preemptiveAnswer = null;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._queryingService)
 | 
			
		||||
            this.clear();
 | 
			
		||||
 | 
			
		||||
        this._queryingService = serviceName;
 | 
			
		||||
        this.setPasswordChar(passwordChar);
 | 
			
		||||
        this.setQuestion(question);
 | 
			
		||||
 | 
			
		||||
        if (passwordChar) {
 | 
			
		||||
            if (this._userVerifier.reauthenticating)
 | 
			
		||||
                this.nextButton.label = _("Unlock");
 | 
			
		||||
            else
 | 
			
		||||
                this.nextButton.label = C_("button", "Sign In");
 | 
			
		||||
        } else {
 | 
			
		||||
            this.nextButton.label = _("Next");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this.emit('prompted');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOVirtUserAuthenticated: function() {
 | 
			
		||||
        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSmartcardStatusChanged: function() {
 | 
			
		||||
        this.smartcardDetected = this._userVerifier.smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        // Most of the time we want to reset if the user inserts or removes
 | 
			
		||||
        // a smartcard. Smartcard insertion "preempts" what the user was
 | 
			
		||||
        // doing, and smartcard removal aborts the preemption.
 | 
			
		||||
        // The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
 | 
			
		||||
        //                        with a smartcard
 | 
			
		||||
        //                     2) Don't reset if we've already succeeded at verification and
 | 
			
		||||
        //                        the user is getting logged in.
 | 
			
		||||
        if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
 | 
			
		||||
            this.verificationStatus == AuthPromptStatus.VERIFYING &&
 | 
			
		||||
            this.smartcardDetected)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowMessage: function(userVerifier, message, type) {
 | 
			
		||||
        this.setMessage(message, type);
 | 
			
		||||
        this.emit('prompted');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationFailed: function() {
 | 
			
		||||
        this._queryingService = null;
 | 
			
		||||
        this.clear();
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this.setActorInDefaultButtonWell(null);
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationComplete: function() {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
        this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addActorToDefaultButtonWell: function(actor) {
 | 
			
		||||
        this._defaultButtonWell.add_child(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setActorInDefaultButtonWell: function(actor, animate) {
 | 
			
		||||
        if (!this._defaultButtonWellActor &&
 | 
			
		||||
            !actor)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldActor = this._defaultButtonWellActor;
 | 
			
		||||
 | 
			
		||||
        if (oldActor)
 | 
			
		||||
            Tweener.removeTweens(oldActor);
 | 
			
		||||
 | 
			
		||||
        let isSpinner;
 | 
			
		||||
        if (actor == this._spinner.actor)
 | 
			
		||||
            isSpinner = true;
 | 
			
		||||
        else
 | 
			
		||||
            isSpinner = false;
 | 
			
		||||
 | 
			
		||||
        if (this._defaultButtonWellActor != actor && oldActor) {
 | 
			
		||||
            if (!animate) {
 | 
			
		||||
                oldActor.opacity = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                Tweener.addTween(oldActor,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear',
 | 
			
		||||
                                   onCompleteScope: this,
 | 
			
		||||
                                   onComplete: function() {
 | 
			
		||||
                                      if (isSpinner) {
 | 
			
		||||
                                          if (this._spinner)
 | 
			
		||||
                                              this._spinner.stop();
 | 
			
		||||
                                      }
 | 
			
		||||
                                   }
 | 
			
		||||
                                 });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actor) {
 | 
			
		||||
            if (isSpinner)
 | 
			
		||||
                this._spinner.play();
 | 
			
		||||
 | 
			
		||||
            if (!animate)
 | 
			
		||||
                actor.opacity = 255;
 | 
			
		||||
            else
 | 
			
		||||
                Tweener.addTween(actor,
 | 
			
		||||
                                 { opacity: 255,
 | 
			
		||||
                                   time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWellActor = actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startSpinning: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(this._spinner.actor, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    stopSpinning: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(null, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        this._entry.text = '';
 | 
			
		||||
        this.stopSpinning();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setPasswordChar: function(passwordChar) {
 | 
			
		||||
        this._entry.clutter_text.set_password_char(passwordChar);
 | 
			
		||||
        this._entry.menu.isPassword = passwordChar != '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setQuestion: function(question) {
 | 
			
		||||
        this._label.set_text(question);
 | 
			
		||||
 | 
			
		||||
        this._label.show();
 | 
			
		||||
        this._entry.show();
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAnswer: function() {
 | 
			
		||||
        let text;
 | 
			
		||||
 | 
			
		||||
        if (this._preemptiveAnswer) {
 | 
			
		||||
            text = this._preemptiveAnswer;
 | 
			
		||||
            this._preemptiveAnswer = null;
 | 
			
		||||
        } else {
 | 
			
		||||
            text = this._entry.get_text();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return text;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOutMessage: function() {
 | 
			
		||||
        if (this._message.opacity == 0)
 | 
			
		||||
            return;
 | 
			
		||||
        Tweener.removeTweens(this._message);
 | 
			
		||||
        Tweener.addTween(this._message,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: MESSAGE_FADE_OUT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMessage: function(message, type) {
 | 
			
		||||
        if (type == GdmUtil.MessageType.ERROR)
 | 
			
		||||
            this._message.add_style_class_name('login-dialog-message-warning');
 | 
			
		||||
        else
 | 
			
		||||
            this._message.remove_style_class_name('login-dialog-message-warning');
 | 
			
		||||
 | 
			
		||||
        if (type == GdmUtil.MessageType.HINT)
 | 
			
		||||
            this._message.add_style_class_name('login-dialog-message-hint');
 | 
			
		||||
        else
 | 
			
		||||
            this._message.remove_style_class_name('login-dialog-message-hint');
 | 
			
		||||
 | 
			
		||||
        if (message) {
 | 
			
		||||
            Tweener.removeTweens(this._message);
 | 
			
		||||
            this._message.text = message;
 | 
			
		||||
            this._message.opacity = 255;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._message.opacity = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateNextButtonSensitivity: function(sensitive) {
 | 
			
		||||
        this.nextButton.reactive = sensitive;
 | 
			
		||||
        this.nextButton.can_focus = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSensitivity: function(sensitive) {
 | 
			
		||||
        this._updateNextButtonSensitivity(sensitive);
 | 
			
		||||
        this._entry.reactive = sensitive;
 | 
			
		||||
        this._entry.clutter_text.editable = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(null, true);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        this._message.opacity = 0;
 | 
			
		||||
 | 
			
		||||
        this.setUser(null);
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this._entry.set_text('');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setUser: function(user) {
 | 
			
		||||
        if (user) {
 | 
			
		||||
            let userWidget = new UserWidget.UserWidget(user);
 | 
			
		||||
            this._userWell.set_child(userWidget.actor);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._userWell.set_child(null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        let oldStatus = this.verificationStatus;
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
 | 
			
		||||
        if (oldStatus == AuthPromptStatus.VERIFYING)
 | 
			
		||||
            this._userVerifier.cancel();
 | 
			
		||||
 | 
			
		||||
        this._queryingService = null;
 | 
			
		||||
        this.clear();
 | 
			
		||||
        this._message.opacity = 0;
 | 
			
		||||
        this.setUser(null);
 | 
			
		||||
        this.stopSpinning();
 | 
			
		||||
 | 
			
		||||
        if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
 | 
			
		||||
            this.emit('failed');
 | 
			
		||||
 | 
			
		||||
        let beginRequestType;
 | 
			
		||||
 | 
			
		||||
        if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
 | 
			
		||||
            // The user is constant at the unlock screen, so it will immediately
 | 
			
		||||
            // respond to the request with the username
 | 
			
		||||
            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
 | 
			
		||||
        } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
 | 
			
		||||
                   this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
 | 
			
		||||
            // We don't need to know the username if the user preempted the login screen
 | 
			
		||||
            // with a smartcard or with preauthenticated oVirt credentials
 | 
			
		||||
            beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
 | 
			
		||||
        } else {
 | 
			
		||||
            // In all other cases, we should get the username up front.
 | 
			
		||||
            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('reset', beginRequestType);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        if (!this._entry.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
        this._entry.clutter_text.insert_unichar(unichar);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    begin: function(params) {
 | 
			
		||||
        params = Params.parse(params, { userName: null,
 | 
			
		||||
                                        hold: null });
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(false);
 | 
			
		||||
 | 
			
		||||
        let hold = params.hold;
 | 
			
		||||
        if (!hold)
 | 
			
		||||
            hold = new Batch.Hold();
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.begin(params.userName, hold);
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFYING;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    finish: function(onComplete) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            onComplete();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                  Lang.bind(this, function() {
 | 
			
		||||
                                                      this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                      onComplete();
 | 
			
		||||
                                                  }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this.emit('cancelled');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AuthPrompt.prototype);
 | 
			
		||||
@@ -13,9 +13,7 @@
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,13 @@ const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
 | 
			
		||||
<method name='GetDefaultDevice'>
 | 
			
		||||
    <arg type='o' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const FprintManagerIface = '<node> \
 | 
			
		||||
<interface name="net.reactivated.Fprint.Manager"> \
 | 
			
		||||
<method name="GetDefaultDevice"> \
 | 
			
		||||
    <arg type="o" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,7 @@
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
@@ -32,12 +30,13 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const AuthPrompt = imports.gdm.authPrompt;
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const CtrlAltTab = imports.ui.ctrlAltTab;
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Realmd = imports.gdm.realmd;
 | 
			
		||||
@@ -46,20 +45,9 @@ const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
const _FADE_ANIMATION_TIME = 0.25;
 | 
			
		||||
const _SCROLL_ANIMATION_TIME = 0.5;
 | 
			
		||||
const _DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
 | 
			
		||||
const _DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
 | 
			
		||||
const _DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
 | 
			
		||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
 | 
			
		||||
const _LOGO_ICON_HEIGHT = 48;
 | 
			
		||||
 | 
			
		||||
let _loginDialog = null;
 | 
			
		||||
 | 
			
		||||
const DefaultButtonWellMode = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    SESSION_MENU_BUTTON: 1,
 | 
			
		||||
    SPINNER: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const UserListItem = new Lang.Class({
 | 
			
		||||
    Name: 'UserListItem',
 | 
			
		||||
 | 
			
		||||
@@ -68,7 +56,7 @@ const UserListItem = new Lang.Class({
 | 
			
		||||
        this._userChangedId = this.user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._onUserChanged));
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
@@ -77,47 +65,21 @@ const UserListItem = new Lang.Class({
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._userAvatar = new UserWidget.Avatar(this.user,
 | 
			
		||||
                                                 { styleClass: 'login-dialog-user-list-item-icon' });
 | 
			
		||||
        layout.add(this._userAvatar.actor);
 | 
			
		||||
        let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
 | 
			
		||||
                                            vertical:    true });
 | 
			
		||||
        layout.add(textLayout, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
 | 
			
		||||
        this.actor.label_actor = this._nameLabel;
 | 
			
		||||
        textLayout.add(this._nameLabel,
 | 
			
		||||
                       { y_fill: false,
 | 
			
		||||
                         y_align: St.Align.MIDDLE,
 | 
			
		||||
                         expand: true });
 | 
			
		||||
        this._userWidget = new UserWidget.UserWidget(this.user);
 | 
			
		||||
        layout.add(this._userWidget.actor);
 | 
			
		||||
 | 
			
		||||
        this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
 | 
			
		||||
                                                 scale_x: 0 });
 | 
			
		||||
        textLayout.add(this._timedLoginIndicator,
 | 
			
		||||
                       { x_fill: true,
 | 
			
		||||
                         x_align: St.Align.MIDDLE,
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         y_align: St.Align.END });
 | 
			
		||||
        layout.add(this._timedLoginIndicator);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
        this._onUserChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserChanged: function() {
 | 
			
		||||
        this._nameLabel.set_text(this.user.get_real_name());
 | 
			
		||||
        this._userAvatar.update();
 | 
			
		||||
        this._updateLoggedIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    syncStyleClasses: function() {
 | 
			
		||||
        this._updateLoggedIn();
 | 
			
		||||
 | 
			
		||||
        if (global.stage.get_key_focus() == this.actor)
 | 
			
		||||
            this.actor.add_style_pseudo_class('focus');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('focus');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLoggedIn: function() {
 | 
			
		||||
        if (this.user.is_logged_in())
 | 
			
		||||
            this.actor.add_style_pseudo_class('logged-in');
 | 
			
		||||
@@ -203,7 +165,6 @@ const UserList = new Lang.Class({
 | 
			
		||||
        for (let userName in this._items) {
 | 
			
		||||
            let item = this._items[userName];
 | 
			
		||||
            item.actor.sync_hover();
 | 
			
		||||
            item.syncStyleClasses();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -323,10 +284,6 @@ const SessionMenuButton = new Lang.Class({
 | 
			
		||||
                                    this._button.remove_style_pseudo_class('active');
 | 
			
		||||
                           }));
 | 
			
		||||
 | 
			
		||||
        let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
 | 
			
		||||
                                                                   reactive: false });
 | 
			
		||||
        this._menu.addMenuItem(subtitle);
 | 
			
		||||
 | 
			
		||||
        this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
 | 
			
		||||
        this._manager.addMenu(this._menu);
 | 
			
		||||
 | 
			
		||||
@@ -402,6 +359,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(parentActor) {
 | 
			
		||||
        this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
 | 
			
		||||
                                     layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                     style_class: 'login-dialog',
 | 
			
		||||
                                     visible: false });
 | 
			
		||||
 | 
			
		||||
@@ -410,10 +368,10 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        parentActor.add_child(this.actor);
 | 
			
		||||
 | 
			
		||||
        this._userManager = AccountsService.UserManager.get_default()
 | 
			
		||||
        this._greeterClient = new Gdm.Client();
 | 
			
		||||
        let gdmClient = new Gdm.Client();
 | 
			
		||||
 | 
			
		||||
        if (GLib.getenv('GDM_GREETER_TEST') != '1') {
 | 
			
		||||
            this._greeter = this._greeterClient.get_greeter_sync(null);
 | 
			
		||||
            this._greeter = gdmClient.get_greeter_sync(null);
 | 
			
		||||
 | 
			
		||||
            this._greeter.connect('default-session-name-changed',
 | 
			
		||||
                                  Lang.bind(this, this._onDefaultSessionChanged));
 | 
			
		||||
@@ -424,15 +382,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, this._onTimedLoginRequested));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
 | 
			
		||||
        this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
 | 
			
		||||
        this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
 | 
			
		||||
        this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
 | 
			
		||||
        this._userVerifier.connect('reset', Lang.bind(this, this._reset));
 | 
			
		||||
        this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
 | 
			
		||||
        this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
 | 
			
		||||
        this._verifyingUser = false;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
 | 
			
		||||
@@ -449,10 +398,12 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                   Lang.bind(this, this._updateLogoTexture));
 | 
			
		||||
 | 
			
		||||
        this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
 | 
			
		||||
                                                    vertical: true });
 | 
			
		||||
        this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
 | 
			
		||||
                                                                            align_axis: Clutter.AlignAxis.BOTH,
 | 
			
		||||
                                                                            factor: 0.5 }));
 | 
			
		||||
                                                    x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                                    y_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                                    x_expand: true,
 | 
			
		||||
                                                    y_expand: true,
 | 
			
		||||
                                                    vertical: true,
 | 
			
		||||
                                                    visible: false });
 | 
			
		||||
        this.actor.add_child(this._userSelectionBox);
 | 
			
		||||
 | 
			
		||||
        this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
 | 
			
		||||
@@ -466,61 +417,11 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._promptBox.connect('button-press-event',
 | 
			
		||||
                                 Lang.bind(this, function(actor, event) {
 | 
			
		||||
                                    if (event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
                                        this.cancel();
 | 
			
		||||
                                    }
 | 
			
		||||
                                 }));
 | 
			
		||||
 | 
			
		||||
        this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
 | 
			
		||||
                                                                     align_axis: Clutter.AlignAxis.BOTH,
 | 
			
		||||
                                                                     factor: 0.5 }));
 | 
			
		||||
        this.actor.add_child(this._promptBox);
 | 
			
		||||
        this._promptUser = new St.Bin({ x_fill: true,
 | 
			
		||||
                                        x_align: St.Align.START });
 | 
			
		||||
        this._promptBox.add(this._promptUser,
 | 
			
		||||
                            { x_align: St.Align.START,
 | 
			
		||||
                              x_fill: true,
 | 
			
		||||
                              y_fill: true,
 | 
			
		||||
                              expand: true });
 | 
			
		||||
        this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
 | 
			
		||||
 | 
			
		||||
        this._promptBox.add(this._promptLabel,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: true,
 | 
			
		||||
                              y_fill: true,
 | 
			
		||||
                              x_align: St.Align.START });
 | 
			
		||||
        this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._promptEntryTextChangedId = 0;
 | 
			
		||||
        this._promptEntryActivateId = 0;
 | 
			
		||||
        this._promptBox.add(this._promptEntry,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: true,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._promptMessage = new St.Label({ visible: false });
 | 
			
		||||
        this._promptBox.add(this._promptMessage, { x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
        this._promptBox.add(this._promptLoginHint);
 | 
			
		||||
 | 
			
		||||
        this._buttonBox = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
        this._promptBox.add(this._buttonBox,
 | 
			
		||||
                            { expand:  true,
 | 
			
		||||
                              x_align: St.Align.MIDDLE,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
        this._cancelButton = null;
 | 
			
		||||
        this._signInButton = null;
 | 
			
		||||
 | 
			
		||||
        this._promptBox.hide();
 | 
			
		||||
        this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
 | 
			
		||||
        this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
 | 
			
		||||
        this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
 | 
			
		||||
        this._authPrompt.hide();
 | 
			
		||||
        this.actor.add_child(this._authPrompt.actor);
 | 
			
		||||
 | 
			
		||||
        // translators: this message is shown below the user list on the
 | 
			
		||||
        // login screen. It can be activated to reveal an entry for
 | 
			
		||||
@@ -535,7 +436,8 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                                x_align: St.Align.START,
 | 
			
		||||
                                                x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
 | 
			
		||||
        this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAskForUsernameAndBeginVerification));
 | 
			
		||||
 | 
			
		||||
        this._notListedButton.hide();
 | 
			
		||||
 | 
			
		||||
        this._userSelectionBox.add(this._notListedButton,
 | 
			
		||||
@@ -543,36 +445,19 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
 | 
			
		||||
        this._logoBin.set_y_align(Clutter.ActorAlign.END);
 | 
			
		||||
        this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
 | 
			
		||||
                                                                   align_axis: Clutter.AlignAxis.X_AXIS,
 | 
			
		||||
                                                                   factor: 0.5 }));
 | 
			
		||||
        this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
 | 
			
		||||
                                                                   align_axis: Clutter.AlignAxis.Y_AXIS,
 | 
			
		||||
                                                                   factor: 1.0 }));
 | 
			
		||||
        this._logoBin = new St.Widget({ style_class: 'login-dialog-logo-bin',
 | 
			
		||||
                                        x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                        y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                        x_expand: true,
 | 
			
		||||
                                        y_expand: true });
 | 
			
		||||
        this.actor.add_child(this._logoBin);
 | 
			
		||||
        this._updateLogo();
 | 
			
		||||
 | 
			
		||||
        if (!this._userManager.is_loaded)
 | 
			
		||||
            this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
 | 
			
		||||
                                                                  Lang.bind(this, function() {
 | 
			
		||||
                                                                      if (this._userManager.is_loaded) {
 | 
			
		||||
                                                                          this._loadUserList();
 | 
			
		||||
                                                                          this._userManager.disconnect(this._userManagerLoadedId);
 | 
			
		||||
                                                                          this._userManagerLoadedId = 0;
 | 
			
		||||
                                                                      }
 | 
			
		||||
                                                                  }));
 | 
			
		||||
        else
 | 
			
		||||
            this._loadUserList();
 | 
			
		||||
 | 
			
		||||
        this._userList.connect('activate',
 | 
			
		||||
                               Lang.bind(this, function(userList, item) {
 | 
			
		||||
                                   this._onUserListActivated(item);
 | 
			
		||||
                               }));
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWell = new St.Widget();
 | 
			
		||||
        this._defaultButtonWellMode = DefaultButtonWellMode.NONE;
 | 
			
		||||
 | 
			
		||||
        this._sessionMenuButton = new SessionMenuButton();
 | 
			
		||||
        this._sessionMenuButton.connect('session-activated',
 | 
			
		||||
@@ -581,34 +466,60 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                            }));
 | 
			
		||||
        this._sessionMenuButton.actor.opacity = 0;
 | 
			
		||||
        this._sessionMenuButton.actor.show();
 | 
			
		||||
        this._defaultButtonWell.add_child(this._sessionMenuButton.actor);
 | 
			
		||||
        this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
 | 
			
		||||
 | 
			
		||||
        let spinnerIcon = global.datadir + '/theme/process-working.svg';
 | 
			
		||||
        this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, _DEFAULT_BUTTON_WELL_ICON_SIZE);
 | 
			
		||||
        this._workSpinner.actor.opacity = 0;
 | 
			
		||||
        this._workSpinner.actor.show();
 | 
			
		||||
        this._disableUserList = undefined;
 | 
			
		||||
        this._userListLoaded = false;
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWell.add_child(this._workSpinner.actor);
 | 
			
		||||
        this._sessionMenuButton.actor.add_constraint(new Clutter.AlignConstraint({ source: this._workSpinner.actor,
 | 
			
		||||
                                                                                   align_axis: Clutter.AlignAxis.BOTH,
 | 
			
		||||
                                                                                   factor: 0.5 }));
 | 
			
		||||
   },
 | 
			
		||||
        LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
 | 
			
		||||
 | 
			
		||||
        // If the user list is enabled, it should take key focus; make sure the
 | 
			
		||||
        // screen shield is initialized first to prevent it from stealing the
 | 
			
		||||
        // focus later
 | 
			
		||||
        Main.layoutManager.connect('startup-complete',
 | 
			
		||||
                                   Lang.bind(this, this._updateDisableUserList));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureUserListLoaded: function() {
 | 
			
		||||
        if (!this._userManager.is_loaded) {
 | 
			
		||||
            this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
 | 
			
		||||
                                                                  Lang.bind(this, function() {
 | 
			
		||||
                                                                      if (this._userManager.is_loaded) {
 | 
			
		||||
                                                                          this._loadUserList();
 | 
			
		||||
                                                                          this._userManager.disconnect(this._userManagerLoadedId);
 | 
			
		||||
                                                                          this._userManagerLoadedId = 0;
 | 
			
		||||
                                                                      }
 | 
			
		||||
                                                                  }));
 | 
			
		||||
        } else {
 | 
			
		||||
            let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
 | 
			
		||||
            GLib.Source.set_name_by_id(id, '[gnome-shell] _loadUserList');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDisableUserList: function() {
 | 
			
		||||
        let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
 | 
			
		||||
 | 
			
		||||
        // If this is the first time around, set initial focus
 | 
			
		||||
        if (this._disableUserList == undefined && disableUserList)
 | 
			
		||||
            this.setInitialKeyFocus(this._promptEntry);
 | 
			
		||||
 | 
			
		||||
        if (disableUserList != this._disableUserList) {
 | 
			
		||||
            this._disableUserList = disableUserList;
 | 
			
		||||
 | 
			
		||||
            if (!this._verifyingUser)
 | 
			
		||||
                this._reset();
 | 
			
		||||
            if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
 | 
			
		||||
                this._authPrompt.reset();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCancelButton: function() {
 | 
			
		||||
        let cancelVisible;
 | 
			
		||||
 | 
			
		||||
        // Hide the cancel button if the user list is disabled and we're asking for
 | 
			
		||||
        // a username
 | 
			
		||||
        if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING && this._disableUserList)
 | 
			
		||||
            cancelVisible = false;
 | 
			
		||||
        else
 | 
			
		||||
            cancelVisible = true;
 | 
			
		||||
 | 
			
		||||
        this._authPrompt.cancelButton.visible = cancelVisible;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBanner: function() {
 | 
			
		||||
        let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
 | 
			
		||||
        let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
 | 
			
		||||
@@ -625,11 +536,13 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        if (this._logoFileUri != uri)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let icon = null;
 | 
			
		||||
        if (this._logoFileUri)
 | 
			
		||||
            icon = this._textureCache.load_uri_async(this._logoFileUri,
 | 
			
		||||
                                                     -1, _LOGO_ICON_HEIGHT);
 | 
			
		||||
        this._logoBin.set_child(icon);
 | 
			
		||||
        this._logoBin.destroy_all_children();
 | 
			
		||||
        if (this._logoFileUri) {
 | 
			
		||||
            let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
            this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri,
 | 
			
		||||
                                                                      -1, _LOGO_ICON_HEIGHT,
 | 
			
		||||
                                                                      scaleFactor));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLogo: function() {
 | 
			
		||||
@@ -639,286 +552,53 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._updateLogoTexture(this._textureCache, this._logoFileUri);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
        this._userVerifier.clear();
 | 
			
		||||
    _onPrompted: function() {
 | 
			
		||||
        this._sessionMenuButton.updateSensitivity(true);
 | 
			
		||||
 | 
			
		||||
        if (this._shouldShowSessionMenuButton())
 | 
			
		||||
            this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
 | 
			
		||||
        this._showPrompt();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function(authPrompt, beginRequest) {
 | 
			
		||||
        this._sessionMenuButton.updateSensitivity(true);
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptMessage.hide();
 | 
			
		||||
        this._user = null;
 | 
			
		||||
        this._verifyingUser = false;
 | 
			
		||||
 | 
			
		||||
        if (this._disableUserList)
 | 
			
		||||
            this._hideUserListAndLogIn();
 | 
			
		||||
        else
 | 
			
		||||
            this._showUserList();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getActorForDefaultButtonWellMode: function(mode) {
 | 
			
		||||
        let actor;
 | 
			
		||||
 | 
			
		||||
        if (mode == DefaultButtonWellMode.NONE)
 | 
			
		||||
            actor = null;
 | 
			
		||||
        else if (mode == DefaultButtonWellMode.SPINNER)
 | 
			
		||||
            actor = this._workSpinner.actor;
 | 
			
		||||
        else if (mode == DefaultButtonWellMode.SESSION_MENU_BUTTON)
 | 
			
		||||
            actor = this._sessionMenuButton.actor;
 | 
			
		||||
 | 
			
		||||
        return actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setDefaultButtonWellMode: function(mode, immediately) {
 | 
			
		||||
        if (this._defaultButtonWellMode == DefaultButtonWellMode.NONE &&
 | 
			
		||||
            mode == DefaultButtonWellMode.NONE)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldActor = this._getActorForDefaultButtonWellMode(this._defaultButtonWellMode);
 | 
			
		||||
 | 
			
		||||
        if (oldActor)
 | 
			
		||||
            Tweener.removeTweens(oldActor);
 | 
			
		||||
 | 
			
		||||
        let actor = this._getActorForDefaultButtonWellMode(mode);
 | 
			
		||||
 | 
			
		||||
        if (this._defaultButtonWellMode != mode && oldActor) {
 | 
			
		||||
            if (immediately)
 | 
			
		||||
                oldActor.opacity = 0;
 | 
			
		||||
        if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
 | 
			
		||||
            if (!this._disableUserList)
 | 
			
		||||
                this._showUserList();
 | 
			
		||||
            else
 | 
			
		||||
                Tweener.addTween(oldActor,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: _DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: _DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear',
 | 
			
		||||
                                   onCompleteScope: this,
 | 
			
		||||
                                   onComplete: function() {
 | 
			
		||||
                                       if (mode == DefaultButtonWellMode.SPINNER) {
 | 
			
		||||
                                           if (this._workSpinner)
 | 
			
		||||
                                               this._workSpinner.stop();
 | 
			
		||||
                                       }
 | 
			
		||||
                                   }
 | 
			
		||||
                                 });
 | 
			
		||||
 | 
			
		||||
                this._hideUserListAskForUsernameAndBeginVerification();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._hideUserListAndBeginVerification();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actor) {
 | 
			
		||||
            if (mode == DefaultButtonWellMode.SPINNER)
 | 
			
		||||
                this._workSpinner.play();
 | 
			
		||||
 | 
			
		||||
            if (immediately)
 | 
			
		||||
                actor.opacity = 255;
 | 
			
		||||
            else
 | 
			
		||||
                Tweener.addTween(actor,
 | 
			
		||||
                                 { opacity: 255,
 | 
			
		||||
                                   time: _DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: _DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWellMode = mode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _verificationFailed: function() {
 | 
			
		||||
        this._promptEntry.text = '';
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDefaultSessionChanged: function(client, sessionId) {
 | 
			
		||||
        this._sessionMenuButton.setActiveSession(sessionId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showMessage: function(userVerifier, message, styleClass) {
 | 
			
		||||
        if (message) {
 | 
			
		||||
            this._promptMessage.text = message;
 | 
			
		||||
            this._promptMessage.styleClass = styleClass;
 | 
			
		||||
            this._promptMessage.show();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._promptMessage.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showLoginHint: function(verifier, message) {
 | 
			
		||||
        this._promptLoginHint.set_text(message)
 | 
			
		||||
        this._promptLoginHint.show();
 | 
			
		||||
        this._promptLoginHint.opacity = 255;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideLoginHint: function() {
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
        this._promptLoginHint.set_text('');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        if (this._verifyingUser)
 | 
			
		||||
            this._userVerifier.cancel();
 | 
			
		||||
        else
 | 
			
		||||
            this._reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldShowSessionMenuButton: function() {
 | 
			
		||||
        if (this._verifyingUser)
 | 
			
		||||
          return true;
 | 
			
		||||
 | 
			
		||||
        if (!this._user)
 | 
			
		||||
        if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFYING &&
 | 
			
		||||
            this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_FAILED)
 | 
			
		||||
          return false;
 | 
			
		||||
 | 
			
		||||
        if (this._user.is_logged_in)
 | 
			
		||||
        if (this._user && this._user.is_loaded && this._user.is_logged_in())
 | 
			
		||||
          return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showPrompt: function(forSecret) {
 | 
			
		||||
        this._promptLabel.show();
 | 
			
		||||
        this._promptEntry.show();
 | 
			
		||||
        this._promptLoginHint.opacity = 0;
 | 
			
		||||
        this._promptLoginHint.show();
 | 
			
		||||
        this._promptBox.opacity = 0;
 | 
			
		||||
        this._promptBox.show();
 | 
			
		||||
        Tweener.addTween(this._promptBox,
 | 
			
		||||
    _showPrompt: function() {
 | 
			
		||||
        if (this._authPrompt.actor.visible)
 | 
			
		||||
            return;
 | 
			
		||||
        this._authPrompt.actor.opacity = 0;
 | 
			
		||||
        this._authPrompt.actor.show();
 | 
			
		||||
        Tweener.addTween(this._authPrompt.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: _FADE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad' });
 | 
			
		||||
 | 
			
		||||
        if (this._shouldShowSessionMenuButton())
 | 
			
		||||
            this._setDefaultButtonWellMode(DefaultButtonWellMode.SESSION_MENU_BUTTON, true);
 | 
			
		||||
        else
 | 
			
		||||
            this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
 | 
			
		||||
 | 
			
		||||
        this._promptEntry.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        let hold = new Batch.Hold();
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         this._prepareDialog(forSecret, hold);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     hold];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConcurrentBatch(this, tasks);
 | 
			
		||||
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _prepareDialog: function(forSecret, hold) {
 | 
			
		||||
        this._buttonBox.visible = true;
 | 
			
		||||
        this._buttonBox.remove_all_children();
 | 
			
		||||
 | 
			
		||||
        if (!this._disableUserList || this._verifyingUser) {
 | 
			
		||||
            this._cancelButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                                 button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                                 reactive: true,
 | 
			
		||||
                                                 can_focus: true,
 | 
			
		||||
                                                 label: _("Cancel") });
 | 
			
		||||
            this._cancelButton.connect('clicked',
 | 
			
		||||
                                       Lang.bind(this, function() {
 | 
			
		||||
                                           this.cancel();
 | 
			
		||||
                                       }));
 | 
			
		||||
            this._buttonBox.add(this._cancelButton,
 | 
			
		||||
                                { expand: false,
 | 
			
		||||
                                  x_fill: false,
 | 
			
		||||
                                  y_fill: false,
 | 
			
		||||
                                  x_align: St.Align.START,
 | 
			
		||||
                                  y_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._buttonBox.add(this._defaultButtonWell,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._signInButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                             reactive: true,
 | 
			
		||||
                                             can_focus: true,
 | 
			
		||||
                                             label: forSecret ? C_("button", "Sign In") : _("Next") });
 | 
			
		||||
        this._signInButton.connect('clicked',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       hold.release();
 | 
			
		||||
                                   }));
 | 
			
		||||
        this._signInButton.add_style_pseudo_class('default');
 | 
			
		||||
        this._buttonBox.add(this._signInButton,
 | 
			
		||||
                            { expand: false,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
 | 
			
		||||
 | 
			
		||||
        this._promptEntryTextChangedId =
 | 
			
		||||
            this._promptEntry.clutter_text.connect('text-changed',
 | 
			
		||||
                                                    Lang.bind(this, function() {
 | 
			
		||||
                                                        this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
 | 
			
		||||
                                                    }));
 | 
			
		||||
 | 
			
		||||
        this._promptEntryActivateId =
 | 
			
		||||
            this._promptEntry.clutter_text.connect('activate', function() {
 | 
			
		||||
                hold.release();
 | 
			
		||||
            });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function(sensitive) {
 | 
			
		||||
        this._promptEntry.reactive = sensitive;
 | 
			
		||||
        this._promptEntry.clutter_text.editable = sensitive;
 | 
			
		||||
        this._sessionMenuButton.updateSensitivity(sensitive);
 | 
			
		||||
        this._updateSignInButtonSensitivity(sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSignInButtonSensitivity: function(sensitive) {
 | 
			
		||||
        if (this._signInButton) {
 | 
			
		||||
            this._signInButton.reactive = sensitive;
 | 
			
		||||
            this._signInButton.can_focus = sensitive;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hidePrompt: function() {
 | 
			
		||||
        if (this._promptEntryTextChangedId > 0) {
 | 
			
		||||
            this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
 | 
			
		||||
            this._promptEntryTextChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._promptEntryActivateId > 0) {
 | 
			
		||||
            this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
 | 
			
		||||
            this._promptEntryActivateId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._setDefaultButtonWellMode(DefaultButtonWellMode.NONE, true);
 | 
			
		||||
        this._promptBox.hide();
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
 | 
			
		||||
        this._promptUser.set_child(null);
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
 | 
			
		||||
        this._sessionMenuButton.close();
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
 | 
			
		||||
        this._buttonBox.remove_all_children();
 | 
			
		||||
        this._signInButton = null;
 | 
			
		||||
        this._cancelButton = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _askQuestion: function(verifier, serviceName, question, passwordChar) {
 | 
			
		||||
        this._promptLabel.set_text(question);
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char(passwordChar);
 | 
			
		||||
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return this._showPrompt(!!passwordChar);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         let text = this._promptEntry.get_text();
 | 
			
		||||
                         this._updateSensitivity(false);
 | 
			
		||||
                         this._setDefaultButtonWellMode(DefaultButtonWellMode.SPINNER, false);
 | 
			
		||||
                         this._userVerifier.answerQuery(serviceName, text);
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showRealmLoginHint: function(realmManager, hint) {
 | 
			
		||||
@@ -931,34 +611,64 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Translators: this message is shown below the username entry field
 | 
			
		||||
        // to clue the user in on how to login to the local network realm
 | 
			
		||||
        this._showLoginHint(null, _("(e.g., user or %s)").format(hint));
 | 
			
		||||
        this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _askForUsernameAndLogIn: function() {
 | 
			
		||||
        this._promptLabel.set_text(_("Username: "));
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char('');
 | 
			
		||||
    _askForUsernameAndBeginVerification: function() {
 | 
			
		||||
        this._authPrompt.setPasswordChar('');
 | 
			
		||||
        this._authPrompt.setQuestion(_("Username: "));
 | 
			
		||||
 | 
			
		||||
        let realmManager = new Realmd.Manager();
 | 
			
		||||
        let signalId = realmManager.connect('login-format-changed',
 | 
			
		||||
	                                        Lang.bind(this, this._showRealmLoginHint));
 | 
			
		||||
        let realmSignalId = realmManager.connect('login-format-changed',
 | 
			
		||||
                                                 Lang.bind(this, this._showRealmLoginHint));
 | 
			
		||||
        this._showRealmLoginHint(realmManager.loginFormat);
 | 
			
		||||
 | 
			
		||||
        let tasks = [this._showPrompt,
 | 
			
		||||
        let nextSignalId = this._authPrompt.connect('next',
 | 
			
		||||
                                                    Lang.bind(this, function() {
 | 
			
		||||
                                                        this._authPrompt.disconnect(nextSignalId);
 | 
			
		||||
                                                        this._authPrompt.updateSensitivity(false);
 | 
			
		||||
                                                        let answer = this._authPrompt.getAnswer();
 | 
			
		||||
                                                        this._user = this._userManager.get_user(answer);
 | 
			
		||||
                                                        this._authPrompt.clear();
 | 
			
		||||
                                                        this._authPrompt.startSpinning();
 | 
			
		||||
                                                        this._authPrompt.begin({ userName: answer });
 | 
			
		||||
                                                        this._updateCancelButton();
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         let userName = this._promptEntry.get_text();
 | 
			
		||||
                         this._promptEntry.reactive = false;
 | 
			
		||||
                         return this._beginVerificationForUser(userName);
 | 
			
		||||
                     },
 | 
			
		||||
                                                        realmManager.disconnect(realmSignalId)
 | 
			
		||||
                                                        realmManager.release();
 | 
			
		||||
                                                    }));
 | 
			
		||||
        this._updateCancelButton();
 | 
			
		||||
        this._showPrompt();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         realmManager.disconnect(signalId)
 | 
			
		||||
                         realmManager.release();
 | 
			
		||||
                     }];
 | 
			
		||||
    _loginScreenSessionActivated: function() {
 | 
			
		||||
        if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: _FADE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onUpdate: function() {
 | 
			
		||||
                               let children = Main.layoutManager.uiGroup.get_children();
 | 
			
		||||
 | 
			
		||||
                               for (let i = 0; i < children.length; i++) {
 | 
			
		||||
                                   if (children[i] != Main.layoutManager.screenShieldGroup)
 | 
			
		||||
                                       children[i].opacity = this.actor.opacity;
 | 
			
		||||
                               }
 | 
			
		||||
                           },
 | 
			
		||||
                           onUpdateScope: this,
 | 
			
		||||
                           onComplete: function() {
 | 
			
		||||
                               this._authPrompt.reset();
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _gotGreeterSessionProxy: function(proxy) {
 | 
			
		||||
        proxy.connect('g-properties-changed', Lang.bind(this, function() {
 | 
			
		||||
            if (proxy.Active)
 | 
			
		||||
                this._loginScreenSessionActivated();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startSession: function(serviceName) {
 | 
			
		||||
@@ -976,24 +686,19 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                           },
 | 
			
		||||
                           onUpdateScope: this,
 | 
			
		||||
                           onComplete: function() {
 | 
			
		||||
                               Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
                               let id = Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
                                   this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
 | 
			
		||||
                                   return false;
 | 
			
		||||
                                   return GLib.SOURCE_REMOVE;
 | 
			
		||||
                               }));
 | 
			
		||||
                               GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync');
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionOpened: function(client, serviceName) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
        this._authPrompt.finish(Lang.bind(this, function() {
 | 
			
		||||
            this._startSession(serviceName);
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                          this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                          this._startSession(serviceName);
 | 
			
		||||
                                                      }));
 | 
			
		||||
        }
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _waitForItemForUser: function(userName) {
 | 
			
		||||
@@ -1039,7 +744,9 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                                                     function() {
 | 
			
		||||
                                                                         this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
 | 
			
		||||
                                                                         hold.release();
 | 
			
		||||
                                                                         return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                                     });
 | 
			
		||||
        GLib.Source.set_name_by_id(this._timedLoginIdleTimeOutId, '[gnome-shell] this._timedLoginAnimationTime');
 | 
			
		||||
        return hold;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1103,7 +810,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        global.stage.connect('captured-event',
 | 
			
		||||
                             Lang.bind(this, function(actor, event) {
 | 
			
		||||
                                if (this._timedLoginDelay == undefined)
 | 
			
		||||
                                    return false;
 | 
			
		||||
                                    return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
                                if (event.type() == Clutter.EventType.KEY_PRESS ||
 | 
			
		||||
                                    event.type() == Clutter.EventType.BUTTON_PRESS) {
 | 
			
		||||
@@ -1116,7 +823,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                    this._resetTimedLogin();
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                return false;
 | 
			
		||||
                                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1125,39 +832,42 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._userSelectionBox.visible = expanded;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideUserListAndLogIn: function() {
 | 
			
		||||
    _hideUserList: function() {
 | 
			
		||||
        this._setUserListExpanded(false);
 | 
			
		||||
        GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
 | 
			
		||||
        this._askForUsernameAndLogIn();
 | 
			
		||||
        if (this._userSelectionBox.visible)
 | 
			
		||||
            GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideUserListAskForUsernameAndBeginVerification: function() {
 | 
			
		||||
        this._hideUserList();
 | 
			
		||||
        this._askForUsernameAndBeginVerification();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideUserListAndBeginVerification: function() {
 | 
			
		||||
        this._hideUserList();
 | 
			
		||||
        this._authPrompt.begin();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showUserList: function() {
 | 
			
		||||
        this._hidePrompt();
 | 
			
		||||
        this._ensureUserListLoaded();
 | 
			
		||||
        this._authPrompt.hide();
 | 
			
		||||
        this._sessionMenuButton.close();
 | 
			
		||||
        this._setUserListExpanded(true);
 | 
			
		||||
        this._notListedButton.show();
 | 
			
		||||
        this._userList.actor.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerificationForUser: function(userName) {
 | 
			
		||||
    _beginVerificationForItem: function(item) {
 | 
			
		||||
        this._authPrompt.setUser(item.user);
 | 
			
		||||
 | 
			
		||||
        let userName = item.user.get_user_name();
 | 
			
		||||
        let hold = new Batch.Hold();
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.begin(userName, hold);
 | 
			
		||||
        this._verifyingUser = true;
 | 
			
		||||
        this._authPrompt.begin({ userName: userName,
 | 
			
		||||
                                 hold: hold });
 | 
			
		||||
        return hold;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerificationForItem: function(item) {
 | 
			
		||||
        let userWidget = new UserWidget.UserWidget(item.user);
 | 
			
		||||
        this._promptUser.set_child(userWidget.actor);
 | 
			
		||||
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         let userName = item.user.get_user_name();
 | 
			
		||||
                         return this._beginVerificationForUser(userName);
 | 
			
		||||
                     }];
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserListActivated: function(activatedItem) {
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
 | 
			
		||||
@@ -1168,6 +878,8 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._user = activatedItem.user;
 | 
			
		||||
 | 
			
		||||
        this._updateCancelButton();
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
 | 
			
		||||
                                                     this._beginVerificationForItem(activatedItem)]);
 | 
			
		||||
        batch.run();
 | 
			
		||||
@@ -1181,14 +893,17 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadUserList: function() {
 | 
			
		||||
        if (this._userListLoaded)
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
 | 
			
		||||
        this._userListLoaded = true;
 | 
			
		||||
 | 
			
		||||
        let users = this._userManager.list_users();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < users.length; i++) {
 | 
			
		||||
            this._userList.addUser(users[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateDisableUserList();
 | 
			
		||||
 | 
			
		||||
        this._userManager.connect('user-added',
 | 
			
		||||
                                  Lang.bind(this, function(userManager, user) {
 | 
			
		||||
                                      this._userList.addUser(user);
 | 
			
		||||
@@ -1198,6 +913,8 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, function(userManager, user) {
 | 
			
		||||
                                      this._userList.removeUser(user);
 | 
			
		||||
                                  }));
 | 
			
		||||
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
@@ -1207,6 +924,12 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                        { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
 | 
			
		||||
        this._userList.actor.grab_key_focus();
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: 1,
 | 
			
		||||
                           transition: 'easeInQuad' });
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1215,8 +938,16 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._authPrompt.cancel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        this._promptEntry.clutter_text.insert_unichar(unichar);
 | 
			
		||||
        this._authPrompt.addCharacter(unichar);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    finish: function(onComplete) {
 | 
			
		||||
        this._authPrompt.finish(onComplete);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LoginDialog.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								js/gdm/oVirt.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,64 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsIface = '<node> \
 | 
			
		||||
<interface name="org.ovirt.vdsm.Credentials"> \
 | 
			
		||||
<signal name="UserAuthenticated"> \
 | 
			
		||||
    <arg type="s" name="token"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
 | 
			
		||||
 | 
			
		||||
let _oVirtCredentialsManager = null;
 | 
			
		||||
 | 
			
		||||
function OVirtCredentials() {
 | 
			
		||||
    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
 | 
			
		||||
                                   g_interface_name: OVirtCredentialsInfo.name,
 | 
			
		||||
                                   g_interface_info: OVirtCredentialsInfo,
 | 
			
		||||
                                   g_name: 'org.ovirt.vdsm.Credentials',
 | 
			
		||||
                                   g_object_path: '/org/ovirt/vdsm/Credentials',
 | 
			
		||||
                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsManager = new Lang.Class({
 | 
			
		||||
    Name: 'OVirtCredentialsManager',
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._token = null;
 | 
			
		||||
 | 
			
		||||
        this._credentials = new OVirtCredentials();
 | 
			
		||||
        this._credentials.connectSignal('UserAuthenticated',
 | 
			
		||||
                                        Lang.bind(this, this._onUserAuthenticated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserAuthenticated: function(proxy, sender, [token]) {
 | 
			
		||||
        this._token = token;
 | 
			
		||||
        this.emit('user-authenticated', token);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasToken: function() {
 | 
			
		||||
        return this._token != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getToken: function() {
 | 
			
		||||
        return this._token;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    resetToken: function() {
 | 
			
		||||
        this._token = null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
 | 
			
		||||
 | 
			
		||||
function getOVirtCredentialsManager() {
 | 
			
		||||
    if (!_oVirtCredentialsManager)
 | 
			
		||||
        _oVirtCredentialsManager = new OVirtCredentialsManager();
 | 
			
		||||
 | 
			
		||||
    return _oVirtCredentialsManager;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,129 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Red Hat, Inc
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const PowerMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'PowerMenuButton',
 | 
			
		||||
    Extends: PanelMenu.SystemStatusButton,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        /* Translators: accessible name of the power menu in the login screen */
 | 
			
		||||
        this.parent('system-shutdown-symbolic', _("Power"));
 | 
			
		||||
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::disable-restart-buttons',
 | 
			
		||||
                               Lang.bind(this, this._updateVisibility));
 | 
			
		||||
 | 
			
		||||
        this._createSubMenu();
 | 
			
		||||
 | 
			
		||||
        // ConsoleKit doesn't send notifications when shutdown/reboot
 | 
			
		||||
        // are disabled, so we update the menu item each time the menu opens
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this,
 | 
			
		||||
            function(menu, open) {
 | 
			
		||||
                if (open) {
 | 
			
		||||
                    this._updateHaveShutdown();
 | 
			
		||||
                    this._updateHaveRestart();
 | 
			
		||||
                    this._updateHaveSuspend();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        this._updateHaveShutdown();
 | 
			
		||||
        this._updateHaveRestart();
 | 
			
		||||
        this._updateHaveSuspend();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
 | 
			
		||||
        this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveShutdown: function() {
 | 
			
		||||
        this._loginManager.canPowerOff(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveShutdown = result;
 | 
			
		||||
            this._powerOffItem.actor.visible = this._haveShutdown;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveRestart: function() {
 | 
			
		||||
        this._loginManager.canReboot(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveRestart = result;
 | 
			
		||||
            this._restartItem.actor.visible = this._haveRestart;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveSuspend: function() {
 | 
			
		||||
        this._loginManager.canSuspend(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveSuspend = result;
 | 
			
		||||
            this._suspendItem.actor.visible = this._haveSuspend;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSubMenu: function() {
 | 
			
		||||
        let item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Suspend"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivateSuspend));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._suspendItem = item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Restart"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivateRestart));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._restartItem = item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Power Off"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._powerOffItem = item;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateSuspend: function() {
 | 
			
		||||
        if (!this._haveSuspend)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.suspend();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateRestart: function() {
 | 
			
		||||
        if (!this._haveRestart)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.reboot();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivatePowerOff: function() {
 | 
			
		||||
        if (!this._haveShutdown)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.powerOff();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -5,52 +5,58 @@ const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
 | 
			
		||||
    <property name="Name" type="s" access="read"/>
 | 
			
		||||
    <property name="Version" type="s" access="read"/>
 | 
			
		||||
    <property name="Realms" type="ao" access="read"/>
 | 
			
		||||
    <method name="Discover">
 | 
			
		||||
        <arg name="string" type="s" direction="in"/>
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/>
 | 
			
		||||
        <arg name="relevance" type="i" direction="out"/>
 | 
			
		||||
        <arg name="realm" type="ao" direction="out"/>
 | 
			
		||||
    </method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ProviderIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.realmd.Provider"> \
 | 
			
		||||
    <property name="Name" type="s" access="read"/> \
 | 
			
		||||
    <property name="Version" type="s" access="read"/> \
 | 
			
		||||
    <property name="Realms" type="ao" access="read"/> \
 | 
			
		||||
    <method name="Discover"> \
 | 
			
		||||
        <arg name="string" type="s" direction="in"/> \
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/> \
 | 
			
		||||
        <arg name="relevance" type="i" direction="out"/> \
 | 
			
		||||
        <arg name="realm" type="ao" direction="out"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
 | 
			
		||||
 | 
			
		||||
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
 | 
			
		||||
    <method name="Cancel">
 | 
			
		||||
        <arg name="operation" type="s" direction="in"/>
 | 
			
		||||
    </method>
 | 
			
		||||
    <method name="Release" />
 | 
			
		||||
    <method name="SetLocale">
 | 
			
		||||
        <arg name="locale" type="s" direction="in"/>
 | 
			
		||||
    </method>
 | 
			
		||||
    <signal name="Diagnostics">
 | 
			
		||||
        <arg name="data" type="s"/>
 | 
			
		||||
        <arg name="operation" type="s"/>
 | 
			
		||||
    </signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ServiceIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.realmd.Service"> \
 | 
			
		||||
    <method name="Cancel"> \
 | 
			
		||||
        <arg name="operation" type="s" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <method name="Release" /> \
 | 
			
		||||
    <method name="SetLocale"> \
 | 
			
		||||
        <arg name="locale" type="s" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <signal name="Diagnostics"> \
 | 
			
		||||
        <arg name="data" type="s"/> \
 | 
			
		||||
        <arg name="operation" type="s"/> \
 | 
			
		||||
    </signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
 | 
			
		||||
 | 
			
		||||
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
 | 
			
		||||
    <property name="Name" type="s" access="read"/>
 | 
			
		||||
    <property name="Configured" type="s" access="read"/>
 | 
			
		||||
    <property name="Details" type="a(ss)" access="read"/>
 | 
			
		||||
    <property name="LoginFormats" type="as" access="read"/>
 | 
			
		||||
    <property name="LoginPolicy" type="s" access="read"/>
 | 
			
		||||
    <property name="PermittedLogins" type="as" access="read"/>
 | 
			
		||||
    <property name="SupportedInterfaces" type="as" access="read"/>
 | 
			
		||||
    <method name="ChangeLoginPolicy">
 | 
			
		||||
        <arg name="login_policy" type="s" direction="in"/>
 | 
			
		||||
        <arg name="permitted_add" type="as" direction="in"/>
 | 
			
		||||
        <arg name="permitted_remove" type="as" direction="in"/>
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/>
 | 
			
		||||
    </method>
 | 
			
		||||
    <method name="Deconfigure">
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/>
 | 
			
		||||
    </method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const RealmIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.realmd.Realm"> \
 | 
			
		||||
    <property name="Name" type="s" access="read"/> \
 | 
			
		||||
    <property name="Configured" type="s" access="read"/> \
 | 
			
		||||
    <property name="Details" type="a(ss)" access="read"/> \
 | 
			
		||||
    <property name="LoginFormats" type="as" access="read"/> \
 | 
			
		||||
    <property name="LoginPolicy" type="s" access="read"/> \
 | 
			
		||||
    <property name="PermittedLogins" type="as" access="read"/> \
 | 
			
		||||
    <property name="SupportedInterfaces" type="as" access="read"/> \
 | 
			
		||||
    <method name="ChangeLoginPolicy"> \
 | 
			
		||||
        <arg name="login_policy" type="s" direction="in"/> \
 | 
			
		||||
        <arg name="permitted_add" type="as" direction="in"/> \
 | 
			
		||||
        <arg name="permitted_remove" type="as" direction="in"/> \
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
    <method name="Deconfigure"> \
 | 
			
		||||
        <arg name="options" type="a{sv}" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
 | 
			
		||||
 | 
			
		||||
const Manager = new Lang.Class({
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										284
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						@@ -6,20 +6,28 @@ const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const Fprint = imports.gdm.fingerprint;
 | 
			
		||||
const OVirt = imports.gdm.oVirt;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const SmartcardManager = imports.misc.smartcardManager;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
 | 
			
		||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
 | 
			
		||||
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
 | 
			
		||||
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
 | 
			
		||||
const FADE_ANIMATION_TIME = 0.16;
 | 
			
		||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
 | 
			
		||||
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
 | 
			
		||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
 | 
			
		||||
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
 | 
			
		||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
 | 
			
		||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
 | 
			
		||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
 | 
			
		||||
@@ -30,6 +38,13 @@ const DISABLE_USER_LIST_KEY = 'disable-user-list';
 | 
			
		||||
// Give user 16ms to read each character of a PAM message
 | 
			
		||||
const USER_READ_TIME = 16
 | 
			
		||||
 | 
			
		||||
const MessageType = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    ERROR: 1,
 | 
			
		||||
    INFO: 2,
 | 
			
		||||
    HINT: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function fadeInActor(actor) {
 | 
			
		||||
    if (actor.opacity == 255 && actor.visible)
 | 
			
		||||
        return null;
 | 
			
		||||
@@ -114,19 +129,45 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this._client = client;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed',
 | 
			
		||||
                               Lang.bind(this, this._updateDefaultService));
 | 
			
		||||
        this._updateDefaultService();
 | 
			
		||||
 | 
			
		||||
        this._fprintManager = new 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._checkForSmartcard();
 | 
			
		||||
 | 
			
		||||
        this._smartcardManager.connect('smartcard-inserted',
 | 
			
		||||
                                       Lang.bind(this, this._checkForSmartcard));
 | 
			
		||||
        this._smartcardManager.connect('smartcard-removed',
 | 
			
		||||
                                       Lang.bind(this, this._checkForSmartcard));
 | 
			
		||||
 | 
			
		||||
        this._messageQueue = [];
 | 
			
		||||
        this._messageQueueTimeoutId = 0;
 | 
			
		||||
        this.hasPendingMessages = false;
 | 
			
		||||
        this.reauthenticating = false;
 | 
			
		||||
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
 | 
			
		||||
        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
 | 
			
		||||
 | 
			
		||||
        if (this._oVirtCredentialsManager.hasToken())
 | 
			
		||||
            this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
 | 
			
		||||
 | 
			
		||||
        this._oVirtCredentialsManager.connect('user-authenticated',
 | 
			
		||||
                                              Lang.bind(this, this._oVirtUserAuthenticated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    begin: function(userName, hold) {
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
        this._hold = hold;
 | 
			
		||||
        this._userName = userName;
 | 
			
		||||
        this.reauthenticating = false;
 | 
			
		||||
 | 
			
		||||
        this._checkForFingerprintReader();
 | 
			
		||||
 | 
			
		||||
@@ -144,8 +185,10 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        if (this._cancellable)
 | 
			
		||||
            this._cancellable.cancel();
 | 
			
		||||
 | 
			
		||||
        if (this._userVerifier)
 | 
			
		||||
        if (this._userVerifier) {
 | 
			
		||||
            this._userVerifier.call_cancel_sync(null);
 | 
			
		||||
            this.clear();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
@@ -163,15 +206,14 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    answerQuery: function(serviceName, answer) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            this._clearMessageQueue();
 | 
			
		||||
        if (!this.hasPendingMessages) {
 | 
			
		||||
            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                          this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                          this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
                                                      }));
 | 
			
		||||
            let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                        Lang.bind(this, function() {
 | 
			
		||||
                                            this.disconnect(signalId);
 | 
			
		||||
                                            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
                                        }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -200,21 +242,24 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let message = this._messageQueue.shift();
 | 
			
		||||
        this.emit('show-message', message.text, message.iconName);
 | 
			
		||||
 | 
			
		||||
        this.emit('show-message', message.text, message.type);
 | 
			
		||||
 | 
			
		||||
        this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                       message.interval,
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._messageQueueTimeoutId = 0;
 | 
			
		||||
                                                           this._queueMessageTimeout();
 | 
			
		||||
                                                           return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                       }));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._messageQueueTimeoutId, '[gnome-shell] this._queueMessageTimeout');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueMessage: function(message, iconName) {
 | 
			
		||||
    _queueMessage: function(message, messageType) {
 | 
			
		||||
        let interval = this._getIntervalForMessage(message);
 | 
			
		||||
 | 
			
		||||
        this.hasPendingMessages = true;
 | 
			
		||||
        this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
 | 
			
		||||
        this._messageQueue.push({ text: message, type: messageType, interval: interval });
 | 
			
		||||
        this._queueMessageTimeout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -225,27 +270,57 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            GLib.source_remove(this._messageQueueTimeoutId);
 | 
			
		||||
            this._messageQueueTimeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('show-message', null, null);
 | 
			
		||||
        this.emit('show-message', null, MessageType.NONE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkForFingerprintReader: function() {
 | 
			
		||||
        this._haveFingerprintReader = false;
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
 | 
			
		||||
            this._updateDefaultService();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
 | 
			
		||||
            function(device, error) {
 | 
			
		||||
                if (!error && device)
 | 
			
		||||
                    this._haveFingerprintReader = true;
 | 
			
		||||
                    this._updateDefaultService();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _oVirtUserAuthenticated: function(token) {
 | 
			
		||||
        this._preemptingService = OVIRT_SERVICE_NAME;
 | 
			
		||||
        this.emit('ovirt-user-authenticated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkForSmartcard: function() {
 | 
			
		||||
        let smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
 | 
			
		||||
            smartcardDetected = false;
 | 
			
		||||
        else if (this._reauthOnly)
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
 | 
			
		||||
        else
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedTokens();
 | 
			
		||||
 | 
			
		||||
        if (smartcardDetected != this.smartcardDetected) {
 | 
			
		||||
            this.smartcardDetected = smartcardDetected;
 | 
			
		||||
 | 
			
		||||
            if (this.smartcardDetected)
 | 
			
		||||
                this._preemptingService = SMARTCARD_SERVICE_NAME;
 | 
			
		||||
            else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
 | 
			
		||||
                this._preemptingService = null;
 | 
			
		||||
 | 
			
		||||
            this.emit('smartcard-status-changed');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reportInitError: function(where, error) {
 | 
			
		||||
        logError(error, where);
 | 
			
		||||
        this._hold.release();
 | 
			
		||||
 | 
			
		||||
        this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
 | 
			
		||||
        this._queueMessage(_("Authentication error"), MessageType.ERROR);
 | 
			
		||||
        this._verificationFailed(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -266,6 +341,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.reauthenticating = true;
 | 
			
		||||
        this._connectSignals();
 | 
			
		||||
        this._beginVerification();
 | 
			
		||||
        this._hold.release();
 | 
			
		||||
@@ -296,105 +372,119 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerification: function() {
 | 
			
		||||
    _getForegroundService: function() {
 | 
			
		||||
        if (this._preemptingService)
 | 
			
		||||
            return this._preemptingService;
 | 
			
		||||
 | 
			
		||||
        return this._defaultService;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serviceIsForeground: function(serviceName) {
 | 
			
		||||
        return serviceName == this._getForegroundService();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serviceIsDefault: function(serviceName) {
 | 
			
		||||
        return serviceName == this._defaultService;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDefaultService: function() {
 | 
			
		||||
        if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
 | 
			
		||||
            this._defaultService = PASSWORD_SERVICE_NAME;
 | 
			
		||||
        else if (this.smartcardDetected)
 | 
			
		||||
            this._defaultService = SMARTCARD_SERVICE_NAME;
 | 
			
		||||
        else if (this._haveFingerprintReader)
 | 
			
		||||
            this._defaultService = FINGERPRINT_SERVICE_NAME;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startService: function(serviceName) {
 | 
			
		||||
        this._hold.acquire();
 | 
			
		||||
 | 
			
		||||
        if (this._userName) {
 | 
			
		||||
            this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
 | 
			
		||||
                                                                this._userName,
 | 
			
		||||
                                                                this._cancellable,
 | 
			
		||||
                                                                Lang.bind(this, function(obj, result) {
 | 
			
		||||
                try {
 | 
			
		||||
                    obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
                } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    this._reportInitError('Failed to start verification for user', e);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
           this._userVerifier.call_begin_verification_for_user(serviceName,
 | 
			
		||||
                                                               this._userName,
 | 
			
		||||
                                                               this._cancellable,
 | 
			
		||||
                                                               Lang.bind(this, function(obj, result) {
 | 
			
		||||
               try {
 | 
			
		||||
                   obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
               } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                   return;
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   this._reportInitError('Failed to start verification for user', e);
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
                this._hold.release();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            if (this._haveFingerprintReader) {
 | 
			
		||||
                this._hold.acquire();
 | 
			
		||||
 | 
			
		||||
                this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
 | 
			
		||||
                                                                    this._userName,
 | 
			
		||||
                                                                    this._cancellable,
 | 
			
		||||
                                                                    Lang.bind(this, function(obj, result) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
                    } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    } catch(e) {
 | 
			
		||||
                        this._reportInitError('Failed to start fingerprint verification for user', e);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this._hold.release();
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
               this._hold.release();
 | 
			
		||||
           }));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
 | 
			
		||||
                                                       this._cancellable,
 | 
			
		||||
                                                       Lang.bind(this, function(obj, result) {
 | 
			
		||||
                try {
 | 
			
		||||
                    obj.call_begin_verification_finish(result);
 | 
			
		||||
                } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    this._reportInitError('Failed to start verification', e);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
           this._userVerifier.call_begin_verification(serviceName,
 | 
			
		||||
                                                      this._cancellable,
 | 
			
		||||
                                                      Lang.bind(this, function(obj, result) {
 | 
			
		||||
               try {
 | 
			
		||||
                   obj.call_begin_verification_finish(result);
 | 
			
		||||
               } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                   return;
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   this._reportInitError('Failed to start verification', e);
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
                this._hold.release();
 | 
			
		||||
            }));
 | 
			
		||||
               this._hold.release();
 | 
			
		||||
           }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerification: function() {
 | 
			
		||||
        this._startService(this._getForegroundService());
 | 
			
		||||
 | 
			
		||||
        if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
 | 
			
		||||
            this._startService(FINGERPRINT_SERVICE_NAME);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInfo: function(client, serviceName, info) {
 | 
			
		||||
        // We don't display fingerprint messages, because they
 | 
			
		||||
        // have words like UPEK in them. Instead we use the messages
 | 
			
		||||
        // as a cue to display our own message.
 | 
			
		||||
        if (serviceName == FINGERPRINT_SERVICE_NAME &&
 | 
			
		||||
        if (this.serviceIsForeground(serviceName)) {
 | 
			
		||||
            this._queueMessage(info, MessageType.INFO);
 | 
			
		||||
        } else if (serviceName == FINGERPRINT_SERVICE_NAME &&
 | 
			
		||||
            this._haveFingerprintReader) {
 | 
			
		||||
            // We don't show fingerprint messages directly since it's
 | 
			
		||||
            // not the main auth service. Instead we use the messages
 | 
			
		||||
            // as a cue to display our own message.
 | 
			
		||||
 | 
			
		||||
            // Translators: this message is shown below the password entry field
 | 
			
		||||
            // to indicate the user can swipe their finger instead
 | 
			
		||||
            this.emit('show-login-hint', _("(or swipe finger)"));
 | 
			
		||||
        } else if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
            this._queueMessage(info, 'login-dialog-message-info');
 | 
			
		||||
            this._queueMessage(_("(or swipe finger)"), MessageType.HINT);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onProblem: function(client, serviceName, problem) {
 | 
			
		||||
        // we don't want to show auth failed messages to
 | 
			
		||||
        // users who haven't enrolled their fingerprint.
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
            return;
 | 
			
		||||
        this._queueMessage(problem, 'login-dialog-message-warning');
 | 
			
		||||
 | 
			
		||||
        this._queueMessage(problem, MessageType.ERROR);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInfoQuery: function(client, serviceName, question) {
 | 
			
		||||
        // We only expect questions to come from the main auth service
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.emit('ask-question', serviceName, question, '');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
 | 
			
		||||
        // We only expect secret requests to come from the main auth service
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (serviceName == OVIRT_SERVICE_NAME) {
 | 
			
		||||
            // The only question asked by this service is "Token?"
 | 
			
		||||
            this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        // Clear previous attempts to authenticate
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
        this._updateDefaultService();
 | 
			
		||||
 | 
			
		||||
        this.emit('reset');
 | 
			
		||||
    },
 | 
			
		||||
@@ -423,24 +513,24 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
 | 
			
		||||
 | 
			
		||||
        if (canRetry) {
 | 
			
		||||
            if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            if (!this.hasPendingMessages) {
 | 
			
		||||
                this._retry();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                          Lang.bind(this, function() {
 | 
			
		||||
                                                              this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                              this._retry();
 | 
			
		||||
                                                          }));
 | 
			
		||||
                let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this.disconnect(signalId);
 | 
			
		||||
                                                this._retry();
 | 
			
		||||
                                            }));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            if (!this.hasPendingMessages) {
 | 
			
		||||
                this._cancelAndReset();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                          Lang.bind(this, function() {
 | 
			
		||||
                                                              this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                              this._cancelAndReset();
 | 
			
		||||
                                                          }));
 | 
			
		||||
                let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this.disconnect(signalId);
 | 
			
		||||
                                                this._cancelAndReset();
 | 
			
		||||
                                            }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -448,14 +538,22 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onConversationStopped: function(client, serviceName) {
 | 
			
		||||
        // If the login failed with the preauthenticated oVirt credentials
 | 
			
		||||
        // then discard the credentials and revert to default authentication
 | 
			
		||||
        // mechanism.
 | 
			
		||||
        if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
 | 
			
		||||
            this._oVirtCredentialsManager.resetToken();
 | 
			
		||||
            this._preemptingService = null;
 | 
			
		||||
            this._verificationFailed(false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if the password service fails, then cancel everything.
 | 
			
		||||
        // But if, e.g., fingerprint fails, still give
 | 
			
		||||
        // password authentication a chance to succeed
 | 
			
		||||
        if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
        if (this.serviceIsForeground(serviceName)) {
 | 
			
		||||
            this._verificationFailed(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('hide-login-hint');
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										115
									
								
								js/js-resources.gresource.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,115 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<gresources>
 | 
			
		||||
  <gresource prefix="/org/gnome/shell">
 | 
			
		||||
    <file>gdm/authPrompt.js</file>
 | 
			
		||||
    <file>gdm/batch.js</file>
 | 
			
		||||
    <file>gdm/fingerprint.js</file>
 | 
			
		||||
    <file>gdm/loginDialog.js</file>
 | 
			
		||||
    <file>gdm/oVirt.js</file>
 | 
			
		||||
    <file>gdm/realmd.js</file>
 | 
			
		||||
    <file>gdm/util.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>extensionPrefs/main.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>misc/config.js</file>
 | 
			
		||||
    <file>misc/extensionUtils.js</file>
 | 
			
		||||
    <file>misc/fileUtils.js</file>
 | 
			
		||||
    <file>misc/gnomeSession.js</file>
 | 
			
		||||
    <file>misc/history.js</file>
 | 
			
		||||
    <file>misc/jsParse.js</file>
 | 
			
		||||
    <file>misc/loginManager.js</file>
 | 
			
		||||
    <file>misc/modemManager.js</file>
 | 
			
		||||
    <file>misc/objectManager.js</file>
 | 
			
		||||
    <file>misc/params.js</file>
 | 
			
		||||
    <file>misc/smartcardManager.js</file>
 | 
			
		||||
    <file>misc/util.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>perf/core.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>ui/altTab.js</file>
 | 
			
		||||
    <file>ui/animation.js</file>
 | 
			
		||||
    <file>ui/appDisplay.js</file>
 | 
			
		||||
    <file>ui/appFavorites.js</file>
 | 
			
		||||
    <file>ui/backgroundMenu.js</file>
 | 
			
		||||
    <file>ui/background.js</file>
 | 
			
		||||
    <file>ui/boxpointer.js</file>
 | 
			
		||||
    <file>ui/calendar.js</file>
 | 
			
		||||
    <file>ui/checkBox.js</file>
 | 
			
		||||
    <file>ui/ctrlAltTab.js</file>
 | 
			
		||||
    <file>ui/dash.js</file>
 | 
			
		||||
    <file>ui/dateMenu.js</file>
 | 
			
		||||
    <file>ui/dnd.js</file>
 | 
			
		||||
    <file>ui/endSessionDialog.js</file>
 | 
			
		||||
    <file>ui/environment.js</file>
 | 
			
		||||
    <file>ui/extensionDownloader.js</file>
 | 
			
		||||
    <file>ui/extensionSystem.js</file>
 | 
			
		||||
    <file>ui/focusCaretTracker.js</file>
 | 
			
		||||
    <file>ui/grabHelper.js</file>
 | 
			
		||||
    <file>ui/ibusCandidatePopup.js</file>
 | 
			
		||||
    <file>ui/iconGrid.js</file>
 | 
			
		||||
    <file>ui/keyboard.js</file>
 | 
			
		||||
    <file>ui/layout.js</file>
 | 
			
		||||
    <file>ui/lightbox.js</file>
 | 
			
		||||
    <file>ui/lookingGlass.js</file>
 | 
			
		||||
    <file>ui/magnifier.js</file>
 | 
			
		||||
    <file>ui/magnifierDBus.js</file>
 | 
			
		||||
    <file>ui/main.js</file>
 | 
			
		||||
    <file>ui/messageTray.js</file>
 | 
			
		||||
    <file>ui/modalDialog.js</file>
 | 
			
		||||
    <file>ui/notificationDaemon.js</file>
 | 
			
		||||
    <file>ui/osdWindow.js</file>
 | 
			
		||||
    <file>ui/overview.js</file>
 | 
			
		||||
    <file>ui/overviewControls.js</file>
 | 
			
		||||
    <file>ui/panel.js</file>
 | 
			
		||||
    <file>ui/panelMenu.js</file>
 | 
			
		||||
    <file>ui/pointerWatcher.js</file>
 | 
			
		||||
    <file>ui/popupMenu.js</file>
 | 
			
		||||
    <file>ui/remoteMenu.js</file>
 | 
			
		||||
    <file>ui/remoteSearch.js</file>
 | 
			
		||||
    <file>ui/runDialog.js</file>
 | 
			
		||||
    <file>ui/screenShield.js</file>
 | 
			
		||||
    <file>ui/screencast.js</file>
 | 
			
		||||
    <file>ui/screenshot.js</file>
 | 
			
		||||
    <file>ui/scripting.js</file>
 | 
			
		||||
    <file>ui/search.js</file>
 | 
			
		||||
    <file>ui/separator.js</file>
 | 
			
		||||
    <file>ui/sessionMode.js</file>
 | 
			
		||||
    <file>ui/shellDBus.js</file>
 | 
			
		||||
    <file>ui/shellEntry.js</file>
 | 
			
		||||
    <file>ui/shellMountOperation.js</file>
 | 
			
		||||
    <file>ui/slider.js</file>
 | 
			
		||||
    <file>ui/switcherPopup.js</file>
 | 
			
		||||
    <file>ui/tweener.js</file>
 | 
			
		||||
    <file>ui/unlockDialog.js</file>
 | 
			
		||||
    <file>ui/userWidget.js</file>
 | 
			
		||||
    <file>ui/viewSelector.js</file>
 | 
			
		||||
    <file>ui/windowAttentionHandler.js</file>
 | 
			
		||||
    <file>ui/windowMenu.js</file>
 | 
			
		||||
    <file>ui/windowManager.js</file>
 | 
			
		||||
    <file>ui/workspace.js</file>
 | 
			
		||||
    <file>ui/workspaceSwitcherPopup.js</file>
 | 
			
		||||
    <file>ui/workspaceThumbnail.js</file>
 | 
			
		||||
    <file>ui/workspacesView.js</file>
 | 
			
		||||
    <file>ui/xdndHandler.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>ui/components/__init__.js</file>
 | 
			
		||||
    <file>ui/components/autorunManager.js</file>
 | 
			
		||||
    <file>ui/components/automountManager.js</file>
 | 
			
		||||
    <file>ui/components/networkAgent.js</file>
 | 
			
		||||
    <file>ui/components/polkitAgent.js</file>
 | 
			
		||||
    <file>ui/components/telepathyClient.js</file>
 | 
			
		||||
    <file>ui/components/keyring.js</file>
 | 
			
		||||
 | 
			
		||||
    <file>ui/status/accessibility.js</file>
 | 
			
		||||
    <file>ui/status/brightness.js</file>
 | 
			
		||||
    <file>ui/status/location.js</file>
 | 
			
		||||
    <file>ui/status/keyboard.js</file>
 | 
			
		||||
    <file>ui/status/network.js</file>
 | 
			
		||||
    <file>ui/status/power.js</file>
 | 
			
		||||
    <file>ui/status/rfkill.js</file>
 | 
			
		||||
    <file>ui/status/volume.js</file>
 | 
			
		||||
    <file>ui/status/bluetooth.js</file>
 | 
			
		||||
    <file>ui/status/screencast.js</file>
 | 
			
		||||
    <file>ui/status/system.js</file>
 | 
			
		||||
  </gresource>
 | 
			
		||||
</gresources>
 | 
			
		||||
@@ -6,6 +6,8 @@ const PACKAGE_NAME = '@PACKAGE_NAME@';
 | 
			
		||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
 | 
			
		||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
 | 
			
		||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
 | 
			
		||||
/* 1 if networkmanager is available, 0 otherwise */
 | 
			
		||||
const HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
 | 
			
		||||
/* gettext package */
 | 
			
		||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
 | 
			
		||||
/* locale dir */
 | 
			
		||||
 
 | 
			
		||||
@@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({
 | 
			
		||||
        this.emit('extension-found', extension);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionsLoaded: function() {
 | 
			
		||||
        this.emit('extensions-loaded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scanExtensions: function() {
 | 
			
		||||
        let perUserDir = Gio.File.new_for_path(global.userdatadir);
 | 
			
		||||
        FileUtils.collectFromDatadirsAsync('extensions',
 | 
			
		||||
                                           { processFile: Lang.bind(this, this._loadExtension),
 | 
			
		||||
                                             loadedCallback: Lang.bind(this, this._extensionsLoaded),
 | 
			
		||||
                                             includeUserDir: true,
 | 
			
		||||
                                             data: perUserDir });
 | 
			
		||||
        FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ExtensionFinder.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
function listDirAsync(file, callback) {
 | 
			
		||||
    let allFiles = [];
 | 
			
		||||
    file.enumerate_children_async('standard::name,standard::type',
 | 
			
		||||
                                  Gio.FileQueryInfoFlags.NONE,
 | 
			
		||||
                                  GLib.PRIORITY_LOW, null, function (obj, res) {
 | 
			
		||||
        let enumerator = obj.enumerate_children_finish(res);
 | 
			
		||||
        function onNextFileComplete(obj, res) {
 | 
			
		||||
            let files = obj.next_files_finish(res);
 | 
			
		||||
            if (files.length) {
 | 
			
		||||
                allFiles = allFiles.concat(files);
 | 
			
		||||
                enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
 | 
			
		||||
            } else {
 | 
			
		||||
                enumerator.close(null);
 | 
			
		||||
                callback(allFiles);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _collectFromDirectoryAsync(dir, loadState) {
 | 
			
		||||
    function done() {
 | 
			
		||||
        loadState.numLoading--;
 | 
			
		||||
        if (loadState.loadedCallback &&
 | 
			
		||||
            loadState.numLoading == 0)
 | 
			
		||||
            loadState.loadedCallback(loadState.data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
 | 
			
		||||
        GLib.PRIORITY_DEFAULT, null, function(object, res) {
 | 
			
		||||
            try {
 | 
			
		||||
                object.query_info_finish(res);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
 | 
			
		||||
                    log(e.message);
 | 
			
		||||
                done();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            listDirAsync(dir, Lang.bind(this, function(infos) {
 | 
			
		||||
                for (let i = 0; i < infos.length; i++)
 | 
			
		||||
                    loadState.processFile(dir.get_child(infos[i].get_name()),
 | 
			
		||||
                                          infos[i], loadState.data);
 | 
			
		||||
                done();
 | 
			
		||||
            }));
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function collectFromDatadirsAsync(subdir, params) {
 | 
			
		||||
    params = Params.parse(params, { includeUserDir: false,
 | 
			
		||||
                                    processFile: null,
 | 
			
		||||
                                    loadedCallback: null,
 | 
			
		||||
                                    data: null });
 | 
			
		||||
    let loadState = { data: params.data,
 | 
			
		||||
                      numLoading: 0,
 | 
			
		||||
                      loadedCallback: params.loadedCallback,
 | 
			
		||||
                      processFile: params.processFile };
 | 
			
		||||
 | 
			
		||||
    if (params.processFile == null) {
 | 
			
		||||
        if (params.loadedCallback)
 | 
			
		||||
            params.loadedCallback(params.data);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
function collectFromDatadirs(subdir, includeUserDir, processFile) {
 | 
			
		||||
    let dataDirs = GLib.get_system_data_dirs();
 | 
			
		||||
    if (params.includeUserDir)
 | 
			
		||||
    if (includeUserDir)
 | 
			
		||||
        dataDirs.unshift(GLib.get_user_data_dir());
 | 
			
		||||
    loadState.numLoading = dataDirs.length;
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < dataDirs.length; i++) {
 | 
			
		||||
        let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
 | 
			
		||||
        let dir = Gio.File.new_for_path(path);
 | 
			
		||||
 | 
			
		||||
        _collectFromDirectoryAsync(dir, loadState);
 | 
			
		||||
        let fileEnum;
 | 
			
		||||
        try {
 | 
			
		||||
            fileEnum = dir.enumerate_children('standard::name,standard::type',
 | 
			
		||||
                                              Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            fileEnum = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (fileEnum != null) {
 | 
			
		||||
            let info;
 | 
			
		||||
            while ((info = fileEnum.next_file(null)))
 | 
			
		||||
                processFile(fileEnum.get_child(info), info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -117,7 +64,6 @@ function recursivelyMoveDir(srcDir, destDir) {
 | 
			
		||||
        let type = info.get_file_type();
 | 
			
		||||
        let srcChild = srcDir.get_child(info.get_name());
 | 
			
		||||
        let destChild = destDir.get_child(info.get_name());
 | 
			
		||||
        log([srcChild.get_path(), destChild.get_path()]);
 | 
			
		||||
        if (type == Gio.FileType.REGULAR)
 | 
			
		||||
            srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
 | 
			
		||||
        else if (type == Gio.FileType.DIRECTORY)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,17 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
 | 
			
		||||
<method name="SetStatus">
 | 
			
		||||
    <arg type="u" direction="in"/>
 | 
			
		||||
</method>
 | 
			
		||||
<property name="status" type="u" access="readwrite"/>
 | 
			
		||||
<signal name="StatusChanged">
 | 
			
		||||
    <arg type="u" direction="out"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const PresenceIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SessionManager.Presence"> \
 | 
			
		||||
<method name="SetStatus"> \
 | 
			
		||||
    <arg type="u" direction="in"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="status" type="u" access="readwrite"/> \
 | 
			
		||||
<signal name="StatusChanged"> \
 | 
			
		||||
    <arg type="u" direction="out"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const PresenceStatus = {
 | 
			
		||||
    AVAILABLE: 0,
 | 
			
		||||
@@ -30,14 +32,16 @@ function Presence(initCallback, cancellable) {
 | 
			
		||||
// Note inhibitors are immutable objects, so they don't
 | 
			
		||||
// change at runtime (changes always come in the form
 | 
			
		||||
// of new inhibitors)
 | 
			
		||||
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
 | 
			
		||||
<method name="GetAppId">
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetReason">
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const InhibitorIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SessionManager.Inhibitor"> \
 | 
			
		||||
<method name="GetAppId"> \
 | 
			
		||||
    <arg type="s" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetReason"> \
 | 
			
		||||
    <arg type="s" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
 | 
			
		||||
function Inhibitor(objectPath, initCallback, cancellable) {
 | 
			
		||||
@@ -45,27 +49,29 @@ function Inhibitor(objectPath, initCallback, cancellable) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Not the full interface, only the methods we use
 | 
			
		||||
const SessionManagerIface = <interface name="org.gnome.SessionManager">
 | 
			
		||||
<method name="Logout">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Shutdown" />
 | 
			
		||||
<method name="Reboot" />
 | 
			
		||||
<method name="CanShutdown">
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="IsInhibited">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="SessionIsActive" type="b" access="read"/>
 | 
			
		||||
<signal name="InhibitorAdded">
 | 
			
		||||
    <arg type="o" direction="out"/>
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name="InhibitorRemoved">
 | 
			
		||||
    <arg type="o" direction="out"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const SessionManagerIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SessionManager"> \
 | 
			
		||||
<method name="Logout"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Shutdown" /> \
 | 
			
		||||
<method name="Reboot" /> \
 | 
			
		||||
<method name="CanShutdown"> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="IsInhibited"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="SessionIsActive" type="b" access="read"/> \
 | 
			
		||||
<signal name="InhibitorAdded"> \
 | 
			
		||||
    <arg type="o" direction="out"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<signal name="InhibitorRemoved"> \
 | 
			
		||||
    <arg type="o" direction="out"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
 | 
			
		||||
function SessionManager(initCallback, cancellable) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								js/misc/hash.js
									
									
									
									
									
								
							
							
						
						@@ -1,144 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const System = imports.system;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
// This is an implementation of EcmaScript SameValue algorithm,
 | 
			
		||||
// which returns true if two values are not observably distinguishable.
 | 
			
		||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
 | 
			
		||||
//
 | 
			
		||||
// In the future, we may want to use the 'is' operator instead.
 | 
			
		||||
function _sameValue(x, y) {
 | 
			
		||||
    if (x === y) {
 | 
			
		||||
        // 0 === -0, but they are not identical
 | 
			
		||||
        return x !== 0 || 1 / x === 1 / y;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // NaN !== NaN, but they are identical.
 | 
			
		||||
    // NaNs are the only non-reflexive value, i.e., if x !== x,
 | 
			
		||||
    // then x is a NaN.
 | 
			
		||||
    // isNaN is broken: it converts its argument to number, so
 | 
			
		||||
    // isNaN("foo") => true
 | 
			
		||||
    return x !== x && y !== y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _hashers = {
 | 
			
		||||
    object: function(o) { return o ? System.addressOf(o) : 'null'; },
 | 
			
		||||
    function: function(f) { return System.addressOf(f); },
 | 
			
		||||
    string: function(s) { return s; },
 | 
			
		||||
    number: function(n) { return String(n); },
 | 
			
		||||
    undefined: function() { return 'undefined'; },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Map is meant to be similar in usage to ES6 Map, which is
 | 
			
		||||
   described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
 | 
			
		||||
   without requiring more than ES5 + Gjs extensions.
 | 
			
		||||
 | 
			
		||||
   Known differences from other implementations:
 | 
			
		||||
   Polyfills around the web usually implement HashMaps for
 | 
			
		||||
   primitive values and reversed WeakMaps for object keys,
 | 
			
		||||
   but we want real maps with real O(1) semantics in all cases,
 | 
			
		||||
   and the easiest way is to have different hashers for different
 | 
			
		||||
   types.
 | 
			
		||||
 | 
			
		||||
   Known differences from the ES6 specification:
 | 
			
		||||
   - Map is a Lang.Class, not a ES6 class, so inheritance,
 | 
			
		||||
     prototype, sealing, etc. work differently.
 | 
			
		||||
   - items(), keys() and values() don't return iterators,
 | 
			
		||||
     they return actual arrays, so they incur a full copy everytime
 | 
			
		||||
     they're called, and they don't see changes if you mutate
 | 
			
		||||
     the table while iterating
 | 
			
		||||
     (admittedly, the ES6 spec is a bit unclear on this, and
 | 
			
		||||
     the reference code would just blow up)
 | 
			
		||||
*/
 | 
			
		||||
const Map = new Lang.Class({
 | 
			
		||||
    Name: 'Map',
 | 
			
		||||
 | 
			
		||||
    _init: function(iterable) {
 | 
			
		||||
        this._pool = { };
 | 
			
		||||
        this._size = 0;
 | 
			
		||||
 | 
			
		||||
        if (iterable) {
 | 
			
		||||
            for (let i = 0; i < iterable.length; i++) {
 | 
			
		||||
                let [key, value] = iterable[i];
 | 
			
		||||
                this.set(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hashKey: function(key) {
 | 
			
		||||
        let type = typeof(key);
 | 
			
		||||
        return type + ':' + _hashers[type](key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _internalGet: function(key) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node && _sameValue(node.key, key))
 | 
			
		||||
            return [true, node.value];
 | 
			
		||||
        else
 | 
			
		||||
            return [false, null];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get: function(key) {
 | 
			
		||||
        return this._internalGet(key)[1];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    has: function(key) {
 | 
			
		||||
        return this._internalGet(key)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set: function(key, value) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node) {
 | 
			
		||||
            node.key = key;
 | 
			
		||||
            node.value = value;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._pool[hash] = { key: key, value: value };
 | 
			
		||||
            this._size++;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    delete: function(key) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node && _sameValue(node.key, key)) {
 | 
			
		||||
            delete this._pool[hash];
 | 
			
		||||
            this._size--;
 | 
			
		||||
            return [node.key, node.value];
 | 
			
		||||
        } else {
 | 
			
		||||
            return [null, null];
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    keys: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return pool[hash].key;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    values: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return pool[hash].value;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    items: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return [pool[hash].key, pool[hash].value];
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    size: function() {
 | 
			
		||||
        return this._size;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
@@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Down) {
 | 
			
		||||
            return this._setNextItem(entry.get_text());
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _indexChanged: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,74 +7,45 @@ const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
 | 
			
		||||
<method name='PowerOff'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Reboot'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Suspend'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanPowerOff'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanReboot'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanSuspend'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Inhibit'>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='h' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='GetSession'>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='o' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='ListSessions'>
 | 
			
		||||
    <arg name='sessions' type='a(susso)' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<signal name='PrepareForSleep'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const SystemdLoginManagerIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.login1.Manager"> \
 | 
			
		||||
<method name="Suspend"> \
 | 
			
		||||
    <arg type="b" direction="in"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="CanSuspend"> \
 | 
			
		||||
    <arg type="s" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Inhibit"> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="h" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetSession"> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="o" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ListSessions"> \
 | 
			
		||||
    <arg name="sessions" type="a(susso)" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="PrepareForSleep"> \
 | 
			
		||||
    <arg type="b" direction="out"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
const SystemdLoginSessionIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.login1.Session"> \
 | 
			
		||||
<signal name="Lock" /> \
 | 
			
		||||
<signal name="Unlock" /> \
 | 
			
		||||
<property name="Active" type="b" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
 | 
			
		||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
 | 
			
		||||
 | 
			
		||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
 | 
			
		||||
<method name='CanRestart'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanStop'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Restart' />
 | 
			
		||||
<method name='Stop' />
 | 
			
		||||
<method name='GetCurrentSession'>
 | 
			
		||||
    <arg type='o' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
 | 
			
		||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
 | 
			
		||||
 | 
			
		||||
function haveSystemd() {
 | 
			
		||||
    return GLib.access("/run/systemd/seats", 0) >= 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -84,8 +55,10 @@ function versionCompare(required, reference) {
 | 
			
		||||
    reference = reference.split('.');
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        if (required[i] != reference[i])
 | 
			
		||||
            return required[i] < reference[i];
 | 
			
		||||
        let requiredInt = parseInt(required[i]);
 | 
			
		||||
        let referenceInt = parseInt(reference[i]);
 | 
			
		||||
        if (requiredInt != referenceInt)
 | 
			
		||||
            return requiredInt < referenceInt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
@@ -102,7 +75,7 @@ function canLock() {
 | 
			
		||||
                                               -1, null);
 | 
			
		||||
 | 
			
		||||
        let version = result.deep_unpack()[0].deep_unpack();
 | 
			
		||||
        return versionCompare('3.5.91', version);
 | 
			
		||||
        return haveSystemd() && versionCompare('3.5.91', version);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
@@ -120,7 +93,7 @@ function getLoginManager() {
 | 
			
		||||
        if (haveSystemd())
 | 
			
		||||
            _loginManager = new LoginManagerSystemd();
 | 
			
		||||
        else
 | 
			
		||||
            _loginManager = new LoginManagerConsoleKit();
 | 
			
		||||
            _loginManager = new LoginManagerDummy();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return _loginManager;
 | 
			
		||||
@@ -137,9 +110,6 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, this._prepareForSleep));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function(callback) {
 | 
			
		||||
        if (this._currentSession) {
 | 
			
		||||
            callback (this._currentSession);
 | 
			
		||||
@@ -159,24 +129,6 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanPowerOffRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRebootRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanSuspendRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
@@ -195,14 +147,6 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.PowerOffRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RebootRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this._proxy.SuspendRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
@@ -218,7 +162,7 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
                let fd = -1;
 | 
			
		||||
                try {
 | 
			
		||||
                    let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
 | 
			
		||||
                    fd = fdList.steal_fds(outVariant.deep_unpack())[0];
 | 
			
		||||
                    fd = fdList.steal_fds()[0];
 | 
			
		||||
                    callback(new Gio.UnixInputStream({ fd: fd }));
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    logError(e, "Error getting systemd inhibitor");
 | 
			
		||||
@@ -233,53 +177,13 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LoginManagerSystemd.prototype);
 | 
			
		||||
 | 
			
		||||
const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
    Name: 'LoginManagerConsoleKit',
 | 
			
		||||
const LoginManagerDummy = new Lang.Class({
 | 
			
		||||
    Name: 'LoginManagerDummy',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._proxy = new ConsoleKitManager(Gio.DBus.system,
 | 
			
		||||
                                            'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                            '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function(callback) {
 | 
			
		||||
        if (this._currentSession) {
 | 
			
		||||
            callback (this._currentSession);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._proxy.GetCurrentSessionRemote(Lang.bind(this,
 | 
			
		||||
            function(result, error) {
 | 
			
		||||
                if (error) {
 | 
			
		||||
                    logError(error, 'Could not get a proxy for the current session');
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._currentSession = new ConsoleKitSession(Gio.DBus.system,
 | 
			
		||||
                                                                 'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                                                 result[0]);
 | 
			
		||||
                    callback(this._currentSession);
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanStopRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRestartRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
        // we could return a DummySession object that fakes whatever callers
 | 
			
		||||
        // expect (at the time of writing: connect() and connectSignal()
 | 
			
		||||
        // methods), but just never calling the callback should be safer
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
@@ -290,14 +194,6 @@ const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
        asyncCallback([]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.StopRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RestartRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this.emit('prepare-for-sleep', true);
 | 
			
		||||
        this.emit('prepare-for-sleep', false);
 | 
			
		||||
@@ -307,4 +203,4 @@ const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
        callback(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
 | 
			
		||||
Signals.addSignalMethods(LoginManagerDummy.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -92,37 +92,41 @@ function _findProviderForSid(sid) {
 | 
			
		||||
// The following are not the complete interfaces, just the methods we need
 | 
			
		||||
// (or may need in the future)
 | 
			
		||||
 | 
			
		||||
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
 | 
			
		||||
<method name="GetRegistrationInfo">
 | 
			
		||||
    <arg type="(uss)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetSignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="AccessTechnology" type="u" access="read" />
 | 
			
		||||
<signal name="SignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name="RegistrationInfo">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
    <arg type="s" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ModemGsmNetworkInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \
 | 
			
		||||
<method name="GetRegistrationInfo"> \
 | 
			
		||||
    <arg type="(uss)" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetSignalQuality"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="AccessTechnology" type="u" access="read" /> \
 | 
			
		||||
<signal name="SignalQuality"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<signal name="RegistrationInfo"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
    <arg type="s" direction="out" /> \
 | 
			
		||||
    <arg type="s" direction="out" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
 | 
			
		||||
 | 
			
		||||
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
 | 
			
		||||
<method name="GetSignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetServingSystem">
 | 
			
		||||
    <arg type="(usu)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="SignalQuality">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ModemCdmaInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ModemManager.Modem.Cdma"> \
 | 
			
		||||
<method name="GetSignalQuality"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetServingSystem"> \
 | 
			
		||||
    <arg type="(usu)" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="SignalQuality"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
@@ -218,20 +222,26 @@ Signals.addSignalMethods(ModemCdma.prototype);
 | 
			
		||||
// Support for the new ModemManager1 interface (MM >= 0.7)
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
 | 
			
		||||
<property name="SignalQuality" type="(ub)" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModemInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ModemManager1.Modem"> \
 | 
			
		||||
<property name="SignalQuality" type="(ub)" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
 | 
			
		||||
<property name="OperatorCode" type="s" access="read" />
 | 
			
		||||
<property name="OperatorName" type="s" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModem3gppInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \
 | 
			
		||||
<property name="OperatorCode" type="s" access="read" /> \
 | 
			
		||||
<property name="OperatorName" type="s" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
 | 
			
		||||
<property name="Sid" type="u" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModemCdmaInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \
 | 
			
		||||
<property name="Sid" type="u" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModem = new Lang.Class({
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										259
									
								
								js/misc/objectManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,259 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
// Specified in the D-Bus specification here:
 | 
			
		||||
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
 | 
			
		||||
const ObjectManagerIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.DBus.ObjectManager"> \
 | 
			
		||||
  <method name="GetManagedObjects"> \
 | 
			
		||||
    <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
 | 
			
		||||
  </method> \
 | 
			
		||||
  <signal name="InterfacesAdded"> \
 | 
			
		||||
    <arg name="objectPath" type="o"/> \
 | 
			
		||||
    <arg name="interfaces" type="a{sa{sv}}" /> \
 | 
			
		||||
  </signal> \
 | 
			
		||||
  <signal name="InterfacesRemoved"> \
 | 
			
		||||
    <arg name="objectPath" type="o"/> \
 | 
			
		||||
    <arg name="interfaces" type="as" /> \
 | 
			
		||||
  </signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
 | 
			
		||||
 | 
			
		||||
const ObjectManager = new Lang.Class({
 | 
			
		||||
    Name: 'ObjectManager',
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { connection: null,
 | 
			
		||||
                                        name: null,
 | 
			
		||||
                                        objectPath: null,
 | 
			
		||||
                                        knownInterfaces: null,
 | 
			
		||||
                                        cancellable: null,
 | 
			
		||||
                                        onLoaded: null });
 | 
			
		||||
 | 
			
		||||
        this._connection = params.connection;
 | 
			
		||||
        this._serviceName = params.name;
 | 
			
		||||
        this._managerPath = params.objectPath;
 | 
			
		||||
        this._cancellable = params.cancellable;
 | 
			
		||||
 | 
			
		||||
        this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
 | 
			
		||||
                                                 g_interface_name: ObjectManagerInfo.name,
 | 
			
		||||
                                                 g_interface_info: ObjectManagerInfo,
 | 
			
		||||
                                                 g_name: this._serviceName,
 | 
			
		||||
                                                 g_object_path: this._managerPath,
 | 
			
		||||
                                                 g_flags: Gio.DBusProxyFlags.NONE });
 | 
			
		||||
 | 
			
		||||
        this._interfaceInfos = {};
 | 
			
		||||
        this._objects = {};
 | 
			
		||||
        this._interfaces = {};
 | 
			
		||||
        this._onLoaded = params.onLoaded;
 | 
			
		||||
 | 
			
		||||
        if (params.knownInterfaces)
 | 
			
		||||
            this._registerInterfaces(params.knownInterfaces);
 | 
			
		||||
 | 
			
		||||
        // Start out inhibiting load until at least the proxy
 | 
			
		||||
        // manager is loaded and the remote objects are fetched
 | 
			
		||||
        this._numLoadInhibitors = 1;
 | 
			
		||||
        this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                      this._cancellable,
 | 
			
		||||
                                      Lang.bind(this, this._onManagerProxyLoaded));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tryToCompleteLoad: function() {
 | 
			
		||||
        this._numLoadInhibitors--;
 | 
			
		||||
        if (this._numLoadInhibitors == 0) {
 | 
			
		||||
            if (this._onLoaded)
 | 
			
		||||
                this._onLoaded();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addInterface: function(objectPath, interfaceName, onFinished) {
 | 
			
		||||
        let info = this._interfaceInfos[interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (!info) {
 | 
			
		||||
           if (onFinished)
 | 
			
		||||
               onFinished();
 | 
			
		||||
           return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let proxy = new Gio.DBusProxy({ g_connection: this._connection,
 | 
			
		||||
                                       g_name: this._serviceName,
 | 
			
		||||
                                       g_object_path: objectPath,
 | 
			
		||||
                                       g_interface_name: interfaceName,
 | 
			
		||||
                                       g_interface_info: info,
 | 
			
		||||
                                       g_flags: Gio.DBusProxyFlags.NONE });
 | 
			
		||||
 | 
			
		||||
        proxy.init_async(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                         this._cancellable,
 | 
			
		||||
                         Lang.bind(this, function(initable, result) {
 | 
			
		||||
               let error = null;
 | 
			
		||||
               try {
 | 
			
		||||
                   initable.init_finish(result);
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   logError(e, 'could not initialize proxy for interface ' + interfaceName);
 | 
			
		||||
 | 
			
		||||
                   if (onFinished)
 | 
			
		||||
                       onFinished();
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
               let isNewObject;
 | 
			
		||||
               if (!this._objects[objectPath]) {
 | 
			
		||||
                   this._objects[objectPath] = {};
 | 
			
		||||
                   isNewObject = true;
 | 
			
		||||
               } else {
 | 
			
		||||
                   isNewObject = false;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
               this._objects[objectPath][interfaceName] = proxy;
 | 
			
		||||
 | 
			
		||||
               if (!this._interfaces[interfaceName])
 | 
			
		||||
                   this._interfaces[interfaceName] = [];
 | 
			
		||||
 | 
			
		||||
               this._interfaces[interfaceName].push(proxy);
 | 
			
		||||
 | 
			
		||||
               if (isNewObject)
 | 
			
		||||
                   this.emit('object-added', objectPath);
 | 
			
		||||
 | 
			
		||||
               this.emit('interface-added', interfaceName, proxy);
 | 
			
		||||
 | 
			
		||||
               if (onFinished)
 | 
			
		||||
                   onFinished();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeInterface: function(objectPath, interfaceName) {
 | 
			
		||||
        if (!this._objects[objectPath])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let proxy = this._objects[objectPath][interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (this._interfaces[interfaceName]) {
 | 
			
		||||
            let index = this._interfaces[interfaceName].indexOf(proxy);
 | 
			
		||||
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
                this._interfaces[interfaceName].splice(index, 1);
 | 
			
		||||
 | 
			
		||||
            if (this._interfaces[interfaceName].length == 0)
 | 
			
		||||
                delete this._interfaces[interfaceName];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('interface-removed', interfaceName, proxy);
 | 
			
		||||
 | 
			
		||||
        this._objects[objectPath][interfaceName] = null;
 | 
			
		||||
 | 
			
		||||
        if (Object.keys(this._objects[objectPath]).length == 0) {
 | 
			
		||||
            delete this._objects[objectPath];
 | 
			
		||||
            this.emit('object-removed', objectPath);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerProxyLoaded: function(initable, result) {
 | 
			
		||||
        let error = null;
 | 
			
		||||
        try {
 | 
			
		||||
            initable.init_finish(result);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logError(e, 'could not initialize object manager for object ' + params.name);
 | 
			
		||||
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._managerProxy.connectSignal('InterfacesAdded',
 | 
			
		||||
                                         Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
 | 
			
		||||
                                             let interfaceNames = Object.keys(interfaces);
 | 
			
		||||
                                             for (let i = 0; i < interfaceNames.length; i++)
 | 
			
		||||
                                                 this._addInterface(objectPath, interfaceNames[i]);
 | 
			
		||||
                                         }));
 | 
			
		||||
        this._managerProxy.connectSignal('InterfacesRemoved',
 | 
			
		||||
                                         Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
 | 
			
		||||
                                             for (let i = 0; i < interfaceNames.length; i++)
 | 
			
		||||
                                                 this._removeInterface(objectPath, interfaceNames[i]);
 | 
			
		||||
                                         }));
 | 
			
		||||
 | 
			
		||||
        if (Object.keys(this._interfaceInfos).length == 0) {
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
 | 
			
		||||
            if (!result) {
 | 
			
		||||
                if (error) {
 | 
			
		||||
                   logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this._tryToCompleteLoad();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let [objects] = result;
 | 
			
		||||
 | 
			
		||||
            let objectPaths = Object.keys(objects);
 | 
			
		||||
            for (let i = 0; i < objectPaths.length; i++) {
 | 
			
		||||
                let objectPath = objectPaths[i];
 | 
			
		||||
                let object = objects[objectPath];
 | 
			
		||||
 | 
			
		||||
                let interfaceNames = Object.getOwnPropertyNames(object);
 | 
			
		||||
                for (let j = 0; j < interfaceNames.length; j++) {
 | 
			
		||||
                    let interfaceName = interfaceNames[j];
 | 
			
		||||
 | 
			
		||||
                    // Prevent load from completing until the interface is loaded
 | 
			
		||||
                    this._numLoadInhibitors++;
 | 
			
		||||
                    this._addInterface(objectPath,
 | 
			
		||||
                                       interfaceName,
 | 
			
		||||
                                       Lang.bind(this, this._tryToCompleteLoad));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _registerInterfaces: function(interfaces) {
 | 
			
		||||
        for (let i = 0; i < interfaces.length; i++) {
 | 
			
		||||
            let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
 | 
			
		||||
            this._interfaceInfos[info.name] = info;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProxy: function(objectPath, interfaceName) {
 | 
			
		||||
        let object = this._objects[objectPath];
 | 
			
		||||
 | 
			
		||||
        if (!object)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return object[interfaceName];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProxiesForInterface: function(interfaceName) {
 | 
			
		||||
        let proxyList = this._interfaces[interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (!proxyList)
 | 
			
		||||
            return [];
 | 
			
		||||
 | 
			
		||||
        return proxyList;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAllProxies: function() {
 | 
			
		||||
        let proxies = [];
 | 
			
		||||
 | 
			
		||||
        let objectPaths = Object.keys(this._objects);
 | 
			
		||||
        for (let i = 0; i < objectPaths.length; i++) {
 | 
			
		||||
            let object = this._objects[objectPaths];
 | 
			
		||||
 | 
			
		||||
            let interfaceNames = Object.keys(object);
 | 
			
		||||
            for (let j = 0; i < interfaceNames.length; i++) {
 | 
			
		||||
                let interfaceName = interfaceNames[i];
 | 
			
		||||
                if (object[interfaceName])
 | 
			
		||||
                    proxies.push(object(interfaceName));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return proxies;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ObjectManager.prototype);
 | 
			
		||||
							
								
								
									
										119
									
								
								js/misc/smartcardManager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,119 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const ObjectManager = imports.misc.objectManager;
 | 
			
		||||
 | 
			
		||||
const SmartcardTokenIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
 | 
			
		||||
  <property name="Name" type="s" access="read"/> \
 | 
			
		||||
  <property name="Driver" type="o" access="read"/> \
 | 
			
		||||
  <property name="IsInserted" type="b" access="read"/> \
 | 
			
		||||
  <property name="UsedToLogin" type="b" access="read"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
let _smartcardManager = null;
 | 
			
		||||
 | 
			
		||||
function getSmartcardManager() {
 | 
			
		||||
    if (_smartcardManager == null)
 | 
			
		||||
        _smartcardManager = new SmartcardManager();
 | 
			
		||||
 | 
			
		||||
    return _smartcardManager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SmartcardManager = new Lang.Class({
 | 
			
		||||
    Name: 'SmartcardManager',
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
 | 
			
		||||
                                                                name: "org.gnome.SettingsDaemon.Smartcard",
 | 
			
		||||
                                                                objectPath: '/org/gnome/SettingsDaemon/Smartcard',
 | 
			
		||||
                                                                knownInterfaces: [ SmartcardTokenIface ],
 | 
			
		||||
                                                                onLoaded: Lang.bind(this, this._onLoaded) });
 | 
			
		||||
        this._insertedTokens = {};
 | 
			
		||||
        this._loginToken = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLoaded: function() {
 | 
			
		||||
        let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < tokens.length; i++)
 | 
			
		||||
            this._addToken(tokens[i]);
 | 
			
		||||
 | 
			
		||||
        this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
 | 
			
		||||
            if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
 | 
			
		||||
                this._addToken(proxy);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
 | 
			
		||||
            if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
 | 
			
		||||
                this._removeToken(proxy);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateToken: function(token) {
 | 
			
		||||
        let objectPath = token.get_object_path();
 | 
			
		||||
 | 
			
		||||
        delete this._insertedTokens[objectPath];
 | 
			
		||||
 | 
			
		||||
        if (token.IsInserted)
 | 
			
		||||
            this._insertedTokens[objectPath] = token;
 | 
			
		||||
 | 
			
		||||
        if (token.UsedToLogin)
 | 
			
		||||
            this._loginToken = token;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addToken: function(token) {
 | 
			
		||||
        this._updateToken(token);
 | 
			
		||||
 | 
			
		||||
        token.connect('g-properties-changed',
 | 
			
		||||
                      Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
                          if ('IsInserted' in properties.deep_unpack()) {
 | 
			
		||||
                              this._updateToken(token);
 | 
			
		||||
 | 
			
		||||
                              if (token.IsInserted) {
 | 
			
		||||
                                  this.emit('smartcard-inserted', token);
 | 
			
		||||
                              } else {
 | 
			
		||||
                                  this.emit('smartcard-removed', token);
 | 
			
		||||
                              }
 | 
			
		||||
                          }
 | 
			
		||||
                      }));
 | 
			
		||||
 | 
			
		||||
        // Emit a smartcard-inserted at startup if it's already plugged in
 | 
			
		||||
        if (token.IsInserted)
 | 
			
		||||
            this.emit('smartcard-inserted', token);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeToken: function(token) {
 | 
			
		||||
        let objectPath = token.get_object_path();
 | 
			
		||||
 | 
			
		||||
        if (this._insertedTokens[objectPath] == token) {
 | 
			
		||||
            delete this._insertedTokens[objectPath];
 | 
			
		||||
            this.emit('smartcard-removed', token);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._loginToken == token)
 | 
			
		||||
            this._loginToken = null;
 | 
			
		||||
 | 
			
		||||
        token.disconnectAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasInsertedTokens: function() {
 | 
			
		||||
        return Object.keys(this._insertedTokens).length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasInsertedLoginToken: function() {
 | 
			
		||||
        if (!this._loginToken)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (!this._loginToken.IsInserted)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SmartcardManager.prototype);
 | 
			
		||||
							
								
								
									
										112
									
								
								js/misc/util.js
									
									
									
									
									
								
							
							
						
						@@ -1,7 +1,9 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
@@ -78,6 +80,22 @@ function spawnCommandLine(command_line) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// spawnApp:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
// Runs @argv as if it was an application, handling startup notification
 | 
			
		||||
function spawnApp(argv) {
 | 
			
		||||
    try {
 | 
			
		||||
        let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
 | 
			
		||||
                                                      Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
 | 
			
		||||
 | 
			
		||||
        let context = global.create_app_launch_context(0, -1);
 | 
			
		||||
        app.launch([], context);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        _handleSpawnError(argv[0], err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawn:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
@@ -111,7 +129,7 @@ function trySpawn(argv)
 | 
			
		||||
    // Dummy child watch; we don't want to double-fork internally
 | 
			
		||||
    // because then we lose the parent-child relationship, which
 | 
			
		||||
    // can break polkit.  See https://bugzilla.redhat.com//show_bug.cgi?id=819275
 | 
			
		||||
    GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
 | 
			
		||||
    GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawnCommandLine:
 | 
			
		||||
@@ -135,35 +153,10 @@ function trySpawnCommandLine(command_line) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _handleSpawnError(command, err) {
 | 
			
		||||
    let title = _("Execution of '%s' failed:").format(command);
 | 
			
		||||
    let title = _("Execution of “%s” failed:").format(command);
 | 
			
		||||
    Main.notifyError(title, err.message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// killall:
 | 
			
		||||
// @processName: a process name
 | 
			
		||||
//
 | 
			
		||||
// Kills @processName. If no process with the given name is found,
 | 
			
		||||
// this will fail silently.
 | 
			
		||||
function killall(processName) {
 | 
			
		||||
    try {
 | 
			
		||||
        // pkill is more portable than killall, but on Linux at least
 | 
			
		||||
        // it won't match if you pass more than 15 characters of the
 | 
			
		||||
        // process name... However, if you use the '-f' flag to match
 | 
			
		||||
        // the entire command line, it will work, but we have to be
 | 
			
		||||
        // careful in that case that we can match
 | 
			
		||||
        // '/usr/bin/processName' but not 'gedit processName.c' or
 | 
			
		||||
        // whatever...
 | 
			
		||||
 | 
			
		||||
        let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
 | 
			
		||||
        GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
 | 
			
		||||
        // It might be useful to return success/failure, but we'd need
 | 
			
		||||
        // a wrapper around WIFEXITED and WEXITSTATUS. Since none of
 | 
			
		||||
        // the current callers care, we don't bother.
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logError(e, 'Failed to kill ' + processName);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lowerBound:
 | 
			
		||||
// @array: an array or array-like object, already sorted
 | 
			
		||||
//         according to @cmp
 | 
			
		||||
@@ -214,28 +207,57 @@ function insertSorted(array, val, cmp) {
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeCloseButton() {
 | 
			
		||||
    let closeButton = new St.Button({ style_class: 'notification-close'});
 | 
			
		||||
const CloseButton = new Lang.Class({
 | 
			
		||||
    Name: 'CloseButton',
 | 
			
		||||
    Extends: St.Button,
 | 
			
		||||
 | 
			
		||||
    // This is a bit tricky. St.Bin has its own x-align/y-align properties
 | 
			
		||||
    // that compete with Clutter's properties. This should be fixed for
 | 
			
		||||
    // Clutter 2.0. Since St.Bin doesn't define its own setters, the
 | 
			
		||||
    // setters are a workaround to get Clutter's version.
 | 
			
		||||
    closeButton.set_x_align(Clutter.ActorAlign.END);
 | 
			
		||||
    closeButton.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
    _init: function(boxpointer) {
 | 
			
		||||
        this.parent({ style_class: 'notification-close'});
 | 
			
		||||
 | 
			
		||||
    // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
 | 
			
		||||
    // to respect the alignments.
 | 
			
		||||
    closeButton.set_x_expand(true);
 | 
			
		||||
    closeButton.set_y_expand(true);
 | 
			
		||||
        // This is a bit tricky. St.Bin has its own x-align/y-align properties
 | 
			
		||||
        // that compete with Clutter's properties. This should be fixed for
 | 
			
		||||
        // Clutter 2.0. Since St.Bin doesn't define its own setters, the
 | 
			
		||||
        // setters are a workaround to get Clutter's version.
 | 
			
		||||
        this.set_x_align(Clutter.ActorAlign.END);
 | 
			
		||||
        this.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
 | 
			
		||||
    closeButton.connect('style-changed', function() {
 | 
			
		||||
        let themeNode = closeButton.get_theme_node();
 | 
			
		||||
        closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
 | 
			
		||||
        closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
 | 
			
		||||
    });
 | 
			
		||||
        // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
 | 
			
		||||
        // to respect the alignments.
 | 
			
		||||
        this.set_x_expand(true);
 | 
			
		||||
        this.set_y_expand(true);
 | 
			
		||||
 | 
			
		||||
    return closeButton;
 | 
			
		||||
        this._boxPointer = boxpointer;
 | 
			
		||||
        if (boxpointer)
 | 
			
		||||
            this._boxPointer.connect('arrow-side-changed', Lang.bind(this, this._sync));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeBoxPointerOffset: function() {
 | 
			
		||||
        if (!this._boxPointer || !this._boxPointer.actor.get_stage())
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let side = this._boxPointer.arrowSide;
 | 
			
		||||
        if (side == St.Side.TOP)
 | 
			
		||||
            return this._boxPointer.getArrowHeight();
 | 
			
		||||
        else
 | 
			
		||||
            return 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
 | 
			
		||||
        let offY = this._computeBoxPointerOffset();
 | 
			
		||||
        this.translation_x = themeNode.get_length('-shell-close-overlap-x')
 | 
			
		||||
        this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_style_changed: function() {
 | 
			
		||||
        this._sync();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function makeCloseButton(boxpointer) {
 | 
			
		||||
    return new CloseButton(boxpointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ensureActorVisibleInScrollView(scrollView, actor) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
@@ -23,7 +24,7 @@ const WINDOW_PREVIEW_SIZE = 128;
 | 
			
		||||
const APP_ICON_SIZE = 96;
 | 
			
		||||
const APP_ICON_SIZE_SMALL = 48;
 | 
			
		||||
 | 
			
		||||
const iconSizes = [96, 64, 48, 32, 22];
 | 
			
		||||
const baseIconSizes = [96, 64, 48, 32, 22];
 | 
			
		||||
 | 
			
		||||
const AppIconMode = {
 | 
			
		||||
    THUMBNAIL_ONLY: 1,
 | 
			
		||||
@@ -106,6 +107,8 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._switcherList = new AppSwitcher(apps, this);
 | 
			
		||||
        this._items = this._switcherList.icons;
 | 
			
		||||
        if (this._items.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
@@ -164,6 +167,8 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
                this._select(this._selectedIndex, this._nextWindow());
 | 
			
		||||
            else if (keysym == Clutter.Up)
 | 
			
		||||
                this._select(this._selectedIndex, null, true);
 | 
			
		||||
            else
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (keysym == Clutter.Left)
 | 
			
		||||
                this._select(this._previous());
 | 
			
		||||
@@ -171,7 +176,11 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
                this._select(this._next());
 | 
			
		||||
            else if (keysym == Clutter.Down)
 | 
			
		||||
                this._select(this._selectedIndex, 0);
 | 
			
		||||
            else
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scrollHandler: function(direction) {
 | 
			
		||||
@@ -302,6 +311,7 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
            this._thumbnailTimeoutId = Mainloop.timeout_add (
 | 
			
		||||
                THUMBNAIL_POPUP_TIME,
 | 
			
		||||
                Lang.bind(this, this._timeoutPopupThumbnails));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -310,7 +320,7 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
            this._createThumbnails();
 | 
			
		||||
        this._thumbnailTimeoutId = 0;
 | 
			
		||||
        this._thumbnailsFocused = false;
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyThumbnails : function() {
 | 
			
		||||
@@ -355,11 +365,14 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
    Name: 'WindowSwitcherPopup',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherPopup,
 | 
			
		||||
 | 
			
		||||
    _init: function(items) {
 | 
			
		||||
        this.parent(items);
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getWindowList: function() {
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
        let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
 | 
			
		||||
                                                                       : null;
 | 
			
		||||
        return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
 | 
			
		||||
        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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSwitcher: function() {
 | 
			
		||||
@@ -368,9 +381,13 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
        if (windows.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._switcherList = new WindowList(windows);
 | 
			
		||||
        let mode = this._settings.get_enum('app-icon-mode');
 | 
			
		||||
        this._switcherList = new WindowList(windows, mode);
 | 
			
		||||
        this._items = this._switcherList.icons;
 | 
			
		||||
 | 
			
		||||
        if (this._items.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -393,7 +410,11 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
                this._select(this._previous());
 | 
			
		||||
            else if (keysym == Clutter.Right)
 | 
			
		||||
                this._select(this._next());
 | 
			
		||||
            else
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish: function() {
 | 
			
		||||
@@ -420,7 +441,6 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    set_size: function(size) {
 | 
			
		||||
        this.icon = this.app.create_icon_texture(size);
 | 
			
		||||
        this._iconBin.set_size(size, size);
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -436,8 +456,10 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
        this._arrows = [];
 | 
			
		||||
 | 
			
		||||
        let windowTracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
 | 
			
		||||
                                                     global.screen, null);
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
 | 
			
		||||
        let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
 | 
			
		||||
                                                                       : null;
 | 
			
		||||
        let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
 | 
			
		||||
 | 
			
		||||
        // Construct the AppIcons, add to the popup
 | 
			
		||||
        for (let i = 0; i < apps.length; i++) {
 | 
			
		||||
@@ -447,7 +469,10 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
            appIcon.cachedWindows = allWindows.filter(function(w) {
 | 
			
		||||
                return windowTracker.get_window_app (w) == appIcon.app;
 | 
			
		||||
            });
 | 
			
		||||
            this._addIcon(appIcon);
 | 
			
		||||
            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;
 | 
			
		||||
@@ -463,12 +488,13 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
            Mainloop.source_remove(this._mouseTimeOutId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (actor, forWidth, alloc) {
 | 
			
		||||
    _setIconSize: function() {
 | 
			
		||||
        let j = 0;
 | 
			
		||||
        while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
 | 
			
		||||
                j++;
 | 
			
		||||
        }
 | 
			
		||||
        let themeNode = this._items[j].get_theme_node();
 | 
			
		||||
 | 
			
		||||
        let iconPadding = themeNode.get_horizontal_padding();
 | 
			
		||||
        let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
 | 
			
		||||
        let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
 | 
			
		||||
@@ -479,19 +505,22 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let height = 0;
 | 
			
		||||
 | 
			
		||||
        for(let i =  0; i < iconSizes.length; i++) {
 | 
			
		||||
                this._iconSize = iconSizes[i];
 | 
			
		||||
                height = iconSizes[i] + iconSpacing;
 | 
			
		||||
                let w = height * this._items.length + totalSpacing;
 | 
			
		||||
                if (w <= availWidth)
 | 
			
		||||
                        break;
 | 
			
		||||
        }
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        let iconSizes = baseIconSizes.map(function(s) {
 | 
			
		||||
            return s * scaleFactor;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (this._items.length == 1) {
 | 
			
		||||
            this._iconSize = iconSizes[0];
 | 
			
		||||
            height = iconSizes[0] + iconSpacing;
 | 
			
		||||
            this._iconSize = baseIconSizes[0];
 | 
			
		||||
        } else {
 | 
			
		||||
            for(let i =  0; i < baseIconSizes.length; i++) {
 | 
			
		||||
                this._iconSize = baseIconSizes[i];
 | 
			
		||||
                let height = iconSizes[i] + iconSpacing;
 | 
			
		||||
                let w = height * this._items.length + totalSpacing;
 | 
			
		||||
                if (w <= availWidth)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(let i = 0; i < this.icons.length; i++) {
 | 
			
		||||
@@ -499,9 +528,11 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
                break;
 | 
			
		||||
            this.icons[i].set_size(this._iconSize);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = height;
 | 
			
		||||
        alloc.natural_size = height;
 | 
			
		||||
    _getPreferredHeight: function (actor, forWidth, alloc) {
 | 
			
		||||
        this._setIconSize();
 | 
			
		||||
        this.parent(actor, forWidth, alloc);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (actor, box, flags) {
 | 
			
		||||
@@ -533,8 +564,9 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
                                                        Lang.bind(this, function () {
 | 
			
		||||
                                                                            this._enterItem(index);
 | 
			
		||||
                                                                            this._mouseTimeOutId = 0;
 | 
			
		||||
                                                                            return false;
 | 
			
		||||
                                                                            return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                        }));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem');
 | 
			
		||||
        } else
 | 
			
		||||
           this._itemEntered(index);
 | 
			
		||||
    },
 | 
			
		||||
@@ -634,17 +666,19 @@ const ThumbnailList = new Lang.Class({
 | 
			
		||||
        totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding();
 | 
			
		||||
        let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
 | 
			
		||||
        let spacing = this._items[0].child.get_theme_node().get_length('spacing');
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
 | 
			
		||||
 | 
			
		||||
        availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE);
 | 
			
		||||
        availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
 | 
			
		||||
        let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing;
 | 
			
		||||
        binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight);
 | 
			
		||||
        binHeight = Math.min(thumbnailSize, binHeight);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._thumbnailBins.length; i++) {
 | 
			
		||||
            let mutterWindow = this._windows[i].get_compositor_private();
 | 
			
		||||
            if (!mutterWindow)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let clone = _createWindowClone(mutterWindow, THUMBNAIL_DEFAULT_SIZE);
 | 
			
		||||
            let clone = _createWindowClone(mutterWindow, thumbnailSize);
 | 
			
		||||
            this._thumbnailBins[i].set_height(binHeight);
 | 
			
		||||
            this._thumbnailBins[i].add_actor(clone);
 | 
			
		||||
            this._clones.push(clone);
 | 
			
		||||
@@ -658,7 +692,7 @@ const ThumbnailList = new Lang.Class({
 | 
			
		||||
const WindowIcon = new Lang.Class({
 | 
			
		||||
    Name: 'WindowIcon',
 | 
			
		||||
 | 
			
		||||
    _init: function(window) {
 | 
			
		||||
    _init: function(window, mode) {
 | 
			
		||||
        this.window = window;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
 | 
			
		||||
@@ -676,8 +710,7 @@ const WindowIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._icon.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
        switch (settings.get_enum('app-icon-mode')) {
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
            case AppIconMode.THUMBNAIL_ONLY:
 | 
			
		||||
                size = WINDOW_PREVIEW_SIZE;
 | 
			
		||||
                this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
 | 
			
		||||
@@ -715,7 +748,7 @@ const WindowList = new Lang.Class({
 | 
			
		||||
    Name: 'WindowList',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherList,
 | 
			
		||||
 | 
			
		||||
    _init : function(windows) {
 | 
			
		||||
    _init : function(windows, mode) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
@@ -727,7 +760,7 @@ const WindowList = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            let win = windows[i];
 | 
			
		||||
            let icon = new WindowIcon(win);
 | 
			
		||||
            let icon = new WindowIcon(win, mode);
 | 
			
		||||
 | 
			
		||||
            this.addItem(icon.actor, icon.label);
 | 
			
		||||
            this.icons.push(icon);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -20,7 +21,9 @@ const Animation = new Lang.Class({
 | 
			
		||||
        this._isPlaying = false;
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
        this._frame = 0;
 | 
			
		||||
        this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
 | 
			
		||||
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height, scaleFactor,
 | 
			
		||||
                                                                            Lang.bind(this, this._animationsLoaded));
 | 
			
		||||
        this.actor.set_child(this._animations);
 | 
			
		||||
    },
 | 
			
		||||
@@ -31,6 +34,7 @@ const Animation = new Lang.Class({
 | 
			
		||||
                this._showFrame(0);
 | 
			
		||||
 | 
			
		||||
            this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isPlaying = true;
 | 
			
		||||
@@ -59,7 +63,7 @@ const Animation = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._showFrame(this._frame + 1);
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animationsLoaded: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1329
									
								
								js/ui/appDisplay.js
									
									
									
									
									
								
							
							
						
						@@ -14,15 +14,15 @@ const AppFavorites = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._favorites = {};
 | 
			
		||||
        global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
 | 
			
		||||
        this._reload();
 | 
			
		||||
        this.reload();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFavsChanged: function() {
 | 
			
		||||
        this._reload();
 | 
			
		||||
        this.reload();
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reload: function() {
 | 
			
		||||
    reload: function() {
 | 
			
		||||
        let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        let apps = ids.map(function (id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -50,11 +50,9 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE });
 | 
			
		||||
 | 
			
		||||
        let content = null;
 | 
			
		||||
 | 
			
		||||
        let candidateContent = null;
 | 
			
		||||
        for (let i = 0; i < this._patterns.length; i++) {
 | 
			
		||||
            if (!this._patterns[i])
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (this._patterns[i].get_shading() != params.shadingType)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +86,6 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._patterns.push(content);
 | 
			
		||||
 | 
			
		||||
        return content;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -116,9 +113,9 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _removeContent: function(contentList, content) {
 | 
			
		||||
        let index = contentList.indexOf(content);
 | 
			
		||||
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            contentList.splice(index, 1);
 | 
			
		||||
        if (index < 0)
 | 
			
		||||
            throw new Error("Trying to remove invalid content: " + content);
 | 
			
		||||
        contentList.splice(index, 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removePatternContent: function(content) {
 | 
			
		||||
@@ -128,12 +125,32 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
    removeImageContent: function(content) {
 | 
			
		||||
        let filename = content.get_filename();
 | 
			
		||||
 | 
			
		||||
        if (filename && this._fileMonitors[filename])
 | 
			
		||||
        let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); });
 | 
			
		||||
        if (!hasOtherUsers)
 | 
			
		||||
            delete this._fileMonitors[filename];
 | 
			
		||||
 | 
			
		||||
        this._removeContent(this._images, content);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _attachCallerToFileLoad: function(caller, fileLoad) {
 | 
			
		||||
        fileLoad.callers.push(caller);
 | 
			
		||||
 | 
			
		||||
        if (!caller.cancellable)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        caller.cancellable.connect(Lang.bind(this, function() {
 | 
			
		||||
            let idx = fileLoad.callers.indexOf(caller);
 | 
			
		||||
            fileLoad.callers.splice(idx, 1);
 | 
			
		||||
 | 
			
		||||
            if (fileLoad.callers.length == 0) {
 | 
			
		||||
                fileLoad.cancellable.cancel();
 | 
			
		||||
 | 
			
		||||
                let idx = this._pendingFileLoads.indexOf(fileLoad);
 | 
			
		||||
                this._pendingFileLoads.splice(idx, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadImageContent: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        style: null,
 | 
			
		||||
@@ -142,27 +159,28 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                                        cancellable: null,
 | 
			
		||||
                                        onFinished: null });
 | 
			
		||||
 | 
			
		||||
        let caller = { monitorIndex: params.monitorIndex,
 | 
			
		||||
                       effects: params.effects,
 | 
			
		||||
                       cancellable: params.cancellable,
 | 
			
		||||
                       onFinished: params.onFinished };
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._pendingFileLoads.length; i++) {
 | 
			
		||||
            if (this._pendingFileLoads[i].filename == params.filename &&
 | 
			
		||||
                this._pendingFileLoads[i].style == params.style) {
 | 
			
		||||
                this._pendingFileLoads[i].callers.push({ shouldCopy: true,
 | 
			
		||||
                                                         monitorIndex: params.monitorIndex,
 | 
			
		||||
                                                         effects: params.effects,
 | 
			
		||||
                                                         onFinished: params.onFinished });
 | 
			
		||||
            let fileLoad = this._pendingFileLoads[i];
 | 
			
		||||
 | 
			
		||||
            if (fileLoad.filename == params.filename &&
 | 
			
		||||
                fileLoad.style == params.style) {
 | 
			
		||||
                this._attachCallerToFileLoad(caller, fileLoad);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._pendingFileLoads.push({ filename: params.filename,
 | 
			
		||||
                                      style: params.style,
 | 
			
		||||
                                      callers: [{ shouldCopy: false,
 | 
			
		||||
                                                  monitorIndex: params.monitorIndex,
 | 
			
		||||
                                                  effects: params.effects,
 | 
			
		||||
                                                  onFinished: params.onFinished }] });
 | 
			
		||||
        let fileLoad = { filename: params.filename,
 | 
			
		||||
                         style: params.style,
 | 
			
		||||
                         cancellable: new Gio.Cancellable(),
 | 
			
		||||
                         callers: [] };
 | 
			
		||||
        this._attachCallerToFileLoad(caller, fileLoad);
 | 
			
		||||
 | 
			
		||||
        let content = new Meta.Background({ meta_screen: global.screen,
 | 
			
		||||
                                            monitor: params.monitorIndex,
 | 
			
		||||
                                            effects: params.effects });
 | 
			
		||||
        let content = new Meta.Background({ meta_screen: global.screen });
 | 
			
		||||
 | 
			
		||||
        content.load_file_async(params.filename,
 | 
			
		||||
                                params.style,
 | 
			
		||||
@@ -173,31 +191,26 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                                                  content.load_file_finish(result);
 | 
			
		||||
 | 
			
		||||
                                                  this._monitorFile(params.filename);
 | 
			
		||||
                                                  this._images.push(content);
 | 
			
		||||
                                              } catch(e) {
 | 
			
		||||
                                                  content = null;
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              for (let i = 0; i < this._pendingFileLoads.length; i++) {
 | 
			
		||||
                                                  let pendingLoad = this._pendingFileLoads[i];
 | 
			
		||||
                                                  if (pendingLoad.filename != params.filename ||
 | 
			
		||||
                                                      pendingLoad.style != params.style)
 | 
			
		||||
                                                      continue;
 | 
			
		||||
                                              for (let i = 0; i < fileLoad.callers.length; i++) {
 | 
			
		||||
                                                  let caller = fileLoad.callers[i];
 | 
			
		||||
                                                  if (caller.onFinished) {
 | 
			
		||||
                                                      let newContent;
 | 
			
		||||
 | 
			
		||||
                                                  for (let j = 0; j < pendingLoad.callers.length; j++) {
 | 
			
		||||
                                                      if (pendingLoad.callers[j].onFinished) {
 | 
			
		||||
                                                          if (content && pendingLoad.callers[j].shouldCopy) {
 | 
			
		||||
                                                              content = object.copy(pendingLoad.callers[j].monitorIndex,
 | 
			
		||||
                                                                                    pendingLoad.callers[j].effects);
 | 
			
		||||
 | 
			
		||||
                                                          }
 | 
			
		||||
 | 
			
		||||
                                                          pendingLoad.callers[j].onFinished(content);
 | 
			
		||||
                                                      if (content) {
 | 
			
		||||
                                                          newContent = content.copy(caller.monitorIndex, caller.effects);
 | 
			
		||||
                                                          this._images.push(newContent);
 | 
			
		||||
                                                      }
 | 
			
		||||
                                                  }
 | 
			
		||||
 | 
			
		||||
                                                  this._pendingFileLoads.splice(i, 1);
 | 
			
		||||
                                                      caller.onFinished(newContent);
 | 
			
		||||
                                                  }
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              let idx = this._pendingFileLoads.indexOf(fileLoad);
 | 
			
		||||
                                              this._pendingFileLoads.splice(idx, 1);
 | 
			
		||||
                                          }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -210,11 +223,9 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                                        onFinished: null });
 | 
			
		||||
 | 
			
		||||
        let content = null;
 | 
			
		||||
 | 
			
		||||
        let candidateContent = null;
 | 
			
		||||
        for (let i = 0; i < this._images.length; i++) {
 | 
			
		||||
            if (!this._images[i])
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (this._images[i].get_style() != params.style)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
@@ -222,7 +233,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
 | 
			
		||||
                this._images[i].monitor_index != this._monitorIndex)
 | 
			
		||||
                this._images[i].monitor != params.monitorIndex)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            candidateContent = this._images[i];
 | 
			
		||||
@@ -260,9 +271,11 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._animationFilename == params.filename) {
 | 
			
		||||
            if (params.onLoaded) {
 | 
			
		||||
                GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                    params.onLoaded(this._animation);
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }));
 | 
			
		||||
                GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -274,9 +287,11 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                           this._animation = animation;
 | 
			
		||||
 | 
			
		||||
                           if (params.onLoaded) {
 | 
			
		||||
                               GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                               let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                                   params.onLoaded(this._animation);
 | 
			
		||||
                                   return GLib.SOURCE_REMOVE;
 | 
			
		||||
                               }));
 | 
			
		||||
                               GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
 | 
			
		||||
                           }
 | 
			
		||||
                       }));
 | 
			
		||||
    }
 | 
			
		||||
@@ -295,14 +310,15 @@ const Background = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        layoutManager: Main.layoutManager,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE });
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        settings: null });
 | 
			
		||||
        this.actor = new Meta.BackgroundGroup();
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._destroySignalId = this.actor.connect('destroy',
 | 
			
		||||
                                                   Lang.bind(this, this._destroy));
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
 | 
			
		||||
        this._settings = params.settings;
 | 
			
		||||
        this._monitorIndex = params.monitorIndex;
 | 
			
		||||
        this._layoutManager = params.layoutManager;
 | 
			
		||||
        this._effects = params.effects;
 | 
			
		||||
@@ -314,13 +330,12 @@ const Background = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._brightness = 1.0;
 | 
			
		||||
        this._vignetteSharpness = 0.2;
 | 
			
		||||
        this._saturation = 1.0;
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
        this.isLoaded = false;
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
                                   this.emit('changed');
 | 
			
		||||
                               }));
 | 
			
		||||
        this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
                                            this.emit('changed');
 | 
			
		||||
                                        }));
 | 
			
		||||
 | 
			
		||||
        this._load();
 | 
			
		||||
    },
 | 
			
		||||
@@ -361,6 +376,10 @@ const Background = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.disconnect(this._destroySignalId);
 | 
			
		||||
        this._destroySignalId = 0;
 | 
			
		||||
 | 
			
		||||
        if (this._settingsChangedSignalId != 0)
 | 
			
		||||
            this._settings.disconnect(this._settingsChangedSignalId);
 | 
			
		||||
        this._settingsChangedSignalId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setLoaded: function() {
 | 
			
		||||
@@ -369,10 +388,11 @@ const Background = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.isLoaded = true;
 | 
			
		||||
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
        let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
            this.emit('loaded');
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        }));
 | 
			
		||||
        GLib.Source.set_name_by_id(id, '[gnome-shell] this.emit');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadPattern: function() {
 | 
			
		||||
@@ -410,29 +430,26 @@ const Background = new Lang.Class({
 | 
			
		||||
        this._fileWatches[filename] = signalId;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addImage: function(content, index, filename) {
 | 
			
		||||
        content.saturation = this._saturation;
 | 
			
		||||
        content.brightness = this._brightness;
 | 
			
		||||
        content.vignette_sharpness = this._vignetteSharpness;
 | 
			
		||||
    _ensureImage: function(index) {
 | 
			
		||||
        if (this._images[index])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let actor = new Meta.BackgroundActor();
 | 
			
		||||
        actor.content = content;
 | 
			
		||||
 | 
			
		||||
        // The background pattern is the first actor in
 | 
			
		||||
        // the group, and all images should be above that.
 | 
			
		||||
        this.actor.insert_child_at_index(actor, index + 1);
 | 
			
		||||
 | 
			
		||||
        this._images[index] = actor;
 | 
			
		||||
        this._watchCacheFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateImage: function(content, index, filename) {
 | 
			
		||||
        content.saturation = this._saturation;
 | 
			
		||||
    _updateImage: function(index, content, filename) {
 | 
			
		||||
        content.brightness = this._brightness;
 | 
			
		||||
        content.vignette_sharpness = this._vignetteSharpness;
 | 
			
		||||
 | 
			
		||||
        this._cache.removeImageContent(this._images[index].content);
 | 
			
		||||
        this._images[index].content = content;
 | 
			
		||||
        let image = this._images[index];
 | 
			
		||||
        if (image.content)
 | 
			
		||||
            this._cache.removeImageContent(content);
 | 
			
		||||
        image.content = content;
 | 
			
		||||
        this._watchCacheFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -480,11 +497,8 @@ const Background = new Lang.Class({
 | 
			
		||||
                                                  return;
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              if (!this._images[i]) {
 | 
			
		||||
                                                  this._addImage(content, i, files[i]);
 | 
			
		||||
                                              } else {
 | 
			
		||||
                                                  this._updateImage(content, i, files[i]);
 | 
			
		||||
                                              }
 | 
			
		||||
                                              this._ensureImage(i);
 | 
			
		||||
                                              this._updateImage(i, content, files[i]);
 | 
			
		||||
 | 
			
		||||
                                              if (numPendingImages == 0) {
 | 
			
		||||
                                                  this._setLoaded();
 | 
			
		||||
@@ -519,8 +533,9 @@ const Background = new Lang.Class({
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                                    this._updateAnimationTimeoutId = 0;
 | 
			
		||||
                                                                    this._updateAnimation();
 | 
			
		||||
                                                                    return false;
 | 
			
		||||
                                                                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                                }));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._updateAnimationTimeoutId, '[gnome-shell] this._updateAnimation');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadAnimation: function(filename) {
 | 
			
		||||
@@ -539,30 +554,33 @@ const Background = new Lang.Class({
 | 
			
		||||
                                           });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadFile: function(filename) {
 | 
			
		||||
    _loadImage: function(filename) {
 | 
			
		||||
        this._cache.getImageContent({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                      effects: this._effects,
 | 
			
		||||
                                      style: this._style,
 | 
			
		||||
                                      filename: filename,
 | 
			
		||||
                                      cancellable: this._cancellable,
 | 
			
		||||
                                      onFinished: Lang.bind(this, function(content) {
 | 
			
		||||
                                          if (!content) {
 | 
			
		||||
                                              if (!this._cancellable.is_cancelled())
 | 
			
		||||
                                                  this._loadAnimation(filename);
 | 
			
		||||
                                              return;
 | 
			
		||||
                                          if (content) {
 | 
			
		||||
                                              this._ensureImage(0);
 | 
			
		||||
                                              this._updateImage(0, content, filename);
 | 
			
		||||
                                          }
 | 
			
		||||
 | 
			
		||||
                                          this._addImage(content, 0, filename);
 | 
			
		||||
                                          this._setLoaded();
 | 
			
		||||
                                      })
 | 
			
		||||
                                    });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadFile: function(filename) {
 | 
			
		||||
        if (filename.endsWith('.xml'))
 | 
			
		||||
            this._loadAnimation(filename);
 | 
			
		||||
        else
 | 
			
		||||
            this._loadImage(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _load: function () {
 | 
			
		||||
        this._cache = getBackgroundCache();
 | 
			
		||||
 | 
			
		||||
        this._loadPattern(this._cache);
 | 
			
		||||
        this._loadPattern();
 | 
			
		||||
 | 
			
		||||
        this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
 | 
			
		||||
        if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
 | 
			
		||||
@@ -585,24 +603,6 @@ const Background = new Lang.Class({
 | 
			
		||||
        this._loadFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get saturation() {
 | 
			
		||||
        return this._saturation;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set saturation(saturation) {
 | 
			
		||||
        this._saturation = saturation;
 | 
			
		||||
 | 
			
		||||
        if (this._pattern && this._pattern.content)
 | 
			
		||||
            this._pattern.content.saturation = saturation;
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(this._images);
 | 
			
		||||
        for (let i = 0; i < keys.length; i++) {
 | 
			
		||||
            let image = this._images[keys[i]];
 | 
			
		||||
            if (image && image.content)
 | 
			
		||||
                image.content.saturation = saturation;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get brightness() {
 | 
			
		||||
        return this._brightness;
 | 
			
		||||
    },
 | 
			
		||||
@@ -654,7 +654,13 @@ const SystemBackground = new Lang.Class({
 | 
			
		||||
                                          this.emit('loaded');
 | 
			
		||||
                                      })
 | 
			
		||||
                                    });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this._cache.removeImageContent(this.actor.content);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SystemBackground.prototype);
 | 
			
		||||
 | 
			
		||||
@@ -716,8 +722,10 @@ const BackgroundManager = new Lang.Class({
 | 
			
		||||
                                        layoutManager: Main.layoutManager,
 | 
			
		||||
                                        monitorIndex: null,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        controlPosition: true });
 | 
			
		||||
                                        controlPosition: true,
 | 
			
		||||
                                        settingsSchema: BACKGROUND_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: params.settingsSchema });
 | 
			
		||||
        this._container = params.container;
 | 
			
		||||
        this._layoutManager = params.layoutManager;
 | 
			
		||||
        this._effects = params.effects;
 | 
			
		||||
@@ -740,30 +748,31 @@ const BackgroundManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBackground: function(background, monitorIndex) {
 | 
			
		||||
        let newBackground = this._createBackground(monitorIndex);
 | 
			
		||||
        newBackground.vignetteSharpness = background.vignetteSharpness;
 | 
			
		||||
        newBackground.brightness = background.brightness;
 | 
			
		||||
        newBackground.saturation = background.saturation;
 | 
			
		||||
        newBackground.visible = background.visible;
 | 
			
		||||
    _updateBackground: function() {
 | 
			
		||||
        let newBackground = this._createBackground();
 | 
			
		||||
        newBackground.vignetteSharpness = this.background.vignetteSharpness;
 | 
			
		||||
        newBackground.brightness = this.background.brightness;
 | 
			
		||||
        newBackground.visible = this.background.visible;
 | 
			
		||||
 | 
			
		||||
        newBackground.loadedSignalId = newBackground.connect('loaded',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                newBackground.disconnect(newBackground.loadedSignalId);
 | 
			
		||||
                newBackground.loadedSignalId = 0;
 | 
			
		||||
                Tweener.addTween(background.actor,
 | 
			
		||||
 | 
			
		||||
                if (this._newBackground != newBackground) {
 | 
			
		||||
                    /* Not interesting, we queued another load */
 | 
			
		||||
                    newBackground.actor.destroy();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Tweener.addTween(this.background.actor,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: FADE_ANIMATION_TIME,
 | 
			
		||||
                                   transition: 'easeOutQuad',
 | 
			
		||||
                                   onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                       if (this._newBackground == newBackground) {
 | 
			
		||||
                                           this.background = newBackground;
 | 
			
		||||
                                           this._newBackground = null;
 | 
			
		||||
                                       } else {
 | 
			
		||||
                                           newBackground.actor.destroy();
 | 
			
		||||
                                       }
 | 
			
		||||
 | 
			
		||||
                                       background.actor.destroy();
 | 
			
		||||
                                       this.background.actor.destroy();
 | 
			
		||||
                                       this.background = newBackground;
 | 
			
		||||
                                       this._newBackground = null;
 | 
			
		||||
 | 
			
		||||
                                       this.emit('changed');
 | 
			
		||||
                                   })
 | 
			
		||||
@@ -776,7 +785,8 @@ const BackgroundManager = new Lang.Class({
 | 
			
		||||
    _createBackground: function() {
 | 
			
		||||
        let background = new Background({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                          layoutManager: this._layoutManager,
 | 
			
		||||
                                          effects: this._effects });
 | 
			
		||||
                                          effects: this._effects,
 | 
			
		||||
                                          settings: this._settings });
 | 
			
		||||
        this._container.add_child(background.actor);
 | 
			
		||||
 | 
			
		||||
        let monitor = this._layoutManager.monitors[this._monitorIndex];
 | 
			
		||||
@@ -790,7 +800,7 @@ const BackgroundManager = new Lang.Class({
 | 
			
		||||
        background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
            background.disconnect(background.changeSignalId);
 | 
			
		||||
            background.changeSignalId = 0;
 | 
			
		||||
            this._updateBackground(background, this._monitorIndex);
 | 
			
		||||
            this._updateBackground();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        background.actor.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,8 @@ const BackgroundMenu = new Lang.Class({
 | 
			
		||||
    Name: 'BackgroundMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        this.parent(source, 0, St.Side.TOP);
 | 
			
		||||
    _init: function(layoutManager) {
 | 
			
		||||
        this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
 | 
			
		||||
 | 
			
		||||
        this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
 | 
			
		||||
        this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
@@ -22,23 +22,20 @@ const BackgroundMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.add_style_class_name('background-menu');
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        layoutManager.uiGroup.add_actor(this.actor);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function addBackgroundMenu(actor) {
 | 
			
		||||
    let cursor = new St.Bin({ opacity: 0 });
 | 
			
		||||
    Main.uiGroup.add_actor(cursor);
 | 
			
		||||
 | 
			
		||||
function addBackgroundMenu(actor, layoutManager) {
 | 
			
		||||
    actor.reactive = true;
 | 
			
		||||
    actor._backgroundMenu = new BackgroundMenu(cursor);
 | 
			
		||||
    actor._backgroundMenu = new BackgroundMenu(layoutManager);
 | 
			
		||||
    actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
 | 
			
		||||
    actor._backgroundManager.addMenu(actor._backgroundMenu);
 | 
			
		||||
 | 
			
		||||
    function openMenu() {
 | 
			
		||||
        let [x, y] = global.get_pointer();
 | 
			
		||||
        cursor.set_position(x, y);
 | 
			
		||||
        Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
 | 
			
		||||
        actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -59,10 +56,8 @@ function addBackgroundMenu(actor) {
 | 
			
		||||
    actor.add_action(clickAction);
 | 
			
		||||
 | 
			
		||||
    actor.connect('destroy', function() {
 | 
			
		||||
                      actor._backgroundMenu.destroy();
 | 
			
		||||
                      actor._backgroundMenu = null;
 | 
			
		||||
                      actor._backgroundManager = null;
 | 
			
		||||
 | 
			
		||||
                      cursor.destroy();
 | 
			
		||||
                  });
 | 
			
		||||
        actor._backgroundMenu.destroy();
 | 
			
		||||
        actor._backgroundMenu = null;
 | 
			
		||||
        actor._backgroundManager = null;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,9 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -61,10 +62,14 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this._muteInput();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get arrowSide() {
 | 
			
		||||
        return this._arrowSide;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _muteInput: function() {
 | 
			
		||||
        if (this._capturedEventId == 0)
 | 
			
		||||
            this._capturedEventId = this.actor.connect('captured-event',
 | 
			
		||||
                                                       function() { return true; });
 | 
			
		||||
                                                       function() { return Clutter.EVENT_STOP; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unmuteInput: function() {
 | 
			
		||||
@@ -116,6 +121,9 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function(animate, onComplete) {
 | 
			
		||||
        if (!this.actor.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let xOffset = 0;
 | 
			
		||||
        let yOffset = 0;
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
@@ -180,7 +188,9 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let borderWidth = themeNode.get_length('-arrow-border-width');
 | 
			
		||||
        let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        this._adjustAllocationForArrow(false, alloc);
 | 
			
		||||
@@ -277,38 +287,40 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        let skipBottomLeft = false;
 | 
			
		||||
        let skipBottomRight = false;
 | 
			
		||||
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
            if (this._arrowOrigin == x1)
 | 
			
		||||
                skipTopLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == x2)
 | 
			
		||||
                skipTopRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
        if (rise) {
 | 
			
		||||
            switch (this._arrowSide) {
 | 
			
		||||
            case St.Side.TOP:
 | 
			
		||||
                if (this._arrowOrigin == x1)
 | 
			
		||||
                    skipTopLeft = true;
 | 
			
		||||
                else if (this._arrowOrigin == x2)
 | 
			
		||||
                    skipTopRight = true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            if (this._arrowOrigin == y1)
 | 
			
		||||
                skipTopRight = true;
 | 
			
		||||
            else if (this._arrowOrigin == y2)
 | 
			
		||||
                skipBottomRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
            case St.Side.RIGHT:
 | 
			
		||||
                if (this._arrowOrigin == y1)
 | 
			
		||||
                    skipTopRight = true;
 | 
			
		||||
                else if (this._arrowOrigin == y2)
 | 
			
		||||
                    skipBottomRight = true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            if (this._arrowOrigin == x1)
 | 
			
		||||
                skipBottomLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == x2)
 | 
			
		||||
                skipBottomRight = true;
 | 
			
		||||
            break;
 | 
			
		||||
            case St.Side.BOTTOM:
 | 
			
		||||
                if (this._arrowOrigin == x1)
 | 
			
		||||
                    skipBottomLeft = true;
 | 
			
		||||
                else if (this._arrowOrigin == x2)
 | 
			
		||||
                    skipBottomRight = true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
            if (this._arrowOrigin == y1)
 | 
			
		||||
                skipTopLeft = true;
 | 
			
		||||
            else if (this._arrowOrigin == y2)
 | 
			
		||||
                skipBottomLeft = true;
 | 
			
		||||
            break;
 | 
			
		||||
            case St.Side.LEFT:
 | 
			
		||||
                if (this._arrowOrigin == y1)
 | 
			
		||||
                    skipTopLeft = true;
 | 
			
		||||
                else if (this._arrowOrigin == y2)
 | 
			
		||||
                    skipBottomLeft = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cr.moveTo(x1 + borderRadius, y1);
 | 
			
		||||
        if (this._arrowSide == St.Side.TOP) {
 | 
			
		||||
        if (this._arrowSide == St.Side.TOP && rise) {
 | 
			
		||||
            if (skipTopLeft) {
 | 
			
		||||
                cr.moveTo(x1, y2 - borderRadius);
 | 
			
		||||
                cr.lineTo(x1, y1 - rise);
 | 
			
		||||
@@ -330,7 +342,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                   3*Math.PI/2, Math.PI*2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.RIGHT) {
 | 
			
		||||
        if (this._arrowSide == St.Side.RIGHT && rise) {
 | 
			
		||||
            if (skipTopRight) {
 | 
			
		||||
                cr.lineTo(x2 + rise, y1);
 | 
			
		||||
                cr.lineTo(x2 + rise, y1 + halfBase);
 | 
			
		||||
@@ -351,7 +363,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                   0, Math.PI/2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.BOTTOM) {
 | 
			
		||||
        if (this._arrowSide == St.Side.BOTTOM && rise) {
 | 
			
		||||
            if (skipBottomLeft) {
 | 
			
		||||
                cr.lineTo(x1 + halfBase, y2);
 | 
			
		||||
                cr.lineTo(x1, y2 + rise);
 | 
			
		||||
@@ -372,7 +384,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                   Math.PI/2, Math.PI);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.LEFT) {
 | 
			
		||||
        if (this._arrowSide == St.Side.LEFT && rise) {
 | 
			
		||||
            if (skipTopLeft) {
 | 
			
		||||
                cr.lineTo(x1, y1 + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, y1);
 | 
			
		||||
@@ -612,6 +624,8 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                this._container.queue_relayout();
 | 
			
		||||
                return false;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this.emit('arrow-side-changed');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -639,5 +653,21 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    get opacity() {
 | 
			
		||||
        return this.actor.opacity;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateArrowSide: function(side) {
 | 
			
		||||
        this._arrowSide = side;
 | 
			
		||||
        this._border.queue_repaint();
 | 
			
		||||
 | 
			
		||||
        this.emit('arrow-side-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPadding: function(side) {
 | 
			
		||||
        return this.bin.get_theme_node().get_padding(side);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getArrowHeight: function() {
 | 
			
		||||
        return this.actor.get_theme_node().get_length('-arrow-rise');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(BoxPointer.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -14,19 +14,24 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
 | 
			
		||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
 | 
			
		||||
 | 
			
		||||
// alias to prevent xgettext from picking up strings translated in GTK+
 | 
			
		||||
const gtk30_ = Gettext_gtk30.gettext;
 | 
			
		||||
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
function _sameDay(dateA, dateB) {
 | 
			
		||||
    return (dateA.getDate() == dateB.getDate() &&
 | 
			
		||||
            dateA.getMonth() == dateB.getMonth() &&
 | 
			
		||||
            dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameYear(dateA, dateB) {
 | 
			
		||||
    return (dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameMonth(dateA, dateB) {
 | 
			
		||||
    return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameDay(dateA, dateB) {
 | 
			
		||||
    return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: maybe needs config - right now we assume that Saturday and
 | 
			
		||||
 * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
 | 
			
		||||
 * Monday there)
 | 
			
		||||
@@ -190,16 +195,18 @@ const EmptyEventSource = new Lang.Class({
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(EmptyEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
 | 
			
		||||
<method name="GetEvents">
 | 
			
		||||
    <arg type="x" direction="in" />
 | 
			
		||||
    <arg type="x" direction="in" />
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
    <arg type="a(sssbxxa{sv})" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="HasCalendars" type="b" access="read" />
 | 
			
		||||
<signal name="Changed" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const CalendarServerIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.CalendarServer"> \
 | 
			
		||||
<method name="GetEvents"> \
 | 
			
		||||
    <arg type="x" direction="in" /> \
 | 
			
		||||
    <arg type="x" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="in" /> \
 | 
			
		||||
    <arg type="a(sssbxxa{sv})" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="HasCalendars" type="b" access="read" /> \
 | 
			
		||||
<signal name="Changed" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const CalendarServerInfo  = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
 | 
			
		||||
 | 
			
		||||
@@ -327,25 +334,22 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._curRequestBegin && this._curRequestEnd){
 | 
			
		||||
            let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
                callFlags = Gio.DBusCallFlags.NONE;
 | 
			
		||||
            this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
 | 
			
		||||
                                            this._curRequestEnd.getTime() / 1000,
 | 
			
		||||
                                            forceReload,
 | 
			
		||||
                                            Lang.bind(this, this._onEventsReceived),
 | 
			
		||||
                                            callFlags);
 | 
			
		||||
                                            Gio.DBusCallFlags.NONE);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end, forceReload) {
 | 
			
		||||
        if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
        if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
 | 
			
		||||
            this.isLoading = true;
 | 
			
		||||
            this._lastRequestBegin = begin;
 | 
			
		||||
            this._lastRequestEnd = end;
 | 
			
		||||
            this._curRequestBegin = begin;
 | 
			
		||||
            this._curRequestEnd = end;
 | 
			
		||||
            this._loadEvents(forceReload);
 | 
			
		||||
            this._loadEvents(false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -386,7 +390,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Find the ordering for month/year in the calendar heading
 | 
			
		||||
        this._headerFormatWithoutYear = '%B';
 | 
			
		||||
        switch (Gettext_gtk30.gettext('calendar:MY')) {
 | 
			
		||||
        switch (gtk30_('calendar:MY')) {
 | 
			
		||||
        case 'calendar:MY':
 | 
			
		||||
            this._headerFormat = '%B %Y';
 | 
			
		||||
            break;
 | 
			
		||||
@@ -402,9 +406,11 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        // Start off with the current date
 | 
			
		||||
        this._selectedDate = new Date();
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Table({ homogeneous: false,
 | 
			
		||||
                                    style_class: 'calendar',
 | 
			
		||||
                                    reactive: true });
 | 
			
		||||
        this._shouldDateGrabFocus = false;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'calendar',
 | 
			
		||||
                                     layout_manager: new Clutter.TableLayout(),
 | 
			
		||||
                                     reactive: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('scroll-event',
 | 
			
		||||
                           Lang.bind(this, this._onScroll));
 | 
			
		||||
@@ -417,41 +423,45 @@ const Calendar = new Lang.Class({
 | 
			
		||||
    setEventSource: function(eventSource) {
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
            this._update(false);
 | 
			
		||||
            this._rebuildCalendar();
 | 
			
		||||
            this._update();
 | 
			
		||||
        }));
 | 
			
		||||
        this._update(true);
 | 
			
		||||
        this._rebuildCalendar();
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the calendar to show a specific date
 | 
			
		||||
    setDate: function(date, forceReload) {
 | 
			
		||||
        if (!_sameDay(date, this._selectedDate)) {
 | 
			
		||||
            this._selectedDate = date;
 | 
			
		||||
            this._update(forceReload);
 | 
			
		||||
            this.emit('selected-date-changed', new Date(this._selectedDate));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
                this._update(forceReload);
 | 
			
		||||
        }
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (_sameDay(date, this._selectedDate))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._selectedDate = date;
 | 
			
		||||
        this._update();
 | 
			
		||||
        this.emit('selected-date-changed', new Date(this._selectedDate));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildHeader: function() {
 | 
			
		||||
        let layout = this.actor.layout_manager;
 | 
			
		||||
        let offsetCols = this._useWeekdate ? 1 : 0;
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        // Top line of the calendar '<| September 2009 |>'
 | 
			
		||||
        this._topBox = new St.BoxLayout();
 | 
			
		||||
        this.actor.add(this._topBox,
 | 
			
		||||
                       { row: 0, col: 0, col_span: offsetCols + 7 });
 | 
			
		||||
        layout.pack(this._topBox, 0, 0);
 | 
			
		||||
        layout.set_span(this._topBox, offsetCols + 7, 1);
 | 
			
		||||
 | 
			
		||||
        this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
 | 
			
		||||
                                           accessible_name: _("Previous month"),
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._topBox.add(this._backButton);
 | 
			
		||||
        this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label',
 | 
			
		||||
                                         can_focus: true });
 | 
			
		||||
        this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
 | 
			
		||||
                                              accessible_name: _("Next month"),
 | 
			
		||||
                                              can_focus: true });
 | 
			
		||||
        this._topBox.add(this._forwardButton);
 | 
			
		||||
        this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
 | 
			
		||||
@@ -470,10 +480,8 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
 | 
			
		||||
            let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
 | 
			
		||||
                                       text: customDayAbbrev });
 | 
			
		||||
            this.actor.add(label,
 | 
			
		||||
                           { row: 1,
 | 
			
		||||
                             col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
 | 
			
		||||
                             x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            let col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
 | 
			
		||||
            layout.pack(label, col, 1);
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -492,6 +500,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            this._onNextMonthButtonClicked();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPrevMonthButtonClicked: function() {
 | 
			
		||||
@@ -515,7 +524,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._backButton.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
        this.setDate(newDate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNextMonthButtonClicked: function() {
 | 
			
		||||
@@ -539,28 +548,26 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._forwardButton.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
        this.setDate(newDate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSettingsChange: function() {
 | 
			
		||||
        this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
 | 
			
		||||
        this._buildHeader();
 | 
			
		||||
        this._update(false);
 | 
			
		||||
        this._rebuildCalendar();
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function(forceReload) {
 | 
			
		||||
    _rebuildCalendar: function() {
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
 | 
			
		||||
        if (_sameYear(this._selectedDate, now))
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
 | 
			
		||||
        else
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
 | 
			
		||||
 | 
			
		||||
        // Remove everything but the topBox and the weekday labels
 | 
			
		||||
        let children = this.actor.get_children();
 | 
			
		||||
        for (let i = this._firstDayIndex; i < children.length; i++)
 | 
			
		||||
            children[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._buttons = [];
 | 
			
		||||
 | 
			
		||||
        // Start at the beginning of the week before the start of the month
 | 
			
		||||
        //
 | 
			
		||||
        // We want to show always 6 weeks (to keep the calendar menu at the same
 | 
			
		||||
@@ -578,11 +585,13 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        // Actually computing the number of weeks is complex, but we know that the
 | 
			
		||||
        // problematic categories (2 and 4) always start on week start, and that
 | 
			
		||||
        // all months at the end have 6 weeks.
 | 
			
		||||
 | 
			
		||||
        let beginDate = new Date(this._selectedDate);
 | 
			
		||||
        beginDate.setDate(1);
 | 
			
		||||
        beginDate.setSeconds(0);
 | 
			
		||||
        beginDate.setHours(12);
 | 
			
		||||
 | 
			
		||||
        this._calendarBegin = new Date(beginDate);
 | 
			
		||||
 | 
			
		||||
        let year = beginDate.getYear();
 | 
			
		||||
 | 
			
		||||
        let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
 | 
			
		||||
@@ -591,6 +600,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
        let layout = this.actor.layout_manager;
 | 
			
		||||
        let iter = new Date(beginDate);
 | 
			
		||||
        let row = 2;
 | 
			
		||||
        // nRows here means 6 weeks + one header + one navbar
 | 
			
		||||
@@ -603,13 +613,10 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            if (this._eventSource.isDummy)
 | 
			
		||||
                button.reactive = false;
 | 
			
		||||
 | 
			
		||||
            let iterStr = iter.toUTCString();
 | 
			
		||||
            button._date = new Date(iter);
 | 
			
		||||
            button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                this._shouldDateGrabFocus = true;
 | 
			
		||||
 | 
			
		||||
                let newlySelectedDate = new Date(iterStr);
 | 
			
		||||
                this.setDate(newlySelectedDate, false);
 | 
			
		||||
 | 
			
		||||
                this.setDate(button._date);
 | 
			
		||||
                this._shouldDateGrabFocus = false;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
@@ -617,9 +624,9 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            let styleClass = 'calendar-day-base calendar-day';
 | 
			
		||||
 | 
			
		||||
            if (_isWorkDay(iter))
 | 
			
		||||
                styleClass += ' calendar-work-day'
 | 
			
		||||
                styleClass += ' calendar-work-day';
 | 
			
		||||
            else
 | 
			
		||||
                styleClass += ' calendar-nonwork-day'
 | 
			
		||||
                styleClass += ' calendar-nonwork-day';
 | 
			
		||||
 | 
			
		||||
            // Hack used in lieu of border-collapse - see gnome-shell.css
 | 
			
		||||
            if (row == 2)
 | 
			
		||||
@@ -636,26 +643,20 @@ const Calendar = new Lang.Class({
 | 
			
		||||
                styleClass += ' calendar-other-month-day';
 | 
			
		||||
 | 
			
		||||
            if (hasEvents)
 | 
			
		||||
                styleClass += ' calendar-day-with-events'
 | 
			
		||||
                styleClass += ' calendar-day-with-events';
 | 
			
		||||
 | 
			
		||||
            button.style_class = styleClass;
 | 
			
		||||
 | 
			
		||||
            let offsetCols = this._useWeekdate ? 1 : 0;
 | 
			
		||||
            this.actor.add(button,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
 | 
			
		||||
            let col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
 | 
			
		||||
            layout.pack(button, col, row);
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(this._selectedDate, iter)) {
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
 | 
			
		||||
                if (this._shouldDateGrabFocus)
 | 
			
		||||
                    button.grab_key_focus();
 | 
			
		||||
            }
 | 
			
		||||
            this._buttons.push(button);
 | 
			
		||||
 | 
			
		||||
            if (this._useWeekdate && iter.getDay() == 4) {
 | 
			
		||||
                let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
 | 
			
		||||
                                           style_class: 'calendar-day-base calendar-week-number'});
 | 
			
		||||
                this.actor.add(label,
 | 
			
		||||
                               { row: row, col: 0, y_align: St.Align.MIDDLE });
 | 
			
		||||
                layout.pack(label, 0, row);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
@@ -663,9 +664,32 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            if (iter.getDay() == this._weekStart)
 | 
			
		||||
                row++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Signal to the event source that we are interested in events
 | 
			
		||||
        // only from this date range
 | 
			
		||||
        this._eventSource.requestRange(beginDate, iter, forceReload);
 | 
			
		||||
        this._eventSource.requestRange(beginDate, iter);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
 | 
			
		||||
        if (_sameYear(this._selectedDate, now))
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
 | 
			
		||||
        else
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
 | 
			
		||||
 | 
			
		||||
        if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin))
 | 
			
		||||
            this._rebuildCalendar();
 | 
			
		||||
 | 
			
		||||
        this._buttons.forEach(Lang.bind(this, function(button) {
 | 
			
		||||
            if (_sameDay(button._date, this._selectedDate)) {
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
                if (this._shouldDateGrabFocus)
 | 
			
		||||
                    button.grab_key_focus();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                button.remove_style_pseudo_class('active');
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -675,7 +699,10 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    Name: 'EventsList',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.Table({ style_class: 'events-table' });
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'events-table',
 | 
			
		||||
                                     layout_manager: layout });
 | 
			
		||||
        layout.hookup_style(this.actor);
 | 
			
		||||
        this._date = new Date();
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
@@ -699,9 +726,11 @@ const EventsList = new Lang.Class({
 | 
			
		||||
        dayLabel.clutter_text.line_wrap = false;
 | 
			
		||||
        dayLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(dayLabel, { row: index, col: 0,
 | 
			
		||||
                                   x_expand: false, x_align: St.Align.END,
 | 
			
		||||
                                   y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        let layout = this.actor.layout_manager;
 | 
			
		||||
        layout.pack(dayLabel, 0, index);
 | 
			
		||||
        layout.child_set(dayLabel, { x_expand: false,
 | 
			
		||||
                                     x_align: Clutter.TableAlignment.END,
 | 
			
		||||
                                     y_align: Clutter.TableAlignment.START });
 | 
			
		||||
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let timeString = _formatEventTime(event, clockFormat);
 | 
			
		||||
@@ -710,18 +739,17 @@ const EventsList = new Lang.Class({
 | 
			
		||||
        timeLabel.clutter_text.line_wrap = false;
 | 
			
		||||
        timeLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(timeLabel, { row: index, col: 1,
 | 
			
		||||
                                    x_expand: false, x_align: St.Align.MIDDLE,
 | 
			
		||||
                                    y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        layout.pack(timeLabel, 1, index);
 | 
			
		||||
        layout.child_set(timeLabel, { x_expand: false,
 | 
			
		||||
                                      y_align: Clutter.TableAlignment.START });
 | 
			
		||||
 | 
			
		||||
        let titleLabel = new St.Label({ style_class: 'events-day-task',
 | 
			
		||||
                                        text: event.summary });
 | 
			
		||||
        titleLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        titleLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(titleLabel, { row: index, col: 2,
 | 
			
		||||
                                     x_expand: true, x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        layout.pack(titleLabel, 2, index);
 | 
			
		||||
        layout.child_set(titleLabel, { x_expand: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
@@ -730,13 +758,10 @@ const EventsList = new Lang.Class({
 | 
			
		||||
        if (events.length == 0 && !showNothingScheduled)
 | 
			
		||||
            return index;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
 | 
			
		||||
                       { row: index, col: 0, col_span: 3,
 | 
			
		||||
                         // In theory, x_expand should be true here, but x_expand
 | 
			
		||||
                         // is a property of the column for StTable, ie all day cells
 | 
			
		||||
                         // get it too
 | 
			
		||||
                         x_expand: false, x_align: St.Align.START,
 | 
			
		||||
                         y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        let label = new St.Label({ style_class: 'events-day-header', text: header });
 | 
			
		||||
        let layout = this.actor.layout_manager;
 | 
			
		||||
        layout.pack(label, 0, index);
 | 
			
		||||
        layout.child_set(label, { column_span: 3, x_expand: false });
 | 
			
		||||
        index++;
 | 
			
		||||
 | 
			
		||||
        for (let n = 0; n < events.length; n++) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,115 +1,40 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const CheckBoxContainer = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBoxContainer',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.connect('get-preferred-width',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this.actor.connect('allocate',
 | 
			
		||||
                           Lang.bind(this, this._allocate));
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let node = this.actor.get_theme_node();
 | 
			
		||||
                this._spacing = node.get_length('spacing');
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin();
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this.label = new St.Label();
 | 
			
		||||
        this.label.clutter_text.set_line_wrap(true);
 | 
			
		||||
        this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 | 
			
		||||
        this.actor.add_actor(this.label);
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = minWidth + this._spacing;
 | 
			
		||||
        alloc.natural_size = natWidth + this._spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        /* FIXME: StBoxlayout currently does not handle
 | 
			
		||||
           height-for-width children correctly, so hard-code
 | 
			
		||||
           two lines for the label until that problem is fixed.
 | 
			
		||||
 | 
			
		||||
           https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
 | 
			
		||||
/*
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(forWidth);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(forWidth);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
 | 
			
		||||
*/
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let [minBoxWidth, natBoxWidth] =
 | 
			
		||||
            this._box.get_preferred_width(-1);
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = box.x1;
 | 
			
		||||
        childBox.x2 = box.x1 + natBoxWidth;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + natBoxHeight;
 | 
			
		||||
        this._box.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + natBoxWidth + this._spacing;
 | 
			
		||||
        childBox.x2 = availWidth - childBox.x1;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y2;
 | 
			
		||||
        this.label.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const CheckBox = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBox',
 | 
			
		||||
 | 
			
		||||
    _init: function(label) {
 | 
			
		||||
        let container = new St.BoxLayout();
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'check-box',
 | 
			
		||||
                                     child: container,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this._container = new CheckBoxContainer();
 | 
			
		||||
        this.actor.set_child(this._container.actor);
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin();
 | 
			
		||||
        this._box.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
        container.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this._label.clutter_text.set_line_wrap(true);
 | 
			
		||||
        this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 | 
			
		||||
        container.add_actor(this._label);
 | 
			
		||||
 | 
			
		||||
        if (label)
 | 
			
		||||
            this.setLabel(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLabel: function(label) {
 | 
			
		||||
        this._container.label.set_text(label);
 | 
			
		||||
        this._label.set_text(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getLabelActor: function() {
 | 
			
		||||
        return this._container.label;
 | 
			
		||||
        return this._label;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
        this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
 | 
			
		||||
 | 
			
		||||
        this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._mountAllId, '[gnome-shell] this._startupMountAll');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
@@ -77,7 +78,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._mountAllId = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveConnected: function() {
 | 
			
		||||
@@ -234,10 +235,11 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allowAutorunExpire: function(volume) {
 | 
			
		||||
        Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
 | 
			
		||||
        let id = Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
 | 
			
		||||
            volume.allowAutorun = false;
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        });
 | 
			
		||||
        GLib.Source.set_name_by_id(id, '[gnome-shell] volume.allowAutorun');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
const Component = AutomountManager;
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        retval = app.launch(files, 
 | 
			
		||||
                            global.create_app_launch_context())
 | 
			
		||||
                            global.create_app_launch_context(0, -1))
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        log('Unable to launch the application ' + app.get_name()
 | 
			
		||||
            + ': ' + e.toString());
 | 
			
		||||
@@ -75,12 +75,14 @@ function startAppForMount(app, mount) {
 | 
			
		||||
 | 
			
		||||
/******************************************/
 | 
			
		||||
 | 
			
		||||
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
 | 
			
		||||
<method name="SniffURI">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const HotplugSnifferIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.HotplugSniffer"> \
 | 
			
		||||
<method name="SniffURI"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
 | 
			
		||||
function HotplugSniffer() {
 | 
			
		||||
@@ -168,17 +170,6 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
        this._transDispatcher = new AutorunTransientDispatcher(this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureResidentSource: function() {
 | 
			
		||||
        if (this._residentSource)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._residentSource = new AutorunResidentSource(this);
 | 
			
		||||
        let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            this._residentSource.disconnect(destroyId);
 | 
			
		||||
            this._residentSource = null;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        this._scanMounts();
 | 
			
		||||
 | 
			
		||||
@@ -187,17 +178,12 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        if (this._residentSource)
 | 
			
		||||
            this._residentSource.destroy();
 | 
			
		||||
        this._volumeMonitor.disconnect(this._mountAddedId);
 | 
			
		||||
        this._volumeMonitor.disconnect(this._mountRemovedId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _processMount: function(mount, hotplug) {
 | 
			
		||||
        let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
 | 
			
		||||
            this._ensureResidentSource();
 | 
			
		||||
            this._residentSource.addMount(mount, apps);
 | 
			
		||||
 | 
			
		||||
            if (hotplug)
 | 
			
		||||
                this._transDispatcher.addMount(mount, apps, contentTypes);
 | 
			
		||||
        }));
 | 
			
		||||
@@ -222,8 +208,6 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onMountRemoved: function(monitor, mount) {
 | 
			
		||||
        this._transDispatcher.removeMount(mount);
 | 
			
		||||
        if (this._residentSource)
 | 
			
		||||
            this._residentSource.removeMount(mount);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ejectMount: function(mount) {
 | 
			
		||||
@@ -286,153 +270,6 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AutorunResidentSource = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunResidentSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
 | 
			
		||||
    _init: function(manager) {
 | 
			
		||||
        this.parent(_("Removable Devices"), 'media-removable');
 | 
			
		||||
        this.resident = true;
 | 
			
		||||
 | 
			
		||||
        this._mounts = [];
 | 
			
		||||
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
        this._notification = new AutorunResidentNotification(this._manager, this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new MessageTray.NotificationPolicy({ showInLockScreen: false });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    buildRightClickMenu: function() {
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMount: function(mount, apps) {
 | 
			
		||||
        if (!shouldAutorunMount(mount, false))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let filtered = this._mounts.filter(function (element) {
 | 
			
		||||
            return (element.mount == mount);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (filtered.length != 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let element = { mount: mount, apps: apps };
 | 
			
		||||
        this._mounts.push(element);
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeMount: function(mount) {
 | 
			
		||||
        this._mounts =
 | 
			
		||||
            this._mounts.filter(function (element) {
 | 
			
		||||
                return (element.mount != mount);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        if (this._mounts.length == 0) {
 | 
			
		||||
            this._notification.destroy();
 | 
			
		||||
            this.destroy();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._notification.updateForMounts(this._mounts);
 | 
			
		||||
 | 
			
		||||
        // add ourselves as a source, and push the notification
 | 
			
		||||
        if (!Main.messageTray.contains(this)) {
 | 
			
		||||
            Main.messageTray.add(this);
 | 
			
		||||
            this.pushNotification(this._notification);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AutorunResidentNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunResidentNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(manager, source) {
 | 
			
		||||
        this.parent(source, source.title, null, { customContent: true });
 | 
			
		||||
 | 
			
		||||
        // set the notification as resident
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
 | 
			
		||||
        this.addActor(this._layout,
 | 
			
		||||
                      { x_expand: true,
 | 
			
		||||
                        x_fill: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateForMounts: function(mounts) {
 | 
			
		||||
        // remove all the layout content
 | 
			
		||||
        this._layout.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        for (let idx = 0; idx < mounts.length; idx++) {
 | 
			
		||||
            let element = mounts[idx];
 | 
			
		||||
 | 
			
		||||
            let actor = this._itemForMount(element.mount, element.apps);
 | 
			
		||||
            this._layout.add(actor, { x_fill: true,
 | 
			
		||||
                                      expand: true });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _itemForMount: function(mount, apps) {
 | 
			
		||||
        let item = new St.BoxLayout();
 | 
			
		||||
 | 
			
		||||
        // prepare the mount button content
 | 
			
		||||
        let mountLayout = new St.BoxLayout();
 | 
			
		||||
 | 
			
		||||
        let mountIcon = new St.Icon({ gicon: mount.get_icon(),
 | 
			
		||||
                                      style_class: 'hotplug-resident-mount-icon' });
 | 
			
		||||
        mountLayout.add_actor(mountIcon);
 | 
			
		||||
 | 
			
		||||
        let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
 | 
			
		||||
        let mountLabel =
 | 
			
		||||
            new St.Label({ text: mount.get_name(),
 | 
			
		||||
                           style_class: 'hotplug-resident-mount-label',
 | 
			
		||||
                           track_hover: true,
 | 
			
		||||
                           reactive: true });
 | 
			
		||||
        labelBin.add_actor(mountLabel);
 | 
			
		||||
        mountLayout.add_actor(labelBin);
 | 
			
		||||
 | 
			
		||||
        let mountButton = new St.Button({ child: mountLayout,
 | 
			
		||||
                                          x_align: St.Align.START,
 | 
			
		||||
                                          x_fill: true,
 | 
			
		||||
                                          style_class: 'hotplug-resident-mount',
 | 
			
		||||
                                          button_mask: St.ButtonMask.ONE });
 | 
			
		||||
        item.add(mountButton, { x_align: St.Align.START,
 | 
			
		||||
                                expand: true });
 | 
			
		||||
 | 
			
		||||
        let ejectIcon = 
 | 
			
		||||
            new St.Icon({ icon_name: 'media-eject-symbolic',
 | 
			
		||||
                          style_class: 'hotplug-resident-eject-icon' });
 | 
			
		||||
 | 
			
		||||
        let ejectButton =
 | 
			
		||||
            new St.Button({ style_class: 'hotplug-resident-eject-button',
 | 
			
		||||
                            button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                            child: ejectIcon });
 | 
			
		||||
        item.add(ejectButton, { x_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        // now connect signals
 | 
			
		||||
        mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            startAppForMount(apps[0], mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        ejectButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._manager.ejectMount(mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AutorunTransientDispatcher = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunTransientDispatcher',
 | 
			
		||||
 | 
			
		||||
@@ -557,12 +394,12 @@ const AutorunTransientNotification = new Lang.Class({
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(manager, source) {
 | 
			
		||||
        this.parent(source, source.title, null, { customContent: true });
 | 
			
		||||
        this.parent(source, source.title);
 | 
			
		||||
 | 
			
		||||
        this._manager = manager;
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
 | 
			
		||||
                                       vertical: true });
 | 
			
		||||
        this.addActor(this._box);
 | 
			
		||||
        this._bodyBin.child = this._box;
 | 
			
		||||
 | 
			
		||||
        this._mount = source.mount;
 | 
			
		||||
 | 
			
		||||
@@ -579,7 +416,6 @@ const AutorunTransientNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // set the notification to transient and urgent, so that it
 | 
			
		||||
        // expands out
 | 
			
		||||
        this.setTransient(true);
 | 
			
		||||
        this.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,6 @@ const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const CheckBox = imports.ui.checkBox;
 | 
			
		||||
 | 
			
		||||
let prompter = null;
 | 
			
		||||
 | 
			
		||||
const KeyringDialog = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
@@ -47,7 +45,9 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        this._messageBox.add(subject,
 | 
			
		||||
                             { y_fill:  false,
 | 
			
		||||
                             { x_fill: false,
 | 
			
		||||
                               y_fill:  false,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let description = new St.Label({ style_class: 'prompt-dialog-description' });
 | 
			
		||||
@@ -80,23 +80,26 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildControlTable: function() {
 | 
			
		||||
        let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
 | 
			
		||||
                                    layout_manager: layout });
 | 
			
		||||
        layout.hookup_style(table);
 | 
			
		||||
        let row = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.prompt.password_visible) {
 | 
			
		||||
            let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
 | 
			
		||||
            let label = new St.Label({ style_class: 'prompt-dialog-password-label' });
 | 
			
		||||
            label.set_text(_("Password:"));
 | 
			
		||||
            table.add(label, { row: row, col: 0,
 | 
			
		||||
                               x_expand: false, x_fill: true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
            layout.pack(label, 0, row);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                 text: '',
 | 
			
		||||
                                                 can_focus: true});
 | 
			
		||||
                                                 can_focus: true });
 | 
			
		||||
            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
 | 
			
		||||
            this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
 | 
			
		||||
            table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            layout.pack(this._passwordEntry, 1, row);
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._passwordEntry = null;
 | 
			
		||||
@@ -105,17 +108,16 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        if (this.prompt.confirm_visible) {
 | 
			
		||||
            var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
 | 
			
		||||
            label.set_text(_("Type again:"));
 | 
			
		||||
            table.add(label, { row: row, col: 0,
 | 
			
		||||
                               x_expand: false, x_fill: true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            layout.pack(label, 0, row);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                text: '',
 | 
			
		||||
                                                can_focus: true});
 | 
			
		||||
                                                can_focus: true });
 | 
			
		||||
            this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
 | 
			
		||||
            this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
 | 
			
		||||
            table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            layout.pack(this._confirmEntry, 1, row);
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._confirmEntry = null;
 | 
			
		||||
@@ -128,14 +130,15 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
            let choice = new CheckBox.CheckBox();
 | 
			
		||||
            this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
 | 
			
		||||
            table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            layout.pack(choice.actor, 1, row);
 | 
			
		||||
            row++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
 | 
			
		||||
        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        warning.clutter_text.line_wrap = true;
 | 
			
		||||
        table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        layout.pack(warning, 1, row);
 | 
			
		||||
        layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
 | 
			
		||||
        this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
@@ -221,27 +224,56 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const KeyringDummyDialog = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringDummyDialog',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.prompt = new Shell.KeyringPrompt();
 | 
			
		||||
        this.prompt.connect('show-password',
 | 
			
		||||
                            Lang.bind(this, this._cancelPrompt));
 | 
			
		||||
        this.prompt.connect('show-confirm', Lang.bind(this,
 | 
			
		||||
                            this._cancelPrompt));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelPrompt: function() {
 | 
			
		||||
        this.prompt.cancel();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const KeyringPrompter = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringPrompter',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._prompter = new Gcr.SystemPrompter();
 | 
			
		||||
        this._prompter.connect('new-prompt', function(prompter) {
 | 
			
		||||
            let dialog = new KeyringDialog();
 | 
			
		||||
            return dialog.prompt;
 | 
			
		||||
        });
 | 
			
		||||
        this._prompter.connect('new-prompt', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let dialog = this._enabled ? new KeyringDialog()
 | 
			
		||||
                                           : new KeyringDummyDialog();
 | 
			
		||||
                this._currentPrompt = dialog.prompt;
 | 
			
		||||
                return this._currentPrompt;
 | 
			
		||||
            }));
 | 
			
		||||
        this._dbusId = null;
 | 
			
		||||
        this._registered = false;
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
        this._currentPrompt = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        this._prompter.register(Gio.DBus.session);
 | 
			
		||||
        this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
 | 
			
		||||
                                                 Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
 | 
			
		||||
        if (!this._registered) {
 | 
			
		||||
            this._prompter.register(Gio.DBus.session);
 | 
			
		||||
            this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
 | 
			
		||||
                                                     Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
 | 
			
		||||
            this._registered = true;
 | 
			
		||||
        }
 | 
			
		||||
        this._enabled = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        this._prompter.unregister(false);
 | 
			
		||||
        Gio.DBus.session.unown_name(this._dbusId);
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
 | 
			
		||||
        if (this._prompter.prompting)
 | 
			
		||||
            this._currentPrompt.cancel();
 | 
			
		||||
        this._currentPrompt = null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,14 +62,9 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._content.message != null) {
 | 
			
		||||
            let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
                                                  text: this._content.message,
 | 
			
		||||
                                                  // HACK: for reasons unknown to me, the label
 | 
			
		||||
                                                  // is not asked the correct height for width,
 | 
			
		||||
                                                  // and thus is underallocated
 | 
			
		||||
                                                  // place a fixed height to avoid overflowing
 | 
			
		||||
                                                  style: 'height: 3em'
 | 
			
		||||
                                                });
 | 
			
		||||
                                                  text: this._content.message });
 | 
			
		||||
            descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
            descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
 | 
			
		||||
            messageBox.add(descriptionLabel,
 | 
			
		||||
                           { y_fill:  true,
 | 
			
		||||
@@ -77,13 +72,18 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
                             expand: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
 | 
			
		||||
                                          layout_manager: layout });
 | 
			
		||||
        layout.hookup_style(secretTable);
 | 
			
		||||
 | 
			
		||||
        let initialFocusSet = false;
 | 
			
		||||
        let pos = 0;
 | 
			
		||||
        for (let i = 0; i < this._content.secrets.length; i++) {
 | 
			
		||||
            let secret = this._content.secrets[i];
 | 
			
		||||
            let label = new St.Label({ style_class: 'prompt-dialog-password-label',
 | 
			
		||||
                                       text: secret.label });
 | 
			
		||||
            label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
 | 
			
		||||
            let reactive = secret.key != null;
 | 
			
		||||
 | 
			
		||||
@@ -116,11 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
            } else
 | 
			
		||||
                secret.valid = true;
 | 
			
		||||
 | 
			
		||||
            secretTable.add(label, { row: pos, col: 0,
 | 
			
		||||
                                     x_expand: false, x_fill: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
 | 
			
		||||
            layout.pack(label, 0, pos);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            layout.pack(secret.entry, 1, pos);
 | 
			
		||||
            pos++;
 | 
			
		||||
 | 
			
		||||
            if (secret.password)
 | 
			
		||||
@@ -139,6 +138,8 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
                           key:    Clutter.KEY_Escape,
 | 
			
		||||
                         },
 | 
			
		||||
                         this._okButton]);
 | 
			
		||||
 | 
			
		||||
        this._updateOkButton();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateOkButton: function() {
 | 
			
		||||
@@ -254,6 +255,7 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
        case 'leap':
 | 
			
		||||
        case 'ttls':
 | 
			
		||||
        case 'peap':
 | 
			
		||||
        case 'fast':
 | 
			
		||||
            // TTLS and PEAP are actually much more complicated, but this complication
 | 
			
		||||
            // is not visible here since we only care about phase2 authentication
 | 
			
		||||
            // (and don't even care of which one)
 | 
			
		||||
@@ -307,7 +309,7 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
            wirelessSetting = this._connection.get_setting_wireless();
 | 
			
		||||
            ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
 | 
			
		||||
            content.title = _("Authentication required by wireless network");
 | 
			
		||||
            content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
 | 
			
		||||
            content.message = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid);
 | 
			
		||||
            this._getWirelessSecrets(content.secrets, wirelessSetting);
 | 
			
		||||
            break;
 | 
			
		||||
        case '802-3-ethernet':
 | 
			
		||||
@@ -334,7 +336,7 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
        case 'cdma':
 | 
			
		||||
        case 'bluetooth':
 | 
			
		||||
            content.title = _("Mobile broadband network password");
 | 
			
		||||
            content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
 | 
			
		||||
            content.message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
 | 
			
		||||
            this._getMobileSecrets(content.secrets, connectionType);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
@@ -385,11 +387,7 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
            this._childPid = pid;
 | 
			
		||||
            this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
 | 
			
		||||
            this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
 | 
			
		||||
            // We need this one too, even if don't actually care of what the process
 | 
			
		||||
            // has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
 | 
			
		||||
            // is kept open indefinitely
 | 
			
		||||
            let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
 | 
			
		||||
            stderrStream.close(null);
 | 
			
		||||
            GLib.close(stderr);
 | 
			
		||||
            this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
 | 
			
		||||
 | 
			
		||||
            if (this._newStylePlugin)
 | 
			
		||||
@@ -437,6 +435,7 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _vpnChildFinished: function(pid, status, requestObj) {
 | 
			
		||||
        this._childWatch = 0;
 | 
			
		||||
        if (this._newStylePlugin) {
 | 
			
		||||
            // For new style plugin, all work is done in the async reading functions
 | 
			
		||||
            // Just reap the process here
 | 
			
		||||
@@ -511,10 +510,12 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _showNewStyleDialog: function() {
 | 
			
		||||
        let keyfile = new GLib.KeyFile();
 | 
			
		||||
        let data;
 | 
			
		||||
        let contentOverride;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            let data = this._dataStdout.peek_buffer();
 | 
			
		||||
            data = this._dataStdout.peek_buffer();
 | 
			
		||||
 | 
			
		||||
            keyfile.load_from_data(data.toString(), data.length,
 | 
			
		||||
                                   GLib.KeyFileFlags.NONE);
 | 
			
		||||
 | 
			
		||||
@@ -547,13 +548,16 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logError(e, 'error while reading VPN plugin output keyfile');
 | 
			
		||||
            // No output is a valid case it means "both secrets are stored"
 | 
			
		||||
            if (data.length > 0) {
 | 
			
		||||
                logError(e, 'error while reading VPN plugin output keyfile');
 | 
			
		||||
 | 
			
		||||
            this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
 | 
			
		||||
            return;
 | 
			
		||||
                this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (contentOverride.secrets.length) {
 | 
			
		||||
        if (contentOverride && contentOverride.secrets.length) {
 | 
			
		||||
            // Only show the dialog if we actually have something to ask
 | 
			
		||||
            this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], contentOverride);
 | 
			
		||||
            this._shellDialog.open(global.get_current_time());
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
                                            text: _("Authentication Required") });
 | 
			
		||||
 | 
			
		||||
        messageBox.add(this._subjectLabel,
 | 
			
		||||
                       { y_fill:  false,
 | 
			
		||||
                       { x_fill: false,
 | 
			
		||||
                         y_fill:  false,
 | 
			
		||||
                         x_align: St.Align.START,
 | 
			
		||||
                         y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
@@ -63,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        this._descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        messageBox.add(this._descriptionLabel,
 | 
			
		||||
                       { y_fill:  true,
 | 
			
		||||
                       { x_fill: false,
 | 
			
		||||
                         y_fill:  true,
 | 
			
		||||
                         x_align: St.Align.START,
 | 
			
		||||
                         y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        if (userNames.length > 1) {
 | 
			
		||||
@@ -95,7 +99,8 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        if (userIsRoot) {
 | 
			
		||||
            let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
 | 
			
		||||
                                            text: userRealName }));
 | 
			
		||||
            messageBox.add(userLabel);
 | 
			
		||||
            messageBox.add(userLabel, { x_fill: false,
 | 
			
		||||
                                        x_align: St.Align.START });
 | 
			
		||||
        } else {
 | 
			
		||||
            let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
@@ -137,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
 | 
			
		||||
        this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._errorMessageLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        messageBox.add(this._errorMessageLabel);
 | 
			
		||||
        messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this._errorMessageLabel.hide();
 | 
			
		||||
 | 
			
		||||
        this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Recorder = new Lang.Class({
 | 
			
		||||
    Name: 'Recorder',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
 | 
			
		||||
        this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
 | 
			
		||||
        this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
 | 
			
		||||
        this._recorder = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        Main.wm.addKeybinding('toggle-recording',
 | 
			
		||||
                              this._bindingSettings,
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                              Shell.KeyBindingMode.ALL,
 | 
			
		||||
                              Lang.bind(this, this._toggleRecorder));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        Main.wm.removeKeybinding('toggle-recording');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorder: function() {
 | 
			
		||||
        if (this._recorder == null)
 | 
			
		||||
            this._recorder = new Shell.Recorder({ stage: global.stage });
 | 
			
		||||
        return this._recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _toggleRecorder: function() {
 | 
			
		||||
        let recorder = this._ensureRecorder();
 | 
			
		||||
        if (recorder.is_recording()) {
 | 
			
		||||
            recorder.close();
 | 
			
		||||
            Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
        } else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
 | 
			
		||||
            // read the parameters from GSettings always in case they have changed
 | 
			
		||||
            recorder.set_framerate(this._recorderSettings.get_int('framerate'));
 | 
			
		||||
            /* Translators: this is a filename used for screencast recording */
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
 | 
			
		||||
            let pipeline = this._recorderSettings.get_string('pipeline');
 | 
			
		||||
 | 
			
		||||
            if (!pipeline.match(/^\s*$/))
 | 
			
		||||
                recorder.set_pipeline(pipeline);
 | 
			
		||||
            else
 | 
			
		||||
                recorder.set_pipeline(null);
 | 
			
		||||
 | 
			
		||||
            Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
            recorder.record();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Component = Recorder;
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
@@ -13,7 +14,6 @@ const Tp = imports.gi.TelepathyGLib;
 | 
			
		||||
const History = imports.misc.history;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +29,8 @@ const SCROLLBACK_HISTORY_LINES = 10;
 | 
			
		||||
// See Notification._onEntryChanged
 | 
			
		||||
const COMPOSING_STOP_TIMEOUT = 5;
 | 
			
		||||
 | 
			
		||||
const CLOCK_FORMAT_KEY = 'clock-format';
 | 
			
		||||
 | 
			
		||||
const NotificationDirection = {
 | 
			
		||||
    SENT: 'chat-sent',
 | 
			
		||||
    RECEIVED: 'chat-received'
 | 
			
		||||
@@ -100,15 +102,6 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
        this._tpClient.set_handle_channels_func(
 | 
			
		||||
            Lang.bind(this, this._handleChannels));
 | 
			
		||||
 | 
			
		||||
        // Watch subscription requests and connection errors
 | 
			
		||||
        this._subscriptionSource = null;
 | 
			
		||||
        this._accountSource = null;
 | 
			
		||||
 | 
			
		||||
        // Workaround for gjs not supporting GPtrArray in signals.
 | 
			
		||||
        // See BGO bug #653941 for context.
 | 
			
		||||
        this._tpClient.set_contact_list_changed_func(
 | 
			
		||||
            Lang.bind(this, this._contactListChanged));
 | 
			
		||||
 | 
			
		||||
        // Allow other clients (such as Empathy) to pre-empt our channels if
 | 
			
		||||
        // needed
 | 
			
		||||
        this._tpClient.set_delegated_channels_callback(
 | 
			
		||||
@@ -122,17 +115,12 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
            throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
 | 
			
		||||
                                                                             Lang.bind(this, this._accountValidityChanged));
 | 
			
		||||
 | 
			
		||||
        if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
 | 
			
		||||
            this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        this._tpClient.unregister();
 | 
			
		||||
        this._accountManager.disconnect(this._accountManagerValidityChangedId);
 | 
			
		||||
        this._accountManagerValidityChangedId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _observeChannels: function(observer, account, conn, channels,
 | 
			
		||||
@@ -217,33 +205,6 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _displayRoomInvitation: function(conn, channel, dispatchOp, context) {
 | 
			
		||||
        // We can only approve the rooms if we have been invited to it
 | 
			
		||||
        let selfContact = channel.group_get_self_contact();
 | 
			
		||||
        if (selfContact == null) {
 | 
			
		||||
            context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
 | 
			
		||||
                                        message: 'Not invited to the room' }));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
 | 
			
		||||
        if (!invited) {
 | 
			
		||||
            context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
 | 
			
		||||
                                        message: 'Not invited to the room' }));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // FIXME: We don't have a 'chat room' icon (bgo #653737) use
 | 
			
		||||
        // system-users for now as Empathy does.
 | 
			
		||||
        let source = new ApproverSource(dispatchOp, _("Invitation"),
 | 
			
		||||
                                        Gio.icon_new_for_string('system-users'));
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
 | 
			
		||||
        let notif = new RoomInviteNotification(source, dispatchOp, channel, inviter);
 | 
			
		||||
        source.notify(notif);
 | 
			
		||||
        context.accept();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _approveChannels: function(approver, account, conn, channels,
 | 
			
		||||
                               dispatchOp, context) {
 | 
			
		||||
        let channel = channels[0];
 | 
			
		||||
@@ -257,10 +218,6 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
 | 
			
		||||
            this._approveTextChannel(account, conn, channel, dispatchOp, context);
 | 
			
		||||
        else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
 | 
			
		||||
            this._approveCall(account, conn, channel, dispatchOp, context);
 | 
			
		||||
        else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
 | 
			
		||||
            this._approveFileTransfer(account, conn, channel, dispatchOp, context);
 | 
			
		||||
        else
 | 
			
		||||
            context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
 | 
			
		||||
                                        message: 'Unsupported channel type' }));
 | 
			
		||||
@@ -281,45 +238,9 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
                }}));
 | 
			
		||||
 | 
			
		||||
            context.accept();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._displayRoomInvitation(conn, channel, dispatchOp, context);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _approveCall: function(account, conn, channel, dispatchOp, context) {
 | 
			
		||||
        let isVideo = false;
 | 
			
		||||
 | 
			
		||||
        let props = channel.borrow_immutable_properties();
 | 
			
		||||
 | 
			
		||||
        if (props[Tp.PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO])
 | 
			
		||||
          isVideo = true;
 | 
			
		||||
 | 
			
		||||
        // We got the TpContact
 | 
			
		||||
        let source = new ApproverSource(dispatchOp, _("Call"), isVideo ?
 | 
			
		||||
                                        Gio.icon_new_for_string('camera-web') :
 | 
			
		||||
                                        Gio.icon_new_for_string('audio-input-microphone'));
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
 | 
			
		||||
        let notif = new AudioVideoNotification(source, dispatchOp, channel,
 | 
			
		||||
            channel.get_target_contact(), isVideo);
 | 
			
		||||
        source.notify(notif);
 | 
			
		||||
        context.accept();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
 | 
			
		||||
        // Use the icon of the file being transferred
 | 
			
		||||
        let gicon = Gio.content_type_get_icon(channel.get_mime_type());
 | 
			
		||||
 | 
			
		||||
        // We got the TpContact
 | 
			
		||||
        let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
 | 
			
		||||
        let notif = new FileTransferNotification(source, dispatchOp, channel,
 | 
			
		||||
            channel.get_target_contact());
 | 
			
		||||
        source.notify(notif);
 | 
			
		||||
        context.accept();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _delegatedChannelsCb: function(client, channels) {
 | 
			
		||||
        // Nothing to do as we don't make a distinction between observed and
 | 
			
		||||
        // handled channels.
 | 
			
		||||
@@ -327,105 +248,7 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _accountManagerPrepared: function(am, result) {
 | 
			
		||||
        am.prepare_finish(result);
 | 
			
		||||
 | 
			
		||||
        let accounts = am.get_valid_accounts();
 | 
			
		||||
        for (let i = 0; i < accounts.length; i++) {
 | 
			
		||||
            this._accountValidityChanged(am, accounts[i], true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _accountValidityChanged: function(am, account, valid) {
 | 
			
		||||
        if (!valid)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // It would be better to connect to "status-changed" but we cannot.
 | 
			
		||||
        // See discussion in https://bugzilla.gnome.org/show_bug.cgi?id=654159
 | 
			
		||||
        account.connect("notify::connection-status",
 | 
			
		||||
                        Lang.bind(this, this._accountConnectionStatusNotifyCb));
 | 
			
		||||
 | 
			
		||||
        account.connect('notify::connection',
 | 
			
		||||
                        Lang.bind(this, this._connectionChanged));
 | 
			
		||||
        this._connectionChanged(account);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectionChanged: function(account) {
 | 
			
		||||
        let conn = account.get_connection();
 | 
			
		||||
        if (conn == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._tpClient.grab_contact_list_changed(conn);
 | 
			
		||||
        if (conn.get_contact_list_state() == Tp.ContactListState.SUCCESS) {
 | 
			
		||||
            this._contactListChanged(conn, conn.dup_contact_list(), []);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _contactListChanged: function(conn, added, removed) {
 | 
			
		||||
        for (let i = 0; i < added.length; i++) {
 | 
			
		||||
            let contact = added[i];
 | 
			
		||||
 | 
			
		||||
            contact.connect('subscription-states-changed',
 | 
			
		||||
                            Lang.bind(this, this._subscriptionStateChanged));
 | 
			
		||||
            this._subscriptionStateChanged(contact);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _subscriptionStateChanged: function(contact) {
 | 
			
		||||
        if (contact.get_publish_state() != Tp.SubscriptionState.ASK)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        /* Implicitly accept publish requests if contact is already subscribed */
 | 
			
		||||
        if (contact.get_subscribe_state() == Tp.SubscriptionState.YES ||
 | 
			
		||||
            contact.get_subscribe_state() == Tp.SubscriptionState.ASK) {
 | 
			
		||||
 | 
			
		||||
            contact.authorize_publication_async(function(src, result) {
 | 
			
		||||
                src.authorize_publication_finish(result)});
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Display notification to ask user to accept/reject request */
 | 
			
		||||
        let source = this._ensureAppSource();
 | 
			
		||||
 | 
			
		||||
        let notif = new SubscriptionRequestNotification(source, contact);
 | 
			
		||||
        source.notify(notif);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _accountConnectionStatusNotifyCb: function(account) {
 | 
			
		||||
        let connectionError = account.connection_error;
 | 
			
		||||
 | 
			
		||||
        if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED ||
 | 
			
		||||
            connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let notif = this._accountNotifications[account.get_object_path()];
 | 
			
		||||
        if (notif)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        /* Display notification that account failed to connect */
 | 
			
		||||
        let source = this._ensureAppSource();
 | 
			
		||||
 | 
			
		||||
        notif = new AccountNotification(source, account, connectionError);
 | 
			
		||||
        this._accountNotifications[account.get_object_path()] = notif;
 | 
			
		||||
        notif.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            delete this._accountNotifications[account.get_object_path()];
 | 
			
		||||
        }));
 | 
			
		||||
        source.notify(notif);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureAppSource: function() {
 | 
			
		||||
        if (this._appSource == null) {
 | 
			
		||||
            this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
 | 
			
		||||
            this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
 | 
			
		||||
            Main.messageTray.add(this._appSource);
 | 
			
		||||
            this._appSource.connect('destroy', Lang.bind(this, function () {
 | 
			
		||||
                this._appSource = null;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._appSource;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ChatSource = new Lang.Class({
 | 
			
		||||
@@ -447,6 +270,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
 | 
			
		||||
 | 
			
		||||
        this._notification = new ChatNotification(this);
 | 
			
		||||
        this._notification.connect('clicked', Lang.bind(this, this.open));
 | 
			
		||||
        this._notification.setUrgency(MessageTray.Urgency.HIGH);
 | 
			
		||||
        this._notifyTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
@@ -488,7 +312,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
        return new MessageTray.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAlias: function() {
 | 
			
		||||
@@ -542,23 +366,22 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _updateAvatarIcon: function() {
 | 
			
		||||
        this.iconUpdated();
 | 
			
		||||
        this._notification.update(this._notification.title, null, { customContent: true });
 | 
			
		||||
        this._notification.update(this._notification.title);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(notification) {
 | 
			
		||||
          if (this._client.is_handling_channel(this._channel)) {
 | 
			
		||||
              // We are handling the channel, try to pass it to Empathy
 | 
			
		||||
              this._client.delegate_channels_async([this._channel],
 | 
			
		||||
                  global.get_current_time(),
 | 
			
		||||
                  'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
              // We are not the handler, just ask to present the channel
 | 
			
		||||
              let dbus = Tp.DBusDaemon.dup();
 | 
			
		||||
              let cd = Tp.ChannelDispatcher.new(dbus);
 | 
			
		||||
    open: function() {
 | 
			
		||||
        if (this._client.is_handling_channel(this._channel)) {
 | 
			
		||||
            // We are handling the channel, try to pass it to Empathy
 | 
			
		||||
            this._client.delegate_channels_async([this._channel],
 | 
			
		||||
                                                 global.get_current_time(),
 | 
			
		||||
                                                 'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
 | 
			
		||||
        } else {
 | 
			
		||||
            // We are not the handler, just ask to present the channel
 | 
			
		||||
            let dbus = Tp.DBusDaemon.dup();
 | 
			
		||||
            let cd = Tp.ChannelDispatcher.new(dbus);
 | 
			
		||||
 | 
			
		||||
              cd.present_channel_async(this._channel, global.get_current_time(), null);
 | 
			
		||||
          }
 | 
			
		||||
            cd.present_channel_async(this._channel, global.get_current_time(), null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getLogMessages: function() {
 | 
			
		||||
@@ -622,7 +445,11 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
            this.notify();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _channelClosed: function() {
 | 
			
		||||
    destroy: function(reason) {
 | 
			
		||||
        if (this._destroyed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._destroyed = true;
 | 
			
		||||
        this._channel.disconnect(this._closedId);
 | 
			
		||||
        this._channel.disconnect(this._receivedId);
 | 
			
		||||
        this._channel.disconnect(this._pendingId);
 | 
			
		||||
@@ -632,7 +459,14 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        this._contact.disconnect(this._notifyAvatarId);
 | 
			
		||||
        this._contact.disconnect(this._presenceChangedId);
 | 
			
		||||
 | 
			
		||||
        this.destroy();
 | 
			
		||||
        if (this._timestampTimeoutId)
 | 
			
		||||
            Mainloop.source_remove(this._timestampTimeoutId);
 | 
			
		||||
 | 
			
		||||
        this.parent(reason);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _channelClosed: function() {
 | 
			
		||||
        this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /* All messages are new messages for Telepathy sources */
 | 
			
		||||
@@ -668,6 +502,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
            Mainloop.source_remove(this._notifyTimeoutId);
 | 
			
		||||
        this._notifyTimeoutId = Mainloop.timeout_add(500,
 | 
			
		||||
            Lang.bind(this, this._notifyTimeout));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._notifyTimeoutId, '[gnome-shell] this._notifyTimeout');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notifyTimeout: function() {
 | 
			
		||||
@@ -676,7 +511,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._notifyTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This is called for both messages we send from
 | 
			
		||||
@@ -723,7 +558,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        title = GLib.markup_escape_text(this.title, -1);
 | 
			
		||||
 | 
			
		||||
        this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
 | 
			
		||||
        this._notification.update(this._notification.title, null, { secondaryGIcon: this.getSecondaryIcon() });
 | 
			
		||||
 | 
			
		||||
        if (message)
 | 
			
		||||
            msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
 | 
			
		||||
@@ -750,8 +585,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
        this.parent(source, source.title, null, { secondaryGIcon: source.getSecondaryIcon() });
 | 
			
		||||
 | 
			
		||||
        this._responseEntry = new St.Entry({ style_class: 'chat-response',
 | 
			
		||||
                                             can_focus: true });
 | 
			
		||||
@@ -767,16 +601,17 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
            this.emit('unfocused');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._createScrollArea();
 | 
			
		||||
        this._lastGroup = null;
 | 
			
		||||
        this._lastGroupActor = null;
 | 
			
		||||
 | 
			
		||||
        this._bodyBox = new St.BoxLayout({ style_class: 'chat-notification-body-box' });
 | 
			
		||||
        this._bodyBin.child = this._bodyBox;
 | 
			
		||||
 | 
			
		||||
        // Keep track of the bottom position for the current adjustment and
 | 
			
		||||
        // force a scroll to the bottom if things change while we were at the
 | 
			
		||||
        // bottom
 | 
			
		||||
        this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
 | 
			
		||||
        this._scrollArea.add_style_class_name('chat-notification-scrollview');
 | 
			
		||||
        this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
 | 
			
		||||
        this._oldMaxScrollValue = this._bodyScrollArea.vscroll.adjustment.value;
 | 
			
		||||
        this._bodyScrollArea.add_style_class_name('chat-notification-scrollview');
 | 
			
		||||
        this._bodyScrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
 | 
			
		||||
            if (adjustment.value == this._oldMaxScrollValue)
 | 
			
		||||
                this.scrollTo(St.Side.BOTTOM);
 | 
			
		||||
            this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
 | 
			
		||||
@@ -813,8 +648,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (message.direction == NotificationDirection.RECEIVED) {
 | 
			
		||||
            this.update(this.source.title, messageBody, { customContent: true,
 | 
			
		||||
                                                          bannerMarkup: true });
 | 
			
		||||
            this.update(this.source.title, messageBody, { bannerMarkup: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let group = (message.direction == NotificationDirection.RECEIVED ?
 | 
			
		||||
@@ -851,7 +685,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
                expired[i].actor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let groups = this._contentArea.get_children();
 | 
			
		||||
        let groups = this._bodyBox.get_children();
 | 
			
		||||
        for (let i = 0; i < groups.length; i++) {
 | 
			
		||||
            let group = groups[i];
 | 
			
		||||
            if (group.get_n_children() == 0)
 | 
			
		||||
@@ -883,9 +717,9 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
        if (this._timestampTimeoutId)
 | 
			
		||||
            Mainloop.source_remove(this._timestampTimeoutId);
 | 
			
		||||
 | 
			
		||||
        let highlighter = new MessageTray.URLHighlighter(props.body,
 | 
			
		||||
                                                         true,  // line wrap?
 | 
			
		||||
                                                         true); // allow markup?
 | 
			
		||||
        let highlighter = new MessageTray.URLHighlighter();
 | 
			
		||||
        highlighter.actor.clutter_text.line_wrap = true;
 | 
			
		||||
        highlighter.setMarkup(props.body, true);
 | 
			
		||||
 | 
			
		||||
        let body = highlighter.actor;
 | 
			
		||||
 | 
			
		||||
@@ -895,30 +729,30 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let group = props.group;
 | 
			
		||||
        if (group != this._lastGroup) {
 | 
			
		||||
            let style = 'chat-group-' + group;
 | 
			
		||||
            this._lastGroup = group;
 | 
			
		||||
            this._lastGroupActor = new St.BoxLayout({ style_class: style,
 | 
			
		||||
                                                      vertical: true });
 | 
			
		||||
            this.addActor(this._lastGroupActor);
 | 
			
		||||
            let emptyLine = new St.Label({ style_class: 'chat-empty-line' });
 | 
			
		||||
            this._bodyBox.add_child(emptyLine);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._lastGroupActor.add(body, props.childProps);
 | 
			
		||||
 | 
			
		||||
        this.updated();
 | 
			
		||||
        this._lastMessageBox = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this._lastMessageBox.add(body, props.childProps);
 | 
			
		||||
        this._bodyBox.add_child(this._lastMessageBox);
 | 
			
		||||
 | 
			
		||||
        let timestamp = props.timestamp;
 | 
			
		||||
        this._history.unshift({ actor: body, time: timestamp,
 | 
			
		||||
                                realMessage: group != 'meta' });
 | 
			
		||||
 | 
			
		||||
        if (!props.noTimestamp) {
 | 
			
		||||
            if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
 | 
			
		||||
            if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
 | 
			
		||||
                this.appendTimestamp();
 | 
			
		||||
            else
 | 
			
		||||
            } else {
 | 
			
		||||
                // Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME
 | 
			
		||||
                // from the timestamp of the message.
 | 
			
		||||
                this._timestampTimeoutId = Mainloop.timeout_add_seconds(
 | 
			
		||||
                    SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp),
 | 
			
		||||
                    Lang.bind(this, this.appendTimestamp));
 | 
			
		||||
                GLib.Source.set_name_by_id(this._timestampTimeoutId, '[gnome-shell] this.appendTimestamp');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._filterMessages();
 | 
			
		||||
@@ -931,50 +765,98 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let format;
 | 
			
		||||
 | 
			
		||||
        // Show only the hour if date is on today
 | 
			
		||||
        if(daysAgo < 1){
 | 
			
		||||
            format = "<b>%H:%M</b>";
 | 
			
		||||
        }
 | 
			
		||||
        // Show the word "Yesterday" and time if date is on yesterday
 | 
			
		||||
        else if(daysAgo <2){
 | 
			
		||||
            /* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            format = _("<b>Yesterday</b>, <b>%H:%M</b>");
 | 
			
		||||
        }
 | 
			
		||||
        // Show a week day and time if date is in the last week
 | 
			
		||||
        else if (daysAgo < 7) {
 | 
			
		||||
            /* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            format = _("<b>%A</b>, <b>%H:%M</b>");
 | 
			
		||||
        let desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        let clockFormat = desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let hasAmPm = date.toLocaleFormat('%p') != '';
 | 
			
		||||
 | 
			
		||||
        } else if (date.getYear() == now.getYear()) {
 | 
			
		||||
            /* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
 | 
			
		||||
        if (clockFormat == '24h' || !hasAmPm) {
 | 
			
		||||
            // Show only the time if date is on today
 | 
			
		||||
            if(daysAgo < 1){
 | 
			
		||||
                /* Translators: Time in 24h format */
 | 
			
		||||
                format = _("%H\u2236%M");
 | 
			
		||||
            }
 | 
			
		||||
            // Show the word "Yesterday" and time if date is on yesterday
 | 
			
		||||
            else if(daysAgo <2){
 | 
			
		||||
                /* Translators: this is the word "Yesterday" followed by a
 | 
			
		||||
                 time string in 24h format. i.e. "Yesterday, 14:30" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("Yesterday, %H\u2236%M");
 | 
			
		||||
            }
 | 
			
		||||
            // Show a week day and time if date is in the last week
 | 
			
		||||
            else if (daysAgo < 7) {
 | 
			
		||||
                /* Translators: this is the week day name followed by a time
 | 
			
		||||
                 string in 24h format. i.e. "Monday, 14:30" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%A, %H\u2236%M");
 | 
			
		||||
 | 
			
		||||
            } else if (date.getYear() == now.getYear()) {
 | 
			
		||||
                /* Translators: this is the month name and day number
 | 
			
		||||
                 followed by a time string in 24h format.
 | 
			
		||||
                 i.e. "May 25, 14:30" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%B %d, %H\u2236%M");
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Translators: this is the month name, day number, year
 | 
			
		||||
                 number followed by a time string in 24h format.
 | 
			
		||||
                 i.e. "May 25 2012, 14:30" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%B %d %Y, %H\u2236%M");
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
 | 
			
		||||
        }
 | 
			
		||||
            // Show only the time if date is on today
 | 
			
		||||
            if(daysAgo < 1){
 | 
			
		||||
                /* Translators: Time in 24h format */
 | 
			
		||||
                format = _("%l\u2236%M %p");
 | 
			
		||||
            }
 | 
			
		||||
            // Show the word "Yesterday" and time if date is on yesterday
 | 
			
		||||
            else if(daysAgo <2){
 | 
			
		||||
                /* Translators: this is the word "Yesterday" followed by a
 | 
			
		||||
                 time string in 12h format. i.e. "Yesterday, 2:30 pm" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("Yesterday, %l\u2236%M %p");
 | 
			
		||||
            }
 | 
			
		||||
            // Show a week day and time if date is in the last week
 | 
			
		||||
            else if (daysAgo < 7) {
 | 
			
		||||
                /* Translators: this is the week day name followed by a time
 | 
			
		||||
                 string in 12h format. i.e. "Monday, 2:30 pm" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%A, %l\u2236%M %p");
 | 
			
		||||
 | 
			
		||||
            } else if (date.getYear() == now.getYear()) {
 | 
			
		||||
                /* Translators: this is the month name and day number
 | 
			
		||||
                 followed by a time string in 12h format.
 | 
			
		||||
                 i.e. "May 25, 2:30 pm" */
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%B %d, %l\u2236%M %p");
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Translators: this is the month name, day number, year
 | 
			
		||||
                 number followed by a time string in 12h format.
 | 
			
		||||
                 i.e. "May 25 2012, 2:30 pm"*/
 | 
			
		||||
                // xgettext:no-c-format
 | 
			
		||||
                format = _("%B %d %Y, %l\u2236%M %p");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return date.toLocaleFormat(format);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    appendTimestamp: function() {
 | 
			
		||||
        this._timestampTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        let lastMessageTime = this._history[0].time;
 | 
			
		||||
        let lastMessageDate = new Date(lastMessageTime * 1000);
 | 
			
		||||
 | 
			
		||||
        let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
 | 
			
		||||
                                       group: 'meta',
 | 
			
		||||
                                       styles: ['chat-meta-message'],
 | 
			
		||||
                                       childProps: { expand: true, x_fill: false,
 | 
			
		||||
                                                     x_align: St.Align.END },
 | 
			
		||||
                                       noTimestamp: true,
 | 
			
		||||
                                       timestamp: lastMessageTime });
 | 
			
		||||
        let timeLabel = new St.Label({ text: this._formatTimestamp(lastMessageDate),
 | 
			
		||||
                                       style_class: 'chat-meta-message',
 | 
			
		||||
                                       x_expand: true,
 | 
			
		||||
                                       y_expand: true,
 | 
			
		||||
                                       x_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                       y_align: Clutter.ActorAlign.END });
 | 
			
		||||
 | 
			
		||||
        this._lastMessageBox.add_actor(timeLabel);
 | 
			
		||||
 | 
			
		||||
        this._filterMessages();
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    appendAliasChange: function(oldAlias, newAlias) {
 | 
			
		||||
@@ -989,7 +871,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
                                   group: 'meta',
 | 
			
		||||
                                   styles: ['chat-meta-message'] });
 | 
			
		||||
 | 
			
		||||
        this.update(newAlias, null, { customContent: true });
 | 
			
		||||
        this.update(newAlias);
 | 
			
		||||
 | 
			
		||||
        this._filterMessages();
 | 
			
		||||
    },
 | 
			
		||||
@@ -1012,7 +894,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.source.setChatState(Tp.ChannelChatState.PAUSED);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryChanged: function() {
 | 
			
		||||
@@ -1035,389 +917,11 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
            this._composingTimeoutId = Mainloop.timeout_add_seconds(
 | 
			
		||||
                COMPOSING_STOP_TIMEOUT,
 | 
			
		||||
                Lang.bind(this, this._composingStopTimeout));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout');
 | 
			
		||||
        } else {
 | 
			
		||||
            this.source.setChatState(Tp.ChannelChatState.ACTIVE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ApproverSource = new Lang.Class({
 | 
			
		||||
    Name: 'ApproverSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
 | 
			
		||||
    _init: function(dispatchOp, text, gicon) {
 | 
			
		||||
        this._gicon = gicon;
 | 
			
		||||
 | 
			
		||||
        this.parent(text);
 | 
			
		||||
 | 
			
		||||
        this._dispatchOp = dispatchOp;
 | 
			
		||||
 | 
			
		||||
        // Destroy the source if the channel dispatch operation is invalidated
 | 
			
		||||
        // as we can't approve any more.
 | 
			
		||||
        this._invalidId = dispatchOp.connect('invalidated',
 | 
			
		||||
                                             Lang.bind(this, function(domain, code, msg) {
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._invalidId != 0) {
 | 
			
		||||
            this._dispatchOp.disconnect(this._invalidId);
 | 
			
		||||
            this._invalidId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIcon: function() {
 | 
			
		||||
        return this._gicon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RoomInviteNotification = new Lang.Class({
 | 
			
		||||
    Name: 'RoomInviteNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, dispatchOp, channel, inviter) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    /* translators: argument is a room name like
 | 
			
		||||
                     * room@jabber.org for example. */
 | 
			
		||||
                    _("Invitation to %s").format(channel.get_identifier()),
 | 
			
		||||
                    null,
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        /* translators: first argument is the name of a contact and the second
 | 
			
		||||
         * one the name of a room. "Alice is inviting you to join room@jabber.org
 | 
			
		||||
         * for example. */
 | 
			
		||||
        this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
 | 
			
		||||
 | 
			
		||||
        this.addButton('decline', _("Decline"));
 | 
			
		||||
        this.addButton('accept', _("Accept"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'decline':
 | 
			
		||||
                dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
 | 
			
		||||
                                                '', function(src, result) {
 | 
			
		||||
                    src.leave_channels_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            case 'accept':
 | 
			
		||||
                dispatchOp.handle_with_time_async('', global.get_current_time(),
 | 
			
		||||
                                                  function(src, result) {
 | 
			
		||||
                    src.handle_with_time_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Audio Video
 | 
			
		||||
const AudioVideoNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AudioVideoNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, dispatchOp, channel, contact, isVideo) {
 | 
			
		||||
        let title = '';
 | 
			
		||||
 | 
			
		||||
        if (isVideo)
 | 
			
		||||
             /* translators: argument is a contact name like Alice for example. */
 | 
			
		||||
            title = _("Video call from %s").format(contact.get_alias());
 | 
			
		||||
        else
 | 
			
		||||
             /* translators: argument is a contact name like Alice for example. */
 | 
			
		||||
            title = _("Call from %s").format(contact.get_alias());
 | 
			
		||||
 | 
			
		||||
        this.parent(source, title, null, { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this.addButton('reject', _("Decline"));
 | 
			
		||||
        /* translators: this is a button label (verb), not a noun */
 | 
			
		||||
        this.addButton('answer', _("Answer"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'reject':
 | 
			
		||||
                dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
 | 
			
		||||
                                                '', function(src, result) {
 | 
			
		||||
                    src.leave_channels_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            case 'answer':
 | 
			
		||||
                dispatchOp.handle_with_time_async('', global.get_current_time(),
 | 
			
		||||
                                                  function(src, result) {
 | 
			
		||||
                    src.handle_with_time_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// File Transfer
 | 
			
		||||
const FileTransferNotification = new Lang.Class({
 | 
			
		||||
    Name: 'FileTransferNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, dispatchOp, channel, contact) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    /* To translators: The first parameter is
 | 
			
		||||
                     * the contact's alias and the second one is the
 | 
			
		||||
                     * file name. The string will be something
 | 
			
		||||
                     * like: "Alice is sending you test.ogg"
 | 
			
		||||
                     */
 | 
			
		||||
                    _("%s is sending you %s").format(contact.get_alias(),
 | 
			
		||||
                                                     channel.get_filename()),
 | 
			
		||||
                    null,
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this.addButton('decline', _("Decline"));
 | 
			
		||||
        this.addButton('accept', _("Accept"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'decline':
 | 
			
		||||
                dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
 | 
			
		||||
                                                '', function(src, result) {
 | 
			
		||||
                    src.leave_channels_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            case 'accept':
 | 
			
		||||
                dispatchOp.handle_with_time_async('', global.get_current_time(),
 | 
			
		||||
                                                  function(src, result) {
 | 
			
		||||
                    src.handle_with_time_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Subscription request
 | 
			
		||||
const SubscriptionRequestNotification = new Lang.Class({
 | 
			
		||||
    Name: 'SubscriptionRequestNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, contact) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    /* To translators: The parameter is the contact's alias */
 | 
			
		||||
                    _("%s would like permission to see when you are online").format(contact.get_alias()),
 | 
			
		||||
                    null, { customContent: true });
 | 
			
		||||
 | 
			
		||||
        this._contact = contact;
 | 
			
		||||
        this._connection = contact.get_connection();
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
 | 
			
		||||
        // Display avatar
 | 
			
		||||
        let iconBox = new St.Bin({ style_class: 'avatar-box' });
 | 
			
		||||
        iconBox._size = 48;
 | 
			
		||||
 | 
			
		||||
        let textureCache = St.TextureCache.get_default();
 | 
			
		||||
        let file = contact.get_avatar_file();
 | 
			
		||||
 | 
			
		||||
        if (file) {
 | 
			
		||||
            let uri = file.get_uri();
 | 
			
		||||
            iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            iconBox.child = new St.Icon({ icon_name: 'avatar-default',
 | 
			
		||||
                                          icon_size: iconBox._size });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        layout.add(iconBox);
 | 
			
		||||
 | 
			
		||||
        // subscription request message
 | 
			
		||||
        let label = new St.Label({ style_class: 'subscription-message',
 | 
			
		||||
                                   text: contact.get_publish_request() });
 | 
			
		||||
 | 
			
		||||
        layout.add(label);
 | 
			
		||||
 | 
			
		||||
        this.addActor(layout);
 | 
			
		||||
 | 
			
		||||
        this.addButton('decline', _("Decline"));
 | 
			
		||||
        this.addButton('accept', _("Accept"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'decline':
 | 
			
		||||
                contact.remove_async(function(src, result) {
 | 
			
		||||
                    src.remove_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            case 'accept':
 | 
			
		||||
                // Authorize the contact and request to see his status as well
 | 
			
		||||
                contact.authorize_publication_async(function(src, result) {
 | 
			
		||||
                    src.authorize_publication_finish(result)});
 | 
			
		||||
 | 
			
		||||
                contact.request_subscription_async('', function(src, result) {
 | 
			
		||||
                    src.request_subscription_finish(result)});
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // rely on _subscriptionStatesChangedCb to destroy the
 | 
			
		||||
            // notification
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._changedId = contact.connect('subscription-states-changed',
 | 
			
		||||
            Lang.bind(this, this._subscriptionStatesChangedCb));
 | 
			
		||||
        this._invalidatedId = this._connection.connect('invalidated',
 | 
			
		||||
            Lang.bind(this, this.destroy));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._changedId != 0) {
 | 
			
		||||
            this._contact.disconnect(this._changedId);
 | 
			
		||||
            this._changedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._invalidatedId != 0) {
 | 
			
		||||
            this._connection.disconnect(this._invalidatedId);
 | 
			
		||||
            this._invalidatedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _subscriptionStatesChangedCb: function(contact, subscribe, publish, msg) {
 | 
			
		||||
        // Destroy the notification if the subscription request has been
 | 
			
		||||
        // answered
 | 
			
		||||
        if (publish != Tp.SubscriptionState.ASK)
 | 
			
		||||
            this.destroy();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Messages from empathy/libempathy/empathy-utils.c
 | 
			
		||||
// create_errors_to_message_hash()
 | 
			
		||||
 | 
			
		||||
/* Translator note: these should be the same messages that are
 | 
			
		||||
 * used in Empathy, so just copy and paste from there. */
 | 
			
		||||
let _connectionErrorMessages = {};
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR)]
 | 
			
		||||
  = _("Network error");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED)]
 | 
			
		||||
  = _("Authentication failed");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR)]
 | 
			
		||||
  = _("Encryption error");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED)]
 | 
			
		||||
  = _("Certificate not provided");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED)]
 | 
			
		||||
  = _("Certificate untrusted");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED)]
 | 
			
		||||
  = _("Certificate expired");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED)]
 | 
			
		||||
  = _("Certificate not activated");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH)]
 | 
			
		||||
  = _("Certificate hostname mismatch");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH)]
 | 
			
		||||
  = _("Certificate fingerprint mismatch");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED)]
 | 
			
		||||
  = _("Certificate self-signed");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CANCELLED)]
 | 
			
		||||
  = _("Status is set to offline");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE)]
 | 
			
		||||
  = _("Encryption is not available");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INVALID)]
 | 
			
		||||
  = _("Certificate is invalid");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED)]
 | 
			
		||||
  = _("Connection has been refused");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
 | 
			
		||||
  = _("Connection can't be established");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
 | 
			
		||||
  = _("Connection has been lost");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
 | 
			
		||||
  = _("This account is already connected to the server");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
 | 
			
		||||
  = _("Connection has been replaced by a new connection using the same resource");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
 | 
			
		||||
  = _("The account already exists on the server");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)]
 | 
			
		||||
  = _("Server is currently too busy to handle the connection");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED)]
 | 
			
		||||
  = _("Certificate has been revoked");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
 | 
			
		||||
  = _("Certificate uses an insecure cipher algorithm or is cryptographically weak");
 | 
			
		||||
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
 | 
			
		||||
  = _("The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library");
 | 
			
		||||
_connectionErrorMessages['org.freedesktop.DBus.Error.NoReply']
 | 
			
		||||
  = _("Internal error");
 | 
			
		||||
 | 
			
		||||
const AccountNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AccountNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, account, connectionError) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    /* translators: argument is the account name, like
 | 
			
		||||
                     * name@jabber.org for example. */
 | 
			
		||||
                    _("Unable to connect to %s").format(account.get_display_name()),
 | 
			
		||||
                    this._getMessage(connectionError));
 | 
			
		||||
 | 
			
		||||
        this._account = account;
 | 
			
		||||
 | 
			
		||||
        this.addButton('view', _("View account"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'view':
 | 
			
		||||
                let cmd = 'empathy-accounts --select-account=' +
 | 
			
		||||
                          account.get_path_suffix();
 | 
			
		||||
                let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
 | 
			
		||||
                app_info.launch([], global.create_app_launch_context());
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._enabledId = account.connect('notify::enabled',
 | 
			
		||||
                                          Lang.bind(this, function() {
 | 
			
		||||
                                              if (!account.is_enabled())
 | 
			
		||||
                                                  this.destroy();
 | 
			
		||||
                                          }));
 | 
			
		||||
 | 
			
		||||
        this._invalidatedId = account.connect('invalidated',
 | 
			
		||||
                                              Lang.bind(this, this.destroy));
 | 
			
		||||
 | 
			
		||||
        this._connectionStatusId = account.connect('notify::connection-status',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                let status = account.connection_status;
 | 
			
		||||
                if (status == Tp.ConnectionStatus.CONNECTED) {
 | 
			
		||||
                    this.destroy();
 | 
			
		||||
                } else if (status == Tp.ConnectionStatus.DISCONNECTED) {
 | 
			
		||||
                    this.update(this.title, this._getMessage(account.connection_error));
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMessage: function(connectionError) {
 | 
			
		||||
        let message;
 | 
			
		||||
        if (connectionError in _connectionErrorMessages) {
 | 
			
		||||
            message = _connectionErrorMessages[connectionError];
 | 
			
		||||
        } else {
 | 
			
		||||
            message = _("Unknown reason");
 | 
			
		||||
        }
 | 
			
		||||
        return message;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._enabledId != 0) {
 | 
			
		||||
            this._account.disconnect(this._enabledId);
 | 
			
		||||
            this._enabledId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._invalidatedId != 0) {
 | 
			
		||||
            this._account.disconnect(this._invalidatedId);
 | 
			
		||||
            this._invalidatedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._connectionStatusId != 0) {
 | 
			
		||||
            this._account.disconnect(this._connectionStatusId);
 | 
			
		||||
            this._connectionStatusId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
const Component = TelepathyClient;
 | 
			
		||||
 
 | 
			
		||||
@@ -58,14 +58,10 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusGroup: function(item, timestamp) {
 | 
			
		||||
        if (item.focusCallback) {
 | 
			
		||||
        if (item.focusCallback)
 | 
			
		||||
            item.focusCallback(timestamp);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (global.stage_input_mode == Shell.StageInputMode.NORMAL)
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
 | 
			
		||||
        else
 | 
			
		||||
            item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sort the items into a consistent order; panel first, tray last,
 | 
			
		||||
@@ -91,7 +87,7 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
        if (Main.sessionMode.hasWindows && !Main.overview.visible) {
 | 
			
		||||
            let screen = global.screen;
 | 
			
		||||
            let display = screen.get_display();
 | 
			
		||||
            let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
 | 
			
		||||
            let windows = display.get_tab_list(Meta.TabList.DOCKS, screen.get_active_workspace ());
 | 
			
		||||
            let windowTracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            let textureCache = St.TextureCache.get_default();
 | 
			
		||||
            for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
@@ -136,8 +132,6 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _focusWindows: function(timestamp) {
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
        global.stage.key_focus = null;
 | 
			
		||||
        global.screen.focus_default_window(timestamp);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -171,6 +165,10 @@ const CtrlAltTabPopup = new Lang.Class({
 | 
			
		||||
            this._select(this._previous());
 | 
			
		||||
        else if (keysym == Clutter.Right)
 | 
			
		||||
            this._select(this._next());
 | 
			
		||||
        else
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish : function(time) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
@@ -380,6 +381,8 @@ const DashActor = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const baseIconSizes = [ 16, 22, 24, 32, 48, 64 ];
 | 
			
		||||
 | 
			
		||||
const Dash = new Lang.Class({
 | 
			
		||||
    Name: 'Dash',
 | 
			
		||||
 | 
			
		||||
@@ -423,7 +426,10 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            AppFavorites.getAppFavorites().reload();
 | 
			
		||||
            this._queueRedisplay();
 | 
			
		||||
        }));
 | 
			
		||||
        AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
 | 
			
		||||
@@ -502,15 +508,21 @@ const Dash = new Lang.Class({
 | 
			
		||||
        Main.queueDeferredWork(this._workId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hookUpLabel: function(item) {
 | 
			
		||||
    _hookUpLabel: function(item, appIcon) {
 | 
			
		||||
        item.child.connect('notify::hover', Lang.bind(this, function() {
 | 
			
		||||
            this._onHover(item);
 | 
			
		||||
            this._syncLabel(item, appIcon);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function() {
 | 
			
		||||
            this._labelShowing = false;
 | 
			
		||||
            item.hideLabel();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (appIcon) {
 | 
			
		||||
            appIcon.connect('sync-tooltip', Lang.bind(this, function() {
 | 
			
		||||
                this._syncLabel(item, appIcon);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createAppItem: function(app) {
 | 
			
		||||
@@ -539,7 +551,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
        item.setLabelText(app.get_name());
 | 
			
		||||
 | 
			
		||||
        appIcon.icon.setIconSize(this.iconSize);
 | 
			
		||||
        this._hookUpLabel(item);
 | 
			
		||||
        this._hookUpLabel(item, appIcon);
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
@@ -557,16 +569,20 @@ const Dash = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onHover: function (item) {
 | 
			
		||||
        if (item.child.get_hover()) {
 | 
			
		||||
    _syncLabel: function (item, appIcon) {
 | 
			
		||||
        let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
 | 
			
		||||
 | 
			
		||||
        if (shouldShow) {
 | 
			
		||||
            if (this._showLabelTimeoutId == 0) {
 | 
			
		||||
                let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
 | 
			
		||||
                this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this._labelShowing = true;
 | 
			
		||||
                        item.showLabel();
 | 
			
		||||
                        return false;
 | 
			
		||||
                        this._showLabelTimeoutId = 0;
 | 
			
		||||
                        return GLib.SOURCE_REMOVE;
 | 
			
		||||
                    }));
 | 
			
		||||
                GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
 | 
			
		||||
                if (this._resetHoverTimeoutId > 0) {
 | 
			
		||||
                    Mainloop.source_remove(this._resetHoverTimeoutId);
 | 
			
		||||
                    this._resetHoverTimeoutId = 0;
 | 
			
		||||
@@ -581,8 +597,10 @@ const Dash = new Lang.Class({
 | 
			
		||||
                this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this._labelShowing = false;
 | 
			
		||||
                        return false;
 | 
			
		||||
                        this._resetHoverTimeoutId = 0;
 | 
			
		||||
                        return GLib.SOURCE_REMOVE;
 | 
			
		||||
                    }));
 | 
			
		||||
                GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -618,25 +636,24 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let minHeight, natHeight;
 | 
			
		||||
 | 
			
		||||
        // Enforce the current icon size during the size request
 | 
			
		||||
        let [currentWidth, currentHeight] = firstIcon.icon.get_size();
 | 
			
		||||
 | 
			
		||||
        firstIcon.icon.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
        firstIcon.setIconSize(this.iconSize);
 | 
			
		||||
        [minHeight, natHeight] = firstButton.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        firstIcon.icon.set_size(currentWidth, currentHeight);
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        let iconSizes = baseIconSizes.map(function(s) {
 | 
			
		||||
            return s * scaleFactor;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Subtract icon padding and box spacing from the available height
 | 
			
		||||
        availHeight -= iconChildren.length * (natHeight - this.iconSize) +
 | 
			
		||||
        availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
 | 
			
		||||
                       (iconChildren.length - 1) * spacing;
 | 
			
		||||
 | 
			
		||||
        let availSize = availHeight / iconChildren.length;
 | 
			
		||||
 | 
			
		||||
        let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
 | 
			
		||||
 | 
			
		||||
        let newIconSize = 16;
 | 
			
		||||
        let newIconSize = baseIconSizes[0];
 | 
			
		||||
        for (let i = 0; i < iconSizes.length; i++) {
 | 
			
		||||
            if (iconSizes[i] < availSize)
 | 
			
		||||
                newIconSize = iconSizes[i];
 | 
			
		||||
                newIconSize = baseIconSizes[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newIconSize == this.iconSize)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,7 @@ const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
 | 
			
		||||
function _onVertSepRepaint (area)
 | 
			
		||||
{
 | 
			
		||||
function _onVertSepRepaint(area) {
 | 
			
		||||
    let cr = area.get_context();
 | 
			
		||||
    let themeNode = area.get_theme_node();
 | 
			
		||||
    let [width, height] = area.get_surface_size();
 | 
			
		||||
@@ -33,7 +32,7 @@ function _onVertSepRepaint (area)
 | 
			
		||||
    cr.setLineWidth(stippleWidth);
 | 
			
		||||
    cr.stroke();
 | 
			
		||||
    cr.$dispose();
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DateMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'DateMenuButton',
 | 
			
		||||
@@ -49,11 +48,13 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
            menuAlignment = 1.0 - menuAlignment;
 | 
			
		||||
        this.parent(menuAlignment);
 | 
			
		||||
 | 
			
		||||
        this._clockDisplay = new St.Label();
 | 
			
		||||
        this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.label_actor = this._clockDisplay;
 | 
			
		||||
        this.actor.add_actor(this._clockDisplay);
 | 
			
		||||
        this.actor.add_style_class_name ('clock-display');
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({name: 'calendarArea' });
 | 
			
		||||
        this.menu.addActor(hbox);
 | 
			
		||||
        hbox = new St.BoxLayout({ name: 'calendarArea' });
 | 
			
		||||
        this.menu.box.add_child(hbox);
 | 
			
		||||
 | 
			
		||||
        // Fill up the first column
 | 
			
		||||
 | 
			
		||||
@@ -61,10 +62,17 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label();
 | 
			
		||||
        this.actor.label_actor = this._clockDisplay;
 | 
			
		||||
        this._date.style_class = 'datemenu-date-label';
 | 
			
		||||
        vbox.add(this._date);
 | 
			
		||||
        // Having the ability to go to the current date if the user is already
 | 
			
		||||
        // on the current date can be confusing. So don't make the button reactive
 | 
			
		||||
        // until the selected date changes.
 | 
			
		||||
        this._date = new St.Button({ style_class: 'datemenu-date-label',
 | 
			
		||||
                                     reactive: false
 | 
			
		||||
                                   });
 | 
			
		||||
        this._date.connect('clicked',
 | 
			
		||||
                           Lang.bind(this, function() {
 | 
			
		||||
                               this._calendar.setDate(new Date(), false);
 | 
			
		||||
                           }));
 | 
			
		||||
        vbox.add(this._date, { x_fill: false  });
 | 
			
		||||
 | 
			
		||||
        this._eventList = new Calendar.EventsList();
 | 
			
		||||
        this._calendar = new Calendar.Calendar();
 | 
			
		||||
@@ -76,6 +84,9 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
                                  // and the calender makes those dates unclickable when instantiated with
 | 
			
		||||
                                  // a null event source
 | 
			
		||||
                                   this._eventList.setDate(date);
 | 
			
		||||
 | 
			
		||||
                                   // Make the button reactive only if the selected date is not the current date.
 | 
			
		||||
                                   this._date.can_focus = this._date.reactive = !this._isToday(date)
 | 
			
		||||
                               }));
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
@@ -112,22 +123,7 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
 | 
			
		||||
            if (isOpen) {
 | 
			
		||||
                let now = new Date();
 | 
			
		||||
                /* Passing true to setDate() forces events to be reloaded. We
 | 
			
		||||
                 * want this behavior, because
 | 
			
		||||
                 *
 | 
			
		||||
                 *   o It will cause activation of the calendar server which is
 | 
			
		||||
                 *     useful if it has crashed
 | 
			
		||||
                 *
 | 
			
		||||
                 *   o It will cause the calendar server to reload events which
 | 
			
		||||
                 *     is useful if dynamic updates are not supported or not
 | 
			
		||||
                 *     properly working
 | 
			
		||||
                 *
 | 
			
		||||
                 * Since this only happens when the menu is opened, the cost
 | 
			
		||||
                 * isn't very big.
 | 
			
		||||
                 */
 | 
			
		||||
                this._calendar.setDate(now, true);
 | 
			
		||||
                // No need to update this._eventList as ::selected-date-changed
 | 
			
		||||
                // signal will fire
 | 
			
		||||
                this._calendar.setDate(now);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
@@ -141,6 +137,13 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isToday: function(date) {
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        return now.getYear() == date.getYear() &&
 | 
			
		||||
               now.getMonth() == date.getMonth() &&
 | 
			
		||||
               now.getDay() == date.getDay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appInstalledChanged: function() {
 | 
			
		||||
        this._calendarApp = undefined;
 | 
			
		||||
        this._updateEventsVisibility();
 | 
			
		||||
@@ -200,7 +203,7 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
         */
 | 
			
		||||
        let dateFormat = _("%A %B %e, %Y");
 | 
			
		||||
        let displayDate = new Date();
 | 
			
		||||
        this._date.set_text(displayDate.toLocaleFormat(dateFormat));
 | 
			
		||||
        this._date.set_label(displayDate.toLocaleFormat(dateFormat));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCalendarApp: function() {
 | 
			
		||||
@@ -216,7 +219,7 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getClockApp: function() {
 | 
			
		||||
        return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
 | 
			
		||||
        return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenCalendarActivate: function() {
 | 
			
		||||
@@ -225,7 +228,7 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        let app = this._getCalendarApp();
 | 
			
		||||
        if (app.get_id() == 'evolution.desktop')
 | 
			
		||||
            app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
 | 
			
		||||
        app.launch([], global.create_app_launch_context());
 | 
			
		||||
        app.launch([], global.create_app_launch_context(0, -1));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenClocksActivate: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						@@ -5,6 +5,7 @@ const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -27,9 +28,9 @@ const DragMotionResult = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DRAG_CURSOR_MAP = {
 | 
			
		||||
    0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Shell.Cursor.DND_COPY,
 | 
			
		||||
    2: Shell.Cursor.DND_MOVE
 | 
			
		||||
    0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Meta.Cursor.DND_COPY,
 | 
			
		||||
    2: Meta.Cursor.DND_MOVE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DragDropResult = {
 | 
			
		||||
@@ -85,11 +86,6 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            this._actorDestroyed = true;
 | 
			
		||||
 | 
			
		||||
            // If the drag actor is destroyed and we were going to fix
 | 
			
		||||
            // up its hover state, fix up the parent hover state instead
 | 
			
		||||
            if (this.actor == this._firstLeaveActor)
 | 
			
		||||
                this._firstLeaveActor = this._dragOrigParent;
 | 
			
		||||
 | 
			
		||||
            if (this._dragInProgress && this._dragCancellable)
 | 
			
		||||
                this._cancelDrag(global.get_current_time());
 | 
			
		||||
            this.disconnectAll();
 | 
			
		||||
@@ -105,21 +101,15 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
 | 
			
		||||
        this._dragCancellable = true;
 | 
			
		||||
 | 
			
		||||
        // During the drag, we eat enter/leave events so that actors don't prelight.
 | 
			
		||||
        // But we remember the actors that we first left/last entered so we can
 | 
			
		||||
        // fix up the hover state after the drag ends.
 | 
			
		||||
        this._firstLeaveActor = null;
 | 
			
		||||
        this._lastEnterActor = null;
 | 
			
		||||
 | 
			
		||||
        this._eventsGrabbed = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress : function (actor, event) {
 | 
			
		||||
        if (event.get_button() != 1)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (Tweener.getTweenCount(actor))
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this._buttonDown = true;
 | 
			
		||||
        this._grabActor();
 | 
			
		||||
@@ -128,7 +118,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._dragStartX = stageX;
 | 
			
		||||
        this._dragStartY = stageY;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabActor: function() {
 | 
			
		||||
@@ -174,11 +164,11 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            } else if (this._dragActor != null && !this._animationInProgress) {
 | 
			
		||||
                // Drag must have been cancelled with Esc.
 | 
			
		||||
                this._dragComplete();
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Drag has never started.
 | 
			
		||||
                this._ungrabActor();
 | 
			
		||||
                return false;
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
            }
 | 
			
		||||
        // 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
 | 
			
		||||
@@ -194,16 +184,11 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            let symbol = event.get_key_symbol();
 | 
			
		||||
            if (symbol == Clutter.Escape) {
 | 
			
		||||
                this._cancelDrag(event.get_time());
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.LEAVE) {
 | 
			
		||||
            if (this._firstLeaveActor == null)
 | 
			
		||||
                this._firstLeaveActor = event.get_source();
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.ENTER) {
 | 
			
		||||
            this._lastEnterActor = event.get_source();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -244,14 +229,14 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        if (this._onEventId)
 | 
			
		||||
            this._ungrabActor();
 | 
			
		||||
        this._grabEvents();
 | 
			
		||||
        global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
 | 
			
		||||
 | 
			
		||||
        this._dragX = this._dragStartX = stageX;
 | 
			
		||||
        this._dragY = this._dragStartY = stageY;
 | 
			
		||||
 | 
			
		||||
        if (this.actor._delegate && this.actor._delegate.getDragActor) {
 | 
			
		||||
            this._dragActor = this.actor._delegate.getDragActor();
 | 
			
		||||
            this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
            Main.uiGroup.add_child(this._dragActor);
 | 
			
		||||
            this._dragActor.raise_top();
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
 | 
			
		||||
@@ -300,7 +285,8 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            this._dragOffsetX = actorStageX - this._dragStartX;
 | 
			
		||||
            this._dragOffsetY = actorStageY - this._dragStartY;
 | 
			
		||||
 | 
			
		||||
            this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
            this._dragOrigParent.remove_actor(this._dragActor);
 | 
			
		||||
            Main.uiGroup.add_child(this._dragActor);
 | 
			
		||||
            this._dragActor.raise_top();
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
        }
 | 
			
		||||
@@ -360,6 +346,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDragHover : function () {
 | 
			
		||||
        this._updateHoverId = 0;
 | 
			
		||||
        let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
 | 
			
		||||
                                                                  this._dragX, this._dragY);
 | 
			
		||||
        let dragEvent = {
 | 
			
		||||
@@ -374,8 +361,8 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            if (motionFunc) {
 | 
			
		||||
                let result = motionFunc(dragEvent);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -392,22 +379,23 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                                                             targY,
 | 
			
		||||
                                                             0);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            target = target.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
        global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
        return false;
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateDragHover: function() {
 | 
			
		||||
        if (this._updateHoverId)
 | 
			
		||||
            GLib.source_remove(this._updateHoverId);
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                            Lang.bind(this, this._updateDragHover));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._updateHoverId, '[gnome-shell] this._updateDragHover');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDragPosition : function (event) {
 | 
			
		||||
@@ -470,7 +458,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this._dragInProgress = false;
 | 
			
		||||
                    global.unset_cursor();
 | 
			
		||||
                    global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
                    this.emit('drag-end', event.get_time(), true);
 | 
			
		||||
                    this._dragComplete();
 | 
			
		||||
                    return true;
 | 
			
		||||
@@ -517,17 +505,12 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelDrag: function(eventTime) {
 | 
			
		||||
        if (this._updateHoverId) {
 | 
			
		||||
            GLib.source_remove(this._updateHoverId);
 | 
			
		||||
            this._updateHoverId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('drag-cancelled', eventTime);
 | 
			
		||||
        this._dragInProgress = false;
 | 
			
		||||
        let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
 | 
			
		||||
 | 
			
		||||
        if (this._actorDestroyed) {
 | 
			
		||||
            global.unset_cursor();
 | 
			
		||||
            global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
            if (!this._buttonDown)
 | 
			
		||||
                this._dragComplete();
 | 
			
		||||
            this.emit('drag-end', eventTime, false);
 | 
			
		||||
@@ -575,13 +558,14 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onAnimationComplete : function (dragActor, eventTime) {
 | 
			
		||||
        if (this._dragOrigParent) {
 | 
			
		||||
            dragActor.reparent(this._dragOrigParent);
 | 
			
		||||
            Main.uiGroup.remove_child(this._dragActor);
 | 
			
		||||
            this._dragOrigParent.add_actor(this._dragActor);
 | 
			
		||||
            dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
 | 
			
		||||
            dragActor.set_position(this._dragOrigX, this._dragOrigY);
 | 
			
		||||
        } else {
 | 
			
		||||
            dragActor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
        global.unset_cursor();
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
        this.emit('drag-end', eventTime, false);
 | 
			
		||||
 | 
			
		||||
        this._animationInProgress = false;
 | 
			
		||||
@@ -589,32 +573,16 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            this._dragComplete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Actor is an actor we have entered or left during the drag; call
 | 
			
		||||
    // st_widget_sync_hover on all StWidget ancestors
 | 
			
		||||
    _syncHover: function(actor) {
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            let parent = actor.get_parent();
 | 
			
		||||
            if (actor instanceof St.Widget)
 | 
			
		||||
                actor.sync_hover();
 | 
			
		||||
 | 
			
		||||
            actor = parent;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragComplete: function() {
 | 
			
		||||
        if (!this._actorDestroyed)
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, false);
 | 
			
		||||
 | 
			
		||||
        this._ungrabEvents();
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
 | 
			
		||||
        if (this._firstLeaveActor) {
 | 
			
		||||
            this._syncHover(this._firstLeaveActor);
 | 
			
		||||
            this._firstLeaveActor = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._lastEnterActor) {
 | 
			
		||||
            this._syncHover(this._lastEnterActor);
 | 
			
		||||
            this._lastEnterActor = null;
 | 
			
		||||
        if (this._updateHoverId) {
 | 
			
		||||
            GLib.source_remove(this._updateHoverId);
 | 
			
		||||
            this._updateHoverId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dragActor = undefined;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,14 +13,11 @@
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
@@ -28,97 +25,154 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Polkit = imports.gi.Polkit;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const CheckBox = imports.ui.checkBox;
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
let _endSessionDialog = null;
 | 
			
		||||
 | 
			
		||||
const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update';
 | 
			
		||||
 | 
			
		||||
const _ITEM_ICON_SIZE = 48;
 | 
			
		||||
const _DIALOG_ICON_SIZE = 32;
 | 
			
		||||
const _DIALOG_ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
 | 
			
		||||
 | 
			
		||||
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
 | 
			
		||||
<method name="Open">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="ao" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Close" />
 | 
			
		||||
<signal name="ConfirmedLogout" />
 | 
			
		||||
<signal name="ConfirmedReboot" />
 | 
			
		||||
<signal name="ConfirmedShutdown" />
 | 
			
		||||
<signal name="Canceled" />
 | 
			
		||||
<signal name="Closed" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const EndSessionDialogIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SessionManager.EndSessionDialog"> \
 | 
			
		||||
<method name="Open"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
    <arg type="ao" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Close" /> \
 | 
			
		||||
<signal name="ConfirmedLogout" /> \
 | 
			
		||||
<signal name="ConfirmedReboot" /> \
 | 
			
		||||
<signal name="ConfirmedShutdown" /> \
 | 
			
		||||
<signal name="Canceled" /> \
 | 
			
		||||
<signal name="Closed" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const logoutDialogContent = {
 | 
			
		||||
    subjectWithUser: C_("title", "Log Out %s"),
 | 
			
		||||
    subject: C_("title", "Log Out"),
 | 
			
		||||
    inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
 | 
			
		||||
    uninhibitedDescriptionWithUser: function(user, seconds) {
 | 
			
		||||
    descriptionWithUser: function(user, seconds) {
 | 
			
		||||
        return ngettext("%s will be logged out automatically in %d second.",
 | 
			
		||||
                        "%s will be logged out automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(user, seconds);
 | 
			
		||||
    },
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
        return ngettext("You will be logged out automatically in %d second.",
 | 
			
		||||
                        "You will be logged out automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Logging out of the system."),
 | 
			
		||||
    showBatteryWarning: false,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedLogout',
 | 
			
		||||
                       label:  C_("button", "Log Out") }],
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon'
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon',
 | 
			
		||||
    showOtherSessions: false,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const shutdownDialogContent = {
 | 
			
		||||
    subject: C_("title", "Power Off"),
 | 
			
		||||
    inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
    subjectWithUpdates: C_("title", "Install Updates & Power Off"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
        return ngettext("The system will power off automatically in %d second.",
 | 
			
		||||
                        "The system will power off automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Powering off the system."),
 | 
			
		||||
    checkBoxText: C_("checkbox", "Install pending software updates"),
 | 
			
		||||
    showBatteryWarning: true,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") },
 | 
			
		||||
                     { signal: 'ConfirmedShutdown',
 | 
			
		||||
                       label:  C_("button", "Power Off") }],
 | 
			
		||||
    iconName: 'system-shutdown-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartDialogContent = {
 | 
			
		||||
    subject: C_("title", "Restart"),
 | 
			
		||||
    inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
        return ngettext("The system will restart automatically in %d second.",
 | 
			
		||||
                        "The system will restart automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Restarting the system."),
 | 
			
		||||
    showBatteryWarning: false,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") }],
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartInstallDialogContent = {
 | 
			
		||||
 | 
			
		||||
    subject: C_("title", "Restart & Install Updates"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
        return ngettext("The system will automatically restart and install updates in %d second.",
 | 
			
		||||
                        "The system will automatically restart and install updates in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    showBatteryWarning: true,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart & Install") }],
 | 
			
		||||
    unusedFutureButtonForTranslation: C_("button", "Install & Power Off"),
 | 
			
		||||
    unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DialogContent = {
 | 
			
		||||
    0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
 | 
			
		||||
    1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
 | 
			
		||||
    2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
 | 
			
		||||
    2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent,
 | 
			
		||||
    3: restartInstallDialogContent
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
 | 
			
		||||
 | 
			
		||||
const LogindSessionIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.login1.Session"> \
 | 
			
		||||
    <property name="Id" type="s" access="read"/> \
 | 
			
		||||
    <property name="Remote" type="b" access="read"/> \
 | 
			
		||||
    <property name="Class" type="s" access="read"/> \
 | 
			
		||||
    <property name="Type" type="s" access="read"/> \
 | 
			
		||||
    <property name="State" type="s" access="read"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
 | 
			
		||||
 | 
			
		||||
const UPowerIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.UPower"> \
 | 
			
		||||
    <property name="OnBattery" type="b" access="read"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
 | 
			
		||||
 | 
			
		||||
function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    let [desktopFile] = inhibitor.GetAppIdSync();
 | 
			
		||||
    let desktopFile;
 | 
			
		||||
    try {
 | 
			
		||||
        [desktopFile] = inhibitor.GetAppIdSync();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        // XXX -- sometimes JIT inhibitors generated by gnome-session
 | 
			
		||||
        // get removed too soon. Don't fail in this case.
 | 
			
		||||
        log('gnome-session gave us a dead inhibitor: %s'.format(inhibitor.get_object_path()));
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!GLib.str_has_suffix(desktopFile, '.desktop'))
 | 
			
		||||
        desktopFile += '.desktop';
 | 
			
		||||
@@ -126,58 +180,6 @@ function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ListItem = new Lang.Class({
 | 
			
		||||
    Name: 'ListItem',
 | 
			
		||||
 | 
			
		||||
    _init: function(app, reason) {
 | 
			
		||||
        this._app = app;
 | 
			
		||||
        this._reason = reason;
 | 
			
		||||
 | 
			
		||||
        if (this._reason == null)
 | 
			
		||||
          this._reason = '';
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false});
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
 | 
			
		||||
                                     can_focus:   true,
 | 
			
		||||
                                     child:       layout,
 | 
			
		||||
                                     reactive:    true,
 | 
			
		||||
                                     x_align:     St.Align.START,
 | 
			
		||||
                                     x_fill:      true });
 | 
			
		||||
 | 
			
		||||
        this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
 | 
			
		||||
                                   child:       this._icon });
 | 
			
		||||
        layout.add(iconBin);
 | 
			
		||||
 | 
			
		||||
        let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
 | 
			
		||||
                                            vertical:    true });
 | 
			
		||||
        layout.add(textLayout);
 | 
			
		||||
 | 
			
		||||
        this._nameLabel = new St.Label({ text:        this._app.get_name(),
 | 
			
		||||
                                         style_class: 'end-session-dialog-app-list-item-name' });
 | 
			
		||||
        textLayout.add(this._nameLabel,
 | 
			
		||||
                       { expand: false,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ text:        this._reason,
 | 
			
		||||
                                                style_class: 'end-session-dialog-app-list-item-description' });
 | 
			
		||||
        this.actor.label_actor = this._nameLabel;
 | 
			
		||||
        textLayout.add(this._descriptionLabel,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
        this._app.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListItem.prototype);
 | 
			
		||||
 | 
			
		||||
// The logout timer only shows updates every 10 seconds
 | 
			
		||||
// until the last 10 seconds, then it shows updates every
 | 
			
		||||
// second.  This function takes a given time and returns
 | 
			
		||||
@@ -213,6 +215,18 @@ function _setLabelText(label, text) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _setCheckBoxLabel(checkBox, text) {
 | 
			
		||||
    let label = checkBox.getLabelActor();
 | 
			
		||||
 | 
			
		||||
    if (text) {
 | 
			
		||||
        label.set_text(text);
 | 
			
		||||
        checkBox.actor.show();
 | 
			
		||||
    } else {
 | 
			
		||||
        label.set_text('');
 | 
			
		||||
        checkBox.actor.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    // This always returns the same singleton object
 | 
			
		||||
    // By instantiating it initially, we register the
 | 
			
		||||
@@ -228,22 +242,37 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this.parent({ styleClass: 'end-session-dialog',
 | 
			
		||||
                      destroyOnClose: false });
 | 
			
		||||
 | 
			
		||||
        this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
        this._userManager = AccountsService.UserManager.get_default();
 | 
			
		||||
        this._user = this._userManager.get_user(GLib.get_user_name());
 | 
			
		||||
        this._updatesFile = Gio.File.new_for_path('/system-update');
 | 
			
		||||
        this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
 | 
			
		||||
 | 
			
		||||
        this._powerProxy = new UPowerProxy(Gio.DBus.system,
 | 
			
		||||
                                           'org.freedesktop.UPower',
 | 
			
		||||
                                           '/org/freedesktop/UPower',
 | 
			
		||||
                                           Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                               if (error) {
 | 
			
		||||
                                                   log(error.message);
 | 
			
		||||
                                                   return;
 | 
			
		||||
                                               }
 | 
			
		||||
                                               this._powerProxy.connect('g-properties-changed',
 | 
			
		||||
                                                                        Lang.bind(this, this._sync));
 | 
			
		||||
                                               this._sync();
 | 
			
		||||
                                           }));
 | 
			
		||||
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
        this._totalSecondsToStayOpen = 0;
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._sessions = [];
 | 
			
		||||
 | 
			
		||||
        this.connect('destroy',
 | 
			
		||||
                     Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.connect('opened',
 | 
			
		||||
                     Lang.bind(this, this._onOpened));
 | 
			
		||||
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded',
 | 
			
		||||
                                                Lang.bind(this, this._updateContent));
 | 
			
		||||
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._updateContent));
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._sync));
 | 
			
		||||
        this._userChangedId = this._user.connect('changed', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        let mainContentLayout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this.contentLayout.add(mainContentLayout,
 | 
			
		||||
@@ -257,14 +286,17 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let messageLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        let messageLayout = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                               style_class: 'end-session-dialog-layout' });
 | 
			
		||||
        mainContentLayout.add(messageLayout,
 | 
			
		||||
                              { y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this._subjectLabel,
 | 
			
		||||
                          { y_fill:  false,
 | 
			
		||||
                          { x_fill: false,
 | 
			
		||||
                            y_fill:  false,
 | 
			
		||||
                            x_align: St.Align.START,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
 | 
			
		||||
@@ -275,28 +307,46 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER,
 | 
			
		||||
                              Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(scrollView,
 | 
			
		||||
        this._checkBox = new CheckBox.CheckBox();
 | 
			
		||||
        this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
 | 
			
		||||
        messageLayout.add(this._checkBox.actor);
 | 
			
		||||
 | 
			
		||||
        this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
 | 
			
		||||
                                              text: _("Running on battery power: please plug in before installing updates.") });
 | 
			
		||||
        this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._batteryWarning.clutter_text.line_wrap = true;
 | 
			
		||||
        messageLayout.add(this._batteryWarning);
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(this._scrollView,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
        scrollView.hide();
 | 
			
		||||
        this._scrollView.hide();
 | 
			
		||||
 | 
			
		||||
        this._applicationList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        scrollView.add_actor(this._applicationList);
 | 
			
		||||
        this._inhibitorSection = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                                    style_class: 'end-session-dialog-inhibitor-layout' });
 | 
			
		||||
        this._scrollView.add_actor(this._inhibitorSection);
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-added',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_n_children() == 1)
 | 
			
		||||
                                              scrollView.show();
 | 
			
		||||
                                      }));
 | 
			
		||||
        this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
 | 
			
		||||
                                                 text: _("Some applications are busy or have unsaved work.") });
 | 
			
		||||
        this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
 | 
			
		||||
                                                   vertical: true });
 | 
			
		||||
        this._inhibitorSection.add_actor(this._applicationHeader);
 | 
			
		||||
        this._inhibitorSection.add_actor(this._applicationList);
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-removed',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_n_children() == 0)
 | 
			
		||||
                                              scrollView.hide();
 | 
			
		||||
                                      }));
 | 
			
		||||
        this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
 | 
			
		||||
                                             text: _("Other users are logged in.") });
 | 
			
		||||
        this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
 | 
			
		||||
                                               vertical: true });
 | 
			
		||||
        this._inhibitorSection.add_actor(this._sessionHeader);
 | 
			
		||||
        this._inhibitorSection.add_actor(this._sessionList);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('No permission to trigger offline updates: %s'.format(e.toString()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
 | 
			
		||||
@@ -307,52 +357,51 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._user.disconnect(this._userChangedId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDescription: function() {
 | 
			
		||||
        if (this.state != ModalDialog.State.OPENING &&
 | 
			
		||||
            this.state != ModalDialog.State.OPENED)
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED);
 | 
			
		||||
        if (!open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
 | 
			
		||||
        let subject = dialogContent.subject;
 | 
			
		||||
        let description;
 | 
			
		||||
 | 
			
		||||
        if (this._inhibitors.length > 0) {
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
            description = dialogContent.inhibitedDescription;
 | 
			
		||||
        } else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
 | 
			
		||||
            let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                      this._secondsLeft,
 | 
			
		||||
                                                      10);
 | 
			
		||||
        // Use different title when we are installing updates
 | 
			
		||||
        if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
 | 
			
		||||
            subject = dialogContent.subjectWithUpdates;
 | 
			
		||||
 | 
			
		||||
            if (this._user.is_loaded) {
 | 
			
		||||
                let realName = this._user.get_real_name();
 | 
			
		||||
 | 
			
		||||
                if (realName != null) {
 | 
			
		||||
                    if (dialogContent.subjectWithUser)
 | 
			
		||||
                        subject = dialogContent.subjectWithUser.format(realName);
 | 
			
		||||
 | 
			
		||||
                    if (dialogContent.uninhibitedDescriptionWithUser)
 | 
			
		||||
                        description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
 | 
			
		||||
                    else
 | 
			
		||||
                        description = dialogContent.uninhibitedDescription(displayTime);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!description)
 | 
			
		||||
                description = dialogContent.uninhibitedDescription(displayTime);
 | 
			
		||||
        } else {
 | 
			
		||||
            description = dialogContent.endDescription;
 | 
			
		||||
        if (dialogContent.showBatteryWarning) {
 | 
			
		||||
            // Warn when running on battery power
 | 
			
		||||
            if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
 | 
			
		||||
                this._batteryWarning.opacity = 255;
 | 
			
		||||
            else
 | 
			
		||||
                this._batteryWarning.opacity = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _setLabelText(this._subjectLabel, subject);
 | 
			
		||||
        _setLabelText(this._descriptionLabel, description);
 | 
			
		||||
    },
 | 
			
		||||
        let description;
 | 
			
		||||
        let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                  this._secondsLeft,
 | 
			
		||||
                                                  10);
 | 
			
		||||
 | 
			
		||||
    _updateContent: function() {
 | 
			
		||||
        if (this.state != ModalDialog.State.OPENING &&
 | 
			
		||||
            this.state != ModalDialog.State.OPENED)
 | 
			
		||||
            return;
 | 
			
		||||
        if (this._user.is_loaded) {
 | 
			
		||||
            let realName = this._user.get_real_name();
 | 
			
		||||
 | 
			
		||||
            if (realName != null) {
 | 
			
		||||
                if (dialogContent.subjectWithUser)
 | 
			
		||||
                    subject = dialogContent.subjectWithUser.format(realName);
 | 
			
		||||
 | 
			
		||||
                if (dialogContent.descriptionWithUser)
 | 
			
		||||
                    description = dialogContent.descriptionWithUser(realName, displayTime);
 | 
			
		||||
                else
 | 
			
		||||
                    description = dialogContent.description(displayTime);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!description)
 | 
			
		||||
            description = dialogContent.description(displayTime);
 | 
			
		||||
 | 
			
		||||
        _setLabelText(this._descriptionLabel, description);
 | 
			
		||||
        _setLabelText(this._subjectLabel, subject);
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
        if (dialogContent.iconName) {
 | 
			
		||||
@@ -367,7 +416,11 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            avatarWidget.update();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateDescription();
 | 
			
		||||
        let hasApplications = this._applications.length > 0;
 | 
			
		||||
        let hasSessions = this._sessions.length > 0;
 | 
			
		||||
        this._scrollView.visible = hasApplications || hasSessions;
 | 
			
		||||
        this._applicationHeader.visible = hasApplications;
 | 
			
		||||
        this._sessionHeader.visible = hasSessions;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateButtons: function() {
 | 
			
		||||
@@ -407,20 +460,78 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirm: function(signal) {
 | 
			
		||||
        this._fadeOutDialog();
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        this._dbusImpl.emit_signal(signal, null);
 | 
			
		||||
        let callback = Lang.bind(this, function() {
 | 
			
		||||
            this._fadeOutDialog();
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
            this._dbusImpl.emit_signal(signal, null);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Offline update not available; just emit the signal
 | 
			
		||||
        if (!this._checkBox.actor.visible) {
 | 
			
		||||
            callback();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Trigger the offline update as requested
 | 
			
		||||
        if (this._checkBox.actor.checked) {
 | 
			
		||||
            switch (signal) {
 | 
			
		||||
                case "ConfirmedReboot":
 | 
			
		||||
                    this._triggerOfflineUpdateReboot(callback);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "ConfirmedShutdown":
 | 
			
		||||
                    // To actually trigger the offline update, we need to
 | 
			
		||||
                    // reboot to do the upgrade. When the upgrade is complete,
 | 
			
		||||
                    // the computer will shut down automatically.
 | 
			
		||||
                    signal = "ConfirmedReboot";
 | 
			
		||||
                    this._triggerOfflineUpdateShutdown(callback);
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    callback();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            this._triggerOfflineUpdateCancel(callback);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpened: function() {
 | 
			
		||||
        if (this._inhibitors.length == 0)
 | 
			
		||||
            this._startTimer();
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _triggerOfflineUpdateReboot: function(callback) {
 | 
			
		||||
        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _triggerOfflineUpdateShutdown: function(callback) {
 | 
			
		||||
        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _triggerOfflineUpdateCancel: function(callback) {
 | 
			
		||||
        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _pkexecSpawn: function(argv, callback) {
 | 
			
		||||
        let ret, pid;
 | 
			
		||||
        try {
 | 
			
		||||
            [ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null,
 | 
			
		||||
                                          GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH,
 | 
			
		||||
                                          null);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString()));
 | 
			
		||||
            callback();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
 | 
			
		||||
            GLib.spawn_close_pid(pid);
 | 
			
		||||
 | 
			
		||||
            callback();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startTimer: function() {
 | 
			
		||||
        let startTime = GLib.get_monotonic_time();
 | 
			
		||||
        this._secondsLeft = this._totalSecondsToStayOpen;
 | 
			
		||||
        this._updateDescription();
 | 
			
		||||
 | 
			
		||||
        this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
@@ -429,20 +540,22 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
                this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
 | 
			
		||||
                if (this._secondsLeft > 0) {
 | 
			
		||||
                    this._updateDescription();
 | 
			
		||||
                    return true;
 | 
			
		||||
                    this._sync();
 | 
			
		||||
                    return GLib.SOURCE_CONTINUE;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let dialogContent = DialogContent[this._type];
 | 
			
		||||
                let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
 | 
			
		||||
                this._confirm(button.signal);
 | 
			
		||||
                this._timerId = 0;
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
            }));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._timerId, '[gnome-shell] this._confirm');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _stopTimer: function() {
 | 
			
		||||
        if (this._timerId != 0) {
 | 
			
		||||
        if (this._timerId > 0) {
 | 
			
		||||
            Mainloop.source_remove(this._timerId);
 | 
			
		||||
            this._timerId = 0;
 | 
			
		||||
        }
 | 
			
		||||
@@ -450,8 +563,33 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _constructListItemForApp: function(inhibitor, app) {
 | 
			
		||||
        let actor = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item',
 | 
			
		||||
                                       can_focus: true });
 | 
			
		||||
        actor.add(app.create_icon_texture(_ITEM_ICON_SIZE));
 | 
			
		||||
 | 
			
		||||
        let textLayout = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                            y_expand: true,
 | 
			
		||||
                                            y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        actor.add(textLayout);
 | 
			
		||||
 | 
			
		||||
        let nameLabel = new St.Label({ text: app.get_name(),
 | 
			
		||||
                                       style_class: 'end-session-dialog-app-list-item-name' });
 | 
			
		||||
        textLayout.add(nameLabel);
 | 
			
		||||
        actor.label_actor = nameLabel;
 | 
			
		||||
 | 
			
		||||
        let [reason] = inhibitor.GetReasonSync();
 | 
			
		||||
        if (reason) {
 | 
			
		||||
            let reasonLabel = new St.Label({ text: reason,
 | 
			
		||||
                                             style_class: 'end-session-dialog-app-list-item-description' });
 | 
			
		||||
            textLayout.add(reasonLabel);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInhibitorLoaded: function(inhibitor) {
 | 
			
		||||
        if (this._inhibitors.indexOf(inhibitor) < 0) {
 | 
			
		||||
        if (this._applications.indexOf(inhibitor) < 0) {
 | 
			
		||||
            // Stale inhibitor
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -459,43 +597,128 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        let app = findAppFromInhibitor(inhibitor);
 | 
			
		||||
 | 
			
		||||
        if (app) {
 | 
			
		||||
            let [reason] = inhibitor.GetReasonSync();
 | 
			
		||||
            let item = new ListItem(app, reason);
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             this.close();
 | 
			
		||||
                         }));
 | 
			
		||||
            this._applicationList.add(item.actor, { x_fill: true });
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
            let actor = this._constructListItemForApp(inhibitor, app);
 | 
			
		||||
            this._applicationList.add(actor);
 | 
			
		||||
        } else {
 | 
			
		||||
            // inhibiting app is a service, not an application
 | 
			
		||||
            this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
 | 
			
		||||
            this._applications.splice(this._applications.indexOf(inhibitor), 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _constructListItemForSession: function(session) {
 | 
			
		||||
        let avatar = new UserWidget.Avatar(session.user, { iconSize: _ITEM_ICON_SIZE });
 | 
			
		||||
        avatar.update();
 | 
			
		||||
 | 
			
		||||
        let userName = session.user.get_real_name() ? session.user.get_real_name() : session.username;
 | 
			
		||||
        let userLabelText;
 | 
			
		||||
 | 
			
		||||
        if (session.remote)
 | 
			
		||||
            /* Translators: Remote here refers to a remote session, like a ssh login */
 | 
			
		||||
            userLabelText = _("%s (remote)").format(userName);
 | 
			
		||||
        else if (session.type == "tty")
 | 
			
		||||
            /* Translators: Console here refers to a tty like a VT console */
 | 
			
		||||
            userLabelText = _("%s (console)").format(userName);
 | 
			
		||||
        else
 | 
			
		||||
            userLabelText = userName;
 | 
			
		||||
 | 
			
		||||
        let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
 | 
			
		||||
                                       can_focus: true });
 | 
			
		||||
        actor.add(avatar.actor);
 | 
			
		||||
 | 
			
		||||
        let nameLabel = new St.Label({ text: userLabelText,
 | 
			
		||||
                                       style_class: 'end-session-dialog-session-list-item-name',
 | 
			
		||||
                                       y_expand: true,
 | 
			
		||||
                                       y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        actor.add(nameLabel);
 | 
			
		||||
        actor.label_actor = nameLabel;
 | 
			
		||||
 | 
			
		||||
        return actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadSessions: function() {
 | 
			
		||||
        this._loginManager.listSessions(Lang.bind(this, function(result) {
 | 
			
		||||
            let n = 0;
 | 
			
		||||
            for (let i = 0; i < result.length; i++) {
 | 
			
		||||
                let[id, uid, userName, seat, sessionPath] = result[i];
 | 
			
		||||
                let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
 | 
			
		||||
 | 
			
		||||
                if (proxy.Class != 'user')
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (proxy.State == 'closing')
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                let session = { user: this._userManager.get_user(userName),
 | 
			
		||||
                                username: userName,
 | 
			
		||||
                                type: proxy.Type,
 | 
			
		||||
                                remote: proxy.Remote };
 | 
			
		||||
                this._sessions.push(session);
 | 
			
		||||
 | 
			
		||||
                let actor = this._constructListItemForSession(session);
 | 
			
		||||
                this._sessionList.add(actor);
 | 
			
		||||
 | 
			
		||||
                // limit the number of entries
 | 
			
		||||
                n++;
 | 
			
		||||
                if (n == MAX_USERS_IN_SESSION_DIALOG)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._sync();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    OpenAsync: function(parameters, invocation) {
 | 
			
		||||
        let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (this._type == 2 && this._updatesFile.query_exists(null))
 | 
			
		||||
            this._type = 3;
 | 
			
		||||
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        this._sessions = [];
 | 
			
		||||
        this._sessionList.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        if (!(this._type in DialogContent)) {
 | 
			
		||||
            invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
 | 
			
		||||
                                         "Unknown dialog type requested");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < inhibitorObjectPaths.length; i++) {
 | 
			
		||||
            let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                this._onInhibitorLoaded(proxy);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this._inhibitors.push(inhibitor);
 | 
			
		||||
            this._applications.push(inhibitor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dialogContent.showOtherSessions)
 | 
			
		||||
            this._loadSessions();
 | 
			
		||||
 | 
			
		||||
        let preparedUpdate = this._preparedUpdateFile.query_exists(null);
 | 
			
		||||
        let updateAlreadyTriggered = this._updatesFile.query_exists(null);
 | 
			
		||||
        let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
 | 
			
		||||
 | 
			
		||||
        _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
 | 
			
		||||
        this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed);
 | 
			
		||||
        this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered);
 | 
			
		||||
 | 
			
		||||
        // 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 || preparedUpdate && updateAlreadyTriggered && !updatesAllowed));
 | 
			
		||||
 | 
			
		||||
        this._updateButtons();
 | 
			
		||||
 | 
			
		||||
        if (!this.open(timestamp)) {
 | 
			
		||||
@@ -504,7 +727,8 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
        this._startTimer();
 | 
			
		||||
        this._sync();
 | 
			
		||||
 | 
			
		||||
        let signalId = this.connect('opened',
 | 
			
		||||
                                    Lang.bind(this, function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,14 @@ imports.gi.versions.Gio = '2.0';
 | 
			
		||||
imports.gi.versions.Gdk = '3.0';
 | 
			
		||||
imports.gi.versions.GdkPixbuf = '2.0';
 | 
			
		||||
imports.gi.versions.Gtk = '3.0';
 | 
			
		||||
imports.gi.versions.TelepathyGLib = '0.12';
 | 
			
		||||
imports.gi.versions.TelepathyLogger = '0.2';
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;;
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +42,25 @@ function _patchContainerClass(containerClass) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _patchLayoutClass(layoutClass, styleProps) {
 | 
			
		||||
    if (styleProps)
 | 
			
		||||
        layoutClass.prototype.hookup_style = function(container) {
 | 
			
		||||
            container.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
                let node = container.get_theme_node();
 | 
			
		||||
                for (let prop in styleProps) {
 | 
			
		||||
                    let [found, length] = node.lookup_length(styleProps[prop], false);
 | 
			
		||||
                    if (found)
 | 
			
		||||
                        this[prop] = length;
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
    layoutClass.prototype.child_set = function(actor, props) {
 | 
			
		||||
        let meta = this.get_child_meta(actor.get_parent(), actor);
 | 
			
		||||
        for (let prop in props)
 | 
			
		||||
            meta[prop] = props[prop];
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _makeLoggingFunc(func) {
 | 
			
		||||
    return function() {
 | 
			
		||||
        return func([].join.call(arguments, ', '));
 | 
			
		||||
@@ -60,6 +82,12 @@ function init() {
 | 
			
		||||
    _patchContainerClass(St.BoxLayout);
 | 
			
		||||
    _patchContainerClass(St.Table);
 | 
			
		||||
 | 
			
		||||
    _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
 | 
			
		||||
                                             column_spacing: 'spacing-columns' });
 | 
			
		||||
    _patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
 | 
			
		||||
                                            column_spacing: 'spacing-columns' });
 | 
			
		||||
    _patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
 | 
			
		||||
 | 
			
		||||
    Clutter.Actor.prototype.toString = function() {
 | 
			
		||||
        return St.describe_actor(this);
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
 | 
			
		||||
                           default: true
 | 
			
		||||
                         }]);
 | 
			
		||||
 | 
			
		||||
        let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
 | 
			
		||||
        let message = _("Download and install “%s” from extensions.gnome.org?").format(info.name);
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout();
 | 
			
		||||
        this.contentLayout.add(box);
 | 
			
		||||
 
 | 
			
		||||
@@ -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 EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
 | 
			
		||||
 | 
			
		||||
var initted = false;
 | 
			
		||||
var enabled;
 | 
			
		||||
@@ -76,7 +77,11 @@ function disableExtension(uuid) {
 | 
			
		||||
        theme.unload_stylesheet(extension.stylesheet.get_path());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extension.stateObj.disable();
 | 
			
		||||
    try {
 | 
			
		||||
        extension.stateObj.disable();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < order.length; i++) {
 | 
			
		||||
        let uuid = order[i];
 | 
			
		||||
@@ -89,8 +94,10 @@ function disableExtension(uuid) {
 | 
			
		||||
 | 
			
		||||
    extensionOrder.splice(orderIdx, 1);
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    if ( extension.state != ExtensionState.ERROR ) {
 | 
			
		||||
        extension.state = ExtensionState.DISABLED;
 | 
			
		||||
        _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enableExtension(uuid) {
 | 
			
		||||
@@ -117,10 +124,15 @@ function enableExtension(uuid) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extension.stateObj.enable();
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.ENABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    try {
 | 
			
		||||
        extension.stateObj.enable();
 | 
			
		||||
        extension.state = ExtensionState.ENABLED;
 | 
			
		||||
        _signals.emit('extension-state-changed', extension);
 | 
			
		||||
        return;
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logExtensionError(uuid, error) {
 | 
			
		||||
@@ -145,12 +157,15 @@ function loadExtension(extension) {
 | 
			
		||||
    // Default to error, we set success as the last step
 | 
			
		||||
    extension.state = ExtensionState.ERROR;
 | 
			
		||||
 | 
			
		||||
    if (ExtensionUtils.isOutOfDate(extension)) {
 | 
			
		||||
    let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
 | 
			
		||||
 | 
			
		||||
    if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
 | 
			
		||||
        extension.state = ExtensionState.OUT_OF_DATE;
 | 
			
		||||
    } else {
 | 
			
		||||
        let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
 | 
			
		||||
        if (enabled) {
 | 
			
		||||
            initExtension(extension.uuid);
 | 
			
		||||
            if (!initExtension(extension.uuid))
 | 
			
		||||
                return;
 | 
			
		||||
            if (extension.state == ExtensionState.DISABLED)
 | 
			
		||||
                enableExtension(extension.uuid);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -205,7 +220,12 @@ function initExtension(uuid) {
 | 
			
		||||
    extensionModule = extension.imports.extension;
 | 
			
		||||
 | 
			
		||||
    if (extensionModule.init) {
 | 
			
		||||
        extensionState = extensionModule.init(extension);
 | 
			
		||||
        try {
 | 
			
		||||
            extensionState = extensionModule.init(extension);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!extensionState)
 | 
			
		||||
@@ -214,6 +234,7 @@ function initExtension(uuid) {
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-loaded', uuid);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getEnabledExtensions() {
 | 
			
		||||
@@ -235,11 +256,7 @@ function onEnabledExtensionsChanged() {
 | 
			
		||||
    newEnabledExtensions.filter(function(uuid) {
 | 
			
		||||
        return enabledExtensions.indexOf(uuid) == -1;
 | 
			
		||||
    }).forEach(function(uuid) {
 | 
			
		||||
        try {
 | 
			
		||||
            enableExtension(uuid);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
        enableExtension(uuid);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Find and disable all the newly disabled extensions: UUIDs found in the
 | 
			
		||||
@@ -247,27 +264,30 @@ function onEnabledExtensionsChanged() {
 | 
			
		||||
    enabledExtensions.filter(function(item) {
 | 
			
		||||
        return newEnabledExtensions.indexOf(item) == -1;
 | 
			
		||||
    }).forEach(function(uuid) {
 | 
			
		||||
        try {
 | 
			
		||||
            disableExtension(uuid);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
        disableExtension(uuid);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    enabledExtensions = newEnabledExtensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _onVersionValidationChanged() {
 | 
			
		||||
    if (Main.sessionMode.allowExtensions) {
 | 
			
		||||
        enabledExtensions.forEach(function(uuid) {
 | 
			
		||||
            if (ExtensionUtils.extensions[uuid])
 | 
			
		||||
                reloadExtension(ExtensionUtils.extensions[uuid]);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadExtensions() {
 | 
			
		||||
    global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
 | 
			
		||||
    global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged);
 | 
			
		||||
 | 
			
		||||
    enabledExtensions = getEnabledExtensions();
 | 
			
		||||
 | 
			
		||||
    let finder = new ExtensionUtils.ExtensionFinder();
 | 
			
		||||
    finder.connect('extension-found', function(signals, extension) {
 | 
			
		||||
        try {
 | 
			
		||||
            loadExtension(extension);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(extension.uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
    finder.connect('extension-found', function(finder, extension) {
 | 
			
		||||
        loadExtension(extension);
 | 
			
		||||
    });
 | 
			
		||||
    finder.scanExtensions();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								js/ui/focusCaretTracker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,87 @@
 | 
			
		||||
/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2012 Inclusive Design Research Centre, OCAD University.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 *   Joseph Scheuhammer <clown@alum.mit.edu>
 | 
			
		||||
 * Contributor:
 | 
			
		||||
 *   Magdalen Berns <m.berns@sms.ed.ac.uk>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Atspi = imports.gi.Atspi;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const CARETMOVED        = 'object:text-caret-moved';
 | 
			
		||||
const STATECHANGED      = 'object:state-changed';
 | 
			
		||||
 | 
			
		||||
const FocusCaretTracker = new Lang.Class({
 | 
			
		||||
    Name: 'FocusCaretTracker',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        Atspi.init();
 | 
			
		||||
        Atspi.set_timeout(250, 250);
 | 
			
		||||
 | 
			
		||||
        this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
 | 
			
		||||
 | 
			
		||||
        this._focusListenerRegistered = false;
 | 
			
		||||
        this._caretListenerRegistered = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged: function(event) {
 | 
			
		||||
        if (event.type.indexOf(STATECHANGED) == 0)
 | 
			
		||||
            this.emit('focus-changed', event);
 | 
			
		||||
        else if (event.type == CARETMOVED)
 | 
			
		||||
            this.emit('caret-moved', event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerFocusListener: function() {
 | 
			
		||||
        if (this._focusListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Ignore the return value, we get an exception if they fail
 | 
			
		||||
        // And they should never fail
 | 
			
		||||
        this._atspiListener.register(STATECHANGED + ':focused');
 | 
			
		||||
        this._atspiListener.register(STATECHANGED + ':selected');
 | 
			
		||||
        this._focusListenerRegistered = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerCaretListener: function() {
 | 
			
		||||
        if (this._caretListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.register(CARETMOVED);
 | 
			
		||||
        this._caretListenerRegistered = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterFocusListener: function() {
 | 
			
		||||
        if (!this._focusListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.deregister(STATECHANGED + ':focused');
 | 
			
		||||
        this._atspiListener.deregister(STATECHANGED + ':selected');
 | 
			
		||||
        this._focusListenerRegistered = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterCaretListener: function() {
 | 
			
		||||
        if (!this._caretListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.deregister(CARETMOVED);
 | 
			
		||||
        this._caretListenerRegistered = false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(FocusCaretTracker.prototype);
 | 
			
		||||
@@ -10,6 +10,31 @@ const St = imports.gi.St;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
let _capturedEventId = 0;
 | 
			
		||||
let _grabHelperStack = [];
 | 
			
		||||
function _onCapturedEvent(actor, event) {
 | 
			
		||||
    let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
 | 
			
		||||
    return grabHelper.onCapturedEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _pushGrabHelper(grabHelper) {
 | 
			
		||||
    _grabHelperStack.push(grabHelper);
 | 
			
		||||
 | 
			
		||||
    if (_capturedEventId == 0)
 | 
			
		||||
        _capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _popGrabHelper(grabHelper) {
 | 
			
		||||
    let poppedHelper = _grabHelperStack.pop();
 | 
			
		||||
    if (poppedHelper != grabHelper)
 | 
			
		||||
        throw new Error("incorrect grab helper pop");
 | 
			
		||||
 | 
			
		||||
    if (_grabHelperStack.length == 0) {
 | 
			
		||||
        global.stage.disconnect(_capturedEventId);
 | 
			
		||||
        _capturedEventId = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GrabHelper:
 | 
			
		||||
// @owner: the actor that owns the GrabHelper
 | 
			
		||||
// @params: optional parameters to pass to Main.pushModal()
 | 
			
		||||
@@ -31,14 +56,9 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        this._grabStack = [];
 | 
			
		||||
 | 
			
		||||
        this._actors = [];
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
        this._focusWindowChangedId = 0;
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
        this._isUngrabbingCount = 0;
 | 
			
		||||
 | 
			
		||||
        this._modalCount = 0;
 | 
			
		||||
        this._grabFocusCount = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // addActor:
 | 
			
		||||
@@ -118,38 +138,36 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
    // grab:
 | 
			
		||||
    // @params: A bunch of parameters, see below
 | 
			
		||||
    //
 | 
			
		||||
    // Grabs the mouse and keyboard, according to the GrabHelper's
 | 
			
		||||
    // parameters. If @newFocus is not %null, then the keyboard focus
 | 
			
		||||
    // is moved to the first #StWidget:can-focus widget inside it.
 | 
			
		||||
    // The general effect of a "grab" is to ensure that the passed in actor
 | 
			
		||||
    // and all actors inside the grab get exclusive control of the mouse and
 | 
			
		||||
    // keyboard, with the grab automatically being dropped if the user tries
 | 
			
		||||
    // to dismiss it. The actor is passed in through @params.actor.
 | 
			
		||||
    //
 | 
			
		||||
    // The grab will automatically be dropped if:
 | 
			
		||||
    //   - The user clicks outside the grabbed actors
 | 
			
		||||
    //   - The user types Escape
 | 
			
		||||
    //   - The keyboard focus is moved outside the grabbed actors
 | 
			
		||||
    //   - A window is focused
 | 
			
		||||
    // grab() can be called multiple times, with the scope of the grab being
 | 
			
		||||
    // changed to a different actor every time. A nested grab does not have
 | 
			
		||||
    // to have its grabbed actor inside the parent grab actors.
 | 
			
		||||
    //
 | 
			
		||||
    // If @params.actor is not null, then it will be focused as the
 | 
			
		||||
    // new actor. If you attempt to grab an already focused actor, the
 | 
			
		||||
    // request to be focused will be ignored. The actor will not be
 | 
			
		||||
    // added to the grab stack, so do not call a paired ungrab().
 | 
			
		||||
    // Grabs can be automatically dropped if the user tries to dismiss it
 | 
			
		||||
    // in one of two ways: the user clicking outside the currently grabbed
 | 
			
		||||
    // actor, or the user typing the Escape key.
 | 
			
		||||
    //
 | 
			
		||||
    // If @params contains { modal: true }, then grab() will push a modal
 | 
			
		||||
    // on the owner of the GrabHelper. As long as there is at least one
 | 
			
		||||
    // { modal: true } actor on the grab stack, the grab will be kept.
 | 
			
		||||
    // When the last { modal: true } actor is ungrabbed, then the modal
 | 
			
		||||
    // will be dropped. A modal grab can fail if there is already a grab
 | 
			
		||||
    // in effect from aother application; in this case the function returns
 | 
			
		||||
    // false and nothing happens. Non-modal grabs can never fail.
 | 
			
		||||
    // If the user clicks outside the grabbed actors, and the clicked on
 | 
			
		||||
    // actor is part of a previous grab in the stack, grabs will be popped
 | 
			
		||||
    // until that grab is active. However, the click event will not be
 | 
			
		||||
    // replayed to the actor.
 | 
			
		||||
    //
 | 
			
		||||
    // If @params contains { grabFocus: true }, then if you call grab()
 | 
			
		||||
    // while the shell is outside the overview, it will set the stage
 | 
			
		||||
    // input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
 | 
			
		||||
    // revert it back, and re-focus the previously-focused window (if
 | 
			
		||||
    // another window hasn't been explicitly focused before then).
 | 
			
		||||
    // If the user types the Escape key, one grab from the grab stack will
 | 
			
		||||
    // be popped.
 | 
			
		||||
    //
 | 
			
		||||
    // When a grab is popped by user interacting as described above, if you
 | 
			
		||||
    // pass a callback as @params.onUngrab, it will be called with %true.
 | 
			
		||||
    //
 | 
			
		||||
    // If @params.focus is not null, we'll set the key focus directly
 | 
			
		||||
    // to that actor instead of navigating in @params.actor. This is for
 | 
			
		||||
    // use cases like menus, where we want to grab the menu actor, but keep
 | 
			
		||||
    // focus on the clicked on menu item.
 | 
			
		||||
    grab: function(params) {
 | 
			
		||||
        params = Params.parse(params, { actor: null,
 | 
			
		||||
                                        modal: false,
 | 
			
		||||
                                        grabFocus: false,
 | 
			
		||||
                                        focus: null,
 | 
			
		||||
                                        onUngrab: null });
 | 
			
		||||
 | 
			
		||||
@@ -162,24 +180,18 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        params.savedFocus = focus;
 | 
			
		||||
 | 
			
		||||
        if (params.modal && !this._takeModalGrab())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (params.grabFocus && !this._takeFocusGrab(hadFocus))
 | 
			
		||||
        if (!this._takeModalGrab())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._grabStack.push(params);
 | 
			
		||||
 | 
			
		||||
        if (params.focus) {
 | 
			
		||||
            params.focus.grab_key_focus();
 | 
			
		||||
        } else if (newFocus && (hadFocus || params.grabFocus)) {
 | 
			
		||||
        } else if (newFocus && hadFocus) {
 | 
			
		||||
            if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
 | 
			
		||||
                newFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((params.grabFocus || params.modal) && !this._capturedEventId)
 | 
			
		||||
            this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -188,6 +200,8 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (firstGrab) {
 | 
			
		||||
            if (!Main.pushModal(this._owner, this._modalParams))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            _pushGrabHelper(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._modalCount++;
 | 
			
		||||
@@ -199,56 +213,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (this._modalCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        _popGrabHelper(this);
 | 
			
		||||
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._owner);
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _takeFocusGrab: function(hadFocus) {
 | 
			
		||||
        let firstGrab = (this._grabFocusCount == 0);
 | 
			
		||||
        if (firstGrab) {
 | 
			
		||||
            let metaDisplay = global.screen.get_display();
 | 
			
		||||
 | 
			
		||||
            this._grabbedFromKeynav = hadFocus;
 | 
			
		||||
            this._preGrabInputMode = global.stage_input_mode;
 | 
			
		||||
 | 
			
		||||
            if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
 | 
			
		||||
            this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 | 
			
		||||
            this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._grabFocusCount++;
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _releaseFocusGrab: function() {
 | 
			
		||||
        this._grabFocusCount--;
 | 
			
		||||
        if (this._grabFocusCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._keyFocusNotifyId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._keyFocusNotifyId);
 | 
			
		||||
            this._keyFocusNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._focusWindowChangedId > 0) {
 | 
			
		||||
            let metaDisplay = global.screen.get_display();
 | 
			
		||||
            metaDisplay.disconnect(this._focusWindowChangedId);
 | 
			
		||||
            this._focusWindowChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let prePopInputMode = global.stage_input_mode;
 | 
			
		||||
 | 
			
		||||
        if (this._grabbedFromKeynav) {
 | 
			
		||||
            if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
 | 
			
		||||
                prePopInputMode != Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.screen.focus_default_window(global.display.get_current_time_roundtrip());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // ignoreRelease:
 | 
			
		||||
    //
 | 
			
		||||
    // Make sure that the next button release event evaluated by the
 | 
			
		||||
@@ -262,10 +234,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
    // ungrab:
 | 
			
		||||
    // @params: The parameters for the grab; see below.
 | 
			
		||||
    //
 | 
			
		||||
    // Pops an actor from the grab stack, potentially dropping the grab.
 | 
			
		||||
    // Pops @params.actor from the grab stack, potentially dropping
 | 
			
		||||
    // the grab. If the actor is not on the grab stack, this call is
 | 
			
		||||
    // ignored with no ill effects.
 | 
			
		||||
    //
 | 
			
		||||
    // If the actor that was popped from the grab stack was not the actor
 | 
			
		||||
    // That was passed in, this call is ignored.
 | 
			
		||||
    // If the actor is not at the top of the grab stack, grabs are
 | 
			
		||||
    // popped until the grabbed actor is at the top of the grab stack.
 | 
			
		||||
    // The onUngrab callback for every grab is called for every popped
 | 
			
		||||
    // grab with the parameter %false.
 | 
			
		||||
    ungrab: function(params) {
 | 
			
		||||
        params = Params.parse(params, { actor: this.currentGrab.actor,
 | 
			
		||||
                                        isUser: false });
 | 
			
		||||
@@ -274,14 +250,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (grabStackIndex < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // We may get key focus changes when calling onUngrab, which
 | 
			
		||||
        // would cause an extra ungrab() on the next actor in the
 | 
			
		||||
        // stack, which is wrong. Ignore key focus changes during the
 | 
			
		||||
        // ungrab, and restore the saved key focus ourselves afterwards.
 | 
			
		||||
        // We use a count as ungrab() may be re-entrant, as onUngrab()
 | 
			
		||||
        // may ungrab additional actors.
 | 
			
		||||
        this._isUngrabbingCount++;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        let hadFocus = focus && this._isWithinGrabbedActor(focus);
 | 
			
		||||
 | 
			
		||||
@@ -296,18 +264,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            if (poppedGrab.onUngrab)
 | 
			
		||||
                poppedGrab.onUngrab(params.isUser);
 | 
			
		||||
 | 
			
		||||
            if (poppedGrab.modal)
 | 
			
		||||
                this._releaseModalGrab();
 | 
			
		||||
 | 
			
		||||
            if (poppedGrab.grabFocus)
 | 
			
		||||
                this._releaseFocusGrab();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.grabbed && this._capturedEventId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
 | 
			
		||||
            this._ignoreRelease = false;
 | 
			
		||||
            this._releaseModalGrab();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (hadFocus) {
 | 
			
		||||
@@ -315,17 +272,15 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            if (poppedGrab.savedFocus)
 | 
			
		||||
                poppedGrab.savedFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isUngrabbingCount--;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
    onCapturedEvent: function(event) {
 | 
			
		||||
        let type = event.type();
 | 
			
		||||
 | 
			
		||||
        if (type == Clutter.EventType.KEY_PRESS &&
 | 
			
		||||
            event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let press = type == Clutter.EventType.BUTTON_PRESS;
 | 
			
		||||
@@ -334,17 +289,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (release && this._ignoreRelease) {
 | 
			
		||||
            this._ignoreRelease = false;
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!button && this._modalCount == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (this._isWithinGrabbedActor(event.get_source()))
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (Main.keyboard.shouldTakeEvent(event))
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (button) {
 | 
			
		||||
            // If we have a press event, ignore the next event,
 | 
			
		||||
@@ -353,24 +305,9 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
                this._ignoreRelease = true;
 | 
			
		||||
            let i = this._actorInGrabStack(event.get_source()) + 1;
 | 
			
		||||
            this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._modalCount > 0;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function() {
 | 
			
		||||
        if (this._isUngrabbingCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        if (!focus || !this._isWithinGrabbedActor(focus))
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _focusWindowChanged: function() {
 | 
			
		||||
        let metaDisplay = global.screen.get_display();
 | 
			
		||||
        if (metaDisplay.focus_window != null)
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ const CandidateArea = new Lang.Class({
 | 
			
		||||
            let j = i;
 | 
			
		||||
            box.connect('button-release-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
                this.emit('candidate-clicked', j, event.get_button(), event.get_state());
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -114,9 +115,6 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
    Name: 'CandidatePopup',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._cursor = new St.Bin({ opacity: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._cursor);
 | 
			
		||||
 | 
			
		||||
        this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
 | 
			
		||||
        this._boxPointer.actor.visible = false;
 | 
			
		||||
        this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
 | 
			
		||||
@@ -157,10 +155,9 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        panelService.connect('set-cursor-location',
 | 
			
		||||
                             Lang.bind(this, function(ps, x, y, w, h) {
 | 
			
		||||
                                 this._cursor.set_position(x, y);
 | 
			
		||||
                                 this._cursor.set_size(w, h);
 | 
			
		||||
                                 Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
 | 
			
		||||
                                 if (this._boxPointer.actor.visible)
 | 
			
		||||
                                     this._boxPointer.setPosition(this._cursor, 0);
 | 
			
		||||
                                     this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('update-preedit-text',
 | 
			
		||||
                             Lang.bind(this, function(ps, text, cursorPosition, visible) {
 | 
			
		||||
@@ -252,7 +249,7 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
                         this._candidateArea.actor.visible);
 | 
			
		||||
 | 
			
		||||
        if (isVisible) {
 | 
			
		||||
            this._boxPointer.setPosition(this._cursor, 0);
 | 
			
		||||
            this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
 | 
			
		||||
            this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
            this._boxPointer.actor.raise_top();
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,20 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const ICON_SIZE = 48;
 | 
			
		||||
const ICON_SIZE = 96;
 | 
			
		||||
const MIN_ICON_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const BaseIcon = new Lang.Class({
 | 
			
		||||
    Name: 'BaseIcon',
 | 
			
		||||
@@ -17,7 +23,12 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        params = Params.parse(params, { createIcon: null,
 | 
			
		||||
                                        setSizeManually: false,
 | 
			
		||||
                                        showLabel: true });
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'overview-icon',
 | 
			
		||||
 | 
			
		||||
        let styleClass = 'overview-icon';
 | 
			
		||||
        if (params.showLabel)
 | 
			
		||||
            styleClass += ' overview-icon-with-label';
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ style_class: styleClass,
 | 
			
		||||
                                  x_fill: true,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
@@ -132,11 +143,6 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        this.icon = this.createIcon(this.iconSize);
 | 
			
		||||
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
 | 
			
		||||
        // The icon returned by createIcon() might actually be smaller than
 | 
			
		||||
        // the requested icon size (for instance StTextureCache does this
 | 
			
		||||
        // for fallback icons), so set the size explicitly.
 | 
			
		||||
        this._iconBin.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChanged: function() {
 | 
			
		||||
@@ -176,19 +182,31 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { rowLimit: null,
 | 
			
		||||
                                        columnLimit: null,
 | 
			
		||||
                                        minRows: 1,
 | 
			
		||||
                                        minColumns: 1,
 | 
			
		||||
                                        fillParent: false,
 | 
			
		||||
                                        xAlign: St.Align.MIDDLE });
 | 
			
		||||
                                        xAlign: St.Align.MIDDLE,
 | 
			
		||||
                                        padWithSpacing: false });
 | 
			
		||||
        this._rowLimit = params.rowLimit;
 | 
			
		||||
        this._colLimit = params.columnLimit;
 | 
			
		||||
        this._minRows = params.minRows;
 | 
			
		||||
        this._minColumns = params.minColumns;
 | 
			
		||||
        this._xAlign = params.xAlign;
 | 
			
		||||
        this._fillParent = params.fillParent;
 | 
			
		||||
        this._padWithSpacing = params.padWithSpacing;
 | 
			
		||||
 | 
			
		||||
        this.topPadding = 0;
 | 
			
		||||
        this.bottomPadding = 0;
 | 
			
		||||
        this.rightPadding = 0;
 | 
			
		||||
        this.leftPadding = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'icon-grid',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        // Pulled from CSS, but hardcode some defaults here
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._hItemSize = this._vItemSize = ICON_SIZE;
 | 
			
		||||
        this._fixedHItemSize = this._fixedVItemSize = undefined;
 | 
			
		||||
        this._grid = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
 | 
			
		||||
@@ -196,6 +214,20 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this._grid.connect('allocate', Lang.bind(this, this._allocate));
 | 
			
		||||
        this._grid.connect('actor-added', Lang.bind(this, this._childAdded));
 | 
			
		||||
        this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        this.emit('key-focus-in', actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _childAdded: function(grid, child) {
 | 
			
		||||
        child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _childRemoved: function(grid, child) {
 | 
			
		||||
        child.disconnect(child._iconGridKeyFocusInId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function (grid, forHeight, alloc) {
 | 
			
		||||
@@ -204,16 +236,16 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            // later we'll allocate as many children as fit the parent
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        let nChildren = this._grid.get_n_children();
 | 
			
		||||
        let nColumns = this._colLimit ? Math.min(this._colLimit,
 | 
			
		||||
                                                 children.length)
 | 
			
		||||
                                      : children.length;
 | 
			
		||||
        let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
 | 
			
		||||
                                                 nChildren)
 | 
			
		||||
                                      : nChildren;
 | 
			
		||||
        let totalSpacing = Math.max(0, nColumns - 1) * this._getSpacing();
 | 
			
		||||
        // Kind of a lie, but not really an issue right now.  If
 | 
			
		||||
        // we wanted to support some sort of hidden/overflow that would
 | 
			
		||||
        // need higher level design
 | 
			
		||||
        alloc.min_size = this._hItemSize;
 | 
			
		||||
        alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
 | 
			
		||||
        alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding;
 | 
			
		||||
        alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVisibleChildren: function() {
 | 
			
		||||
@@ -231,13 +263,11 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let nColumns, spacing;
 | 
			
		||||
        if (forWidth < 0) {
 | 
			
		||||
        let nColumns;
 | 
			
		||||
        if (forWidth < 0)
 | 
			
		||||
            nColumns = children.length;
 | 
			
		||||
            spacing = this._spacing;
 | 
			
		||||
        } else {
 | 
			
		||||
            [nColumns, , spacing] = this._computeLayout(forWidth);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            [nColumns, ] = this._computeLayout(forWidth);
 | 
			
		||||
 | 
			
		||||
        let nRows;
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
@@ -246,8 +276,8 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            nRows = 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * spacing;
 | 
			
		||||
        let height = nRows * this._vItemSize + totalSpacing;
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing();
 | 
			
		||||
        let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding;
 | 
			
		||||
        alloc.min_size = height;
 | 
			
		||||
        alloc.natural_size = height;
 | 
			
		||||
    },
 | 
			
		||||
@@ -263,48 +293,30 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let leftPadding;
 | 
			
		||||
        let leftEmptySpace;
 | 
			
		||||
        switch(this._xAlign) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                leftPadding = 0;
 | 
			
		||||
                leftEmptySpace = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                leftPadding = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                leftPadding = availWidth - usedWidth;
 | 
			
		||||
                leftEmptySpace = availWidth - usedWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let x = box.x1 + leftPadding;
 | 
			
		||||
        let y = box.y1;
 | 
			
		||||
        let x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
        let y = box.y1 + this.topPadding;
 | 
			
		||||
        let columnIndex = 0;
 | 
			
		||||
        let rowIndex = 0;
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
 | 
			
		||||
                = children[i].get_preferred_size();
 | 
			
		||||
 | 
			
		||||
            /* Center the item in its allocation horizontally */
 | 
			
		||||
            let width = Math.min(this._hItemSize, childNaturalWidth);
 | 
			
		||||
            let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
 | 
			
		||||
            let height = Math.min(this._vItemSize, childNaturalHeight);
 | 
			
		||||
            let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
 | 
			
		||||
 | 
			
		||||
            let childBox = new Clutter.ActorBox();
 | 
			
		||||
            if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
                let _x = box.x2 - (x + width);
 | 
			
		||||
                childBox.x1 = Math.floor(_x - childXSpacing);
 | 
			
		||||
            } else {
 | 
			
		||||
                childBox.x1 = Math.floor(x + childXSpacing);
 | 
			
		||||
            }
 | 
			
		||||
            childBox.y1 = Math.floor(y + childYSpacing);
 | 
			
		||||
            childBox.x2 = childBox.x1 + width;
 | 
			
		||||
            childBox.y2 = childBox.y1 + height;
 | 
			
		||||
            let childBox = this._calculateChildBox(children[i], x, y, box);
 | 
			
		||||
 | 
			
		||||
            if (this._rowLimit && rowIndex >= this._rowLimit ||
 | 
			
		||||
                this._fillParent && childBox.y2 > availHeight) {
 | 
			
		||||
                this._fillParent && childBox.y2 > availHeight - this.bottomPadding) {
 | 
			
		||||
                this._grid.set_skip_paint(children[i], true);
 | 
			
		||||
            } else {
 | 
			
		||||
                children[i].allocate(childBox, flags);
 | 
			
		||||
@@ -318,15 +330,38 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._vItemSize + spacing;
 | 
			
		||||
                x = box.x1 + leftPadding;
 | 
			
		||||
                y += this._getVItemSize() + spacing;
 | 
			
		||||
                x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
            } else {
 | 
			
		||||
                x += this._hItemSize + spacing;
 | 
			
		||||
                x += this._getHItemSize() + spacing;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    childrenInRow: function(rowWidth) {
 | 
			
		||||
    _calculateChildBox: function(child, x, y, box) {
 | 
			
		||||
        let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
 | 
			
		||||
             child.get_preferred_size();
 | 
			
		||||
 | 
			
		||||
        /* Center the item in its allocation horizontally */
 | 
			
		||||
        let width = Math.min(this._getHItemSize(), childNaturalWidth);
 | 
			
		||||
        let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
 | 
			
		||||
        let height = Math.min(this._getVItemSize(), childNaturalHeight);
 | 
			
		||||
        let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            let _x = box.x2 - (x + width);
 | 
			
		||||
            childBox.x1 = Math.floor(_x - childXSpacing);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = Math.floor(x + childXSpacing);
 | 
			
		||||
        }
 | 
			
		||||
        childBox.y1 = Math.floor(y + childYSpacing);
 | 
			
		||||
        childBox.x2 = childBox.x1 + width;
 | 
			
		||||
        childBox.y2 = childBox.y1 + height;
 | 
			
		||||
        return childBox;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    columnsForWidth: function(rowWidth) {
 | 
			
		||||
        return this._computeLayout(rowWidth)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -336,26 +371,19 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _computeLayout: function (forWidth) {
 | 
			
		||||
        let nColumns = 0;
 | 
			
		||||
        let usedWidth = 0;
 | 
			
		||||
        let spacing = this._spacing;
 | 
			
		||||
 | 
			
		||||
        if (this._colLimit) {
 | 
			
		||||
            let itemWidth = this._hItemSize * this._colLimit;
 | 
			
		||||
            let emptyArea = forWidth - itemWidth;
 | 
			
		||||
            spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
 | 
			
		||||
            spacing = Math.round(spacing);
 | 
			
		||||
        }
 | 
			
		||||
        let usedWidth = this.leftPadding + this.rightPadding;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
 | 
			
		||||
        while ((this._colLimit == null || nColumns < this._colLimit) &&
 | 
			
		||||
               (usedWidth + this._hItemSize <= forWidth)) {
 | 
			
		||||
            usedWidth += this._hItemSize + spacing;
 | 
			
		||||
               (usedWidth + this._getHItemSize() <= forWidth)) {
 | 
			
		||||
            usedWidth += this._getHItemSize() + spacing;
 | 
			
		||||
            nColumns += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            usedWidth -= spacing;
 | 
			
		||||
 | 
			
		||||
        return [nColumns, usedWidth, spacing];
 | 
			
		||||
        return [nColumns, usedWidth];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChanged: function() {
 | 
			
		||||
@@ -366,15 +394,56 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        this._grid.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nRows: function(forWidth) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
 | 
			
		||||
        let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        return nRows;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    rowsForHeight: function(forHeight) {
 | 
			
		||||
        return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedHeightForNRows: function(nRows) {
 | 
			
		||||
        return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedWidth: function(forWidth) {
 | 
			
		||||
        return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedWidthForNColumns: function(columns) {
 | 
			
		||||
        let usedWidth = columns  * (this._getHItemSize() + this._getSpacing());
 | 
			
		||||
        usedWidth -= this._getSpacing();
 | 
			
		||||
        return usedWidth + this.leftPadding + this.rightPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._grid.remove_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroyAll: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._grid.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(actor, index) {
 | 
			
		||||
    addItem: function(item, index) {
 | 
			
		||||
        if (!item.icon instanceof BaseIcon)
 | 
			
		||||
            throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
 | 
			
		||||
 | 
			
		||||
        this._items.push(item);
 | 
			
		||||
        if (index !== undefined)
 | 
			
		||||
            this._grid.insert_child_at_index(actor, index);
 | 
			
		||||
            this._grid.insert_child_at_index(item.actor, index);
 | 
			
		||||
        else
 | 
			
		||||
            this._grid.add_actor(actor);
 | 
			
		||||
            this._grid.add_actor(item.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeItem: function(item) {
 | 
			
		||||
        this._grid.remove_child(item.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemAtIndex: function(index) {
 | 
			
		||||
@@ -383,5 +452,316 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    visibleItemsCount: function() {
 | 
			
		||||
        return this._grid.get_n_children() - this._grid.get_n_skip_paint();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSpacing: function(spacing) {
 | 
			
		||||
        this._fixedSpacing = spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSpacing: function() {
 | 
			
		||||
        return this._fixedSpacing ? this._fixedSpacing : this._spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getHItemSize: function() {
 | 
			
		||||
        return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVItemSize: function() {
 | 
			
		||||
        return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSpacingForSize: function(availWidth, availHeight) {
 | 
			
		||||
        let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
 | 
			
		||||
        let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
 | 
			
		||||
        let maxHSpacing, maxVSpacing;
 | 
			
		||||
 | 
			
		||||
        if (this._padWithSpacing) {
 | 
			
		||||
            // minRows + 1 because we want to put spacing before the first row, so it is like we have one more row
 | 
			
		||||
            // to divide the empty space
 | 
			
		||||
            maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows +1));
 | 
			
		||||
            maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns +1));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._minRows <=  1)
 | 
			
		||||
                maxVSpacing = maxEmptyVArea;
 | 
			
		||||
            else
 | 
			
		||||
                maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
 | 
			
		||||
 | 
			
		||||
            if (this._minColumns <=  1)
 | 
			
		||||
                maxHSpacing = maxEmptyHArea;
 | 
			
		||||
            else
 | 
			
		||||
                maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
 | 
			
		||||
        // Limit spacing to the item size
 | 
			
		||||
        maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
 | 
			
		||||
        // The minimum spacing, regardless of whether it satisfies the row/columng minima,
 | 
			
		||||
        // is the spacing we get from CSS.
 | 
			
		||||
        let spacing = Math.max(this._spacing, maxSpacing);
 | 
			
		||||
        this.setSpacing(spacing);
 | 
			
		||||
        if (this._padWithSpacing)
 | 
			
		||||
            this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This function must to be called before iconGrid allocation,
 | 
			
		||||
     * to know how much spacing can the grid has
 | 
			
		||||
     */
 | 
			
		||||
    adaptToSize: function(availWidth, availHeight) {
 | 
			
		||||
        this._fixedHItemSize = this._hItemSize;
 | 
			
		||||
        this._fixedVItemSize = this._vItemSize;
 | 
			
		||||
        this._updateSpacingForSize(availWidth, availHeight);
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
 | 
			
		||||
        if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
 | 
			
		||||
            let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
 | 
			
		||||
            let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
 | 
			
		||||
 | 
			
		||||
            let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
 | 
			
		||||
                                                                  : Math.ceil(neededHeight / this._minRows);
 | 
			
		||||
            this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
 | 
			
		||||
            this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
            if (this._fixedHItemSize < MIN_ICON_SIZE)
 | 
			
		||||
                this._fixedHItemSize = MIN_ICON_SIZE;
 | 
			
		||||
            if (this._fixedVItemSize < MIN_ICON_SIZE)
 | 
			
		||||
                this._fixedVItemSize = MIN_ICON_SIZE;
 | 
			
		||||
 | 
			
		||||
            this._updateSpacingForSize(availWidth, availHeight);
 | 
			
		||||
        }
 | 
			
		||||
        let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
 | 
			
		||||
    _updateChildrenScale: function(scale) {
 | 
			
		||||
        for (let i in this._items) {
 | 
			
		||||
            let newIconSize = Math.floor(ICON_SIZE * scale);
 | 
			
		||||
            this._items[i].icon.setIconSize(newIconSize);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(IconGrid.prototype);
 | 
			
		||||
 | 
			
		||||
const PaginatedIconGrid = new Lang.Class({
 | 
			
		||||
    Name: 'PaginatedIconGrid',
 | 
			
		||||
    Extends: IconGrid,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
        this._nPages = 0;
 | 
			
		||||
        this._rowsPerPage = 0;
 | 
			
		||||
        this._spaceBetweenPages = 0;
 | 
			
		||||
        this._childrenPerPage = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (grid, forWidth, alloc) {
 | 
			
		||||
        alloc.min_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
 | 
			
		||||
        alloc.natural_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (grid, box, flags) {
 | 
			
		||||
         if (this._childrenPerPage == 0)
 | 
			
		||||
            log('computePages() must be called before allocate(); pagination will not work.');
 | 
			
		||||
 | 
			
		||||
        if (this._fillParent) {
 | 
			
		||||
            // Reset the passed in box to fill the parent
 | 
			
		||||
            let parentBox = this.actor.get_parent().allocation;
 | 
			
		||||
            let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
 | 
			
		||||
            box = this._grid.get_theme_node().get_content_box(gridBox);
 | 
			
		||||
        }
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let leftEmptySpace;
 | 
			
		||||
        switch(this._xAlign) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                leftEmptySpace = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                leftEmptySpace = availWidth - usedWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
        let y = box.y1 + this.topPadding;
 | 
			
		||||
        let columnIndex = 0;
 | 
			
		||||
        let rowIndex = 0;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let childBox = this._calculateChildBox(children[i], x, y, box);
 | 
			
		||||
            children[i].allocate(childBox, flags);
 | 
			
		||||
            this._grid.set_skip_paint(children[i], false);
 | 
			
		||||
 | 
			
		||||
            columnIndex++;
 | 
			
		||||
            if (columnIndex == nColumns) {
 | 
			
		||||
                columnIndex = 0;
 | 
			
		||||
                rowIndex++;
 | 
			
		||||
            }
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._getVItemSize() + spacing;
 | 
			
		||||
                if ((i + 1) % this._childrenPerPage == 0)
 | 
			
		||||
                    y +=  this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
 | 
			
		||||
                x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
            } else
 | 
			
		||||
                x += this._getHItemSize() + spacing;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computePages: function (availWidthPerPage, availHeightPerPage) {
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
 | 
			
		||||
        let nRows;
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            nRows = Math.ceil(children.length / nColumns);
 | 
			
		||||
        else
 | 
			
		||||
            nRows = 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
        // We want to contain the grid inside the parent box with padding
 | 
			
		||||
        this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
 | 
			
		||||
        this._nPages = Math.ceil(nRows / this._rowsPerPage);
 | 
			
		||||
        this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) - this._availableHeightPerPageForItems();
 | 
			
		||||
        this._childrenPerPage = nColumns * this._rowsPerPage;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    adaptToSize: function(availWidth, availHeight) {
 | 
			
		||||
        this.parent(availWidth, availHeight);
 | 
			
		||||
        this._computePages(availWidth, availHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _availableHeightPerPageForItems: function() {
 | 
			
		||||
        return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nPages: function() {
 | 
			
		||||
        return this._nPages;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPageHeight: function() {
 | 
			
		||||
        return this._availableHeightPerPageForItems();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPageY: function(pageNumber) {
 | 
			
		||||
        if (!this._nPages)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let firstPageItem = pageNumber * this._childrenPerPage
 | 
			
		||||
        let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
 | 
			
		||||
        return childBox.y1 - this.topPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemPage: function(item) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let index = children.indexOf(item);
 | 
			
		||||
        if (index == -1) {
 | 
			
		||||
            throw new Error('Item not found.');
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return Math.floor(index / this._childrenPerPage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * openExtraSpace:
 | 
			
		||||
    * @sourceItem: the item for which to create extra space
 | 
			
		||||
    * @side: where @sourceItem should be located relative to the created space
 | 
			
		||||
    * @nRows: the amount of space to create
 | 
			
		||||
    *
 | 
			
		||||
    * Pan view to create extra space for @nRows above or below @sourceItem.
 | 
			
		||||
    */
 | 
			
		||||
    openExtraSpace: function(sourceItem, side, nRows) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let index = children.indexOf(sourceItem.actor);
 | 
			
		||||
        if (index == -1) {
 | 
			
		||||
            throw new Error('Item not found.');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let pageIndex = Math.floor(index / this._childrenPerPage);
 | 
			
		||||
        let pageOffset = pageIndex * this._childrenPerPage;
 | 
			
		||||
 | 
			
		||||
        let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
 | 
			
		||||
        let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
 | 
			
		||||
 | 
			
		||||
        let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1
 | 
			
		||||
                                               : sourceRow;
 | 
			
		||||
        let nRowsBelow = this._rowsPerPage - nRowsAbove;
 | 
			
		||||
 | 
			
		||||
        let nRowsUp, nRowsDown;
 | 
			
		||||
        if (side == St.Side.TOP) {
 | 
			
		||||
            nRowsDown = Math.min(nRowsBelow, nRows);
 | 
			
		||||
            nRowsUp = nRows - nRowsDown;
 | 
			
		||||
        } else {
 | 
			
		||||
            nRowsUp = Math.min(nRowsAbove, nRows);
 | 
			
		||||
            nRowsDown = nRows - nRowsUp;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let childrenDown = children.splice(pageOffset +
 | 
			
		||||
                                           nRowsAbove * childrenPerRow,
 | 
			
		||||
                                           nRowsBelow * childrenPerRow);
 | 
			
		||||
        let childrenUp = children.splice(pageOffset,
 | 
			
		||||
                                         nRowsAbove * childrenPerRow);
 | 
			
		||||
 | 
			
		||||
        // Special case: On the last row with no rows below the icon,
 | 
			
		||||
        // there's no need to move any rows either up or down
 | 
			
		||||
        if (childrenDown.length == 0 && nRowsUp == 0) {
 | 
			
		||||
            this._translatedChildren = [];
 | 
			
		||||
            this.emit('space-opened');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._translateChildren(childrenUp, Gtk.DirectionType.UP, nRowsUp);
 | 
			
		||||
            this._translateChildren(childrenDown, Gtk.DirectionType.DOWN, nRowsDown);
 | 
			
		||||
            this._translatedChildren = childrenUp.concat(childrenDown);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _translateChildren: function(children, direction, nRows) {
 | 
			
		||||
        let translationY = nRows * (this._getVItemSize() + this._getSpacing());
 | 
			
		||||
        if (translationY == 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Gtk.DirectionType.UP)
 | 
			
		||||
            translationY *= -1;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            children[i].translation_y = 0;
 | 
			
		||||
            let params = { translation_y: translationY,
 | 
			
		||||
                           time: EXTRA_SPACE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeInOutQuad'
 | 
			
		||||
                         };
 | 
			
		||||
            if (i == (children.length - 1))
 | 
			
		||||
                params.onComplete = Lang.bind(this,
 | 
			
		||||
                    function() {
 | 
			
		||||
                        this.emit('space-opened');
 | 
			
		||||
                    });
 | 
			
		||||
            Tweener.addTween(children[i], params);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    closeExtraSpace: function() {
 | 
			
		||||
        if (!this._translatedChildren || !this._translatedChildren.length) {
 | 
			
		||||
            this.emit('space-closed');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._translatedChildren.length; i++) {
 | 
			
		||||
            if (!this._translatedChildren[i].translation_y)
 | 
			
		||||
                continue;
 | 
			
		||||
            Tweener.addTween(this._translatedChildren[i],
 | 
			
		||||
                             { translation_y: 0,
 | 
			
		||||
                               time: EXTRA_SPACE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeInOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                   function() {
 | 
			
		||||
                                       this.emit('space-closed');
 | 
			
		||||
                                   })
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PaginatedIconGrid.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,27 +23,29 @@ const KEYBOARD_TYPE = 'keyboard-type';
 | 
			
		||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
 | 
			
		||||
 | 
			
		||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
 | 
			
		||||
<method name='Show'>
 | 
			
		||||
    <arg type='u' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Hide'>
 | 
			
		||||
    <arg type='u' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='SetCursorLocation'>
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<method name='SetEntryLocation'>
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
    <arg type='i' direction='in' />
 | 
			
		||||
</method>
 | 
			
		||||
<property name='Name' access='read' type='s' />
 | 
			
		||||
</interface>;
 | 
			
		||||
const CaribouKeyboardIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Caribou.Keyboard"> \
 | 
			
		||||
<method name="Show"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Hide"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="SetCursorLocation"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="SetEntryLocation"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="Name" access="read" type="s" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const Key = new Lang.Class({
 | 
			
		||||
    Name: 'Key',
 | 
			
		||||
@@ -80,8 +82,16 @@ const Key = new Lang.Class({
 | 
			
		||||
                                      style_class: 'keyboard-key' });
 | 
			
		||||
 | 
			
		||||
        button.key_width = this._key.width;
 | 
			
		||||
        button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
 | 
			
		||||
        button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
 | 
			
		||||
        button.connect('button-press-event', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                this._key.press();
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
            }));
 | 
			
		||||
        button.connect('button-release-event', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                this._key.release();
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
@@ -104,8 +114,16 @@ const Key = new Lang.Class({
 | 
			
		||||
            let label = this._getUnichar(extended_key);
 | 
			
		||||
            let key = new St.Button({ label: label, style_class: 'keyboard-key' });
 | 
			
		||||
            key.extended_key = extended_key;
 | 
			
		||||
            key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
 | 
			
		||||
            key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
 | 
			
		||||
            key.connect('button-press-event', Lang.bind(this,
 | 
			
		||||
                function () {
 | 
			
		||||
                    extended_key.press();
 | 
			
		||||
                    return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                }));
 | 
			
		||||
            key.connect('button-release-event', Lang.bind(this,
 | 
			
		||||
                function () {
 | 
			
		||||
                    extended_key.release();
 | 
			
		||||
                    return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                }));
 | 
			
		||||
            this._extended_keyboard.add(key);
 | 
			
		||||
        }
 | 
			
		||||
        this._boxPointer.bin.add_actor(this._extended_keyboard);
 | 
			
		||||
@@ -248,9 +266,14 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._showIdleId)
 | 
			
		||||
            this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
 | 
			
		||||
                                             Lang.bind(this, function() { this.Show(time); }));
 | 
			
		||||
        if (!this._showIdleId) {
 | 
			
		||||
          this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
 | 
			
		||||
                                           Lang.bind(this, function() {
 | 
			
		||||
                                               this.Show(time);
 | 
			
		||||
                                               return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                           }));
 | 
			
		||||
          GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.Show');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createLayersForGroup: function (gname) {
 | 
			
		||||
@@ -292,7 +315,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        else if (release && this._capturedPress)
 | 
			
		||||
            this._hideSubkeys();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addRows : function (keys, layout) {
 | 
			
		||||
@@ -436,7 +459,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
    _createSource: function () {
 | 
			
		||||
        if (this._source == null) {
 | 
			
		||||
            this._source = new KeyboardSource(this);
 | 
			
		||||
            this._source.setTransient(true);
 | 
			
		||||
            Main.messageTray.add(this._source);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -478,7 +500,9 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._show(monitor);
 | 
			
		||||
                                                       return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                   }));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _show: function(monitor) {
 | 
			
		||||
@@ -503,7 +527,9 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._hide();
 | 
			
		||||
                                                       return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                   }));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hide: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										203
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						@@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
@@ -151,7 +150,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._keyboardIndex = -1;
 | 
			
		||||
        this._rightPanelBarrier = null;
 | 
			
		||||
        this._trayBarrier = null;
 | 
			
		||||
 | 
			
		||||
        this._inOverview = false;
 | 
			
		||||
        this._updateRegionIdle = 0;
 | 
			
		||||
@@ -211,7 +209,10 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.trayBox = new St.Widget({ name: 'trayBox',
 | 
			
		||||
                                       layout_manager: new Clutter.BinLayout() }); 
 | 
			
		||||
        this.addChrome(this.trayBox);
 | 
			
		||||
        this._setupTrayPressure();
 | 
			
		||||
 | 
			
		||||
        this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
 | 
			
		||||
                                                layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this.uiGroup.add_actor(this.modalDialogGroup);
 | 
			
		||||
 | 
			
		||||
        this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
 | 
			
		||||
                                              reactive: true,
 | 
			
		||||
@@ -219,6 +220,11 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.addChrome(this.keyboardBox);
 | 
			
		||||
        this._keyboardHeightNotifyId = 0;
 | 
			
		||||
 | 
			
		||||
        // A dummy actor that tracks the mouse or text cursor, based on the
 | 
			
		||||
        // position and size set in setDummyCursorGeometry.
 | 
			
		||||
        this.dummyCursor = new St.Widget({ width: 0, height: 0 });
 | 
			
		||||
        this.uiGroup.add_actor(this.dummyCursor);
 | 
			
		||||
 | 
			
		||||
        global.stage.remove_actor(global.top_window_group);
 | 
			
		||||
        this.uiGroup.add_actor(global.top_window_group);
 | 
			
		||||
 | 
			
		||||
@@ -227,11 +233,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this._backgroundGroup.lower_bottom();
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
 | 
			
		||||
        // This blocks the XDND picks from finding the activities button
 | 
			
		||||
        // and we never attempt to pick anything from it anyway so make
 | 
			
		||||
        // it invisible from picks
 | 
			
		||||
        Shell.util_set_hidden_from_pick(global.top_window_group, true);
 | 
			
		||||
 | 
			
		||||
        // Need to update struts on new workspaces when they are added
 | 
			
		||||
        global.screen.connect('notify::n-workspaces',
 | 
			
		||||
                              Lang.bind(this, this._queueUpdateRegions));
 | 
			
		||||
@@ -256,7 +257,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._inOverview = true;
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
        this._updateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideOverview: function() {
 | 
			
		||||
@@ -357,26 +358,26 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.emit('hot-corners-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createBackground: function(monitorIndex) {
 | 
			
		||||
    _addBackgroundMenu: function(bgManager) {
 | 
			
		||||
        BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createBackgroundManager: function(monitorIndex) {
 | 
			
		||||
        let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
 | 
			
		||||
                                                           layoutManager: this,
 | 
			
		||||
                                                           monitorIndex: monitorIndex });
 | 
			
		||||
        BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
 | 
			
		||||
 | 
			
		||||
        bgManager.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
                              BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
 | 
			
		||||
                          }));
 | 
			
		||||
        bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
 | 
			
		||||
        this._addBackgroundMenu(bgManager);
 | 
			
		||||
 | 
			
		||||
        this._bgManagers.push(bgManager);
 | 
			
		||||
 | 
			
		||||
        return bgManager.background;
 | 
			
		||||
        return bgManager;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSecondaryBackgrounds: function() {
 | 
			
		||||
    _showSecondaryBackgrounds: function() {
 | 
			
		||||
        for (let i = 0; i < this.monitors.length; i++) {
 | 
			
		||||
            if (i != this.primaryIndex) {
 | 
			
		||||
                let background = this._createBackground(i);
 | 
			
		||||
 | 
			
		||||
                let background = this._bgManagers[i].background;
 | 
			
		||||
                background.actor.show();
 | 
			
		||||
                background.actor.opacity = 0;
 | 
			
		||||
                Tweener.addTween(background.actor,
 | 
			
		||||
                                 { opacity: 255,
 | 
			
		||||
@@ -386,10 +387,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPrimaryBackground: function() {
 | 
			
		||||
        this._createBackground(this.primaryIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBackgrounds: function() {
 | 
			
		||||
        let i;
 | 
			
		||||
        for (i = 0; i < this._bgManagers.length; i++)
 | 
			
		||||
@@ -400,11 +397,12 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        if (Main.sessionMode.isGreeter)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._startingUp)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this.monitors.length; i++) {
 | 
			
		||||
            this._createBackground(i);
 | 
			
		||||
            let bgManager = this._createBackgroundManager(i);
 | 
			
		||||
            this._bgManagers.push(bgManager);
 | 
			
		||||
 | 
			
		||||
            if (i != this.primaryIndex && this._startingUp)
 | 
			
		||||
                bgManager.background.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -449,50 +447,9 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setupTrayPressure: function() {
 | 
			
		||||
        this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
 | 
			
		||||
                                                 MESSAGE_TRAY_PRESSURE_TIMEOUT,
 | 
			
		||||
                                                 Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                                 Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
 | 
			
		||||
        this._trayPressure.connect('trigger', function(barrier) {
 | 
			
		||||
            if (Main.layoutManager.bottomMonitor.inFullscreen)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Main.messageTray.openTray();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTrayBarrier: function() {
 | 
			
		||||
        let monitor = this.bottomMonitor;
 | 
			
		||||
 | 
			
		||||
        if (this._trayBarrier) {
 | 
			
		||||
            this._trayPressure.removeBarrier(this._trayBarrier);
 | 
			
		||||
            this._trayBarrier.destroy();
 | 
			
		||||
            this._trayBarrier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._trayBarrier = new Meta.Barrier({ display: global.display,
 | 
			
		||||
                                               x1: monitor.x, x2: monitor.x + monitor.width,
 | 
			
		||||
                                               y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
 | 
			
		||||
                                               directions: Meta.BarrierDirection.NEGATIVE_Y });
 | 
			
		||||
        this._trayPressure.addBarrier(this._trayBarrier);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _trayBarrierEventFilter: function(event) {
 | 
			
		||||
        // Throw out all events where the pointer was grabbed by another
 | 
			
		||||
        // client, as the client that grabbed the pointer expects to have
 | 
			
		||||
        // complete control over it
 | 
			
		||||
        if (event.grabbed && Main.modalCount == 0)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        this._updateMonitors();
 | 
			
		||||
        this._updateBoxes();
 | 
			
		||||
        this._updateTrayBarrier();
 | 
			
		||||
        this._updateHotCorners();
 | 
			
		||||
        this._updateBackgrounds();
 | 
			
		||||
        this._updateFullscreen();
 | 
			
		||||
@@ -528,17 +485,10 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    get focusIndex() {
 | 
			
		||||
        let i = Main.layoutManager.primaryIndex;
 | 
			
		||||
 | 
			
		||||
        if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
 | 
			
		||||
            global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
 | 
			
		||||
            let focusActor = global.stage.key_focus;
 | 
			
		||||
            if (focusActor)
 | 
			
		||||
                i = this.findIndexForActor(focusActor);
 | 
			
		||||
        } else {
 | 
			
		||||
            let focusWindow = global.display.focus_window;
 | 
			
		||||
            if (focusWindow)
 | 
			
		||||
                i = focusWindow.get_monitor();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (global.stage.key_focus != null)
 | 
			
		||||
            i = this.findIndexForActor(global.stage.key_focus);
 | 
			
		||||
        else if (global.display.focus_window != null)
 | 
			
		||||
            i = global.display.focus_window.get_monitor();
 | 
			
		||||
        return i;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -596,15 +546,21 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    // screen. So, we set no_clear_hint at the end of the animation.
 | 
			
		||||
 | 
			
		||||
    _prepareStartupAnimation: function() {
 | 
			
		||||
        // Set ourselves to FULLSCREEN input mode while the animation is running
 | 
			
		||||
        // so events don't get delivered to X11 windows (which are distorted by the animation)
 | 
			
		||||
        global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 | 
			
		||||
        // During the initial transition, add a simple actor to block all events,
 | 
			
		||||
        // so they don't get delivered to X11 windows that have been transformed.
 | 
			
		||||
        this._coverPane = new Clutter.Actor({ opacity: 0,
 | 
			
		||||
                                              width: global.screen_width,
 | 
			
		||||
                                              height: global.screen_height,
 | 
			
		||||
                                              reactive: true });
 | 
			
		||||
        this.addChrome(this._coverPane);
 | 
			
		||||
 | 
			
		||||
        if (Main.sessionMode.isGreeter) {
 | 
			
		||||
            this.panelBox.translation_y = -this.panelBox.height;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._updateBackgrounds();
 | 
			
		||||
 | 
			
		||||
            // We need to force an update of the regions now before we scale
 | 
			
		||||
            // the UI group to get the coorect allocation for the struts.
 | 
			
		||||
            // the UI group to get the correct allocation for the struts.
 | 
			
		||||
            this._updateRegions();
 | 
			
		||||
 | 
			
		||||
            this.trayBox.hide();
 | 
			
		||||
@@ -616,7 +572,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            this.uiGroup.set_pivot_point(x / global.screen_width,
 | 
			
		||||
                                         y / global.screen_height);
 | 
			
		||||
            this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
 | 
			
		||||
            this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
 | 
			
		||||
            this.uiGroup.opacity = 0;
 | 
			
		||||
            global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
 | 
			
		||||
        }
 | 
			
		||||
@@ -629,10 +585,11 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        // until the event loop is uncontended and idle.
 | 
			
		||||
        // This helps to prevent us from running the animation
 | 
			
		||||
        // when the system is bogged down
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
 | 
			
		||||
        let id = GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
 | 
			
		||||
            this._startupAnimation();
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        }));
 | 
			
		||||
        GLib.Source.set_name_by_id(id, '[gnome-shell] this._startupAnimation');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startupAnimation: function() {
 | 
			
		||||
@@ -652,7 +609,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startupAnimationSession: function() {
 | 
			
		||||
        this._createPrimaryBackground();
 | 
			
		||||
        Tweener.addTween(this.uiGroup,
 | 
			
		||||
                         { scale_x: 1,
 | 
			
		||||
                           scale_y: 1,
 | 
			
		||||
@@ -668,7 +624,8 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        // we no longer need to clear the stage
 | 
			
		||||
        global.stage.no_clear_hint = true;
 | 
			
		||||
 | 
			
		||||
        global.stage_input_mode = Shell.StageInputMode.NORMAL;
 | 
			
		||||
        this._coverPane.destroy();
 | 
			
		||||
        this._coverPane = null;
 | 
			
		||||
 | 
			
		||||
        this._systemBackground.actor.destroy();
 | 
			
		||||
        this._systemBackground = null;
 | 
			
		||||
@@ -679,7 +636,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.keyboardBox.show();
 | 
			
		||||
 | 
			
		||||
        if (!Main.sessionMode.isGreeter) {
 | 
			
		||||
            this._createSecondaryBackgrounds();
 | 
			
		||||
            this._showSecondaryBackgrounds();
 | 
			
		||||
            global.window_group.remove_clip();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -729,6 +686,21 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this._updateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // setDummyCursorGeometry:
 | 
			
		||||
    //
 | 
			
		||||
    // The cursor dummy is a standard widget commonly used for popup
 | 
			
		||||
    // menus and box pointers to track, as the box pointer API only
 | 
			
		||||
    // tracks actors. If you want to pop up a menu based on where the
 | 
			
		||||
    // user clicked, or where the text cursor is, the cursor dummy
 | 
			
		||||
    // is what you should use. Given that the menu should not track
 | 
			
		||||
    // the actual mouse pointer as it moves, you need to call this
 | 
			
		||||
    // function before you show the menu to ensure it is at the right
 | 
			
		||||
    // position and has the right size.
 | 
			
		||||
    setDummyCursorGeometry: function(x, y, w, h) {
 | 
			
		||||
        this.dummyCursor.set_position(Math.round(x), Math.round(y));
 | 
			
		||||
        this.dummyCursor.set_size(Math.round(w), Math.round(h));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // addChrome:
 | 
			
		||||
    // @actor: an actor to add to the chrome
 | 
			
		||||
    // @params: (optional) additional params
 | 
			
		||||
@@ -820,13 +792,12 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let actorData = Params.parse(params, defaultParams);
 | 
			
		||||
        actorData.actor = actor;
 | 
			
		||||
        actorData.isToplevel = actor.get_parent() == this.uiGroup;
 | 
			
		||||
        actorData.visibleId = actor.connect('notify::visible',
 | 
			
		||||
                                            Lang.bind(this, this._queueUpdateRegions));
 | 
			
		||||
        actorData.allocationId = actor.connect('notify::allocation',
 | 
			
		||||
                                               Lang.bind(this, this._queueUpdateRegions));
 | 
			
		||||
        actorData.parentSetId = actor.connect('parent-set',
 | 
			
		||||
                                              Lang.bind(this, this._actorReparented));
 | 
			
		||||
        actorData.destroyId = actor.connect('destroy',
 | 
			
		||||
                                            Lang.bind(this, this._untrackActor));
 | 
			
		||||
        // Note that destroying actor will unset its parent, so we don't
 | 
			
		||||
        // need to connect to 'destroy' too.
 | 
			
		||||
 | 
			
		||||
@@ -844,22 +815,11 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this._trackedActors.splice(i, 1);
 | 
			
		||||
        actor.disconnect(actorData.visibleId);
 | 
			
		||||
        actor.disconnect(actorData.allocationId);
 | 
			
		||||
        actor.disconnect(actorData.parentSetId);
 | 
			
		||||
        actor.disconnect(actorData.destroyId);
 | 
			
		||||
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _actorReparented: function(actor, oldParent) {
 | 
			
		||||
        let newParent = actor.get_parent();
 | 
			
		||||
        if (!newParent) {
 | 
			
		||||
            this._untrackActor(actor);
 | 
			
		||||
        } else {
 | 
			
		||||
            let i = this._findActor(actor);
 | 
			
		||||
            let actorData = this._trackedActors[i];
 | 
			
		||||
            actorData.isToplevel = (newParent == this.uiGroup);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
 | 
			
		||||
 | 
			
		||||
@@ -870,8 +830,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            let actorData = this._trackedActors[i], visible;
 | 
			
		||||
            if (!actorData.trackFullscreen)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (!actorData.isToplevel)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (!windowsVisible)
 | 
			
		||||
                visible = true;
 | 
			
		||||
@@ -911,8 +869,8 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._updateRegionIdle)
 | 
			
		||||
            this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
 | 
			
		||||
                                                       Meta.PRIORITY_BEFORE_REDRAW);
 | 
			
		||||
            this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
 | 
			
		||||
                                                    Lang.bind(this, this._updateRegions));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getWindowActorsForWorkspace: function(workspace) {
 | 
			
		||||
@@ -943,7 +901,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        let rects = [], struts = [], i;
 | 
			
		||||
 | 
			
		||||
        if (this._updateRegionIdle) {
 | 
			
		||||
            Mainloop.source_remove(this._updateRegionIdle);
 | 
			
		||||
            Meta.later_remove(this._updateRegionIdle);
 | 
			
		||||
            delete this._updateRegionIdle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1016,23 +974,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
                else
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                // Ensure that the strut rects goes all the way to the screen edge,
 | 
			
		||||
                // as this really what mutter expects.
 | 
			
		||||
                switch (side) {
 | 
			
		||||
                case Meta.Side.TOP:
 | 
			
		||||
                    y1 = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.BOTTOM:
 | 
			
		||||
                    y2 = global.screen_height;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.LEFT:
 | 
			
		||||
                    x1 = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.RIGHT:
 | 
			
		||||
                    x2 = global.screen_width;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
 | 
			
		||||
                let strut = new Meta.Strut({ rect: strutRect, side: side });
 | 
			
		||||
                struts.push(strut);
 | 
			
		||||
@@ -1048,7 +989,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            workspace.set_builtin_struts(struts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LayoutManager.prototype);
 | 
			
		||||
@@ -1235,20 +1176,20 @@ const HotCorner = new Lang.Class({
 | 
			
		||||
            this._entered = true;
 | 
			
		||||
            this._toggleOverview();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCornerLeft : function(actor, event) {
 | 
			
		||||
        if (event.get_related() != this.actor)
 | 
			
		||||
            this._entered = false;
 | 
			
		||||
        // Consume event, otherwise this will confuse onEnvironsLeft
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEnvironsLeft : function(actor, event) {
 | 
			
		||||
        if (event.get_related() != this._corner)
 | 
			
		||||
            this._entered = false;
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,67 @@ const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_FADE_FACTOR = 0.4;
 | 
			
		||||
const VIGNETTE_BRIGHTNESS = 0.8;
 | 
			
		||||
const VIGNETTE_SHARPNESS = 0.7;
 | 
			
		||||
 | 
			
		||||
const VIGNETTE_DECLARATIONS = '\
 | 
			
		||||
uniform float brightness;\n\
 | 
			
		||||
uniform float vignette_sharpness;\n';
 | 
			
		||||
 | 
			
		||||
const VIGNETTE_CODE = '\
 | 
			
		||||
cogl_color_out.a = cogl_color_in.a;\n\
 | 
			
		||||
cogl_color_out.rgb = vec3(0.0, 0.0, 0.0);\n\
 | 
			
		||||
vec2 position = cogl_tex_coord_in[0].xy - 0.5;\n\
 | 
			
		||||
float t = length(2.0 * position);\n\
 | 
			
		||||
t = clamp(t, 0.0, 1.0);\n\
 | 
			
		||||
float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n\
 | 
			
		||||
cogl_color_out.a = cogl_color_out.a * (1 - pixel_brightness * brightness);';
 | 
			
		||||
 | 
			
		||||
const RadialShaderQuad = new Lang.Class({
 | 
			
		||||
    Name: 'RadialShaderQuad',
 | 
			
		||||
    Extends: Shell.GLSLQuad,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
 | 
			
		||||
        this._brightnessLocation = this.get_uniform_location('brightness');
 | 
			
		||||
        this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
 | 
			
		||||
 | 
			
		||||
        this.brightness = 1.0;
 | 
			
		||||
        this.vignetteSharpness = 0.0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_build_pipeline: function() {
 | 
			
		||||
        this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
 | 
			
		||||
                              VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get brightness() {
 | 
			
		||||
        return this._brightness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set brightness(v) {
 | 
			
		||||
        this._brightness = v;
 | 
			
		||||
        this.set_uniform_float(this._brightnessLocation,
 | 
			
		||||
                               1, [this._brightness]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get vignetteSharpness() {
 | 
			
		||||
        return this._sharpness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set vignetteSharpness(v) {
 | 
			
		||||
        this._sharpness = v;
 | 
			
		||||
        this.set_uniform_float(this._sharpnessLocation,
 | 
			
		||||
                               1, [this._sharpness]);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Lightbox:
 | 
			
		||||
@@ -42,20 +98,24 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        params = Params.parse(params, { inhibitEvents: false,
 | 
			
		||||
                                        width: null,
 | 
			
		||||
                                        height: null,
 | 
			
		||||
                                        fadeInTime: null,
 | 
			
		||||
                                        fadeOutTime: null,
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR,
 | 
			
		||||
                                        radialEffect: false,
 | 
			
		||||
                                      });
 | 
			
		||||
 | 
			
		||||
        this._container = container;
 | 
			
		||||
        this._children = container.get_children();
 | 
			
		||||
        this._fadeInTime = params.fadeInTime;
 | 
			
		||||
        this._fadeOutTime = params.fadeOutTime;
 | 
			
		||||
        this._fadeFactor = params.fadeFactor;
 | 
			
		||||
        this.actor = new St.Bin({ x: 0,
 | 
			
		||||
                                  y: 0,
 | 
			
		||||
                                  style_class: 'lightbox',
 | 
			
		||||
                                  reactive: params.inhibitEvents });
 | 
			
		||||
        this._radialEffect = params.radialEffect;
 | 
			
		||||
        if (params.radialEffect)
 | 
			
		||||
            this.actor = new RadialShaderQuad({ x: 0,
 | 
			
		||||
                                                y: 0,
 | 
			
		||||
                                                reactive: params.inhibitEvents });
 | 
			
		||||
        else
 | 
			
		||||
            this.actor = new St.Bin({ x: 0,
 | 
			
		||||
                                      y: 0,
 | 
			
		||||
                                      opacity: 0,
 | 
			
		||||
                                      style_class: 'lightbox',
 | 
			
		||||
                                      reactive: params.inhibitEvents });
 | 
			
		||||
 | 
			
		||||
        container.add_actor(this.actor);
 | 
			
		||||
        this.actor.raise_top();
 | 
			
		||||
@@ -101,14 +161,15 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
    show: function(fadeInTime) {
 | 
			
		||||
        fadeInTime = fadeInTime || 0;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (this._fadeInTime) {
 | 
			
		||||
            this.shown = false;
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
        if (this._radialEffect) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255 * this._fadeFactor,
 | 
			
		||||
                               time: this._fadeInTime,
 | 
			
		||||
                             { brightness: VIGNETTE_BRIGHTNESS,
 | 
			
		||||
                               vignetteSharpness: VIGNETTE_SHARPNESS,
 | 
			
		||||
                               time: fadeInTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.shown = true;
 | 
			
		||||
@@ -116,27 +177,45 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.opacity = 255 * this._fadeFactor;
 | 
			
		||||
            this.shown = true;
 | 
			
		||||
            this.emit('shown');
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255 * this._fadeFactor,
 | 
			
		||||
                               time: fadeInTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.shown = true;
 | 
			
		||||
                                   this.emit('shown');
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
    hide: function(fadeOutTime) {
 | 
			
		||||
        fadeOutTime = fadeOutTime || 0;
 | 
			
		||||
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (this._fadeOutTime) {
 | 
			
		||||
        if (this._radialEffect) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: this._fadeOutTime,
 | 
			
		||||
                             { brightness: 1.0,
 | 
			
		||||
                               vignetteSharpness: 0.0,
 | 
			
		||||
                               opacity: 0,
 | 
			
		||||
                               time: fadeOutTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: fadeOutTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,8 @@ const CHEVRON = '>>> ';
 | 
			
		||||
/* Imports...feel free to add here as needed */
 | 
			
		||||
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                    'const GLib = imports.gi.GLib; ' +
 | 
			
		||||
                    'const GObject = imports.gi.GObject; ' +
 | 
			
		||||
                    'const Gio = imports.gi.Gio; ' +
 | 
			
		||||
                    'const Gtk = imports.gi.Gtk; ' +
 | 
			
		||||
                    'const Mainloop = imports.mainloop; ' +
 | 
			
		||||
                    'const Meta = imports.gi.Meta; ' +
 | 
			
		||||
@@ -109,6 +111,7 @@ const AutoComplete = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
            this._lastTabTime = currTime;
 | 
			
		||||
        }
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Insert characters of text not already included in head at cursor position.  i.e., if text="abc" and head="a",
 | 
			
		||||
@@ -558,7 +561,7 @@ const Inspector = new Lang.Class({
 | 
			
		||||
    _onKeyPressEvent: function (actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape)
 | 
			
		||||
            this._close();
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPressEvent: function (actor, event) {
 | 
			
		||||
@@ -567,7 +570,7 @@ const Inspector = new Lang.Class({
 | 
			
		||||
            this.emit('target', this._target, stageX, stageY);
 | 
			
		||||
        }
 | 
			
		||||
        this._close();
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function (actor, event) {
 | 
			
		||||
@@ -601,12 +604,12 @@ const Inspector = new Lang.Class({
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMotionEvent: function (actor, event) {
 | 
			
		||||
        this._update(event);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function(event) {
 | 
			
		||||
@@ -629,55 +632,6 @@ const Inspector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Inspector.prototype);
 | 
			
		||||
 | 
			
		||||
const Memory = new Lang.Class({
 | 
			
		||||
    Name: 'Memory',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._glibc_uordblks = new St.Label();
 | 
			
		||||
        this.actor.add(this._glibc_uordblks);
 | 
			
		||||
 | 
			
		||||
        this._js_bytes = new St.Label();
 | 
			
		||||
        this.actor.add(this._js_bytes);
 | 
			
		||||
 | 
			
		||||
        this._gjs_boxed = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_boxed);
 | 
			
		||||
 | 
			
		||||
        this._gjs_gobject = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_gobject);
 | 
			
		||||
 | 
			
		||||
        this._gjs_function = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_function);
 | 
			
		||||
 | 
			
		||||
        this._gjs_closure = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_closure);
 | 
			
		||||
 | 
			
		||||
        this._last_gc_seconds_ago = new St.Label();
 | 
			
		||||
        this.actor.add(this._last_gc_seconds_ago);
 | 
			
		||||
 | 
			
		||||
        this._gcbutton = new St.Button({ label: 'Full GC',
 | 
			
		||||
                                         style_class: 'lg-obj-inspector-button' });
 | 
			
		||||
        this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
 | 
			
		||||
        this.actor.add(this._gcbutton, { x_align: St.Align.START,
 | 
			
		||||
                                         x_fill: false });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderText: function() {
 | 
			
		||||
        if (!this.actor.mapped)
 | 
			
		||||
            return;
 | 
			
		||||
        let memInfo = global.get_memory_info();
 | 
			
		||||
        this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
 | 
			
		||||
        this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
 | 
			
		||||
        this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
 | 
			
		||||
        this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
 | 
			
		||||
        this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
 | 
			
		||||
        this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
 | 
			
		||||
        this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Extensions = new Lang.Class({
 | 
			
		||||
    Name: 'Extensions',
 | 
			
		||||
 | 
			
		||||
@@ -718,13 +672,13 @@ const Extensions = new Lang.Class({
 | 
			
		||||
    _onViewSource: function (actor) {
 | 
			
		||||
        let extension = actor._extension;
 | 
			
		||||
        let uri = extension.dir.get_uri();
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1));
 | 
			
		||||
        this._lookingGlass.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWebPage: function (actor) {
 | 
			
		||||
        let extension = actor._extension;
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1));
 | 
			
		||||
        this._lookingGlass.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -849,8 +803,9 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._updateFont();
 | 
			
		||||
 | 
			
		||||
        // We want it to appear to slide out from underneath the panel
 | 
			
		||||
        Main.layoutManager.panelBox.add_actor(this.actor);
 | 
			
		||||
        this.actor.lower_bottom();
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        Main.uiGroup.set_child_below_sibling(this.actor,
 | 
			
		||||
                                             Main.layoutManager.panelBox);
 | 
			
		||||
        Main.layoutManager.panelBox.connect('allocation-changed',
 | 
			
		||||
                                            Lang.bind(this, this._queueResize));
 | 
			
		||||
        Main.layoutManager.keyboardBox.connect('allocation-changed',
 | 
			
		||||
@@ -876,7 +831,23 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
                global.stage.set_key_focus(this._entry);
 | 
			
		||||
            }));
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
 | 
			
		||||
                                   icon_size: 24 });
 | 
			
		||||
        toolbar.add_actor(gcIcon);
 | 
			
		||||
        gcIcon.reactive = true;
 | 
			
		||||
        gcIcon.connect('button-press-event', Lang.bind(this, function () {
 | 
			
		||||
           gcIcon.icon_name = 'gnome-fs-trash-empty';
 | 
			
		||||
           System.gc();
 | 
			
		||||
           this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
 | 
			
		||||
                gcIcon.icon_name = 'gnome-fs-trash-full';
 | 
			
		||||
                this._timeoutId = 0;
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
           }));
 | 
			
		||||
           GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] gcIcon.icon_name = \'gnome-fs-trash-full\'');
 | 
			
		||||
           return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        let notebook = new Notebook();
 | 
			
		||||
@@ -906,9 +877,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._windowList = new WindowList(this);
 | 
			
		||||
        notebook.appendPage('Windows', this._windowList.actor);
 | 
			
		||||
 | 
			
		||||
        this._memory = new Memory();
 | 
			
		||||
        notebook.appendPage('Memory', this._memory.actor);
 | 
			
		||||
 | 
			
		||||
        this._extensions = new Extensions(this);
 | 
			
		||||
        notebook.appendPage('Extensions', this._extensions.actor);
 | 
			
		||||
 | 
			
		||||
@@ -919,7 +887,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
            let text = o.get_text();
 | 
			
		||||
            // Ensure we don't get newlines in the command; the history file is
 | 
			
		||||
            // newline-separated.
 | 
			
		||||
            text.replace('\n', ' ');
 | 
			
		||||
            text = text.replace('\n', ' ');
 | 
			
		||||
            // Strip leading and trailing whitespace
 | 
			
		||||
            text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
            if (text == '')
 | 
			
		||||
@@ -1071,15 +1039,15 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        let myWidth = primary.width * 0.7;
 | 
			
		||||
        let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
 | 
			
		||||
        let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
 | 
			
		||||
        this.actor.x = (primary.width - myWidth) / 2;
 | 
			
		||||
        this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
 | 
			
		||||
        this.actor.x = primary.x + (primary.width - myWidth) / 2;
 | 
			
		||||
        this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
 | 
			
		||||
        this._targetY = this._hiddenY + myHeight;
 | 
			
		||||
        this.actor.y = this._hiddenY;
 | 
			
		||||
        this.actor.width = myWidth;
 | 
			
		||||
        this.actor.height = myHeight;
 | 
			
		||||
        this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
 | 
			
		||||
        this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
 | 
			
		||||
                                              primary.y + this._targetY + Math.floor(myHeight * 0.1));
 | 
			
		||||
        this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
 | 
			
		||||
                                              this._targetY + Math.floor(myHeight * 0.1));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    insertObject: function(obj) {
 | 
			
		||||
@@ -1101,7 +1069,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
            } else {
 | 
			
		||||
                this.close();
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
 | 
			
		||||
        if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
 | 
			
		||||
@@ -1111,7 +1079,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
                this._notebook.nextTab();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open : function() {
 | 
			
		||||
@@ -1149,7 +1117,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._entry);
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
 | 
			
		||||
        Tweener.addTween(this.actor, { time: Math.min(0.5 / St.get_slow_down_factor(), 0.5),
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       y: this._hiddenY,
 | 
			
		||||
                                       onComplete: Lang.bind(this, function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Atspi = imports.gi.Atspi;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GDesktopEnums = imports.gi.GDesktopEnums;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
@@ -7,8 +8,11 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const FocusCaretTracker = imports.ui.focusCaretTracker;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MagnifierDBus = imports.ui.magnifierDBus;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
@@ -36,6 +40,8 @@ const CONTRAST_BLUE_KEY         = 'contrast-blue';
 | 
			
		||||
const LENS_MODE_KEY             = 'lens-mode';
 | 
			
		||||
const CLAMP_MODE_KEY            = 'scroll-at-edges';
 | 
			
		||||
const MOUSE_TRACKING_KEY        = 'mouse-tracking';
 | 
			
		||||
const FOCUS_TRACKING_KEY        = 'focus-tracking';
 | 
			
		||||
const CARET_TRACKING_KEY        = 'caret-tracking';
 | 
			
		||||
const SHOW_CROSS_HAIRS_KEY      = 'show-cross-hairs';
 | 
			
		||||
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
 | 
			
		||||
const CROSS_HAIRS_COLOR_KEY     = 'cross-hairs-color';
 | 
			
		||||
@@ -53,9 +59,9 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        this._zoomRegions = [];
 | 
			
		||||
 | 
			
		||||
        // Create small clutter tree for the magnified mouse.
 | 
			
		||||
        let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
 | 
			
		||||
        let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
 | 
			
		||||
        this._mouseSprite = new Clutter.Texture();
 | 
			
		||||
        xfixesCursor.update_texture_image(this._mouseSprite);
 | 
			
		||||
        Shell.util_cursor_tracker_to_clutter(cursorTracker, this._mouseSprite);
 | 
			
		||||
        this._cursorRoot = new Clutter.Actor();
 | 
			
		||||
        this._cursorRoot.add_actor(this._mouseSprite);
 | 
			
		||||
 | 
			
		||||
@@ -70,8 +76,8 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        let showAtLaunch = this._settingsInit(aZoomRegion);
 | 
			
		||||
        aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
 | 
			
		||||
 | 
			
		||||
        xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite));
 | 
			
		||||
        this._xfixesCursor = xfixesCursor;
 | 
			
		||||
        cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
 | 
			
		||||
        this._cursorTracker = cursorTracker;
 | 
			
		||||
 | 
			
		||||
        // Export to dbus.
 | 
			
		||||
        magDBusService = new MagnifierDBus.ShellMagnifier();
 | 
			
		||||
@@ -83,7 +89,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * Show the system mouse pointer.
 | 
			
		||||
     */
 | 
			
		||||
    showSystemCursor: function() {
 | 
			
		||||
        this._xfixesCursor.show();
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -91,7 +97,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * Hide the system mouse pointer.
 | 
			
		||||
     */
 | 
			
		||||
    hideSystemCursor: function() {
 | 
			
		||||
        this._xfixesCursor.hide();
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -100,19 +106,26 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * @activate:   Boolean to activate or de-activate the magnifier.
 | 
			
		||||
     */
 | 
			
		||||
    setActive: function(activate) {
 | 
			
		||||
        let isActive = this.isActive();
 | 
			
		||||
 | 
			
		||||
        this._zoomRegions.forEach (function(zoomRegion, index, array) {
 | 
			
		||||
            zoomRegion.setActive(activate);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (activate)
 | 
			
		||||
            this.startTrackingMouse();
 | 
			
		||||
        else
 | 
			
		||||
            this.stopTrackingMouse();
 | 
			
		||||
        if (isActive != activate) {
 | 
			
		||||
            if (activate) {
 | 
			
		||||
                Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
                this.startTrackingMouse();
 | 
			
		||||
            } else {
 | 
			
		||||
                Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
                this.stopTrackingMouse();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Make sure system mouse pointer is shown when all zoom regions are
 | 
			
		||||
        // invisible.
 | 
			
		||||
        if (!activate)
 | 
			
		||||
            this._xfixesCursor.show();
 | 
			
		||||
            this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
 | 
			
		||||
        // Notify interested parties of this change
 | 
			
		||||
        this.emit('active-changed', activate);
 | 
			
		||||
@@ -422,9 +435,8 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _updateMouseSprite: function() {
 | 
			
		||||
        this._xfixesCursor.update_texture_image(this._mouseSprite);
 | 
			
		||||
        let xHot = this._xfixesCursor.get_hot_x();
 | 
			
		||||
        let yHot = this._xfixesCursor.get_hot_y();
 | 
			
		||||
        Shell.util_cursor_tracker_to_clutter(this._cursorTracker, this._mouseSprite);
 | 
			
		||||
        let [xHot, yHot] = this._cursorTracker.get_hot();
 | 
			
		||||
        this._mouseSprite.set_anchor_point(xHot, yHot);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -449,6 +461,14 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setMouseTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setFocusTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_enum(CARET_TRACKING_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setCaretTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setInvertLightness(aPref);
 | 
			
		||||
@@ -488,6 +508,10 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
                               Lang.bind(this, this._updateClampMode));
 | 
			
		||||
        this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateMouseTrackingMode));
 | 
			
		||||
        this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateFocusTrackingMode));
 | 
			
		||||
        this._settings.connect('changed::' + CARET_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateCaretTrackingMode));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateInvertLightness));
 | 
			
		||||
@@ -537,7 +561,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
                               Lang.bind(this, function() {
 | 
			
		||||
            this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
@@ -585,6 +608,24 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFocusTrackingMode: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            this._zoomRegions[0].setFocusTrackingMode(
 | 
			
		||||
                this._settings.get_enum(FOCUS_TRACKING_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCaretTrackingMode: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            this._zoomRegions[0].setCaretTrackingMode(
 | 
			
		||||
                this._settings.get_enum(CARET_TRACKING_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateInvertLightness: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
@@ -623,7 +664,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
            contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            this._zoomRegions[0].setContrast(contrast);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Magnifier.prototype);
 | 
			
		||||
 | 
			
		||||
@@ -632,8 +673,11 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(magnifier, mouseSourceActor) {
 | 
			
		||||
        this._magnifier = magnifier;
 | 
			
		||||
        this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
 | 
			
		||||
 | 
			
		||||
        this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
 | 
			
		||||
        this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
 | 
			
		||||
        this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
 | 
			
		||||
        this._clampScrollingAtEdges = false;
 | 
			
		||||
        this._lensMode = false;
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
 | 
			
		||||
@@ -659,9 +703,50 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        this._xMagFactor = 1;
 | 
			
		||||
        this._yMagFactor = 1;
 | 
			
		||||
        this._followingCursor = false;
 | 
			
		||||
        this._xFocus = 0;
 | 
			
		||||
        this._yFocus = 0;
 | 
			
		||||
        this._xCaret = 0;
 | 
			
		||||
        this._yCaret = 0;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._focusCaretTracker.connect('caret-moved',
 | 
			
		||||
                                    Lang.bind(this, this._updateCaret));
 | 
			
		||||
        this._focusCaretTracker.connect('focus-changed',
 | 
			
		||||
                                    Lang.bind(this, this._updateFocus));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFocus: function(caller, event) {
 | 
			
		||||
        let component = event.source.get_component_iface();
 | 
			
		||||
        if (!component || event.detail1 != 1)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents;
 | 
			
		||||
        try {
 | 
			
		||||
            extents = component.get_extents(Atspi.CoordType.SCREEN);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to read extents of focused component: ' + e.message);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [this._xFocus, this._yFocus] = [extents.x + (extents.width / 2),
 | 
			
		||||
                                        extents.y + (extents.height / 2)];
 | 
			
		||||
        this._centerFromFocusPosition();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCaret: function(caller, event) {
 | 
			
		||||
        let text = event.source.get_text_iface();
 | 
			
		||||
        if (!text)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents;
 | 
			
		||||
        try {
 | 
			
		||||
            extents = text.get_character_extents(text.get_caret_offset(), 0);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to read extents of text caret: ' + e.message);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [this._xCaret, this._yCaret] = [extents.x, extents.y];
 | 
			
		||||
        this._centerFromCaretPosition();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -669,16 +754,22 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     * @activate:   Boolean to show/hide the ZoomRegion.
 | 
			
		||||
     */
 | 
			
		||||
    setActive: function(activate) {
 | 
			
		||||
        if (activate && !this.isActive()) {
 | 
			
		||||
        if (activate == this.isActive())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (activate) {
 | 
			
		||||
            this._createActors();
 | 
			
		||||
            if (this._isMouseOverRegion())
 | 
			
		||||
                this._magnifier.hideSystemCursor();
 | 
			
		||||
            this._updateMagViewGeometry();
 | 
			
		||||
            this._updateCloneGeometry();
 | 
			
		||||
            this._updateMousePosition();
 | 
			
		||||
        } else if (!activate && this.isActive()) {
 | 
			
		||||
        } else {
 | 
			
		||||
            this._destroyActors();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._syncCaretTracking();
 | 
			
		||||
        this._syncFocusTracking();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -732,6 +823,44 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        return this._mouseTrackingMode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setFocusTrackingMode
 | 
			
		||||
     * @mode:     One of the enum FocusTrackingMode values.
 | 
			
		||||
     */
 | 
			
		||||
    setFocusTrackingMode: function(mode) {
 | 
			
		||||
        this._focusTrackingMode = mode;
 | 
			
		||||
        this._syncFocusTracking();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setCaretTrackingMode
 | 
			
		||||
     * @mode:     One of the enum CaretTrackingMode values.
 | 
			
		||||
     */
 | 
			
		||||
    setCaretTrackingMode: function(mode) {
 | 
			
		||||
        this._caretTrackingMode = mode;
 | 
			
		||||
        this._syncCaretTracking();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncFocusTracking: function() {
 | 
			
		||||
        let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE &&
 | 
			
		||||
            this.isActive();
 | 
			
		||||
 | 
			
		||||
        if (enabled)
 | 
			
		||||
            this._focusCaretTracker.registerFocusListener();
 | 
			
		||||
        else
 | 
			
		||||
            this._focusCaretTracker.deregisterFocusListener();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncCaretTracking: function() {
 | 
			
		||||
        let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE &&
 | 
			
		||||
            this.isActive();
 | 
			
		||||
 | 
			
		||||
        if (enabled)
 | 
			
		||||
            this._focusCaretTracker.registerCaretListener();
 | 
			
		||||
        else
 | 
			
		||||
            this._focusCaretTracker.deregisterCaretListener();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setViewPort
 | 
			
		||||
     * Sets the position and size of the ZoomRegion on screen.
 | 
			
		||||
@@ -1023,20 +1152,6 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
            this._magShaderEffects.setBrightness(this._brightness);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrive the current brightness of the Zoom Region.
 | 
			
		||||
     * @return  Object containing the brightness change for the red, green,
 | 
			
		||||
     *          and blue channels.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let brightness = {};
 | 
			
		||||
        brightness.r = this._brightness.r;
 | 
			
		||||
        brightness.g = this._brightness.g;
 | 
			
		||||
        brightness.b = this._brightness.b;
 | 
			
		||||
        return brightness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setContrast:
 | 
			
		||||
     * Alter the contrast of the magnified view.
 | 
			
		||||
@@ -1084,13 +1199,17 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        // Add a background for when the magnified uiGroup is scrolled
 | 
			
		||||
        // out of view (don't want to see desktop showing through).
 | 
			
		||||
        this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
 | 
			
		||||
                                               layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                               width: global.screen_width,
 | 
			
		||||
                                               height: global.screen_height });
 | 
			
		||||
        let noiseTexture = (new Background.SystemBackground()).actor;
 | 
			
		||||
        this._background.add_actor(noiseTexture);
 | 
			
		||||
        mainGroup.add_actor(this._background);
 | 
			
		||||
 | 
			
		||||
        // Clone the group that contains all of UI on the screen.  This is the
 | 
			
		||||
        // chrome, the windows, etc.
 | 
			
		||||
        this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
 | 
			
		||||
        this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup,
 | 
			
		||||
                                                 clip_to_allocation: true });
 | 
			
		||||
        mainGroup.add_actor(this._uiGroupClone);
 | 
			
		||||
 | 
			
		||||
        // Add either the given mouseSourceActor to the ZoomRegion, or a clone of
 | 
			
		||||
@@ -1243,19 +1362,47 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let yMouse = this._magnifier.yMouse;
 | 
			
		||||
 | 
			
		||||
        if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
 | 
			
		||||
            return this._centerFromMouseProportional(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromPointProportional(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
 | 
			
		||||
            return this._centerFromMousePush(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromPointPush(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
 | 
			
		||||
            return this._centerFromMouseCentered(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromPointCentered(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null; // Should never be hit
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromMousePush: function(xMouse, yMouse) {
 | 
			
		||||
    _centerFromCaretPosition: function() {
 | 
			
		||||
        let xCaret = this._xCaret;
 | 
			
		||||
        let yCaret = this._yCaret;
 | 
			
		||||
 | 
			
		||||
        if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
 | 
			
		||||
        else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
 | 
			
		||||
        else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
 | 
			
		||||
 | 
			
		||||
        this.scrollContentsTo(xCaret, yCaret);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromFocusPosition: function() {
 | 
			
		||||
        let xFocus = this._xFocus;
 | 
			
		||||
        let yFocus = this._yFocus;
 | 
			
		||||
 | 
			
		||||
        if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
 | 
			
		||||
        else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
 | 
			
		||||
        else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
 | 
			
		||||
 | 
			
		||||
        this.scrollContentsTo(xFocus, yFocus);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromPointPush: function(xPoint, yPoint) {
 | 
			
		||||
        let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
 | 
			
		||||
        let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
 | 
			
		||||
        let xPos = xRoi + widthRoi / 2;
 | 
			
		||||
@@ -1263,20 +1410,20 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let xRoiRight = xRoi + widthRoi - cursorWidth;
 | 
			
		||||
        let yRoiBottom = yRoi + heightRoi - cursorHeight;
 | 
			
		||||
 | 
			
		||||
        if (xMouse < xRoi)
 | 
			
		||||
            xPos -= (xRoi - xMouse);
 | 
			
		||||
        else if (xMouse > xRoiRight)
 | 
			
		||||
            xPos += (xMouse - xRoiRight);
 | 
			
		||||
        if (xPoint < xRoi)
 | 
			
		||||
            xPos -= (xRoi - xPoint);
 | 
			
		||||
        else if (xPoint > xRoiRight)
 | 
			
		||||
            xPos += (xPoint - xRoiRight);
 | 
			
		||||
 | 
			
		||||
        if (yMouse < yRoi)
 | 
			
		||||
            yPos -= (yRoi - yMouse);
 | 
			
		||||
        else if (yMouse > yRoiBottom)
 | 
			
		||||
            yPos += (yMouse - yRoiBottom);
 | 
			
		||||
        if (yPoint < yRoi)
 | 
			
		||||
            yPos -= (yRoi - yPoint);
 | 
			
		||||
        else if (yPoint > yRoiBottom)
 | 
			
		||||
            yPos += (yPoint - yRoiBottom);
 | 
			
		||||
 | 
			
		||||
        return [xPos, yPos];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromMouseProportional: function(xMouse, yMouse) {
 | 
			
		||||
    _centerFromPointProportional: function(xPoint, yPoint) {
 | 
			
		||||
        let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
 | 
			
		||||
        let halfScreenWidth = global.screen_width / 2;
 | 
			
		||||
        let halfScreenHeight = global.screen_height / 2;
 | 
			
		||||
@@ -1285,16 +1432,16 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
 | 
			
		||||
        let xPadding = unscaledPadding / this._xMagFactor;
 | 
			
		||||
        let yPadding = unscaledPadding / this._yMagFactor;
 | 
			
		||||
        let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth;   // -1 ... 1
 | 
			
		||||
        let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
 | 
			
		||||
        let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
 | 
			
		||||
        let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
 | 
			
		||||
        let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth;   // -1 ... 1
 | 
			
		||||
        let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
 | 
			
		||||
        let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
 | 
			
		||||
        let yPos = yPoint - yProportion * (heightRoi /2 - yPadding);
 | 
			
		||||
 | 
			
		||||
        return [xPos, yPos];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromMouseCentered: function(xMouse, yMouse) {
 | 
			
		||||
        return [xMouse, yMouse];
 | 
			
		||||
    _centerFromPointCentered: function(xPoint, yPoint) {
 | 
			
		||||
        return [xPoint, yPoint];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _screenToViewPort: function(screenX, screenY) {
 | 
			
		||||
@@ -1511,15 +1658,6 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        this._vertBottomHair.set_opacity(opacity);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getOpacity:
 | 
			
		||||
     * Retriev how opaque the crosshairs are.
 | 
			
		||||
     * @return: A value between 0 (transparent) and 255 (opaque).
 | 
			
		||||
     */
 | 
			
		||||
    getOpacity: function() {
 | 
			
		||||
        return this._horizLeftHair.get_opacity();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setLength:
 | 
			
		||||
     * Set the length of the vertical and horizontal lines in the crosshairs.
 | 
			
		||||
@@ -1563,15 +1701,6 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
     },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getClip:
 | 
			
		||||
     * Get the dimensions of the clip rectangle.
 | 
			
		||||
     * @return:   An array of the form [width, height].
 | 
			
		||||
     */
 | 
			
		||||
    getClip: function() {
 | 
			
		||||
        return this._clipSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * show:
 | 
			
		||||
     * Show the crosshairs.
 | 
			
		||||
@@ -1667,23 +1796,10 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
        this._inverse.set_enabled(invertFlag);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getInvertLightness:
 | 
			
		||||
     * Report whether the inversion effect is enabled.
 | 
			
		||||
     * @return:     Boolean.
 | 
			
		||||
     */
 | 
			
		||||
    getInvertLightness: function() {
 | 
			
		||||
        return this._inverse.get_enabled();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setColorSaturation: function(factor) {
 | 
			
		||||
        this._colorDesaturation.set_factor(1.0 - factor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getColorSaturation: function() {
 | 
			
		||||
        return 1.0 - this._colorDesaturation.get_factor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setBrightness:
 | 
			
		||||
     * Set the brightness of the magnified view.
 | 
			
		||||
@@ -1708,24 +1824,6 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrieve current brightness of the magnified view.
 | 
			
		||||
     * @return: Object containing the brightness for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard" 
 | 
			
		||||
     *          brightness (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed brightness, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let result = {};
 | 
			
		||||
        let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
 | 
			
		||||
        result.r = bRed;
 | 
			
		||||
        result.g = bGreen;
 | 
			
		||||
        result.b = bBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the contrast of the magnified view.
 | 
			
		||||
     * @contrast:   Object containing the contrast for the red, green,
 | 
			
		||||
@@ -1750,21 +1848,4 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
             bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve current contrast of the magnified view.
 | 
			
		||||
     * @return: Object containing the contrast for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *          contrast (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed contrast, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getContrast: function() {
 | 
			
		||||
        let resutl = {};
 | 
			
		||||
        let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
 | 
			
		||||
        result.r = cRed;
 | 
			
		||||
        result.g = cGreen;
 | 
			
		||||
        result.b = cBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,92 +4,94 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
 | 
			
		||||
const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
 | 
			
		||||
const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion';
 | 
			
		||||
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
 | 
			
		||||
 | 
			
		||||
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded.  See:
 | 
			
		||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
 | 
			
		||||
const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
 | 
			
		||||
<method name="setActive">
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="isActive">
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="showCursor" />
 | 
			
		||||
<method name="hideCursor" />
 | 
			
		||||
<method name="createZoomRegion">
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
    <arg type="ai" direction="in" />
 | 
			
		||||
    <arg type="ai" direction="in" />
 | 
			
		||||
    <arg type="o" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="addZoomRegion">
 | 
			
		||||
    <arg type="o" direction="in" />
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getZoomRegions">
 | 
			
		||||
    <arg type="ao" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="clearAllZoomRegions" />
 | 
			
		||||
<method name="fullScreenCapable">
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="setCrosswireSize">
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getCrosswireSize">
 | 
			
		||||
    <arg type="i" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="setCrosswireLength">
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getCrosswireLength">
 | 
			
		||||
    <arg type="i" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="setCrosswireClip">
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getCrosswireClip">
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="setCrosswireColor">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getCrosswireColor">
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const MagnifierIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Magnifier"> \
 | 
			
		||||
<method name="setActive"> \
 | 
			
		||||
    <arg type="b" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="isActive"> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="showCursor" /> \
 | 
			
		||||
<method name="hideCursor" /> \
 | 
			
		||||
<method name="createZoomRegion"> \
 | 
			
		||||
    <arg type="d" direction="in" /> \
 | 
			
		||||
    <arg type="d" direction="in" /> \
 | 
			
		||||
    <arg type="ai" direction="in" /> \
 | 
			
		||||
    <arg type="ai" direction="in" /> \
 | 
			
		||||
    <arg type="o" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="addZoomRegion"> \
 | 
			
		||||
    <arg type="o" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getZoomRegions"> \
 | 
			
		||||
    <arg type="ao" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="clearAllZoomRegions" /> \
 | 
			
		||||
<method name="fullScreenCapable"> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="setCrosswireSize"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getCrosswireSize"> \
 | 
			
		||||
    <arg type="i" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="setCrosswireLength"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getCrosswireLength"> \
 | 
			
		||||
    <arg type="i" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="setCrosswireClip"> \
 | 
			
		||||
    <arg type="b" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getCrosswireClip"> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="setCrosswireColor"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getCrosswireColor"> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded.  See:
 | 
			
		||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
 | 
			
		||||
const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
 | 
			
		||||
<method name="setMagFactor">
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getMagFactor">
 | 
			
		||||
    <arg type="d" direction="out" />
 | 
			
		||||
    <arg type="d" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="setRoi">
 | 
			
		||||
    <arg type="ai" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="getRoi">
 | 
			
		||||
    <arg type="ai" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="shiftContentsTo">
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="moveResize">
 | 
			
		||||
    <arg type="ai" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ZoomRegionIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Magnifier.ZoomRegion"> \
 | 
			
		||||
<method name="setMagFactor"> \
 | 
			
		||||
    <arg type="d" direction="in" /> \
 | 
			
		||||
    <arg type="d" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getMagFactor"> \
 | 
			
		||||
    <arg type="d" direction="out" /> \
 | 
			
		||||
    <arg type="d" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="setRoi"> \
 | 
			
		||||
    <arg type="ai" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="getRoi"> \
 | 
			
		||||
    <arg type="ai" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="shiftContentsTo"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="moveResize"> \
 | 
			
		||||
    <arg type="ai" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
// For making unique ZoomRegion DBus proxy object paths of the form:
 | 
			
		||||
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const LookingGlass = imports.ui.lookingGlass;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
 | 
			
		||||
const Screencast = imports.ui.screencast;
 | 
			
		||||
const ScreenShield = imports.ui.screenShield;
 | 
			
		||||
const Scripting = imports.ui.scripting;
 | 
			
		||||
const SessionMode = imports.ui.sessionMode;
 | 
			
		||||
@@ -42,6 +43,7 @@ const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
 | 
			
		||||
 | 
			
		||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
 | 
			
		||||
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
 | 
			
		||||
const GNOMESHELL_STARTED_MESSAGE_ID = 'f3ea493c22934e26811cd62abe8e203a';
 | 
			
		||||
 | 
			
		||||
let componentManager = null;
 | 
			
		||||
let panel = null;
 | 
			
		||||
@@ -54,11 +56,12 @@ let screenShield = null;
 | 
			
		||||
let notificationDaemon = null;
 | 
			
		||||
let windowAttentionHandler = null;
 | 
			
		||||
let ctrlAltTabManager = null;
 | 
			
		||||
let osdWindow = null;
 | 
			
		||||
let osdWindowManager = null;
 | 
			
		||||
let sessionMode = null;
 | 
			
		||||
let shellDBusService = null;
 | 
			
		||||
let shellMountOpDBusService = null;
 | 
			
		||||
let screenSaverDBus = null;
 | 
			
		||||
let screencastService = null;
 | 
			
		||||
let modalCount = 0;
 | 
			
		||||
let keybindingMode = Shell.KeyBindingMode.NONE;
 | 
			
		||||
let modalActorFocusStack = [];
 | 
			
		||||
@@ -71,7 +74,6 @@ let _startDate;
 | 
			
		||||
let _defaultCssStylesheet = null;
 | 
			
		||||
let _cssStylesheet = null;
 | 
			
		||||
let _a11ySettings = null;
 | 
			
		||||
let dynamicWorkspacesSchema = null;
 | 
			
		||||
 | 
			
		||||
function _sessionUpdated() {
 | 
			
		||||
    _loadDefaultStylesheet();
 | 
			
		||||
@@ -88,8 +90,12 @@ function _sessionUpdated() {
 | 
			
		||||
                                  Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                  sessionMode.hasRunDialog ? openRunDialog : null);
 | 
			
		||||
 | 
			
		||||
    if (!sessionMode.hasRunDialog && lookingGlass)
 | 
			
		||||
        lookingGlass.close();
 | 
			
		||||
    if (!sessionMode.hasRunDialog) {
 | 
			
		||||
        if (runDialog)
 | 
			
		||||
            runDialog.close();
 | 
			
		||||
        if (lookingGlass)
 | 
			
		||||
            lookingGlass.close();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
@@ -103,13 +109,7 @@ function start() {
 | 
			
		||||
    Gio.DesktopAppInfo.set_desktop_env('GNOME');
 | 
			
		||||
 | 
			
		||||
    sessionMode = new SessionMode.SessionMode();
 | 
			
		||||
    sessionMode.connect('sessions-loaded', _sessionsLoaded);
 | 
			
		||||
    sessionMode.init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sessionsLoaded() {
 | 
			
		||||
    sessionMode.connect('updated', _sessionUpdated);
 | 
			
		||||
    _initializePrefs();
 | 
			
		||||
    _initializeUI();
 | 
			
		||||
 | 
			
		||||
    shellDBusService = new ShellDBus.GnomeShell();
 | 
			
		||||
@@ -118,17 +118,6 @@ function _sessionsLoaded() {
 | 
			
		||||
    _sessionUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _initializePrefs() {
 | 
			
		||||
    let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
 | 
			
		||||
    for (let i = 0; i < keys.length; i++)
 | 
			
		||||
        Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
 | 
			
		||||
 | 
			
		||||
    if (keys.indexOf('dynamic-workspaces') > -1)
 | 
			
		||||
        dynamicWorkspacesSchema = sessionMode.overridesSchema;
 | 
			
		||||
    else
 | 
			
		||||
        dynamicWorkspacesSchema = 'org.gnome.mutter';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _initializeUI() {
 | 
			
		||||
    // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
 | 
			
		||||
    // also initialize ShellAppSystem first.  ShellAppSystem
 | 
			
		||||
@@ -151,17 +140,18 @@ function _initializeUI() {
 | 
			
		||||
    // working until it's updated.
 | 
			
		||||
    uiGroup = layoutManager.uiGroup;
 | 
			
		||||
 | 
			
		||||
    screencastService = new Screencast.ScreencastService();
 | 
			
		||||
    xdndHandler = new XdndHandler.XdndHandler();
 | 
			
		||||
    ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
 | 
			
		||||
    osdWindow = new OsdWindow.OsdWindow();
 | 
			
		||||
    osdWindowManager = new OsdWindow.OsdWindowManager();
 | 
			
		||||
    overview = new Overview.Overview();
 | 
			
		||||
    wm = new WindowManager.WindowManager();
 | 
			
		||||
    magnifier = new Magnifier.Magnifier();
 | 
			
		||||
    if (LoginManager.canLock())
 | 
			
		||||
        screenShield = new ScreenShield.ScreenShield();
 | 
			
		||||
 | 
			
		||||
    panel = new Panel.Panel();
 | 
			
		||||
    messageTray = new MessageTray.MessageTray();
 | 
			
		||||
    panel = new Panel.Panel();
 | 
			
		||||
    keyboard = new Keyboard.Keyboard();
 | 
			
		||||
    notificationDaemon = new NotificationDaemon.NotificationDaemon();
 | 
			
		||||
    windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
 | 
			
		||||
@@ -186,8 +176,6 @@ function _initializeUI() {
 | 
			
		||||
 | 
			
		||||
    _startDate = new Date();
 | 
			
		||||
 | 
			
		||||
    log('GNOME Shell started at ' + _startDate);
 | 
			
		||||
 | 
			
		||||
    let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
 | 
			
		||||
    if (perfModuleName) {
 | 
			
		||||
        let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
 | 
			
		||||
@@ -211,6 +199,17 @@ function _initializeUI() {
 | 
			
		||||
                              if (screenShield) {
 | 
			
		||||
                                  screenShield.lockIfWasLocked();
 | 
			
		||||
                              }
 | 
			
		||||
                              if (LoginManager.haveSystemd() &&
 | 
			
		||||
                                  sessionMode.currentMode != 'gdm' &&
 | 
			
		||||
                                  sessionMode.currentMode != 'initial-setup') {
 | 
			
		||||
                                  // Do not import globally to not depend
 | 
			
		||||
                                  // on systemd on non-systemd systems.
 | 
			
		||||
                                  let GSystem = imports.gi.GSystem;
 | 
			
		||||
                                  GSystem.log_structured_print('GNOME Shell started at ' + _startDate,
 | 
			
		||||
                                                               ['MESSAGE_ID=' + GNOMESHELL_STARTED_MESSAGE_ID]);
 | 
			
		||||
                              } else {
 | 
			
		||||
                                  log('GNOME Shell started at ' + _startDate);
 | 
			
		||||
                              }
 | 
			
		||||
                          });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -260,11 +259,7 @@ function loadTheme() {
 | 
			
		||||
    let themeContext = St.ThemeContext.get_for_stage (global.stage);
 | 
			
		||||
    let previousTheme = themeContext.get_theme();
 | 
			
		||||
 | 
			
		||||
    let cssStylesheet = _defaultCssStylesheet;
 | 
			
		||||
    if (_cssStylesheet != null)
 | 
			
		||||
        cssStylesheet = _cssStylesheet;
 | 
			
		||||
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: _cssStylesheet,
 | 
			
		||||
                                default_stylesheet: _defaultCssStylesheet });
 | 
			
		||||
 | 
			
		||||
    if (previousTheme) {
 | 
			
		||||
@@ -286,7 +281,6 @@ function notify(msg, details) {
 | 
			
		||||
    let source = new MessageTray.SystemNotificationSource();
 | 
			
		||||
    messageTray.add(source);
 | 
			
		||||
    let notification = new MessageTray.Notification(source, msg, details);
 | 
			
		||||
    notification.setTransient(true);
 | 
			
		||||
    source.notify(notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -356,8 +350,6 @@ function pushModal(actor, params) {
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
 | 
			
		||||
 | 
			
		||||
    modalCount += 1;
 | 
			
		||||
    let actorDestroyId = actor.connect('destroy', function() {
 | 
			
		||||
        let index = _findModal(actor);
 | 
			
		||||
@@ -406,7 +398,6 @@ function popModal(actor, timestamp) {
 | 
			
		||||
    if (focusIndex < 0) {
 | 
			
		||||
        global.stage.set_key_focus(null);
 | 
			
		||||
        global.end_modal(timestamp);
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
        keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
 | 
			
		||||
        throw new Error('incorrect pop');
 | 
			
		||||
@@ -454,7 +445,6 @@ function popModal(actor, timestamp) {
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    global.end_modal(timestamp);
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
    Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
    keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
}
 | 
			
		||||
@@ -612,7 +602,8 @@ function queueDeferredWork(workId) {
 | 
			
		||||
        _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
 | 
			
		||||
            _runAllDeferredWork();
 | 
			
		||||
            _deferredTimeoutId = 0;
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        });
 | 
			
		||||
        GLib.Source.set_name_by_id(_deferredTimeoutId, '[gnome-shell] _runAllDeferredWork');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2515
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
							
						
						@@ -41,7 +41,6 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { shellReactive: false,
 | 
			
		||||
                                        styleClass: null,
 | 
			
		||||
                                        parentActor: Main.uiGroup,
 | 
			
		||||
                                        keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
 | 
			
		||||
                                        shouldFadeIn: true,
 | 
			
		||||
                                        destroyOnClose: true });
 | 
			
		||||
@@ -57,7 +56,7 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
                                      x: 0,
 | 
			
		||||
                                      y: 0,
 | 
			
		||||
                                      accessible_role: Atk.Role.DIALOG });
 | 
			
		||||
        params.parentActor.add_actor(this._group);
 | 
			
		||||
        Main.layoutManager.modalDialogGroup.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        let constraint = new Clutter.BindConstraint({ source: global.stage,
 | 
			
		||||
                                                      coordinate: Clutter.BindCoordinate.ALL });
 | 
			
		||||
@@ -79,13 +78,18 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
 | 
			
		||||
                                               vertical:    true });
 | 
			
		||||
        if (params.styleClass != null) {
 | 
			
		||||
        // modal dialogs are fixed width and grow vertically; set the request
 | 
			
		||||
        // mode accordingly so wrapped labels are handled correctly during
 | 
			
		||||
        // size requests.
 | 
			
		||||
        this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
 | 
			
		||||
 | 
			
		||||
        if (params.styleClass != null)
 | 
			
		||||
            this.dialogLayout.add_style_class_name(params.styleClass);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._shellReactive) {
 | 
			
		||||
            this._lightbox = new Lightbox.Lightbox(this._group,
 | 
			
		||||
                                                   { inhibitEvents: true });
 | 
			
		||||
                                                   { inhibitEvents: true,
 | 
			
		||||
                                                     radialEffect: true });
 | 
			
		||||
            this._lightbox.highlight(this._backgroundBin);
 | 
			
		||||
 | 
			
		||||
            this._eventBlocker = new Clutter.Actor({ reactive: true });
 | 
			
		||||
@@ -96,7 +100,8 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.contentLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this.dialogLayout.add(this.contentLayout,
 | 
			
		||||
                              { x_fill:  true,
 | 
			
		||||
                              { expand:  true,
 | 
			
		||||
                                x_fill:  true,
 | 
			
		||||
                                y_fill:  true,
 | 
			
		||||
                                x_align: St.Align.MIDDLE,
 | 
			
		||||
                                y_align: St.Align.START });
 | 
			
		||||
@@ -104,8 +109,7 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
 | 
			
		||||
                                               vertical: false });
 | 
			
		||||
        this.dialogLayout.add(this.buttonLayout,
 | 
			
		||||
                              { expand:  true,
 | 
			
		||||
                                x_align: St.Align.MIDDLE,
 | 
			
		||||
                              { x_align: St.Align.MIDDLE,
 | 
			
		||||
                                y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this.dialogLayout);
 | 
			
		||||
@@ -225,6 +229,7 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(object, event) {
 | 
			
		||||
        this._pressedKey = event.get_key_symbol();
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyReleaseEvent: function(object, event) {
 | 
			
		||||
@@ -233,21 +238,21 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol != pressedKey)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let buttonInfo = this._buttonKeys[symbol];
 | 
			
		||||
        if (!buttonInfo)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let button = buttonInfo['button'];
 | 
			
		||||
        let action = buttonInfo['action'];
 | 
			
		||||
 | 
			
		||||
        if (action && button.reactive) {
 | 
			
		||||
            action();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGroupDestroy: function() {
 | 
			
		||||
@@ -357,8 +362,9 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        if (this._savedKeyFocus) {
 | 
			
		||||
            this._savedKeyFocus.grab_key_focus();
 | 
			
		||||
            this._savedKeyFocus = null;
 | 
			
		||||
        } else
 | 
			
		||||
        } else {
 | 
			
		||||
            this._initialKeyFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._shellReactive)
 | 
			
		||||
            this._eventBlocker.lower_bottom();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GdkPixbuf = imports.gi.GdkPixbuf;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
@@ -15,54 +16,56 @@ const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
let nextNotificationId = 1;
 | 
			
		||||
 | 
			
		||||
// Should really be defined in Gio.js
 | 
			
		||||
const BusIface = <interface name="org.freedesktop.DBus">
 | 
			
		||||
<method name="GetConnectionUnixProcessID">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
    <arg type="u" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const BusIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.DBus"> \
 | 
			
		||||
<method name="GetConnectionUnixProcessID"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="u" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
 | 
			
		||||
function Bus() {
 | 
			
		||||
    return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
 | 
			
		||||
<method name="Notify">
 | 
			
		||||
    <arg type="s" direction="in"/>
 | 
			
		||||
    <arg type="u" direction="in"/>
 | 
			
		||||
    <arg type="s" direction="in"/>
 | 
			
		||||
    <arg type="s" direction="in"/>
 | 
			
		||||
    <arg type="s" direction="in"/>
 | 
			
		||||
    <arg type="as" direction="in"/>
 | 
			
		||||
    <arg type="a{sv}" direction="in"/>
 | 
			
		||||
    <arg type="i" direction="in"/>
 | 
			
		||||
    <arg type="u" direction="out"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="CloseNotification">
 | 
			
		||||
    <arg type="u" direction="in"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetCapabilities">
 | 
			
		||||
    <arg type="as" direction="out"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetServerInformation">
 | 
			
		||||
    <arg type="s" direction="out"/>
 | 
			
		||||
    <arg type="s" direction="out"/>
 | 
			
		||||
    <arg type="s" direction="out"/>
 | 
			
		||||
    <arg type="s" direction="out"/>
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="NotificationClosed">
 | 
			
		||||
    <arg type="u"/>
 | 
			
		||||
    <arg type="u"/>
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name="ActionInvoked">
 | 
			
		||||
    <arg type="u"/>
 | 
			
		||||
    <arg type="s"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const FdoNotificationsIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.Notifications"> \
 | 
			
		||||
<method name="Notify"> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="u" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="s" direction="in"/> \
 | 
			
		||||
    <arg type="as" direction="in"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="in"/> \
 | 
			
		||||
    <arg type="i" direction="in"/> \
 | 
			
		||||
    <arg type="u" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="CloseNotification"> \
 | 
			
		||||
    <arg type="u" direction="in"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetCapabilities"> \
 | 
			
		||||
    <arg type="as" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetServerInformation"> \
 | 
			
		||||
    <arg type="s" direction="out"/> \
 | 
			
		||||
    <arg type="s" direction="out"/> \
 | 
			
		||||
    <arg type="s" direction="out"/> \
 | 
			
		||||
    <arg type="s" direction="out"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="NotificationClosed"> \
 | 
			
		||||
    <arg type="u"/> \
 | 
			
		||||
    <arg type="u"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<signal name="ActionInvoked"> \
 | 
			
		||||
    <arg type="u"/> \
 | 
			
		||||
    <arg type="s"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const NotificationClosedReason = {
 | 
			
		||||
    EXPIRED: 1,
 | 
			
		||||
@@ -88,146 +91,11 @@ const rewriteRules = {
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
 | 
			
		||||
    'bluetooth-applet': 'bluetooth',
 | 
			
		||||
    'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
 | 
			
		||||
                                             // when moved to control center
 | 
			
		||||
    'gnome-sound-applet': 'volume',
 | 
			
		||||
    'nm-applet': 'network',
 | 
			
		||||
    'gnome-power-manager': 'battery',
 | 
			
		||||
    'keyboard': 'keyboard',
 | 
			
		||||
    'a11y-keyboard': 'a11y',
 | 
			
		||||
    'kbd-scrolllock': 'keyboard',
 | 
			
		||||
    'kbd-numlock': 'keyboard',
 | 
			
		||||
    'kbd-capslock': 'keyboard',
 | 
			
		||||
    'ibus-ui-gtk': 'keyboard'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const NotificationGenericPolicy = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationGenericPolicy',
 | 
			
		||||
    Extends: MessageTray.NotificationPolicy,
 | 
			
		||||
const FdoNotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'FdoNotificationDaemon',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        // Don't chain to parent, it would try setting
 | 
			
		||||
        // our properties to the defaults
 | 
			
		||||
 | 
			
		||||
        this.id = 'generic';
 | 
			
		||||
 | 
			
		||||
        this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
 | 
			
		||||
        this._masterSettings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    store: function() { },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._masterSettings.run_dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changed: function(settings, key) {
 | 
			
		||||
        this.emit('policy-changed', key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enable() {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enableSound() {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showBanners() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-banners');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get forceExpanded() {
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showInLockScreen() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-in-lock-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get detailsInLockScreen() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationApplicationPolicy = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationApplicationPolicy',
 | 
			
		||||
    Extends: MessageTray.NotificationPolicy,
 | 
			
		||||
 | 
			
		||||
    _init: function(id) {
 | 
			
		||||
        // Don't chain to parent, it would try setting
 | 
			
		||||
        // our properties to the defaults
 | 
			
		||||
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this._canonicalId = this._canonicalizeId(id)
 | 
			
		||||
 | 
			
		||||
        this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
 | 
			
		||||
                                            path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
 | 
			
		||||
 | 
			
		||||
        this._masterSettings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
        this._settings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    store: function() {
 | 
			
		||||
        this._settings.set_string('application-id', this.id + '.desktop');
 | 
			
		||||
 | 
			
		||||
        let apps = this._masterSettings.get_strv('application-children');
 | 
			
		||||
        if (apps.indexOf(this._canonicalId) < 0) {
 | 
			
		||||
            apps.push(this._canonicalId);
 | 
			
		||||
            this._masterSettings.set_strv('application-children', apps);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._masterSettings.run_dispose();
 | 
			
		||||
        this._settings.run_dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changed: function(settings, key) {
 | 
			
		||||
        this.emit('policy-changed', key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canonicalizeId: function(id) {
 | 
			
		||||
        // Keys are restricted to lowercase alphanumeric characters and dash,
 | 
			
		||||
        // and two dashes cannot be in succession
 | 
			
		||||
        return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enable() {
 | 
			
		||||
        return this._settings.get_boolean('enable');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enableSound() {
 | 
			
		||||
        return this._settings.get_boolean('enable-sound-alerts');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showBanners() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-banners') &&
 | 
			
		||||
            this._settings.get_boolean('show-banners');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get forceExpanded() {
 | 
			
		||||
        return this._settings.get_boolean('force-expanded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showInLockScreen() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-in-lock-screen') &&
 | 
			
		||||
            this._settings.get_boolean('show-in-lock-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get detailsInLockScreen() {
 | 
			
		||||
        return this._settings.get_boolean('details-in-lock-screen');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemon',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
 | 
			
		||||
 | 
			
		||||
        this._sources = [];
 | 
			
		||||
@@ -235,6 +103,8 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        this._notifications = {};
 | 
			
		||||
        this._busProxy = new Bus();
 | 
			
		||||
 | 
			
		||||
        this._nextNotificationId = 1;
 | 
			
		||||
 | 
			
		||||
        this._trayManager = new Shell.TrayManager();
 | 
			
		||||
        this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
 | 
			
		||||
        this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
 | 
			
		||||
@@ -244,7 +114,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        Main.overview.connect('hidden',
 | 
			
		||||
            Lang.bind(this, this._onFocusAppChanged));
 | 
			
		||||
 | 
			
		||||
        this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
 | 
			
		||||
        // this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _imageForNotificationData: function(hints) {
 | 
			
		||||
@@ -296,12 +166,10 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the source associated with ndata.notification if it is set.
 | 
			
		||||
    // Otherwise, returns the source associated with the title and pid if
 | 
			
		||||
    // such source is stored in this._sources and the notification is not
 | 
			
		||||
    // transient. If the existing or requested source is associated with
 | 
			
		||||
    // a tray icon and passed in pid matches a pid of an existing source,
 | 
			
		||||
    // the title match is ignored to enable representing a tray icon and
 | 
			
		||||
    // notifications from the same application with a single source.
 | 
			
		||||
    // If the existing or requested source is associated with a tray icon
 | 
			
		||||
    // and passed in pid matches a pid of an existing source, the title
 | 
			
		||||
    // match is ignored to enable representing a tray icon and notifications
 | 
			
		||||
    // from the same application with a single source.
 | 
			
		||||
    //
 | 
			
		||||
    // If no existing source is found, a new source is created as long as
 | 
			
		||||
    // pid is provided.
 | 
			
		||||
@@ -312,39 +180,23 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        if (!pid && !(ndata && ndata.notification))
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        // We use notification's source for the notifications we still have
 | 
			
		||||
        // around that are getting replaced because we don't keep sources
 | 
			
		||||
        // for transient notifications in this._sources, but we still want
 | 
			
		||||
        // the notification associated with them to get replaced correctly.
 | 
			
		||||
        if (ndata && ndata.notification)
 | 
			
		||||
            return ndata.notification.source;
 | 
			
		||||
 | 
			
		||||
        let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        // We don't want to override a persistent notification
 | 
			
		||||
        // with a transient one from the same sender, so we
 | 
			
		||||
        // always create a new source object for new transient notifications
 | 
			
		||||
        // and never add it to this._sources .
 | 
			
		||||
        if (!isForTransientNotification) {
 | 
			
		||||
            let source = this._lookupSource(title, pid, trayIcon);
 | 
			
		||||
            if (source) {
 | 
			
		||||
                source.setTitle(title);
 | 
			
		||||
                return source;
 | 
			
		||||
            }
 | 
			
		||||
        let source = this._lookupSource(title, pid, trayIcon);
 | 
			
		||||
        if (source) {
 | 
			
		||||
            source.setTitle(title);
 | 
			
		||||
            return source;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
 | 
			
		||||
        source.setTransient(isForTransientNotification);
 | 
			
		||||
        let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
 | 
			
		||||
 | 
			
		||||
        if (!isForTransientNotification) {
 | 
			
		||||
            this._sources.push(source);
 | 
			
		||||
            source.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function() {
 | 
			
		||||
                    let index = this._sources.indexOf(source);
 | 
			
		||||
                    if (index >= 0)
 | 
			
		||||
                        this._sources.splice(index, 1);
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
        this._sources.push(source);
 | 
			
		||||
        source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            let index = this._sources.indexOf(source);
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
                this._sources.splice(index, 1);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
        return source;
 | 
			
		||||
@@ -372,12 +224,13 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
              hints['category'] == 'presence.offline')) {
 | 
			
		||||
            // Ignore replacesId since we already sent back a
 | 
			
		||||
            // NotificationClosed for that id.
 | 
			
		||||
            id = nextNotificationId++;
 | 
			
		||||
            Mainloop.idle_add(Lang.bind(this,
 | 
			
		||||
                                        function () {
 | 
			
		||||
                                            this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
 | 
			
		||||
                                            return false;
 | 
			
		||||
                                        }));
 | 
			
		||||
            id = this._nextNotificationId++;
 | 
			
		||||
            let idle_id = Mainloop.idle_add(Lang.bind(this,
 | 
			
		||||
                                            function () {
 | 
			
		||||
                                                this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
 | 
			
		||||
                                                return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                            }));
 | 
			
		||||
            GLib.Source.set_name_by_id(idle_id, '[gnome-shell] this._emitNotificationClosed');
 | 
			
		||||
            return invocation.return_value(GLib.Variant.new('(u)', [id]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -396,12 +249,13 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        if (!hints['image-path'] && hints['image_path'])
 | 
			
		||||
            hints['image-path'] = hints['image_path']; // version 1.1 of the spec
 | 
			
		||||
 | 
			
		||||
        if (!hints['image-data'])
 | 
			
		||||
        if (!hints['image-data']) {
 | 
			
		||||
            if (hints['image_data'])
 | 
			
		||||
                hints['image-data'] = hints['image_data']; // version 1.1 of the spec
 | 
			
		||||
            else if (hints['icon_data'] && !hints['image-path'])
 | 
			
		||||
                // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
 | 
			
		||||
                hints['image-data'] = hints['icon_data'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ndata = { appName: appName,
 | 
			
		||||
                      icon: icon,
 | 
			
		||||
@@ -415,7 +269,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            ndata.notification = this._notifications[replacesId].notification;
 | 
			
		||||
        } else {
 | 
			
		||||
            replacesId = 0;
 | 
			
		||||
            ndata.id = id = nextNotificationId++;
 | 
			
		||||
            ndata.id = id = this._nextNotificationId++;
 | 
			
		||||
        }
 | 
			
		||||
        this._notifications[id] = ndata;
 | 
			
		||||
 | 
			
		||||
@@ -450,26 +304,30 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            let [pid] = result;
 | 
			
		||||
            source = this._getSource(appName, pid, ndata, sender, null);
 | 
			
		||||
 | 
			
		||||
            // We only store sender-pid entries for persistent sources.
 | 
			
		||||
            // Removing the entries once the source is destroyed
 | 
			
		||||
            // would result in the entries associated with transient
 | 
			
		||||
            // sources removed once the notification is shown anyway.
 | 
			
		||||
            // However, keeping these pairs would mean that we would
 | 
			
		||||
            // possibly remove an entry associated with a persistent
 | 
			
		||||
            // source when a transient source for the same sender is
 | 
			
		||||
            // distroyed.
 | 
			
		||||
            if (!source.isTransient) {
 | 
			
		||||
                this._senderToPid[sender] = pid;
 | 
			
		||||
                source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
                    delete this._senderToPid[sender];
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
            this._senderToPid[sender] = pid;
 | 
			
		||||
            source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
                delete this._senderToPid[sender];
 | 
			
		||||
            }));
 | 
			
		||||
            this._notifyForSource(source, ndata);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return invocation.return_value(GLib.Variant.new('(u)', [id]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _makeButton: function(id, label, useActionIcons) {
 | 
			
		||||
        let button = new St.Button({ can_focus: true,
 | 
			
		||||
                                     x_expand: true,
 | 
			
		||||
                                     style_class: 'notification-button' });
 | 
			
		||||
        let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
 | 
			
		||||
 | 
			
		||||
        if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
 | 
			
		||||
            button.child = new St.Icon({ icon_name: iconName, icon_size: 16 });
 | 
			
		||||
        } else {
 | 
			
		||||
            button.label = label;
 | 
			
		||||
        }
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notifyForSource: function(source, ndata) {
 | 
			
		||||
        let [id, icon, summary, body, actions, hints, notification] =
 | 
			
		||||
            [ndata.id, ndata.icon, ndata.summary, ndata.body,
 | 
			
		||||
@@ -495,10 +353,6 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
                    }
 | 
			
		||||
                    this._emitNotificationClosed(ndata.id, notificationClosedReason);
 | 
			
		||||
                }));
 | 
			
		||||
            notification.connect('action-invoked', Lang.bind(this,
 | 
			
		||||
                function(n, actionId) {
 | 
			
		||||
                    this._emitActionInvoked(ndata.id, actionId);
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mark music notifications so they can be shown in the screen shield
 | 
			
		||||
@@ -507,8 +361,6 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        let gicon = this._iconForNotificationData(icon, hints);
 | 
			
		||||
        let gimage = this._imageForNotificationData(hints);
 | 
			
		||||
 | 
			
		||||
        let image = null;
 | 
			
		||||
 | 
			
		||||
        // If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
 | 
			
		||||
        // and don't show a large image. There are currently many applications that use
 | 
			
		||||
        // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
 | 
			
		||||
@@ -517,10 +369,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        // So the logic here does the right thing for this case. If both an icon and either
 | 
			
		||||
        // one of 'image-data' or 'image-path' are specified, we show both an icon and
 | 
			
		||||
        // a large image.
 | 
			
		||||
        if (gicon && gimage)
 | 
			
		||||
            image = new St.Icon({ gicon: gimage,
 | 
			
		||||
                                  icon_size: notification.IMAGE_SIZE });
 | 
			
		||||
        else if (!gicon && gimage)
 | 
			
		||||
        if (!gicon && gimage)
 | 
			
		||||
            gicon = gimage;
 | 
			
		||||
        else if (!gicon)
 | 
			
		||||
            gicon = this._fallbackIconForNotificationData(hints);
 | 
			
		||||
@@ -530,20 +379,34 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
                                             clear: true,
 | 
			
		||||
                                             soundFile: hints['sound-file'],
 | 
			
		||||
                                             soundName: hints['sound-name'] });
 | 
			
		||||
        notification.setImage(image);
 | 
			
		||||
 | 
			
		||||
        let hasDefaultAction = false;
 | 
			
		||||
 | 
			
		||||
        if (actions.length) {
 | 
			
		||||
            notification.setUseActionIcons(hints['action-icons'] == true);
 | 
			
		||||
            let useActionIcons = (hints['action-icons'] == true);
 | 
			
		||||
 | 
			
		||||
            for (let i = 0; i < actions.length - 1; i += 2) {
 | 
			
		||||
                if (actions[i] == 'default')
 | 
			
		||||
                    notification.connect('clicked', Lang.bind(this,
 | 
			
		||||
                        function() {
 | 
			
		||||
                            this._emitActionInvoked(ndata.id, "default");
 | 
			
		||||
                        }));
 | 
			
		||||
                else
 | 
			
		||||
                    notification.addButton(actions[i], actions[i + 1]);
 | 
			
		||||
                let [actionId, label] = [actions[i], actions[i+1]];
 | 
			
		||||
                if (actionId == 'default') {
 | 
			
		||||
                    hasDefaultAction = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
 | 
			
		||||
                        this._emitActionInvoked(ndata.id, actionId);
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (hasDefaultAction) {
 | 
			
		||||
            notification.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                this._emitActionInvoked(ndata.id, 'default');
 | 
			
		||||
            }));
 | 
			
		||||
        } else {
 | 
			
		||||
            notification.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                source.open();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (hints.urgency) {
 | 
			
		||||
            case Urgency.LOW:
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.LOW);
 | 
			
		||||
@@ -555,10 +418,6 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        notification.setResident(hints.resident == true);
 | 
			
		||||
        // 'transient' is a reserved keyword in JS, so we have to retrieve the value
 | 
			
		||||
        // of the 'transient' hint with hints['transient'] rather than hints.transient
 | 
			
		||||
        notification.setTransient(hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        let sourceGIcon = source.useNotificationIcon ? gicon : null;
 | 
			
		||||
        source.processNotification(notification, sourceGIcon);
 | 
			
		||||
@@ -583,7 +442,6 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            'body-markup',
 | 
			
		||||
            // 'icon-multi',
 | 
			
		||||
            'icon-static',
 | 
			
		||||
            'persistence',
 | 
			
		||||
            'sound',
 | 
			
		||||
        ];
 | 
			
		||||
    },
 | 
			
		||||
@@ -605,7 +463,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        for (let i = 0; i < this._sources.length; i++) {
 | 
			
		||||
            let source = this._sources[i];
 | 
			
		||||
            if (source.app == tracker.focus_app) {
 | 
			
		||||
                source.destroyNonResidentNotifications();
 | 
			
		||||
                source.destroyNotifications();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -622,10 +480,6 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTrayIconAdded: function(o, icon) {
 | 
			
		||||
        let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
 | 
			
		||||
        if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -636,8 +490,8 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Source = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemonSource',
 | 
			
		||||
const FdoNotificationDaemonSource = new Lang.Class({
 | 
			
		||||
    Name: 'FdoNotificationDaemonSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
 | 
			
		||||
    _init: function(title, pid, sender, trayIcon, appId) {
 | 
			
		||||
@@ -672,11 +526,11 @@ const Source = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
        if (this.app && this.app.get_app_info()) {
 | 
			
		||||
            let id = this.app.get_id().replace(/\.desktop$/,'');
 | 
			
		||||
            return new NotificationApplicationPolicy(id);
 | 
			
		||||
            return new MessageTray.NotificationApplicationPolicy(id);
 | 
			
		||||
        } else {
 | 
			
		||||
            return new NotificationGenericPolicy();
 | 
			
		||||
            return new MessageTray.NotificationGenericPolicy();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -697,7 +551,7 @@ const Source = new Lang.Class({
 | 
			
		||||
            this.iconUpdated();
 | 
			
		||||
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        if (notification.resident && this.app && tracker.focus_app == this.app)
 | 
			
		||||
        if (this.app && tracker.focus_app == this.app)
 | 
			
		||||
            this.pushNotification(notification);
 | 
			
		||||
        else
 | 
			
		||||
            this.notify(notification);
 | 
			
		||||
@@ -717,8 +571,8 @@ const Source = new Lang.Class({
 | 
			
		||||
            this.notifications.length > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
 | 
			
		||||
            global.disconnect(id);
 | 
			
		||||
        let id = global.stage.connect('deactivate', Lang.bind(this, function () {
 | 
			
		||||
            global.stage.disconnect(id);
 | 
			
		||||
            this.trayIcon.click(event);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
@@ -734,7 +588,11 @@ const Source = new Lang.Class({
 | 
			
		||||
            return app;
 | 
			
		||||
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class);
 | 
			
		||||
            if (app != null)
 | 
			
		||||
                return app;
 | 
			
		||||
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
 | 
			
		||||
            if (app != null)
 | 
			
		||||
                return app;
 | 
			
		||||
        }
 | 
			
		||||
@@ -748,22 +606,6 @@ const Source = new Lang.Class({
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setApp: function(appId) {
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.app = this._getApp(appId);
 | 
			
		||||
        if (!this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Only override the icon if we were previously using
 | 
			
		||||
        // notification-based icons (ie, not a trayicon) or if it was unset before
 | 
			
		||||
        if (!this.trayIcon) {
 | 
			
		||||
            this.useNotificationIcon = false;
 | 
			
		||||
            this.iconUpdated();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTitle: function(title) {
 | 
			
		||||
        // Do nothing if .app is set, we don't want to override the
 | 
			
		||||
        // app name with whatever is provided through libnotify (usually
 | 
			
		||||
@@ -774,9 +616,9 @@ const Source = new Lang.Class({
 | 
			
		||||
        this.parent(title);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(notification) {
 | 
			
		||||
        this.destroyNonResidentNotifications();
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this.openApp();
 | 
			
		||||
        this.destroyNotifications();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lastNotificationRemoved: function() {
 | 
			
		||||
@@ -788,11 +630,8 @@ const Source = new Lang.Class({
 | 
			
		||||
        if (this.app == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let windows = this.app.get_windows();
 | 
			
		||||
        if (windows.length > 0) {
 | 
			
		||||
            let mostRecentWindow = windows[0];
 | 
			
		||||
            Main.activateWindow(mostRecentWindow);
 | 
			
		||||
        }
 | 
			
		||||
        this.app.activate();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -819,3 +658,276 @@ const Source = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const GtkNotificationDaemonNotification = new Lang.Class({
 | 
			
		||||
    Name: 'GtkNotificationDaemonNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, notification) {
 | 
			
		||||
        this.parent(source);
 | 
			
		||||
        this._serialized = GLib.Variant.new('a{sv}', notification);
 | 
			
		||||
 | 
			
		||||
        let { "title": title,
 | 
			
		||||
              "body": body,
 | 
			
		||||
              "icon": gicon,
 | 
			
		||||
              "urgent": urgent,
 | 
			
		||||
              "buttons": buttons,
 | 
			
		||||
              "default-action": defaultAction,
 | 
			
		||||
              "default-action-target": defaultActionTarget } = notification;
 | 
			
		||||
 | 
			
		||||
        this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
 | 
			
		||||
                                        : MessageTray.Urgency.NORMAL);
 | 
			
		||||
 | 
			
		||||
        if (buttons) {
 | 
			
		||||
            buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
 | 
			
		||||
                this.addAction(button.label.unpack(),
 | 
			
		||||
                               Lang.bind(this, this._onButtonClicked, button));
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._defaultAction = defaultAction ? defaultAction.unpack() : null;
 | 
			
		||||
        this._defaultActionTarget = defaultActionTarget;
 | 
			
		||||
 | 
			
		||||
        this.update(title.unpack(), body ? body.unpack() : null,
 | 
			
		||||
                    { gicon: gicon ? Gio.icon_deserialize(gicon) : null });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activateAction: function(namespacedActionId, target) {
 | 
			
		||||
        if (namespacedActionId) {
 | 
			
		||||
            if (namespacedActionId.startsWith('app.')) {
 | 
			
		||||
                let actionId = namespacedActionId.slice('app.'.length);
 | 
			
		||||
                this.source.activateAction(actionId, target);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            this.source.open();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonClicked: function(button) {
 | 
			
		||||
        let { 'action': action, 'target': actionTarget } = button;
 | 
			
		||||
        this._activateAction(action.unpack(), actionTarget);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this._activateAction(this._defaultAction, this._defaultActionTarget);
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serialize: function() {
 | 
			
		||||
        return this._serialized;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const FdoApplicationIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.Application"> \
 | 
			
		||||
<method name="ActivateAction"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="av" direction="in" /> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Activate"> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
 | 
			
		||||
 | 
			
		||||
function objectPathFromAppId(appId) {
 | 
			
		||||
    return '/' + appId.replace(/\./g, '/');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getPlatformData() {
 | 
			
		||||
    let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
 | 
			
		||||
    return { "desktop-startup-id": startupId };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function InvalidAppError() {}
 | 
			
		||||
 | 
			
		||||
const GtkNotificationDaemonAppSource = new Lang.Class({
 | 
			
		||||
    Name: 'GtkNotificationDaemonAppSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
 | 
			
		||||
    _init: function(appId) {
 | 
			
		||||
        this._appId = appId;
 | 
			
		||||
        this._objectPath = objectPathFromAppId(appId);
 | 
			
		||||
 | 
			
		||||
        this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
 | 
			
		||||
        if (!this._app)
 | 
			
		||||
            throw new InvalidAppError();
 | 
			
		||||
 | 
			
		||||
        this._notifications = {};
 | 
			
		||||
 | 
			
		||||
        this.parent(this._app.get_name());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size) {
 | 
			
		||||
        return this._app.create_icon_texture(size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new MessageTray.NotificationApplicationPolicy(this._appId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createApp: function() {
 | 
			
		||||
        return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateAction: function(actionId, target) {
 | 
			
		||||
        let app = this._createApp();
 | 
			
		||||
        app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
        let app = this._createApp();
 | 
			
		||||
        app.ActivateRemote(getPlatformData());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addNotification: function(notificationId, notificationParams, showBanner) {
 | 
			
		||||
        if (this._notifications[notificationId])
 | 
			
		||||
            this._notifications[notificationId].destroy();
 | 
			
		||||
 | 
			
		||||
        let notification = new GtkNotificationDaemonNotification(this, notificationParams);
 | 
			
		||||
        notification.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            delete this._notifications[notificationId];
 | 
			
		||||
        }));
 | 
			
		||||
        this._notifications[notificationId] = notification;
 | 
			
		||||
 | 
			
		||||
        if (showBanner)
 | 
			
		||||
            this.notify(notification);
 | 
			
		||||
        else
 | 
			
		||||
            this.pushNotification(notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeNotification: function(notificationId) {
 | 
			
		||||
        if (this._notifications[notificationId])
 | 
			
		||||
            this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serialize: function() {
 | 
			
		||||
        let notifications = [];
 | 
			
		||||
        for (let notificationId in this._notifications) {
 | 
			
		||||
            let notification = this._notifications[notificationId];
 | 
			
		||||
            notifications.push([notificationId, notification.serialize()]);
 | 
			
		||||
        }
 | 
			
		||||
        return [this._appId, notifications];
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GtkNotificationsIface = '<node> \
 | 
			
		||||
<interface name="org.gtk.Notifications"> \
 | 
			
		||||
<method name="AddNotification"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="RemoveNotification"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const GtkNotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'GtkNotificationDaemon',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._sources = {};
 | 
			
		||||
 | 
			
		||||
        this._loadNotifications();
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
 | 
			
		||||
 | 
			
		||||
        Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureAppSource: function(appId) {
 | 
			
		||||
        if (this._sources[appId])
 | 
			
		||||
            return this._sources[appId];
 | 
			
		||||
 | 
			
		||||
        let source = new GtkNotificationDaemonAppSource(appId);
 | 
			
		||||
 | 
			
		||||
        source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            delete this._sources[appId];
 | 
			
		||||
            this._saveNotifications();
 | 
			
		||||
        }));
 | 
			
		||||
        source.connect('count-updated', Lang.bind(this, this._saveNotifications));
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
        this._sources[appId] = source;
 | 
			
		||||
        return source;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadNotifications: function() {
 | 
			
		||||
        this._isLoading = true;
 | 
			
		||||
 | 
			
		||||
        let value = global.get_persistent_state('a(sa(sv))', 'notifications');
 | 
			
		||||
        if (value) {
 | 
			
		||||
            let sources = value.deep_unpack();
 | 
			
		||||
            sources.forEach(Lang.bind(this, function([appId, notifications]) {
 | 
			
		||||
                if (notifications.length == 0)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                let source;
 | 
			
		||||
                try {
 | 
			
		||||
                    source = this._ensureAppSource(appId);
 | 
			
		||||
                } catch(e if e instanceof InvalidAppError) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                notifications.forEach(function([notificationId, notification]) {
 | 
			
		||||
                    source.addNotification(notificationId, notification.deep_unpack(), false);
 | 
			
		||||
                });
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isLoading = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _saveNotifications: function() {
 | 
			
		||||
        if (this._isLoading)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let sources = [];
 | 
			
		||||
        for (let appId in this._sources) {
 | 
			
		||||
            let source = this._sources[appId];
 | 
			
		||||
            sources.push(source.serialize());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    AddNotificationAsync: function(params, invocation) {
 | 
			
		||||
        let [appId, notificationId, notification] = params;
 | 
			
		||||
 | 
			
		||||
        let source;
 | 
			
		||||
        try {
 | 
			
		||||
            source = this._ensureAppSource(appId);
 | 
			
		||||
        } catch(e if e instanceof InvalidAppError) {
 | 
			
		||||
            invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        source.addNotification(notificationId, notification, true);
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    RemoveNotificationAsync: function(params, invocation) {
 | 
			
		||||
        let [appId, notificationId] = params;
 | 
			
		||||
        let source = this._sources[appId];
 | 
			
		||||
        if (source)
 | 
			
		||||
            source.removeNotification(notificationId);
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemon',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._fdoNotificationDaemon = new FdoNotificationDaemon();
 | 
			
		||||
        this._gtkNotificationDaemon = new GtkNotificationDaemon();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
@@ -65,19 +66,24 @@ const LevelBar = new Lang.Class({
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const OsdWindow = new Lang.Class({
 | 
			
		||||
    Name: 'OsdWindow',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
    _init: function(monitorIndex) {
 | 
			
		||||
        this._popupSize = 0;
 | 
			
		||||
        this.actor = new St.Widget({ x_expand: true,
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
 | 
			
		||||
 | 
			
		||||
        this._monitorIndex = monitorIndex;
 | 
			
		||||
        let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
 | 
			
		||||
        this.actor.add_constraint(constraint);
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'osd-window',
 | 
			
		||||
                                       vertical: true });
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
@@ -106,7 +112,6 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._monitorsChanged();
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_child(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -122,13 +127,15 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    setLevel: function(level) {
 | 
			
		||||
        this._level.actor.visible = (level != undefined);
 | 
			
		||||
        if (this.actor.visible)
 | 
			
		||||
            Tweener.addTween(this._level,
 | 
			
		||||
                             { level: level,
 | 
			
		||||
                               time: LEVEL_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
        else
 | 
			
		||||
            this._level.level = level;
 | 
			
		||||
        if (level != undefined) {
 | 
			
		||||
            if (this.actor.visible)
 | 
			
		||||
                Tweener.addTween(this._level,
 | 
			
		||||
                                 { level: level,
 | 
			
		||||
                                   time: LEVEL_ANIMATION_TIME,
 | 
			
		||||
                                   transition: 'easeOutQuad' });
 | 
			
		||||
            else
 | 
			
		||||
                this._level.level = level;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
@@ -151,6 +158,7 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
            Mainloop.source_remove(this._hideTimeoutId);
 | 
			
		||||
        this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
 | 
			
		||||
                                                   Lang.bind(this, this._hide));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
@@ -172,6 +180,7 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
                              Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
@@ -182,14 +191,18 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        /* assume 110x110 on a 640x480 display and scale from there */
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let monitor = Main.layoutManager.monitors[this._monitorIndex];
 | 
			
		||||
        if (!monitor)
 | 
			
		||||
            return; // we are about to be removed
 | 
			
		||||
 | 
			
		||||
        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 scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        this._icon.icon_size = this._popupSize / (2 * scaleFactor);
 | 
			
		||||
        this._box.translation_y = monitor.height / 4;
 | 
			
		||||
        this._icon.icon_size = this._popupSize / 2;
 | 
			
		||||
        this._box.style_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -205,6 +218,60 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
        let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
 | 
			
		||||
        let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
 | 
			
		||||
 | 
			
		||||
        this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
 | 
			
		||||
        // 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);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const OsdWindowManager = new Lang.Class({
 | 
			
		||||
    Name: 'OsdWindowManager',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._osdWindows = [];
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                    Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._monitorsChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
 | 
			
		||||
            if (this._osdWindows[i] == undefined)
 | 
			
		||||
                this._osdWindows[i] = new OsdWindow(i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
 | 
			
		||||
            this._osdWindows[i].actor.destroy();
 | 
			
		||||
            this._osdWindows[i] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._osdWindows.length = Main.layoutManager.monitors.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showOsdWindow: function(monitorIndex, icon, label, level) {
 | 
			
		||||
        this._osdWindows[monitorIndex].setIcon(icon);
 | 
			
		||||
        this._osdWindows[monitorIndex].setLabel(label);
 | 
			
		||||
        this._osdWindows[monitorIndex].setLevel(level);
 | 
			
		||||
        this._osdWindows[monitorIndex].show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function(monitorIndex, icon, label, level) {
 | 
			
		||||
        if (monitorIndex != -1) {
 | 
			
		||||
            for (let i = 0; i < this._osdWindows.length; i++) {
 | 
			
		||||
                if (i == monitorIndex)
 | 
			
		||||
                    this._showOsdWindow(i, icon, label, level);
 | 
			
		||||
                else
 | 
			
		||||
                    this._osdWindows[i].cancel();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            for (let i = 0; i < this._osdWindows.length; i++)
 | 
			
		||||
                this._showOsdWindow(i, icon, label, level);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideAll: function() {
 | 
			
		||||
        for (let i = 0; i < this._osdWindows.length; i++)
 | 
			
		||||
            this._osdWindows[i].cancel();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
@@ -13,6 +14,7 @@ const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const LayoutManager = imports.ui.layout;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const OverviewControls = imports.ui.overviewControls;
 | 
			
		||||
@@ -70,7 +72,6 @@ const ShellInfo = new Lang.Class({
 | 
			
		||||
        let notification = null;
 | 
			
		||||
        if (this._source.notifications.length == 0) {
 | 
			
		||||
            notification = new MessageTray.Notification(this._source, text, null);
 | 
			
		||||
            notification.setTransient(true);
 | 
			
		||||
            notification.setForFeedback(forFeedback);
 | 
			
		||||
        } else {
 | 
			
		||||
            notification = this._source.notifications[0];
 | 
			
		||||
@@ -78,10 +79,8 @@ const ShellInfo = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._undoCallback = undoCallback;
 | 
			
		||||
        if (undoCallback) {
 | 
			
		||||
            notification.addButton('system-undo', _("Undo"));
 | 
			
		||||
            notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
 | 
			
		||||
        }
 | 
			
		||||
        if (undoCallback)
 | 
			
		||||
            notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
 | 
			
		||||
 | 
			
		||||
        this._source.notify(notification);
 | 
			
		||||
    }
 | 
			
		||||
@@ -114,9 +113,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // rendering options without duplicating the texture data.
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        this._desktopFade = new St.Bin();
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        let layout = new Clutter.BinLayout();
 | 
			
		||||
        this._stack = new Clutter.Actor({ layout_manager: layout });
 | 
			
		||||
        this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
 | 
			
		||||
@@ -135,6 +131,9 @@ const Overview = new Lang.Class({
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
 | 
			
		||||
        this._desktopFade = new St.Widget();
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        this._activationTime = 0;
 | 
			
		||||
 | 
			
		||||
        this.visible = false;           // animating to overview, in overview, animating out
 | 
			
		||||
@@ -148,8 +147,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Dash elements, or mouseover handlers in the workspaces.
 | 
			
		||||
        this._coverPane = new Clutter.Actor({ opacity: 0,
 | 
			
		||||
                                              reactive: true });
 | 
			
		||||
        this._overview.add_actor(this._coverPane);
 | 
			
		||||
        this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
 | 
			
		||||
        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);
 | 
			
		||||
@@ -197,11 +196,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { brightness: 1.0,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { vignetteSharpness: 0.0,
 | 
			
		||||
                               vignetteSharpness: 0.0,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
@@ -214,12 +209,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
            let background = backgrounds[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { brightness: 0.8,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { vignetteSharpness: 0.7,
 | 
			
		||||
                             { brightness: Lightbox.VIGNETTE_BRIGHTNESS,
 | 
			
		||||
                               vignetteSharpness: Lightbox.VIGNETTE_SHARPNESS,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
@@ -265,7 +256,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Create controls
 | 
			
		||||
        this._controls = new OverviewControls.ControlsManager(this._searchEntry);
 | 
			
		||||
        this._dash = this._controls.dash;
 | 
			
		||||
        this._viewSelector = this._controls.viewSelector;
 | 
			
		||||
        this.viewSelector = this._controls.viewSelector;
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add(this._controls.actor, { y_fill: true, expand: true });
 | 
			
		||||
@@ -285,11 +276,11 @@ const Overview = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        this._viewSelector.addSearchProvider(provider);
 | 
			
		||||
        this.viewSelector.addSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeSearchProvider: function(provider) {
 | 
			
		||||
        this._viewSelector.removeSearchProvider(provider);
 | 
			
		||||
        this.viewSelector.removeSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
@@ -365,12 +356,15 @@ const Overview = new Lang.Class({
 | 
			
		||||
            this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
 | 
			
		||||
            this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this._windowSwitchTimeoutId = 0;
 | 
			
		||||
                                                this._needsFakePointerEvent = true;
 | 
			
		||||
                                                Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
 | 
			
		||||
                                                                    this._windowSwitchTimestamp);
 | 
			
		||||
                                                this.hide();
 | 
			
		||||
                                                this._lastHoveredWindow = null;
 | 
			
		||||
                                                return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                            }));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._windowSwitchTimeoutId, '[gnome-shell] Main.activateWindow');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
@@ -378,6 +372,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this.emit('scroll-event', event);
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addAction: function(action) {
 | 
			
		||||
@@ -445,17 +440,17 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beginWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-begin');
 | 
			
		||||
    beginWindowDrag: function(clone) {
 | 
			
		||||
        this.emit('window-drag-begin', clone);
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-cancelled');
 | 
			
		||||
    cancelledWindowDrag: function(clone) {
 | 
			
		||||
        this.emit('window-drag-cancelled', clone);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-end');
 | 
			
		||||
    endWindowDrag: function(clone) {
 | 
			
		||||
        this.emit('window-drag-end', clone);
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -491,8 +486,13 @@ const Overview = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeOutDesktop: function() {
 | 
			
		||||
        if (!this._desktopFade.child)
 | 
			
		||||
            this._desktopFade.child = this._getDesktopClone();
 | 
			
		||||
        if (!this._desktopFade.get_n_children()) {
 | 
			
		||||
            let clone = this._getDesktopClone();
 | 
			
		||||
            if (!clone)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            this._desktopFade.add_child(clone);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._desktopFade.opacity = 255;
 | 
			
		||||
        this._desktopFade.show();
 | 
			
		||||
@@ -512,17 +512,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this.visibleTarget = true;
 | 
			
		||||
        this._activationTime = Date.now() / 1000;
 | 
			
		||||
 | 
			
		||||
        // All the the actors in the window group are completely obscured,
 | 
			
		||||
        // hiding the group holding them while the Overview is displayed greatly
 | 
			
		||||
        // increases performance of the Overview especially when there are many
 | 
			
		||||
        // windows visible.
 | 
			
		||||
        //
 | 
			
		||||
        // If we switched to displaying the actors in the Overview rather than
 | 
			
		||||
        // clones of them, this would obviously no longer be necessary.
 | 
			
		||||
        //
 | 
			
		||||
        // Disable unredirection while in the overview
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
        this._viewSelector.show();
 | 
			
		||||
        this.viewSelector.show();
 | 
			
		||||
 | 
			
		||||
        this._stack.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
@@ -629,7 +620,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this.animationInProgress = true;
 | 
			
		||||
        this.visibleTarget = false;
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.zoomFromOverview();
 | 
			
		||||
        this.viewSelector.zoomFromOverview();
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
@@ -664,7 +655,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Re-enable unredirection
 | 
			
		||||
        Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.hide();
 | 
			
		||||
        this.viewSelector.hide();
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ const SlideLayout = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this._slideX = 1;
 | 
			
		||||
        this._translationX = 0;
 | 
			
		||||
        this._direction = SlideDirection.LEFT;
 | 
			
		||||
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
@@ -55,18 +56,21 @@ const SlideLayout = new Lang.Class({
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        let child = container.get_first_child();
 | 
			
		||||
 | 
			
		||||
        let [, , natWidth, natHeight] = child.get_preferred_size();
 | 
			
		||||
        let availWidth = Math.round(box.x2 - box.x1);
 | 
			
		||||
        let availHeight = Math.round(box.y2 - box.y1);
 | 
			
		||||
        let [, natWidth] = child.get_preferred_width(availHeight);
 | 
			
		||||
 | 
			
		||||
        // Align the actor inside the clipped box, as the actor's alignment
 | 
			
		||||
        // flags only determine what to do if the allocated box is bigger
 | 
			
		||||
        // than the actor's box.
 | 
			
		||||
        let realDirection = getRtlSlideDirection(this._direction, child);
 | 
			
		||||
        let translationX = (realDirection == SlideDirection.LEFT) ?
 | 
			
		||||
            (availWidth - natWidth) : (natWidth - availWidth);
 | 
			
		||||
        let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
 | 
			
		||||
 | 
			
		||||
        let actorBox = new Clutter.ActorBox({ x1: translationX,
 | 
			
		||||
                                              y1: 0,
 | 
			
		||||
                                              x2: child.x_expand ? availWidth : natWidth,
 | 
			
		||||
                                              y2: child.y_expand ? availHeight : natHeight });
 | 
			
		||||
        let actorBox = new Clutter.ActorBox();
 | 
			
		||||
        actorBox.x1 = box.x1 + alignX + this._translationX;
 | 
			
		||||
        actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
 | 
			
		||||
        actorBox.y1 = box.y1;
 | 
			
		||||
        actorBox.y2 = actorBox.y1 + availHeight;
 | 
			
		||||
 | 
			
		||||
        child.allocate(actorBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
@@ -87,7 +91,16 @@ const SlideLayout = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    get slideDirection() {
 | 
			
		||||
        return this._direction;
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set translationX(value) {
 | 
			
		||||
        this._translationX = value;
 | 
			
		||||
        this.layout_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get translationX() {
 | 
			
		||||
        return this._translationX;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SlidingControl = new Lang.Class({
 | 
			
		||||
@@ -96,8 +109,8 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
        this._visible = true;
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
 | 
			
		||||
        this.layout = new SlideLayout();
 | 
			
		||||
        this.layout.slideDirection = params.slideDirection;
 | 
			
		||||
@@ -106,6 +119,7 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
                                     clip_to_allocation: true });
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
@@ -116,12 +130,12 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
        Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
    _getSlide: function() {
 | 
			
		||||
        throw new Error('getSlide() must be overridden');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSlide: function() {
 | 
			
		||||
        Tweener.addTween(this.layout, { slideX: this.getSlide(),
 | 
			
		||||
    _updateSlide: function() {
 | 
			
		||||
        Tweener.addTween(this.layout, { slideX: this._getSlide(),
 | 
			
		||||
                                        time: SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                                        transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
@@ -148,28 +162,30 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
        let translationEnd = 0;
 | 
			
		||||
        let translation = this._getTranslation();
 | 
			
		||||
 | 
			
		||||
        if (this.visible) {
 | 
			
		||||
        if (this._visible) {
 | 
			
		||||
            translationStart = translation;
 | 
			
		||||
        } else {
 | 
			
		||||
            translationEnd = translation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.actor.translation_x == translationEnd)
 | 
			
		||||
        if (this.layout.translationX == translationEnd)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.translation_x = translationStart;
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: translationEnd,
 | 
			
		||||
                                       time: SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                                       transition: 'easeOutQuad'
 | 
			
		||||
                                     });
 | 
			
		||||
        this.layout.translationX = translationStart;
 | 
			
		||||
        Tweener.addTween(this.layout, { translationX: translationEnd,
 | 
			
		||||
                                        time: SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                                        transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = 0;
 | 
			
		||||
        this._visible = true;
 | 
			
		||||
        this.layout.slideX = this._getSlide();
 | 
			
		||||
        this.layout.translationX = this._getTranslation();
 | 
			
		||||
        this.slideIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewHiding: function() {
 | 
			
		||||
        this.slideOut();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragBegin: function() {
 | 
			
		||||
@@ -181,14 +197,14 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this.inDrag = true;
 | 
			
		||||
        this.actor.translation_x = 0;
 | 
			
		||||
        this.updateSlide();
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
        this.layout.translationX = 0;
 | 
			
		||||
        this._updateSlide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragEnd: function() {
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
        this.updateSlide();
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._updateSlide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeIn: function() {
 | 
			
		||||
@@ -206,12 +222,13 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    slideIn: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this._visible = true;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
        // we will update slideX and the translation from pageEmpty
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    slideOut: function() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this._visible = false;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
        // we will update slideX from pageEmpty
 | 
			
		||||
    },
 | 
			
		||||
@@ -221,7 +238,7 @@ const SlidingControl = new Lang.Class({
 | 
			
		||||
        // selector; this means we can now safely set the full slide for
 | 
			
		||||
        // the next page, since slideIn or slideOut might have been called,
 | 
			
		||||
        // changing the visiblity
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.layout.slideX = this._getSlide();
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -235,25 +252,25 @@ const ThumbnailsSlider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = thumbnailsBox;
 | 
			
		||||
 | 
			
		||||
        // SlideLayout reads the actor's expand flags to decide
 | 
			
		||||
        // whether to allocate the natural size to its child, or the whole
 | 
			
		||||
        // available allocation
 | 
			
		||||
        this._thumbnailsBox.actor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
        this.actor.track_hover = true;
 | 
			
		||||
        this.actor.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide));
 | 
			
		||||
        global.window_manager.connect('switch-workspace', Lang.bind(this, this._updateSlide));
 | 
			
		||||
        this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getAlwaysZoomOut: function() {
 | 
			
		||||
        // Always show the pager when hover, during a drag, or if workspaces are
 | 
			
		||||
        // actually used, e.g. there are windows on more than one
 | 
			
		||||
        let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
 | 
			
		||||
        // actually used, e.g. there are windows on any non-active workspace
 | 
			
		||||
        let alwaysZoomOut = this.actor.hover ||
 | 
			
		||||
                            this._inDrag ||
 | 
			
		||||
                            !Meta.prefs_get_dynamic_workspaces() ||
 | 
			
		||||
                            global.screen.n_workspaces > 2 ||
 | 
			
		||||
                            global.screen.get_active_workspace_index() != 0;
 | 
			
		||||
 | 
			
		||||
        if (!alwaysZoomOut) {
 | 
			
		||||
            let monitors = Main.layoutManager.monitors;
 | 
			
		||||
@@ -278,8 +295,8 @@ const ThumbnailsSlider = new Lang.Class({
 | 
			
		||||
        return child.get_theme_node().get_length('visible-width');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        if (!this.visible)
 | 
			
		||||
    _getSlide: function() {
 | 
			
		||||
        if (!this._visible)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let alwaysZoomOut = this._getAlwaysZoomOut();
 | 
			
		||||
@@ -315,18 +332,18 @@ const DashSlider = new Lang.Class({
 | 
			
		||||
        // whether to allocate the natural size to its child, or the whole
 | 
			
		||||
        // available allocation
 | 
			
		||||
        this._dash.actor.x_expand = true;
 | 
			
		||||
        this._dash.actor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.x_expand = true;
 | 
			
		||||
        this.actor.x_align = Clutter.ActorAlign.START;
 | 
			
		||||
        this.actor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._dash.actor);
 | 
			
		||||
 | 
			
		||||
        this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        if (this.visible || this.inDrag)
 | 
			
		||||
    _getSlide: function() {
 | 
			
		||||
        if (this._visible || this._inDrag)
 | 
			
		||||
            return 1;
 | 
			
		||||
        else
 | 
			
		||||
            return 0;
 | 
			
		||||
@@ -448,9 +465,6 @@ const MessagesIndicator = new Lang.Class({
 | 
			
		||||
        if (source.trayIcon)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (source.isTransient)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        source.connect('count-updated', Lang.bind(this, this._updateCount));
 | 
			
		||||
        this._sources.push(source);
 | 
			
		||||
        this._updateCount();
 | 
			
		||||
@@ -487,6 +501,17 @@ const MessagesIndicator = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsLayout = new Lang.Class({
 | 
			
		||||
    Name: 'ControlsLayout',
 | 
			
		||||
    Extends: Clutter.BinLayout,
 | 
			
		||||
    Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        this.parent(container, box, flags);
 | 
			
		||||
        this.emit('allocation-changed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsManager = new Lang.Class({
 | 
			
		||||
    Name: 'ControlsManager',
 | 
			
		||||
 | 
			
		||||
@@ -507,7 +532,8 @@ const ControlsManager = new Lang.Class({
 | 
			
		||||
        this._indicator = new MessagesIndicator(this.viewSelector);
 | 
			
		||||
        this.indicatorActor = this._indicator.actor;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
        let layout = new ControlsLayout();
 | 
			
		||||
        this.actor = new St.Widget({ layout_manager: layout,
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_expand: true, y_expand: true,
 | 
			
		||||
                                     clip_to_allocation: true });
 | 
			
		||||
@@ -522,7 +548,7 @@ const ControlsManager = new Lang.Class({
 | 
			
		||||
                                                   expand: true });
 | 
			
		||||
        this._group.add_actor(this._thumbnailsSlider.actor);
 | 
			
		||||
 | 
			
		||||
        this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
        layout.connect('allocation-changed', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
 | 
			
		||||
        Main.overview.connect('item-drag-begin', Lang.bind(this,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										350
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						@@ -189,7 +189,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.bind_property("reactive", this.actor, "can-focus", 0);
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
        this._targetIsCurrent = false;
 | 
			
		||||
 | 
			
		||||
        this._container = new Shell.GenericContainer();
 | 
			
		||||
        bin.set_child(this._container);
 | 
			
		||||
@@ -207,20 +206,23 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._iconBox.connect('notify::allocation',
 | 
			
		||||
                              Lang.bind(this, this._updateIconBoxClip));
 | 
			
		||||
        this._container.add_actor(this._iconBox);
 | 
			
		||||
 | 
			
		||||
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
 | 
			
		||||
        this._container.add_actor(this._hbox);
 | 
			
		||||
 | 
			
		||||
        this._label = new TextShadower();
 | 
			
		||||
        this._container.add_actor(this._label.actor);
 | 
			
		||||
        this._label.actor.y_align = Clutter.ActorAlign.CENTER;
 | 
			
		||||
        this._hbox.add_actor(this._label.actor);
 | 
			
		||||
        this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
 | 
			
		||||
        this._hbox.add_actor(this._arrow);
 | 
			
		||||
 | 
			
		||||
        this._iconBottomClip = 0;
 | 
			
		||||
 | 
			
		||||
        this._visible = !Main.overview.visible;
 | 
			
		||||
        if (!this._visible)
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
        this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
            this.show();
 | 
			
		||||
        }));
 | 
			
		||||
        this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
 | 
			
		||||
            this.hide();
 | 
			
		||||
        }));
 | 
			
		||||
        this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync));
 | 
			
		||||
        this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        this._stop = true;
 | 
			
		||||
 | 
			
		||||
@@ -243,13 +245,8 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._visible = true;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        if (!this._targetIsCurrent)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
@@ -263,11 +260,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._visible = false;
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
        if (!this._targetIsCurrent) {
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
@@ -286,9 +278,8 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        this._spinnerIcon = icon;
 | 
			
		||||
        this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
 | 
			
		||||
        this._container.add_actor(this._spinner.actor);
 | 
			
		||||
        this._hbox.add_actor(this._spinner.actor);
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
        this._spinner.actor.lower_bottom();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconBoxStyleChanged: function() {
 | 
			
		||||
@@ -297,13 +288,19 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._updateIconBoxClip();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncIcon: function() {
 | 
			
		||||
        if (!this._targetApp)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconThemeChanged: function() {
 | 
			
		||||
        if (this._iconBox.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._iconBox.child.destroy();
 | 
			
		||||
        let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIconBoxClip: function() {
 | 
			
		||||
@@ -321,7 +318,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._stop = true;
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -341,7 +337,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    startAnimation: function() {
 | 
			
		||||
        this._stop = false;
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -354,7 +349,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        [minSize, naturalSize] = this._label.actor.get_preferred_width(forHeight);
 | 
			
		||||
        [minSize, naturalSize] = this._hbox.get_preferred_width(forHeight);
 | 
			
		||||
        alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
 | 
			
		||||
        alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
 | 
			
		||||
    },
 | 
			
		||||
@@ -363,7 +358,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        [minSize, naturalSize] = this._label.actor.get_preferred_height(forWidth);
 | 
			
		||||
        [minSize, naturalSize] = this._hbox.get_preferred_height(forWidth);
 | 
			
		||||
        if (minSize > alloc.min_size)
 | 
			
		||||
            alloc.min_size = minSize;
 | 
			
		||||
        if (naturalSize > alloc.natural_size)
 | 
			
		||||
@@ -393,11 +388,10 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let iconWidth = childBox.x2 - childBox.x1;
 | 
			
		||||
 | 
			
		||||
        [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.actor.get_preferred_size();
 | 
			
		||||
        [minWidth, naturalWidth] = this._hbox.get_preferred_width(-1);
 | 
			
		||||
 | 
			
		||||
        yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2);
 | 
			
		||||
        childBox.y1 = yPadding;
 | 
			
		||||
        childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight);
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.TextDirection.LTR) {
 | 
			
		||||
            childBox.x1 = Math.floor(iconWidth / 2);
 | 
			
		||||
@@ -406,24 +400,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
 | 
			
		||||
            childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
 | 
			
		||||
        }
 | 
			
		||||
        this._label.actor.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.TextDirection.LTR) {
 | 
			
		||||
            childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._spinner.actor.width;
 | 
			
		||||
            childBox.y1 = box.y1;
 | 
			
		||||
            childBox.y2 = box.y2 - 1;
 | 
			
		||||
            this._spinner.actor.allocate(childBox, flags);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = -this._spinner.actor.width;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._spinner.actor.width;
 | 
			
		||||
            childBox.y1 = box.y1;
 | 
			
		||||
            childBox.y2 = box.y2 - 1;
 | 
			
		||||
            this._spinner.actor.allocate(childBox, flags);
 | 
			
		||||
        }
 | 
			
		||||
        this._hbox.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAppStateChanged: function(appSys, app) {
 | 
			
		||||
@@ -449,104 +426,76 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            // If the app has just lost focus to the panel, pretend
 | 
			
		||||
            // nothing happened; otherwise you can't keynav to the
 | 
			
		||||
            // app menu.
 | 
			
		||||
            if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
 | 
			
		||||
            if (global.stage.key_focus != null)
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
    _findTargetApp: function() {
 | 
			
		||||
        let workspace = global.screen.get_active_workspace();
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let focusedApp = tracker.focus_app;
 | 
			
		||||
        let lastStartedApp = null;
 | 
			
		||||
        let workspace = global.screen.get_active_workspace();
 | 
			
		||||
        if (focusedApp && focusedApp.is_on_workspace(workspace))
 | 
			
		||||
            return focusedApp;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._startingApps.length; i++)
 | 
			
		||||
            if (this._startingApps[i].is_on_workspace(workspace))
 | 
			
		||||
                lastStartedApp = this._startingApps[i];
 | 
			
		||||
                return this._startingApps[i];
 | 
			
		||||
 | 
			
		||||
        let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        if (targetApp == null) {
 | 
			
		||||
            if (!this._targetIsCurrent)
 | 
			
		||||
                return;
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let targetApp = this._findTargetApp();
 | 
			
		||||
 | 
			
		||||
            this.actor.reactive = false;
 | 
			
		||||
            this._targetIsCurrent = false;
 | 
			
		||||
 | 
			
		||||
            Tweener.removeTweens(this.actor);
 | 
			
		||||
            Tweener.addTween(this.actor, { opacity: 0,
 | 
			
		||||
                                           time: Overview.ANIMATION_TIME,
 | 
			
		||||
                                           transition: 'easeOutQuad' });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!targetApp.is_on_workspace(workspace))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._targetIsCurrent) {
 | 
			
		||||
            this.actor.reactive = true;
 | 
			
		||||
            this._targetIsCurrent = true;
 | 
			
		||||
 | 
			
		||||
            Tweener.removeTweens(this.actor);
 | 
			
		||||
            Tweener.addTween(this.actor, { opacity: 255,
 | 
			
		||||
                                           time: Overview.ANIMATION_TIME,
 | 
			
		||||
                                           transition: 'easeOutQuad' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (targetApp == this._targetApp) {
 | 
			
		||||
            if (targetApp &&
 | 
			
		||||
                targetApp.get_state() != Shell.AppState.STARTING &&
 | 
			
		||||
                targetApp.get_state() != Shell.AppState.BUSY) {
 | 
			
		||||
                this.stopAnimation();
 | 
			
		||||
                this._maybeSetMenu();
 | 
			
		||||
            } else if (targetApp &&
 | 
			
		||||
                       targetApp.get_state() == Shell.AppState.BUSY) {
 | 
			
		||||
                this.startAnimation();
 | 
			
		||||
        if (this._targetApp != targetApp) {
 | 
			
		||||
            if (this._appMenuNotifyId) {
 | 
			
		||||
                this._targetApp.disconnect(this._appMenuNotifyId);
 | 
			
		||||
                this._appMenuNotifyId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this._actionGroupNotifyId) {
 | 
			
		||||
                this._targetApp.disconnect(this._actionGroupNotifyId);
 | 
			
		||||
                this._actionGroupNotifyId = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._targetApp = targetApp;
 | 
			
		||||
 | 
			
		||||
            if (this._targetApp) {
 | 
			
		||||
                this._appMenuNotifyId = this._targetApp.connect('notify::menu', Lang.bind(this, this._sync));
 | 
			
		||||
                this._actionGroupNotifyId = this._targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
 | 
			
		||||
                this._label.setText(this._targetApp.get_name());
 | 
			
		||||
                this.actor.set_accessible_name(this._targetApp.get_name());
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._spinner)
 | 
			
		||||
            this._spinner.actor.hide();
 | 
			
		||||
        if (this._iconBox.child != null)
 | 
			
		||||
            this._iconBox.child.destroy();
 | 
			
		||||
        this._iconBox.hide();
 | 
			
		||||
        this._label.setText('');
 | 
			
		||||
        let visible = (this._targetApp != null && !Main.overview.visibleTarget);
 | 
			
		||||
        if (visible)
 | 
			
		||||
            this.show();
 | 
			
		||||
        else
 | 
			
		||||
            this.hide();
 | 
			
		||||
 | 
			
		||||
        if (this._appMenuNotifyId)
 | 
			
		||||
            this._targetApp.disconnect(this._appMenuNotifyId);
 | 
			
		||||
        if (this._actionGroupNotifyId)
 | 
			
		||||
            this._targetApp.disconnect(this._actionGroupNotifyId);
 | 
			
		||||
        if (targetApp) {
 | 
			
		||||
            this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
 | 
			
		||||
            this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._appMenuNotifyId = 0;
 | 
			
		||||
            this._actionGroupNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._targetApp = targetApp;
 | 
			
		||||
        let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        this._label.setText(targetApp.get_name());
 | 
			
		||||
        this.setName(targetApp.get_name());
 | 
			
		||||
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
        this._iconBox.show();
 | 
			
		||||
 | 
			
		||||
        if (targetApp.get_state() == Shell.AppState.STARTING ||
 | 
			
		||||
            targetApp.get_state() == Shell.AppState.BUSY)
 | 
			
		||||
        let isBusy = (this._targetApp != null &&
 | 
			
		||||
                      (this._targetApp.get_state() == Shell.AppState.STARTING ||
 | 
			
		||||
                       this._targetApp.get_state() == Shell.AppState.BUSY));
 | 
			
		||||
        if (isBusy)
 | 
			
		||||
            this.startAnimation();
 | 
			
		||||
        else
 | 
			
		||||
            this._maybeSetMenu();
 | 
			
		||||
            this.stopAnimation();
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = (visible && !isBusy);
 | 
			
		||||
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
        this._maybeSetMenu();
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetMenu: function() {
 | 
			
		||||
        let menu;
 | 
			
		||||
 | 
			
		||||
        if (this._targetApp.action_group && this._targetApp.menu) {
 | 
			
		||||
        if (this._targetApp == null) {
 | 
			
		||||
            menu = null;
 | 
			
		||||
        } else if (this._targetApp.action_group && this._targetApp.menu) {
 | 
			
		||||
            if (this.menu instanceof RemoteMenu.RemoteMenu &&
 | 
			
		||||
                this.menu.actionGroup == this._targetApp.action_group)
 | 
			
		||||
                return;
 | 
			
		||||
@@ -558,7 +507,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this.menu.isDummyQuitMenu)
 | 
			
		||||
            if (this.menu && this.menu.isDummyQuitMenu)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            // fallback to older menu
 | 
			
		||||
@@ -570,7 +519,8 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setMenu(menu);
 | 
			
		||||
        this._menuManager.addMenu(menu);
 | 
			
		||||
        if (menu)
 | 
			
		||||
            this._menuManager.addMenu(menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -615,7 +565,8 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        /* Translators: If there is no suitable word for "Activities"
 | 
			
		||||
           in your language, you can use the word for "Overview". */
 | 
			
		||||
        this._label = new St.Label({ text: _("Activities") });
 | 
			
		||||
        this._label = new St.Label({ text: _("Activities"),
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_actor(this._label);
 | 
			
		||||
 | 
			
		||||
        this.actor.label_actor = this._label;
 | 
			
		||||
@@ -644,6 +595,7 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
            Mainloop.source_remove(this._xdndTimeOut);
 | 
			
		||||
        this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
 | 
			
		||||
                                                 Lang.bind(this, this._xdndToggleOverview, actor));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._xdndTimeOut, '[gnome-shell] this._xdndToggleOverview');
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
@@ -651,13 +603,15 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
 | 
			
		||||
            if (!Main.overview.shouldToggleByCornerOrButton())
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonRelease: function() {
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyRelease: function(actor, event) {
 | 
			
		||||
@@ -665,6 +619,7 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
        if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
 | 
			
		||||
            Main.overview.toggle();
 | 
			
		||||
        }
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _xdndToggleOverview: function(actor) {
 | 
			
		||||
@@ -676,6 +631,7 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        Mainloop.source_remove(this._xdndTimeOut);
 | 
			
		||||
        this._xdndTimeOut = 0;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -846,31 +802,121 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AggregateMenu = new Lang.Class({
 | 
			
		||||
    Name: 'AggregateMenu',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(0.0, _("Settings"), false);
 | 
			
		||||
        this.menu.actor.add_style_class_name('aggregate-menu');
 | 
			
		||||
 | 
			
		||||
        this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
 | 
			
		||||
        this.actor.add_child(this._indicators);
 | 
			
		||||
 | 
			
		||||
        if (Config.HAVE_NETWORKMANAGER) {
 | 
			
		||||
            this._network = new imports.ui.status.network.NMApplet();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._network = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (Config.HAVE_BLUETOOTH) {
 | 
			
		||||
            this._bluetooth = new imports.ui.status.bluetooth.Indicator();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._bluetooth = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._power = new imports.ui.status.power.Indicator();
 | 
			
		||||
        this._rfkill = new imports.ui.status.rfkill.Indicator();
 | 
			
		||||
        this._volume = new imports.ui.status.volume.Indicator();
 | 
			
		||||
        this._brightness = new imports.ui.status.brightness.Indicator();
 | 
			
		||||
        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._indicators.add_child(this._screencast.indicators);
 | 
			
		||||
        this._indicators.add_child(this._location.indicators);
 | 
			
		||||
        if (this._network) {
 | 
			
		||||
            this._indicators.add_child(this._network.indicators);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._bluetooth) {
 | 
			
		||||
            this._indicators.add_child(this._bluetooth.indicators);
 | 
			
		||||
        }
 | 
			
		||||
        this._indicators.add_child(this._rfkill.indicators);
 | 
			
		||||
        this._indicators.add_child(this._volume.indicators);
 | 
			
		||||
        this._indicators.add_child(this._power.indicators);
 | 
			
		||||
        this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(this._volume.menu);
 | 
			
		||||
        this.menu.addMenuItem(this._brightness.menu);
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        if (this._network) {
 | 
			
		||||
            this.menu.addMenuItem(this._network.menu);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._bluetooth) {
 | 
			
		||||
            this.menu.addMenuItem(this._bluetooth.menu);
 | 
			
		||||
        }
 | 
			
		||||
        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._system.menu);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const DateMenuButton2 = new Lang.Class({
 | 
			
		||||
    Name: 'DateMenuButton2',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.container = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
 | 
			
		||||
        this._notificationPreview = Main.messageTray.notificationPreview;
 | 
			
		||||
        this.container.add_child(this._notificationPreview.actor);
 | 
			
		||||
        this._notificationPreview.actor.x_expand = true;
 | 
			
		||||
        this._notificationPreview.actor.x_align = Clutter.ActorAlign.CENTER;
 | 
			
		||||
        this._notificationPreview.connect('updated', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        let dateMenu = new imports.ui.dateMenu.DateMenuButton();
 | 
			
		||||
        this._clock = dateMenu.container;
 | 
			
		||||
        this._clock.x_expand = true;
 | 
			
		||||
        this._clock.x_align = Clutter.ActorAlign.CENTER;
 | 
			
		||||
        this.container.add_child(this._clock);
 | 
			
		||||
 | 
			
		||||
        this._currentlyShowing = 'clock';
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _show: function(which, animate) {
 | 
			
		||||
        if (this._currentlyShowing == which)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._currentlyShowing = which;
 | 
			
		||||
        if (this._currentlyShowing == 'clock') {
 | 
			
		||||
            this._notificationPreview.actor.visible = false;
 | 
			
		||||
            this._clock.visible = true;
 | 
			
		||||
        } else if (this._currentlyShowing == 'notification') {
 | 
			
		||||
            this._notificationPreview.actor.visible = true;
 | 
			
		||||
            this._clock.visible = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        if (this._currentlyShowing == 'clock' && this._notificationPreview.hasNotification)
 | 
			
		||||
            this._show('notification', true);
 | 
			
		||||
        else if (this._currentlyShowing == 'notification' && !this._notificationPreview.hasNotification)
 | 
			
		||||
            this._show('clock', false);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(DateMenuButton2.prototype);
 | 
			
		||||
 | 
			
		||||
const PANEL_ITEM_IMPLEMENTATIONS = {
 | 
			
		||||
    'activities': ActivitiesButton,
 | 
			
		||||
    'aggregateMenu': AggregateMenu,
 | 
			
		||||
    'appMenu': AppMenuButton,
 | 
			
		||||
    'dateMenu': imports.ui.dateMenu.DateMenuButton,
 | 
			
		||||
    'dateMenu': DateMenuButton2,
 | 
			
		||||
    'a11y': imports.ui.status.accessibility.ATIndicator,
 | 
			
		||||
    'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
 | 
			
		||||
    'volume': imports.ui.status.volume.Indicator,
 | 
			
		||||
    'battery': imports.ui.status.power.Indicator,
 | 
			
		||||
    'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
 | 
			
		||||
    'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
 | 
			
		||||
    'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
 | 
			
		||||
    'userMenu': imports.ui.userMenu.UserMenuButton
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (Config.HAVE_BLUETOOTH)
 | 
			
		||||
    PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
 | 
			
		||||
        imports.ui.status.bluetooth.Indicator;
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    PANEL_ITEM_IMPLEMENTATIONS['network'] =
 | 
			
		||||
        imports.ui.status.network.NMApplet;
 | 
			
		||||
} catch(e) {
 | 
			
		||||
    log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Panel = new Lang.Class({
 | 
			
		||||
    Name: 'Panel',
 | 
			
		||||
 | 
			
		||||
@@ -997,23 +1043,23 @@ const Panel = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (Main.modalCount > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_source() != actor)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let button = event.get_button();
 | 
			
		||||
        if (button != 1)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let focusWindow = global.display.focus_window;
 | 
			
		||||
        if (!focusWindow)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
 | 
			
		||||
                                                          : focusWindow;
 | 
			
		||||
        if (!dragWindow)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let rect = dragWindow.get_outer_rect();
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
@@ -1022,7 +1068,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
                        stageX > rect.x && stageX < rect.x + rect.width;
 | 
			
		||||
 | 
			
		||||
        if (!allowDrag)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        global.display.begin_grab_op(global.screen,
 | 
			
		||||
                                     dragWindow,
 | 
			
		||||
@@ -1034,7 +1080,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
                                     event.get_time(),
 | 
			
		||||
                                     stageX, stageY);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggleAppMenu: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,7 @@ const ButtonBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
        let child = children.length > 0 ? children[0] : null;
 | 
			
		||||
        let child = actor.get_first_child();
 | 
			
		||||
 | 
			
		||||
        if (child) {
 | 
			
		||||
            [alloc.min_size, alloc.natural_size] = child.get_preferred_width(-1);
 | 
			
		||||
@@ -55,8 +54,7 @@ const ButtonBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
        let child = children.length > 0 ? children[0] : null;
 | 
			
		||||
        let child = actor.get_first_child();
 | 
			
		||||
 | 
			
		||||
        if (child) {
 | 
			
		||||
            [alloc.min_size, alloc.natural_size] = child.get_preferred_height(-1);
 | 
			
		||||
@@ -66,13 +64,11 @@ const ButtonBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
        if (children.length == 0)
 | 
			
		||||
        let child = actor.get_first_child();
 | 
			
		||||
        if (!child)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let child = children[0];
 | 
			
		||||
        let [minWidth, natWidth] = child.get_preferred_width(-1);
 | 
			
		||||
        let [minHeight, natHeight] = child.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
@@ -86,13 +82,8 @@ const ButtonBox = new Lang.Class({
 | 
			
		||||
            childBox.x2 = availWidth - this._minHPadding;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (natHeight <= availHeight) {
 | 
			
		||||
            childBox.y1 = Math.floor((availHeight - natHeight) / 2);
 | 
			
		||||
            childBox.y2 = childBox.y1 + natHeight;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.y1 = 0;
 | 
			
		||||
            childBox.y2 = availHeight;
 | 
			
		||||
        }
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = availHeight;
 | 
			
		||||
 | 
			
		||||
        child.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
@@ -106,17 +97,17 @@ const Button = new Lang.Class({
 | 
			
		||||
        this.parent({ reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      track_hover: true,
 | 
			
		||||
                      accessible_name: nameText ? nameText : "",
 | 
			
		||||
                      accessible_role: Atk.Role.MENU });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
 | 
			
		||||
        this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
 | 
			
		||||
 | 
			
		||||
        if (dontCreateMenu)
 | 
			
		||||
            this.menu = new PopupMenu.PopupDummyMenu(this.actor);
 | 
			
		||||
        else
 | 
			
		||||
            this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
 | 
			
		||||
 | 
			
		||||
        this.setName(nameText);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSensitive: function(sensitive) {
 | 
			
		||||
@@ -125,22 +116,6 @@ const Button = new Lang.Class({
 | 
			
		||||
        this.actor.track_hover = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setName: function(text) {
 | 
			
		||||
        if (text != null) {
 | 
			
		||||
            // This is the easiest way to provide a accessible name to
 | 
			
		||||
            // this widget. The label could be also used for other
 | 
			
		||||
            // purposes in the future.
 | 
			
		||||
            if (!this.label) {
 | 
			
		||||
                this.label = new St.Label({ text: text });
 | 
			
		||||
                this.actor.label_actor = this.label;
 | 
			
		||||
            } else
 | 
			
		||||
                this.label.text = text;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.label = null;
 | 
			
		||||
            this.actor.label_actor = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMenu: function(menu) {
 | 
			
		||||
        if (this.menu)
 | 
			
		||||
            this.menu.destroy();
 | 
			
		||||
@@ -158,34 +133,43 @@ const Button = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (!this.menu)
 | 
			
		||||
            return;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this.menu.toggle();
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSourceKeyPress: function(actor, event) {
 | 
			
		||||
        if (!this.menu)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
 | 
			
		||||
            this.menu.toggle();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
 | 
			
		||||
            this.menu.close();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Down) {
 | 
			
		||||
            if (!this.menu.isOpen)
 | 
			
		||||
                this.menu.toggle();
 | 
			
		||||
            this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVisibilityChanged: function() {
 | 
			
		||||
        if (!this.menu)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this.actor.visible)
 | 
			
		||||
            this.menu.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuKeyPress: function(actor, event) {
 | 
			
		||||
        if (global.focus_manager.navigate_from_event(event))
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
 | 
			
		||||
@@ -193,10 +177,10 @@ const Button = new Lang.Class({
 | 
			
		||||
            if (group) {
 | 
			
		||||
                let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
 | 
			
		||||
                group.navigate_focus(this.actor, direction, false);
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenStateChanged: function(menu, open) {
 | 
			
		||||
@@ -224,51 +208,35 @@ const Button = new Lang.Class({
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Button.prototype);
 | 
			
		||||
 | 
			
		||||
/* SystemStatusButton:
 | 
			
		||||
/* SystemIndicator:
 | 
			
		||||
 *
 | 
			
		||||
 * This class manages one System Status indicator (network, keyboard,
 | 
			
		||||
 * volume, bluetooth...), which is just a PanelMenuButton with an
 | 
			
		||||
 * icon.
 | 
			
		||||
 * This class manages one system indicator, which are the icons
 | 
			
		||||
 * that you see at the top right. A system indicator is composed
 | 
			
		||||
 * of an icon and a menu section, which will be composed into the
 | 
			
		||||
 * aggregate menu.
 | 
			
		||||
 */
 | 
			
		||||
const SystemStatusButton = new Lang.Class({
 | 
			
		||||
    Name: 'SystemStatusButton',
 | 
			
		||||
    Extends: Button,
 | 
			
		||||
const SystemIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'SystemIndicator',
 | 
			
		||||
 | 
			
		||||
    _init: function(iconName, nameText) {
 | 
			
		||||
        this.parent(0.0, nameText);
 | 
			
		||||
        this.actor.add_style_class_name('panel-status-button');
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        if (iconName)
 | 
			
		||||
            this.setIcon(iconName);
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
 | 
			
		||||
                                             reactive: true });
 | 
			
		||||
        this.indicators.hide();
 | 
			
		||||
        this.menu = new PopupMenu.PopupMenuSection();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get icons() {
 | 
			
		||||
        return this._box.get_children();
 | 
			
		||||
    _syncIndicatorsVisible: function() {
 | 
			
		||||
        this.indicators.visible = this.indicators.get_children().some(function(actor) {
 | 
			
		||||
            return actor.visible;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addIcon: function(gicon) {
 | 
			
		||||
        let icon = new St.Icon({ gicon: gicon,
 | 
			
		||||
                                 style_class: 'system-status-icon' });
 | 
			
		||||
        this._box.add_actor(icon);
 | 
			
		||||
 | 
			
		||||
        this.emit('icons-changed');
 | 
			
		||||
 | 
			
		||||
    _addIndicator: function() {
 | 
			
		||||
        let icon = new St.Icon({ style_class: 'system-status-icon' });
 | 
			
		||||
        this.indicators.add_actor(icon);
 | 
			
		||||
        icon.connect('notify::visible', Lang.bind(this, this._syncIndicatorsVisible));
 | 
			
		||||
        this._syncIndicatorsVisible();
 | 
			
		||||
        return icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setIcon: function(iconName) {
 | 
			
		||||
        if (!this.mainIcon)
 | 
			
		||||
            this.mainIcon = this.addIcon(null);
 | 
			
		||||
        this.mainIcon.icon_name = iconName;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setGIcon: function(gicon) {
 | 
			
		||||
        if (this.mainIcon)
 | 
			
		||||
            this.mainIcon.gicon = gicon;
 | 
			
		||||
        else
 | 
			
		||||
            this.mainIcon = this.addIcon(gicon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SystemIndicator.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const GnomeDesktop = imports.gi.GnomeDesktop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +43,7 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
    Name: 'PointerWatcher',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._idleMonitor = new GnomeDesktop.IdleMonitor();
 | 
			
		||||
        this._idleMonitor = Meta.IdleMonitor.get_core();
 | 
			
		||||
        this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
 | 
			
		||||
        this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
 | 
			
		||||
        this._watches = [];
 | 
			
		||||
@@ -105,11 +107,12 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._timeoutId = Mainloop.timeout_add(minInterval,
 | 
			
		||||
                                               Lang.bind(this, this._onTimeout));
 | 
			
		||||
        GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTimeout: function() {
 | 
			
		||||
        this._updatePointer();
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updatePointer: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1096
									
								
								js/ui/popupMenu.js
									
									
									
									
									
								
							
							
						
						@@ -120,7 +120,7 @@ const RemoteMenuItemMapper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.menuItem = new PopupMenu.PopupBaseMenuItem();
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this.menuItem.addActor(this._label);
 | 
			
		||||
        this.menuItem.actor.add_child(this._label);
 | 
			
		||||
        this.menuItem.actor.label_actor = this._label;
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -12,181 +12,188 @@ const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const KEY_FILE_GROUP = 'Shell Search Provider';
 | 
			
		||||
 | 
			
		||||
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
 | 
			
		||||
<method name="GetInitialResultSet">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetSubsearchResultSet">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetResultMetas">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="aa{sv}" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ActivateResult">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const SearchProviderIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.SearchProvider"> \
 | 
			
		||||
<method name="GetInitialResultSet"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetSubsearchResultSet"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetResultMetas"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="aa{sv}" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ActivateResult"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
 | 
			
		||||
<method name="GetInitialResultSet">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetSubsearchResultSet">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="as" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetResultMetas">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="aa{sv}" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ActivateResult">
 | 
			
		||||
    <arg type="s" direction="in" />
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="LaunchSearch">
 | 
			
		||||
    <arg type="as" direction="in" />
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const SearchProvider2Iface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.SearchProvider2"> \
 | 
			
		||||
<method name="GetInitialResultSet"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetSubsearchResultSet"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetResultMetas"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="aa{sv}" direction="out" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ActivateResult"> \
 | 
			
		||||
    <arg type="s" direction="in" /> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="LaunchSearch"> \
 | 
			
		||||
    <arg type="as" direction="in" /> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
 | 
			
		||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
 | 
			
		||||
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
 | 
			
		||||
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
 | 
			
		||||
 | 
			
		||||
function loadRemoteSearchProviders(addProviderCallback) {
 | 
			
		||||
    let data = { loadedProviders: [],
 | 
			
		||||
                 objectPaths: {},
 | 
			
		||||
                 addProviderCallback: addProviderCallback };
 | 
			
		||||
    FileUtils.collectFromDatadirsAsync('search-providers',
 | 
			
		||||
                                       { loadedCallback: remoteProvidersLoaded,
 | 
			
		||||
                                         processFile: loadRemoteSearchProvider,
 | 
			
		||||
                                         data: data
 | 
			
		||||
                                       });
 | 
			
		||||
}
 | 
			
		||||
function loadRemoteSearchProviders(callback) {
 | 
			
		||||
    let objectPaths = {};
 | 
			
		||||
    let loadedProviders = [];
 | 
			
		||||
 | 
			
		||||
function loadRemoteSearchProvider(file, info, data) {
 | 
			
		||||
    let keyfile = new GLib.KeyFile();
 | 
			
		||||
    let path = file.get_path();
 | 
			
		||||
    function loadRemoteSearchProvider(file) {
 | 
			
		||||
        let keyfile = new GLib.KeyFile();
 | 
			
		||||
        let path = file.get_path();
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        keyfile.load_from_file(path, 0);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!keyfile.has_group(KEY_FILE_GROUP))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let remoteProvider;
 | 
			
		||||
    try {
 | 
			
		||||
        let group = KEY_FILE_GROUP;
 | 
			
		||||
        let busName = keyfile.get_string(group, 'BusName');
 | 
			
		||||
        let objectPath = keyfile.get_string(group, 'ObjectPath');
 | 
			
		||||
 | 
			
		||||
        if (data.objectPaths[objectPath])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let appInfo = null;
 | 
			
		||||
        try {
 | 
			
		||||
            let desktopId = keyfile.get_string(group, 'DesktopId');
 | 
			
		||||
            appInfo = Gio.DesktopAppInfo.new(desktopId);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            log('Ignoring search provider ' + path + ': missing DesktopId');
 | 
			
		||||
            keyfile.load_from_file(path, 0);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let version = '1';
 | 
			
		||||
        if (!keyfile.has_group(KEY_FILE_GROUP))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let remoteProvider;
 | 
			
		||||
        try {
 | 
			
		||||
            version = keyfile.get_string(group, 'Version');
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // ignore error
 | 
			
		||||
            let group = KEY_FILE_GROUP;
 | 
			
		||||
            let busName = keyfile.get_string(group, 'BusName');
 | 
			
		||||
            let objectPath = keyfile.get_string(group, 'ObjectPath');
 | 
			
		||||
 | 
			
		||||
            if (objectPaths[objectPath])
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let appInfo = null;
 | 
			
		||||
            try {
 | 
			
		||||
                let desktopId = keyfile.get_string(group, 'DesktopId');
 | 
			
		||||
                appInfo = Gio.DesktopAppInfo.new(desktopId);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                log('Ignoring search provider ' + path + ': missing DesktopId');
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let version = '1';
 | 
			
		||||
            try {
 | 
			
		||||
                version = keyfile.get_string(group, 'Version');
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                // ignore error
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (version >= 2)
 | 
			
		||||
                remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
 | 
			
		||||
            else
 | 
			
		||||
                remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
 | 
			
		||||
 | 
			
		||||
            objectPaths[objectPath] = remoteProvider;
 | 
			
		||||
            loadedProviders.push(remoteProvider);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to add search provider %s: %s'.format(path, e.toString()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (version >= 2)
 | 
			
		||||
            remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
 | 
			
		||||
        else
 | 
			
		||||
            remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
 | 
			
		||||
 | 
			
		||||
        data.objectPaths[objectPath] = remoteProvider;
 | 
			
		||||
        data.loadedProviders.push(remoteProvider);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        log('Failed to add search provider %s: %s'.format(path, e.toString()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function remoteProvidersLoaded(loadState) {
 | 
			
		||||
    let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
 | 
			
		||||
    if (searchSettings.get_boolean('disable-external')) {
 | 
			
		||||
        callback([]);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
 | 
			
		||||
 | 
			
		||||
    let sortOrder = searchSettings.get_strv('sort-order');
 | 
			
		||||
 | 
			
		||||
    // Special case gnome-control-center to be always active and always first
 | 
			
		||||
    sortOrder.unshift('gnome-control-center.desktop');
 | 
			
		||||
 | 
			
		||||
    loadState.loadedProviders.sort(
 | 
			
		||||
        function(providerA, providerB) {
 | 
			
		||||
            let idxA, idxB;
 | 
			
		||||
            let appIdA, appIdB;
 | 
			
		||||
    loadedProviders = loadedProviders.filter(function(provider) {
 | 
			
		||||
        let appId = provider.appInfo.get_id();
 | 
			
		||||
        let disabled = searchSettings.get_strv('disabled');
 | 
			
		||||
        return disabled.indexOf(appId) == -1;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
            appIdA = providerA.appInfo.get_id();
 | 
			
		||||
            appIdB = providerB.appInfo.get_id();
 | 
			
		||||
    loadedProviders.sort(function(providerA, providerB) {
 | 
			
		||||
        let idxA, idxB;
 | 
			
		||||
        let appIdA, appIdB;
 | 
			
		||||
 | 
			
		||||
            idxA = sortOrder.indexOf(appIdA);
 | 
			
		||||
            idxB = sortOrder.indexOf(appIdB);
 | 
			
		||||
        appIdA = providerA.appInfo.get_id();
 | 
			
		||||
        appIdB = providerB.appInfo.get_id();
 | 
			
		||||
 | 
			
		||||
            // if no provider is found in the order, use alphabetical order
 | 
			
		||||
            if ((idxA == -1) && (idxB == -1)) {
 | 
			
		||||
                let nameA = providerA.appInfo.get_name();
 | 
			
		||||
                let nameB = providerB.appInfo.get_name();
 | 
			
		||||
        idxA = sortOrder.indexOf(appIdA);
 | 
			
		||||
        idxB = sortOrder.indexOf(appIdB);
 | 
			
		||||
 | 
			
		||||
                return GLib.utf8_collate(nameA, nameB);
 | 
			
		||||
            }
 | 
			
		||||
        // if no provider is found in the order, use alphabetical order
 | 
			
		||||
        if ((idxA == -1) && (idxB == -1)) {
 | 
			
		||||
            let nameA = providerA.appInfo.get_name();
 | 
			
		||||
            let nameB = providerB.appInfo.get_name();
 | 
			
		||||
 | 
			
		||||
            // if providerA isn't found, it's sorted after providerB
 | 
			
		||||
            if (idxA == -1)
 | 
			
		||||
                return 1;
 | 
			
		||||
            return GLib.utf8_collate(nameA, nameB);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // if providerB isn't found, it's sorted after providerA
 | 
			
		||||
            if (idxB == -1)
 | 
			
		||||
                return -1;
 | 
			
		||||
        // if providerA isn't found, it's sorted after providerB
 | 
			
		||||
        if (idxA == -1)
 | 
			
		||||
            return 1;
 | 
			
		||||
 | 
			
		||||
            // finally, if both providers are found, return their order in the list
 | 
			
		||||
            return (idxA - idxB);
 | 
			
		||||
        });
 | 
			
		||||
        // if providerB isn't found, it's sorted after providerA
 | 
			
		||||
        if (idxB == -1)
 | 
			
		||||
            return -1;
 | 
			
		||||
 | 
			
		||||
    loadState.loadedProviders.forEach(
 | 
			
		||||
        function(provider) {
 | 
			
		||||
            loadState.addProviderCallback(provider);
 | 
			
		||||
        });
 | 
			
		||||
        // finally, if both providers are found, return their order in the list
 | 
			
		||||
        return (idxA - idxB);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    callback(loadedProviders);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteSearchProvider',
 | 
			
		||||
 | 
			
		||||
    _init: function(appInfo, dbusName, dbusPath, proxyType) {
 | 
			
		||||
        if (!proxyType)
 | 
			
		||||
            proxyType = SearchProviderProxy;
 | 
			
		||||
    _init: function(appInfo, dbusName, dbusPath, proxyInfo) {
 | 
			
		||||
        if (!proxyInfo)
 | 
			
		||||
            proxyInfo = SearchProviderProxyInfo;
 | 
			
		||||
 | 
			
		||||
        this.proxy = new proxyType(Gio.DBus.session,
 | 
			
		||||
                dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
 | 
			
		||||
        this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
 | 
			
		||||
                                         g_name: dbusName,
 | 
			
		||||
                                         g_object_path: dbusPath,
 | 
			
		||||
                                         g_interface_info: proxyInfo,
 | 
			
		||||
                                         g_interface_name: proxyInfo.name,
 | 
			
		||||
                                         g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
 | 
			
		||||
                                                   Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
 | 
			
		||||
        this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
 | 
			
		||||
 | 
			
		||||
        this.appInfo = appInfo;
 | 
			
		||||
        this.id = appInfo.get_id();
 | 
			
		||||
        this.isRemoteProvider = true;
 | 
			
		||||
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onProxyConstructed: function(proxy) {
 | 
			
		||||
        // Do nothing
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size, meta) {
 | 
			
		||||
        let gicon;
 | 
			
		||||
        let gicon = null;
 | 
			
		||||
        let icon = null;
 | 
			
		||||
 | 
			
		||||
        if (meta['icon']) {
 | 
			
		||||
            gicon = Gio.icon_deserialize(meta['icon']);
 | 
			
		||||
        } else if (meta['gicon']) {
 | 
			
		||||
@@ -198,44 +205,49 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                                                       bitsPerSample, width, height, rowStride);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new St.Icon({ gicon: gicon,
 | 
			
		||||
                             icon_size: size });
 | 
			
		||||
        if (gicon)
 | 
			
		||||
            icon = new St.Icon({ gicon: gicon,
 | 
			
		||||
                                 icon_size: size });
 | 
			
		||||
        return icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error) {
 | 
			
		||||
        if (error)
 | 
			
		||||
    filterResults: function(results, maxNumber) {
 | 
			
		||||
        if (results.length <= maxNumber)
 | 
			
		||||
            return results;
 | 
			
		||||
 | 
			
		||||
        let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
 | 
			
		||||
        let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
 | 
			
		||||
 | 
			
		||||
        return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error, callback) {
 | 
			
		||||
        if (error) {
 | 
			
		||||
            if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
 | 
			
		||||
                log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
 | 
			
		||||
            callback([]);
 | 
			
		||||
            return;
 | 
			
		||||
        this.searchSystem.setResults(this, results[0]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        callback(results[0]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this._cancellable.cancel();
 | 
			
		||||
        this._cancellable.reset();
 | 
			
		||||
        try {
 | 
			
		||||
            this.proxy.GetInitialResultSetRemote(terms,
 | 
			
		||||
                                                 Lang.bind(this, this._getResultsFinished),
 | 
			
		||||
                                                 this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            this.searchSystem.setResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    getInitialResultSet: function(terms, callback, cancellable) {
 | 
			
		||||
        this.proxy.GetInitialResultSetRemote(terms,
 | 
			
		||||
                                             Lang.bind(this, this._getResultsFinished, callback),
 | 
			
		||||
                                             cancellable);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, newTerms) {
 | 
			
		||||
        this._cancellable.cancel();
 | 
			
		||||
        this._cancellable.reset();
 | 
			
		||||
        try {
 | 
			
		||||
            this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
 | 
			
		||||
                                                   Lang.bind(this, this._getResultsFinished),
 | 
			
		||||
                                                   this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            this.searchSystem.setResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
 | 
			
		||||
        this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
 | 
			
		||||
                                               Lang.bind(this, this._getResultsFinished, callback),
 | 
			
		||||
                                               cancellable);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultMetasFinished: function(results, error, callback) {
 | 
			
		||||
        if (error) {
 | 
			
		||||
            if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
 | 
			
		||||
                log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
 | 
			
		||||
            callback([]);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -257,17 +269,10 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
        callback(resultMetas);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getResultMetas: function(ids, callback) {
 | 
			
		||||
        this._cancellable.cancel();
 | 
			
		||||
        this._cancellable.reset();
 | 
			
		||||
        try {
 | 
			
		||||
            this.proxy.GetResultMetasRemote(ids,
 | 
			
		||||
                                            Lang.bind(this, this._getResultMetasFinished, callback),
 | 
			
		||||
                                            this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            callback([]);
 | 
			
		||||
        }
 | 
			
		||||
    getResultMetas: function(ids, callback, cancellable) {
 | 
			
		||||
        this.proxy.GetResultMetasRemote(ids,
 | 
			
		||||
                                        Lang.bind(this, this._getResultMetasFinished, callback),
 | 
			
		||||
                                        cancellable);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
@@ -278,7 +283,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());
 | 
			
		||||
        this.appInfo.launch([], global.create_app_launch_context(0, -1));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -287,7 +292,7 @@ const RemoteSearchProvider2 = new Lang.Class({
 | 
			
		||||
    Extends: RemoteSearchProvider,
 | 
			
		||||
 | 
			
		||||
    _init: function(appInfo, dbusName, dbusPath) {
 | 
			
		||||
        this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
 | 
			
		||||
        this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
 | 
			
		||||
 | 
			
		||||
        this.canLaunchSearch = true;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,9 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
        let label = new St.Label({ style_class: 'run-dialog-label',
 | 
			
		||||
                                   text: _("Enter a Command") });
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.add(label, { y_align: St.Align.START });
 | 
			
		||||
        this.contentLayout.add(label, { x_fill: false,
 | 
			
		||||
                                        x_align: St.Align.START,
 | 
			
		||||
                                        y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let entry = new St.Entry({ style_class: 'run-dialog-entry',
 | 
			
		||||
                                   can_focus: true });
 | 
			
		||||
@@ -101,6 +103,8 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
        this._errorMessage.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        this._errorBox.add(this._errorMessage, { expand: true,
 | 
			
		||||
                                                 x_align: St.Align.START,
 | 
			
		||||
                                                 x_fill: false,
 | 
			
		||||
                                                 y_align: St.Align.MIDDLE,
 | 
			
		||||
                                                 y_fill: false });
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +128,7 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
                    !this.pushModal())
 | 
			
		||||
                    this.close();
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.Tab) {
 | 
			
		||||
                let text = o.get_text();
 | 
			
		||||
@@ -138,9 +142,9 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
                    o.insert_text(postfix, -1);
 | 
			
		||||
                    o.set_cursor_position(text.length + postfix.length);
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -229,7 +233,7 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
                    let file = Gio.file_new_for_path(path);
 | 
			
		||||
                    try {
 | 
			
		||||
                        Gio.app_info_launch_default_for_uri(file.get_uri(),
 | 
			
		||||
                                                            global.create_app_launch_context());
 | 
			
		||||
                                                            global.create_app_launch_context(0, -1));
 | 
			
		||||
                    } catch (e) {
 | 
			
		||||
                        // The exception from gjs contains an error string like:
 | 
			
		||||
                        //     Error invoking Gio.app_info_launch_default_for_uri: No application
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GnomeDesktop = imports.gi.GnomeDesktop;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
@@ -15,14 +17,15 @@ const TweenerEquations = imports.tweener.equations;
 | 
			
		||||
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const OVirt = imports.gdm.oVirt;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const ShellDBus = imports.ui.shellDBus;
 | 
			
		||||
const SmartcardManager = imports.misc.smartcardManager;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
@@ -49,12 +52,10 @@ const SUMMARY_ICON_SIZE = 48;
 | 
			
		||||
//   or when cancelling the dialog
 | 
			
		||||
// - BACKGROUND_FADE_TIME is used when the background changes to crossfade to new background
 | 
			
		||||
// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
 | 
			
		||||
// - INITIAL_FADE_IN_TIME is used for the initial fade in at startup
 | 
			
		||||
const STANDARD_FADE_TIME = 10;
 | 
			
		||||
const MANUAL_FADE_TIME = 0.3;
 | 
			
		||||
const BACKGROUND_FADE_TIME = 1.0;
 | 
			
		||||
const CURTAIN_SLIDE_TIME = 0.3;
 | 
			
		||||
const INITIAL_FADE_IN_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const Clock = new Lang.Class({
 | 
			
		||||
    Name: 'ScreenShieldClock',
 | 
			
		||||
@@ -104,15 +105,16 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
        this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
 | 
			
		||||
                                      visible: false });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START,
 | 
			
		||||
                                               hscrollbar_policy: Gtk.PolicyType.NEVER });
 | 
			
		||||
        this._notificationBox = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                                   style_class: 'screen-shield-notifications-box' });
 | 
			
		||||
        scrollView.add_actor(this._notificationBox);
 | 
			
		||||
        this._scrollView.add_actor(this._notificationBox);
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._musicBin);
 | 
			
		||||
        this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
 | 
			
		||||
        this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._sources = new Hash.Map();
 | 
			
		||||
        this._sources = new Map();
 | 
			
		||||
        Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
 | 
			
		||||
            this._sourceAdded(Main.messageTray, source, true);
 | 
			
		||||
        }));
 | 
			
		||||
@@ -127,9 +129,8 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
            this._sourceAddedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let items = this._sources.items();
 | 
			
		||||
        for (let i = 0; i < items.length; i++) {
 | 
			
		||||
            let [source, obj] = items[i];
 | 
			
		||||
        let items = this._sources.entries();
 | 
			
		||||
        for (let [source, obj] of items) {
 | 
			
		||||
            this._removeSource(source, obj);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -195,8 +196,8 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            let body = '';
 | 
			
		||||
            if (n.bannerBodyText) {
 | 
			
		||||
                body = n.bannerBodyMarkup ? n.bannerBodyText :
 | 
			
		||||
                GLib.markup_escape_text(n.bannerBodyMarkup, -1);
 | 
			
		||||
                body = n.bannerBodyMarkup ? n.bannerBodyText
 | 
			
		||||
                                          : GLib.markup_escape_text(n.bannerBodyText, -1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
 | 
			
		||||
@@ -236,11 +237,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
            (source.unseenCount > (musicNotification ? 1 : 0));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sourceAdded: function(tray, source, dontUpdateVisibility) {
 | 
			
		||||
        // Ignore transient sources
 | 
			
		||||
        if (source.isTransient)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
    _sourceAdded: function(tray, source, initial) {
 | 
			
		||||
        let obj = {
 | 
			
		||||
            visible: source.policy.showInLockScreen,
 | 
			
		||||
            detailed: source.policy.detailsInLockScreen,
 | 
			
		||||
@@ -283,8 +280,29 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._sources.set(source, obj);
 | 
			
		||||
 | 
			
		||||
        if (!dontUpdateVisibility)
 | 
			
		||||
        if (!initial) {
 | 
			
		||||
            // block scrollbars while animating, if they're not needed now
 | 
			
		||||
            let boxHeight = this._notificationBox.height;
 | 
			
		||||
            if (this._scrollView.height >= boxHeight)
 | 
			
		||||
                this._scrollView.vscrollbar_policy = Gtk.PolicyType.NEVER;
 | 
			
		||||
 | 
			
		||||
            let widget = obj.sourceBox;
 | 
			
		||||
            let [, natHeight] = widget.get_preferred_height(-1);
 | 
			
		||||
            widget.height = 0;
 | 
			
		||||
            Tweener.addTween(widget,
 | 
			
		||||
                             { height: natHeight,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               time: 0.25,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   this._scrollView.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
 | 
			
		||||
                                   widget.set_height(-1);
 | 
			
		||||
                               },
 | 
			
		||||
                               onCompleteScope: this
 | 
			
		||||
                             });
 | 
			
		||||
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _titleChanged: function(source, obj) {
 | 
			
		||||
@@ -306,7 +324,10 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        obj.sourceBox.visible = obj.visible &&
 | 
			
		||||
            (source.unseenCount > (obj.musicNotification ? 1 : 0));
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        if (obj.sourceBox.visible)
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _visibleChanged: function(source, obj) {
 | 
			
		||||
@@ -320,6 +341,8 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
            source.unseenCount > (obj.musicNotification ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        if (obj.sourceBox.visible)
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _detailedChanged: function(source, obj) {
 | 
			
		||||
@@ -357,6 +380,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
        this._sources.delete(source);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(NotificationsBox.prototype);
 | 
			
		||||
 | 
			
		||||
const Arrow = new Lang.Class({
 | 
			
		||||
    Name: 'Arrow',
 | 
			
		||||
@@ -390,6 +414,7 @@ const Arrow = new Lang.Class({
 | 
			
		||||
        cr.lineTo(w/2, thickness);
 | 
			
		||||
        cr.lineTo(w - thickness / 2, h - thickness / 2);
 | 
			
		||||
        cr.stroke();
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_style_changed: function() {
 | 
			
		||||
@@ -489,16 +514,10 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._lockDialogGroup = new St.Widget({ x_expand: true,
 | 
			
		||||
                                                y_expand: true,
 | 
			
		||||
                                                opacity: 0,
 | 
			
		||||
                                                reactive: true,
 | 
			
		||||
                                                pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
 | 
			
		||||
                                                name: 'lockDialogGroup' });
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._lockDialogGroup,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: INITIAL_FADE_IN_TIME,
 | 
			
		||||
                           transition: 'easeInQuad',
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._lockDialogGroup);
 | 
			
		||||
        this.actor.add_actor(this._lockScreenGroup);
 | 
			
		||||
 | 
			
		||||
@@ -516,6 +535,20 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
 | 
			
		||||
 | 
			
		||||
        this._smartcardManager = SmartcardManager.getSmartcardManager();
 | 
			
		||||
        this._smartcardManager.connect('smartcard-inserted',
 | 
			
		||||
                                       Lang.bind(this, function(manager, token) {
 | 
			
		||||
                                           if (this._isLocked && token.UsedToLogin)
 | 
			
		||||
                                               this._liftShield(true, 0);
 | 
			
		||||
                                       }));
 | 
			
		||||
 | 
			
		||||
        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
 | 
			
		||||
        this._oVirtCredentialsManager.connect('user-authenticated',
 | 
			
		||||
                                              Lang.bind(this, function() {
 | 
			
		||||
                                                  if (this._isLocked)
 | 
			
		||||
                                                      this._liftShield(true, 0);
 | 
			
		||||
                                              }));
 | 
			
		||||
 | 
			
		||||
        this._inhibitor = null;
 | 
			
		||||
        this._aboutToSuspend = false;
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
@@ -542,13 +575,20 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this._becameActiveId = 0;
 | 
			
		||||
        this._lockTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
 | 
			
		||||
                                               { inhibitEvents: true,
 | 
			
		||||
                                                 fadeInTime: STANDARD_FADE_TIME,
 | 
			
		||||
                                                 fadeFactor: 1 });
 | 
			
		||||
        this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
 | 
			
		||||
        // The "long" lightbox is used for the longer (20 seconds) fade from session
 | 
			
		||||
        // to idle status, the "short" is used for quickly fading to black when locking
 | 
			
		||||
        // manually
 | 
			
		||||
        this._longLightbox = new Lightbox.Lightbox(Main.uiGroup,
 | 
			
		||||
                                                   { inhibitEvents: true,
 | 
			
		||||
                                                     fadeFactor: 1 });
 | 
			
		||||
        this._longLightbox.connect('shown', Lang.bind(this, this._onLongLightboxShown));
 | 
			
		||||
        this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup,
 | 
			
		||||
                                                    { inhibitEvents: true,
 | 
			
		||||
                                                      fadeFactor: 1 });
 | 
			
		||||
        this._shortLightbox.connect('shown', Lang.bind(this, this._onShortLightboxShown));
 | 
			
		||||
 | 
			
		||||
        this.idleMonitor = new GnomeDesktop.IdleMonitor();
 | 
			
		||||
        this.idleMonitor = Meta.IdleMonitor.get_core();
 | 
			
		||||
        this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createBackground: function(monitorIndex) {
 | 
			
		||||
@@ -561,7 +601,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let bgManager = new Background.BackgroundManager({ container: widget,
 | 
			
		||||
                                                           monitorIndex: monitorIndex,
 | 
			
		||||
                                                           controlPosition: false });
 | 
			
		||||
                                                           controlPosition: false,
 | 
			
		||||
                                                           settingsSchema: SCREENSAVER_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._bgManagers.push(bgManager);
 | 
			
		||||
 | 
			
		||||
@@ -581,13 +622,28 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _liftShield: function(onPrimary, velocity) {
 | 
			
		||||
        if (this._isLocked) {
 | 
			
		||||
            this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
 | 
			
		||||
            this._hideLockScreen(true /* animate */, velocity);
 | 
			
		||||
            if (this._ensureUnlockDialog(onPrimary, true /* allowCancel */))
 | 
			
		||||
                this._hideLockScreen(true /* animate */, velocity);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.deactivate(true /* animate */);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeCancelDialog: function() {
 | 
			
		||||
        if (!this._dialog)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._dialog.cancel();
 | 
			
		||||
        if (this._isGreeter) {
 | 
			
		||||
            // LoginDialog.cancel() will grab the key focus
 | 
			
		||||
            // on its own, so ensure it stays on lock screen
 | 
			
		||||
            // instead
 | 
			
		||||
            this._lockScreenGroup.grab_key_focus();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._dialog = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _becomeModal: function() {
 | 
			
		||||
        if (this._isModal)
 | 
			
		||||
            return true;
 | 
			
		||||
@@ -613,24 +669,24 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        // down after cancel.
 | 
			
		||||
 | 
			
		||||
        if (this._lockScreenState != MessageTray.State.SHOWN)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
 | 
			
		||||
        if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this._ensureUnlockDialog(true, true);
 | 
			
		||||
 | 
			
		||||
        if (GLib.unichar_isgraph(unichar))
 | 
			
		||||
        if (this._isLocked &&
 | 
			
		||||
            this._ensureUnlockDialog(true, true) &&
 | 
			
		||||
            GLib.unichar_isgraph(unichar))
 | 
			
		||||
            this._dialog.addCharacter(unichar);
 | 
			
		||||
 | 
			
		||||
        this._liftShield(true, 0);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLockScreenScroll: function(actor, event) {
 | 
			
		||||
        if (this._lockScreenState != MessageTray.State.SHOWN)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        let delta = 0;
 | 
			
		||||
        if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
 | 
			
		||||
@@ -645,7 +701,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this._liftShield(true, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _inhibitSuspend: function() {
 | 
			
		||||
@@ -672,6 +728,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this.lock(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._inhibitSuspend();
 | 
			
		||||
 | 
			
		||||
            this._wakeUpScreen();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -694,7 +752,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
@@ -743,13 +801,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                             });
 | 
			
		||||
 | 
			
		||||
            // If we have a unlock dialog, cancel it
 | 
			
		||||
            if (this._dialog) {
 | 
			
		||||
                this._dialog.cancel();
 | 
			
		||||
                if (!this._isGreeter) {
 | 
			
		||||
                    this._dialog = null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this._maybeCancelDialog();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -757,27 +809,9 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        if (status != GnomeSession.PresenceStatus.IDLE)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._dialog) {
 | 
			
		||||
            this._dialog.cancel();
 | 
			
		||||
            if (!this._isGreeter) {
 | 
			
		||||
                this._dialog = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._maybeCancelDialog();
 | 
			
		||||
 | 
			
		||||
        if (!this._becomeModal()) {
 | 
			
		||||
            // We could not become modal, so we can't activate the
 | 
			
		||||
            // screenshield. The user is probably very upset at this
 | 
			
		||||
            // point, but any application using global grabs is broken
 | 
			
		||||
            // Just tell him to stop using this app
 | 
			
		||||
            // 
 | 
			
		||||
            // XXX: another option is to kick the user into the gdm login
 | 
			
		||||
            // screen, where we're not affected by grabs
 | 
			
		||||
            Main.notifyError(_("Unable to lock"),
 | 
			
		||||
                             _("Lock was blocked by an application"));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._lightbox.actor.visible ||
 | 
			
		||||
        if (this._longLightbox.actor.visible ||
 | 
			
		||||
            this._isActive) {
 | 
			
		||||
            // We're either shown and active, or in the process of
 | 
			
		||||
            // showing.
 | 
			
		||||
@@ -790,13 +824,22 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._lightbox.show();
 | 
			
		||||
        if (!this._becomeModal()) {
 | 
			
		||||
            // We could not become modal, so we can't activate the
 | 
			
		||||
            // screenshield. The user is probably very upset at this
 | 
			
		||||
            // point, but any application using global grabs is broken
 | 
			
		||||
            // Just tell him to stop using this app
 | 
			
		||||
            //
 | 
			
		||||
            // XXX: another option is to kick the user into the gdm login
 | 
			
		||||
            // screen, where we're not affected by grabs
 | 
			
		||||
            Main.notifyError(_("Unable to lock"),
 | 
			
		||||
                             _("Lock was blocked by an application"));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._activationTime == 0)
 | 
			
		||||
            this._activationTime = GLib.get_monotonic_time();
 | 
			
		||||
 | 
			
		||||
        this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
 | 
			
		||||
 | 
			
		||||
        let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
 | 
			
		||||
 | 
			
		||||
        if (shouldLock) {
 | 
			
		||||
@@ -804,48 +847,60 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._lockTimeoutId = 0;
 | 
			
		||||
                                                           this.lock(true);
 | 
			
		||||
                                                           return false;
 | 
			
		||||
                                                           this.lock(false);
 | 
			
		||||
                                                           return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                       }));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._lockTimeoutId, '[gnome-shell] this.lock');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activateFade: function(lightbox, time) {
 | 
			
		||||
        lightbox.show(time);
 | 
			
		||||
 | 
			
		||||
        if (this._becameActiveId == 0)
 | 
			
		||||
            this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserBecameActive: function() {
 | 
			
		||||
        // This function gets called here when the user becomes active
 | 
			
		||||
        // after gnome-session changed the status to IDLE
 | 
			
		||||
        // There are four possibilities here:
 | 
			
		||||
        // - we're called when already locked; isActive and isLocked are true,
 | 
			
		||||
        // after we activated a lightbox
 | 
			
		||||
        // There are two possibilities here:
 | 
			
		||||
        // - we're called when already locked/active; isLocked or isActive is true,
 | 
			
		||||
        //   we just go back to the lock screen curtain
 | 
			
		||||
        // - we're called before the lightbox is fully shown; at this point
 | 
			
		||||
        //   isActive is false, so we just hide the ligthbox, reset the activationTime
 | 
			
		||||
        //   and go back to the unlocked desktop
 | 
			
		||||
        // - we're called after showing the lightbox, but before the lock
 | 
			
		||||
        //   delay; this is mostly like the case above, but isActive is true now
 | 
			
		||||
        //   so we need to notify gnome-settings-daemon to go back to the normal
 | 
			
		||||
        //   policies for blanking
 | 
			
		||||
        //   (they're handled by the same code, and we emit one extra ActiveChanged
 | 
			
		||||
        //   signal in the case above)
 | 
			
		||||
        // - we're called after showing the lightbox and after lock-delay; the
 | 
			
		||||
        //   session is effectivelly locked now, it's time to build and show
 | 
			
		||||
        //   the lock screen
 | 
			
		||||
        //   (isActive == isLocked == true: normal case
 | 
			
		||||
        //    isActive == false, isLocked == true: during the fade for manual locking
 | 
			
		||||
        //    isActive == true, isLocked == false: after session idle, before lock-delay)
 | 
			
		||||
        // - we're called because the session is IDLE but before the lightbox
 | 
			
		||||
        //   is fully shown; at this point isActive is false, so we just hide
 | 
			
		||||
        //   the lightbox, reset the activationTime and go back to the unlocked
 | 
			
		||||
        //   desktop
 | 
			
		||||
        //   using deactivate() is a little of overkill, but it ensures we
 | 
			
		||||
        //   don't forget of some bit like modal, DBus properties or idle watches
 | 
			
		||||
        //
 | 
			
		||||
        // Note: if the (long) lightbox is shown then we're necessarily
 | 
			
		||||
        // active, because we call activate() without animation.
 | 
			
		||||
 | 
			
		||||
        this.idleMonitor.remove_watch(this._becameActiveId);
 | 
			
		||||
        this._becameActiveId = 0;
 | 
			
		||||
 | 
			
		||||
        let lightboxWasShown = this._lightbox.shown;
 | 
			
		||||
        this._lightbox.hide();
 | 
			
		||||
 | 
			
		||||
        // Shortcircuit in case the mouse was moved before the fade completed
 | 
			
		||||
        if (!lightboxWasShown) {
 | 
			
		||||
        if (this._isActive || this._isLocked) {
 | 
			
		||||
            this._longLightbox.hide();
 | 
			
		||||
            this._shortLightbox.hide();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.deactivate(false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLightboxShown: function() {
 | 
			
		||||
    _onLongLightboxShown: function() {
 | 
			
		||||
        this.activate(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShortLightboxShown: function() {
 | 
			
		||||
        this._completeLockScreenShown();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showDialog: function() {
 | 
			
		||||
        // Ensure that the stage window is mapped, before taking a grab
 | 
			
		||||
        // otherwise X errors out
 | 
			
		||||
@@ -862,8 +917,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this._isGreeter = Main.sessionMode.isGreeter;
 | 
			
		||||
        this._isLocked = true;
 | 
			
		||||
        this._ensureUnlockDialog(true, true);
 | 
			
		||||
        this._hideLockScreen(false, 0);
 | 
			
		||||
        if (this._ensureUnlockDialog(true, true))
 | 
			
		||||
            this._hideLockScreen(false, 0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideLockScreenComplete: function() {
 | 
			
		||||
@@ -880,6 +935,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._lockScreenState = MessageTray.State.HIDING;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this._lockScreenGroup);
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            // Tween the lock screen out of screen
 | 
			
		||||
            // if velocity is not specified (i.e. we come here from pressing ESC),
 | 
			
		||||
@@ -892,7 +949,6 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            velocity = Math.max(min_velocity, velocity);
 | 
			
		||||
            let time = (delta / velocity) / 1000;
 | 
			
		||||
 | 
			
		||||
            Tweener.removeTweens(this._lockScreenGroup);
 | 
			
		||||
            Tweener.addTween(this._lockScreenGroup,
 | 
			
		||||
                             { y: -h,
 | 
			
		||||
                               time: time,
 | 
			
		||||
@@ -903,7 +959,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this._hideLockScreenComplete();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.stage.show_cursor();
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureUnlockDialog: function(onPrimary, allowCancel) {
 | 
			
		||||
@@ -912,7 +968,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            if (!constructor) {
 | 
			
		||||
                // This session mode has no locking capabilities
 | 
			
		||||
                this.deactivate(true);
 | 
			
		||||
                return;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._dialog = new constructor(this._lockDialogGroup);
 | 
			
		||||
@@ -924,24 +980,22 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                // by the time we reach this...
 | 
			
		||||
                log('Could not open login dialog: failed to acquire grab');
 | 
			
		||||
                this.deactivate(true);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
 | 
			
		||||
            this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dialog.allowCancel = allowCancel;
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUnlockFailed: function() {
 | 
			
		||||
        this._resetLockScreen(true, false);
 | 
			
		||||
        this._resetLockScreen({ animateLockScreen: true,
 | 
			
		||||
                                fadeToBlack: false });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUnlockSucceded: function() {
 | 
			
		||||
        this.deactivate(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetLockScreen: function(animateLockScreen, animateLockDialog) {
 | 
			
		||||
    _resetLockScreen: function(params) {
 | 
			
		||||
        // Don't reset the lock screen unless it is completely hidden
 | 
			
		||||
        // This prevents the shield going down if the lock-delay timeout
 | 
			
		||||
        // fires while the user is dragging (which has the potential
 | 
			
		||||
@@ -956,7 +1010,9 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this._lockScreenGroup.show();
 | 
			
		||||
        this._lockScreenState = MessageTray.State.SHOWING;
 | 
			
		||||
 | 
			
		||||
        if (animateLockScreen) {
 | 
			
		||||
        let fadeToBlack = params.fadeToBlack;
 | 
			
		||||
 | 
			
		||||
        if (params.animateLockScreen) {
 | 
			
		||||
            this._lockScreenGroup.y = -global.screen_height;
 | 
			
		||||
            Tweener.removeTweens(this._lockScreenGroup);
 | 
			
		||||
            Tweener.addTween(this._lockScreenGroup,
 | 
			
		||||
@@ -964,24 +1020,15 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                               time: MANUAL_FADE_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   this._lockScreenShown();
 | 
			
		||||
                                   this._lockScreenShown({ fadeToBlack: fadeToBlack,
 | 
			
		||||
                                                           animateFade: true });
 | 
			
		||||
                               },
 | 
			
		||||
                               onCompleteScope: this
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._lockScreenGroup.fixed_position_set = false;
 | 
			
		||||
            this._lockScreenShown();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (animateLockDialog) {
 | 
			
		||||
            this._lockDialogGroup.opacity = 0;
 | 
			
		||||
            Tweener.removeTweens(this._lockDialogGroup);
 | 
			
		||||
            Tweener.addTween(this._lockDialogGroup,
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               time: MANUAL_FADE_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._lockDialogGroup.opacity = 255;
 | 
			
		||||
            this._lockScreenShown({ fadeToBlack: fadeToBlack,
 | 
			
		||||
                                    animateFade: false });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._lockScreenGroup.grab_key_focus();
 | 
			
		||||
@@ -995,6 +1042,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (!this._arrowAnimationId) {
 | 
			
		||||
            this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
 | 
			
		||||
            GLib.Source.set_name_by_id(this._arrowAnimationId, '[gnome-shell] this._animateArrows');
 | 
			
		||||
            this._animateArrows();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1037,7 +1085,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this._pauseArrowAnimation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lockScreenShown: function() {
 | 
			
		||||
    _lockScreenShown: function(params) {
 | 
			
		||||
        if (this._dialog && !this._isGreeter) {
 | 
			
		||||
            this._dialog.destroy();
 | 
			
		||||
            this._dialog = null;
 | 
			
		||||
@@ -1045,20 +1093,37 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._checkArrowAnimation();
 | 
			
		||||
 | 
			
		||||
        let motionId = global.stage.connect('captured-event', function(stage, event) {
 | 
			
		||||
        let motionId = global.stage.connect('captured-event', Lang.bind(this, function(stage, event) {
 | 
			
		||||
            if (event.type() == Clutter.EventType.MOTION) {
 | 
			
		||||
                global.stage.show_cursor();
 | 
			
		||||
                this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
                global.stage.disconnect(motionId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
        global.stage.hide_cursor();
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(false);
 | 
			
		||||
 | 
			
		||||
        this._lockScreenState = MessageTray.State.SHOWN;
 | 
			
		||||
        this._lockScreenGroup.fixed_position_set = false;
 | 
			
		||||
        this._lockScreenScrollCounter = 0;
 | 
			
		||||
 | 
			
		||||
        if (params.fadeToBlack && params.animateFade) {
 | 
			
		||||
            // Take a beat
 | 
			
		||||
 | 
			
		||||
            let id = Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
 | 
			
		||||
                this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
            }));
 | 
			
		||||
            GLib.Source.set_name_by_id(id, '[gnome-shell] this._activateFade');
 | 
			
		||||
        } else {
 | 
			
		||||
            if (params.fadeToBlack)
 | 
			
		||||
                this._activateFade(this._shortLightbox, 0);
 | 
			
		||||
 | 
			
		||||
            this._completeLockScreenShown();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeLockScreenShown: function() {
 | 
			
		||||
        let prevIsActive = this._isActive;
 | 
			
		||||
        this._isActive = true;
 | 
			
		||||
 | 
			
		||||
@@ -1090,6 +1155,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this._lockScreenContents.add_actor(this._lockScreenContentsBox);
 | 
			
		||||
 | 
			
		||||
        this._notificationsBox = new NotificationsBox();
 | 
			
		||||
        this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', Lang.bind(this, this._wakeUpScreen));
 | 
			
		||||
        this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
 | 
			
		||||
                                                                        y_fill: true,
 | 
			
		||||
                                                                        expand: true });
 | 
			
		||||
@@ -1097,11 +1163,17 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        this._hasLockScreen = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _wakeUpScreen: function() {
 | 
			
		||||
        this._onUserBecameActive();
 | 
			
		||||
        this.emit('wake-up-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearLockScreen: function() {
 | 
			
		||||
        this._clock.destroy();
 | 
			
		||||
        this._clock = null;
 | 
			
		||||
 | 
			
		||||
        if (this._notificationsBox) {
 | 
			
		||||
            this._notificationsBox.disconnect(this._wakeUpScreenId);
 | 
			
		||||
            this._notificationsBox.destroy();
 | 
			
		||||
            this._notificationsBox = null;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1126,6 +1198,15 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deactivate: function(animate) {
 | 
			
		||||
        if (this._dialog)
 | 
			
		||||
            this._dialog.finish(Lang.bind(this, function() {
 | 
			
		||||
                this._continueDeactivate(animate);
 | 
			
		||||
            }));
 | 
			
		||||
        else
 | 
			
		||||
            this._continueDeactivate(animate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _continueDeactivate: function(animate) {
 | 
			
		||||
        this._hideLockScreen(animate, 0);
 | 
			
		||||
 | 
			
		||||
        if (this._hasLockScreen)
 | 
			
		||||
@@ -1136,6 +1217,20 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        if (Main.sessionMode.currentMode == 'unlock-dialog')
 | 
			
		||||
            Main.sessionMode.popMode('unlock-dialog');
 | 
			
		||||
 | 
			
		||||
        if (this._isGreeter) {
 | 
			
		||||
            // We don't want to "deactivate" any more than
 | 
			
		||||
            // this. In particular, we don't want to drop
 | 
			
		||||
            // the modal, hide ourselves or destroy the dialog
 | 
			
		||||
            // But we do want to set isActive to false, so that
 | 
			
		||||
            // gnome-session will reset the idle counter, and
 | 
			
		||||
            // gnome-settings-daemon will stop blanking the screen
 | 
			
		||||
 | 
			
		||||
            this._activationTime = 0;
 | 
			
		||||
            this._isActive = false;
 | 
			
		||||
            this.emit('active-changed');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._dialog && !this._isGreeter)
 | 
			
		||||
            this._dialog.popModal();
 | 
			
		||||
 | 
			
		||||
@@ -1155,12 +1250,13 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeDeactivate: function() {
 | 
			
		||||
        if (this._dialog && !this._isGreeter) {
 | 
			
		||||
        if (this._dialog) {
 | 
			
		||||
            this._dialog.destroy();
 | 
			
		||||
            this._dialog = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._lightbox.hide();
 | 
			
		||||
        this._longLightbox.hide();
 | 
			
		||||
        this._shortLightbox.hide();
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
 | 
			
		||||
        if (this._becameActiveId != 0) {
 | 
			
		||||
@@ -1194,7 +1290,8 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                Main.sessionMode.pushMode('unlock-dialog');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._resetLockScreen(animate, animate);
 | 
			
		||||
        this._resetLockScreen({ animateLockScreen: animate,
 | 
			
		||||
                                fadeToBlack: true });
 | 
			
		||||
        global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
 | 
			
		||||
 | 
			
		||||
        // We used to set isActive and emit active-changed here,
 | 
			
		||||
@@ -1223,7 +1320,14 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
 | 
			
		||||
        St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
 | 
			
		||||
 | 
			
		||||
        this._isLocked = true;
 | 
			
		||||
        let userManager = AccountsService.UserManager.get_default();
 | 
			
		||||
        let user = userManager.get_user(GLib.get_user_name());
 | 
			
		||||
 | 
			
		||||
        if (this._isGreeter)
 | 
			
		||||
            this._isLocked = true;
 | 
			
		||||
        else
 | 
			
		||||
            this._isLocked = user.password_mode != AccountsService.UserPasswordMode.NONE;
 | 
			
		||||
 | 
			
		||||
        this.activate(animate);
 | 
			
		||||
 | 
			
		||||
        this.emit('locked-changed');
 | 
			
		||||
 
 | 
			
		||||
@@ -4,31 +4,33 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
 | 
			
		||||
<method name="Screencast">
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ScreencastArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="StopScreencast">
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ScreencastIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.Screencast"> \
 | 
			
		||||
<method name="Screencast"> \
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ScreencastArea"> \
 | 
			
		||||
    <arg type="i" direction="in" name="x"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="y"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="width"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="height"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="StopScreencast"> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ScreencastService = new Lang.Class({
 | 
			
		||||
    Name: 'ScreencastService',
 | 
			
		||||
@@ -39,30 +41,35 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
 | 
			
		||||
        this._recorders = new Hash.Map();
 | 
			
		||||
        this._recorders = new Map();
 | 
			
		||||
 | 
			
		||||
        Main.sessionMode.connect('updated',
 | 
			
		||||
                                 Lang.bind(this, this._sessionModeChanged));
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get isRecording() {
 | 
			
		||||
        return this._recorders.size > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorderForSender: function(sender) {
 | 
			
		||||
        let recorder = this._recorders.get(sender);
 | 
			
		||||
        if (!recorder) {
 | 
			
		||||
            recorder = new Shell.Recorder({ stage: global.stage });
 | 
			
		||||
            recorder = new Shell.Recorder({ stage: global.stage,
 | 
			
		||||
                                            screen: global.screen });
 | 
			
		||||
            recorder._watchNameId =
 | 
			
		||||
                Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
 | 
			
		||||
                                   Lang.bind(this, this._onNameVanished));
 | 
			
		||||
            this._recorders.set(sender, recorder);
 | 
			
		||||
            this.emit('updated');
 | 
			
		||||
        }
 | 
			
		||||
        return recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionModeChanged: function() {
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
        if (Main.sessionMode.allowScreencast)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let sender in this._recorders.keys())
 | 
			
		||||
            this._recorders.delete(sender);
 | 
			
		||||
        this._recorders.clear();
 | 
			
		||||
        this.emit('updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameVanished: function(connection, name) {
 | 
			
		||||
@@ -77,6 +84,7 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
        Gio.bus_unwatch_name(recorder._watchNameId);
 | 
			
		||||
        recorder.close();
 | 
			
		||||
        this._recorders.delete(sender);
 | 
			
		||||
        this.emit('updated');
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
@@ -95,8 +103,10 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    ScreencastAsync: function(params, invocation) {
 | 
			
		||||
        let returnValue = [false, ''];
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast)
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast) {
 | 
			
		||||
            invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let recorder = this._ensureRecorderForSender(sender);
 | 
			
		||||
@@ -114,8 +124,10 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    ScreencastAreaAsync: function(params, invocation) {
 | 
			
		||||
        let returnValue = [false, ''];
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast)
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast) {
 | 
			
		||||
            invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let recorder = this._ensureRecorderForSender(sender);
 | 
			
		||||
@@ -123,6 +135,16 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
        if (!recorder.is_recording()) {
 | 
			
		||||
            let [x, y, width, height, fileTemplate, options] = params;
 | 
			
		||||
 | 
			
		||||
            if (x < 0 || y < 0 ||
 | 
			
		||||
                width <= 0 || height <= 0 ||
 | 
			
		||||
                x + width > global.screen_width ||
 | 
			
		||||
                y + height > global.screen_height) {
 | 
			
		||||
                invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                                Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                                "Invalid params");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            recorder.set_file_template(fileTemplate);
 | 
			
		||||
            recorder.set_area(x, y, width, height);
 | 
			
		||||
            this._applyOptionalParameters(recorder, options);
 | 
			
		||||
@@ -138,3 +160,4 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
        invocation.return_value(GLib.Variant.new('(b)', [success]));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ScreencastService.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,53 +6,57 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const GrabHelper = imports.ui.grabHelper;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
 | 
			
		||||
<method name="ScreenshotArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ScreenshotWindow">
 | 
			
		||||
    <arg type="b" direction="in" name="include_frame"/>
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Screenshot">
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="SelectArea">
 | 
			
		||||
    <arg type="i" direction="out" name="x"/>
 | 
			
		||||
    <arg type="i" direction="out" name="y"/>
 | 
			
		||||
    <arg type="i" direction="out" name="width"/>
 | 
			
		||||
    <arg type="i" direction="out" name="height"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FlashArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ScreenshotIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.Screenshot"> \
 | 
			
		||||
<method name="ScreenshotArea"> \
 | 
			
		||||
    <arg type="i" direction="in" name="x"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="y"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="width"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="height"/> \
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ScreenshotWindow"> \
 | 
			
		||||
    <arg type="b" direction="in" name="include_frame"/> \
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/> \
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Screenshot"> \
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/> \
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="SelectArea"> \
 | 
			
		||||
    <arg type="i" direction="out" name="x"/> \
 | 
			
		||||
    <arg type="i" direction="out" name="y"/> \
 | 
			
		||||
    <arg type="i" direction="out" name="width"/> \
 | 
			
		||||
    <arg type="i" direction="out" name="height"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="FlashArea"> \
 | 
			
		||||
    <arg type="i" direction="in" name="x"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="y"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="width"/> \
 | 
			
		||||
    <arg type="i" direction="in" name="height"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ScreenshotService = new Lang.Class({
 | 
			
		||||
    Name: 'ScreenshotService',
 | 
			
		||||
@@ -64,6 +68,13 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkArea: function(x, y, width, height) {
 | 
			
		||||
        return x >= 0 && y >= 0 &&
 | 
			
		||||
               width > 0 && height > 0 &&
 | 
			
		||||
               x + width <= global.screen_width &&
 | 
			
		||||
               y + height <= global.screen_height;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
 | 
			
		||||
        if (flash && result) {
 | 
			
		||||
            let flashspot = new Flashspot(area);
 | 
			
		||||
@@ -76,9 +87,10 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    ScreenshotAreaAsync : function (params, invocation) {
 | 
			
		||||
        let [x, y, width, height, flash, filename, callback] = params;
 | 
			
		||||
        if (height <= 0 || width <= 0) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                        "Invalid params");
 | 
			
		||||
        if (!this._checkArea(x, y, width, height)) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                            Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                            "Invalid params");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
@@ -120,9 +132,17 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FlashArea: function(x, y, width, height) {
 | 
			
		||||
    FlashAreaAsync: function(params, invocation) {
 | 
			
		||||
        let [x, y, width, height] = params;
 | 
			
		||||
        if (!this._checkArea(x, y, width, height)) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                            Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                            "Invalid params");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
 | 
			
		||||
        flashspot.fire();
 | 
			
		||||
        invocation.return_value(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -134,6 +154,7 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
        this._startY = -1;
 | 
			
		||||
        this._lastX = 0;
 | 
			
		||||
        this._lastY = 0;
 | 
			
		||||
        this._result = null;
 | 
			
		||||
 | 
			
		||||
        this._initRubberbandColors();
 | 
			
		||||
 | 
			
		||||
@@ -143,12 +164,12 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
                                      y: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        this._grabHelper = new GrabHelper.GrabHelper(this._group);
 | 
			
		||||
 | 
			
		||||
        this._group.connect('button-press-event',
 | 
			
		||||
                            Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this._group.connect('button-release-event',
 | 
			
		||||
                            Lang.bind(this, this._onButtonRelease));
 | 
			
		||||
        this._group.connect('key-press-event',
 | 
			
		||||
                            Lang.bind(this, this._onKeyPress));
 | 
			
		||||
        this._group.connect('motion-event',
 | 
			
		||||
                            Lang.bind(this, this._onMotionEvent));
 | 
			
		||||
 | 
			
		||||
@@ -164,10 +185,12 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (!Main.pushModal(this._group) || this._group.visible)
 | 
			
		||||
        if (!this._grabHelper.grab({ actor: this._group,
 | 
			
		||||
                                     onUngrab: Lang.bind(this, this._onUngrab) }))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        global.set_cursor(Shell.Cursor.CROSSHAIR);
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
 | 
			
		||||
        Main.uiGroup.set_child_above_sibling(this._group, null);
 | 
			
		||||
        this._group.visible = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -197,16 +220,9 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
                 height: Math.abs(this._startY - this._lastY) };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape)
 | 
			
		||||
            this._destroy(null, false);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMotionEvent: function(actor, event) {
 | 
			
		||||
        if (this._startX == -1 || this._startY == -1)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        [this._lastX, this._lastY] = event.get_coords();
 | 
			
		||||
        let geometry = this._getGeometry();
 | 
			
		||||
@@ -214,35 +230,39 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
        this._rubberband.set_position(geometry.x, geometry.y);
 | 
			
		||||
        this._rubberband.set_size(geometry.width, geometry.height);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        [this._startX, this._startY] = event.get_coords();
 | 
			
		||||
        this._rubberband.set_position(this._startX, this._startY);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonRelease: function(actor, event) {
 | 
			
		||||
        this._destroy(this._getGeometry(), true);
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroy: function(geometry, fade) {
 | 
			
		||||
        this._result = this._getGeometry();
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: fade ? 0.2 : 0,
 | 
			
		||||
                           time: 0.2,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   Main.popModal(this._group);
 | 
			
		||||
                                   this._group.destroy();
 | 
			
		||||
                                   global.unset_cursor();
 | 
			
		||||
 | 
			
		||||
                                   this.emit('finished', geometry);
 | 
			
		||||
                                   this._grabHelper.ungrab();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUngrab: function() {
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
        this.emit('finished', this._result);
 | 
			
		||||
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._group.destroy();
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
            }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SelectArea.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
@@ -38,11 +39,12 @@ const Main = imports.ui.main;
 | 
			
		||||
function sleep(milliseconds) {
 | 
			
		||||
    let cb;
 | 
			
		||||
 | 
			
		||||
    Mainloop.timeout_add(milliseconds, function() {
 | 
			
		||||
    let id = Mainloop.timeout_add(milliseconds, function() {
 | 
			
		||||
                             if (cb)
 | 
			
		||||
                                 cb();
 | 
			
		||||
                             return false;
 | 
			
		||||
                             return GLib.SOURCE_REMOVE;
 | 
			
		||||
                         });
 | 
			
		||||
    GLib.Source.set_name_by_id(id, '[gnome-shell] sleep');
 | 
			
		||||
 | 
			
		||||
    return function(callback) {
 | 
			
		||||
        cb = callback;
 | 
			
		||||
@@ -69,16 +71,18 @@ function waitLeisure() {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
 | 
			
		||||
<method name="CreateWindow">
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
    <arg type="i" direction="in" />
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="WaitWindows" />
 | 
			
		||||
<method name="DestroyWindows" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const PerfHelperIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.PerfHelper"> \
 | 
			
		||||
<method name="CreateWindow"> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="i" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="in" /> \
 | 
			
		||||
    <arg type="b" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="WaitWindows" /> \
 | 
			
		||||
<method name="DestroyWindows" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
 | 
			
		||||
function PerfHelper() {
 | 
			
		||||
 
 | 
			
		||||